多头注意力的计算冗余分析哪些头可以被安全剪掉而不损失精度一、多头注意力并非头越多越好Transformer 论文引入多头注意力的初衷是让不同头关注不同的表示子空间——可以类比为 CNN 中不同卷积核学习不同特征。然而后续研究Voita et al., 2019; Michel et al., 2019发现了一个被忽视的事实在训练收敛后很多注意力头的行为高度冗余。具体表现为某些头可以被完整移除而模型精度几乎不受影响某些头在不同输入上的注意力分布几乎完全相同可合并还有些头在任何输入上都输出近乎均匀的注意力分布干脆不工作。本文聚焦注意力头冗余的量化检测方法和安全剪枝策略。核心问题是如何在训练后识别哪些头是冗余的以及剪枝后如何在不重新训练的前提下恢复精度。flowchart TD A[已训练的Transformer模型] -- B[头重要性评估] B -- C1[基于权重的评估] B -- C2[基于激活的评估] B -- C3[基于梯度的评估] C1 -- C1a[L1范数: 权重绝对值求和] C2 -- C2a[注意力熵: 分布的集中度] C2 -- C2b[头间余弦相似度: 检测冗余] C3 -- C3a[Taylor展开: 对loss的影响] C1a C2a C2b C3a -- D[重要性排序] D -- E[剪枝策略选择] E -- F1[逐层均匀: 每层剪相同比例] E -- F2[全局排序: 跨层竞争] E -- F3[层敏感: 底层保留更多头] F1 F2 F3 -- G[剪枝 微调恢复] G -- H[精度验证]二、三种头重要性评估方法的优劣方法一基于权重的L1范数。计算每个头对应的 Q、K、V、O 投影权重的 L1 范数之和。优点是计算成本极低不需要任何前向传播缺点是完全忽略了输入数据的影响——某个头的权重大可能只是因为初始化比别的头大而非它学到了更有意义的表示。方法二基于注意力分布的熵。对一批代表性输入收集每个头的注意力分布计算其熵。高熵意味着分布接近均匀——这个头没有在选择性关注而是在平均地看所有位置。这种头的信息贡献很小通常是首选的剪枝目标。缺点是计算成本随数据量和序列长度线性增长。方法三基于Taylor展开的损失敏感度。计算移除某个头对训练损失的近似影响一阶Taylor展开即权重 × 梯度的乘积。这是理论最严谨的方法因为它直接估计剪枝对模型性能的影响而非间接度量头的重要性。缺点是需要在训练数据上做一次完整的 forwardbackward pass。import torch import torch.nn as nn from typing import Dict, List, Tuple import numpy as np def compute_head_importance_by_entropy( model: nn.Module, dataloader, num_samples: int 500, device: str cuda ) - Dict[str, List[float]]: 通过注意力分布熵评估每个头的信息集中度。 原理 一个有效的注意力头应该对少数关键位置赋予高注意力权重 低熵。如果某个头对所有位置均匀分配注意力高熵 说明它没有学到有意义的位置选择性——可以安全剪掉。 为什么用熵而非方差 方差在大值和大量小值混合时也会很高但高方差不一定 意味着信息集中。熵直接度量分布的峰度。 model.eval() head_entropies {} with torch.no_grad(): sample_count 0 for batch in dataloader: if sample_count num_samples: break batch {k: v.to(device) for k, v in batch.items()} outputs model(**batch, output_attentionsTrue) # outputs.attentions: tuple of [batch_size, num_heads, seq_len, seq_len] for layer_idx, layer_attn in enumerate(outputs.attentions): # 计算每个头的平均熵 bsz, n_heads, seq_len, _ layer_attn.shape # 熵 -sum(p * log(p)), 对最后一维目标位置 # 加极小值避免 log(0) eps 1e-9 entropies -torch.sum( layer_attn * torch.log(layer_attn eps), dim-1 ).mean(dim(0, 2)) # 在 batch 和 query 位置维度上平均 if layer_idx not in head_entropies: head_entropies[layer_idx] [] head_entropies[layer_idx].append(entropies.cpu()) sample_count batch[list(batch.keys())[0]].size(0) # 聚合所有样本的熵取均值 aggregated {} for layer_idx, entropies_list in head_entropies.items(): stacked torch.stack(entropies_list) # [num_batches, num_heads] aggregated[flayer_{layer_idx}] stacked.mean(dim0).tolist() return aggregated def compute_head_cosine_similarity( model: nn.Module, dataloader, num_samples: int 200, device: str cuda ) - Dict[str, np.ndarray]: 检测同层内注意力头之间的行为相似度。 原理 如果两个头在所有输入上的注意力分布高度相似 余弦相似度 0.9则它们捕获的信息几乎重叠。 可以保留其中一个剪掉另一个。 model.eval() # 存储每个头在所有样本上的注意力分布展平 head_patterns {} with torch.no_grad(): sample_count 0 for batch in dataloader: if sample_count num_samples: break batch {k: v.to(device) for k, v in batch.items()} outputs model(**batch, output_attentionsTrue) for layer_idx, layer_attn in enumerate(outputs.attentions): bsz, n_heads, seq_len, _ layer_attn.shape # 展平为 [n_heads, bsz * seq_len * seq_len] flat_attn layer_attn.permute(1, 0, 2, 3).reshape(n_heads, -1) if layer_idx not in head_patterns: head_patterns[layer_idx] [] head_patterns[layer_idx].append(flat_attn.cpu()) sample_count batch[list(batch.keys())[0]].size(0) # 计算同层内头间余弦相似度 similarity_matrices {} for layer_idx, patterns_list in head_patterns.items(): # 拼接所有样本: [n_heads, total_elements] all_patterns torch.cat(patterns_list, dim1) # 行归一化后计算余弦相似度矩阵: [n_heads, n_heads] normed all_patterns / (all_patterns.norm(dim1, keepdimTrue) 1e-9) sim_matrix (normed normed.T).numpy() similarity_matrices[flayer_{layer_idx}] sim_matrix return similarity_matrices三、安全剪枝策略与恢复微调识别出冗余头后的操作顺序直接影响最终模型精度全局排序 逐层均匀不同层的注意力头冗余程度不同。通常底层靠近输入的头冗余度更高因为底层更多在做语法层面的模式匹配多头的互补性较弱。应允许底层剪掉更多头高层保留更多头。每次剪枝后微调一次性剪掉所有冗余头会导致精度急剧下降。渐进式剪枝每次剪10-20%的头然后微调100-500步的最终精度远高于一次性剪枝。微调时冻结已剪枝的结构对剩余头使用较小的学习率原学习率的1/10避免剧烈改变已学到的表示。四、头剪枝的失效场景跨语言模型在多语言BERT中不同头负责不同语言的现象已被多项工作证实。剪枝可能导致某些低资源语言的性能崩溃而英文评测指标完全看不出来。长序列任务对于长文档摘要或长文本分类某些头专门负责长距离依赖。这些头的注意力分布可能看起来稀疏对大多数位置权重近似0但恰好在关键远程位置上有高权重。基于熵的评估会错误地将它们标记为低效头。对抗鲁棒性有证据表明冗余头在对抗攻击和分布外泛化中起到缓冲作用——剪掉它们后模型的对抗鲁棒性可能下降即使干净样本上的精度不变。五、总结注意力头的冗余检测需要多维度交叉验证注意力熵检测不工作的头均匀分布余弦相似度检测重复工作的头。逐层均匀剪枝是次优策略应使用全局重要性排序。渐进式剪枝 低学习率微调的恢复效果远优于一次性剪枝。多语言和长序列场景下冗余头的定义需要重新审视。