
Transformer 全流程从输入到输出的张量形状变化用一个具体例子跟着一条数据走完 Transformer 的每一个节点。 每个节点都标注三件事形状怎么变、为什么这么变、这步在干嘛。 面向初级开发者看完能在脑子里跑通整个数据流。本文用的配置为了让数字具体、可对照我们钉死一套配置采用原始 Transformer base 的标准设置参数值含义B2batch size一次喂 2 句话n8每句 8 个 token词d_model512每个词向量的维度主干维度h8注意力头数d_k64每个头的维度 512 / 8d_ff2048前馈网络中间层维度 4 × 512V30000词表大小L6Encoder / Decoder 各堆叠的层数形状统一用[...]表示最左边一维永远是 batch。重要提醒本例中n 8和h 8数值碰巧相等。所以后面拆多头时你会看到一些形状数字长得一样比如[2, 8, 8, 64]但它们的维度含义完全不同——一个 8 是序列长度第几个词另一个 8 是第几个头。真实项目里这俩通常不相等比如 n128、h8到时候就不会混了。本文每次出现都会标清楚每一维代表什么。全流程总览图先看全貌再抠细节[2, 8] 输入token ID每个词的编号 ↓ 词嵌入 [2, 8, 512] 变成词向量 ↓ 加位置编码 [2, 8, 512] 注入谁前谁后的顺序信息 ↓ 进入 Encoder 层重复 6 次 [2, 8, 512] 算出 Q / K / V各一份 ↓ 拆成多头 [2, 8, 8, 64] 切成 8 个头 ↓ Q × Kᵀ [2, 8, 8, 8] ← 注意力分数表n×nO(n²) 复杂度的根源 ↓ 缩放 / 掩码 / softmax [2, 8, 8, 8] 变成注意力权重每行加起来1 ↓ × V [2, 8, 8, 64] 按权重融合所有词的内容 ↓ 合并多头 输出投影 [2, 8, 512] 回到主干维度 ↓ 残差 LayerNorm [2, 8, 512] ↓ FFN 升维 [2, 8, 2048] 对每个词单独深加工 ↓ FFN 降维 残差 LayerNorm [2, 8, 512] 一层 Encoder 结束 ↓ 重复 6 层 [2, 8, 512] Encoder 最终输出读懂了的表示 ↓ Decoder 用同样方式处理目标句多一个交叉注意力 [2, 8, 512] Decoder 输出 ↓ 投影到词表 [2, 8, 30000] 每个位置上、词表里 3 万个词各自的得分 ↓ softmax [2, 8, 30000] 每个位置的下一个词概率分布 → 最终输出下面逐个节点拆开讲。第一部分输入阶段节点 0原始输入token ID形状[2, 8]内容是整数例如[[101, 2769, 671, 3315, 741, 8043, 102, 0], [101, 3221, 1957, 102, 0, 0, 0, 0]]每一行是一句话每个数字是一个词的身份证号在词表里的编号。101/102是特殊标记句子开始 / 结束0通常是补位符[PAD]第二句短补齐到 8 个。这步的意义文字本身机器不认识必须先查字典把每个词换成编号。此时还只是整数不是向量。节点 1词嵌入Embedding 查表[2, 8] → [2, 8, 512]怎么变的有一张大小为[30000, 512]的嵌入表每个词对应一行 512 维向量。拿每个 ID 去表里取出对应那一行。[2, 8]个 ID每个换成 512 维向量就成了[2, 8, 512]。目的把没有含义的编号变成有语义的向量让意思相近的词如猫狗在向量空间里离得近。这是文字真正变成可计算向量的第一步。这张表是可训练的会随训练更新。节点 2加位置编码[2, 8, 512] 位置编码 [8, 512]自动广播到 2 个 batch → [2, 8, 512]目的注意力机制本身不分先后顺序狗咬人和人咬狗在它眼里词都一样。这一步给每个位置叠加一份号码牌信息让模型知道词的前后次序。形状不变只是把位置信息加进了向量里。第二部分一个 Encoder 层的内部全过程下面是一层Encoder 内部发生的事。输入是[2, 8, 512]记作X。这一整套节点 3~15后面要重复 6 次。节点 3算出 Q、K、VX [2, 8, 512] × W_Q [512, 512] → Q [2, 8, 512] X [2, 8, 512] × W_K [512, 512] → K [2, 8, 512] X [2, 8, 512] × W_V [512, 512] → V [2, 8, 512]目的用三个不同的权重矩阵把同一个输入投影成三种角色QQuery提问我想找什么样的词。KKey标签我是什么样的词。VValue内容我实际携带的信息。形状没变都是[2, 8, 512]但三份的角色含义分化了。注意 Q/K/V 都来自同一个 X所以叫自注意力自己关注自己。节点 4拆成多头Q [2, 8, 512] ↓ reshape把最后的 512 切成 8 个头 × 64 [2, 8, 8, 64] ← 含义是 [batch, 序列长度, 头数, 每头维度] ↓ transpose把头数维提到前面 [2, 8, 8, 64] ← 含义是 [batch, 头数, 序列长度, 每头维度]K、V 也做同样操作各变成[2, 8, 8, 64]。关键说明这两个[2, 8, 8, 64]数字长得一模一样但含义不同reshape 后[batch2, 序列长度8, 头数8, 每头维度64]transpose 后[batch2, 头数8, 序列长度8, 每头维度64]中间那两个8交换了位置。之所以看不出来纯粹是因为本例nh8。如果换成 n128、h8就会清楚地看到[2, 128, 8, 64]→[2, 8, 128, 64]。交换的目的是让头这一维排在前面这样每个头就能拿到一个独立的[序列长度8, 每头维度64]子矩阵各算各的。目的把 512 维切成 8 个 64 维的子空间让 8 个头各看一个角度、并行计算。这步不增加计算量只是把数据重新摆放。节点 5算注意力分数Q 和 K 两两握手Q [2, 8, 8, 64] × Kᵀ [2, 8, 64, 8] → scores [2, 8, 8, 8] └─┬─┘ 最后两维 8×8 是词与词的分数表Kᵀ 表示把 K 的最后两维转置[2, 8, 8, 64]→[2, 8, 64, 8]这样才能做矩阵乘法。目的让每个词的 Q去和所有词的 K 做点积得到两两相关性打分。盯住这个[2, 8, 8, 8]前两维[2, 8] batch 和 头。后两维[8, 8] 一张8 个词互相打分的表scores[i][j]表示第 i 个词对第 j 个词的关注分。这个n × n的分数表就是 Transformer复杂度 O(n²)的来源——词数翻倍这张表的面积翻 4 倍。这是长文本处理慢、费内存的根本原因。节点 6缩放scores / √64 → [2, 8, 8, 8]√64 8即把所有分数除以 8。目的分数如果数值太大下一步 softmax 会变得极端偏科只盯一个词、其余全忽略而且会导致模型学不动梯度趋近 0。除以√d_k把数值拉回合理范围让训练稳定。形状不变。节点 7加掩码可选scores mask → [2, 8, 8, 8]目的把不该被关注的位置分数加上一个极大的负数接近负无穷这样下一步 softmax 后它的权重就变成 0。Encoder 里主要是padding 掩码把补位的[PAD]节点 0 里那些0屏蔽掉不让真实的词去关注空位。形状不变。节点 8softmax 归一化softmax(对最后一维做) → [2, 8, 8, 8]目的把每一行的分数变成一组加起来等于 1 的概率。这样每个词就有了明确的我该把注意力按什么比例分给其他每个词。形状不变但数值含义从原始分数变成了注意力权重。节点 9用权重去加权 Vweights [2, 8, 8, 8] × V [2, 8, 8, 64] → [2, 8, 8, 64]目的按上一步算出的注意力比例把所有词的 V实际内容加权混合到一起。这是注意力的核心产出每个词都拿到了一个融合了全句相关信息的新表示。比如它这个词会把大部分权重给动物于是它的新表示里就揉进了动物的信息。节点 10合并多头[2, 8, 8, 64] 含义 [batch, 头数, 序列长度, 每头维度] ↓ transpose把序列长度维换回来 [2, 8, 8, 64] 含义 [batch, 序列长度, 头数, 每头维度] ↓ reshape把 8 个头 × 64 拼回 512 [2, 8, 512]目的把 8 个头各自的结果拼接回完整的 512 维。形状回到主干的[2, 8, 512]。这里又出现数字相同含义不同的情况原因同节点 4。节点 11输出投影[2, 8, 512] × W_O [512, 512] → [2, 8, 512]目的8 个头的结果只是简单拼在一起这一步用一个矩阵让它们互相融合、交流一下得到更协调的表示。形状不变。节点 12残差连接 层归一化LayerNorm( X节点3的输入 节点11的输出 ) → [2, 8, 512]目的残差连接X ...相当于修一条近路让原始输入可以直接跳过注意力传下去。这样信息不会在深层网络里丢失梯度也能顺畅回传。⭐ 注意残差是逐元素相加所以前后形状必须严格一致——这就是为什么主干要死守[2, 8, 512]。LayerNorm层归一化像个稳压器把数值拉回稳定范围避免忽大忽小。形状不变。注意力子层到此结束。节点 13前馈网络第一层升维[2, 8, 512] × W1 [512, 2048] → [2, 8, 2048] → 经过 ReLU 激活目的把维度撑大 4 倍512 → 2048给模型更大的非线性加工空间。注意前馈网络是对每个词单独处理的同一句话里每个词过的是同一个网络但彼此不交互。分工是注意力节点 3~12负责词与词之间交流信息。前馈网络节点 13~14负责对每个词单独做深加工。节点 14前馈网络第二层降维[2, 8, 2048] × W2 [2048, 512] → [2, 8, 512]目的把维度压回 512以便和主干对齐为了下一步残差相加。节点 15残差 层归一化LayerNorm( 节点12的输出 节点14的输出 ) → [2, 8, 512]目的同节点 12接好近路、稳住数值。这一层 Encoder 到此全部结束输出[2, 8, 512]喂给下一层。第三部分堆叠 6 层节点 3 ~ 15 这一整套重复 6 次。 全程形状始终保持 [2, 8, 512]。目的每一层都让词与词再交流一轮、再深加工一轮层层叠加提炼出越来越深的语义。Encoder 最终输出[2, 8, 512]—— 这就是模型对这两句话彻底读懂后的表示。如果你做的是 BERT只有 Encoder到这里主体就结束了。后面接一个简单的任务头即可。例如做句子分类取 [CLS] 位置的向量 [2, 512] × 分类层 [512, 类别数] → [2, 类别数][CLS]是句首特殊标记它的输出向量被当作整句话的代表。第四部分Decoder 阶段完整 Transformer 才有比如做翻译如果是翻译这类读完源句、再生成目标句的任务就需要 Decoder。假设目标句也是 8 个词先经过自己的嵌入 位置编码得到[2, 8, 512]。Decoder 的每一层比 Encoder多一个环节交叉注意力共三个子层节点 16带掩码的自注意力和节点 3~12 几乎一样形状走向 [2, 8, 512] → ... → [2, 8, 512]区别在掩码这里用的是上三角掩码look-ahead mask把每个词右边未来的词全部屏蔽。目的生成是一个词一个词往外写的写第 3 个词时不能偷看第 4、5……个词。这个上三角掩码就是单向、只能看左边的物理实现也是 GPT 类模型单向的本质。节点 17交叉注意力连接 EncoderQ 来自 Decoder 自己 [2, 8, 512] K、V 来自 Encoder 的输出 [2, 8, 512] ↓ 和普通注意力一样计算 注意力分数 [2, 8, 8, 8] → 输出 [2, 8, 512]目的让正在生成的每个词回头去参考源句Encoder 读懂的结果。翻译时所谓看着原文写译文就发生在这一步。这是 self-attention自注意力和 cross-attention交叉注意力的关键区别自注意力的 Q/K/V 来自同一句话交叉注意力的 Q 来自一句、K/V 来自另一句。节点 18前馈 残差 LayerNorm[2, 8, 512]和 Encoder 的节点 13~15 完全一样。Decoder 这一层到此结束同样堆叠 6 层最终输出[2, 8, 512]。第五部分输出阶段节点 19投影到词表[2, 8, 512] × W_vocab [512, 30000] → [2, 8, 30000]目的把每个位置的 512 维向量映射成词表里 30000 个词各自的得分logits。现在每个位置都有一个长度 30000 的得分向量。节点 20softmax → 最终输出softmax(对最后一维做) → [2, 8, 30000]目的把得分变成概率。现在每个位置都有一个下一个词最可能是谁的概率分布。取概率最大的那个词就是模型在这个位置的输出。这就是 Transformer 的最终产出。整个流程里主干上的形状永远是[2, 8, 512]batch × 序列长度 × 模型维度。它只在两个地方临时变形注意力内部拆成多头时变成四维[2, 8, 8, 64]算分数时出现[2, 8, 8, 8]的词对词分数表。前馈网络内部临时升维到[2, 8, 2048]。但每个子层一结束一定变回[2, 8, 512]。为什么必须变回去因为每个子层都有残差连接输入 子层输出而相加要求两者形状严格一致。抓住主干恒为[2, 8, 512]、中间临时变形、结束必复原这条主线整个 Transformer 的数据流就不会乱。节点速查节点操作输入形状输出形状一句话目的0原始输入—[2, 8]文字变成词编号1词嵌入[2, 8][2, 8, 512]编号变成语义向量2加位置编码[2, 8, 512][2, 8, 512]注入词序信息3算 Q/K/V[2, 8, 512][2, 8, 512]×3分化成提问/标签/内容三角色4拆多头[2, 8, 512][2, 8, 8, 64]切成 8 个角度并行5Q×Kᵀ[2, 8, 8, 64][2, 8, 8, 8]算词与词的相关性分数6缩放[2, 8, 8, 8][2, 8, 8, 8]防止数值过大、稳训练7加掩码[2, 8, 8, 8][2, 8, 8, 8]屏蔽不该看的位置8softmax[2, 8, 8, 8][2, 8, 8, 8]分数变成概率权重9权重×V[2, 8, 8, 8][2, 8, 8, 64]按权重融合内容10合并多头[2, 8, 8, 64][2, 8, 512]8 个头拼回完整维度11输出投影[2, 8, 512][2, 8, 512]让各头信息融合12残差LN[2, 8, 512][2, 8, 512]接近路、稳数值13FFN 升维[2, 8, 512][2, 8, 2048]扩大加工空间14FFN 降维[2, 8, 2048][2, 8, 512]压回主干维度15残差LN[2, 8, 512][2, 8, 512]一层结束3~15重复 6 层[2, 8, 512][2, 8, 512]层层提炼语义19投影到词表[2, 8, 512][2, 8, 30000]算每个词的得分20softmax[2, 8, 30000][2, 8, 30000]得分变成下一词概率