title: 【Transformer 与注意力机制】24|

如果把 Transformer block 画成结构图,残差连接往往只是一根细细的旁路线:从输入绕过 attention 或 FFN,直接和子层输出相加。很多教程讲到这里会轻描淡写地说一句:"这样可以保留原始信息。"

这句话不能说错,但太轻了。残差连接真正解决的是深层网络的优化问题:它给梯度留了一条几乎不受参数影响的直路,让每一层都只需要学一个"小修正",而不是学一整个从零开始的大变换。没有这根直路,Transformer 很难从 6 层长到几十层、上百层。

这一篇我们就把这根线单独拎出来讲。你会看到:残差不仅是训练稳定性的关键,也是后来理解大模型内部机制时一个非常重要的观察窗口------所谓 residual stream,本质上就是整网持续累积和改写的那条主干表示。

原文链接

一、什么叫残差:不是直接学 <math xmlns="http://www.w3.org/1998/Math/MathML"> H ( x ) H(x) </math>H(x),而是学 <math xmlns="http://www.w3.org/1998/Math/MathML"> F ( x ) F(x) </math>F(x)

ResNet 论文给出的原始动机很简单。假设我们想学一个目标映射 <math xmlns="http://www.w3.org/1998/Math/MathML"> H ( x ) H(x) </math>H(x)。普通堆叠层的写法是直接逼近它:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> y = H ( x ) y = H(x) </math>y=H(x)

残差写法把它改成:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> y = x + F ( x ) y = x + F(x) </math>y=x+F(x)

也就是说,不再直接学"应该输出什么",而是学"在输入基础上还需要补多少修正"。

1.1 为什么"修正"比"重来"更容易学

这背后有一个非常朴素的直觉:很多时候,理想变换离恒等映射并不远。

举个语言模型里的例子,一个 token 经过某一层 attention 后,往往不是"完全换个身份",而是:

  • 多知道了一点和前文的关系;
  • 某些维度被加强;
  • 某些噪声被压低;
  • 某些语义特征被显式化。

这些变化更像"在原向量上加一笔",而不是"把原向量推倒重算"。残差把这种结构偏好直接写进了网络。

1.2 Transformer 里的两条残差

每个 Transformer block 至少有两次残差相加:

  1. attention 子层外面一条;
  2. FFN 子层外面一条。

pre-LN 写法下,一层可以写成:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> h ′ = x + Attention ⁡ ( LN ⁡ ( x ) ) y = h ′ + FFN ⁡ ( LN ⁡ ( h ′ ) ) \begin{aligned} h' &= x + \operatorname{Attention}(\operatorname{LN}(x)) \\ y &= h' + \operatorname{FFN}(\operatorname{LN}(h')) \end{aligned} </math>h′y=x+Attention(LN(x))=h′+FFN(LN(h′))

从这个形式能直接看出残差的角色:主路径是 x -> h' -> y 这条直线,attention 和 FFN 更像两次可选的旁路修正。


二、真正关键的是梯度:残差给反向传播留了高速路

如果只从前向角度理解残差,容易把它当成"信息不丢失"的技巧;真正决定训练成败的是反向传播。

设一层输出是:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> y = x + F ( x ) y = x + F(x) </math>y=x+F(x)

对输入求导:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> ∂ y ∂ x = I + ∂ F ( x ) ∂ x \frac{\partial y}{\partial x} = I + \frac{\partial F(x)}{\partial x} </math>∂x∂y=I+∂x∂F(x)

注意那个 <math xmlns="http://www.w3.org/1998/Math/MathML"> I I </math>I。它不等于"梯度一定没问题",但它确实改变了深层网络的默认处境:反向里不再只剩纯粹的 Jacobian 连乘,而是多了一个恒等项,使整体更容易靠近恒等映射附近训练。

2.1 没有残差时,深层梯度是连续矩阵乘

如果一串层都没有残差,梯度要穿过很多 Jacobian 连乘:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> ∂ L ∂ x 0 = ∂ L ∂ x L ∏ ℓ = 1 L ∂ x ℓ ∂ x ℓ − 1 \frac{\partial \mathcal{L}}{\partial x_0} = \frac{\partial \mathcal{L}}{\partial x_L} \prod_{\ell=1}^{L} \frac{\partial x_\ell}{\partial x_{\ell-1}} </math>∂x0∂L=∂xL∂Lℓ=1∏L∂xℓ−1∂xℓ

这些矩阵的谱范数只要稍微偏大或偏小,层数一深就会爆炸或消失。RNN、早期深层 CNN 都被这个问题折磨过。

2.2 有了残差,最坏情况更接近恒等映射

残差不能从数学上彻底消灭梯度问题,但会显著改善最坏情况:就算子层暂时没学会什么,网络整体也更接近一堆恒等映射叠起来,而不是一堆随机矩阵叠起来。这里的关键词是"更接近",不是"自动稳定";稳定到什么程度,还取决于归一化、初始化和学习率配方。

这件事的工程意义非常大。因为训练开始时,attention、FFN 的权重几乎都是随机的。没有残差,网络最开始就像一串随机变换;有残差,它更像"以恒等映射为基底,慢慢往上叠修正"。

2.3 这就是为什么残差和归一化总是成对出现

残差解决的是"有直路";归一化解决的是"旁路修正别太野"。两者合起来,才把深层训练拉到一个稳定区间。

这也解释了为什么 24 和 25 两篇必须连着读:残差负责路径,LayerNorm 负责路径上的数值尺度。


三、把残差理解成"迭代 refinement"会更贴切

除了梯度视角,还有一个更适合直觉的解释:每一层不是重新编码,而是在前一层表示上做一次迭代修正。

3.1 每层只负责把表示往前推一小步

设第 <math xmlns="http://www.w3.org/1998/Math/MathML"> ℓ \ell </math>ℓ 层输入是 <math xmlns="http://www.w3.org/1998/Math/MathML"> x ( ℓ ) x^{(\ell)} </math>x(ℓ),输出是:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> x ( ℓ + 1 ) = x ( ℓ ) + Δ ( ℓ ) x^{(\ell+1)} = x^{(\ell)} + \Delta^{(\ell)} </math>x(ℓ+1)=x(ℓ)+Δ(ℓ)

其中 <math xmlns="http://www.w3.org/1998/Math/MathML"> Δ ( ℓ ) \Delta^{(\ell)} </math>Δ(ℓ) 就是这一层 attention 或 FFN 学到的修正。这样看,多层 Transformer 像是在一条主干表示上做很多次微小迭代:

  • 某一层把指代关系补进去;
  • 下一层把否定关系显式化;
  • 再下一层把任务相关特征拉高。

把整网展开写,会更清楚:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> x ( L ) = x ( 0 ) + ∑ ℓ = 0 L − 1 Δ ( ℓ ) x^{(L)} = x^{(0)} + \sum_{\ell=0}^{L-1} \Delta^{(\ell)} </math>x(L)=x(0)+ℓ=0∑L−1Δ(ℓ)

在 pre-LN block 里,可以把每个增量进一步写成:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> Δ ( ℓ ) = F ℓ ( LN ⁡ ( x ( ℓ ) ) ) \Delta^{(\ell)} = F_\ell(\operatorname{LN}(x^{(\ell)})) </math>Δ(ℓ)=Fℓ(LN(x(ℓ)))

这个式子把前面讲的三件事串到了一起:主路径负责把旧表示一路带下去,子层负责写入增量,深层能力则来自很多次可控的小步累积。

如果没有残差,这种"逐层增量改写"的语义会弱很多。

3.2 这也解释了为什么中间层表示经常已经很好用

很多 probing 研究会发现,Transformer 中间层往往已经学出了可分的句法或语义结构。更近一点的 logit lens、tuned lens 之类工作,也能看到中间层表征经常已经朝最终预测方向推进了一大截。更稳妥的说法不是"这单独证明了残差",而是:这类现象和 residual stream 的逐层累积视角高度一致。

这和纯前馈深网那种"越靠前越原始、越靠后越任务化"的感觉不完全一样。Transformer 的 residual stream 让不同层更像在共享一条不断变丰富的公共表示。


四、深层 Transformer 为什么格外依赖残差

ResNet 在视觉里已经证明了残差有用,但 Transformer 对残差的依赖更强,原因有三层。

4.1 每层本身就很"激进"

Transformer 子层里的 attention 和 FFN 都是大矩阵变换:

  • attention 会把全序列的信息混进来;
  • FFN 会把每个 token 拉到高维再压回来。

它们不像卷积那样只改一个局部感受野,而是每一层都有能力做大改写。没有残差,这种改写太容易把表示空间搞乱。

4.2 Transformer 没有 CNN 那种天然局部平滑偏置

CNN 的卷积、池化、局部连接天然带着"别一下子改太猛"的结构约束。Transformer 的全连接 attention 没这个保护,残差就成了最重要的结构性缓冲。

4.3 大模型需要很多层来积累能力

当模型从 6 层涨到 32 层、80 层时,单层学一点点修正是合理的;让每层都独立做大幅重写几乎不可能训练稳。残差正好支持这种"很多小步叠加成一个大能力"的扩展方式。


五、Pre-Norm 为什么比 Post-Norm 更容易训深

残差写法不只一种。对 Transformer 影响最大的区别,是 LayerNorm 放在加法前还是后。

5.1 Post-Norm:原论文的写法

原论文用的是:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> y = LN ⁡ ( x + F ( x ) ) y = \operatorname{LN}(x + F(x)) </math>y=LN(x+F(x))

这时残差和旁路修正先相加,再一起归一化。问题在于:梯度沿主路径回传时,也会经过 LN 的 Jacobian。层数一深,这条"高速路"就不再那么直。

5.2 Pre-Norm:现代主流写法

现代更常见的是:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> y = x + F ( LN ⁡ ( x ) ) y = x + F(\operatorname{LN}(x)) </math>y=x+F(LN(x))

这时主路径上的 x -> y 基本是直通,归一化只作用在旁路输入上。于是:

  • 子层得到稳定输入;
  • 主路径的恒等传递更完整;
  • 深层训练明显更稳。

5.3 这不是"谁更先进",而是谁更适合大深度

在 6 层翻译模型里,post-norm 完全能用;在 48 层、80 层的大语言模型里,pre-norm 通常更省心。这不是品味问题,而是优化几何不同导致的现实选择。

现代还会进一步叠加:

  • residual scaling;
  • smaller init;
  • DeepNorm、ReZero、NormFormer 一类改动;
  • 更谨慎的学习率和 warmup。

这些方法的做法并不相同:DeepNorm 重点是深层稳定性,ReZero 重点是残差分支初始化,NormFormer 则继续细调 block 内的归一化与缩放。但它们都在围着同一个目标转:让 residual 主路径既稳定,又保留足够表达力。


六、Residual Stream:从优化问题走到表示流动

前面几节说的是优化;下面这一节换个角度,看表示是怎么在整网里流动的。近几年做机制可解释性的人,最爱盯的对象之一就是 residual stream。原因很简单:attention 和 FFN 的更新,最后都会以增量形式写回这条主干表示。

6.1 每个子层都像在往同一个共享总线上写入

如果把一个 block 抽象成:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> x ← x + Δ attn + Δ ffn x \leftarrow x + \Delta_{\text{attn}} + \Delta_{\text{ffn}} </math>x←x+Δattn+Δffn

那 residual stream 就像一条共享总线:

  • 某个 attention head 把"把主语搬到当前位置"的信息写进去;
  • 某个 FFN 把"这是首都问答模式"的特征写进去;
  • 后续层再在这条总线上继续读取和追加。

这样看,Transformer 不像一串完全分离的层,更像很多模块在共同改写同一份活动内存。前面那条展开式
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> x ( L ) = x ( 0 ) + ∑ ℓ = 0 L − 1 Δ ( ℓ ) x^{(L)} = x^{(0)} + \sum_{\ell=0}^{L-1} \Delta^{(\ell)} </math>x(L)=x(0)+ℓ=0∑L−1Δ(ℓ)

也正好说明了为什么这条流值得盯:最终输出不是来自某一层"独占"的表示,而是来自一连串增量的累计结果。

6.2 模型编辑和可解释性常常落在这条流上

ROME、MEMIT、logit lens、tuned lens 这类工作,很多都会把分析对象落在这条流上。例如:

  • 哪些层最明显地写入了事实相关更新;
  • 中间层的当前表示离最终预测还有多远;
  • 某些局部增量被改动后,最终输出会朝哪个方向偏移。

所以残差早就不只是训练技巧。到了大模型研究里,它也成了理解"模型内部状态到底如何流动"的核心切入点之一。


七、几个工程上很实际的细节

7.1 旁路并不是越大越好

残差的本意是学小修正。如果某层的 <math xmlns="http://www.w3.org/1998/Math/MathML"> F ( x ) F(x) </math>F(x) 数值幅度长期远大于 <math xmlns="http://www.w3.org/1998/Math/MathML"> x x </math>x,那这层实际上就在强行覆盖主路径,训练会变得不稳。所以很多实现会:

  • 调整初始化;
  • 对 attention / FFN 输出加缩放;
  • 在深层模型里显式控制 residual branch 的方差。

7.2 训练时更值得监控的是残差分支的尺度

工程上,一个常见做法不是只盯总 loss,而是顺手看 attention / FFN 输出范数,以及它们相对主干表示的比例。如果某几层的 residual branch 突然抬得很高,往往意味着:

  • 学习率过激;
  • 初始化或缩放不合适;
  • 归一化配方没有把子层输入稳住。

这类监控本身不解决问题,但常常能比"等 loss 炸掉"更早暴露问题。

7.3 残差不能单独拯救坏配方

要注意,残差不是万能药。学习率太大、归一化放错、初始化离谱、mask 写错,模型照样会崩。残差只是把可训练区域大幅扩大,不是让所有坏设置都自动没事。


八、几个常见误解

8.1 "残差就是为了保留原始信息"

这句话太弱了。更准确的说法是:残差让每层学修正而不是重算,并给梯度留一条近似恒等的回传路径。

8.2 "有残差就不会梯度消失"

也不绝对。残差显著缓解问题,但不能从数学上保证任何深度下都绝对稳定。归一化、初始化、学习率同样重要。

8.3 "残差只在很深的模型里才重要"

6 层模型里它也重要,只是问题没那么显眼。到了几十层,重要性从"有帮助"升级为"几乎不可缺"。

8.4 "残差让模型变成线性的"

不会。虽然主路径是相加,但旁路里的 attention、FFN、归一化都还是高度非线性的。残差只是改变了函数的组织方式,不是拿掉了非线性。

8.5 "把更多模块都接到 residual 上一定更好"

也不一定。模块往 residual stream 里写得太多、太猛,会带来干扰、方差累积和优化困难。为什么现代架构会反复讨论门控、缩放和 norm,本质上都是在管这件事。


九、结语

残差连接真正让 Transformer 成立的地方,不在于它"看起来优雅",而在于它把深层网络最难的一件事------稳定优化------处理成了"在主路径上逐层叠加小修正"。一旦把这个视角建立起来,你再看 pre-norm、residual stream、模型编辑、深层扩展,就会发现它们其实都在围绕同一根主线展开:如何让这条主干既传得稳,又写得进新信息。

下一篇我们继续沿着这条主线走,不过把焦点从"有没有直路"换成"每层输出的数值尺度怎么稳住"------也就是 Layer Normalization 为什么会成为 Transformer 的标准配置。


十、参考文献

  1. He, K. et al. "Deep Residual Learning for Image Recognition." CVPR 2016. 残差学习的原始提出。
  2. Vaswani, A. et al. "Attention Is All You Need." NeurIPS 2017. Transformer 中 attention / FFN 子层外残差的标准写法。
  3. Xiong, R. et al. "On Layer Normalization in the Transformer Architecture." ICML 2020. 分析 pre-LN 与 post-LN 的训练稳定性差异。
  4. Wang, H. et al. "DeepNet: Scaling Transformers to 1,000 Layers." IEEE TPAMI 2024. 深层 Transformer 中 residual scaling 的代表工作。
  5. Bachlechner, T. et al. "ReZero is All You Need: Fast Convergence at Large Depth." UAI 2021. 通过控制残差分支初始化改善深层训练。
  6. Shleifer, S. et al. "NormFormer: Improved Transformer Pretraining with Extra Normalization." ICLR 2022. 通过额外归一化与缩放改进 Transformer 训练。
  7. Elhage, N. et al. "A Mathematical Framework for Transformer Circuits." Transformer Circuits Thread, 2021. 从 residual stream 视角分析 Transformer 内部计算。
  8. Tenney, I. et al. "BERT Rediscovers the Classical NLP Pipeline." ACL 2019. 早期 probing 工作,展示中间层逐步形成语言学结构。
  9. Belrose, N. et al. "Eliciting Latent Predictions from Transformers with the Tuned Lens." ICLR 2024. 用 tuned lens 观察中间层表征与最终预测的关系。
  10. Meng, K. et al. "Locating and Editing Factual Associations in GPT." NeurIPS 2022. ROME 方法,研究事实信息主要写入哪些层。
  11. Meng, K. et al. "Mass-Editing Memory in a Transformer." ICLR 2023. MEMIT 方法,研究大规模事实编辑与写入位置。

← 上一篇:23|Decoder 详解 | 下一篇:25|Layer Normalization

相关推荐
范什么特西7 小时前
Spring 动态代理 静态代理
java·后端·spring
醇氧7 小时前
Spring 动态注册 Bean 深度解析:从源码到实践
java·后端·spring
zb200641207 小时前
Laravel7.x十大核心特性解析
spring boot·后端·laravel
明月_清风8 小时前
FastAPI 从入门到实战:3 分钟构建高性能异步 API
后端·python·fastapi
小村儿8 小时前
连载10-Sub-agents 深度解析:从源码理解 Claude Code 的分身术
前端·后端·ai编程
他们叫我阿冠8 小时前
Day5学习--SpringBoot详解
spring boot·后端·学习
枕星而眠8 小时前
Linux 四大进程/线程同步锁详解:互斥锁、读写锁、条件变量、文件锁
linux·c语言·后端·ubuntu·学习方法
IT_陈寒9 小时前
Vite动态导入把我坑惨了,原来要这样用才对
前端·人工智能·后端
计算机魔术师10 小时前
【AI面试八股文 Vol.3.4:训练微调部署选型】从预训练到量化部署:LLM 工程落地如何做模型选择
人工智能·后端·面试·架构·moe·vol.3.3·vol.3.4