티스토리 뷰

반응형

https://docs.vllm.ai/en/latest/design/arch_overview.html

2024.11.24 에 작성한 글입니다. 업데이트를 반영하지 않았습니다. 

 

Table of Contents


Entrypoints

vLLM은 시스템과 상호작용하기 위한 여러 entrypoint를 제공합니다. 아래 다이어그램은 이들 간의 관계를 보여줍니다.

LLM Class

https://docs.vllm.ai/en/latest/dev/offline_inference/llm.html

LLM 클래스는 별도의 model inference server를 사용하지 않고 모델과 상호작용할 수 있는 오프라인 inference를 수행하기 위한 기본 Python 인터페이스를 제공합니다.

여기서 보이는 LLMEngine.from_engine_args 가 엔진을 구동하는 함수입니다.

More API details can be found in the Offline Inference section of the API docs.

The code for the LLM class can be found in vllm/entrypoints/llm.py.

from vllm import LLM, SamplingParams

# Define a list of input params
prompts = [
  "Hello, my name is",
  "The capital of France is",
  "The largest ocean is"
]

# Define sampling parameters
sampling_params = SamplingParams(temperature=0.8,top_p=0.95)
# https://docs.vllm.ai/en/latest/dev/sampling_params.html

# Initialize the LLM engine with the OPT-125M model
llm = LLM(model = "Qwen/Qwen2.5-1.5B-Instruct")

# Generate outputs for the input prompts
outputs = llm.generate(prompts, sampling_params)

# Print the genertate outputs
for output in outputs:
     prompt = output.prompt
     generated_text = output.outputs[0].text
      print(f"Prompt: {prompt!r}, Generated text: {generated_text!r}")

OpenAI-compatible API server

vLLM의 두 번째 주요 인터페이스는 OpenAI-compatible API server를 통한 것입니다. 이 서버는 vllm serve 명령어를 사용하여 시작할 수 있습니다.

vllm serve <model>

The code for the vllm CLI can be found in vllm/scripts.py.

때로는 vllm CLI 명령어를 사용하지 않고 API server entrypoint를 직접 사용하는 경우를 볼 수 있습니다. 예를 들어:

python -m vllm.entrypoints.openai.api_server --model <model>

That code can be found in vllm/entrypoints/openai/api_server.py.

More details on the API server can be found in the OpenAI Compatible Server document.

참고 : OpenAI Compatible Server

API Reference

We currently support the following OpenAI APIs:

  • Completions API
    • 프롬프트를 제공하면 모델은 하나 이상의 예측된 completions와 각 위치에서 대체 토큰의 확률을 반환합니다. 대부분의 개발자는 최신의 최적화된 모델을 활용하기 위해 Chat Completions API를 사용하는 것이 좋습니다.
  • Chat Completions API
    • OpenAI는 ChatGPT를 사용하는 것처럼 프롬프트에서 텍스트를 생성하기 위한 간단한 API를 제공합니다. 이러한 모델은 멀티미디어 입력과 자연어 지시를 이해할 수 있도록 방대한 데이터를 기반으로 학습되었습니다. 프롬프트를 통해 모델은 코드, 수학 방정식, 구조화된 JSON 데이터, 또는 사람처럼 자연스러운 문장과 같은 거의 모든 종류의 텍스트 응답을 생성할 수 있습니다.
  • Embeddings API
    • inputs 대신, messages의 리스트를 전달할 수 있습니다(Chat Completions API와 동일한 스키마). 이는 모델의 chat template에 따라 단일 프롬프트로 처리됩니다.

LLM engine

LLMEngine 클래스와 AsyncLLMEngine 클래스는 vLLM 시스템의 핵심 요소로, 모델 inference와 비동기 요청 처리를 담당합니다.

 

 

LLMEngine

LLMEngine 클래스는 vLLM 엔진의 핵심 구성 요소로, 클라이언트로부터 요청을 수신하고 모델로부터 출력을 생성하는 역할을 담당합니다. LLMEngine은 입력 처리, 모델 실행(여러 호스트와/또는 GPU에 분산 가능), 스케줄링, 출력 처리를 포함합니다.

주요 구성 요소

  1. Input Processing: 지정된 tokenizer를 사용하여 입력 텍스트를 토크나이즈 처리합니다.
  2. Scheduling: 각 단계에서 처리할 요청을 선택합니다.
  3. Model Execution: 언어 모델의 실행을 관리하며, 여러 GPU에 걸친 분산 실행도 포함됩니다.
  4. Output Processing: 언어 모델에서 생성된 토큰 ID를 사람이 읽을 수 있는 텍스트로 디코딩합니다.

LLMEngine의 코드는 vllm/engine/llm_engine.py에서 확인할 수 있습니다.

AsyncLLMEngine

AsyncLLMEngine 클래스는 LLMEngine 클래스의 비동기 래퍼로, asyncio를 사용하여 백그라운드에서 요청을 지속적으로 처리하는 루프를 생성합니다. AsyncLLMEngine은 다중 동시 요청을 처리하고 클라이언트로 출력 결과를 스트리밍할 수 있도록 설계되어, 온라인 서빙 환경에서 사용됩니다.

주요 특징

  1. 비동기 처리: asyncio를 활용하여 요청을 병렬적으로 처리합니다.
  2. 온라인 서빙: 다수의 동시 요청을 효과적으로 처리하며, 결과를 스트리밍 방식으로 클라이언트에게 제공합니다.

API 서버 활용

  • OpenAI 호환 API 서버: AsyncLLMEngine을 사용하여 구현되었습니다.
  • 데모 API 서버: 단순한 예제로, vllm/entrypoints/api_server.py에서 확인할 수 있습니다.

코드 위치

AsyncLLMEngine의 코드는 vllm/engine/async_llm_engine.py에서 확인할 수 있습니다.

The OpenAI-compatible API server uses the AsyncLLMEngine. There is also a demo API server that serves as a simpler example in vllm/entrypoints/api_server.py.

The code for AsyncLLMEngine can be found in vllm/engine/async_llm_engine.py.

Worker

Worker는 모델 inference를 실행하는 프로세스를 의미합니다. vLLM은 GPU와 같은 가속 장치를 제어하기 위해 하나의 프로세스가 하나의 장치를 관리하는 일반적인 방식을 따릅니다.

예를 들어, tensor parallelism이 2이고 pipeline parallelism이 2인 경우, 총 4개의 worker가 생성됩니다.


(주석) worker에 cache_engine 이 달려있다. 

self.cache_engine: List[CacheEngine]
# Initialize gpu_cache as embedding models don't initialize kv_caches
self.gpu_cache: Optional[List[List[torch.Tensor]]] = None
self._seq_group_metadata_cache: Dict[str, SequenceGroupMetadata] = {}

Worker의 식별

  • rank: 글로벌 오케스트레이션(전체 작업 조율)에 사용됩니다.
  • local_rank: 가속 장치 할당과 파일 시스템, 공유 메모리 같은 로컬 리소스 접근에 주로 사용됩니다.

Worker 구성 예시

  • Tensor parallelism과 Pipeline parallelism을 조합하여 모델을 병렬로 처리함으로써 성능을 극대화할 수 있습니다.
  • rank와 local_rank를 통해 작업 및 리소스를 효율적으로 분배합니다.

Model Runner

worker는 하나의 model runner 객체를 가지며, 이는 모델을 로드하고 실행하는 역할을 담당합니다. 모델 실행의 많은 로직이 여기에 포함되어 있으며, 입력 텐서를 준비하고 CUDA 그래프를 캡처하는 작업을 수행합니다.

 

Model

model runner 객체는 하나의 model 객체를 가지며, 이는 실제 torch.nn.Module 인스턴스입니다. 다양한 설정이 최종적으로 얻는 클래스에 어떻게 영향을 미치는지에 대한 내용은 Integration with HuggingFace 를 참조하세요.

Class Hierarchy

The following figure shows the class hierarchy of vLLM:

VllmConfig

이 클래스 계층 구조 뒤에는 몇 가지 중요한 설계 선택이 있습니다:

  1. Extensibility: 계층 구조의 모든 클래스는 필요한 모든 정보를 포함하는 configuration 객체를 수용합니다. VllmConfig 클래스는 전달되는 주요 configuration 객체입니다. 클래스 계층은 꽤 깊으며, 각 클래스는 관심 있는 configuration을 읽어야 합니다. 모든 configuration을 하나의 객체에 캡슐화하면, configuration 객체를 쉽게 전달하고 필요한 configuration에 접근할 수 있습니다. 예를 들어, 새로운 feature를 추가하고 싶다고 가정해봅시다(LLM inference 분야가 빠르게 발전하고 있으므로 흔히 발생하는 경우입니다). 이 feature가 model runner에만 영향을 미친다면, VllmConfig 클래스에 새로운 configuration 옵션을 추가해야 합니다. 전체 config 객체를 전달하기 때문에, VllmConfig 클래스에만 옵션을 추가하면 model runner가 이를 직접 접근할 수 있습니다. 새로운 configuration 옵션을 전달하기 위해 engine, worker, model 클래스의 생성자를 변경할 필요가 없습니다.
  2. Uniformity: model runner는 모델을 생성하고 초기화하기 위한 통합된 interface가 필요합니다. vLLM은 50개 이상의 인기 있는 오픈소스 모델 유형을 지원합니다. 각 모델은 고유한 초기화 로직을 가지고 있습니다. 생성자 시그니처가 모델마다 다르면, model runner는 복잡하고 오류가 발생하기 쉬운 inspection 로직 없이 생성자를 적절히 호출하는 방법을 알 수 없습니다. model 클래스의 생성자를 통일되게 만듦으로써, model runner는 특정 모델 유형을 알지 않고도 쉽게 모델을 생성하고 초기화할 수 있습니다. 이는 모델을 구성하는 데에도 유용합니다. Vision-language 모델은 종종 vision 모델과 language 모델로 구성됩니다. 생성자를 통일하면 vision 모델과 language 모델을 쉽게 생성하고 이를 vision-language 모델로 구성할 수 있습니다.
  3. Sharding and Quantization at Initialization: 일부 기능은 모델의 가중치를 변경해야 합니다. 예를 들어, tensor parallelism은 모델 가중치를 shard 해야 하며, quantization은 모델 가중치를 양자화해야 합니다. 이를 구현하는 방법은 두 가지가 있습니다. 첫 번째 방법은 모델이 초기화된 후 가중치를 변경하는 것이고, 두 번째 방법은 모델 초기화 중에 가중치를 변경하는 것입니다. vLLM은 후자를 선택합니다.

첫 번째 접근 방식은 대규모 모델에 대해 확장 가능하지 않습니다. 예를 들어, 405B 모델(약 810GB 가중치)을 16개의 H100 80GB GPU에서 실행하려고 한다고 가정해 봅시다. 이상적으로는 각 GPU가 50GB의 가중치만 로드해야 합니다. 모델이 초기화된 후 가중치를 변경한다면, 각 GPU는 전체 810GB의 가중치를 로드한 뒤 shard를 생성해야 하므로 막대한 메모리 오버헤드가 발생합니다. 반면, 모델 초기화 중에 가중치를 shard한다면, 각 레이어는 필요한 가중치의 shard만 생성하므로 메모리 오버헤드가 훨씬 작아집니다.

같은 아이디어는 quantization에도 적용됩니다. 또한 모델 생성자에 추가적인 prefix 인자를 추가하여, 모델이 prefix에 따라 다르게 초기화될 수 있도록 합니다. 이는 모델의 서로 다른 부분을 다르게 양자화하는 non-uniform quantization에 유용합니다. prefix는 일반적으로 최상위 모델의 경우 빈 문자열이고, 서브모델의 경우 "vision"이나 "language" 같은 문자열이 됩니다. 일반적으로 이는 체크포인트 파일의 module의 state dict 이름과 일치합니다.

이 설계의 한 가지 단점은 vLLM의 개별 구성 요소에 대한 단위 테스트를 작성하기 어렵다는 점입니다. 모든 구성 요소가 완전한 config 객체로 초기화되어야 하기 때문입니다. 이 문제를 해결하기 위해, 모든 필드가 None으로 설정된 기본 config 객체를 생성하는 default initialization function을 제공합니다. 테스트하려는 구성 요소가 config 객체의 일부 필드에만 관심이 있다면, 기본 config 객체를 생성하고 필요한 필드를 설정하면 됩니다. 이를 통해 구성 요소를 독립적으로 테스트할 수 있습니다. 하지만 vLLM의 많은 테스트는 시스템 전체를 테스트하는 end-to-end 테스트이므로, 이는 큰 문제가 되지 않습니다.

완전한 config 객체인 VllmConfig는 모든 vLLM 클래스 간에 공유되는 엔진 수준의 글로벌 상태로 간주될 수 있습니다.

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함