当前位置: 首页 > news >正文

从零到一 | CV转多模态大模型 | week17 | LLM 推理优化 vLLM 详解

本周初识 vllm 框架,理解 vllm 的作用和基本原理,能够使用 vllm 部署语言模型服务,并通过代码实战,体会纯 transformer 推理和 vllm 推理的吞吐量区别。

实战代码地址:
https://github.com/wz940216/From0to1-MLLM-StudyLog

vLLM 是一个 LLM 推理/服务引擎。它跑的仍然是 Transformer 模型,比如 Llama、Qwen、Mistral,只是把怎么调度请求、怎么管理 KV cache、怎么高效占满 GPU这件事做得比普通 transformers.generate() 更像生产级服务系统。举个例子:Transformers 单独推理像是你自己开车一趟一趟送人;vLLM 像调度中心,把很多乘客动态拼车,并且把车内座位管理得非常细。是一个集成了显存调度、管理、模型量化、算子融合、多线程/多进程、异步请求等技术的推理引擎。vLLM 也支持 LoRA、PEFT、LoCon 等 adapter 推理。

普通 Transformers 推理在做什么

以 Hugging Face transformers 为例,典型流程是:

model = AutoModelForCausalLM.from_pretrained(...) tokenizer = AutoTokenizer.from_pretrained(...) output = model.generate(...)

模型生成 token 时,本质是自回归:
先处理 prompt,计算第一步所需的 hidden states。每生成一个 token,就把这个 token 再喂回模型。
为了避免每一步都重算历史 token 的 Key/Value,会维护 KV cache。下一步 attention 只计算当前 token 的 query,但会读取历史所有 token 的 cached K/V。
这对单请求、实验、debug 很方便。但如果同时服务很多用户,问题就来了:每个请求长度不同、生成长度未知、KV cache 动态增长,GPU 内存会被浪费得很厉害。

vLLM 主要解决什么问题

vLLM 解决的核心是多个请求怎么高效批处理,每个请求的 KV cache 怎么放进 GPU 内存?请求来了、结束了、变长了,怎么动态调度?怎么让 GPU 尽量别空着?长上下文、多并发、多采样、beam search 怎么减少内存浪费?

beam search 在这里是第一次出现。官方的说 beam search 是一种生成策略,通常用于生成更高质量的文本。它会在每一步生成多个候选 token(称为 beams),并根据概率选择最优的几个 beams 继续生成。这样可以探索更多的可能性,但也会增加 KV cache 的需求,因为每个 beam 都需要维护自己的缓存。
但其实 beam search 就是在生成 token 中保留最优的 beam size 个候选序列,并且每一步只维护 beam size 个最好的结果。例如:

beam size = 3 我 喜欢 吃 ... 我 喜欢 看 ... 我 喜欢 学习 ...

beam search 每一步会比较这些候选序列的分数,保留分数最高的 beam size 条。
不过有个细节:只用概率相乘会偏爱短句。因为每多生成一个 token,就要再乘一个小于 1 的概率,整体概率通常会下降。所以实际 beam search 经常会加一个 长度惩罚 / 长度归一化:
score = log概率总和 / 长度惩罚项
这样不会让模型过分倾向特别短的输出。
具体算的时候,通常是累计每个 token 的 log probability,再配合长度惩罚等修正。

vLLM 论文Efficient Memory Management for Large Language
Model Serving with PagedAttention指出,LLM serving 的瓶颈很大程度来自 KV cache。模型权重常驻显存,activation 只是短暂存在,而 KV cache 会随请求数和序列长度增长,管理不好会限制 batch size 和吞吐。

vllm核心原理:PagedAttention

vLLM 最核心的思想是 PagedAttention。普通做法容易把一个请求的 KV cache 当作一整段连续内存来管理。但问题是:输出长度事先不知道。输入有的请求很短,有的很长。如果预留最大长度,会浪费显存。如果动态申请连续显存,会导致显存碎片化,找不到连续大块的显存可用,有涉及到显存搬移。有时多个采样分支、beam search 中有大量共享前缀,但普通连续 cache 不好共享。

PagedAttention 借鉴操作系统的虚拟内存分页思想,将显存分成了不同个 block 单元。每个请求的 KV cache 不再要求连续,而是按需分配到不同 block 中。程序上看起来还是一个连续的 token 序列,但物理显存中可以分散存放在固定大小的 KV block 里。

PagedAttention: request A logical blocks: block 0 -> physical block 17 block 1 -> physical block 03 block 2 -> physical block 91

也就是说,一个请求逻辑上还是连续的 token 序列,但物理显存中可以分散存放在固定大小的 KV block 里。

这种分配的好处是:

  • 按需分配:生成到哪里,KV block 分配到哪里,动态申请动态释放。
  • 减少内部碎片:不用为最大输出长度提前占满,没占满一个 block 后分配下一个,也就最后一块 block 可能出现内部碎片。
  • 减少外部碎片:所有 block 大小一致,更容易复用,外部不需要一块很大的且连续的存储块。
  • 支持共享:共享 prompt、beam search、多采样时,多个序列可以引用同一批 KV block。
    c- opy-on-write:分支需要改写时再将需要改的 block 复制一份,改复制的那一份即可,其他 block 不需要改变,仍可复用。

continuous batching

普通 batching 常见做法是:收集一批请求,一起跑完。问题是 LLM 生成长度不同:

request A: 生成 10 token request B: 生成 300 token request C: 生成 50 token

如果等整批结束,短请求会被长请求拖住;如果 padding 到一样长,计算又浪费。

vLLM 使用更细粒度的调度,通常称为 continuous batching / iteration-level scheduling:
做法是这样的,假如有四个 batch 请求A/B/C/D:

step 1: A B C D 一起生成 [A B C D]
step 2: A 完成后将 E 加入 [A B C D] -> [B C D E]
step 3: C 完成,F 加入 [B C D E] -> [B D E F]
step 4: B D E F 继续生成

也就是每个 decoding step 都可以重新组织 batch。vLLM 架构中 engine core 会运行 scheduler,管理 KV cache,并协调 GPU worker 执行模型。

vLLM 的优点

1、吞吐高:多并发下优势明显。原论文报告在评估场景中相对 FasterTransformer、Orca 达到 2-4 倍吞吐提升,尤其长序列、大模型、复杂 decoding 更明显。
2、显存利用率高:PagedAttention 减少 KV cache 浪费。
3、适合在线服务:支持 OpenAI-compatible API server。
4、调度能力强:continuous batching、preemption、chunked prefill 等。
5、长上下文更友好:KV cache 是长上下文推理的主要压力点之一,vLLM 正是围绕它优化。
6、生产能力多:tensor parallel、pipeline parallel、quantization、LoRA、prefix caching、speculative decoding、metrics 等。
7、共享前缀省显存:相同 prompt、多采样、beam search 等场景收益明显。

vLLM 的缺点

1、不是所有模型都支持:Transformers 通常是模型支持最全的地方;vLLM 对模型结构、attention 实现、tokenizer、特殊 forward 逻辑有要求。
2、部署复杂度更高:服务进程、GPU worker、scheduler、显存参数、并行参数都要调。
3、小并发未必划算:如果只是本地单条 prompt 测试,Transformers 更轻、更直观。
4、调参敏感:max_num_batched_tokens、max_num_seqs、gpu_memory_utilization、并行策略会影响 TTFT、ITL、吞吐、OOM 风险。
5、调试模型内部更麻烦:研究时想改 attention、插 hook、看 hidden states,Transformers 更顺手。
6、预占显存明显:vLLM 通常会预留较多显存给 KV cache。
极端情况下会 preempt/recompute,也就是当显存不够时,KV cache 空间不足会抢占请求,为了不 OOM, 先暂停某些请求并释放它们的 KV cache,后续再重算,这能保证系统健壮性,但会影响端到端延迟。为什么 vllm 不将 KV cache 先临时放到 CPU 内存,后续再搬回 GPU 呢,猜测也许是 KV catch 很大,在没有 NVLink 的情况下,搬运数据也比较占资源。

什么时候用哪个

用 Transformers 直接推理:

  • 研究、实验、调模型结构。
  • 单用户、低并发。
  • 模型很新,vLLM 暂不支持。
  • 需要完全控制 forward、logits、hidden states。
  • 本地简单 demo。

用 vLLM:

  • 要部署 API 服务。
  • 多用户并发。
  • 需要高吞吐、低成本。
  • prompt/output 长度差异大。
  • 长上下文、多采样、beam search。
  • 想要 OpenAI-compatible server。
  • 要做生产监控、并行、量化、LoRA serving。

vllm 应用

vllm 的调用方式和 transformers 很像,都是通过generate()来生成 token。

一个简单的例子:

defbenchmark_vllm(args:argparse.Namespace)->BenchResult:fromvllmimportLLM,SamplingParams# 加载原始模型的 tokenizer,vllm 也有自己的 tokenizer,但为了和 transformers 保持一致,这里直接用原始模型的 tokenizertokenizer=load_tokenizer(args.model_path)prompt=build_chat_prompt(tokenizer,args.prompt)sampling_params=SamplingParams(max_tokens=args.max_new_tokens,temperature=args.temperatureifargs.do_sampleelse0.0,top_p=args.top_p,)# vllm 的 LLM 类是一个高性能的推理引擎,支持多并发、显存管理、量化等特性llm=LLM(model=args.model_path,tokenizer=args.model_path,trust_remote_code=True,dtype=args.dtype,tensor_parallel_size=args.tensor_parallel_size,max_num_seqs=args.concurrency,gpu_memory_utilization=args.gpu_memory_utilization,)ifargs.warmup>0:llm.generate([prompt]*args.warmup,sampling_params,use_tqdm=False,)prompts=[prompt]*args.num_requests start=time.perf_counter()# generate() 方法会返回一个列表,每个元素是一个生成结果对象,包含生成的 token ids、logits 等信息outputs=llm.generate(prompts,sampling_params,use_tqdm=False)elapsed=time.perf_counter()-start output_tokens=sum(len(item.outputs[0].token_ids)foriteminoutputs)returnBenchResult(backend="vllm",num_requests=args.num_requests,concurrency=args.concurrency,elapsed_sec=elapsed,qps=args.num_requests/elapsed,output_tokens=output_tokens,tokens_per_sec=output_tokens/elapsed,)

也可以起一个 server :

defrun_vllm_server(args:argparse.Namespace)->None:command=[sys.executable,"-m","vllm.entrypoints.openai.api_server","--model",args.model_path,"--tokenizer",args.model_path,"--served-model-name",args.served_model_name,"--host",args.host,"--port",str(args.port),"--dtype",args.dtype,"--tensor-parallel-size",str(args.tensor_parallel_size),"--gpu-memory-utilization",str(args.gpu_memory_utilization),"--trust-remote-code",]print("Starting vLLM server:")print(" ".join(command))subprocess.run(command,check=True)

更多实战代码见仓库。
以上笔记来源于我的仓库: https://github.com/wz940216/From0to1-MLLM-StudyLog.git

我正在连载一个从零到一的多模态大模型学习笔记。
如果你对多模态大模型感兴趣,或者也在准备往大模型方向转
可以点赞/Fork我的仓库: https://github.com/wz940216/From0to1-MLLM-StudyLog.git

也可评论区留言交流,后面我会继续把每周的学习记录、踩坑经验陆续更新到仓库和这里。

http://www.jsqmd.com/news/1169884/

相关文章:

  • 保安岗亭厂家测评:日硕科技质量定制出色,服务响应却稍慢!
  • 产品经理开会:2026年5款视频号内容总结工具,快速生成会议纪要
  • SQL性能优化实战
  • Tab补全总卡顿、不精准、漏提示?这5个配置项改完立竿见影,团队已全员落地
  • Unity中5自由度机械臂物理仿真:从ArticulationBody到关节控制实战
  • AI Agent市场2026年关键词:从“能不能聊”到“能不能干” | 企业级数字员工落地深度实测
  • 终极解决方案:3步彻底修复TranslucentTB的Microsoft.VCLibs依赖缺失问题
  • 2026 年国内真人数字人制作平台TOP10:晟诺科讯达第一,企业选型正规参考
  • 8086 实模式寻址:20位地址总线下的基址偏移计算与 1MB 内存访问
  • 解决设备状态黑盒:注塑机数据采集从现场实施到报表生成
  • 2026家装瓷砖十大品牌盘点:主流瓷砖品类特点汇总
  • Windows内核驱动中C++异常处理(vcrtl)原理与实践指南
  • 系统装好了,高拍仪等外设却“罢工”?这个老大难终于有解了
  • 2026年7月最新太原劳力士官方售后客服中心地址电话及服务网点分布 - 劳力士官方服务中心
  • 为什么Google内部团队弃用JetBrains改用Windsurf IDE?——源码级性能压测报告首次公开
  • 实现RAG系统的24小时极速原型:从Paper到可运行Demo的工程笔记
  • 三星AR眼镜2026量产:MicroLED与Android XR生态协同落地
  • 如何实现跨平台直播内容的智能捕获与永久保存?
  • 亚马逊多店铺独立运营如何选择环境隔离工具(隐私隔离浏览器技术解析)
  • 如何三步完成微博备份到PDF:Speechless 终极指南
  • Windows 11优化终极指南:如何用Win11Debloat让你的系统更干净更快速
  • LM358/LM393 选型实战:3个关键参数对比与5个典型电路应用场景
  • ChatGPT-o1推理模型性能真相:实测128K上下文下P99延迟达4.8s——但通过3层缓存预热+逻辑单元隔离可压至1.2s!
  • Rust实现Windows本地搜索工具
  • 不掉屑卫生纸哪家性价比高:联盛森宝专业一流 - MXyuyu
  • 别踩 2026做B站视频总结的常见误区:我总结了控成本实操经验
  • 如何在老旧Android电视上获得流畅的4K直播体验?mytv-android原生解决方案
  • 2026年工业胶带厂家推荐榜:德莎/tesa胶带,汽车线束胶带,耐高温布基胶带,水溶性飞接胶带,泡棉双面胶带源头工厂详解 - 甄选服务推荐
  • 2026年真人数字人制作平台选购攻略:3 步筛选法,选出适配靠谱工具
  • SenseNova-U1-8B多模态模型:从信息图生成到一体化AI工作流