vLLM

vLLM

vLLM 是一个快速、易用的库,用于 LLM 推理和提供服务。

vLLM 非常快速

  • 最顶尖的推理服务性能
  • 通过 PagedAttention 高效管理注意力键值对内存
  • 持续对传入的请求进行批处理
  • 通过 CUDA/HIP 图实现快速模型执行
  • 量化方法:GPTQ、AWQ、INT4、INT8 和 FP8
  • 优化 CUDA 内核,并集成了 FlashAttention 和 FlashInfer 功能。

vLLM 灵活方便,易于使用

  • 与热门 HuggingFace 模型实现无缝集成
  • 高吞吐量服务,支持并行采样、集束搜索等多种解码算法
  • 支持张量并行和流水线并行进行分布式推理
  • Streaming outputs 流式输出结果
  • OpenAI 兼容的 API 服务器
  • 支持 NVIDIA GPU、AMD CPU 及 GPU、Intel CPU、Gaudi®加速器与 GPU、IBM Power CPU、TPU,以及 AWS Trainium 和 Inferentia 加速器。
  • Prefix caching support 支持前缀缓存功能
  • 支持多种 LoRA 模型

安装

GPU

GPU:计算能力需达到 7.0 或更高(例如 V100、T4、RTX20xx、A100、L4、H100 等)

创建环境

1
2
3
# (Recommended) Create a new uv environment. Use `--seed` to install `pip` and `setuptools` in the environment.
uv venv --python 3.12 --seed
source .venv/bin/activate

Pre-built wheels

1
2
uv pip install vllm # If you are using uv.
# 0.8.5.post1

他会不匹配并且不是对应cuda版本的pytorch

重新安装pytorch,本机wsl2使用11.8

1
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

测试

vLLM 可以部署为服务器,该服务器实现了 OpenAI API 协议。这使得 vLLM 可以作为使用 OpenAI API 的应用程序的即插即用替代品。默认情况下,它会在 http://localhost:8000 启动服务器。您可以通过 --host--port 参数指定服务器的地址。目前,服务器每次只托管一个模型,并提供列出模型、创建聊天完成和创建完成等端点功能。

1
uv run serve facebook/opt-125m
1
2
3
4
5
6
7
8
9
10
11
12
INFO 05-22 08:27:11 [launcher.py:36] Route: /pooling, Methods: POST
INFO 05-22 08:27:11 [launcher.py:36] Route: /score, Methods: POST
INFO 05-22 08:27:11 [launcher.py:36] Route: /v1/score, Methods: POST
INFO 05-22 08:27:11 [launcher.py:36] Route: /v1/audio/transcriptions, Methods: POST
INFO 05-22 08:27:11 [launcher.py:36] Route: /rerank, Methods: POST
INFO 05-22 08:27:11 [launcher.py:36] Route: /v1/rerank, Methods: POST
INFO 05-22 08:27:11 [launcher.py:36] Route: /v2/rerank, Methods: POST
INFO 05-22 08:27:11 [launcher.py:36] Route: /invocations, Methods: POST
INFO 05-22 08:27:11 [launcher.py:36] Route: /metrics, Methods: GET
INFO: Started server process [882]
INFO: Waiting for application startup.
INFO: Application startup complete.
1
curl http://localhost:8000/v1/models
1
{"object":"list","data":[{"id":"facebook/opt-125m","object":"model","created":1747873971,"owned_by":"vllm","root":"facebook/opt-125m","parent":null,"max_model_len":2048,"permission":[{"id":"modelperm-17800b4c404f41299d0260de78eef5a9","object":"model_permission","created":1747873971,"allow_create_engine":false,"allow_sampling":true,"allow_logprobs":true,"allow_search_indices":false,"allow_view":true,"allow_fine_tuning":false,"organization":"*","group":null,"is_blocking":false}
1
2
3
4
5
6
7
8
curl http://localhost:8000/v1/completions \
-H "Content-Type: application/json" \
-d '{
"model": "facebook/opt-125m",
"prompt": "San Francisco is a",
"max_tokens": 7,
"temperature": 0
}'
1
{"id":"cmpl-1ef53a77d33e4ad489f17638427eb4e1","object":"text_completion","created":1747874128,"model":"facebook/opt-125m","choices":[{"index":0,"text":" great place to live.  I","logprobs":null,"finish_reason":"length","stop_reason":null,"prompt_logprobs":null}],"usage":{"prompt_tokens":5,"total_tokens":12,"completion_tokens":7,"prompt_tokens_details":null}}

或者,你也可以使用 openai Python 包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from openai import OpenAI
# Set OpenAI's API key and API base to use vLLM's API server.
openai_api_key = "EMPTY"
openai_api_base = "http://localhost:8000/v1"

client = OpenAI(
api_key=openai_api_key,
base_url=openai_api_base,
)

chat_response = client.chat.completions.create(
model="Qwen/Qwen2.5-1.5B-Instruct",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Tell me a joke."},
]
)
print("Chat response:", chat_response)

Attention Backends

目前,vLLM 支持多种后端,以在不同平台和加速器架构上实现高效的注意力计算。它会根据您的系统和模型规格,自动选择最匹配的高性能后端。

如果需要,您也可以通过将环境变量 VLLM_ATTENTION_BACKEND 设置为以下选项之一: FLASH_ATTNFLASHINFERXFORMERS 来手动选择您所需的后端。

Offline Inference

你可以在自己的代码里,针对一系列提示来运行 vLLM。

1
2
3
from vllm import LLM

llm = LLM(model="facebook/opt-125m")

1.Tensor Parallelism (TP)

以下代码将模型分布在 2 个 GPU 上。

1
2
3
4
from vllm import LLM

llm = LLM(model="ibm-granite/granite-3.1-8b-instruct",
tensor_parallel_size=2)

2.Quantization

  • AutoAWQ

    • pip install autoawq

      要使用 vLLM 运行 AWQ 模型,您可以使用 TheBloke/Llama-2-7b-Chat-AWQ 模型,并使用以下命令:

      python examples/offline_inference/llm_engine_example.py --model TheBloke/Llama-2-7b-Chat-AWQ --quantization awq

  • INT8 W8A8

    • 要使用 vLLM 的 INT8 量化,您需要安装 llm-compressor 库:

      pip install llmcompressor

      此外,请安装 vllmlm-evaluation-harness 以便进行评估:

      pip install vllm lm-eval==0.4.4

      量化过程主要包含四个步骤:

      1. Loading the model 加载模型
      2. Preparing calibration data
        准备校准数据
      3. Applying quantization 应用量化
      4. Evaluating accuracy in vLLM
        评估 vLLM 的精确度
      1. Loading the Model

      使用标准的 transformers AutoModel 类加载模型和分词器:

      1
      2
      3
      4
      5
      6
      7
      from transformers import AutoTokenizer, AutoModelForCausalLM

      MODEL_ID = "meta-llama/Meta-Llama-3-8B-Instruct"
      model = AutoModelForCausalLM.from_pretrained(
      MODEL_ID, device_map="auto", torch_dtype="auto",
      )
      tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
      2. Preparing Calibration Data

      当将激活值量化为 INT8 时,需要样本数据来估计激活值的缩放比例。最佳做法是使用与部署数据高度匹配的校准数据。对于通用指令微调模型,可以使用像 ultrachat 这样的数据集。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      from datasets import load_dataset

      NUM_CALIBRATION_SAMPLES = 512
      MAX_SEQUENCE_LENGTH = 2048

      # Load and preprocess the dataset
      ds = load_dataset("HuggingFaceH4/ultrachat_200k", split="train_sft")
      ds = ds.shuffle(seed=42).select(range(NUM_CALIBRATION_SAMPLES))

      def preprocess(example):
      return {"text": tokenizer.apply_chat_template(example["messages"], tokenize=False)}
      ds = ds.map(preprocess)

      def tokenize(sample):
      return tokenizer(sample["text"], padding=False, max_length=MAX_SEQUENCE_LENGTH, truncation=True, add_special_tokens=False)
      ds = ds.map(tokenize, remove_columns=ds.column_names)
      3. Applying Quantization

      应用量化算法:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      from llmcompressor.transformers import oneshot
      from llmcompressor.modifiers.quantization import GPTQModifier
      from llmcompressor.modifiers.smoothquant import SmoothQuantModifier

      # Configure the quantization algorithms
      recipe = [
      SmoothQuantModifier(smoothing_strength=0.8),
      GPTQModifier(targets="Linear", scheme="W8A8", ignore=["lm_head"]),
      ]

      # Apply quantization
      oneshot(
      model=model,
      dataset=ds,
      recipe=recipe,
      max_seq_length=MAX_SEQUENCE_LENGTH,
      num_calibration_samples=NUM_CALIBRATION_SAMPLES,
      )

      # Save the compressed model: Meta-Llama-3-8B-Instruct-W8A8-Dynamic-Per-Token
      SAVE_DIR = MODEL_ID.split("/")[1] + "-W8A8-Dynamic-Per-Token"
      model.save_pretrained(SAVE_DIR, save_compressed=True)
      tokenizer.save_pretrained(SAVE_DIR)
      这个过程会创建一个 W8A8 模型,其权重和激活值均量化为 8 位整数。
      4. Evaluating Accuracy

      量化完成后,在 vLLM 中加载并运行模型:

      1
      2
      3
      from vllm import LLM
      model = LLM("./Meta-Llama-3-8B-Instruct-W8A8-Dynamic-Per-Token")
      要评估准确性,您可以使用 `lm_eval` :
      1
      2
      3
      4
      5
      6
      $ lm_eval --model vllm \
      --model_args pretrained="./Meta-Llama-3-8B-Instruct-W8A8-Dynamic-Per-Token",add_bos_token=true \
      --tasks gsm8k \
      --num_fewshot 5 \
      --limit 250 \
      --batch_size 'auto'

量化模型牺牲了精度,但占用的内存更少。

静态量化模型可以直接从 HF Hub 下载(一些流行的模型可在 Red Hat AI 获取),无需额外配置

通过 quantization 选项也支持动态量化

3.上下文长度和批量大小

1
2
3
4
5
from vllm import LLM

llm = LLM(model="adept/fuyu-8b",
max_model_len=2048,
max_num_seqs=2)

4.Reduce CUDA Graphs

默认情况下,我们通过 CUDA 图优化模型推理,这会占用 GPU 的额外内存。

你可以调整 compilation_config ,以在推理速度和内存使用之间取得更好的平衡:

1
2
3
4
5
6
7
8
9
10
11
from vllm import LLM
from vllm.config import CompilationConfig, CompilationLevel

llm = LLM(
model="meta-llama/Llama-3.1-8B-Instruct",
compilation_config=CompilationConfig(
level=CompilationLevel.PIECEWISE,
# By default, it goes up to max_num_seqs
cudagraph_capture_sizes=[1, 2, 4, 8, 16],
),
)