02|一次Transformer前向:逐张量、逐算子讲明白
本章目录
本章预备知识
本章所有矩阵式子都由两件事组成:把一些数乘上不同系数再相加,以及重新排列这些数。注意力先找匹配分数,再变成总和为1的份额,最后按份额混合内容。
本章各题列出所需基础。可先完成相应基础课,也可通过自检确认掌握程度。
进入深读前的小检查:先手算q=[1,0]与两行K的分数,再用V=[[10,0],[0,20]]算结果。不要把Q、K、V当三种独立输入文本。
术语带虚线时可点击速查。公式中的字母先看定义;不要用猜缩写替代推导。
本章各张表的来源与去向
首先把参数、函数和中间结果分开。下面的名称在本章局部公式中使用;同一字母在其他公式里须重新按定义读。
| 名字 | 对象身份 | 由谁产生,交给谁 |
|---|---|---|
| X、U、R、Y | 输入或临时结果数组 | X是当前层输入;U/R/Y是相应等式算出后给结果起的名字,不是额外参数。U在不同段落可能被重新定义。 |
| Wq、Wk、Wv、Wo、W1、W2 | 模型的系数表 | 加载训练好的参数;作用到各位置,生成新的表示。F11逐格演示Q/K/V。 |
| Norm、Attention、FFN | 可调用的运算或子网络 | 接收一个数组并返回数组;函数内部是否含参数由结构定义。 |
| S、A、O、M | 分数、份额、输出及读取约束 | S由Q/K比较,A由softmax,O由A混合V;M由读取规则构造。 |
| B、T、d、H、Dh | 尺寸整数 | 批大小、位置数、隐藏宽度、头数、单头宽度;不是五份输入文本。 |
| I、γ、β、ε | 特例矩阵、规范化参数与稳定常数 | I保持乘法前后值不变;γ/β可学习;ε是设置的小正数。不能把W=I的演示当作真实模型通则。 |
本章以教学用的decoder-only、pre-norm结构为主。2017年原论文是encoder-decoder架构;RMSNorm、RoPE与SwiGLU是后来常见的选择,不能把这些组合说成原论文或所有商业模型的统一实现。全章的形状和数值用于说明算法,不是实际服务配置。
沿着对象与状态理解本章
本章可以分成两种动作:同一位置内部改变特征,与不同位置之间混合信息。X乘参数表属于前者,注意力份额乘V属于后者。先在下图指出“输入数据”“参数”“本次结果”,再读多层公式,避免把W与Q/K/V当成同一种东西。
下方图解的固定输入在“输入与适用条件”中声明。它用于解释本章机制,不与本章其他独立例子的数值暗中接续。
Q/K/V:先算从哪读,再算读进多少
Query、Key、Value各自是什么数,为什么分三条路径?
计算某个位置的新表示时,它是接收者;所有可见位置都是潜在信息提供者。Query与Key决定各提供者的读取份额,Value提供实际被混合的数。可以借“提问—匹配—取内容”记住分工,但Q不是自然语言问题,K不是数据库主键,V不是正确答案。
输入与适用条件:沿用F11正文的独立两位置算例:X=[[1,1],[1,−1]]。本图省略偏置、RoPE、归一化和多头。所有Q/K/V和输出都由配套程序计算。
1 / 5 · 同一输入,三条参数路径
窄屏可左右滑动图形,或点击“放大图形”。文字解释紧随图后。
先不打分。两行输入分别代表“我”和“爱”;Wq、Wk、Wv是三张乘法系数表。它们不是另外三份文字。
本步状态由图形与说明共同给出。默认静止。用“下一步”看完解释再继续;播放到最后一步自动停止。键盘聚焦播放器后,可用左右键逐步查看、空格播放/暂停。图中颜色辅助区分,含义同时用文字注明。
为什么需要
让匹配特征与被传递内容分开学习,给模型不同的变换能力。
怎样实现
先分别投影每行,再跨位置打分、屏蔽、softmax、混合V。
可能的代价与误解
“注意力最大”不直接证明模型最终回答的因果原因;若只有一个可见Key,softmax永远为1,不能用来展示竞争。
如何判断是否做对
核对六行投影、两项分数、份额和输出;改变Wv不应在本层直接改变由Q/K算出的份额。
Q005|一串Token进入Transformer后,究竟依次经过哪些计算?
核心回答
对每一层,先把当前隐藏状态归一化,再投影出Q/K/V;加入位置机制和可见性约束,计算注意力并混合Value;经输出投影后加回残差;再经过归一化与前馈网络,加第二次残差。重复各层后做最终归一化与词表投影,最后位置的logits用于预测下一Token。[TR][ROPE][GQA]
把每个张量的身份固定下来
设B=1,T=4,d=8,H=2,Dh=4,暂用MHA。X的形状是[1,4,8],4个位置各有8个数。注意:一个位置是一个Token,不一定是一个单词。
| 顺序 | 运算 | 形状 | 这一步实际改变什么 |
|---|---|---|---|
| 1 | U=Norm(X) |
[1,4,8] |
控制每个位置的特征尺度,不混合不同位置 |
| 2 | Q=U Wq,K=U Wk,V=U Wv |
各[1,4,8] |
同一输入映射成匹配、被匹配、被读取三种子空间 |
| 3 | 拆头并换轴 | 各[1,2,4,4] |
2个头,每头处理4个位置、4维特征 |
| 4 | 对Q/K施加位置变换 | 同上 | 让相关性包含位置关系 |
| 5 | S=QKᵀ/√Dh+M |
[1,2,4,4] |
每个查询位置对每个可见键位置评分 |
| 6 | 沿最后一维softmax | [1,2,4,4] |
每个查询的一行评分变成读取权重 |
| 7 | O=A V |
[1,2,4,4] |
按每行权重加权历史Value |
| 8 | 合头、Wo投影、残差 |
[1,4,8] |
把各头结果融合回主隐藏空间 |
| 9 | Norm→FFN→残差 | [1,4,8] |
对每个位置做非线性特征变换 |
第5行的两个“4”意义不同:一个是查询长度,一个是键长度;缓存解码时它们会变成1与历史总长度,不能因为训练时恰好相等就写死。第6行softmax必须在键位置轴上,这决定当前Token从哪些位置读信息。沿头轴softmax虽然可能形状合法,算法却已经被换掉。
一层结束后,输出是下一层的输入。下一层重新用自己的Wq/Wk/Wv投影,不会复用上一层的K/V。层数增加的意义不是把同一搜索重复若干次,而是在已经融合上下文的表示上进一步变换。最终词表投影一般不是每个头各自采样一个词,而是先融合整层表示,再对整个词表评分。
一个两位置单层Decoder,从输入一直算到词表概率
输入与条件。采用教学pre-norm Decoder:X=[[1,0],[0,2]];LayerNorm的ε=1e−5,γ=1、β=0;Wq=Wk=Wv=Wo=I;因果注意力;FFN(z)=0.5×ReLU(z);最后再LayerNorm并乘词表头 [[1,0,-1],[0,1,0.5]]。不使用RoPE和bias。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1输入归一化 | 逐行Norm,不跨位置 | 约 [[0.999980,−0.999980],[−0.999995,0.999995]]。 |
| 2生成Q/K/V | 本例投影是单位矩阵 | 三者都等于归一化输入;这是为方便计算而选的特例。 |
| 3算因果注意力 | 第1位置只能看自己,第2位置可看两处 | 第2行权重约 [0.055810,0.944190],加权输出约 [−0.888377,0.888377]。 |
| 4第一次残差 | 把注意力输出加回原X | 第2位置变 [−0.888377,2.888377]。 |
| 5Norm与FFN | 第2位置归一化约 [−0.999999,0.999999] | ReLU与0.5缩放后约 [0,0.499999],再加残差得 [−0.888377,3.388376]。 |
| 6生成分数 | 最后Norm后乘词表头 | logits约 [−0.999999,0.999999,1.499998],概率约 [0.048611,0.359188,0.592201]。 |
把过程连起来。模型内部每一个“层名”都对应一份实际新数组。词表概率出现在最后投影之后,而不是Attention权重直接变成词。
边界与反例。全部数值来自随包程序同一条前向链,展示值经舍入;这是未训练微型结构,不是任何商业模型的架构或输出。
依据与适用范围:[SDPA]。示例输入和场景由本书明确设定。
追问:为什么prefill通常需要计算所有输入位置,而不是只算最后一个?
最后位置在第2层要读取前面位置的第2层K/V;这些K/V来自前面位置经过第1层后的隐藏状态。因此不能从原始Embedding直接跨过所有历史位置的中间层。因果mask使历史位置相互依赖有方向,但不消除它们作为后续层输入的必要性。实现可省略不需要的中间logits输出,不能普遍省掉建立历史表示的工作。
追问:Attention输出已经混合信息,为什么还要Wo?
每个头在自己的子空间给出结果,拼接只把它们并排放置,Wo使模型能学习如何跨头线性组合并映射回残差宽度。没有Wo并非数学上不可能构造模型,但表达结构改变,需要重新训练评价,不能在现有模型中随意删除。
追问:怎么判断自己真的理解了这条链?
给出B,T,H,Dh后能写出所有形状,能指出softmax的轴、转置的轴、残差加法发生处,以及最后哪个位置预测下一个Token。仅能背“Embedding—Attention—MLP”还不足以调试真实实现。
Q006|Q、K、V为什么要分成三份?Attention到底算出什么?
核心回答
Q与K共同决定“某位置如何从可见位置取信息”,V决定“取到的内容是什么”。它们来自可学习投影,语义不是人为固定的“问题、关键词、答案”。Attention输出是Value的加权混合,不是直接生成词,也不是数据库精确查询。[TR]
用一个可以手算的例子走一遍
令三个位置的输入为x1=[1,0]、x2=[0,1]、x3=[1,1],只看一头并暂不做归一化和位置旋转。设Wq=Wk=I,Wv=[[1,2],[3,4]],则:
q3=[1,1]
k1=[1,0] k2=[0,1] k3=[1,1]
v1=[1,2] v2=[3,4] v3=[4,6]
第三位置能够看到三个位置。三个点积是1、1、2,除以√2后为0.7071、0.7071、1.4142。softmax得到约0.248255、0.248255、0.503490。于是第三位置输出是:
o3 = 0.248255×[1,2] + 0.248255×[3,4] + 0.503490×[4,6]
≈ [3.006980, 4.510470]
权重在三个位置上求和为1,但输出不是某个原始Value,而是混合结果。这解释了为什么不能简单把最大权重对应的Token称为“唯一原因”。残差、其他头、后续前馈和层间变化也都影响最终输出。
若把Wv改掉而不动Q/K,读取比例可以不变,被读取的内容却变了;若只改变Q,当前查询会以不同方式分配读取比例。Q/K/V分开允许匹配空间和内容空间具有不同用途。把三者强行共享会降低参数并改变归纳偏置,不等于与标准结构完全等价。
第一位置只看自己,权重应为[1,0,0];第二位置只看前两个。这个例子也展示因果mask与内容相关性是两道不同机制:相关性高但在未来的Token仍必须被屏蔽。
只改Value,与只改Query,分别让什么改变
输入与条件。取q=[1,0],k0=[1,0]、k1=[0,1],原v0=[10,0]、v1=[0,20],宽度2。原注意力份额约 [0.669762,0.330238]。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1保持Q/K | 把两个Value都乘2 | 注意力份额不变,输出从约 [6.697615,6.604769] 变为 [13.395231,13.209538]。 |
| 2恢复Value | 把q改成 [0,1] | 点积从 [1,0] 变 [0,1]。 |
| 3重新softmax | 新的份额交换 | 约 [0.330238,0.669762]。 |
| 4计算新输出 | 原Value按新份额混合 | 约 [3.302385,13.395231]。 |
| 5意义对照 | Value改内容,Q/K改读取分配 | 两条路径影响不同,不能把V解释成K的另一个名字。 |
把过程连起来。这个对照实验比只背Q查询、K键、V值更能说明三者分工。
边界与反例。Q/K/V是人为给定,分量没有固定人类语义;此例不是余弦检索。
依据与适用范围:[SDPA]。示例输入和场景由本书明确设定。
追问:Self-Attention中的“self”是只看自己吗?
不是。它表示Q与K/V来自同一序列或同一表示来源;序列中不同位置仍可互相读取,范围由mask决定。Cross-Attention中Q通常来自一个序列,K/V来自另一个,例如解码器读取编码器输出。decoder-only聊天模型不需要每层都有Cross-Attention。
追问:Value为何不直接用原始X?
直接用X是可设计的变体,但可学习的V投影让模型选择适合被聚合的内容表示,也能改变每头宽度。现有权重依赖训练时的结构,不能把V投影删除而期待同样结果。类似地,K不是固定索引表:每个位置的K依赖该层的上下文表示。
追问:Attention是否就是“语义相似度”?
它是模型学出来的匹配函数,可能表达位置复制、语法依赖、模式匹配等,不一定符合人类语义相似度。点积还受向量尺度影响;它通常也不是对外部文档Embedding做余弦检索。把两者都叫检索有类比价值,但不能混用算法和评价指标。
Q007|为什么除以√d?为什么softmax?数值稳定为什么必须减最大值?
核心回答
在一个常用初始化近似下,d维点积的方差随d增长;除以√d使尺度不随维度线性膨胀,降低softmax过早饱和的风险。softmax把分数转换成非负、和为1的权重。减最大值保持数学分布不变,却显著降低指数溢出风险。[TR]
从方差推导缩放,不要只背结论
设qi,ki独立、均值0、方差1,且各维独立,则E[qi ki]=0,Var(qi ki)=1。点积S=Σi qi ki有Var(S)=d,标准差为√d。所以S/√d的方差为1。真实训练后的向量不严格满足这些条件,但这解释了缩放的设计动机;它不是对每个样本方差的精确估计。
若分数变成[100,0,-100],softmax几乎全部质量集中在第一项。softmax导数包含pi(1-pi)和-pi pj,当p极端时很多梯度变得很小。模型在训练初期就过度尖锐,会使学习困难。缩放是稳定训练的一部分,不替代归一化、初始化、学习率等其他因素。
对任意常数c,有exp(zi-c)/Σj exp(zj-c)=exp(zi)/Σj exp(zj)。取c=max(z)后所有指数不超过1,避免exp(1000)溢出。这里不是“为了让概率更平滑”,因为分布在精确数学下完全不变。它是同一公式的数值稳定实现。
一次稳定softmax计算,加一个全屏蔽反例
输入与条件。分数是 [1002,1001,1000]。常见浮点环境直接计算exp(1002)会溢出,但目标概率本身并不巨大。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1减最大值 | 变 [0,−1,−2] | 所有指数≤1。 |
| 2求概率 | 与 [2,1,0] 得到完全相同的数学分布 | 约 [0.665241,0.244728,0.090031]。 |
| 3解释不变量 | 给全部分数加同一常数 | 分子分母同乘exp(c),比例不变。 |
| 4错误输入 | 若mask后变 [−∞,−∞] | 不存在可选项,不能再进行普通归一化。 |
| 5程序处理 | 教学函数对全非有限值显式报错 | 防止NaN悄悄进入下一层。 |
把过程连起来。稳定实现改变算式的数值路径,不改变目标分布;它不能修复没有任何合法候选的语义错误。
边界与反例。每个生产内核对全mask行的约定可能不同,调用者应显式确认,而不是依赖本例异常策略。
依据与适用范围:[SDPA]。示例输入和场景由本书明确设定。
追问:全mask行为什么容易出现NaN?
若所有分数都是-∞,最大值也是-∞,相减出现-∞-(-∞),没有有效归一化分布。正确实现要事先避免这样的查询,或约定输出零、跳过padding位置等行为。不能指望“使用稳定softmax”自动解决未定义输入。核的具体约定要与上层mask逻辑一致。
追问:为什么不直接用argmax取一个Value?
硬选择会改变模型表达和梯度性质;softmax提供可微的软混合,让多个位置共同贡献并能通过损失更新匹配参数。训练后出现尖锐或分散权重都可能有用途。某些稀疏或路由模型确实使用不同选择机制,但需要单独设计,不能直接替换已训练网络的注意力。
追问:Attention的softmax与生成时softmax是同一个东西吗?
是相同类型的数学归一化,作用轴与含义不同。Attention在“可见键位置”上归一化,得到信息读取权重;输出softmax在“词表Token”上归一化,得到下一Token概率。前者不直接决定输出哪个词,后者才参与解码采样。
Q008|为什么一层除了Attention还需要残差、Norm和FFN?
核心回答
Attention负责位置间的信息交互,FFN负责逐位置非线性变换,残差提供直接的信息与梯度通路,Norm控制特征尺度。它们解决的问题不同,不能把FFN称为可有可无的装饰,也不能把残差说成保证训练永不失败的机制。[TR][V3]
把pre-norm的一层写成两条等式
U = X + Attention(Norm1(X))
Y = U + FFN(Norm2(U))
在这个结构里,残差主路中的X没有被直接替换为注意力输出,模型学习的是在现有表示上增加什么。反向传播时,∂U/∂X包含恒等项I,梯度可以沿较直接路径传回;这缓解深层优化困难,但整体梯度仍受到各分支Jacobian、训练配置和数值状态影响。
LayerNorm对每个位置的特征减均值,再除标准差并应用可学习缩放/平移。RMSNorm通常不减均值,而用均方根缩放并应用可学习权重。它们不是跨整个batch统计;所以这里的Norm不同于图像网络中常见的BatchNorm。pre-norm先归一化再进入子层,post-norm则在残差相加后归一化,优化行为不同。
普通两层FFN可以写为σ(XW1+b1)W2+b2:先升维,再非线性,再降回d。SwiGLU形式可写为(SiLU(XWg) ⊙ XWu)Wd,乘法门控控制哪些特征通过。它对每个位置使用同一组权重,但不直接读取其他位置;其他位置的信息先通过Attention进入该位置表示,再由FFN加工。
参数量也能看出FFN不是小附件。忽略bias,MHA四个d×d投影约4d²参数;若普通FFN中间维度为4d,两次投影约8d²。SwiGLU若中间维度m,则三矩阵约3dm。具体现代模型可能用不同m、GQA或MoE,但“Attention一定占最多参数/算力”不能当通则。
把一个残差分支删掉,形状没变但函数已改变
输入与条件。令x=[2,−1],分支F(x)=[0.5,0.25]。比较正确残差 y=x+F(x) 与错误覆盖 y=F(x)。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1正确前向 | 逐格相加 | y=[2.5,−0.75]。 |
| 2错误前向 | 直接用分支覆盖 | y=[0.5,0.25];形状仍是[2],不会自动报维度错。 |
| 3反向看差异 | 正确导数是I+J_F,错误只有J_F | 恒等通路已经被删。 |
| 4规范化反例 | 对x增加未经训练的新Norm | 数值分布与原架构不同,不能以“更稳定”为由直接加。 |
| 5验证方式 | 固定输入对比逐层中间值 | 先发现首个偏离,而不是只判断最后句子流不流畅。 |
把过程连起来。结构正确性不能仅靠shape检查。相同形状的张量可以代表完全不同的函数。
边界与反例。分支输出是教学给定;不推断所有残差架构有同样数值,也不把残差视为训练稳定性的绝对保证。
依据与适用范围:[DERIVE]。示例输入和场景由本书明确设定。
追问:FFN是否就是存事实的数据库?
有研究将部分前馈行为解释为键值关联,但不能把所有知识精确定位为一条条数据库记录。事实复现依赖Embedding、注意力、前馈、层间路径和上下文共同作用。把某个神经元当作唯一事实开关,是对分布式表示的过度简化。
追问:Norm是不是越多越稳定?
不是随意增加就好。新增归一化改变信息尺度、残差结构和训练分布,可能破坏已有权重行为。对已训练模型,首先使用原架构;研究变体时需要训练与评测。工程调参也应先定位NaN出现在何处,而不是见到NaN就在所有位置添加Norm。
追问:一层坏了通常表现成什么?
softmax轴错可能没有异常但输出失真;残差误用覆盖会改变网络;FFN维度转置可能直接报shape错误;低精度溢出可能传播NaN。排障从单算子形状、有限值、固定输入对照开始,再比较整层与整模型。训练loss下降只能说明某个目标在被优化,不能证明实现符合预期架构。