前面我们已经认识了卷积神经网络、循环神经网络,也大致知道模型是怎么“看图”“读句子”的。到了这一章,我们要学一种更像人脑的能力:不是把所有信息一视同仁,而是学会把力气花在重点上

这就是注意力机制

你可以把它理解成这样一件事:

  • 你在图书馆找书,不会把每一本书都翻完,而是先看目录、标签和书名。
  • 你在做阅读理解,也不会把每个字都同等对待,而是会先盯住题目相关的句子。
  • 你在跟人聊天,耳边有很多杂音,但真正重要的那句话,你会下意识地抓住。

模型也一样。面对大量输入时,它不应该平均用力,而应该学会:哪些信息值得多看一眼,哪些信息可以少看一点

这一篇我们用最通俗的话,把注意力机制从头讲清楚,内容会覆盖:

  • 注意力提示、查询、键和值
  • Nadaraya-Watson 核回归
  • 注意力评分函数:掩蔽 softmax、加性注意力、缩放点积注意力
  • Bahdanau 注意力
  • 多头注意力
  • 自注意力和位置编码
  • Transformer 的整体结构

一、为什么模型需要注意力?

1. 人类本来就会“挑重点”

想象你在一个热闹的集市里找朋友。周围有很多人、很多声音、很多招牌,但你不会把每个细节都完整记住。你会先盯住对方的衣服颜色、站姿、招手动作,快速缩小范围。

这就是注意力。

注意力机制的核心,不是“记住全部”,而是“根据当前任务,决定该关注什么”。

2. 传统模型的问题:总想平均分配注意力

很多早期模型遇到一段输入时,容易把所有位置看成差不多重要。这样做有两个问题:

  1. 信息太多时,会被噪声淹没。
  2. 距离太远的关键信息,模型不一定能及时抓住。

比如翻译一句话时,英文单词和中文词语之间经常不是一一对应的。如果模型只按固定顺序往前读,遇到长句就容易“前面忘了,后面又来不及看”。

注意力机制就是为了解决这个问题而生的:让模型在需要的时候,动态回头看更相关的内容

3. 一个很直白的比喻:查字典

你要查“卷积”这个词时,会先在目录里找“卷”或“convolution”附近的条目。

  • 你的目的,就是查找现在真正想要的内容,这叫 查询(Query)
  • 每个词条都有自己的名字、标签、索引,这些像 键(Key)
  • 真正的词条解释内容,像 值(Value)

模型也是同样的思路:

  • Query:我现在想解决什么问题
  • Key:每个候选信息的“标签”
  • Value:被取出来的真实内容

注意力机制做的事,就是先比较 Query 和每个 Key 的相关程度,再把对应的 Value 按重要性加权汇总。


二、注意力提示:模型该往哪看?

1. 生物学里的注意力提示

在现实世界里,注意力不是随机发生的,而是会被一些“提示”触发。

比如:

  • 你听到自己的名字,会立刻抬头。
  • 你看到红色警示牌,会比普通文字更敏感。
  • 你在做数学题时,会更在意数字、符号和条件,而不是题干里的背景故事。

这些能引导注意力的线索,就叫 注意力提示

2. 查询、键和值:注意力机制的三件套

这是注意力机制最重要的一组概念。

名称 通俗理解 作用
Query 我现在想找什么 发出需求
Key 每条信息的标签 用来比较相关性
Value 这条信息真正的内容 被加权汇总

你可以把它想成“在商场里找商品”:

  • Query 是你心里想买的东西。
  • Key 是每个货架贴的商品标签。
  • Value 是货架上真正的商品。

模型先看 Query 和 Key 像不像,再决定从哪些 Value 多拿一点。

3. 注意力其实就是加权平均

如果只说一句话,注意力机制可以概括成:

先算每个位置的重要性,再做加权平均。

数学上可以写成:

output=sumialphaivioutput = sum_i alpha_i v_ioutput=sumialphaivi

其中:

  • alphaialpha_ialphai 表示第 iii 个位置的权重
  • viv_ivi 表示第 iii 个位置的值

权重越大,这个位置对结果影响越大。

4. 一个生活化例子

假设你在读一句话:

小明把苹果放进了冰箱,因为它很容易坏。

这里“它”指的是“苹果”,而不是“冰箱”。

如果模型想判断“它”指谁,就不能平均看前文所有词,而应该更关注“苹果”这个词。注意力机制做的,就是这种“把目光投向更相关词语”的工作。

5. 注意力的可视化

注意力不是神秘黑箱。我们通常可以把权重画成热力图,看看模型到底在关注哪里。

比如在机器翻译里:

  • 译“我喜欢这本书”时,模型可能会重点关注“喜欢”和“书”。
  • 译长句时,某个中文词可能会对应英文里的几个词。

可视化的价值很大,因为它能帮我们回答一个很实际的问题:

模型是“真的看懂了”,还是只是碰巧猜对了?


三、注意力汇聚:Nadaraya-Watson 核回归

注意力机制并不是先有深度学习才出现的。其实早在 1964 年,人们就在统计学习里用过一个非常接近注意力思想的方法:Nadaraya-Watson 核回归

它的思想特别适合入门理解,因为它几乎就是“注意力机制的雏形”。

1. 先看最简单的平均汇聚

如果你想预测一个点的值,最偷懒的方法就是平均周围样本。

比如周围 5 个点分别是:2、4、6、8、10。

直接平均就是 6。

这就是平均汇聚:不管远近,不管相似不相似,大家权重都一样。

这个方法简单,但太粗糙了。因为离你更近、更像你的样本,往往更值得参考。

2. 非参数注意力汇聚:相似的多看,不相似的少看

Nadaraya-Watson 的核心做法是:

  1. 先看当前输入和每个样本有多像。
  2. 越像的样本,权重越大。
  3. 最后把所有样本的值加权平均。

这就像你在问同学题目答案时:

  • 跟你学得差不多的人,建议更可信。
  • 完全不在一个水平的人,参考价值就低一些。

所以它不是简单平均,而是根据相似度分配权重

3. 一个最小示例

下面是一个非常小的示意代码,帮助你理解“权重越大,越像被重点关注”。

import torch

def attention_pooling(query, keys, values):
    # query: 当前想预测的位置(一个标量或向量)
    # keys: 所有候选样本的位置(与 query 同类型的张量)
    # values: 候选样本对应的真实值(将被加权汇总)
    # 下面计算每个 key 与 query 的相似性得分:这里用负平方距离,距离越小得分越大
    scores = -((keys - query) ** 2)
    # 把得分转换为概率权重,softmax 会把所有分数规范化为正数并使其和为 1
    weights = torch.softmax(scores, dim=0)
    # 用权重对 values 做加权求和,得到注意力汇聚结果;同时返回权重便于可视化或调试
    return (weights * values).sum(), weights

# --- 示范用法 ---
# 构造一个示例查询(query)和值/键(keys, values)
query = torch.tensor(2.5)                          # 我们想预测的位置是 2.5
keys = torch.tensor([1.0, 2.0, 3.0, 4.0])          # 四个候选位置
values = torch.tensor([1.0, 2.0, 3.0, 4.0])        # 对应的真实值(这里简单用位置本身作为示例)

# 调用注意力汇聚函数得到输出和每个候选的权重
output, weights = attention_pooling(query, keys, values)

# 打印结果:output 是汇聚后的预测值,weights 是每个候选被关注的程度
print('output =', output)
print('weights =', weights)

这里的重点不是代码多复杂,而是思路:

  • 先算 query 和每个 key 的相关性。
  • 再把相关性变成权重。
  • 最后对 values 做加权求和。

这和现代注意力机制的主干完全一致。

4. 带参数的注意力汇聚

前面的方法里,相关性是“手工定义”的,比如距离越近权重越大。

但真实问题里,我们希望模型自己学会什么叫“相似”。于是就会引入参数,让模型自动学习权重怎么分配。

这样一来,注意力不再是固定公式,而是一个可以训练的模块。

这就是从“统计里的核回归”走向“深度学习里的注意力层”的关键一步。

5. 小结

Nadaraya-Watson 核回归告诉我们一件很重要的事:

模型不一定要平均看待所有输入,它可以根据相关程度,给不同输入不同权重。

这就是注意力思想的原点。


四、注意力评分函数:先打分,再汇总

注意力机制的核心流程可以拆成三步:

  1. 计算 Query 和 Key 的相关性。
  2. 把相关性变成权重。
  3. 对 Value 做加权求和。

第一步最关键,因为“怎么打分”,会直接影响注意力怎么分配。

1. 掩蔽 softmax:有些位置不能看

在很多任务里,并不是所有位置都能看。

比如:

  • 句子补齐后的空白部分,不能被当成真实词语。
  • 在生成文本时,模型不能偷看未来词,只能看已经生成的过去词。

这时候就要用 掩蔽 softmax

它的作用很简单:

  • 不允许看的位置,分数直接设成一个极小值。
  • 这样经过 softmax 后,这些位置的权重就接近 0。

可以把它理解成“黑名单机制”。

2. 加性注意力:用一个小网络来打分

加性注意力的思路是:

  1. 把 Query 和 Key 拼在一起。
  2. 送进一个小神经网络。
  3. 输出一个相关性分数。

通俗理解:

不只是看“像不像”,还让模型自己学会“怎么判断像不像”。

这类方法通常比较灵活,适合处理 Query 和 Key 维度不同的情况。

3. 缩放点积注意力:更直接、更高效

另一种非常常见的方法是 缩放点积注意力

它的打分方式很直接:

score=(q⋅k)/sqrt(d)score = (q \cdot k) / sqrt(d)score=(qk)/sqrt(d)

其中:

  • qqq 是 Query
  • kkk 是 Key
  • ddd 是向量维度

为什么要除以 sqrt(d)sqrt(d)sqrt(d)

因为维度越大,点积数值越容易变得很大,softmax 就会变得特别“偏激”,容易只盯住某一个位置。缩放以后,训练会更稳定。

4. 什么时候用哪种?

你可以这样记:

  • 加性注意力:更灵活,适合不一样的 Query 和 Key。
  • 缩放点积注意力:更简单、更快,Transformer 里最常见。

5. 小结

注意力评分函数就像“打分器”。

先给每个候选信息打分,再把分数变成权重,最后汇总值。

这件事看起来普通,但它是所有注意力模型的共同骨架。


五、Bahdanau 注意力:让翻译模型学会“回头看”

如果说前面的内容是在讲“注意力是什么”,那 Bahdanau 注意力就是注意力机制真正进入深度学习主流的代表作之一。

它最早在机器翻译任务里大放异彩。

1. 传统 Seq2Seq 的问题

早期的编码器-解码器模型,通常会把整句输入压缩成一个固定长度的向量,再交给解码器生成翻译。

这个做法的问题很明显:

  • 短句还行。
  • 长句容易丢信息。

就像你把一整本书压缩成一张便签,再指望它帮你复述全文,难度太大了。

2. Bahdanau 注意力的思路

Bahdanau 注意力的关键改进是:

解码器在生成每一个词时,都可以回头看编码器输出的全部位置,并决定当前最该关注哪里。

也就是说,翻译不是一次性看完整句就结束,而是每一步都重新分配注意力

3. 直白理解:翻译时边写边查

你可以把它想成写作文时查资料:

  • 写到这一句时,你会回头翻前面的笔记。
  • 下一句又可能参考另外一页。
  • 每一步关注的位置都可能不一样。

Bahdanau 注意力做的,就是这种“边生成,边回看”的事情。

4. 结构上发生了什么?

最简单地说:

  • 编码器把输入句子变成一串隐藏状态。
  • 解码器当前的状态作为 Query。
  • 编码器每个时间步的隐藏状态作为 Key 和 Value。
  • 计算注意力权重后,得到一个上下文向量。
  • 解码器用这个上下文向量去生成下一个词。

5. 一个小示意图

输入句子

编码器隐藏状态序列

注意力打分

解码器当前状态

上下文向量

生成下一个词

这个循环会一直重复,直到整句翻译完成。

6. 训练时要注意什么?

Bahdanau 注意力本身是可微分的,所以可以直接用反向传播训练。

训练时通常会配合 teacher forcing,也就是:

  • 训练阶段,把上一时刻的真实词语喂给解码器。
  • 推理阶段,模型自己根据上一步的预测继续往下生成。

7. 小结

Bahdanau 注意力最重要的价值,不只是“翻译效果变好了”,而是它证明了:

模型可以在生成过程中动态对齐输入和输出。

这一步非常关键,因为它让注意力真正成为了序列建模的核心工具。


六、多头注意力:一个人看不够,就让很多人一起看

1. 为什么要多头?

单个注意力头,通常只能从一个角度看问题。

但真实语言往往很复杂:

  • 有的词关系是主谓关系。
  • 有的词关系是修饰关系。
  • 有的词关系隔得很远。

如果只用一种关注方式,模型很可能看漏。

所以就有了 多头注意力

2. 多头注意力的直观理解

你可以把它想成开会:

  • 一个同学负责看主语和谓语。
  • 一个同学负责看修饰词。
  • 一个同学负责看长距离依赖。
  • 最后大家把结论汇总起来。

这比一个人从头看到尾更稳。

3. 多头到底做了什么?

多头注意力通常包含以下步骤:

  1. 把输入投影到多个子空间。
  2. 每个子空间单独做一次注意力。
  3. 把多个头的结果拼接起来。
  4. 再做一次线性变换。

这样模型就能同时从多个角度“读同一句话”。

4. 一个简单的比喻

如果单头注意力像一副望远镜,那么多头注意力就像:

  • 一组不同焦距的望远镜
  • 一起看同一个目标

有的看细节,有的看全局。

5. 多头的好处

  • 表达能力更强。
  • 能学到不同类型的关系。
  • 更适合复杂序列任务。

6. 小结

多头注意力的核心思想其实很朴素:

让多个注意力模块并行工作,每个模块负责看不同的“重点”。

这也是 Transformer 之所以强大的关键组件之一。


七、自注意力和位置编码:同一句话里,词语彼此交流

1. 什么是自注意力?

前面我们讲的注意力,常常是“一个输入去关注另一个输入”。

自注意力 更直接:

同一个序列里的每个位置,都会去关注同一序列里的其他位置。

也就是说,句子里的每个词,都可以“和自己队伍里的其他词开会”。

比如在句子“我把书放在桌子上”里:

  • “放”可能会关注“书”和“桌子”。
  • “桌子”可能会关注“上”。
  • “书”可能会关注“放”。

于是,每个词都能拿到一个融合了上下文的新表示。

2. 自注意力为什么强?

它有三个很明显的优点:

  1. 可以并行计算,不像 RNN 那样必须一步一步读。
  2. 能直接建立远距离依赖,不需要层层传递。
  3. 表达灵活,能根据任务动态决定关注谁。

3. 和 CNN、RNN 比一比

模型 主要特点 优点 局限
CNN 局部感受野,擅长看邻近区域 计算高效,局部特征强 远距离依赖需要多层堆叠
RNN 按顺序读取序列 自然适合时间序列 难并行,长距离信息容易衰减
自注意力 序列内部任意位置都能互相看 全局建模强,并行能力好 没有位置编码时,不知道顺序

如果只看这一张表,你就已经抓住了自注意力的主线:

它强在“全局看、并行算、动态选重点”。

4. 但它也有一个天然问题:不知道顺序

注意力机制本身只会算“谁和谁相关”,但它并不知道谁在前、谁在后。

如果你把“我爱你”和“你爱我”的词都打乱,单靠注意力很难判断顺序差异。

所以我们还需要 位置编码

5. 位置编码:给每个位置发一个“座位号”

位置编码就是给序列里的每个位置加上一个表示“我在第几个”的信息。

这样模型就不只知道“有哪些词”,还知道“这些词的先后顺序”。

经典的正弦位置编码可以理解成:

  • 低维度变化快,容易区分相邻位置。
  • 高维度变化慢,帮助模型感知更远的距离。

6. 一个简化版代码示意

import torch

def positional_encoding(num_positions, num_hiddens):
    # 初始化一个全零张量,形状为 (1, 序列长度, 隐藏维度)
    X = torch.zeros((1, num_positions, num_hiddens))
    # positions 是每个位置的索引,形状为 (num_positions, 1)
    positions = torch.arange(0, num_positions).reshape(-1, 1)
    # div_term 用于控制不同维度正弦/余弦函数的频率
    # 这里按照论文做法,在偶数和奇数维上使用不同的频率比例
    div_term = torch.exp(
        torch.arange(0, num_hiddens, 2) * (-torch.log(torch.tensor(10000.0)) / num_hiddens)
    )
    # 偶数维用 sin,步长为 2(0::2),把 positions 与频率相乘后取 sin
    X[0, :, 0::2] = torch.sin(positions * div_term)
    # 奇数维用 cos,步长为 2(1::2)
    X[0, :, 1::2] = torch.cos(positions * div_term)
    # 返回位置编码张量,通常会加到输入的词嵌入上
    return X

# --- 示范用法 ---
pe = positional_encoding(6, 8)   # 生成长度为 6、隐藏维度为 8 的位置编码
print('positional encoding shape =', pe.shape)  # 打印形状以验证输出:应为 (1, 6, 8)

你不用死记这段代码,只要记住一句话:

位置编码是在告诉模型:这些词虽然同时出现,但它们的顺序不能乱。

7. 小结

自注意力负责“看内容关系”,位置编码负责“看顺序信息”。

两者合在一起,模型才真正知道一句话是什么、先后顺序又是什么。


八、Transformer:只靠注意力,也能搭起强大系统

1. Transformer 为什么重要?

2017 年,Transformer 一出现,就把很多序列任务的规则改写了。

它最大的特点是:

不靠循环,主要靠注意力。

这意味着它既能并行处理,又能建模长距离关系,还特别适合大规模训练。

2. Transformer 的整体结构

最经典的 Transformer 可以分成两部分:

  • 编码器(Encoder):把输入句子编码成更有信息的表示。
  • 解码器(Decoder):根据编码结果逐步生成输出。

你可以把它想成“读题 + 写答案”:

  • 编码器负责读懂题目。
  • 解码器负责一步步写答案。

3. 一个 Transformer 块里有什么?

每个基本模块通常包括:

  1. 多头注意力
  2. 基于位置的前馈网络
  3. 残差连接
  4. 层规范化

这几样东西组合起来,让模型既能学关系,又能保持训练稳定。

4. 基于位置的前馈网络:给每个位置再加工一次

注意力负责“把信息聚合起来”,前馈网络负责“把每个位置再加工一遍”。

你可以把它理解成:

  • 注意力像开会,把有用信息汇总出来。
  • 前馈网络像个人复盘,把自己的理解再提炼一次。

5. 残差连接和层规范化:训练稳定的关键

深层网络最怕什么?

怕信号传不过去,怕训练不稳定。

所以 Transformer 里要加:

  • 残差连接:让信息可以“绕一下路”直接传过去。
  • 层规范化:让每层输出别太极端,训练更稳。

它们就像给模型装上减震器和旁路通道。

6. 解码器为什么要“看未来”被禁止?

在生成任务里,模型不能提前偷看后面的词。

比如你在写句子时,不能先看到答案再写过程。为了保证训练和生成逻辑一致,解码器里的自注意力会用掩蔽机制屏蔽未来位置。

这就是为什么 Transformer 解码器里有 masked self-attention

7. 训练时发生什么?

训练 Transformer 时,通常还是用交叉熵损失。

步骤可以非常直白地理解成:

  1. 编码器读入源句子。
  2. 解码器根据已知前缀预测下一个词。
  3. 和真实答案比对,算损失。
  4. 反向传播更新参数。

8. Transformer 的优势和代价

优势:

  • 并行能力强。
  • 长距离建模好。
  • 结构统一,容易扩展。

代价:

  • 自注意力计算量随序列长度增长很快。
  • 序列很长时,显存和算力压力会变大。

也就是说,它很强,但并不是“没有代价的万能钥匙”。

9. 一张总览图

输入序列

位置编码

多头自注意力

残差连接 + 层规范化

前馈网络

残差连接 + 层规范化

编码器输出

解码器自注意力

编码器-解码器注意力

前馈网络

输出序列

如果你把这个图看懂了,Transformer 的主线就基本抓住了。

10. 小结

Transformer 的真正厉害之处,不是某一个小技巧,而是它把以下几件事整合到了一起:

  • 注意力
  • 多头并行
  • 位置编码
  • 残差连接
  • 层规范化

最后形成了一个既能表达复杂关系、又能高效训练的通用架构。


九、把整章串起来:注意力机制到底在解决什么?

如果用一句话总结整章,那就是:

注意力机制让模型学会根据当前任务,动态决定“看哪里、看多少、怎么看”。

它不是替代所有模型,而是给模型增加了一种非常重要的能力:有选择地处理信息

你可以把整章内容串成一条线:

  1. 注意力提示告诉我们,模型可以像人一样先抓重点。
  2. Nadaraya-Watson 说明了“按相关性加权”的原始思想。
  3. 评分函数把“相关性”变成可计算、可训练的权重。
  4. Bahdanau 注意力让序列模型能边生成边回看。
  5. 多头注意力让模型从多个角度同时看问题。
  6. 自注意力让序列内部的信息彼此交流。
  7. 位置编码补上顺序信息。
  8. Transformer 把这些部分组合成一个强大的通用架构。

这就是从“会关注”到“善于关注”的完整演化。


十、给新手的记忆法

如果你刚学这一章,不用一口气背完所有公式。先记住下面这几句就够了:

  1. Query 是我现在想找什么。
  2. Key 是每条信息的标签。
  3. Value 是真正要取走的内容。
  4. 注意力就是先打分,再加权平均。
  5. 多头注意力就是让多个“关注视角”一起工作。
  6. 自注意力是序列内部自己和自己交流。
  7. 位置编码是给模型补上顺序感。
  8. Transformer 是只靠注意力搭起来的强大架构。

只要这几句话在脑子里站稳了,后面看论文、看代码、看实现都会顺很多。


十一、练习一下

你可以先不写代码,直接尝试回答这几个问题:

  1. 为什么注意力机制比“平均看所有输入”更聪明?
  2. Query、Key、Value 三者分别像什么?
  3. 为什么翻译长句时,Bahdanau 注意力比早期 Seq2Seq 更有优势?
  4. 多头注意力为什么不是“重复计算”,而是“多角度建模”?
  5. 为什么自注意力需要位置编码?
  6. Transformer 里的残差连接和层规范化,分别在帮什么忙?

如果你能用自己的话把这些问题讲出来,说明你已经真正入门了。


写在最后

注意力机制最迷人的地方,不是公式有多漂亮,而是它的思路非常符合人类直觉:世界很大,不可能处处用力,真正聪明的做法,是把资源花在最相关的地方

从查询、键和值,到 Bahdanau 注意力,再到多头注意力和 Transformer,你会发现整条技术路线其实是在做同一件事:

让模型学会像人一样,先看重点,再做判断。

这也是现代深度学习里最重要的能力之一。

如果你接下来想继续学,可以把这篇当成“注意力机制总览图”。先抓主线,再回头看细节,学习效率会高很多。

注:本文参考《动手学深度学习》注意力机制:https://zh.d2l.ai/chapter_attention-mechanisms/index.html

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐