4. 模型加载与推理基础
4.1 核心原理:从模型权重到可服务状态
在深入使用 vLLM 之前,需要先理解其加载模型与执行推理的底层逻辑。vLLM 并非简单地将 Hugging Face Transformers 的模型包装一层 API,而是通过 PagedAttention 和 Continuous Batching 机制,对 KV Cache 进行块级管理,从而在推理过程中实现高吞吐与低显存浪费。
当调用 LLM 类或启动 兼容服务器 时,vLLM 会经历以下关键阶段:
- 权重加载:从本地磁盘或 Hugging Face Hub 读取模型权重,支持
safetensors格式,并按张量并行度(tensor_parallel_size)切分到多张 GPU。 - 图编译与优化:将模型的计算图转换为 CUDA Graph,减少内核启动开销,并预分配 KV Cache 的显存池。
- 请求调度器:维护一个等待队列和运行队列,采用连续批处理策略,每步迭代动态决定哪些序列继续生成、哪些序列被抢占或终止。
- 采样与输出:根据
temperature、top_p等参数从 logits 中采样,逐步生成 ,直到遇到EOS或达到max_tokens限制。
下图展示了 vLLM 从加载到生成的核心数据流与状态管理逻辑:
理解这张图的关键在于:vLLM 将显存管理从“静态预留”变为“动态分页”,因此多个请求可以共享同一块 GPU 显存,而不会因为某个序列的 KV Cache 增长导致其他序列被阻塞。
4.2 方法流程:离线批量推理与在线服务
vLLM 提供两种使用方式:离线批量推理(适合脚本、测试、数据处理)和 在线 兼容服务(适合生产环境 API 部署)。下面分别介绍其标准流程。
4.2.1 离线批量推理
使用 LLM 类是最直接的方式。核心代码如下:
from vllm import LLM, SamplingParams
# 1. 初始化模型(加载权重 + 构建 KV Cache)
llm = LLM(
model="Qwen/Qwen2.5-7B-Instruct",
tensor_parallel_size=1, # 单卡推理
gpu_memory_utilization=0.85, # 使用 85% 显存
max_model_len=8192, # 最大序列长度
trust_remote_code=True
)
# 2. 配置采样参数
sampling_params = SamplingParams(
temperature=0.7,
top_p=0.9,
max_tokens=512,
stop=["</s>", "<|im_end|>"]
)
# 3. 构造输入(支持多轮对话模板)
prompts = [
"请解释什么是PagedAttention?",
"写一段Python代码实现快速排序。"
]
# 4. 执行推理
outputs = llm.generate(prompts, sampling_params)
# 5. 解析结果
for output in outputs:
prompt = output.prompt
generated_text = output.outputs[0].text
print(f"Prompt: {prompt}\n生成: {generated_text}\n")
关键参数说明:
| 参数 | 作用 | 建议值 |
|---|---|---|
gpu_memory_utilization |
控制 KV Cache 可用的显存比例 | 0.80 ~ 0.90(留出激活值空间) |
max_model_len |
限制输入+输出的最大 数 | 根据模型训练长度设置 |
tensor_parallel_size |
多卡张量并行度 | 1(单卡)或 2/4(多卡) |
dtype |
权重精度 | auto 或 bfloat16 |
4.2.2 在线 兼容服务
启动服务只需一行命令:
python -m vllm.entrypoints..api_server \
--model Qwen/Qwen2.5-7B-Instruct \
--served-model-name qwen2.5-7b \
--port 8000 \
--gpu-memory-utilization 0.85 \
--max-model-len 8192
然后通过 HTTP 请求调用:
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen2.5-7b",
"messages": [
{"role": "user", "content": "用一句话介绍vLLM"}
],
"temperature": 0.3,
"max_tokens": 128
}'
返回的 JSON 中包含 choices[0].message.content 即为生成文本。在线模式的优势在于:支持流式输出(stream=True)、多用户并发、以及自动的请求排队与批处理。
4.3 案例实战:加载 Qwen2.5 并完成多轮对话
下面演示一个完整的案例:使用 vLLM 加载 Qwen2.5-7B-Instruct,并模拟多轮对话场景。该案例展示了如何结合 tokenizer.apply_chat_template 构造符合模型要求的输入格式。
from vllm import LLM, SamplingParams
from transformers import AutoTokenizer
# 加载 tokenizer(用于对话模板)
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-7B-Instruct")
# 初始化 vLLM
llm = LLM(model="Qwen/Qwen2.5-7B-Instruct", gpu_memory_utilization=0.85)
# 构造多轮对话
messages = [
{"role": "system", "content": "你是一个乐于助人的AI助手。"},
{"role": "user", "content": "请解释什么是KV Cache?"},
{"role": "assistant", "content": "KV Cache是Transformer推理时缓存Key和Value矩阵的技术,用于避免重复计算。"},
{"role": "user", "content": "那PagedAttention如何优化它?"}
]
# 应用对话模板
prompt = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)
# 生成回复
sampling_params = SamplingParams(temperature=0.7, top_p=0.9, max_tokens=256)
outputs = llm.generate([prompt], sampling_params)
print(outputs[0].outputs[0].text)
运行结果示例:
PagedAttention将KV Cache划分为固定大小的块(block),
通过块表(block table)动态映射逻辑位置到物理显存地址。
这样不同序列可以共享显存块,减少碎片化浪费,
同时支持高效的并行采样和抢占调度。
gpu_memory_utilization 至 0.7,而不是减小 max_model_len。因为 KV Cache 是动态分配的,降低利用率会减少缓存容量,但能保证模型正常加载。
4.4 常见误区与复盘
在实际使用中,以下问题出现频率最高,需要特别注意:
- 误区一:忽略对话模板。直接拼接用户输入而不使用
apply_chat_template,会导致模型生成质量下降,甚至产生格式混乱。vLLM 的LLM类不会自动应用对话模板,必须手动处理。 - 误区二:max_tokens 设置过大。当
max_tokens加上输入长度接近max_model_len时,vLLM 会直接报错或截断。建议预留 10% 的 余量。 - 误区三:多卡推理时忘记设置
tensor_parallel_size。如果使用多张 GPU 但未指定并行度,vLLM 默认只使用单卡,导致显存溢出。 - 误区四:混淆
gpu_memory_utilization与max_model_len。前者控制显存分配比例,后者控制序列长度上限。两者独立,但共同影响可并发处理的请求数。
CUDA out of memory,首先检查 nvidia-smi 确认显存占用。若 vLLM 已占用 85% 仍溢出,说明模型权重或激活值超出预期,可尝试:① 降低
gpu_memory_utilization 至 0.7;② 使用
--dtype float16 强制半精度;③ 启用
--enforce-eager 关闭 CUDA Graph(牺牲部分性能)。
复盘一个典型故障:某次部署中,用户反馈并发请求时吞吐骤降。排查发现 max_num_seqs(默认 256)设置过高,导致调度器频繁抢占。将 max_num_seqs 调整为 64 后,吞吐恢复稳定。这说明 vLLM 的性能调优需要结合具体负载特征,并非参数越大越好。