ViT Transformer 编码器


文档摘要

ViT Transformer 编码器 本节摘要:光 patch 看不见。12 层 pre-LN transformer 带 12 注意力头把 patch token 序列变成上下文 token 序列,CLS token 在其末隐状态池整图特征。本节是每个现代视觉语言模型的引擎室。patch 嵌入产 197 token 序列,每个是对其他 patch 无觉察的向量;一张猫的图要每 patch 知道哪些 patch 含胡须、哪些含背景、哪些含眼,transformer 是一注意力层一层建觉察的机制。

ViT Transformer 编码器

本节摘要:光 patch 看不见。12 层 pre-LN transformer 带 12 注意力头把 patch token 序列变成上下文 token 序列,CLS token 在其末隐状态池整图特征。本节是每个现代视觉语言模型的引擎室。patch 嵌入产 197 token 序列,每个是对其他 patch 无觉察的向量;一张猫的图要每 patch 知道哪些 patch 含胡须、哪些含背景、哪些含眼,transformer 是一注意力层一层建觉察的机制。标准配方是 12 块深、12 头宽、pre-LayerNorm 放置、GELU 激活、4 倍前馈扩展——这是 CLIP ViT-L、SigLIP、DINOv2、Qwen-VL 族、InternVL、2025-2026 每个开源视觉编码器的脊柱,稳到你可读那些论文并默认此块形状,除非它们另说。

对应原课程:Phase 19 · Lesson 59 · vit-transformer(原英文 phases/19-capstone-projects/59-vit-transformer/docs/en.md)。本节属「多模态/VLM」赛道第二节。

学习目标

阅读完本节,你应当能够:

  1. 实现 pre-LN transformer 块,含多头自注意力与前馈子层。
  2. 叠 12 块 12 头组 ViT-Base 编码器。
  3. 把第 56 节前端接进编码器并跑前向。
  4. 验证 CLS token 从每 patch 聚合信息。

一、问题与直觉

patch 嵌入产 197 token 序列,每个是对其他 patch 无觉察的向量。猫图要每 patch 知道哪些含胡须、哪些含背景、哪些含眼。transformer 是一注意力层一层建此觉察的机制。无它,patch 前端只是个聪明分词器无理解。标准配方 12 块深、12 头宽、pre-LN、GELU、4 倍前馈扩展,是每个现代视觉语言模型用的版本。

Pre-LN vs post-LN

原版 Transformer 把 LayerNorm 放残差后。Pre-LN(LayerNorm 在每子层前)是每个现代视觉语言模型用的版本,因它训练稳无需学习率预热技巧。差异在前向是一行,12+ 深度的梯度流是天壤之别。

多头自注意力

每头把 token 向量投到自己的 (query, key, value) 三元组,维 head_dim = hidden/num_headshidden=768heads=12 时每头 dim=64。12 头并行注意,输出拼回 768 维过输出投影。多头的点是一头可学「注意猫眼」另一头学「注意背景梯度」互不干扰。

为什么 4 倍前馈扩展

FFN 是 hidden -> 4*hidden -> hidden,中间 GELU。因子 4 是经验值,自 2017 跨语言与视觉 transformer 都成立。更小(2 倍)欠拟合;更大(8 倍)固定数据预算下过拟合。MLP 是模型存大部分学到事实的地方,更宽的中间是它们坐处。

组件 ViT-Base 规模参数
每块 qkv 投影 3*768*768 = 1.77M
每块输出投影 768*768 = 590K
每块 FFN(4 倍) 2*768*4*768 = 4.72M
每块 LayerNorm 4*768 = 3K
每块总 约 7.1M
12 块 约 85M
加前端 约 86M 总

ViT-Base 是 86M 参数编码器,2026 标准算小(SigLIP-So400M 400M,Qwen-VL ViT 675M),但架构至宽深相同。

因果掩码与否?

视觉 transformer 是纯编码器双向:token i 可注意任意 j。无掩码。第 59 节解码器侧交叉注意力会用因果掩码,但视觉编码器内注意力全连接。

CLS token 学什么

CLS token 起作学参数,无自己 patch 内容,跨每块经注意力累积信息。到末层,CLS 行是整图向量摘要;下游头把这单向量投成类 logits、对比嵌入、或文本解码器的交叉注意力键。

二、从零实现

code/main.py 实现:

  • MultiHeadSelfAttention:含 qkv 与输出投影、缩放点积注意力数学、形状断言。
  • FeedForward:4 倍扩展 GELU MLP。
  • Block:pre-LN 块组注意力与前馈子层带残差。
  • ViT:12 块栈加最终 LayerNorm。
  • VisionEncoder:把第 56 节 VisionFrontEnd 接到 ViT 栈,暴露 forward() 返上下文序列与池化 CLS 向量。
  • demo:跑合成 224x224 夹具图过全编码器,打输入形状、输出形状、参数量、每隔一层的 CLS 范数。
class Block(nn.Module): def __init__(self, hidden=768, heads=12, mlp_ratio=4): self.ln1 = nn.LayerNorm(hidden) self.attn = MultiHeadSelfAttention(hidden, heads) self.ln2 = nn.LayerNorm(hidden) self.mlp = FeedForward(hidden, hidden * mlp_ratio) # 4 倍扩展 def forward(self, x): h = self.ln1(x); x = x + self.attn(h) # pre-LN: LN 在子层前 h = self.ln2(x); x = x + self.mlp(h) # 残差加 return x class VisionEncoder(nn.Module): def __init__(self, img_size=224, patch=16, hidden=768, depth=12, heads=12): self.front = VisionFrontEnd(img_size, patch, hidden) # 第 56 节前端 self.blocks = nn.ModuleList([Block(hidden, heads) for _ in range(depth)]) self.norm = nn.LayerNorm(hidden) def forward(self, x): tokens = self.front(x) # (B, 197, 768) for blk in self.blocks: tokens = blk(tokens) tokens = self.norm(tokens) return tokens, tokens[:, 0] # 序列 + CLS 池化

code/test_main.py 覆盖:单块保形状且对输入批大小不变;注意力分数沿键轴和为一(softmax 健全);残差路径接好(零输入仍经 CLS token 产非零输出);4 层叠前向产对形状;梯度从 CLS 输出流到 patch 投影。

设计要点:pre-LN 的 x = x + sublayer(ln(x)) 是现代稳训练的关键——LN 在子层前使梯度经残差直接回传,12+ 深度无预热技巧。4 倍 FFN 扩展是经验最优,MLP 存模型大部分事实。CLS token 经跨块注意力从每 patch 聚合,末层成整图摘要。无因果掩码——视觉编码器全连接双向,与第 34/37 节因果 LM 不同。

三、框架对比

此处定义的编码器至宽深是 2025-2026 每个开源 VLM 内的同块栈。差异在:宽深(ViT-Large hidden=1024,depth=24,heads=16;SigLIP So400M hidden=1152,depth=27,heads=16,同块);池化头(CLS 池化本节 vs 平均池化 SigLIP vs 注意力池化后期 VLM);位置处理(固定正弦第 56 节 vs 学习 1D vs ALiBi vs 2D RoPE,块数学不变);register token(DINOv2 前置 4 个额外学 token,一行代码)。本节块栈是基底,第 58~61 节站其上。timm 的 VisionTransformer 是同实现可加载预训权重。本节手写让你看清 pre-LN、多头、4 倍 FFN、CLS 池化——这些在用 timm 时是黑盒。

四、可复用产物

code/main.py + code/test_main.py。demo 在 CPU 上几十秒跑完:夹具编码成 (1, 197, 768),CLS 范数随层组合向上漂、在最终 LayerNorm 稳定,总参数约 86M。VisionEncoder 是第 58~61 节的视觉底座,产 197 token 上下文序列 + CLS 池化向量供下游(投影层、交叉注意力)消费。

五、练习

  1. post-LN 对比:把 pre-LN 换 post-LN(LN 在残差后),在微型分类上比首 epoch 稳定性。
  2. 头数扫描:扫头数 4/8/12/16,观察参数量(不变)与表示力变化。
  3. FFN 扩展:扫 mlp_ratio 2/4/8,对比参数量与欠/过拟合。
  4. 平均池化:把 CLS 池化换平均池化(197 token 均值),对比下游分类精度。
  5. register token:前置 4 个额外学 token(DINOv2 风格),确认前向与梯度流。

本节要点回顾

  1. patch 不够:transformer 一注意力层一层建 patch 间觉察。
  2. pre-LN 稳训练:LN 在子层前,梯度经残差直传,12+ 深度无预热。
  3. 多头并行:每头投自己 qkv,一头学一模式互不干扰。
  4. 4 倍 FFN:经验最优,MLP 存大部分事实,更宽中间是事实坐处。
  5. 无因果掩码:视觉编码器全连接双向,与因果 LM 不同。
  6. CLS 池整图:学参数无 patch 内容,跨块注意力累积成整图摘要。

下一节,我们做「模态对齐投影层」——两层 MLP 把视觉 token 投进文本嵌入空间,余弦对齐损失拉近配对字幕。


发布者: 作者: Rohit Gupta 转发
评论区 (0)
U