Skip to content

多模态处理 — 概念

多模态输入处理流水线

输入类型

图像输入

python
# 图像输入格式
{
    "prompt": "描述这张图片",
    "multi_modal_data": {
        "image": PILImage or URL or base64,
    }
}

处理流程:

  1. 图像解码和预处理(resize、normalize)
  2. Vision Encoder 提取特征
  3. 投影层映射到语言模型的 embedding 空间
  4. 替换文本中的 <image> 占位符

音频输入

处理流程:

  1. 音频解码和特征提取(mel spectrogram)
  2. Audio Encoder(通常是 Whisper 编码器)提取特征
  3. 投影层映射到 embedding 空间

流式语音转写(realtime ASR)模型声明 SupportsRealtimebuffer_realtime_audio 持续消费异步音频流并产出 prompt 段),engine runner 在 is_realtime=True 时随生成持续注入音频嵌入,而非只在初始 prompt 一次性给出。

视频输入

处理流程:

  1. 视频解码,均匀采样帧
  2. 每帧通过 Vision Encoder
  3. 时序聚合(pooling 或 temporal attention)

vLLM 通过 VideoLoaderRegistry(基于 ExtensionManager)按模型的 HF video_processor 类名自动匹配并选择视频解码后端,各 VLM 在注册时声明其对应的 processor,运行时无需手动指定。目前已为 Qwen3-VL、Qwen2-VL/Qwen2.5-VL、GLM4.6V 等提供专用视频加载器。v0.26.1rc0 后 1100+ 行的 multimodal/video.py 巨型文件拆为 vllm/multimodal/video_decoders/ 包(#49155):base.py(抽象基类)+ opencv.pypyav.py已弃用,#54231,仅剩占位)、pynvvideocodec.pytorchcodec.pydeepstream.py 各后端,由 __init__.py 统一分发——新推荐 torchcodec / OpenCV。

多模态 processor 本体(multimodal/processing/processor.py)同期做了一轮「MM processor 只吃 token ids,text 归 Renderer」的简化(#53093/#53275/#53372/#53385/#53560):BaseMultiModalProcessor.__call__prompt 参数从 str 收窄为 str | list[int](str 就地 encode),HF processor 调用变为纯 token-id 输入(_apply_hf_processor_main(prompt: list[int], ...)),删除 _apply_hf_processor_text_only 等文本旁路与 tok_kwargs 全链路透传;仅当 tokenizer/chat template 需要在 MM 处理前插入 placeholder 的模型才置 hf_processor_applies_updates=True。配套性能优化:cached_encodetruncation 参数并缓存重复 tokenizer 调用、placeholder/token-match 扫描命中时跳过冗余扫描。这与前端的 InputPreprocessor 移除是同一轮「text 处理收敛到 Renderer」重构的两侧。

部分模型(Qwen VL 等)支持 EVS(Efficient Video Sampling,高效视频采样):根据时间元数据(second_per_grid_tstokens_per_second)计算保留掩码,在送入 LLM 前丢弃冗余视频帧、直接减少 vision token 数,再按保留的嵌入重算 mrope 位置(multimodal/video_prune/evs.pyrecompute_mrope_positions,由 v1/worker/gpu/model_states/default.py 每步驱动),用更少的 token 表达同一段视频。

直传 Embedding(prompt_embeds)

prompt_embeds 是一种"直传模态"(无 grid_thw 元信息),允许直接传入预计算的多模态 embedding。在 M-RoPE 中其位置按文本位置处理,便于复用外部编码器输出。

编码器缓存

多模态编码器的计算开销很大。vLLM 实现了编码器缓存:

  • EncoderCacheManager:管理编码器输出的缓存
  • 缓存键:输入内容的哈希值
  • 缓存淘汰:LRU 或引用计数策略

多模态注册表

每个模型声明支持的多模态类型:

python
# 简化示意
@ModelRegistry.register("LlavaForConditionalGeneration")
@SupportsMultiModal
class LlavaForConditionalGeneration:
    supported_modalities = ["image"]

    def get_multimodal_processor(self):
        return LlavaProcessor()

新模型:OpenVLA

Vision-Language-Action 模型,用于机器人操控任务:

  • 使用融合 DINOv2 + SigLIP 视觉骨干(通过 timm
  • PrismaticProjector 进行多模态桥接
  • 动作 token 预测输出

新模型:Gemma4 Unified(encoder-free)

Gemma4 Unified 是无编码器(encoder-free)的多模态变体,不再使用 SigLIP 视觉塔与音频塔,而是将原始像素 patch 通过 Dense + LayerNorm 与因式分解 2D 位置嵌入直接投影到语言模型空间,音频也以原始波形直接投影 —— 挑战了"必须先经专门视觉/音频编码器"的传统流水线。

编码器 CUDA Graph

多模态编码器现在支持 CUDA Graph 加速:

  • SupportsEncoderCudaGraph 协议重构为 get_encoder_cudagraph_item_specs() 返回 EncoderItemSpec 对象
  • 支持 Step3VL 等模型的编码器 CUDA Graph 捕获(含 Step3-VL 的双路径 ViT CUDA Graph)
  • 覆盖模型持续扩大:Gemma3(SupportsEncoderCudaGraph,单路径 image-only 图,捕获 vision_tower → projector → flatten,budget 从 mm_tokens_per_imagemin(max_num_batched_tokens, max_model_len))、DeepSeek-OCR(双路径)、GLM-4.1V、InternVL、Kimi-VL、Mllama4、Qwen2/2.5/3-VL、Step3-VL、LFM2-VL 等均已支持
  • 新增 postprocess_encoder_output() 方法,默认行为为 scatter_output_slices
  • 底层 model_states 将原先硬编码的 WhisperModelState 泛化为 EncoderDecoderModelState,判定条件由"架构名匹配 Whisper/CohereAsr"改为"模型是否包含 CrossAttention 层",从而统一支持所有交叉注意力编码器-解码器模型(Whisper、CohereASR、NemotronParse 等)

多模态 GPU 显存准入(VRAM Semaphore)

当 API server(前端进程)在 GPU 上解码视频/图像(如硬件 NVDEC)时,解码出的帧缓冲会与引擎的权重、激活、KV cache 争抢同一块显存。MultiModalGPUMemoryPoolmultimodal/gpu_ipc_memory.py)在前端进程内以字节计数的信号量做准入控制 —— 解码路径在分配显存前 acquire(nbytes),预算不足则阻塞等待他人释放 lease。引擎侧通过 --mm-ipc-gpu-memory-gb 预先从 KV cache 预留对应容量(多个 API 进程共享一个引擎时按 total / api_process_count 分配),保证物理上有余量,避免解码把引擎显存挤爆。

特征融合策略

多模态特征与文本 embedding 的融合方式:

策略描述适用模型
Token 替换图像 token 替换 <image> 占位符LLaVA
Prefix 拼接图像特征拼接到文本前面Qwen-VL
交叉注意力图像特征作为交叉注意力的 K/VFlamingo
交错融合文本和图像 token 交错排列InternVL

多模态模型性能优化

批处理挑战

多模态输入导致 token 数量差异很大:

  • 纯文本请求:~100 tokens
  • 多图请求:~2000+ tokens(每张图可能几百个 token)

优化策略:

  • 动态批处理:根据总 token 数(文本 + 多模态)调整 batch size
  • 编码器预算管理:限制编码器计算的并发量
  • 异步编码:编码器计算与 decode 并行
  • 多模态项处理 O(n)→O(log n)EncoderCacheManager 原先每步遍历请求的所有多模态输入项判断缓存命中(O(n)),现维护 request_cached_ids 反向索引(request_id → 已缓存 input_id 集合),将 get_cached_input_ids() 降为 O(1) 字典查询,配合 bisect 定位使整体调度降为 O(log n) per step,显著改善高并发多模态吞吐
  • ASR 预处理多线程化:语音转文字(ASR)的 CPU 端预处理通过多线程化获得约 2.5x 实时率(RTFx)提升,缓解 mel spectrogram 等预处理在 CPU 端的瓶颈

多模态 token 计量

OpenAI 兼容接口的 usage.prompt_tokens_details 新增 multimodal_tokens 字段,按模态(image/audio/video)分项报告 prompt 中由多模态占位符贡献的 token 数,便于精确计量多模态请求成本。

相关概念