多头注意力

一次注意力只能捕捉一种关系,多个头并行,让模型同时从多个角度看句子。

知识课 · 约 30 分钟 · 更新于 2026-08-14

句子里的关系不止一种

在「缩放点积注意力」中,我们给注意力装上了可学习的 。但一组矩阵训练完后只能形成 一种 固定的关注模式,而句子里的关系远不止一种:

看出问题了吗:同一个词「追着」,在「谁在追」里要去关注「小猫」,在「追什么」里却要去关注「球」。 同一个词在不同关系下要关注的对象并不相同,彼此还会互相冲突 。 一套固定的注意力分配只能形成一种关注模式,无法同时覆盖这么多种关系。解决思路很直接: 并行运行多组独立的注意力 。

多头注意力:并行的多组视角

多头注意力(Multi-Head Attention) 就是并行运行 h 组相互独立的注意力。每一组称为一个 头(head) ,第 i 个头有自己的一套投影矩阵 , 独立完成「投影 → 打分 → 归一化 → 加权求和」的全过程。

各头的参数互不共享,训练中自然会 分化出不同的关注模式 : 有的头可能学会关注语法关系,有的头学会捕捉语义关联。

维度切分:每个头一个低维子空间

h 个头会不会让计算量变成 h 倍?不会,这里有个精巧的安排: 不是每个头都用全维度,而是把 切成 h 份 , 每个头在自己的低维子空间里做注意力。

「缩放点积注意力」中约定的记号在此细化:从现在起,

例如 、h = 8 时,每个头的 Q、K、V 都是 64 维。每个头的投影矩阵形状是 ,把 h 个头的 Q、K、V 投影合起来,参数量和计算量与一个全维度的单头基本相当。 把维度切给多个头,几乎不增加这部分开销 。

合并:拼接 + 输出投影

每个头都输出一份 L × 的结果,h 份怎么合成一份?分两步:先把它们沿特征维 拼接(concatenate) 回 L × ,再经过一个可学习的 输出投影矩阵 把各头的信息融合起来:

拼接只是把各头结果排在一起,互相还没有「交流」; 负责把不同头发现的信息加权组合成最终输出。

实现 MultiHeadAttention:形状是最大难点

多头注意力的代码逻辑不难,难在 张量形状的变换 :怎么把 (B, L, d_model) 切成 h 个头、做完注意力再拼回去。 逐步往下走,左侧代码会跟着高亮,下方画面同步展示这一步张量形状怎么变。 这里取 、h = 8、 , 输入是「小猫 追着 球 跑」4 个词(B = 1、L = 4)。

看一看:每个头在注意什么

在同一个句子上画出各头的注意力热力图。需要先说明:我们手里这个模型是 随机初始化、未经训练 的, 此时只能观察到一个 弱结论 :各头因投影矩阵不同,给出的权重分布 各不相同 。 至于「有的头关注邻近词、有的头关注远距离词」这种有意义的分工,要训练之后才会出现。

下图把这个未训练模型在「小猫 追着 球 跑」上的注意力画了出来: 4 个头各一张方格图 ,每张的行是当前词、列是它正在注意的词, 每一行的权重之和为 1,颜色越深表示分到的注意力越多。 点击「重新随机初始化」会给所有头换一套随机投影矩阵、重算一遍。

未训练模型在「小猫 追着 球 跑」上各头的注意力权重(行=当前词、列=被注意词,每行和为 1)。各头的投影矩阵是随机初始化的,因此权重分布 彼此不同 ;反复随机会发现这些模式都在随机变化, 此时还谈不上「某个头专门关注某种关系」这类有意义的分工,训练之后才会出现。

随机初始化只能看到「各头彼此不同」。那训练之后呢?下面是一张 训练好的 Transformer 模型上各注意力头的真实可视化, 来自论文 What Does BERT Look At? An Analysis of BERT's Attention 。每个头连出的线表示它把注意力分给了哪些词,和随机初始化时的杂乱不同, 训练后 不同的头分化出了各自明确、可解读的分工 。

一个看似多余的设计

注意上面实现里的一个细节: forward(query, key, value) 接收 三个独立的输入 。做自注意力时,三者传的是同一个张量 x ,既然如此,为什么不直接只收一个参数?

这是有意为之:Query 和 Key / Value 不必来自同一个序列 。这个接口现在看着冗余,之后讲到「交叉注意力与解码器」时它会直接派上用场,一行都不用改。先记住这个伏笔。

思想实验:把词序打乱会怎样?

最后留一个思想实验。回想注意力的计算过程:

直觉上「我打你」和「你打我」天差地别,输出理应不同。但仔细想想打分和求和的过程…… 好像哪里不对劲。之后的《位置编码》会用实验揭晓答案。