ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

vLLM实战指南:从安装到显存调优,轻松部署大模型推理服务

vLLM实战指南:从安装到显存调优,轻松部署大模型推理服务 模型部署最让人头疼的几件事其实翻来覆去就那么几个安装半天装不上、启动起来就闪退、显存莫名其妙爆掉。而围绕vLLM把这些点理顺基本就解决了一多半问题。这篇文章就是带着你从零开始把vLLM的安装、启动和显存调优完整过一遍中间穿插我自己踩过的坑和实测数据看完能直接上手。先说这内容适合谁手里有一块6GB以上显存的NVIDIA显卡、想把开源模型跑起来做接口服务或者本地验证的人以及已经能跑起来但经常OOM、想优化吞吐和显存占用的同学。vLLM是目前开源推理框架里速度最快的那一档核心卖点是PagedAttention和Continuous Batching这两个机制下面细讲。准备工作不复杂但顺序错了会很痛苦所以先别急着输命令。1. 动手之前先把环境盘清楚1.1 硬件与驱动门槛先看这三样vLLM和普通Python库不一样它底层直接吃CUDA对显卡、驱动、Python版本的要求都比较具体。我见过太多人上来就pip install vllm装倒是装成功了一启动就报错回头一看驱动是老的白折腾。开始之前先确认三件事显卡型号和显存大小。NVIDIA的卡最省心Ampere架构30系、A100、A10等和Ada架构40系、L20、L40等支持最完整。如果只有6GB显存能跑7B模型但序列长度和并发要做限制12GB以上体验会舒服很多。驱动版本。直接在终端跑nvidia-smi看右上角的CUDA Version这个不是说你装了CUDA工具包而是指驱动支持的最高CUDA版本。vLLM的预编译包通常要求驱动支持CUDA 12.1或12.4以上建议驱动不要太老。操作系统的环境。Linux是最顺的Windows用户建议直接走WSL2微软官方支持的子系统不用双系统也不用担心VMware显卡透传这些问题。我个人的建议是先跑一遍nvidia-smi和nvcc --version把结果截图或者写下来。很多问题排查到最后根源就是这里不对齐。注意nvidia-smi里的CUDA Version和nvcc -V显示的版本可能不一样前者是驱动带的后者是工具包安装的vLLM编译和运行主要看前者。1.2 Python虚拟环境这一步千万别省vLLM的依赖锁得比较死如果直接装进系统Python很容易把torch、transformers这些包搞得乱七八糟。你想象一下电脑上跑了三个项目一个要torch 2.1一个要torch 2.4全塞在一起今天装一个库把另一个项目搞挂是常有的事。所以用虚拟环境是铁律推荐用conda或者Python自带的venv也行。conda create -n vllm python3.10 -y conda activate vllmPython版本建议3.9到3.123.10和3.11最稳。我实测下来3.10踩坑最少碰到某些编译报错的概率低一些。装完后顺手把pip升级一下避免旧pip解析依赖时脑溢血。python -m pip install --upgrade pip这里还隐藏着一个容易忽略的点先别急着装vLLM先确认你的torch环境是不是干净的。有些机器上已经装了其他版本torchvLLM安装时检测到会提示冲突或直接降级这时候干脆把虚拟环境删了重建别省那一两分钟。新建虚拟环境里没有任何torch是最干净的状态。2. 安装vLLMpip、源码编译与Windows实战2.1 pip安装五分钟内跑通如果你的显卡型号不算太老系统是Linux或者WSL2pip install vllm是最快的一条路。它会自动拉取对应的预编译wheel把torch、transformers、xformers这一串依赖一并装好整个流程一般在五分钟左右取决于下载速度。pip install vllm装完验证一下这一步必须做python -c import vllm; print(vllm.__version__)如果正常打印出版本号比如0.7.2安装这关就过了。没有报错不代表一定能用所以紧接着看一眼GPU是否可见python -c import pynvml; pynvml.nvmlInit(); print(pynvml.nvmlDeviceGetName(pynvml.nvmlDeviceGetHandleByIndex(0)))这条能打印出显卡名称说明pynvml和驱动的通路没问题。很多装完导入不报错、一推理就报CUDA driver version is insufficient的人就是GPU这一层没确认。需要注意的点pip安装的版本是预编译好的CPU指令集、CUDA运行时和你本机的匹配程度有限。如果你的GPU太新而wheel里的CUDA版本偏旧可能会出现no kernel image available for execution on the device。遇到这种情况往下看编译安装或者升级pip源里的vLLM版本。还有一个常见坑是下载速度慢导致超时可以给pip配一个镜像源这属于常规操作pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple2.2 源码编译安装适合谁的折腾方案如果你需要最新特性、或者你的显卡架构比较新、又或者你想跑自定义模型结构源码编译是绕不开的。它的本质是在你机器上现场编译CUDA算子所以耗时久但对硬件的适配是最贴合的。git clone https://github.com/vllm-project/vllm.git cd vllm pip install -e .编译前务必备一份依赖清单手动先装好torch和对应的CUDA toolkit。我踩过的一个坑是直接跑pip install -e .编译到一半报缺少ninja虽然pip会自动装但某些环境下ninja版本很旧导致编译任务调度有问题。建议提前单独装pip install ninja cmake wheel编译过程视机器性能而定十几分钟到四十分钟都有可能。看到Successfully installed vllm就结束了。编译过程中我最常用的一条命令是nvidia-smi因为编译时GPU占用虽然不高但CPU和内存会吃很多最好中途别开太多任务。源码编译的隐藏风险是环境变量没配对。CUDA_HOME、PATH里的nvcc都要正确指向同一个CUDA版本。如果机器上装了好几个CUDA很容易出现编译用的nvcc是11.8运行时驱动匹配却是12.4的情况最后报错找半天也不知道哪里不对。所以编译前先执行which nvcc和echo $CUDA_HOME确认统一。2.3 Windows用户到底怎么办官方对Windows原生支持很有限这是vLLM的老大难问题。如果你想直接在Windows命令行里装大概率会碰壁因为很多算子需要Linux下的GCC和CUDA工具链配合编译Windows下不是不行但坑极多我不建议普通用户尝试。最省心的方案是装WSL2在WSL里用Ubuntu 22.04或者24.04然后按照Linux的流程安装即可。WSL2的显卡驱动是直接共享Windows驱动的所以只要Windows下的nvidia-smi能看到显卡WSL里也能看到不需要额外装驱动。注意在WSL里装CUDA工具包时选择WSL版本的安装包而不是原生的Linux版本。至于有些社区分享的Windows原生安装方法能不能用据我了解有一部分情况能跑通但问题是性能损耗和依赖冲突远超出预期而且出了问题很难在官方库找到答案。我自己的经验是如果有条件直接在Linux真机或者云GPU实例上跑如果本地是Windows就老老实实用WSL2时间和心情都能省不少。2.4 安装完成后的自检清单装完不是直接跑模型先按下面几步快速自检可以规避掉后面80%的启动问题vllm --help能不能正常输出参数列表。python -c import vllm; print(vllm.__version__)版本是否正常。执行一次极小模型的推理测试比如从局域网你能访问到的模型仓库下载一个几百MB的小模型或者用本地已有的模型文件跑通一次完整生成。观察日志里有没有CUDA相关警告比如torch not compiled with CUDA enabled。很多人在第一步自检就翻车原因多半是torch和CUDA版本不匹配。检查方法是在Python里执行import torch; print(torch.cuda.is_available())如果返回False说明torch装成了CPU版需要重新安装对应的CUDA版torch。这个不用想太复杂直接按vLLM要求的torch版本来就好。3. 启动推理从命令行到Python调用3.1 OpenAI兼容接口一条命令把服务跑起来vLLM最方便的地方是它自带OpenAI兼容的HTTP服务。这意味着你不需要写任何Web框架代码也不用理解FastAPI内部逻辑一条命令就能把模型变成一个标准的API服务然后像调用OpenAI接口一样调用它。以社区常见的Qwen/Qwen2.5-7B-Instruct为例vllm serve Qwen/Qwen2.5-7B-Instruct \ --port 8000 \ --gpu-memory-utilization 0.85 \ --max-model-len 8192 \ --dtype bfloat16启动后看到类似INFO: Application startup complete和Uvicorn running on http://0.0.0.0:8000就说明服务起来了。第一次启动会比较慢因为要下载模型文件、加载权重、构建KV Cache。当你看到Finished loading the model这一行日志恭喜模型已经加载到显存里了。这时候用curl验证一下接口是否真的可用curl http://127.0.0.1:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: Qwen/Qwen2.5-7B-Instruct, messages: [{role: user, content: 你好简要介绍一下你自己}], max_tokens: 256 }返回里有choices和content字段说明接口完全正常。这一套流程跑通后你想部署DeepSeek的蒸馏版模型比如社区里很火的DeepSeek-R1-Distill-Qwen-7B就是把模型路径换一下的事其余参数和不一致的地方基本不用动。3.2 启动参数逐句拆解你会发现启动命令里那串参数每个都在起作用我挑几个最有影响的展开说。--gpu-memory-utilization是最关键的显存控制参数。它的含义是“vLLM最多占用多大比例的显存”默认值是0.9。比如一张16GB的显卡0.85意味着vLLM预留约13.6GB可用除了放模型权重还要给KV Cache留空间。调太低了浪费显存调太高了容易和其他的显存占用冲突导致启动时直接OOM。这个参数我们下一章细讲。--max-model-len控制的是模型能处理的最大上下文长度。它的大小直接决定KV Cache的分配上限。上下文越长同样一块显存能同时处理的请求数就越少。如果不知道模型训练时的上下文窗口可以先设成4096试跑确认无误再往上加。--dtype指定权重精度常用的是float16和bfloat16。30系和40系显卡对bfloat16支持都很好但不同型号表现略有差异如果遇到数值异常换回float16试试。老一点的显卡比如20系bfloat16支持得不好建议直接用float16。还有个参数--tensor-parallel-size多卡用户会用到。它把模型切分到多张显卡上并行推理。两张卡就设2四张卡就设4但前提是卡之间的NVLink或者PCIe带宽够否则通信开销可能抵消并行收益甚至更慢。3.3 Python调用流式输出与原生接口服务跑起来之后实际业务代码里一般用OpenAI SDK来对接。先装一下pip install openai然后写一个流式对话脚本from openai import OpenAI client OpenAI( base_urlhttp://127.0.0.1:8000/v1, api_keyEMPTY, ) response client.chat.completions.create( modelQwen/Qwen2.5-7B-Instruct, messages[{role: user, content: 给我讲一个数据库索引的小故事}], streamTrue, ) for chunk in response: delta chunk.choices[0].delta.content if delta: print(delta, end, flushTrue)为什么推荐流式因为大模型生成是逐token的一段几百字的回答在非流式模式下要等全部生成完才返回用户体验就是“卡住十几秒没反应”流式则能像打字机一样一个字一个字蹦出来体感好得多。如果你不想走HTTP也可以直接在Python进程里调用vLLM的原生接口from vllm import LLM, SamplingParams llm LLM( modelQwen/Qwen2.5-7B-Instruct, gpu_memory_utilization0.85, max_model_len8192, ) params SamplingParams(temperature0.7, max_tokens512) outputs llm.generate([请用一句话解释什么是分页], params) print(outputs[0].outputs[0].text)原生接口适合做离线批量推理或者嵌入到自己的pipeline里没有网络开销少了HTTP解析的损耗。要注意的是原生接口和HTTP服务是两种使用方式不能同时混着开除非你在代码里用多进程分别初始化。3.4 启动过程中的典型卡点排查启动命令敲下去最常见的就是模型下载卡住。vLLM会从模型仓库拉取权重如果网络环境不好会在下载阶段等很久。这一类的处理办法是提前设置模型下载的镜像端点例如export HF_ENDPOINThttps://hf-mirror.com设完再重新执行启动命令它会走国内可达的镜像地址下载速度会好很多。如果设完依然是蜗牛速度建议先把模型下载到本地然后用本地路径启动例如vllm serve /data/models/qwen2.5-7b-instruct --port 8000本地路径启动还能规避一个坑部分模型仓库里的权重文件名不标准导致自动下载时解析失败本地路径则可以直接加载。另一个常见卡点是端口被占。默认8000端口很容易被别的服务占用启动报错信息里通常直接写了[Errno 98] Address already in use这时候换个端口就行。也有一种情况是启动后日志一直停在某一步不往下走大概率是权重加载卡在磁盘IO特别是从机械硬盘加载上百GB模型时不用焦虑再等等。4. 显存调优榨干每一兆显存4.1 先搞懂显存到底花在哪了说到调优首先得知道显存被谁吃了。vLLM推理时显存消耗主要来自四个部分模型权重7B模型用FP16存就是约14GBINT4量化后能压到约4GB。KV Cache每处理一个token每个注意力头都要缓存Key和Value序列越长、并发越高占用越大。激活值和中间buffer前向计算过程中的临时张量。CUDA context和框架自身的开销torch/CUDA初始化就会固定吃掉一部分显存这块无法避免。PagedAttention是vLLM的核心创新它把KV Cache分成固定大小的块block类似操作系统里的内存分页。这样一来显存碎片化问题少了很多而且不需要预先给整条序列分配连续空间只动态给实际用到的部分分配即可。这就是为什么vLLM能做到高并发、高吞吐能同时容纳很多条长序列。理解了这个显存调优的本质就很清晰了在权重和KV Cache之间找到平衡。权重是刚性需求模型换不了就得一直占着KV Cache是弹性需求决定了你能同时跑多少请求、多大并发、多长上下文。4.2 核心调优参数从原理到实战最核心的参数是--gpu-memory-utilization。默认0.9也就是90%的显存都会交给vLLM调度。如果你只跑一个模型、没有其他程序占显存0.9是合理值如果想留一点显存跑其他服务就降到0.7左右。我用一张24GB的卡实测7B模型FP16权重约占14GB剩下约8GB归KV Cache跑8192上下文、并发32是很轻松的。vllm serve Qwen/Qwen2.5-7B-Instruct \ --gpu-memory-utilization 0.9 \ --max-model-len 8192 \ --max-num-seqs 64--max-num-seqs控制的是同时进入批处理的请求数上限。调高它吞吐量可以上一个台阶但每个请求能分到的KV Cache会变少极端情况下长序列请求会被拒掉调低它单个请求的响应更稳定延迟更低。一个粗略的经验序列长度中等2K-4K的场景32到64是甜点值序列很长16K以上的场景建议压到16左右。--max-model-len也要跟着场景走。如果你的业务大多数是短文本问答没必要把上限设到32K因为vLLM会按上限预留KV Cache即使实际不用也相当于占了显存。反过来短文本场景把max-model-len设短一点等于变向腾出了更多并发空间。量化是更激进的显存节省手段。AWQ和GPTQ可以把7B模型压到4GB附近FP8则介于FP16和4bit之间。启动时加--quantization awq并给到对应的权重仓库即可。要注意的是量化会带来一定的精度损失且不同模型的损失程度不一样我自己的经验是7B以下模型量化后能明显感到回答质量下降大一点的模型比如13B或32B量化后损失相对小。4.3 实战案例小显存硬跑大模型以单张24GB显卡部署32B模型为例FP16的32B体重直接吃满64GB显然塞不下。这时候两个思路一是量化二是极端限制KV Cache。用AWQ 4bit量化后的32B模型权重约17GB24GB显存勉强装下剩下约7GB给KV Cache但并发和上下文长度都会受限。我的调法是vllm serve 模型路径 \ --quantization awq \ --gpu-memory-utilization 0.92 \ --max-model-len 4096 \ --max-num-seqs 8 \ --enforce-eager--enforce-eager是我加进去的一个开关它让vLLM跳过CUDA graph的优化虽然牺牲一点性能但能省下几百MB到1GB的显存在小显存场景非常值得。反过来显存充足的时候不推荐加这个参数因为CUDA graph带来的加速收益很明显。我实际跑下来的数据做个对比同一张24GB卡同样的模型和请求负载不调优启动直接爆显存闪退按上面参数调完稳定运行单请求平均延迟增加并不夸张这已经很符合本地开发或小规模服务的预期了。4.4 用日志判断调优是否到位调优不能靠猜vLLM的启动日志里其实把所有信息都写清楚了。重点看这几行GPU KV cache size分配了多少显存给KV Cache。Maximum concurrency当前配置下能支持的最大并发请求数。Total number of tokensKV Cache能容纳的总token数。举个例子一行日志显示GPU KV cache size: 6.42 GB、Maximum concurrency: 128说明当前配置大约能容纳128个并发的短请求。如果你嫌并发太低可以回调--gpu-memory-utilization或者调短--max-model-len如果显存还有富余就完全没有必要降参数。这套反馈循环比在网上找现成参数组合要靠谱得多。同样地如果日志里出现RuntimeError: CUDA out of memory并不是代码写错了纯粹是分配策略太激进。优先检查三件事gpu_memory_utilization是不是超过了实际空闲显存的比例、max_model_len是不是设得太大、max-num-seqs是不是太高。从上到下逐个调低基本都能救回来。5. 常见问题速查与排坑手记5.1 启动失败与进程闪退这类问题占排障里的大头我把高频症状和对应解法整理成表方便直接对照症状原因解法CUDA driver version is insufficient驱动太旧跟不上vLLM依赖的CUDA版本升级NVIDIA驱动或安装对旧驱动兼容的vLLM版本no kernel image available for execution显卡架构太新预编译wheel里没有对应SM架构改用源码编译安装或者选择支持新架构的新版本RuntimeError: CUDA out of memory显存分配超限降低gpu-memory-utilization、max-model-len或max-num-seqsAddress already in use端口被占用换--port或杀掉占用进程启动后无日志进程直接退出显卡驱动异常或显存不足执行nvidia-smi确认GPU状态关闭无关占显存进程AssertionError: Torch not compiled with CUDA enabledtorch装成CPU版重装CUDA版的torch再装vLLM闪退的问题特别容易发生在Windows用户刚换了WSL的环节常见原因是WSL里装了CPU版的torch。提前执行python -c import torch; print(torch.cuda.is_available())能省下重装vLLM的十几分钟。5.2 推理速度慢启动没问题但生成token很慢这类问题更让人烦躁。先看是不是真的在用GPU推理watch -n 1 nvidia-smi如果GPU利用率很低CPU却很高大概率是模型在CPU上跑检查torch的CUDA是否可用。如果GPU利用率正常但单token生成延迟依然高那就得考虑并发策略了。vLLM的Continuous Batching机制会让多个请求同时在GPU上跑单请求反而未必最快但整体吞吐会高很多。所以小流量场景下感觉“慢”其实是它在优先服务并发请求。还有一类情况是模型输入特别长每次生成前都要先做预填充prefill这个过程是计算密集型的没法走流式优化。如果业务里总出现超长输入试一下用--max-model-len限制输入长度或者从模型侧把输入做截断响应会明显变快。5.3 接口超时与并发异常HTTP服务起来后并发一高就出现超时或报错通常不是vLLM挂了而是并发参数和显存容量不匹配。打开日志看两个关键数字Maximum concurrency和当前实际并发。如果实际并发已经顶到上限而请求还在进来新请求会排队超过了排队的耐性就表现为超时。应对方式要么是提高gpu-memory-utilization里留给KV Cache的空间要么是调整--max-num-seqs。需要注意的是max-num-seqs不要盲目拉到很高它会放大显存波动某个长序列请求进来后把KV Cache吃满后面所有请求都遭殃。比较稳的做法是先用默认值跑观察日志里的实际分配和空闲显存再一点一点往上加。还有一个容易被忽略的点临时目录空间不足。vLLM在加载大模型时会在临时目录写入一些中间文件如果用的是系统默认的/tmp且空间只剩几个G加载一个30多G的量化模型就可能报No space left on device。解决办法是把临时目录指到磁盘空间大的位置export TMPDIR/data/tmp我在一台配置比较奇怪的机器上就栽过这个跟头日志一直停在权重加载阶段排查半天最后发现是临时目录满了把TMPDIR指走就秒加载完毕。这种问题不属于显存范畴但很容易和显存问题混淆。5.4 模型输出异常模型跑起来了但生成的内容质量堪忧或者出现大量重复输出这通常不是vLLM的问题而是推理参数没调对。SamplingParams里的temperature、top_p、repetition_penalty直接影响输出质量。vLLM默认参数相对保守输出会比较平均。我自己做本地问答应用时常用的组合是params SamplingParams( temperature0.7, top_p0.9, repetition_penalty1.05, max_tokens1024, )temperature太高容易胡说八道太低容易照本宣科repetition_penalty大于1能降低重复率但设到1.2以上又会显得语句僵硬。这几项没有绝对正确的值按场景和模型调几组对比一下就能找到手感。还有一个隐藏问题是模型上下文被截断。当你输入的内容超出模型实际窗口vLLM不会报错而是静默截掉前面或后面的部分这会导致回答“前言不搭后语”。排查方法是开启日志里的提示词记录或者自己用短文本分批测试把模型窗口的极限摸清楚别盲目相信模型卡上的参数描述。经验补充几个少有人提的调优习惯最后再聊几个我长期跑vLLM养成的习惯不写进官方文档但很实用。一个是每换一个环境就建立一套“启动基准”。比如同样一个7B模型记录下默认配置下的显存占用、首token延迟和并发上限。以后任何一次调参都拿这套基准来衡量而不是凭感觉。这一步养成习惯排查问题的时候效率能翻倍。另一个是不要把模型路径散落在各处。建议在一个固定目录下管理本地模型用目录名标注精度和版本比如qwen25-7b-instruct-fp16和qwen25-7b-instruct-awq。启动命令里写本地路径不仅避开下载问题也让日志里出现模型信息时一目了然。第三个是善用在线API和离线推理的切换。开发调试阶段跑HTTP服务配合OpenAI SDK非常顺手但批量评测、压测或者批处理文档时直接用原生Python接口能省掉HTTP的序列化开销。两种方式代码结构差别不大但各有所长。根据我个人的使用感受vLLM最值得花时间的部分其实不在安装而在理解显存分配。把gpu_memory_utilization、max_model_len、max_num_seqs这三者的关系吃透你就能在任意显卡上快速找到适合自己业务的那组参数。这也是为什么这篇文章花了最大篇幅在讲这块。刚开始折腾的时候我也曾经因为显存不足反复重启后来把日志里的GPU KV cache size和Maximum concurrency当成调试仪表盘之后一切才真正变得可控。
返回列表