本文是Transformer架构梳理的第一篇文章。
内容为经典Encoder-Decoder架构的可视化拆解,它本身只是我的个人笔记
Transformer是一台翻译机
Transformer架构登场于Google的Attention is all you need论文里,是Machine Learning领域的一个革新点,它的创作初衷是更高效地解决机器翻译Seq2seq(Sequence to Sequence)问题。
所以,我将Transformer视作翻译机。 请看下面的迷你架构图:
图中,原先的句子先是被encoder处理成中间态的语义上下文,decoder再据上下文,逐字(自回归)地生成一个结果句。
整个过程是一个翻译行为。要理解经典Transformer之前,代表着我们需要理解翻译行为本身。
翻译的难点
物理世界里,翻译行为可以分为两步:
- 译者收到原信息,结合自身知识理解上下文,并暂存消化后上下文
- 译者根据上下文陈述
Transformer也是这样子做的。Encoder对应第一步,Decoder对应第二步。 不过, 机器过程没有人类逻辑复杂,我们早已习惯翻译,胆子大点,来理一理机器过程吧。
看起来只有两个步骤,而难点在于——要怎么实现呢?
Encoder - 理解的实现
Encoder是这样做的。例,源句为Iloveyourdog:
嵌入(Embedding)
嵌入分为几步:
- Tokenization
- Embedding
- Position Embedding
原 Sequence(语句)经过Tokenization被拆解为多个片段,成为Token(词元)序列(不同算法拆分结果不一,但为了保持示例简洁,下文的所有示例token都采用整词拆解形式)。设拆解个数为 n,表示为:
X=(x1,x2,…,xn)
上述过程中,Iloveyourdog首先被拆解为(I,love,your,dog)
然后,对每个token进行数学建模。 比如第一个token 可以写成: x1=[0.12,−0.31,0.77,…]∈Rdmodel
把整句堆起来后,就得到输入矩阵: X∈Rn×dmodel
假设我们的模型训练维度为6维,每个 token 就被转化成 6 维向量表示:
X=0.120.0−0.20.05−0.50.880.1−0.11.00.10.950.00.01.2−0.10.80.45−0.31.10.3−0.10.50.01.5(I)(love)(your)(dog)
几何视角上,这就是 4 个漂浮在 6 维空间里的点。它们现在是“孤立”的, 只有词义,没有句义:I 只是I,love 只是love,dog 也还只是词典意义上的dog。 如何让这4个词互相分享信息,各自影响(表现为向量修正), 这是下一步Attention要做的事情。
Position Embedding 位置编码
前面只完成了原词义向量化任务,第三步还需要在结果向量中携带每个token位于源句的位置信息。 这里边会用到位置编码,有了位置信息,才能确保语句上下文自注意力有效。这里不展开。
1) Self-Attention: 自注意力机制
Attention 解决的是:让表示矩阵中的每一行都完成它自身的语义补充。流程如下:
数学表示见:
Attention(Q,K,V)=softmax(dkQK⊤)V
式子中参数虽多,但从函数视角看是非常清晰的。
我们逐个展开。
Attention过程涉及大量的矩阵运算,如果你对此一无所知,我推荐你先查看矩阵的几何直观.
Q、K、V
我们的输入是X,而式子中,被Attention函数包裹的是QKV, 应该好奇,X和QKV的关系是什么?
实际上,QKV代表的, 正是模型训练后习得的权重经验从三个角度对源句矩阵X进行投影所得的具有特殊目的矩阵——权重矩阵。
权重矩阵通常称为 W,像一份被反复打磨过的"舞台剧本":
句子X是一出戏剧,每个 token 都是戏剧中的一个角色,角色们一开始只知道自己的角色内容,整出戏是否完美(语义是否完美),要靠它和其余演员充分对戏(Attention)。
到了 Self-Attention 这里,这份“剧本W”分成三部分:WQ,WK,WV。它们不是输入句子的一部分,而是模型的参数。输入表示矩阵 X 每进入一层,都会按这三组参数生成对应的 Q,K,V 矩阵。
模型之初,W内部只是一个装着一堆随机数的大矩阵。这个初始矩阵不太可能实现良好的最终seq2seq输出,但经过训练,初始的权重随机数将不断优化而得到更加有效的新矩阵。
实现上,先把当前表示矩阵 X 投影成三组矩阵:
Q=XWQ,K=XWK,V=XWV
如果只看矩阵中的第 i 行,它们代表着:
- qi:这一行表示“我在找什么”(和我对戏的有哪些角色)
- ki:这一行表示“我如何被匹配”(我的角色是什么)
- vi:这一行表示“我能贡献什么内容”(我的戏份是什么)
X一次性和W发生运算。它等价于每一行分别生成自己的 qi,ki,vi:
qi=xiWQ,ki=xiWK,vi=xiWV
注:权重矩阵W虽然分为WQ,WK,WV三个部分,但在实际工程中表现成一个大矩阵,在编码中通过切片进行运算获取对应的QKV矩阵。 而之所以这样做,是为了充分发挥GPU大矩阵乘法的优势
Q、K、V 生成例子
假设当前输入矩阵 X 是 4×6, 同时拟三尺寸先沟通的WQ,WK,WV。注意:这些数值只是为了演示矩阵链路,不代表真实模型训练出来的参数。
权重矩阵的训练时机并在谷歌Attention is all you need论文的某个环节里,它的内容足够单开文章,这里暂不进行展开。
三组权重矩阵可以一起写成:
WQ=100.5001010.500.5000010.51∈R6×3
WK=0.60.00.40.10.00.50.10.80.30.00.70.00.00.20.10.90.40.8∈R6×3
WV=0.90.00.20.00.10.00.00.80.10.20.00.10.10.00.90.00.30.00.00.20.00.80.10.40.20.00.30.10.90.00.00.10.00.40.00.9∈R6×6
于是整句一次性相乘,得到三组结果矩阵:
Q=XWQ=0.5200.5500.2751.5500.2250.7801.1250.0500.1251.5500.4502.450(I)(love)(your)(dog)
K=XWK=0.4220.4100.2500.8600.2270.5241.1150.1350.1001.5460.4652.020(I)(love)(your)(dog)
V=XWV=0.353−0.0100.1200.075−0.3101.0040.1550.2301.0470.0001.1650.095−0.0951.3060.0501.2500.729−0.1201.2250.360−0.1401.018−0.0301.660(I)(love)(your)(dog)
同一个 X被投影到了三种不同的权重矩阵里, 而这三个矩阵构成Attention的逻辑核心。
注1: Q,K,V 都是整句话共同组成的矩阵,而不是 1×n 的临时向量。后面之所以能抽出 qlove 或 kI,只是因为它们分别是 Q,K 矩阵中的某一行。
注2: 这里让 WV 输出 6 维,是为了让softmax结果可以直接回到主模型维度d_model; 真实工程中也常见先输出较短的 dv,最后在额外用 WO 投回 dmodel。
Q·K^T (点积)
获得三个矩阵后,Attention(拍戏)过程的第一步是QK矩阵运算,但特别说明,形式为QK˙T,对K矩阵进行倒置, 因为6X3和3X6才是合法的矩阵运算, 结果我们暂称S。公式为:
S=QK⊤∈Rn×n
这里第 i 行表示:
- “第 i 个 token 表示行在看谁”,
- 第 j 列表示“它看第 j 个 token 表示行的分数”
这里拿 love 看 I 做一次完整展开。为了避免跳读,先把完整 Q,K 再摆出来:
Q=0.5200.5500.2751.5500.2250.7801.1250.0500.1251.5500.4502.450(I)(love)(your)(dog)
K=0.4220.4100.2500.8600.2270.5241.1150.1350.1001.5460.4652.020(I)(love)(your)(dog)
于是:
qlove=Qlove,:=[0.5500.7801.550]
kI=KI,:=[0.4220.2270.100]
它们的关联度(需要对戏的程度)得分为: Scorelove,I=qlovekI⊤=[0.550.781.55]0.4220.2270.100=0.55×0.422+0.78×0.227+1.55×0.100=0.564
这里发生的是两个向量的点积,结果形式为数值。
该数值反映的是对词love而言词i的相关性,它意味着在最终翻译love时,应该从i中获取补充的程度。 用拍戏比喻,它所代表的是为了完美演出该剧本,角色love的戏份里应与角色i的对戏占据多少:
由于我们的模型d_model维度很小,这个点积结果可以直观成下面的雷达图, 其中,重合度反映相关度。
不要忘了,我们只是取了其中一对token进行了举例,而实际运算是整个矩阵单次运算。
这是Attention的第一步,该阶段的产物,可以说是每个角色都知道了剧本中和其它角色的对戏关系。
Scaled - dk与softmax
得到 scores 后,每一行会先除以 dk 做缩放,再过softmax:
不过,为了更好讲明白这两个内容,需要先解释softmax的工作:
经过softmax,原先相似度结果矩阵的每个值sij将从score(相似得分)变成score_rate(得分占比),含义在于对Tokeni而言,其全部的注意力中所拿出来关注Tokenj的注意力占比。
其经典的数学实现为:
softmax(xi)=∑j=1nexjexi,i=1,2,…,n
此时,对矩阵里任意Tokeni(x_i)所持行向量而言,向量每个值变成占比,得分越高占比越大,总和为1。可以将该过程视为注意力分配。
在softmax公式中,可见其依赖mathrme,这意味着,如果存在极大极小的两极情况,会导致全部比重聚焦在极大区域。
而dk就是来解决这个问题的,它位置称为缩放因子。缩放因子的用意在于对Q⋅KT做范围控制,控制结果矩阵的方差在一个区间避免两极情况。
缩放因子为什么是dk而不是别的?Transformer原论文中之所以出现这个值源自一个假设,既矩阵Q,K内的向量q,k满足均值为0,方差为1的特征,它们点击后结果方差为dk。softmax要处理的数据,理想方差为1,而能够使得缩放后结果方差为1的缩放因子值便是dk。
而这个假设源自机器学习行业的常规认知,它并非唯一解。如果感兴趣,可以查看下面两篇文章
苏剑林-浅谈Transformer的初始化、参数化与标准化
苏剑林-从熵不变性看Attention的Scale操作
V
通过上一步我们得到了一个比例矩阵,它代表着每个Token都知道在最终原句语义生成时,向其它token获取语义补充的权重。 拍戏比喻下,这意味着每个角色都知道在权重矩阵W剧本下,为了完美演绎,角色应该和哪些其它同事进行不同程度的对戏。
然而光是知道对戏还不够,还需要知道的是——对方的戏份。而这便是权重矩阵V的位置。
整体过程为:
这就是输出矩阵 O 的第 i 行,也就是第 i 个 token 表示行吸收全场信息后的新表示。
比如某一层里,love 对全句的注意力比例可能是:
alove=[0.30,0.45,0.10,0.15]
那么它的新表示就是:
olove=0.30vI+0.45vlove+0.10vyour+0.15vdog
这一步就是“向量修正”。原本 love 所在的坐标点,会吸收 I,your,dog 等 token 的信息。于是它不再只是词典里那个孤立的动词,而变成了“由 I 发出、指向某个对象的 love”。 其中,I参与了love的词义补正,这个过程有术语叫"Attend To"。 这也符合我们的拍戏比喻,角色I"Attend To"了角色love的戏份。
归纳
总览全程,Self-Attention过程分为:
- 用 WQ,WK,WV 把表示矩阵投影成三种视角
- 用 QK⊤ 得到 token 表示行之间的对戏强度
- 用 softmax 把强度变成关注比例
- 用这些比例混合 V,完成每个 token 的向量修正
也就是说,每一层 Attention 都在让词向量发生一次空间位移。层数堆起来后,每个 token 的向量里都会逐渐带上全句的影子。 读者可以在此处再次脑补一下我们的拍戏比喻。
多头注意力
Multi-Head Attention并不复杂。它只是同一群演员换了排练方式,从一种角度更换成多种角度(Head):
- 有的头关注语法依赖,
- 有的头关注指代关系,
- 有的头关注语义搭配。
如果输入仍记作 X∈Rn×dmodel,第 i 个 head 会拥有自己的一组投影矩阵:
QiKiVi=XWiQ,WiQ∈Rdmodel×dk=XWiK,WiK∈Rdmodel×dk=XWiV,WiV∈Rdmodel×dv
所以第 i 个 head 的输出就是:
headi=Attention(Qi,Ki,Vi)=Attention(XWiQ,XWiK,XWiV)
多个 head 的结果拼接后,再通过 WO 投回主模型维度,继续交给后续层处理:
MultiHead(Q,K,V)WO=Concat(head1,head2,…,headh)WO∈Rhdv×dmodel
其中,dk=dv=dmodel/h,每个 head 只看一小段子空间;h 个 head 拼接起来后,维度又回到 dmodel。 读者可以自行推演Q,K,V矩阵尺寸与模型维度dmodel的关系。
拿我们上面的例子来看,过程图为:
注意: 多头意味着Attention所依赖的权重矩阵应有多个, 并且每个头对应的Wi都是独立的权重参数。
2) Add & Norm
Self-Attention 已经把每个 token 的向量修正过一次。但直接把这个修正结果传下去,深层训练很容易出问题:数值可能越漂越大,原始词义也可能慢慢丢失。
所以 Transformer结构中每一层都加入一个稳定组件:
X′=LayerNorm(X+SelfAttention(X))
它分为两步:
- Add:把 Self-Attention 的输出和原始输入 X 相加,保留原始信号。
- LayerNorm:对相加后的结果做归一化,把数值压回稳定范围。
FFN 之后还会再来一次:
Xnext=LayerNorm(X′+FFN(X′))
也就是说,Attention 和 FFN 各自前后都有一次 Add & Norm。它们共同保证信息的稳定传递。
Add - 残差连接
假设经过 Self-Attention 后,token love 的向量从:
xlove=[0.4,0.5,0.6]
被修正成:
SelfAttention(xlove)=[2.1,−1.0,0.3]
如果没有残差连接,下一层收到的就是 [2.1, -1.0, 0.3]。这个向量已经和 love 的原始向量 [0.4, 0.5, 0.6] 没多大关系了。如果每层都这样改,6 层(论文中Encoder Layer的层数)之后,模型可能完全忘记 love 原本是什么意思。
加上残差连接后: xlove′=xlove+SelfAttention(xlove)=[0.4,0.5,0.6]+[2.1,−1.0,0.3]=[2.5,−0.5,0.9]
虽然数值变了,但 [2.5, -0.5, 0.9] 这个向量仍然包含原始 [0.4, 0.5, 0.6] 的成分。原始信号没有被丢弃,而是作为“底子”保留了下来。
用拍戏比喻来说:Attention 让每个演员重新理解了自己的戏份,但Add让演员不会忘记自己“原本是谁”。
除了保留信息,Add 还有另一个重要作用: 深度学习算法中是通过反向传播机制来调整模型参数实现训练的,直接依赖于梯度,通过梯度指导模型参数优化。 梯度的计算是从深层(靠近输出)向浅层(靠近输入)不断链式累乘运算,如果不做任何处理,会出现梯度在前面就趋向于零(梯度消失),这样,浅层的参数将无法得到有效训练, 而通过Add,在运算时就能够确保梯度稳定到达浅层。
核心就在于残差连接的数学形式:y = x + f(x) 的导数里天然带一个 +1(单位矩阵)。展开一下:
- 没有 Add 时
假设网络的一层是:
y=f(x)
其中 f 可能包含 Attention、FFN、LayerNorm 等。
反向传播求梯度:
∂x∂L=∂y∂L⋅∂x∂y=∂y∂L⋅f′(x)
如果网络很深(仍以6层为例):
x6=f6(f5(f4(f3(f2(f1(x))))))
那么梯度从 Loss 传回 x1 时要连乘:
∂x1∂L=∂x6∂L⋅f6′(x5)⋅f5′(x4)⋅f4′(x3)⋅f3′(x2)⋅f2′(x1)⋅f1′(x)
如果每个 fi′(x) 的范数都小于 1(比如 0.6),那么 6 层乘起来:
0.66≈0.047
越往浅层,梯度越小,参数更新越慢,这就是梯度消失。
- 有 Add 时
残差连接变成:
y=x+f(x)
导数变成:
∂x∂y=I+f′(x)
其中 I 是单位矩阵。
反向传播:
∂x∂L=∂y∂L⋅∂x∂y=∂y∂L⋅(I+f′(x))
即使 f′(x) 很小,梯度里仍然保留了一个完整的 ∂y∂L 项。
Layer Normalization 标准/归一化
如果没有 Norm,残差相加后的数值会一层一层累加。例如,假设每层的修正量都差不多:
[0.4,0.5,0.6]+[2.1,−1.0,0.3]=[2.5,−0.5,0.9]
第二层继续加:
[2.5,−0.5,0.9]+[2.1,−1.0,0.3]=[4.6,−1.5,1.2]
到第六层时,向量可能已经变成:
[13.0,−5.5,2.4]
数值越来越大,分布也越来越不稳定。后面的层拿到这种输入,训练会变得非常困难。
那么,LayerNorm 的作用来了:对每个 token 向量做归一化,把均值压到 0、方差压到 1,再通过可学习的 γ 和 β 微调:
LayerNorm(x)=γ⋅σ2+εx−μ+β
在原论文的 Transformer 里,Add 和 Norm 是 Post-Norm 形式:先加,再归一化。现代很多大模型(如 GPT、LLaMA)则采用 Pre-Norm:
Xnext=X+FFN(LayerNorm(X))
3) FFN:MLP is still what you need
Attention is all you need 这篇论文的名字只强调了 Attention,但 Transformer 的真正核心其实是 Attention + FFN。
- Attention 解决“token 之间如何交换信息”。
- FFN 解决“每个 token 拿到信息之后,如何在自己内部重新加工”。
如果把每个 token 看作一个演员,那么 Attention 是让演员之间对戏,FFN 则是让演员在对完戏后,自己消化自己的剧本进行后续发挥。
FFN 的公式是:
FFN(x)=max(0,xW1+b1)W2+b2
其中 max(0,…) 是 ReLU 激活,现代模型也常用 GELU。
Attention + FFN?
Attention 的输出本质上是:
Attention Output=j∑aijvj
也就是说,每个位置的新向量是其他位置 Value 向量的加权平均。它仍然是输入空间里的线性组合,只是权重不同。
如果只有 Attention,不管堆多少层,模型学到的东西都会受到限制:它只能重新分配已有的信息,不能创造新的、更抽象的特征。
FFN 的作用就是给模型加入非线性变换能力,让模型掌握类如“如果存在某特征,就增强;如果不存在,就抑制”的能力。
FFN是单层共用的:
对第 i 个 token:
xi′→FFN(xi′) 对第 j 个 token:
xj′→FFN(xj′) 两者使用同一套参数 W1,b1,W2,b2。
升维、激活、降维
FFN 过程为升维 -> 激活 -> 降维:
dmodel→dff→dmodel
原论文里 dff=4×dmodel。比如 dmodel=512 时,FFN 中间维度就是 2048。
这样的好处是:
- 升维:把语义展开到更高维空间,让原本纠缠在一起的特征更容易分开处理。
- 激活:用非线性函数把线性不可分的问题变成可分的问题。
- 降维:把结果压回 dmodel,保证下一层 Encoder 能继续接下去。
Key-Value 记忆
除了非线性变换,FFN 还承担了一个重要角色:存储知识。
近年来很多可解释性研究认为,FFN 可以被理解为一种 Key-Value 记忆。
- W1 负责把输入向量匹配到某些“键”(特定神经元)。
- W2 负责输出这些神经元对应的“值”(语义补充)。
每个隐藏层神经元就像一个知识条目。当输入向量匹配某个条目时,这个神经元被激活,然后通过 W2 把相关知识写进残差流。
比如输入中出现 Michael Jordan:
- 它的向量经过 W1 投影。
- 某些神经元被激活,这些神经元在训练过程中学会了“Michael Jordan 相关模式”。
- 被激活的神经元通过 W2 输出一段方向,对应“篮球运动员”、“NBA”、“芝加哥公牛”等语义。
- 这段输出通过残差连接加到原始向量上。
所以,Michael Jordan 是篮球运动员这个信息,不在输入 token 里,而在 FFN 的权重里。
因为FFN层承载如此多的信息,所以一个 Transformer 模型的主要参数不在 Attention,而在 FFN:
FFN 每层参数量≈2×dmodel×dff
如果 dff=4×dmodel,那么 FFN 参数就是 Attention 参数的数倍。
所以“把模型做大”很大程度上就是扩大 FFN 的隐藏层维度。FFN 越大,能存储和处理的知识条目就越多,模型能力通常也越强。
和 Attention 后面一样,FFN 输出之后也要做一次AddNorm:
Xnext=LayerNorm(X′+FFN(X′))
到此,一个层内的流程跑完。输出结果将传递到下一层,如果已经是Encoder堆的最后一层,Encoder 输出最终上下文表示: Z=(z1,z2,…,zn)
姑且把 Z 叫作 memory(Attention is all you need 论文本身没有命名它,但多数实现代码中叫 memory),作为模型“已理解后的语义记忆”,随后交给 Decoder 去完成陈述与生成。
Decoder:陈述
Encoder 把源句读成 memory Z 后,Decoder 负责根据这份记忆,逐词生成目标句。
它不是一次性输出整句,而是自回归地一个 token 一个 token 生成:
<BOS>→我→爱→你的→狗
<BOS> 是 begin of sentence,
<EOS> 是 end of sentence。
生成 <EOS> 时,代表整句翻译完成。
Decoder 的输入不是源句X,而是目标端已经生成的部分,经过 Embedding 和 Position Embedding 后得到矩阵 Y。它的处理 流程如下:
Decoder中特别地,有两个Attention层。
Masked Self-Attention
Decoder 的第一层 Attention 和 Encoder 很像,但多了一个 mask:生成第 t 个词时,不能看第 t+1 个及以后的位置。否则训练时它会直接抄答案。
Decoder有两层Attention,它们各自拥有不同的权重矩阵W。
公式上只在 softmax 前加一个 mask 矩阵 M:
MaskedAttention(QY,KY,VY)=softmax(dkQYKY⊤+M)VY
允许看的位置填 0,未来位置填 −∞。这样 softmax 后,未来位置的概率就变成 0。
目标端有 4 个位置时,mask长这样: M=0000−∞000−∞−∞00−∞−∞−∞0 通过置入极小值,屏蔽后续值干扰
Cross-Attention
Cross-Attention 的 Cross 来自于它跨越两个序列:Decoder 的当前序列和 Encoder 的源序列。
它和Self-Attention的关键区别:Q 通过Masked传入,K,V 来自 Encoder memory:
Q=Y′WQ,K=ZWK,V=ZWV
这里 Y′ 是 Masked Self-Attention 的输出,Z 是 Encoder 最终输出的 memory。
含义是:Decoder 当前位置通过 Query 去问,“原文里哪些位置和我现在要翻译的内容相关?”然后取走对应 Value 里的语义。
比如生成“狗”时,Decoder 的 Cross-Attention 会强烈关注 Encoder memory 中对应 dog 的那一行;生成“你的”时,则更关注 your。这就是翻译中的“对齐”。
例 - 两个时刻
我们用源句 I love your dog,挑选两个自回归时刻做演示:
<BOS>→我→爱→你的→狗
时刻一:生成“我”
Decoder 输入只有 <BOS>。经过 Masked Self-Attention 后,它知道“句子刚开始”。Cross-Attention 的 Query 带着这个状态去问原文 memory:
“源句开头是什么?该先翻译什么?”
原文 memory 中对应 I 的响应最强,所以 Decoder 输出 “我”。
时刻二:生成“狗”
此前 Decoder 已经生成 <BOS> 我 爱 你的。Masked Self-Attention 看到这段前缀,形成状态 Y′:接下来需要接一个名词。Cross-Attention 的 Query 带着这个状态去问原文:
“我已经说了‘我 爱 你的’,接下来需要名词,原文里哪个词该被翻译?”
原文 memory 中对应 dog 的响应最强,所以 Decoder 输出 “狗”。
每个时刻生成的 token 都会被追加回目标序列,继续下一轮。
Add & Norm
Decoder 层内有两处 Attention,一样的,后面都接 Add & Norm,最后再过 FFN:
Y′=LayerNorm(Y+MaskedSelfAttention(Y)) Y′′=LayerNorm(Y′+CrossAttention(Y′,Z)) Ynext=LayerNorm(Y′′+FFN(Y′′))
这里不再展开。
完成这一步,当前层的decoder任务就算完成了,下一步和encoder是一样的。如果当前层不是最后一层,则继续往下一层encoder层传递,反之作为最后一层的输出进入到下一步。
Linear + Softmax
Decoder 最后一层输出的是一个矩阵。在自回归生成中,我们只需要最后一个位置的向量 ot 来决定下一个词。但 ot 还在内部语义空间 Rdmodel 里,要变成词表上的概率,需要两步。
第一步:Linear 投影
通过一个线性层,把 ot 映射到词表维度:
logits=otWvocab+b
Rdmodel→R∣V∣
Wvocab 的每一行对应词表里的一个词。投影后,每个维度代表一个词的“分数”,分数越高,说明这个词越适合作为下一个 token。
例如词表里有 5 万个词,logits 就是一个 5 万维向量。假设其中“狗”的分数是 4.2,“猫”是 1.5,“人”是 0.3,模型当前最倾向于生成“狗”。
第二步:Softmax
Softmax 把 logits 转成合法的概率分布,所有词的概率之和为 1:
pt=softmax(logits)
接上面的例子: P(狗)=0.62,P(猫)=0.11,P(人)=0.04
最后,模型根据这个概率分布选择下一个 token, 这里选词方案也存在多种,论文中使用的是beam search,这里不展开。 选出的 token 会被追加回目标序列,再送入 Decoder 继续下一轮。直到生成 <EOS>,整句翻译结束。 到这里,经典 Encoder-Decoder Transformer 的主线就闭合了:Encoder 负责把源句读成 memory,Decoder 负责根据 memory 一步步说出目标句。
结语
到此,我们已经领略了经典Transformer架构全程。这个典型结构为了解决机器翻译seq2seq问题而提出,先入为主地我们将其比作了翻译机,然后从encoder-decoder到内层,逐层拆解,从最初的分词到memory的全句语义构建,再到decoder进行逐字输出译文, 完成地说明了每个层,每个组件的工作机制及用意。
掌握了翻译机结构后,我们才能走向目前炙手可热的大模型知识领域。