【大模型】对齐机制(上):MLP vs Q-Former,视觉特征到底怎么塞进 LLM?|多模态大模型专栏④

📅 2026/7/9 7:02:19 👁️ 阅读次数 📝 编程学习
【大模型】对齐机制(上):MLP vs Q-Former,视觉特征到底怎么塞进 LLM?|多模态大模型专栏④

对齐机制(上):MLP vs Q-Former,视觉特征到底怎么塞进 LLM?|多模态大模型专栏④

一句话讲透本篇:视觉编码器输出(N, d_v)的特征,LLM 需要d_h维的 token——中间这个"翻译官"即 Projector。其设计取向,直接决定一个 VLM 属于主流范式还是早期方案。


写在前面

前三篇 已依次讲完:架构全景 → CLIP 如何训练视觉编码器 → 张量如何一路流动。由此可知,视觉特征最终都要"塞进" LLM 的文本流。

但有个关键问题一直被我们悬置了:视觉编码器输出是(N, d_v)(比如 SigLIP 的729 × 1152),而 LLM 的每个 token 必须是d_h维(比如 Qwen2.5 的3584)。这个维度差的桥,到底怎么搭?

这就是对齐机制(alignment)——VLM 区别于纯 LLM 的灵魂。这部分内容很厚(Projector 设计、M-RoPE、动态分辨率),塞进一篇会又长又散,所以我拆成上下两篇:

  • 本篇(上):Projector 的设计选择(MLP vs Q-Former)+ 训练侧 token layout 与 loss mask
  • 下篇:M-RoPE 与动态分辨率(视觉 token 如何"知道"自己在图里的位置)

阅读本篇后可获得:

  • ✅ 讲清 MLP Projector 和 Q-Former 的本质区别,知道为什么前者一统江湖
  • ✅ 理解为什么文档/OCR 任务绝不能用 Q-Former(呼应本专栏的文档领域定位)
  • ✅ 动手构建 VLM 训练的三个并行数组(input_ids / attention_mask / labels)
  • ✅ 跑通一份零依赖的 token layout demo,避开手写训练 loop 的头号 silent bug

一、核心命题:维度对齐问题

给定两个东西:

  • 视觉编码器输出:V ∈ R^{N_v × d_v}(如 SigLIP-SO400M:729 × 1152
  • LLM 期望输入:每个 token 都是d_h维(如 Qwen2.5-7B:3584

问题:怎么把V变成 LLM 能消化的东西?约束有三条:

  1. 维度必须对齐d_v → d_h
  2. 数量可保留可压缩:直接喂N_v个,或压成更少
  3. 选哪种,取决于训练目标、数据、任务

这三条约束,历史上演化出了三种方案。


二、三种 Projector 方案演进

方案 A:MLP Projector(LLaVA 范式)—— 当今主流

V (N_v, d_v) → Linear(d_v → d_h) → GELU → Linear(d_h → d_h) → 输出 (N_v, d_h)

核心特征:只改维度,不改数量729个视觉 token 一个不丢地映射成d_h维,全送进 LLM。用 2 层 MLP 是 LLaVA-1.5 起的标准做法。

方案 B:Q-Former(BLIP-2 范式)—— 早期方案

learnable queries Q ∈ R^{32 × d_h} → CrossAttention(Q, V) → 32 个 token

用一组可学习的 query,通过 cross-attention 从729个视觉特征里"提取"出固定32个 token。强行压缩729 → 32

方案 C:Perceiver Resampler(Flamingo 范式)

和 Q-Former 类似,也是 cross-attention 压缩,结构略有不同。PaliGemma-2 等仍在用变体。

一张图看懂核心区别

Q-Former · BLIP-2(强压缩)

MLP Projector · LLaVA(不压缩)

2 层 MLP

CrossAttn
32 个 query

视觉特征
(729, 1152)

(729, 3584)
token 数不变

视觉特征
(729, 1152)

(32, 3584)
压缩 23×

进 LLM:729 + 文本 ≈ 1000+ token
OCR/细节 ✓ 强

进 LLM:32 + 文本 ≈ 100 token
OCR/细节 ✗ 差

完整对比:

维度MLP Projector(LLaVA)Q-Former(BLIP-2)
视觉 token 进 LLM 数729(不压缩)32(压缩 23×)
LLM 序列总长729 + 文本 ≈ 1000+32 + 文本 ≈ 100
attention 显存O(729²),高O(32²),低
信息保真度无损有损(瓶颈严重)
OCR / 文档 / 细粒度✅ 强❌ 致命差
通用对话✅(32 token 够用)
训练复杂度1 阶段(只训 projector)多阶段(先 Q-Former 再 LLM)

一句话总结:MLP 不压缩(729→729),Q-Former 强压缩(729→32)。

历史趋势

2022:Q-Former 当道(BLIP-2、MiniGPT-4) 2023 起:MLP Projector 一统江湖(LLaVA-1.5、Qwen-VL、InternVL)

结论:先吃透 MLP Projector,Q-Former 当作历史了解。现代主流 VLM 清一色 MLP 路线。


三、为什么文档 VLM 绝不能用 Q-Former

这点对本专栏的文档领域定位尤其关键,值得单独说。

32个 token 想表达729个 patch 的信息,相当于每个 token 要"代表" 23 个 patch。在文档/票据任务里,一个数字往往就占好几个 patch——32个 token 根本装不下一张发票的所有数字。

一张发票:日期、金额、税号、商品明细……每个数字都是独立 patch MLP:729 token,每个数字有专属载体 → 能准确读出 "12,345.67" Q-Former:32 token,23 个 patch 挤一个 → 数字必然被丢、串行错乱

正因如此,LLaVA-1.5 之后的文档向 VLM(Qwen-VL、InternVL、Donut 类)全面抛弃 Q-Former、转向 MLP。面向文档理解任务时有一条铁律:Projector 不得压缩,信息无损是底线

💡 Q-Former 的价值场景:少样本对话、视觉问答这种"不需要精确细节"的任务,32 个 token 够概括图像大意。但一旦要精确 OCR,它就力不从心。


四、LLaVA:现代范式的奠基

LLaVA(Visual Instruction Tuning, NeurIPS 2023)是当今所有主流 VLM 的参考实现。它的架构极简:

Image → CLIP-ViT-L/14 (frozen) → (576, 1024) features → MLP Projector (2-layer, trainable) → (576, 4096) → concat with text embeddings → Vicuna-7B/13B

LLaVA-1.5-7B 的三组件参数(来自实际模型):

组件参数量训练状态
Vision Tower(CLIP-ViT-L)~304M🔒 frozen
Projector(2 层 MLP)~8M🔥trainable(Stage 1 唯一)
LLM(Vicuna-7B)~6738M🔒 frozen → ⚡ LoRA

注意Projector 只有 8M 参数,相比 LLM 的 6.7B 微不足道——这正是它能用大学习率(1e-3)、在几小时内训完的原因。

它的 Projector 实现简单到令人发指(code/02_alignment/02_projector_train.py):

classProjector(nn.Module):"""2-layer MLP projector, exactly LLaVA-1.5's design."""def__init__(self,d_v:int,d_h:int):super().__init__()self.mlp=nn.Sequential(nn.Linear(d_v,d_h),# 1024 -> 4096nn.GELU(),nn.Linear(d_h,d_h),# 4096 -> 4096)defforward(self,x):returnself.mlp(x)

没错,LLaVA-1.5 区别于纯 LLM 的"灵魂组件",就是这么一个两层 MLP。换到 SigLIP-SO400M(1152)→ Qwen2.5-7B(3584)的场景,Projector 参数约1152·3584 + 3584·3584 ≈ 17M,依然很小。

两阶段训练(关键)

阶段训什么数据目的
1. Pretrain Projector只训 MLP558K 图文对学"把视觉特征翻译到 LLM 维度"
2. Instruction Tuningprojector + LLM (LoRA)150K GPT-4V 指令数据学指令对齐、视觉问答

这就是 LLM 训练里没有的"第一阶段"(详见第①篇的三阶段矩阵)。Stage 1 的目标极其纯粹:让随机初始化的 Projector,在冻结的视觉编码器和冻结的 LLM 之间,学会"翻译"。


五、训练侧的 token layout(最容易踩坑的地方)

到这里架构清楚了。但真要自己写 VLM 训练 loop,绝大多数人死在 token 序列的构建上。这是 NLP 工程师转 VLM 的头号坑。

核心认知:图像不是 1 个特殊 token,而是 N 个 token 组成的子序列。训练时要构建三个并行数组。我们跑 demo 看真实过程(code/02_alignment/03_token_layout_demo.py,零依赖可跑)。

Step 1-2:<image>占位符如何展开

用户的 prompt 是What is in this image?,chat template 会插入一个<image>占位符:

prompt_ids | len= 8 | 1 IMG 10 11 12 13 14 15 ↑ <image> 占位符(1 个 token)

然后这个IMG占位符展开成 N 个 slot(这里用 N=9 演示,真实 SigLIP@384 是 729,Qwen2-VL@672 是 2304):

expanded | len= 16 | 1 IMG IMG IMG IMG IMG IMG IMG IMG IMG 10 11 12 13 14 15 └──────────── 9 个视觉 slot ────────────┘

注意:展开后是9 个相同 id 的 slot,不是 9 个不同 id。因为 LLM 不会真的用nn.Embedding去查这些 id,框架会拦截它们、替换成 Projector 输出的真实视觉 embedding。id 只是"位置标记"

Step 3:三个并行数组(训练的灵魂)

把 assistant 的回答拼接上,构建训练三元组。真实输出:

input_ids | len= 24 | 1 IMG IMG IMG IMG IMG IMG IMG IMG IMG 10 11 12 13 14 15 20 21 22 23 24 25 26 2 attention_mask | len= 24 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 labels (X=-100) | len= 24 | X X X X X X X X X X X X X X X X 20 21 22 23 24 25 26 2

读这三行,就懂了 VLM 训练的全部 label 策略:

位置label含义
视觉 token(9 个 IMG)-100不算 loss——它们是输入不是目标
prompt 文本(10..15-100不算 loss——只学怎么"回答",不学怎么"提问"
answer token(20..26, 2真实 id只在这里算 next-token loss
pad-100同时 mask 掉 loss 和 attention

label=-100

label=-100

label=真实 id

label=-100, attn=0

视觉 token
×9

❌ 不算 loss

prompt 文本
What is in this image?

answer 文本
A cat sitting on a table.

✅ 算 loss

pad token

❌ 不算 loss 不参与 attn

Step 4:每个训练者都该做的断言

demo 末尾自动做了 sanity check:

total sequence length : 24 visual tokens : 9 (expected N_VISUAL=9) prompt text tokens : 7 answer tokens : 8 (these compute loss) tokens with loss : 8 (must equal answer length) All assertions passed.

tokens with loss必须严格等于 answer 长度——这一条断言能挡住一大半手写训练 loop 的 bug。

三个最容易写错的坑(亲历血泪)

  1. slot 找错:找<image>位置必须定位第一个;多图场景要循环找出所有位置依次替换。
  2. visual_emb 行顺序错乱visual_emb[i]必须严格对应图像 row-major 第 i 个 patch。如果 dataloader 做了transpose/flip,patch 顺序就乱了——模型直接学不动,loss 不收敛但不报错(头号 silent bug)。
  3. attention_mask 没扩展:拼接后序列变长,attention_mask 也要 pad 出对应长度的 1,否则 LLM 把视觉 token 当 pad 忽略掉。

📌配图说明:上面的三数组对齐输出和 mermaid label 策略图,是本篇最该收藏的两张图。终端输出截图在 CSDN 很吃香,建议把 demo 跑一遍截真实图。


六、4 个关键点

  1. Projector 只做一件事:d_v → d_h的维度翻译,MLP 不压缩、Q-Former 强压缩,这是两派分水岭。
  2. MLP 一统江湖的原因是信息无损——尤其文档/OCR 任务,压缩就是丢数字。
  3. Projector 参数极小(几 M ~ 二十 M),能用大学习率快速训完,是 Stage 1 的唯一主角。
  4. 训练 label 的核心规则:只在 answer token 上算 loss,视觉和 prompt 全-100visual_emb行顺序必须匹配 patch row-major 顺序,否则静默失败。

📎 本篇涉及代码

文件说明
code/02_alignment/02_projector_train.pyLLaVA Projector 实现 + Stage 1 训练(仅训 Projector)
code/02_alignment/01_minimal_llava.pyLLaVA 架构检视:三组件 + 参数量
code/02_alignment/03_token_layout_demo.pytoken layout 与 label mask 构建演示

小结

概念一句话
对齐问题把视觉特征(N, d_v)翻译成 LLM 要的d_h
MLP Projector2 层 MLP,不压缩(729→729),主流
Q-Formercross-attn 压缩(729→32),文档任务致命
LLaVA Stage 1只训 8M 的 Projector,学"翻译"
label 策略只在 answer 算 loss,视觉/prompt 全-100

下一篇是对齐机制(下):视觉 token 怎么"知道"自己在图里的位置?我们会从 1D-RoPE 推到M-RoPE(Qwen2-VL 的核心创新),并讲清动态分辨率(AnyRes Tile vs Native Resolution + Bucket)。配套一个零依赖的 4 段 demo,能亲眼验证"M-RoPE 在文本上严格等于 1D-RoPE"。关注专栏,更新第一时间通知。

专栏:《从 NLP 到 VLM:多模态大模型研发实战》
上一篇:③ 张量级 walkthrough | 本篇:④ 对齐机制(上)| 下一篇:⑤ 对齐机制(下)· M-RoPE 与动态分辨率