本项目用 vLLM 的 PagedAttention 设计校对 minivLLM 的 KV cache 管理。vLLM 从 v0.5.0 起逐步加入多模态推理支持,覆盖 LLaVA、Qwen-VL 和 InternVL 等 VLM。
vLLM 的多模态实现包含这些设计:
MultiModalProcessor,负责将图像/视频预处理后转换为 visual token,并嵌入到文本 token 序列中。这个过程对 KV cache 层是透明的。
用户请求: "描述这张图片" + image.jpg │ ▼ MultiModalProcessor: - 图像 load + resize(按 max_pixels / min_pixels 约束) - 调用模型的 visual tower(HF processor)产出 visual embeddings - 将 visual embeddings 插入到 prompt token 序列的 <image> 位置 │ ▼ Scheduler: - 预处理后的 token 序列作为正常 prompt 进入调度队列 - can_allocate() 检查 visual token + text token 的总 block 需求 - 分配 block table(与纯文本相同逻辑) │ ▼ ModelRunner: - prefill: visual token + text token 一起做前向,KV 写入 block - decode: 追加生成的文本 token 到 block table │ ▼ Output Processor: - 解码生成 token → 文本输出
vLLM 的 PagedAttention 在 VLM 场景下无需修改,因为:
[block_size, num_kv_heads, head_dim],与 token 是"视觉"还是"文本"无关。
visual token 的位置编码需要单独处理。使用 M-RoPE 的模型(如 Qwen2-VL/Qwen3-VL)为 visual token 提供三维坐标 (t, h, w),text token 仍使用一维位置。vLLM 通过 AttentionMetadata 的 position_ids 区分两者。
在受限显存环境中,vLLM 的 PagedAttention 可以减少连续分配与碎片带来的浪费:
| 机制 | 对受限显存配置的价值 |
|---|---|
| Block 级分配 | 不需要为 visual token 预留完整的 max_seq_len 连续空间。visual token 在 prefill 时写入若干个 block,decode 阶段使用新 block。 |
| 碎片率 <4% | 若以一张中档显存卡为例,仅浪费约 480MB。在 contiguous 方案下碎片可能高达 5~6GB,直接阻塞推理。 |
| Automatic Prefix Caching | system prompt 的 KV 共享可以节省约 20~50MB 的重复 prefill 计算(取决于 prompt 长度),并减少 batch 服务中的同前缀计算。 |
| Swap Out / Recomputation | 当显存不足时,可将不活跃请求的 KV block swap 到 CPU 内存或直接释放(需要时重新 prefill 恢复)。 |
在我们使用 run_qwen_vl_reference.py(Task 10)做参考推理时,4 个候选模型(Qwen3-VL-4B、Qwen2.5-VL-3B、InternVL3.5-4B、SmolVLM2-2.2B)都因 device_map="auto" 依赖 accelerate 而加载失败。这是 vLLM/TGI 等推理引擎通常绕过的环境问题,它们自己管理设备映射,不依赖 HF transformers 的 device_map="auto"。
这次实验也暴露了参考实现对环境的依赖。本机是 macOS + MPS,没有 CUDA,基于 device_map 的高层加载路径会受阻。minivLLM 改用手动权重加载,绕开了这项依赖。