概述
vLLM(Vectorized Large Language Model Serving System)是一个快速、易于使用的LLM推理和服务库。最初由加州大学伯克利分校的天空计算实验室(Sky Computing Lab)开发,如今已发展成为一个由学术界和工业界共同贡献的社区驱动项目。
功能:
- 最先进的服务吞吐量
- 使用PagedAttention高效管理注意力键和值的内存
- 连续批处理(Continuous Batching)传入请求
- 使用CUDA/HIP图实现快速执行模型
- 量化:GPTQ、AWQ、INT4、INT8、FP8、W8A8
- 优化CUDA内核,包括与FlashAttention和FlashInfer的集成
- 推测性解码
- 分块预填充
特性:
- 无缝集成HuggingFace模型
- 使用各种解码算法实现高吞吐量服务,包括并行采样、束搜索等
- 支持张量并行和流水线并行的分布式推理
- 流式输出
- OpenAI兼容API服务器
- 支持NVIDIA GPU、AMD CPU和GPU、Intel CPU和GPU、PowerPC CPU、TPU、AWS Neuron
- 前缀缓存支持
- 多LoRA支持
安装
根据不同的操作系统、卡(CPU或GPU)等,有好多不同的安装方式。
pip安装:
py
pip install vllm
源码编译:
bash
git clone https://github.com/vllm-project/vllm.git
cd vllm
# 限制并行编译任务数,避免系统负载过高,如WSL环境
export MAX_JOBS=6
export VLLM_TARGET_DEVICE=empty
pip install -e .
Docker安装:
py
docker run --runtime nvidia --gpus all vllm/vllm-openai:latest
vllm/vllm-openai是vLLM官方维护的镜像,此外还有很多其他组织或个人开发者发布的Docker镜像。支持其他容器引擎,如Podman:
py
podman run --runtime nvidia --gpus all vllm/vllm-openai:latest
离线批量推理:为输入提示列表生成文本。主要使用两个类:
- LLM:用于运行vLLM引擎离线推理的主类;
- SamplingParams:指定采样过程的参数。
vLLM默认从HuggingFace下载模型。可通过设置环境变量VLLM_USE_MODELSCOPE来使用ModelScope模型。加载通义千问模型时需要将trust_remote_code设置为true。
py
from vllm import LLM, SamplingParams
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)
llm = LLM(model="Qwen/Qwen3-0.6B")
llm = LLM(model="/home/models/Qwen3-4B", trust_remote_code=True)

Serve参数
https://docs.vllm.ai/en/stable/configuration/serve_args.html
https://mp.weixin.qq.com/s/cB5yTDLv4_yxh013DNnGLA
load_model
加载模型权重,整体流程:Executor -> Worker.load_model() -> ModelRunner.load_model(),
- 当MultiProcExecutor创建其下的若干Workers时,它会让每个worker都执行一次
self.worker.load_model(); self.worker.load_model(),实际执行的是self.model_runner.load_model()。worker上真正负责计算干活的,是ModelRunner;- ModelRunner的
load_model()方法
py
def load_model(self) -> None:
logger.info("Starting to load model %s...", self.model_config.model)
with DeviceMemoryProfiler() as m: # noqa: SIM117
time_before_load = time.perf_counter()
# 核心代码:
# 1. 权重下载(如有必要)
# 2. 模型架构初始化(每个ModelRunner上维护着自己的切片架构)
# 3. 权重注入模型架构(一般权重是完整的,在注入模型架构时,每个ModelRunner读取自己需要的部分切片)
# 最终返回结果,self.model = model.eval()
self.model = get_model(vllm_config=self.vllm_config)
if self.lora_config:
...
get_model方法:
py
def get_model(*, vllm_config: VllmConfig) -> nn.Module:
# 默认情况下(LoadFormat.Auto),loader = DefaultModelLoader
loader = get_model_loader(vllm_config.load_config)
# 返回model.eval(),此时已将权重加载完毕
return loader.load_model(vllm_config=vllm_config)
loader的类型由LoaderFormat决定,默认值为Auto,假设使用DefaultModelLoader。
DefaultModelLoader.load_model()方法:
py
def load_model(self, vllm_config: VllmConfig) -> nn.Module:
device_config = vllm_config.device_config
model_config = vllm_config.model_config
# 确定目标设备(如cuda:0)
target_device = torch.device(device_config.device)
# 设置默认的pytorch数据类型(如torch.float16)
with set_default_torch_dtype(model_config.dtype):
# 在目标设备上初始化模型权重
with target_device:
# 1. 构建模型架构(每张卡上维护自己的那部分模型架构切片,但还没有实际装载模型)
model = _initialize_model(vllm_config=vllm_config)
# 收集这张卡上所有需要加载的模型参数名称
weights_to_load = {name for name, _ in model.named_parameters()}
# 2. 实际加载权重
# (1) _get_all_weights:生成权重迭代器,形式如(权重名称 ,tensor)
# - 下载权重到本地
# - 生成权重迭代器,形式如(权重名称 ,tensor),迭代器的作用是,先不去加载权重,
# 到第二步 model.loads_weights时,遍历到哪一块权重,再具体去加载
# (2) model.load_weights:真正将模型权重注入本卡上所维护的模型切片中,在注入的过程中,
# 如有需要,会对送来的这部分权重进行切片
loaded_weights = model.load_weights(
self._get_all_weights(model_config, model))
# 记录加载权重的耗时
self.counter_after_loading_weights = time.perf_counter()
logger.info(
"Loading weights took %.2f seconds",
self.counter_after_loading_weights - self.counter_before_loading_weights)
# We only enable strict check for non-quantized models that have loaded weights tracking currently.
# 检查权重完整性
if model_config.quantization isNoneand loaded_weights isnotNone:
# 计算未成功加载的权重,如果存在没有成功加载的情况,直接报错
weights_not_loaded = weights_to_load - loaded_weights
if weights_not_loaded:
raise ValueError(
"Following weights were not initialized from "
f"checkpoint: {weights_not_loaded}")
# 后处理:量化权重处理或者特定层调整
_process_weights_after_loading(model, model_config, target_device)
return model.eval()
ModelRunner加载模型主要分成两步:初始化模型架构和实际加载权重。
初始化模型架构:目标是在各个ModelRunner(各张卡)上初始化模型架构分片,但不会涉及权重的实际装载。例如使用tp做分布式推理,那每个ModelRunner上只维护部分模型,所谓分片。
py
def _initialize_model(
vllm_config: VllmConfig,
*,
prefix: str = "",
) -> nn.Module:
"""Initialize a model with the given configurations."""
# (vllm类, hf类名)
# 例如 (<class 'vllm.model_executor.models.qwen.QWenLMHeadModel'>, "QWenLMHeadModel")
model_config = vllm_config.model_config
model_class, _ = get_model_architecture(model_config)
# 如果配置了量化(如AWQ/GPTQ),动态修改 model_class 的层定义(例如将 Linear 替换为 QuantLinear)
if vllm_config.quant_config isnotNone:
configure_quant_config(vllm_config.quant_config, model_class)
# 检查model_class是否支持新版vllm(要求接受vllm_config和prefix)
signatures = inspect.signature(model_class.__init__)
all_params = [param.name for param in signatures.parameters.values()]
# 新版模型初始化(推荐路径)
if"vllm_config"in all_params and"prefix"in all_params:
# new-style model class
with set_current_vllm_config(vllm_config, check_compile=True):
# 真正执行初始化模型实例(这时每张卡上维护的就是自己的那部分模型切片,只不过还没有实际装载模型)
return model_class(vllm_config=vllm_config, prefix=prefix)
# 旧版模型初始化
msg = ("vLLM model class should accept `vllm_config` and `prefix` as "
"input arguments. Possibly you have an old-style model class"
" registered from out of tree and it is used for new vLLM version. "
"Check https://docs.vllm.ai/en/latest/design/arch_overview.html "
"for the design and update the model class accordingly.")
...
get_model_architecture返回值包含2个元素:
- vllm类(model_class):本质是一个python class,形式如<class 'vllm.model_executor.models.qwen.QWenLMHeadModel'>,最终vllm将使用它来初始化ModelRunner上维护的模型架构
- hf类:本质是一个string,形式如"QWenLMHeadModel"
在vLLM中,通过registry.py文件,注册所有vLLM支持的模型,并构建key->value(其中key是HF类,value是vLLM类)的映射关系。如果想往vLLM中注册新模型,也需要操作此文件。
--tensor-parallel-size:必须等于GPU数量?
若遇Bfloat16 not supported错误,添加--dtype half。
实战
之前写过可参考: