fromoptimum.rblnimportRBLNLlamaForCausalLM# HuggingFace PyTorch Llama3 모델을 RBLN 컴파일된 모델로 내보내기model_id="meta-llama/Meta-Llama-3-8B-Instruct"compiled_model=RBLNLlamaForCausalLM.from_pretrained(model_id=model_id,export=True,rbln_max_seq_len=8192,rbln_num_devices=1,rbln_batch_size=1,)compiled_model.save_pretrained("rbln-Llama-3-8B-Instruct")
fromoptimum.rblnimportRBLNLlamaForCausalLM# HuggingFace PyTorch Llama3 모델을 RBLN 컴파일된 모델로 내보내기model_id="meta-llama/Meta-Llama-3-8B-Instruct"compiled_model=RBLNLlamaForCausalLM.from_pretrained(model_id=model_id,export=True,rbln_max_seq_len=8192,rbln_num_devices=4,# Rebellions Scalable Design (RSD)를 위한 ATOM™+ 개수rbln_batch_size=4,# Continuous batching을 위해 batch_size > 1 권장)compiled_model.save_pretrained("rbln-Llama-3-8B-Instruct")
Note
배치 크기는 서빙 환경에 맞게 선택할 수 있습니다. 여기에서는 4로 설정합니다. Llama3-8B 모델을 사용하려면 4개의 RBLN NPU가 필요하며, 모델별 구동에 필요한 RBLN NPU 개수는 RBLN Optimum Multi-NPUs Supported Models에서 확인할 수 있습니다.
+-- vllm_backend/ # Triton vLLM 백엔드 메인 디렉토리
| +-- samples/ # 애플리케이션 예제 디렉토리
| | +-- model_repository/ # 모델 저장소 (model_repositories)
| | | +-- vllm_model/ # Triton 으로 서빙할 개별 모델
| | | | +-- config.pbtxt # Triton 모델 설정 파일
| | | | +-- 1/ # 버전 디렉토리
| | | | | +-- model.json # vLLM 서빙을 위한 모델 설정
| | | | | +-- rbln-Llama-3-8B-Instruct/ # 컴파일된 모델 파일들(rbln files)
| | | | | | +-- decoder.rbln
| | | | | | +-- prefill.rbln
| | | | | | +-- config.json
| | | | | | +-- (기타 모델 파일들)
| | +-- (기타 예제 파일들)
| +-- (기타 백엔드 파일들)
Note
기존의 비전 모델에서 사용했던 Triton 서빙과는 달리 NVIDIA Triton Server의 vLLM 백엔드는 별도의 model.py 파일이 필요하지 않습니다. vLLM 백엔드가 이미 내부적으로 필요한 모델 처리 로직(도커 컨테이너 내 backends/vllm/model.py)을 포함하고 있기 때문에, model.json 파일만으로 모델 설정이 가능합니다.
config.pbtxt의 경우에도 저장소의 config.pbtxt를 그대로 사용해도 무방합니다. config.pbtxt가 없는 경우, 다음을 참고해서 만들어주시면 됩니다. 위에서 언급했던 vLLM 백엔드 처리 로직에 따라 처리되므로, input, output 입력은 고정해두어야 합니다(4단계. gRPC 클라이언트 추론 요청 참고).
Triton 추론 서버의 vLLM 백엔드는 자체적으로 model.py를 정의하고 있기 때문에, Resnet50 튜토리얼에서 정의한 model.py와는 입출력 시그니쳐가 다릅니다. Resnet50 튜토리얼에서 정의한 model.py은 입력을 INPUT__0, 출력을 OUTPUT__0이라고 불렀지만 vLLM에서는 입력은 text_input, 출력은 text_output이라고 부릅니다. 따라서 클라이언트도 여기에 맞춰 수정해야 합니다. 자세한 내용은 vLLM model.py 페이지에서 확인할 수 있습니다.
다음 코드는 vLLM 백엔드를 호출하기 위한 클라이언트 코드입니다. 이 코드 역시 Resnet50 튜토리얼의 클라이언트와 마찬가지로 실행하기 위해서 tritonclient와 grpcio 패키지가 필요합니다.
Note
grpc 클라이언트를 적절하게 사용하려면 채팅 템플릿을 적용해야 합니다. 대화는 system, user, assistant 역할로 포맷되어야 하며, 또한 샘플링 파라미터sampling_params에 적절한 값이 다음과 같이 포함되어있어야 합니다.
importasyncioimportjsonimportnumpyasnpimporttritonclient.grpc.aioasgrpcclient# Define a simple chat message classclassChatMessage:def__init__(self,role,content):self.role=roleself.content=content# Apply a simple chat template to the messagesdefapply_chat_template(messages):lines=[]system_msg=ChatMessage(role="system",content="You are a helpful assistant.")formsgin[system_msg,*messages,ChatMessage(role="assistant",content="")]:lines.append(f"[{msg.role.capitalize()}]\n{msg.content}")return"\n".join(lines)asyncdeftry_request():url=("<host and port number of the triton inference server>"# e.g. "localhost:8001")client=grpcclient.InferenceServerClient(url=url,verbose=False)model_name="vllm_model"defcreate_request(messages,request_id):prompt=apply_chat_template(messages)print(f"prompt:\n{prompt}\n---")# print promptinput=grpcclient.InferInput("text_input",[1],"BYTES")prompt_data=np.array([prompt.encode("utf-8")])input.set_data_from_numpy(prompt_data)stream_setting=grpcclient.InferInput("stream",[1],"BOOL")stream_setting.set_data_from_numpy(np.array([True]))sampling_params={"temperature":0.0,"stop":["[User]","[System]","[Assistant]"],# add stop tokens}sampling_parameters=grpcclient.InferInput("sampling_parameters",[1],"BYTES")sampling_parameters.set_data_from_numpy(np.array([json.dumps(sampling_params).encode("utf-8")],dtype=object))inputs=[input,stream_setting,sampling_parameters]output=grpcclient.InferRequestedOutput("text_output")outputs=[output]return{"model_name":model_name,"inputs":inputs,"outputs":outputs,"request_id":request_id,}messages=[ChatMessage(role="user",content="What is the first letter of English alphabets?")]request_id="req-0"asyncdefrequests_gen():yieldcreate_request(messages,request_id)response_stream=client.stream_infer(requests_gen())prompt=apply_chat_template(messages)is_first_response=Trueasyncforresponseinresponse_stream:result,error=responseiferror:print("Error occurred!")else:output=result.as_numpy("text_output")foriinoutput:decoded=i.decode("utf-8")ifis_first_response:ifdecoded.startswith(prompt):decoded=decoded[len(prompt):]is_first_response=Falseprint(decoded,end="",flush=True)print("\n")# end of streamasyncio.run(try_request())