夜深了,派出所值班室接到一连串报警:一只猫被狗追赶,撞翻了三家摊位。目击者说法各不相同——水果摊主坚定地说“狗追猫”,面馆老板却咬定“猫先惹的狗”,巷口大爷只记得“有猫有狗在跑”。如果只派一位侦探,他大概率只会盯住自己熟悉的那条线索。但如果同时派八组侦探,一组查脚印、一组查监控、一组走访目击者……最后把八份报告拼在一起,真相就立体了起来。 多头注意力(Multi-Head Attention)正是Transformer里的这支“侦探团”。普通的注意力机制像一位侦探,每次只能从一个视角打量句子;多头注意力则把模型拆成多个“头”,每个头用自己的投影矩阵将句子映射到不同子空间,并行计算各自的注意力,再把结果拼接融合。于是模型能同时捕捉语法、指代、语义等多种关系。 这篇文章就带你从“一组侦探怎么干活”讲起,一步步看懂多头为什么有效、头数怎么选、和单头差在哪、如何可视化观察——全程用案件侦查的故事来讲,零公式负担也能跟上。
学懂多头注意力,你就握住了Transformer的心脏;面试聊架构、项目做调优、研究看论文,都离不开这套“多视角并行”的思维框架。
可视化演示
动态演示Multi-Head多头注意的核心机制,通过全局/局部/动态/静态四种模式展示关键概念与交互流程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
Multi-Head多头注意:多头注意 多头注意力的拆分与并行计算机制 多头相比单头的优势与信息互补 核心机制可视化 Multi-Head多头注意整体架构与关键组件
注意力回顾
在认识多组侦探之前,先看一位侦探怎么独立查案。假设现在要弄清“猫追老鼠”里“追”的主语是谁。这位侦探手里有三样东西:Q(案件问题)——“谁是'追'这个动作的发出者?”;K(线索标签)——每个词贴着的姓名牌,写着“我是猫”“我是追”“我是老鼠”;V(线索内容)——每个词承载的实际信息。侦探的做法是:拿着问题 Q,依次和所有线索标签 K 比对,看谁最匹配,然后按匹配程度从对应的线索内容 V 里提取答案。
这正是缩放点积注意力的工作方式。输入序列的每个词都生成 Query、Key、Value 三个向量,注意力层用 Q 与所有 K 做点积打分,经过缩放和 softmax 后得到权重,最后对 V 做加权求和。整个过程像侦探在一次侦查中把全部线索过了一遍。
问题在于:这位侦探只有一种“查案习惯”。他拿“谁是主语”这个问题也许能答对,但让他同时关注“猫和老鼠谁先招惹谁”“句子里还有没有第三只动物”就力不从心了。单头注意力一次只产生一种注意力分布——这正是要引出多头的理由。
- Q 查询向量当前词发出的“问题”,例如“我在找谁跟我是同类”。
- K 键向量句子中每个词自带的“身份标签”,用来和问题匹配。
- V 值向量每个词承载的实际“线索内容”,匹配通过后按权重取出。
- 点积打分 QK^T把问题与每个标签做内积,分数越高代表越相关。
- 缩放除以 √d_k维度越大点积数值越大,除以根号维度防止 softmax 进入饱和区。
- softmax 归一化把分数变成一组总和为 1 的权重,代表“分给每个词多少注意力”。
- 加权求和乘以 V按权重把线索内容混合在一起,得到当前词的新表示。
多头核心思想
于是局长决定派八组侦探同时查案:一组专攻语法结构,一组专盯动作指向,一组只看语义关系……每组侦探都领到一本“专属查案手册”,手册上规定了他们侧重把线索往哪个方向解读。八组互不干扰、并行办案。
多头注意力的“头”,其实就是一组独立运行的注意力子模块。每个头拥有自己的一套 Q/K/V 投影矩阵,把原始输入映射到不同的子空间。注意:这里不是把句子复制八份,而是用八个不同的线性变换去“变换视角”——同一句话,在不同子空间里被看到的结构完全不同。
你可以把 W_i^Q 想成第 i 组侦探戴的“眼镜”——戴上它,同一句话在他们眼里呈现出不同的重点。八组侦探各看一遍,看到的侧面完全不同,信息自然互补,这正是“多头”的核心价值。
- QW_i^Q 查询投影用第 i 个头专属的“查案手册”把问题向量映射到该头所在子空间。
- KW_i^K 键投影把每条线索的标签也映射到同一子空间,保持口径一致。
- VW_i^V 值投影把线索内容映射过去,供该头后续做加权求和使用。
- Attention 独立运算在各自子空间中执行标准缩放点积注意力,得到该头的输出。
头的拆分与并行
一组侦探一本手册,八组就是八本不同的手册。侦探们领到的任务书里最关键的一条规则是:把整块“线索板”按人数切成均匀的小块,每人负责一块区域,互不越界。
在实现层面,多头拆分发生在维度上。设模型维度为 d_model,输入矩阵 Q 的形状是(序列长度,d_model)。要拆成 h 个头,就准备 h 个形状为(d_model, d_k)的投影矩阵,其中 d_k = d_model / h。每个头只在自己的 d_k 维子空间里做注意力计算,因此天然可以并行。
就像八组侦探每人只负责自己的责任区,头多了单头维度会变窄,但这正是“分而治之”的代价与妙处——每个头在低维空间里计算,速度更快,组合起来又能覆盖全部信息。
- 原始矩阵 Q未拆分时的查询矩阵,形状为(序列长度,模型维度)。
- 投影矩阵 W_i^Q第 i 个头的专属线性变换,负责从大空间中切出一块子空间。
- 矩阵乘法 QW_i^Q通过矩阵乘法完成“视角切换”,每个词被投影到 d_k 维子空间。
- K、V 同样处理键和值做一模一样的分头投影,三者共享同一切分逻辑。
拼接与融合
各组侦查报告写完后,总部会议室坐满八组侦探。他们不会各自向局长汇报,而是先由秘书把所有报告按顺序钉成一本厚册子,再交给局长做最终批示——这个“汇总再批示”的动作,就是多头注意力的最后两步。
对应到公式:先把 h 个头的输出按特征维度拼接(Concat),得到宽度为 h×d_k 的向量;再乘一个输出投影矩阵 W^O,把宽度压回 d_model。这样输出形状与输入一致,方便接残差连接和后续的层堆叠。
拼接让八个侧面的信息完整保留,W^O 则像局长“综合批示”,把各组线索揉成一份能直接用的行动方案。到此,一次多头注意力的前向计算就完成了。
- 各头输出 head_i第 i 个头独立计算出的注意力结果,形状为(序列长度,d_k)。
- 拼接 Concat把所有头的输出按特征维度首尾相接,得到(序列长度,h×d_k)。
- 输出投影 W^O一个可学习的线性变换矩阵,负责把宽向量压回 d_model 维。
- 整体融合拼接结果与 W^O 相乘,完成跨头信息交换,得到最终输出。
头数与维度选择
局长现在面临一个现实问题:到底派几组侦探?派少了,角度不够;派多了,每组只剩巴掌大的责任区,谁都查不细,会议室也坐不下。
h 的选择不是越多越好。d_model 是固定的总预算,头数越多,每个头的维度 d_k = d_model / h 越小,单头的表达能力会下降。标准 Transformer 在 d_model=512 时取 h=8,即 d_k=64;更大的模型常用 h=16;也有实验用 h=12。头数必须能整除模型维度,否则切分不均匀。
经验法则:先保证 d_model 能被 h 整除,再在 8~16 之间试验。就像侦查组规模——人手充足(模型大)可以多派几组,人手紧张(模型小)就专注一组,避免每个头都“营养不良”。
- d_model 总预算整个多头层的特征宽度(如 512),是固定的“资源池”。
- h 头数把资源池平均分成几份,决定“视角”的数量。
- d_k 每头维度每头分到的宽度,等于模型维度除以头数,必须保证整除。
单头对比分析
有人问:派一位全能侦探,一个人把所有角度都看一遍,不行吗?理论上有这种可能,但实际上一位侦探的思维方式是线性的,一次只能抓一条主线;八组侦探却可以同时追踪八条线索,互不干扰。
数学上,单头注意力的一步只能产生一种注意力分布,即每个位置对其它位置的一组分派权重;多头则产生 h 种分布,模型能同时建模多种关系——语法、指代、语义可以各用一个头去捕捉。而参数量上,标准实现把多头所需的投影矩阵合并成一个大矩阵,总参数与单头基本持平。
所以多头的优势不是“更多人”,而是“更多并行视角”:用几乎相同的参数成本,换来 h 倍的关系建模宽度。这就是 Transformer 选择多头、而不是堆叠多个单头层的深层原因。
- 4 组投影矩阵Q、K、V、O 四组线性变换,是多头层参数的主体。
- d_model^2 每组的规模每个投影矩阵形状为 d_model×d_model,约 d_model² 个参数。
- 总参数 4d_model²四组合计约 4 倍 d_model 平方,单头与多头标准实现下规模一致。
- 约等于关系多头只是把一个大矩阵重新组织为分组形式,参数总量基本不变。
可视化与调参
案件结束后,局长翻开八组侦探的报告。报告上每句话旁边都标着醒目色块:深色表示这组侦探重点关注的词,浅色表示顺带扫过。八份报告的标色方式完全不同——这就是注意力热力图的故事。
把第 h 个头中第 i 个词对第 j 个词的注意力权重画成矩阵,颜色越深代表权重越大。研究者在 BERT 上做过大量可视化,发现底层头偏向局部语法关系(相邻词、修饰关系),高层头偏向长程语义关系(指代、共指)。同一个词在不同头眼中,受关注的对象可以截然不同。
这也给调参提供了方向:如果发现某些头的热力图几乎一模一样,说明头间冗余,可以适当减少头数;如果某层全靠一个头,其他头成了“摆设”,就要检查初始化或训练稳定性。可视化让多头这头“大象”不再黑箱——打开报告,每个头干了什么一目了然。
- 原始分数 e_{ij}^h第 h 个头中,第 i 个词对第 j 个词的未归一化注意力得分。
- 指数化 exp把任意实数分数映射为正数,方便后续当作权重使用。
- 分母求和固定 i,对该行所有词求指数和,作为归一化因子。
- 注意力权重 α归一化后得到 0–1 之间的权重,表示第 i 个词分配给第 j 个词的注意力比例。
AI 应用场景
机器翻译中的多视角对齐
阶段:编码器-解码器注意力(模型训练与推理)
解决的问题:传统统计机器翻译需要人工设计词对齐规则,语序差异大时容易错位。多头注意力让解码器生成每个词时,用多个头分别对齐源句的不同层面:一个头对齐词面,一个头对齐句法,一个头对齐语义,翻译更准确。
具体案例:翻译“那只被狗追的猫跑进了巷子”时,一个头关注“猫←追”的主被动关系,另一个头关注“巷子←跑进”的位置关系,避免把“狗”误当成被追对象。DeepL、Google Translate 的底层架构都依赖该机制。
句子语义匹配与问答系统
阶段:自注意力特征抽取(预训练与微调)
解决的问题:传统方法用词向量平均或 RNN 串行编码,长文本信息容易被稀释。多头自注意力让模型在判断两句话是否相似时,一个头关注字面重叠,另一个头关注深层语义结构,显著提升匹配准确率。
具体案例:搜索引擎判断“如何防止手机掉电快”和“手机耗电快怎么办”是同一意图时,一个头抓“手机、掉电、耗电”等词面重叠,另一个头抓“如何、怎么办”的意图结构,从而正确匹配,返回更相关的结果。
Python 演示
import numpy as np def softmax(x): # 数值稳定版softmax, 先减去每行最大值防止指数溢出 e_x = np.exp(x - np.max(x, axis=-1, keepdims=True)) return e_x / np.sum(e_x, axis=-1, keepdims=True) def scaled_dot_product_attention(Q, K, V): # 缩放点积注意力, scores = Q*K^T / sqrt(d_k) d_k = Q.shape[-1] # transpose(0,1,3,2) 交换K最后两维, 实现批量矩阵转置 scores = np.matmul(Q, K.transpose(0, 1, 3, 2)) / np.sqrt(d_k) weights = softmax(scores) out = np.matmul(weights, V) return out, weights class MultiHeadAttention: def __init__(self, d_model=8, num_heads=2): # 保证 d_model 能被 num_heads 整除 assert d_model % num_heads == 0 self.d_model = d_model self.num_heads = num_heads self.d_k = d_model // num_heads # 四组可学习投影矩阵, 参数量与单头基本一致 self.W_Q = np.random.randn(d_model, d_model) * 0.1 self.W_K = np.random.randn(d_model, d_model) * 0.1 self.W_V = np.random.randn(d_model, d_model) * 0.1 self.W_O = np.random.randn(d_model, d_model) * 0.1 def split_heads(self, x): # 将 (batch, seq, d_model) 拆成 (batch, heads, seq, d_k) batch, seq, _ = x.shape x = x.reshape(batch, seq, self.num_heads, self.d_k) return x.transpose(0, 2, 1, 3) def combine_heads(self, x): # 把 (batch, heads, seq, d_k) 重新拼回 (batch, seq, d_model) batch, heads, seq, d_k = x.shape x = x.transpose(0, 2, 1, 3) return x.reshape(batch, seq, heads * d_k) def forward(self, Q, K, V, return_weights=False): # 1. 线性投影, 把输入映射到各头子空间 Q = np.matmul(Q, self.W_Q) K = np.matmul(K, self.W_K) V = np.matmul(V, self.W_V) # 2. 拆分成 h 个头 Q, K, V = self.split_heads(Q), self.split_heads(K), self.split_heads(V) # 3. 每个头独立做缩放点积注意力 attn, weights = scaled_dot_product_attention(Q, K, V) # 4. 拼接所有头的结果 attn = self.combine_heads(attn) # 5. 输出融合回模型维度 out = np.matmul(attn, self.W_O) if return_weights: return out, weights return out if __name__ == "__main__": np.random.seed(42) batch, seq_len, d_model = 1, 4, 8 x = np.random.randn(batch, seq_len, d_model) mha = MultiHeadAttention(d_model=8, num_heads=2) out, weights = mha.forward(x, x, x, return_weights=True) print("输入形状:", x.shape) print("输出形状:", out.shape) print("各头注意力权重(行=查询词, 列=被关注词):") for h in range(mha.num_heads): print("头%d:\n" % h, np.round(weights[0, h], 2))
e_x = np.exp(x - np.max(x, axis=-1, keepdims=True))先减去最大值再算指数,防止大数溢出,是 softmax 的数值稳定写法scores = np.matmul(Q, K.transpose(0, 1, 3, 2)) / np.sqrt(d_k)批量做 Q 和 K 的点积并除以根号维度,完成缩放打分self.d_k = d_model // num_heads每个头分到的维度,整数除法保证 d_model 被 h 整除x = x.reshape(batch, seq, self.num_heads, self.d_k)把模型维度切分成 h 块,是“拆头”的关键一步x = x.transpose(0, 2, 1, 3)把头维度挪到第二维,让所有头可以批量并行计算Q = np.matmul(Q, self.W_Q)先把输入投影到子空间,相当于每队侦探戴上“专属眼镜”out = np.matmul(weights, V)按注意力权重对线索内容加权求和,得到该头输出attn = self.combine_heads(attn)把 h 个头的输出拼接回 d_model 维,完成“汇总报告”
常见误区
误区新手调参时常觉得多头就是“多多益善”,直接把 h 设成 64 甚至 128,以为更多头=更多视角=更强模型。
正解头数受维度预算约束。d_model 固定时,h 越大每头维度 d_k = d_model / h 越小,单头表达能力严重下降,甚至退化成噪声。标准实践是 h=8~16,且必须保证 d_model 能整除 h。就像侦探团不是人越多越好——每组只剩一个人的话,线索根本看不全。
为何常见:直觉上“更多=更好”,忽略了头之间要瓜分同一份维度资源,没有建立“总预算固定”的成本意识。
误区以为多头是对同一组 Q、K、V 重复计算 h 次,最后取个平均,相当于“多算几遍更保险”。
正解每个头有独立的投影矩阵 W_i^Q / W_i^K / W_i^V,会把 Q、K、V 映射到不同子空间。各头学到的注意力分布完全不同,信息互补,而不是重复计算取平均。就像不是让同一批侦探反复问同样的问题,而是让不同专长的侦探用各自的查案手册去查不同方向。
为何常见:看到“多个头”容易联想到“复制几份”,没意识到投影矩阵才是每个头“视角不同”的根本来源。
误区认为只要是多头就必然吊打单头,任何任务上换多头都能提升指标。
正解数据量少时多头更容易过拟合,简单任务单头已经足够;多头带来更强表达能力,但也需要更多数据和算力来“喂饱”。小案件派八组侦探反而添乱,一个干练的老侦探就够了。
为何常见:Transformer 的流行给人“多头=先进”的刻板印象,忽略了模型容量与数据量需要匹配的基本规律。
误区调试时不看热力图,默认每个头算出来的结果差别不大,“反正都是注意力”。
正解正常训练下各头会分化出不同分工:有的头关注相邻词(语法),有的头关注远距离词(指代),有的头关注语义关系。如果所有头热力图几乎一样,说明存在头间冗余或初始化退化,需要处理,而不是正常现象。
为何常见:缺少可视化的直观感受,没看过“头部分工”的真实分布,容易凭空脑补成均匀一致。
面试题
Q1. 在多头注意力机制中,“头”的主要作用是什么?
- A.每个头计算不同的注意力分布← 正确答案
- B.所有头共享同一套QKV矩阵
- C.头数设置越多模型越强
- D.头的存在是为了降低运算复杂度
答案:A。每个头拥有独立的 Q/K/V 投影矩阵,从不同子空间捕捉信息,所以 A 正确。B 错误,各头投影矩阵不同;C 错误,头数过多每头维度太小反而变差;D 错误,多头并不会降低运算复杂度。
Q2. 设 d_model=512,h=8,每个头的 Key 维度 d_k 是多少?
- A.512/8=64,头维度为64← 正确答案
- B.512/4=128,头维度为128
- C.512/2=256,头维度为256
- D.512保持不变,不做拆分
答案:A。d_k = d_model / h = 512/8 = 64。这是多头拆分的基本约束,保证各头均分模型维度且能整除。其余选项分别对应错误的除法或“不拆分”。
Q3. 注意力机制中 Q、K、V 分别对应侦探比喻中的什么?
- A.问题、标签、内容← 正确答案
- B.标签、问题、内容
- C.内容、标签、问题
- D.问题、内容、标签
答案:A。Q 是当前词发出的“问题”(要查什么),K 是每个词的身份“标签”(靠什么被匹配),V 是实际“内容”(匹配后取出什么)。对应关系固定,故选 A。
Q4. 多头注意力中拼接后乘以 W^O 的主要目的是什么?
- A.将拼接结果线性融合回原维度← 正确答案
- B.把每个头的维度扩展到两倍
- C.对Q、K、V做一次额外的归一化
- D.将注意力权重强行等分防过拟合
答案:A。Concat 后的宽度为 h×d_k,W^O 将它投影回 d_model,使输出形状与输入一致。B 方向反了,C、D 都不是 W^O 的用途。
Q5. 缩放点积注意力中除以 √d_k 的主要原因是?
- A.防止点积过大导致梯度消失区域← 正确答案
- B.让矩阵乘法的计算量减少一半
- C.将特征维度扩大并且保留更多信息
- D.对注意力权重做最大最小归一化统一
答案:A。d_k 较大时点积数值随维度增长而变大,softmax 会进入梯度极小的饱和区。除以 √d_k 把分数拉回合理范围。B、C、D 都与缩放目的无关。
Q6. 关于多头与单头的参数量,下列说法正确的是?
- A.标准实现下两者参数量基本相近← 正确答案
- B.多头参数量一定明显小于单头
- C.参数会随头数增加按倍数翻增
- D.多头机制完全不增加任何参数
答案:A。标准实现将各头的投影合并为一个大矩阵(d_model×d_model),再加上同样规模的 W^O 输出投影,总参数与单头基本一致。C 说按倍数翻增不对,B、D 也过于绝对。
Q7. 下列哪些属于多头注意力相比单头的优势?
- A.能从不同子空间捕捉多种信息← 正确答案
- B.增强模型对复杂关系的表达能力← 正确答案
- C.一定可以减少模型的总参数数量
- D.能够同时关注序列中多个不同位置← 正确答案
答案:ABD。A、B、D 都是多头的核心优势。多头并不减少参数量(标准实现与单头相当),C 错误。
Q8. 关于多头注意力的拼接与融合,下列说法正确的有?
- A.各头输出先拼接再整体线性变换← 正确答案
- B.输出投影矩阵将信息融合回原维度← 正确答案
- C.各头相互独立可以完全并行计算← 正确答案
- D.拼接操作会让特征维度直接减半
答案:ABC。公式明确是先 Concat 再乘 W^O,W^O 负责融合回 d_model,各头独立计算天然可并行。D 错误,拼接是增加维度而非减半。
Q9. 可视化分析多头注意力时,通常能观察到什么?
- A.不同头通常显现出不同的分工← 正确答案
- B.底层头倾向于关注局部语法关系← 正确答案
- C.高层头倾向于关注长程语义关系← 正确答案
- D.所有头的注意力分布几乎完全相同
答案:ABC。大量可视化研究发现底层头关注相邻词和语法,高层头关注指代和语义,各头分工不同。若所有头几乎相同,往往是冗余或训练问题,不是正常现象。
Q10. 调试时发现某个多头模型所有头的热力图几乎相同,最可能的原因是?
- A.模型已充分收敛的正常表现
- B.头间冗余或初始化导致的退化← 正确答案
- C.序列长度过短的必然结果
- D.学习率设置过低的正常现象
答案:B。正常训练下各头会分化出不同关注模式。全部相同说明头间冗余、初始化不良或容量利用不充分。A、C、D 都不是“正常现象”的合理解释,B 最符合实践经验。