vLLM理论及实战入门

概述

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;
  • ModelRunnerload_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

实战

之前写过可参考:

相关推荐
我有2只猫1 天前
vLLM Docker 本地部署小模型
docker·容器·vllm
ZJU_统一阿萨姆1 天前
【推理优化进阶】调度器的数学内核:排队论、SLO 与在线决策
开发语言·人工智能·语言模型·系统架构·vllm
Albart5751 天前
vLLM多卡部署终极踩坑:CUDA error worker进程异常退出 完整定位&生产根治方案
cuda·nccl·vllm·大模型部署·多卡推理·大模型踩坑
ZJU_统一阿萨姆2 天前
【推理优化进阶】性能实验科学:工作负载、统计显著性与尾延迟归因
开发语言·人工智能·语言模型·系统架构·vllm
JAI科研3 天前
Deepseek Agent Harness教程(二) | DeepSeek Harness 设计思路
人工智能·深度学习·算法·机器学习·自然语言处理·transformer·vllm
码云骑士3 天前
108-vLLM推理引擎-PagedAttention-连续批处理-吞吐提升10倍
python·vllm
ZJU_统一阿萨姆4 天前
【推理优化】KV Cache 量化:在不牺牲质量的前提下压榨更多内存
人工智能·语言模型·系统架构·vllm
Soonyang Zhang5 天前
vllm 分析(十一)——deepseek v4 kv cache layout
vllm
RobinDevNotes6 天前
K8s+Ray+vLLM打穿大模型全生命周期(有实践步骤)
人工智能·云原生·容器·kubernetes·生活·vllm