前四篇把注意力讲完了:Q、K、V 怎么打分,几个头怎么分工,位置怎么写进向量。打开原论文的结构图,注意力旁边还有几个方框,一个写着 Add & Norm,一个写着 Feed Forward。它们就是这一篇要讲的三个零件:残差连接、层归一化和前馈网络。注意力负责让词和词交换信息,这三个零件让这件事能一层一层叠下去。

想先看个大概,可以先播放下面的动画讲解,两分钟把整条线过一遍,再往下读正文。

动画讲解残差、层归一化和前馈网络,两分钟看一遍14 步 · 约 2 分钟 · 可以随时暂停、跳步
1 / 14

前四篇讲完了注意力。原论文的结构图里,注意力旁边还有 Add & Norm 和 Feed Forward 两个方框,也就是残差连接、层归一化和前馈网络。

朗读用的是浏览器自带的语音,不同设备声音不一样;没有语音的浏览器只显示字幕。

一、注意力的输出交给谁

还是那句「小明把书借给了小红」,看「借」这个词。它进入一层时是一个向量,示意里写成三个数 [0.8, -0.3, 0.5],真实模型里有几百到上万个数。注意力拿「借」的 Q 和每个词的 K 打分,用 softmax 变成百分比,再按百分比把各个词的 V 加起来。加出来的是一个新向量,比如 [0.2, 0.6, -0.1]。它就是注意力对「借」的输出,里面装的是从「小明」「书」「小红」那里拿来的信息。

把这一层的计算记作 f,「借」进来的向量记作 x,注意力的输出就是 f(x)。这个新向量要交给下一层,当作下一层里「借」的向量。最直接的做法是用 f(x) 替换 x。

注意力的输出直接替换时,「借」自己的信息每层只剩三成;相加时,原来的向量一直保留,每层只加一份补充

这种现象叫坍缩。2021 年有一篇论文专门算过:只有注意力、没有其他零件时,词向量之间的差别随着层数增加飞快地消失,没几层所有词就几乎变成同一个向量。论文同时发现,加上残差连接以后,坍缩就被挡住了。

二、残差连接:只补充,不改写

Transformer 不替换,用相加:

输出 = x + f(x)

「借」原来的向量原样保留,注意力的输出当作一份补充加上去。两层下来是这样:

「借」的向量
进入第 1 层 [0.8, -0.3, 0.5]
第 1 层加上补充 [0.2, 0.6, -0.1] [1.0, 0.3, 0.4]
第 2 层加上补充 [0.1, -0.2, 0.3] [1.1, 0.1, 0.7]

原来的 [0.8, -0.3, 0.5] 一直在里面,每一层只往上加一点新信息。这条让输入绕过计算、直接加到输出上的路叫残差连接(residual connection)。「残差」指的就是 f(x):在输入之外还要补上的那一部分。它来自 2015 年何恺明等人提出的 ResNet,最早用在图像识别上。

可以把它想成一条传送带。每个词的向量放在传送带上往后走,每一层从传送带上读一份,算出一份补充,再加回去,原来的东西不拿走。这条传送带常被叫作残差流。

残差还有第二个作用,和训练有关。

模型训练靠的是从最后的输出往回推:先看输出错了多少,再一层层往回算每个矩阵该往哪个方向改。没有残差时,这个信号要穿过每一层的计算,一层没学好就被削弱一次,层数一多,前面的层几乎收不到该怎么改的信号。有残差时,每一层旁边都有一条直通的路,信号可以顺着它直接传回最前面。

ResNet 的论文用实验证明了这个问题:56 层的普通网络,在训练数据上的错误率反而比 20 层的高。按理说层数多了,模型至少不该比浅的差,可它就是训练不动。加上残差连接以后,他们训练出了 152 层的网络。

所以残差让每一层的起点都是「什么都不做」。学到一点就加一点,一层学得不好,顶多等于没加这一层,不会毁掉前面的信息。层数这才能从几层叠到几十层、上百层。

三、层归一化:把数字拉回同一个范围

残差解决了信息丢失,也带来一个新问题:每一层都往向量上加东西,加了几十次以后,数字可能越来越大。「借」到后面可能变成 [30, -8, 52],另一个词还是 [0.5, 0.2, 0.1] 这么小。下一层的注意力还要拿这些向量做点积,数字大了,打出来的分数也跟着变大。

分数 1、2、3 经过 softmax,三个词都分到注意力;分数放大到 50、100、150,注意力几乎全给了一个词

这有两个坏处。一是「借」只从一个词那里拿信息,其他词全被忽略。二是训练会卡住:权重已经贴在 0 和 1 上,分数改大一点或改小一点,权重几乎不动,模型不知道该往哪个方向改。

层归一化(Layer Normalization)对每个词的向量单独算,分两步。以 [30, -8, 52] 为例:

  1. 算这几个数的平均值,约 24.67,每个数减去它,得到 [5.33, -32.67, 27.33]。
  2. 算这几个数离平均值有多远,这个量叫标准差,约 24.78,每个数除以它,得到约 [0.22, -1.32, 1.10]。

结果的平均值是 0,标准差是 1。不管「借」之前被加到多大,归一化以后都落在差不多的范围里,别的词也一样,下一层的点积就不会大到让 softmax 变成 [0, 0, 1]。

层归一化:「借」的向量 30、-8、52 减去平均值,再除以标准差,变成 0.22、-1.32、1.10,各维的高低形状不变

归一化以后还有一步:每一维再乘一个数、加一个数,这两组数是训练出来的。如果某一维确实需要大一点,模型可以自己调回来。LLaMA 这些模型用的是简化版 RMSNorm,省掉减平均值这一步,只除以一个衡量整体大小的数,算得更快,效果差不多。

下面的实验台里,先把「数字放大」往右拖,看注意力怎么挤到一个词上;再把它拖回 ×1,拖「整体偏移」,看注意力怎么跑到别的词身上;最后勾上层归一化,不管怎么拖,结果都不再变。

动手试一试把「借」的数字放大、偏移,看注意力怎么变
叠了很多层以后的「借」

送进注意力的「借」

词分数「借」给它的注意力
小明00%
书00%
小红00%

示意用 4 维向量,分数 = 「借」和每个词的 K 做点积再除以 √4;层归一化省掉了训练出来的缩放和平移。

层归一化放在哪也有讲究。2017 年的原论文把它放在相加之后,写成「归一化(x + f(x))」,结构图里的 Add & Norm 就是这个意思。GPT-2 以后的模型改成放在注意力前面,写成「x + f(归一化(x))」,叫 Pre-LN。这样残差那条传送带上不做任何处理,「借」原来的向量照样原样往后传。放在相加之后,每层都要改一次传送带上的数,层数一多就难训练,开始时要把学习率从很小慢慢调上去才稳。

四、前馈网络:每个词自己消化

所以注意力做的主要是搬运和调配:它能把「书」的信息搬给「借」,可结果总在已有的几个 V 中间,造不出这些 V 里都没有的新特征。理解一句话却需要新特征。「借」拿到「书」和「小红」的信息以后,要得出「这里是借出,借出去的是一本书,接收的人是小红」,这个判断在哪个 V 里都没有现成的。前馈网络(Feed-Forward Network)做的就是这件事。

它对每个词的向量单独处理,词和词之间不再交换信息。打个比方,注意力是大家开会交换信息,前馈网络是散会以后每个人回到自己座位上消化。它分三步:

  1. 乘矩阵 W1,把向量撑大。原论文从 512 维撑到 2048 维,撑大四倍。
  2. 把负数变成 0。这一步叫激活函数,原论文用的是 ReLU,比如 [1.2, -0.7, 0.3, -2.1] 变成 [1.2, 0, 0.3, 0]。
  3. 乘矩阵 W2,缩回 512 维,再通过残差加回「借」的向量上。

前馈网络:「借」的 512 维向量乘 W1 撑大到 2048 维,ReLU 把负数变成 0,再乘 W2 缩回 512 维,通过残差加回「借」

所以「把负数变成 0」是撑大能起作用的关键。可以把中间的 2048 维看成 2048 个探测器,每个检查输入里有没有某种组合特征:有,算出来是正数,放行;没有,算出来是负数,变成 0,关掉。比如某个探测器专门在「借」后面跟着一本书时亮起来。输入不同,亮起来的探测器就不同,关掉的探测器在 W1 和 W2 里对应的那部分也就不起作用。这相当于每个输入用了一个不同的矩阵,W2W1 合不成一个固定的矩阵,前馈网络才造得出新特征。

现在的模型大多换成了更平滑的激活函数,GPT 系列用 GELU,LLaMA 用 SwiGLU。它们和 ReLU 的作用一样,都是加入矩阵乘法做不到的非线性。

前馈网络在每一层里占了大部分参数。设向量是 d 维:

部分 参数量
注意力的 W_Q、W_K、W_V、W_O 4 × d²
前馈网络的 W1、W2,中间撑大 4 倍 8 × d²

前馈网络占一层参数的三分之二。GPT-3 的 d 是 12288,前馈网络中间撑到 49152 维。2021 年有研究发现,很多探测器对应着训练文本里具体的文字模式,W2 里存着这些模式出现以后,接下来该出现什么词的线索。所以一般认为,模型从训练数据里记住的大量知识主要存在前馈网络里。

五、拼成一层,再叠很多层

三个零件加上注意力,就是一层 Transformer。按 GPT-2 以后的 Pre-LN 放法,「借」进入一层以后依次经过:

  1. 层归一化:把数字拉回同一个范围。
  2. 多头注意力:从别的词那里拿信息。
  3. 加回原向量:残差连接,保住「借」自己。
  4. 层归一化:再拉回一次。
  5. 前馈网络:自己消化,造出新特征。
  6. 再加回:又一次残差连接。

写成公式是两行:

x = x + 注意力(归一化(x))

x = x + 前馈网络(归一化(x))

一层 Transformer:「借」先做层归一化、进多头注意力,结果加回原向量;再做层归一化、进前馈网络,结果再加回;残差流原样往下传

一层的输出和输入一样长,所以可以直接当作下一层的输入,一层接一层叠上去。每一层有自己的一套参数,各学各的。原论文的编码器和解码器各 6 层,GPT-2 最大的版本 48 层,GPT-3 有 96 层。所有层走完以后,最后做一次层归一化,再拿句子最后一个位置的向量去预测下一个词,这是下一篇的内容。

检验一下

注意力的输出为什么要加回输入,不直接替换?

训练刚开始,f(x) 接近 0。有残差连接时,这一层的输出是什么?

层归一化以后,向量里的哪些信息还在?

前馈网络中间为什么要把负数变成 0?

想深入可以看

← 全部文章