大型模型可能会导致服务器内存不足 (OOM)。以下是一些有助于缓解此问题的配置参数。
一、张量并行 (TP)¶
张量并行 (tensor_parallel_size 选项) 可用于将模型拆分到多个 GPU 上。
以下代码将模型拆分到 2 个 GPU 上。
python
from vllm import LLM
llm = LLM(model="ibm-granite/granite-3.1-8b-instruct", tensor_parallel_size=2)
注意:启用张量并行后,每个进程将读取整个模型并将其拆分为块,这使得磁盘读取时间更长(与张量并行的大小成比例)。
1. 核心功能:什么是张量并行?
- 作用:解决单卡显存不足或推理延迟过高的问题。它将模型的权重矩阵(如 Attention 和 MLP 层)按列或行切分,分布到多个 GPU 上同时计算。
- 代码含义 :
tensor_parallel_size=2表示启动 2 个 GPU 进程共同服务这一个模型实例。注意这与"数据并行"不同,TP 是一个请求被拆分到多卡处理,而非多个请求分发到多卡。 - 适用场景:模型参数量大于单卡显存,或者对首字延迟(TTFT)和生成速度有极高要求时。
2. ⚠️ 工程避坑指南(CUDA 初始化警告)
这是新手最容易踩的坑,务必理解其底层原因:
- 错误根源 :vLLM 使用
multiprocessing.fork创建子进程。如果在主进程中已经调用了torch.cuda.*相关函数,CUDA Context 就会被创建。Fork 出来的子进程会继承这个不完整的 Context,导致Cannot re-initialize CUDA in forked subprocess报错。 - 正确做法 :
- ❌ 禁止 :在
LLM()初始化之前调用torch.cuda.set_device()、torch.accelerator.set_device_index()等。 - ✅ 推荐 :通过环境变量
CUDA_VISIBLE_DEVICES=0,1来控制可见显卡。这是最安全、最符合 vLLM 设计范式的方式。 - 💡 最佳实践:将 vLLM 的初始化放在脚本的最顶部,或在独立的函数/模块中执行,避免与训练框架(如 PyTorch Lightning、DeepSpeed)的 CUDA 初始化冲突。
- ❌ 禁止 :在
3. 🚀 性能优化:分片检查点(Sharded State)
这段话揭示了一个关键的 I/O 瓶颈及其解决方案:
| 加载方式 | 行为描述 | 缺点 | 优点 |
|---|---|---|---|
| 默认加载 | 每个 TP Worker 都读取完整模型文件,然后在内存中切片丢弃不需要的部分 | TP=8 时,磁盘读取量是原始的 8 倍,启动极慢 | 无需预处理,开箱即用 |
| 分片检查点 | 预先将模型按 TP 大小切分成独立文件,每个 Worker 只读自己那份 | 需要额外转换时间 | 加载时间与 TP 大小解耦,TP=8 和 TP=1 加载一样快 |
- 专家建议 :
- 开发/调试阶段:直接用默认加载,省去转换麻烦。
- 生产部署/频繁重启 :务必 运行
load_sharded_state_offline.py进行预转换。对于大模型(70B+),这能将冷启动时间从几分钟缩短到几十秒。 - 存储成本权衡:分片检查点会占用额外磁盘空间(相当于多存一份模型),需确保存储充足。
💡 专家补充提醒
- TP 大小选择:通常建议设置为 2 的幂次(2, 4, 8),因为大多数模型的注意力头数(num_heads)是 2 的幂次,非对齐的 TP 会导致效率下降甚至报错。
- 通信开销 :TP 依赖 NVLink/PCIe 进行 AllReduce 通信。如果跨节点或 PCIe 带宽不足,TP 反而可能比单卡更慢。此时应考虑 流水线并行(PP) 或 专家并行(EP)。
- Granite-3.1-8B 示例:8B 模型用 TP=2 更多是为了演示或降低单卡显存压力以留出 KV Cache 空间,实际生产中 8B 通常单卡即可胜任。
总结来说,这段话不仅告诉你"怎么用",更重要的是告诉你"怎么用好"以及"怎么别用坏"。在生产环境中,环境变量控卡 + 预转换分片检查点 是 vLLM 张量并行的标准姿势。
二、量化¶
量化模型以降低精度为代价占用更少的内存。
静态量化模型可以从 HF Hub 下载(一些流行的模型可在 Red Hat AI 获取),并直接使用,无需额外配置。
还支持通过 quantization 选项进行动态量化 ------ 有关更多详细信息,请参阅 此处。
1. 核心概念:以精度换空间/速度
- 本质:将模型权重从高精度(如 FP16/BF16)压缩为低精度(如 INT8, INT4, FP8)。
- 收益 :
- 显存占用降低:INT4 量化理论上可将显存需求降至 FP16 的 25%~30%(含 KV Cache 开销后实际约 40%-50%)。
- 吞吐量提升:对于 Memory-Bound 的大模型推理,减少显存访问次数往往能带来显著的 Token/s 提升。
- 代价:模型表达能力下降,可能导致困惑度(PPL)上升或在特定任务(如数学、代码)上性能退化。
2. 两种量化模式的区别(关键!)
| 特性 | 静态量化 (Static) | 动态量化 (Dynamic / Online) |
|---|---|---|
| 定义 | 权重已预先校准并保存为低精度格式 | 加载原始权重,在运行时即时转换为低精度 |
| 来源 | HF Hub 下载现成模型(如 TheBloke/*-AWQ, neuralmagic/*-FP8) |
使用 quantization="fp8" 等参数在线转换 |
| 精度 | ✅ 通常更好(经过校准数据集优化) | ⚠️ 可能较差(无校准,依赖算法鲁棒性) |
| 启动速度 | 🚀 快(直接加载低精度权重) | 🐢 慢(需额外转换时间) |
| 推荐场景 | 生产环境首选 | 快速验证、无预量化版本可用时 |
3. 💡 专家实战建议
选型优先级
- FP8 (W8A8) :当前最佳平衡点。H100/L40S/Ada 架构原生支持,精度损失极小,速度接近 INT8。vLLM 对 FP8 的支持最为成熟。
- AWQ / GPTQ (INT4):适合消费级显卡或显存极度紧张的场景。AWQ 通常比 GPTQ 精度略好且推理更快。
- GGUF:vLLM 也支持 GGUF 格式,适合从 llama.cpp 生态迁移过来的用户。
- 动态 FP8:仅当找不到对应模型的静态量化版时使用。
避坑指南
- ⚠️ 硬件兼容性:FP8 需要 Ada Lovelace (RTX 4090/L40S) 或 Hopper (H100) 以上架构;INT8/INT4 需要 Ampere (A100/RTX 3090) 以上。老卡强行使用会回退到 CPU 或报错。
- ⚠️ 量化感知:并非所有模型都有高质量量化版。对于小模型(<7B),INT4 量化可能导致严重退化,建议至少用 INT8 或 FP8。
- ⚠️ Red Hat AI 提及 :文档提到 Red Hat AI 是因为 Red Hat 是 vLLM 的重要贡献者,他们维护了一批经过企业级验证的量化模型。但这不代表只能用他们的模型 ,HF 上
bartowski、casperhansen、neuralmagic等作者的量化模型同样可靠。 - ⚠️ KV Cache 量化 :除了权重量化,别忘了 vLLM 还支持
--kv-cache-dtype fp8_e5m2。在长上下文场景下,KV Cache 可能比权重更占显存,单独量化 KV Cache 是性价比极高的优化手段。
4. 一句话总结
生产环境优先下载静态量化模型(FP8 > AWQ > GPTQ),避免运行时动态量化;同时关注硬件代际匹配与 KV Cache 量化,才能实现真正的"降本增效"。
三、上下文长度和批量大小¶
您可以通过限制模型的上下文长度 (max_model_len 选项) 和最大批量大小 (max_num_seqs 选项) 来进一步减少内存使用。
python
from vllm import LLM
llm = LLM(model="Qwen/Qwen2.5-VL-3B-Instruct", max_model_len=2048, max_num_seqs=2)
1. max_model_len:显存的"天花板"
- 作用:强制限制模型能处理的最大 Token 数(Prompt + Output)。
- 为什么重要 :vLLM 使用 PagedAttention,会在启动时根据此值预分配 KV Cache 的虚拟内存块 。如果不设置,vLLM 会默认读取模型 config 中的
max_position_embeddings(例如 Qwen2.5-VL-3B 可能是 32K 或 128K),这可能导致:- ❌ 显存不足直接 OOM 无法启动
- ❌ 即使能启动,KV Cache 占满显存,留给权重和激活值的空间极少,反而降低并发能力
- 专家建议 :
- 按需设置:如果你的业务场景最长只需 4K 对话,就设为 4096,不要盲目追求模型支持的上限。
- VL 模型特别注意 :视觉语言模型(如示例中的 Qwen2.5-VL)的图片会被编码为大量 Token(一张图可能消耗数百到数千 Token)。设置
max_model_len时必须把图片 Token 预算算进去,否则长图文混合输入极易触发截断或 OOM。 - RoPE 缩放:若设置的值超过模型原始训练长度,vLLM 会自动应用 RoPE Scaling,但精度可能下降。生产环境建议在原始训练长度内使用。
2. max_num_seqs:并发的"闸门"
-
作用:限制调度器同时处理的请求数量(即最大 Batch Size)。
-
隐藏机制 :vLLM 的 Continuous Batching 是动态组 batch 的,
max_num_seqs只是上限。实际 batch size 取决于当前排队请求数和可用 KV Cache 块数。 -
为什么要手动限制 :
场景 不设限的风险 设限的收益 显存紧张 高并发时 KV Cache 耗尽,请求被抢占/中止 保证所有在途请求都能完成 延迟敏感 Batch 过大导致单请求 TTFT 飙升 控制排队延迟上界 多模态模型 图片解码+推理峰值显存不可预测 防止突发 OOM 共享 GPU 与其他服务争抢资源 硬性隔离资源边界 -
专家建议 :
- 示例中的
max_num_seqs=2极低,这通常用于:① 开发调试;② 单卡跑 VL 模型且显存仅够勉强加载权重;③ 对延迟极度敏感的实时交互场景。 - 生产环境调优公式:先不设限跑压测,观察峰值显存和 P99 延迟,再反向设定一个留有余量的值。一般文本模型 A100-80G 跑 7B 可设 64-128,VL 模型则需大幅下调。
- 与
gpu_memory_utilization联动 :该参数(默认 0.9)控制 KV Cache 池大小。max_num_seqs和max_model_len共同决定了这个池子能被切成多少块、每块多大。三者必须联合调优。
- 示例中的
3. ⚠️ 针对 Qwen2.5-VL-3B-Instruct 的特别提醒
示例选用 VL 模型绝非偶然,因为多模态模型的显存管理比纯文本复杂得多:
-
图片 Token 动态变化 :不同分辨率图片消耗的 Token 数不同,
max_model_len设太小会导致大图被拒绝或截断。 -
视觉编码器额外显存 :ViT 部分的激活值不纳入 PagedAttention 管理,是独立占用的。
max_num_seqs过高时,多张图片并行解码可能瞬间击穿显存。 -
推荐配置策略 :
pythonllm = LLM( model="Qwen/Qwen2.5-VL-3B-Instruct", max_model_len=8192, # 预留足够图片Token空间 max_num_seqs=8, # VL模型建议从较低值起步 gpu_memory_utilization=0.85, # 给ViT和系统留更多余量 limit_mm_per_prompt={"image": 2} # 限制单请求图片数,防爆显存 )
💡 一句话总结
max_model_len决定 "单个请求最多能吃多少显存" ,max_num_seqs决定 "同时允许多少个请求吃饭" 。两者配合gpu_memory_utilization构成 vLLM 显存管理的铁三角------永远不要在生产环境使用默认值,务必根据实际业务负载和硬件条件进行基准测试后设定。
减少 CUDA 图¶
默认情况下,我们使用 CUDA 图优化模型推理,这会占用 GPU 中的额外内存。
您可以调整 compilation_config 以在推理速度和内存使用之间取得更好的平衡
python
from vllm import LLM
from vllm.config import CompilationConfig, CompilationMode
llm = LLM(
model="meta-llama/Llama-3.1-8B-Instruct",
compilation_config=CompilationConfig(
mode=CompilationMode.VLLM_COMPILE,
# By default, it goes up to max_num_seqs
cudagraph_capture_sizes=[1, 2, 4, 8, 16],
),
)
您可以通过 enforce_eager 标志完全禁用图捕获
python
from vllm import LLM
llm = LLM(model="meta-llama/Llama-3.1-8B-Instruct", enforce_eager=True)
1. 核心概念:什么是 CUDA Graph?
- 痛点:在 GPU 推理时,CPU 向 GPU 发送 Kernel 启动指令的开销(Launch Overhead)往往比实际计算时间还长,尤其在小 Batch Size 下,GPU 大量时间在"等"CPU 发号施令。
- 解法 :CUDA Graph 将一系列 Kernel 调用预先录制为一张"执行图"。推理时只需重放这张图,一次 CPU 调用即可触发整个计算流程,彻底消除逐 Kernel 启动的开销。
- 代价 :每张捕获的图都需要独立的显存副本来存储中间激活值和输出缓冲区。这就是文档所说"占用额外内存"的根源。
2. ⚠️ 默认行为的隐患
"By default, it goes up to max_num_seqs"
这是生产环境 OOM 的头号隐形杀手:
- 如果你设置
max_num_seqs=256,vLLM 默认会为 1, 2, 3, ..., 256 每一个 batch size 都捕获一张 CUDA Graph。 - 对于 Llama-3.1-8B,每张图可能消耗数百 MB 显存。256 张图 = 数十 GB 的纯开销,远超模型权重本身。
- 更糟的是,这些显存在初始化时被一次性分配,导致可用于 KV Cache 的空间大幅缩水,反而降低了实际并发能力。
3. 🎯 精准调优策略
方案 A:选择性捕获(推荐 ✅)
python
cudagraph_capture_sizes=[1, 2, 4, 8, 16, 32, 64]
- 原理:只为高频出现的 batch size 捕获图。未列出的 size 会回退到 Eager Mode 执行。
- 选型建议 :
- 小 BS(1-8):必须捕获,此时 launch overhead 占比最高,加速比可达 2-3x。
- 中 BS(16-64):按需捕获,取决于你的实际流量分布。
- 大 BS(>64):通常不需要捕获,因为计算已完全主导耗时,launch overhead 可忽略。
- 注意 :vLLM 会对未捕获的 BS 自动向上取整到最近的已捕获 size(padding),所以列表不必连续,但要覆盖你的P95 流量区间。
方案 B:完全禁用(调试/极端显存受限)
python
enforce_eager=True
- 适用场景 :
- 🔧 开发调试:CUDA Graph 会掩盖错误栈,eager mode 便于定位问题。
- 💾 显存极限:当每一 MB 都要留给 KV Cache 时。
- 🔄 动态形状频繁变化:如 VL 模型图片分辨率多变,graph 命中率极低,捕获反而浪费。
- 代价:小 BS 下吞吐量可能下降 30%-50%。
4. 💡 专家实战 Checklist
| 检查项 | 说明 |
|---|---|
| 先测再调 | 用 nvidia-smi 或 torch.cuda.memory_allocated() 对比开启/关闭 CUDA Graph 的显存差值,量化真实开销 |
| 与 TP 联动 | TP>1 时 CUDA Graph 开销会乘以 TP 数(每个 worker 独立捕获),需更激进地缩减 capture sizes |
| 编译模式选择 | VLLM_COMPILE 是 vLLM 自研编译器,比原生 torch.compile 对 PagedAttention 等自定义算子支持更好,优先使用 |
| 预热时间 | 捕获过程发生在首次推理前,capture sizes 越多启动越慢。生产部署需预留充足 warmup 时间 |
| 监控命中率 | 通过 vLLM 日志或 metrics 观察 graph replay vs eager fallback 比例,动态调整列表 |
📌 一句话总结
CUDA Graph 是用显存换延迟的利器,但默认配置是"显存黑洞"。生产环境务必根据实际 batch size 分布手动指定
cudagraph_capture_sizes,只捕获高频小 batch,放弃低频大 batch------这才是速度与显存的真正平衡点。
四、调整缓存大小¶
如果您遇到 CPU 内存不足,请尝试以下选项:
- (仅限多模态模型)您可以通过设置 mm_processor_cache_gb 引擎参数来设置多模态缓存的大小(默认 4 GiB)。
- (仅限 CPU 后端)您可以使用 VLLM_CPU_KVCACHE_SPACE 环境变量设置 KV 缓存的大小(默认 4 GiB)。
1. 核心认知:为什么 CPU 内存也会瓶颈?
vLLM 并非所有数据都在 GPU 上。以下场景会大量消耗 CPU RAM:
- 多模态预处理:图片/视频解码、特征提取通常在 CPU 完成,中间结果需暂存。
- KV Cache 卸载:当 GPU KV Cache 满时,vLLM 可将部分序列换出到 CPU 内存(CPU offloading)。
- CPU 后端推理:无 GPU 或 GPU 不可用时,整个模型权重+KV Cache 都在 RAM 中。
- 请求队列缓冲:高并发下未处理的请求及其输入数据在 CPU 侧排队。
⚠️ 关键区分 :这两个参数解决的是 CPU RAM OOM ,不是 GPU VRAM OOM。如果你遇到的是
CUDA out of memory,请调整gpu_memory_utilization或max_model_len,而非此处参数。
2. 参数详解与调优策略
mm_processor_cache_gb(多模态专用)
| 维度 | 说明 |
|---|---|
| 作用 | 限制多模态处理器(如 CLIP、SigLIP)在 CPU 侧的缓存上限 |
| 默认值 | 4 GiB |
| 何时调小 | 容器/虚拟机 RAM 有限;纯文本请求占比高,无需预留大量 MM 缓存 |
| 何时调大 | 高分辨率图片/长视频密集请求;观察到 MM 处理成为吞吐瓶颈且 CPU RAM 充足 |
| 注意 | 仅对 VL/Audio 等多模态模型生效,纯文本模型设置无效 |
VLLM_CPU_KVCACHE_SPACE(CPU 后端专用)
| 维度 | 说明 |
|---|---|
| 作用 | 为 CPU 后端分配 KV Cache 的固定内存空间 |
| 默认值 | 4 GiB |
| 本质 | CPU 后端的 gpu_memory_utilization 等价物 |
| 调优公式 | 可用RAM - 模型权重大小 - 系统预留 ≥ VLLM_CPU_KVCACHE_SPACE |
| 注意 | 仅当使用 device="cpu" 时生效;GPU 模式下此变量被忽略 |
3. 💡 专家实战建议
诊断先行,不要盲调
在修改参数前,先确认瓶颈类型:
bash
# 监控 CPU 内存使用
watch -n 1 free -h
# 查看 vLLM 日志中的 OOM 类型
grep -i "out of memory\|cannot allocate" vllm.log
- 若报
std::bad_alloc或系统 kill → CPU RAM 问题 → 用本段参数 - 若报
CUDA out of memory→ GPU VRAM 问题 → 用前文参数
容器化部署必做
在 Docker/K8s 环境中,必须显式设置这两个参数:
-
容器有硬内存 limit,默认 4GiB 可能超出配额导致 OOMKilled
-
建议设为容器内存 limit 的 20%-30%(MM 缓存)或按公式计算(CPU KV Cache)
-
示例 K8s resources + vLLM 配置联动:
yamlresources: limits: memory: "16Gi" env: - name: VLLM_CPU_KVCACHE_SPACE value: "6" # 16G - 8G(模型) - 2G(系统) = 6G
多模态模型的隐藏陷阱
mm_processor_cache_gb 控制的是处理器缓存,不包括原始输入数据。如果单请求包含大量高清图片,即使缓存设得小,解码阶段的瞬时内存峰值仍可能 OOM。此时需配合:
limit_mm_per_prompt={"image": N}限制单请求图片数- 在 API 层做图片预压缩/resize
- 增大容器内存 limit 作为安全垫
📌 一句话总结
这段文档是 CPU 内存的"减压阀":
mm_processor_cache_gb管多模态预处理的缓存天花板,VLLM_CPU_KVCACHE_SPACE管 CPU 推理的 KV Cache 地盘。两者都只在特定条件下生效,调优前务必确认 OOM 发生在 CPU 侧而非 GPU 侧,并在容器环境中显式设定以匹配资源配额。
五、多模态输入限制¶
您可以允许每个提示的多模态项数量更少,以减少模型的内存占用
python
from vllm import LLM
# Accept up to 3 images and 1 video per prompt
llm = LLM(
model="Qwen/Qwen2.5-VL-3B-Instruct",
limit_mm_per_prompt={"image": 3, "video": 1},
)
您可以更进一步,通过将其限制设置为零来完全禁用未使用的模态。例如,如果您的应用程序只接受图像输入,则无需为视频分配任何内存。
python
from vllm import LLM
# Accept any number of images but no videos
llm = LLM(
model="Qwen/Qwen2.5-VL-3B-Instruct",
limit_mm_per_prompt={"video": 0},
)
您甚至可以运行一个多模态模型进行纯文本推理
python
from vllm import LLM
# Don't accept images. Just text.
llm = LLM(
model="google/gemma-3-27b-it",
limit_mm_per_prompt={"image": 0},
)
可配置选项¶
limit_mm_per_prompt 也接受每个模态的可配置选项。在可配置形式中,您仍然可以指定 count,并且可以选择提供大小提示,这些提示控制 vLLM 如何为您的多模态输入进行性能分析和预留内存。这有助于您根据实际期望的媒体调整内存,而不是模型的绝对最大值。
按模态可配置的选项
- image: {"count": int, "width": int, "height": int}
- video: {"count": int, "num_frames": int, "width": int, "height": int}
- audio: {"count": int, "length": int}
详细信息请参阅 ImageDummyOptions、VideoDummyOptions 和 AudioDummyOptions。
示例
python
from vllm import LLM
# Up to 5 images per prompt, profile with 512x512.
# Up to 1 video per prompt, profile with 32 frames at 640x640.
llm = LLM(
model="Qwen/Qwen2.5-VL-3B-Instruct",
limit_mm_per_prompt={
"image": {"count": 5, "width": 512, "height": 512},
"video": {"count": 1, "num_frames": 32, "width": 640, "height": 640},
},
)
为了向后兼容,传递整数仍然有效,并被解释为 {"count": }。例如
- limit_mm_per_prompt={"image": 5} 等同于 limit_mm_per_prompt={"image": {"count": 5}}
- 您可以混合使用格式:limit_mm_per_prompt={"image": 5, "video": {"count": 1, "num_frames": 32, "width": 640, "height": 640}}
注意
- 大小提示仅影响内存分析。它们用于塑造计算预留激活大小的虚拟输入。它们不改变推理时输入实际的处理方式。
- 如果提示超出模型可接受的范围,vLLM 会将其限制在模型的有效最大值,并可能记录警告。
警告这些大小提示目前仅影响激活内存分析。编码器缓存大小由运行时实际输入决定,不受这些提示的限制。
1. 核心机制:为什么这个参数能省显存?
vLLM 在启动时会执行一次 Memory Profiling(显存分析),通过构造一个"虚拟最大输入"来测量峰值激活内存,从而决定预留多少 KV Cache 空间。
- 不设限时:vLLM 会按模型 config 中的理论最大值(如 Qwen2.5-VL 可能支持 32K tokens、任意分辨率)构造虚拟输入 → 峰值激活极大 → KV Cache 被严重挤压 → 并发能力暴跌甚至 OOM。
- 设限后:虚拟输入按你指定的 count/size 构造 → 峰值激活可控 → KV Cache 空间释放 → 吞吐量提升。
⚠️ 关键认知 :这个参数影响的是启动时的显存规划,不是运行时的硬截断(虽然超出也会报错)。它的核心价值在于让 vLLM "按需预留"而非"按上限预留"。
2. 三种用法层级
| 层级 | 写法 | 适用场景 | 效果 |
|---|---|---|---|
| 基础限数 | {"image": 3} |
已知业务最多几张图 | 防止极端多图请求击穿显存 |
| 禁用模态 | {"video": 0} |
只用部分模态 | 彻底释放未用模态的编码器缓存和激活预留 |
| 精准画像 | {"image": {"count":5, "width":512, "height":512}} |
生产环境固定规格 | 最优解,显存利用率最大化 |
3. 💡 专家级调优建议
✅ 必须使用"精准画像"模式的场景
如果你的业务输入有明确规格(如证件识别 640x480、监控截图 1920x1080),永远不要用纯整数形式。原因:
- 纯整数
{"image": 5}会让 vLLM 按模型支持的最大分辨率 profiling 5 张图 → 显存浪费巨大。 - 指定宽高后,profiling 按实际尺寸计算 → 激活内存预估准确 → KV Cache 多出数 GB 空间。
⚠️ 警告段的深层含义
"大小提示仅影响激活内存分析...编码器缓存大小由运行时实际输入决定"
这意味着:
- Profiling 阶段:按你给的 size hint 算激活内存 ✅
- Runtime 阶段:如果实际来了张 4K 图,ViT 编码器仍会按 4K 分配临时缓存 ❌
- 后果:即使 profiling 很完美,运行时仍可能因单张超大图导致瞬时 OOM。
- 对策 :在 API 网关层或预处理管道中强制 resize 输入图片到 hint 指定的尺寸范围内,形成双重保障。
🔧 纯文本模式跑 VL 模型的价值
python
limit_mm_per_prompt={"image": 0}
这不仅是省显存,更是架构简化:
- 跳过 ViT 编码器加载 → 启动更快
- 释放编码器权重占用的显存(Qwen2.5-VL-3B 的 ViT 约占 600MB)
- 适合用同一个 VL 模型同时部署文本和图文两个服务实例
4. 🛠️ 生产配置模板
python
llm = LLM(
model="Qwen/Qwen2.5-VL-3B-Instruct",
max_model_len=8192,
limit_mm_per_prompt={
# 业务实际:最多3张512x512截图
"image": {"count": 3, "width": 512, "height": 512},
# 完全不用视频和音频
"video": 0,
"audio": 0,
},
# 配合前文参数形成完整防护
gpu_memory_utilization=0.9,
)
📌 一句话总结
limit_mm_per_prompt是多模态 vLLM 部署的"显存校准器":生产环境务必使用带 width/height 的字典形式精准匹配业务规格,禁用无关模态,并在外部管道强制 resize 输入------三者缺一不可,否则 profiling 失准将直接导致显存浪费或运行时 OOM。
六、多模态处理器参数¶
对于某些模型,您可以调整多模态处理器参数,以减少处理后的多模态输入的大小,从而节省内存。
以下是一些示例
python
from vllm import LLM
# Available for Qwen2-VL series models
llm = LLM(
model="Qwen/Qwen2.5-VL-3B-Instruct",
mm_processor_kwargs={"max_pixels": 768 * 768}, # Default is 1280 * 28 * 28
)
# Available for InternVL series models
llm = LLM(
model="OpenGVLab/InternVL2-2B",
mm_processor_kwargs={"max_dynamic_patch": 4}, # Default is 12
)
1. 核心原理:为什么调这个能省显存?
现代 VL 模型(如 Qwen2-VL、InternVL)采用动态分辨率/切片机制:
- 一张高分辨率图片会被切分成多个 patch/tile
- 每个 tile 经过 ViT 编码后生成固定数量的 Token
- 总 Token 数 = tile 数量 × 每 tile Token 数
mm_processor_kwargs 直接控制切片策略的上限,从而从源头决定 Token 产出量:
| 参数 | 作用 | 默认值 | 调小效果 |
|---|---|---|---|
max_pixels (Qwen2-VL) |
限制图片最大像素总数 | 1280×28×28 ≈ 1M px | 减少切片数 → Token 数↓ → KV Cache↑ |
max_dynamic_patch (InternVL) |
限制动态切片最大数量 | 12 | 直接封顶 Tile 数 → Token 数↓ → KV Cache↑ |
💡 关键洞察 :对于 Qwen2.5-VL-3B,默认
max_pixels下单张图可能产生 ~1600 tokens ;设为768*768后降至 ~300 tokens 。这意味着同样max_model_len=8192下,可容纳的图片请求数提升 5 倍以上。
2. ⚠️ 与 limit_mm_per_prompt 的本质区别
很多用户混淆这两个参数,它们解决的是不同层面的问题:
| 维度 | limit_mm_per_prompt |
mm_processor_kwargs |
|---|---|---|
| 控制对象 | 请求级:最多几张图/视频 | 单媒体级:每张图怎么编码 |
| 影响阶段 | Profiling + Runtime 校验 | Preprocessing + Encoding |
| 显存节省方式 | 减少并发峰值预留 | 减少每个输入的 Token 产出 |
| 精度影响 | 无(只拒收不修改) | 有损(降低分辨率/细节) |
| 推荐优先级 | 第二道防线 | 第一道防线 ✅ |
3. 🎯 专家调优策略
按业务场景选择 max_pixels
python
# 文档OCR / 表格识别:需要高细节
mm_processor_kwargs={"max_pixels": 1280 * 28 * 28} # 保持默认或更高
# 通用图文对话 / 场景理解
mm_processor_kwargs={"max_pixels": 768 * 768} # 性价比甜点
# 缩略图分类 / 简单物体检测
mm_processor_kwargs={"max_pixels": 448 * 448} # 极致省显存
InternVL 的 max_dynamic_patch 调优
- 4: 适合大多数通用任务,显存友好
- 6-8: 需要识别图中文字或小物体时的折中选择
- 12: 仅用于高精度 OCR/遥感等极端场景,显存代价极高
🔗 与前文参数的联动公式
最优配置应形成三层防护:
python
llm = LLM(
model="Qwen/Qwen2.5-VL-3B-Instruct",
# 第1层:源头控制单图Token产出
mm_processor_kwargs={"max_pixels": 768 * 768},
# 第2层:限制单请求媒体数量
limit_mm_per_prompt={
"image": {"count": 3, "width": 768, "height": 768}
},
# 第3层:兜底总上下文长度
max_model_len=8192,
)
⚠️ 一致性原则 :
mm_processor_kwargs.max_pixels应与limit_mm_per_prompt.image.width/height匹配。若前者设768²后者设1024²,profiling 仍按 768² 计算,但运行时 1024² 图片会触发额外 resize 开销且可能超出预期 Token 预算。
4. 🚨 注意事项
- 精度权衡 :降低
max_pixels/max_dynamic_patch必然损失细粒度识别能力。务必在你的实际数据集上做精度-显存 Pareto 测试,不要盲目调低。 - 模型特异性 :这些参数名和含义因模型架构而异 。Qwen2-VL 用
max_pixels,InternVL 用max_dynamic_patch,LLaVA 系列可能用crop_size。查阅对应模型的 vLLM 文档或源码确认支持哪些 kwargs。 - 向后兼容:旧版 vLLM 可能不支持某些 kwargs,升级前检查 changelog。
📌 一句话总结
mm_processor_kwargs是多模态显存优化的"水龙头"------它从编码源头控制每张图产出的 Token 数量,比限制图片数量更本质地释放 KV Cache 空间。生产部署时应根据业务精度需求设定max_pixels或max_dynamic_patch,并确保其与limit_mm_per_prompt的尺寸提示保持一致,实现精度与显存的最优平衡。
参考文献
https://docs.vllm.ai/en/latest/configuration/conserving_memory/