Flash Attention은 메모리 사용량을 줄이고 처리 속도를 높여, Llama-3.1-8B와 같은 모델이 긴 문맥을 효율적으로 처리할 수 있도록 도와줍니다. 이 튜토리얼에서는 RBLN vLLM과 NVIDIA Triton Inference Server를 사용하여 Flash Attention을 적용한 Llama3.1-8B 모델을 서빙하는 방법을 안내합니다.
이 페이지에서 소개된 모델 컴파일 및 triton vllm_backend 설정에 필요한 커맨드들은 RBLN Model Zoo에서 확인할 수 있습니다.
Note
이 튜토리얼은 사용자가 RBLN SDK 기반의 모델 컴파일 및 추론에 대해 잘 이해하고 있다는 가정하에 작성되었습니다. RBLN SDK 사용법에 익숙하지 않다면 RBLN Optimum 튜토리얼과 파이썬 API 페이지를 먼저 읽어 보는 것을 권합니다.
importosfromoptimum.rblnimportRBLNLlamaForCausalLMmodel_id="meta-llama/Llama-3.1-8B-Instruct"# Compile and exportmodel=RBLNLlamaForCausalLM.from_pretrained(model_id=model_id,export=True,# Export a PyTorch model to RBLN model with Optimumrbln_batch_size=1,# Batch sizerbln_max_seq_len=131_072,# Maximum sequence lengthrbln_num_devices=1,# Number of NPUsrbln_kvcache_partition_len=8_192,# Length of KV cache partitions for flash attention)# Save compiled results to diskmodel.save_pretrained(os.path.basename(model_id))
importosfromoptimum.rblnimportRBLNLlamaForCausalLMmodel_id="meta-llama/Llama-3.1-8B-Instruct"# Compile and exportmodel=RBLNLlamaForCausalLM.from_pretrained(model_id=model_id,export=True,# Export a PyTorch model to RBLN model with Optimumrbln_batch_size=1,# Batch sizerbln_max_seq_len=131_072,# Maximum sequence lengthrbln_num_devices=8,# Number of NPUsrbln_kvcache_partition_len=16_384,# Length of KV cache partitions for flash attention)# Save compiled results to diskmodel.save_pretrained(os.path.basename(model_id))
Note
배치 크기는 서빙 환경에 맞게 선택할 수 있습니다. 여기에서는 1로 설정합니다. Llama3.1-8B 모델을 사용하려면 8개의 RBLN NPU가 필요하며, 모델별 구동에 필요한 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 서빙위한 모델 설정
| | | | | +-- Llama-3.1-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())