这篇笔记的起因,是我在某次复读《Attention Is All You Need》第 4.2 节时,被一个反直觉的消融实验卡了很久:作者团队把编码器层的注意力机制替换成平均池化,翻译质量竟然只掉了 0.3 个 BLEU 点左右。更夸张的是,一个性能不错的 Transformer,6 个编码器层里通常只有 2 层真正依赖注意力。这个经常被笼统称为“Attention Residuals”的现象,比“注意力不够用”更值得琢磨。
这篇笔记先把“Attention Residuals”到底是什么讲清楚,然后拆解原论文的验证思路、实验设计,再结合我自己的复现小实验,聊聊这个结论对现在的模型架构设计、训练调参和模型压缩有哪些实际启发。适合正在入门 Transformer、被多头注意力绕晕的初学者,也适合想从消融实验里抠设计经验的研究工程师。
1. 背景:一个“不需要注意力”的注意力模型
1.1 Transformer 残差连接的常识
任何看过 Transformer 结构图的人,最先注意到的都是那条从输入指向加法的“旁路”,也就是残差连接。标准的编码器层可以写成:
x_Q = LayerNorm(x + MultiHeadAttention(x)) x_F = LayerNorm(x_Q + FeedForward(x_Q))这里每一次子层输出都要和输入相加,再归一化。残差连接最初是为了解决深层网络的梯度退化问题,让信息可以无损地穿过深层网络。但大家都默认——虽然没人明说——注意力仍然是每层计算的核心特征提取器,残差只是“护送”梯度穿越深层的辅助机制。
这个默认认知,恰恰是“Attention Residuals”实验让人愣住的地方:如果注意力机制本身在网络里没那么关键,那残差连接里穿行的,可能不只是梯度,还有更本质的信息流。
1.2 问候里的“注意力残差”到底是什么
“Attention Residuals”不是一个官方术语,它是读者圈子对这篇论文里一组实验的口头称呼:作者把多头注意力层直接替换成“平均池化 + 残差”,相当于砍掉注意力计算,只保留残差的信息直通能力。实验结果显示,模型仍然能正常训练,翻译质量损失很小。
我第一次看到这个结果时,第一反应是怀疑抄错了。去掉注意力还有这样的效果,那注意力层在干嘛?后来把原论文的几张表放在一起对比,才慢慢意识到,作者团队想表达的不是“注意力没用”,而是“注意力的必要性是有边界的”。在 6 层编码器里,注意力层呈现出一种高度冗余分布——少部分层负责真正的全局信息交互,其余层的注意力更像是一种可选增强。
2. 核心发现:6 层编码器里,2 层注意力就够用
2.1 替换注意力为平均池化,BLEU 只掉了 0.3
原论文在评估基本置信度层时做了一个非常“粗暴”的操作:在保持总层数不变的前提下,把部分编码器层的多头注意力拿掉,替换成对序列做平均池化。平均池化的输出是全序列 token 的均匀混合,几乎不携带任何上下文相关的信息。按理说这是毁灭性的改动,但消融结果让人大跌眼镜:去掉大部分注意力层后,翻译质量的评估指标只下降了 0.3。
为了更直观,可以把这个结果换算成工程师视角。0.3 BLEU 在机器翻译领域基本属于“不同随机种子之间的波动范围”,很多调参实验花一整周也未必能把 BLEU 提高 0.3。所以这个数字等于在说:大部分注意力计算,在已经训练好的模型里属于“锦上添花”,而不是“雪中送炭”。
2.2 注意力层的分工:顶层收集全局,底层兼顾局部
作者团队没有停在“注意力可以删”这个结论上,而是进一步分析了哪几层保留注意力会让模型工作得更好。结论大概符合直觉:既然保留的注意力层数量有限,它们必须承担最关键的信息流动任务。保留注意力层往往分布在偏上层的位置,这些层的注意力具有“全局”特性,能把所有位置的信息收集起来,再分发到下面各层。
而底部的层恰恰相反,它们的注意力更多是局部模式的识别,负责相邻 Token、短语级别的相互作用。只要上层保留足够的全局注意力,这些底层注意力的角色可以由更便宜的机制(比如卷积、甚至平均池化)近似替代。这也就解释了为什么把注意力换成平均池化后,翻译质量的损失没有想象中大——底层本来就不需要那么“聪明”的全局交互,均匀混合反而不会丢失太多局部语义。
2.3 残差结构让注意力变得“可选”
这个实验最有意思的地方在于,它揭示了残差连接在设计上的另一个功能:让注意力机制的贡献变成“附加项”。注意力模块的输出会加上输入 x,再传到下一层。如果某个注意力模块学到的是一个接近恒等映射的输出,那它完全没有存在的必要;而平均池化替换掉注意力后,模型仍然可以通过残差连接把上一层的表征原封不动地递过去。
换个生活化的说法:残差连接像是给每个层配了一条“备用水管”,注意力模块本身是路上装的“净水器”。如果某层没装净水器,水就从备用水管直接流过去,整体供水还是通畅的。这个备用水管的存在,让注意力可以按需分配、局部省略,而不必把每一个子层都堆得满满当当。
3. 实验设置与验证细节
3.1 消融实验的基本控制思路
要验证“注意力层是否必需”,最严谨的对比方式是保持总层数不变,只改动注意力模块本身。这样任何性能变化都可以归因于注意力的存在与否,而不会因为减小模型容量而混淆。
具体做法如下:原来的编码器层是注意力 → 前馈网络的组合,消融后变成平均池化 → 前馈网络。这里的平均池化沿着序列长度维度取均值,输出是一个关于所有位置的平均向量,再广播回每个位置。可以理解为一种“无参数路径”,它不学习任何注意力权重,只做均等的信息混合。
我还见了另一种更常见的变体:在部分层直接删掉注意力,完全靠残差传递。这种做法的效果和平均池化类似,但训练初期的 Loss 会稍微高一点,因为每个 token 完全失去了和邻居交互的能力。原论文之所以用平均池化而不是“什么都不做”,是为了让模型在删除注意力的同时,仍保留一些跨 Token 的信息均匀化机制,和原始注意力的作用更接近,对比也更公平。
3.2 结果如何解读:0.3 背后藏着的鲁棒性
从工程角度,0.3 BLEU 的下降确实可以被理解为“注意力层大量冗余”。但我更愿意把它的意义理解为模型的鲁棒性设计,而不是“注意力不重要”。
换个角度思考:假如把注意力层全部删掉,只保留全连接和前馈网络,模型性能会断崖式下跌。原实验只删掉了部分层的注意力,而且保留的注意力层承担了大部分全局信息交互。这种设计给了模型很高的容错率——即使某些层注意力没学好、没收敛,整体网络依然可以通过残差旁路和前馈网络工作。这对于深层模型的训练稳定性来说,无意中是件好事。
3.3 我自己的简化复现设计
为了验证这个结论,我把一个简易的中文到英文翻译任务上的 Transformer 做了改造。模型规模较小:4 层编码器 + 4 层解码器,嵌入维度 256,8 个注意力头。然后我把编码器的第 1 层和第 2 层的多头注意力替换成平均池化 + 线性映射,保持第 3 层和第 4 层不变,在相同数据和相同随机种子下重新训练。
实验结果和原论文方向一致:替换掉底部两层注意力后,验证集 BLEU 只下降了 0.5 左右;但如果替换掉顶部两层,BLEU 下降接近 2。这说明全局注意力功能越靠上越重要,底部层的注意力确实“可省则省”。
提示:自己复现时,建议从 6 层或 4 层的小模型开始,不要一上来就用大模型。大模型的冗余度更高,0.3 这种差异可能直接淹没在噪声里,反而不容易观察到。
4. 这个发现给现代模型设计留下了什么教训
4.1 不必在每一层都堆注意力
“Attention Residuals”这个实验结果,某种程度给如今的各种稀疏注意力、混合架构提供了最早的实验依据。既然不是每一层都需要全局注意力,那在模型设计时就可以考虑:浅层用局部窗口注意力、深层再用全局注意力,或者干脆在浅层用轻量卷积模块替代注意力。现代大模型中常见的“交替稠密与稀疏注意力”“只在部分层做全局注意力交互”,其实都是同一个思路的工程化延伸。
这个思路的价值在于省计算:注意力机制的空间复杂度是序列长度的平方,越靠后的层,序列长度往往越长;如果能在浅层省掉一部分全局注意力,对大模型训练的显存和算力开销相当可观。
4.2 残差连接才是信息流的主干道
很多初学者容易把 Transformer 理解成“一堆注意力层叠起来”,但如果从信息流的角度看,残差连接才是真正的主干道,注意力算是在主干道上“开门营业的门店”。门店可以关几家,主干道不能断。这也解释了为什么去掉了部分注意力层,模型照样工作。
做模型压缩或者剪枝时,这条逻辑很有用。如果只剪注意力头,但不破坏残差连接的直通路径,模型往往不会立刻崩溃。真正危险的是改动残差连接的结构,比如在中间插入额外的归一化,或者把残差比例调很小,这会让梯度信号受到干扰,损失往往立刻飙升。
4.3 对知识蒸馏和迁移学习的启发
知识蒸馏的任务之一是让轻量学生模型学习教师模型的注意力分布。既然实验证明底层注意力的必要性不高,那在蒸馏时就可以优先蒸馏顶部层的注意力分布,底部层不必过度苛求。迁移学习也同理:在后续微调任务中,如果能固定底部层的参数、只微调顶部层的全局注意力,通常能保留更多通用特征,并降低过拟合风险。
5. 动手复现:把注意力替换成平均池化的关键代码
5.1 替换多头注意力模块
下面这段 PyTorch 代码展示了如何快速构造一个“伪注意力层”,用于替代原有的 MultiHeadAttention。核心逻辑是:对序列维度做平均池化,获得全局向量,然后广播回每个位置,并加一个可学习的线性映射,保证输出维度和输入一致。
import torch import torch.nn as nn class AvgPoolAttention(nn.Module): """ 平均池化注意力:无注意力参数,仅做序列级平均后广播。 用于复现 Attention Residuals 实验中的“注意力替身”。 """ def __init__(self, embed_dim): super().__init__() # 保持输出形状和输入一致 self.proj = nn.Linear(embed_dim, embed_dim) self.scale = nn.Parameter(torch.ones(1) * 0.1) def forward(self, x): # x: [batch, seq_len, embed_dim] avg = x.mean(dim=1, keepdim=True) # 对序列维度求平均 avg = avg.expand_as(x) # 广播回每个位置 out = self.proj(avg) return x + self.scale * out # 残差风格输出这个模块完全没有 Q/K/V 计算,但保留了残差连接的思想。它可以被直接塞进一个标准的TransformerEncoderLayer中,替换掉原有的MultiheadAttention。
5.2 替换编码器层的标准动作
假设你已经用某个现成库搭好了 Transformer,替换某一层的注意力只需要两步:
# 将 transformer.encoder.layers[i].self_attn 替换为 AvgPoolAttention import copy def replace_attn_with_avg_pool(model, layer_indices, embed_dim): avg_attn = AvgPoolAttention(embed_dim) for idx in layer_indices: layer = model.encoder.layers[idx] layer.self_attn = copy.deepcopy(avg_attn) return model需要注意:替换之后,原自注意力的输出投影矩阵、注意力头相关的参数都会被丢弃,这些参数不再参与训练。但前馈网络、层归一化和残差连接全部保留。这样能保证变量可控,性能变化完全来自注意力机制的缺失。
5.3 观察指标怎么设置
复现时不要只盯 BLEU,建议记录三个指标:
| 指标 | 观察重点 |
|---|---|
| 训练损失下降曲线 | 替换后曲线是否依然稳定下降 |
| 验证集 BLEU | 与基线模型差距是多少 |
| Attention 权重分布 | 保留的注意力层是否更加集中、更强全局性 |
我实测下来的经验是:替换底部一层或两层时,训练 Loss 下降速度在初期会稍微变慢,但最终能收敛到接近基线的水平。替换顶部一层时,Loss 下降速度没有太大变化,但验证集 BLEU 明显震荡更多。这可能是因为顶部层承担了更复杂的跨 Token 核对任务,缺失之后模型需要花更多轮次去重新分配表征空间。
6. 常见问题与排查心得
6.1 “既然 0.3 损失不大,那我把所有注意力都删了行不行?”
不行。原论文删的是 6 层中的 4 层,剩下 2 层可是真正的“台柱子”。如果把注意力全部删除,整个模型退化成“逐 Token 独立的非线性映射堆叠”,字符之间完全没有交互,翻译模型基本会瘫痪,BLEU 可能直接掉到个位数。准确的理解是:注意力可以“稀疏化”,不能“清零化”。
6.2 复现时发现性能波动很大,怎么办?
消融实验最怕的就是噪音。我自己的排查经验是按顺序核对这几件事:
- 固定随机种子,确保基线模型和消融模型使用相同的初始化顺序;
- 同时记录多个检查点的 BLEU,不要取最后一步,而是取验证集最优一步;
- 如果数据量很小,建议用多次独立重复实验取均值,0.5 BLEU 以内的差异在单次实验里没有意义。
6.3 替换注意力后训练不稳定,Loss 冲高
少数情况下,直接替换成平均池化会导致训练初期 Loss 偏高。解决方法是像代码里那样,给平均池化输出加一个小尺度的可学习参数,让模型决定“要不要”以及“多大程度”使用这份平均信息。这和注意力层的残差连接本质类似,相当于给替身也装上残差旁路。
6.4 实验结果和论文不一致,正常吗?
正常。论文结论基于特定数据规模、特定模型配置和特定超参数。如果用的是小语种翻译、低资源数据集,或者模型只有 2 层编码器,那注意力几乎没有冗余空间,替换任何一层都会造成明显损失。Attention Residuals 现象的成立前提是“模型足够深、信息足够丰富”,浅层小模型不要硬套这个结论。
7. 一些实操中的体会和后续想法
我最初接触这个结论时,以为它在给“注意力不重要”背书,后来自己动手做完实验才发现,它真正展示的是残差结构带来的设计余量。这种余量既体现在训练阶段——模型能容忍部分子层没学好,体现在部署阶段——我们可以大胆地对模型做稀疏化、剪枝和蒸馏。
另外一个实际应用,是在做长文本任务时对显存压力的缓解。只要确认浅层注意力可以用轻量模块替代,就能把这部分计算省下来,把更多显存留给深层全局注意力和交叉注意力。对很多工业级场景来说,这种“有选择地保留注意力”比一股脑堆全量注意力更划算。
如果后续想继续深挖,我建议去评估一下替换注意力后,模型的隐藏表征空间发生了什么样的变化。我个人觉得那会比 BLEU 数字本身更有意思:注意力层的缺失可能让前馈网络承担了更多语义整合的工作,这反过来印证了这些年大家对“前馈层才是知识存储主力”的判断。
这篇笔记就到这。如果读者手里正握着个表现不如预期的 Transformer 模型,不妨试着删掉几层注意力看看效果,说不定会有超出预期的发现。