从BERT到GPT:拆解Transformer核心,聊聊Multi-Head Attention那些‘头’到底在看什么?

当你在使用ChatGPT生成流畅的回复,或是通过BERT模型获取精准的文本理解时,背后默默工作的Transformer架构就像一支训练有素的交响乐团。而Multi-Head Attention(多头注意力机制)无疑是这支乐团中最耀眼的独奏家们——但你是否好奇过,这些"头"究竟在关注什么?它们是如何分工协作的?

2017年Transformer架构的横空出世,彻底改变了自然语言处理的游戏规则。而多头注意力机制作为其核心组件,赋予了模型同时关注文本不同层面的能力——就像人类阅读时既能把握句子结构,又能理解语义关联。本文将带你深入现代预训练模型的"大脑",观察这些注意力头的实际工作模式。

1. 多头注意力机制的本质:分而治之的艺术

想象你在阅读一段技术文档时,大脑会同时处理多种信息:识别专业术语(词汇层面)、分析句子结构(语法层面)、追踪代词指代(语义层面)。多头注意力机制的设计灵感正源于此——通过多个独立的"注意力头"并行处理不同维度的信息。

在Hugging Face的transformers库中,一个12层的BERT-base模型包含144个注意力头(12层×12头)。研究发现这些头大致可分为几种典型模式:

  • 位置关注型:专注于固定相对位置的token(如关注前一个词)
  • 语法关注型:追踪句法结构(如动词与宾语的关联)
  • 语义关注型:捕捉同义替换或指代关系(如"它"指代的前述名词)
  • 全局关注型:平等关注所有token(常见于[CLS]等特殊token)
# 使用Hugging Face查看注意力权重的示例
from transformers import BertModel, BertTokenizer
import torch

model = BertModel.from_pretrained('bert-base-uncased', output_attentions=True)
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')

inputs = tokenizer("The cat sat on the mat because it was tired", return_tensors="pt")
outputs = model(**inputs)
attentions = outputs.attentions  # 包含各层各头的注意力矩阵

2. 注意力头的专业化分工:来自实证研究的发现

2019年《Analyzing Multi-Head Self-Attention》的突破性研究通过大量实验揭示了注意力头的专业化现象。下表展示了在GLUE基准测试中BERT模型不同头的典型功能分布:

注意力头类型 占比 典型作用 示例
句法关注头 38% 捕捉主谓一致、修饰关系 "running"与"dog"的关联
指代关注头 22% 解析代词所指 "it"指向前文提到的名词
位置关注头 25% 跟踪相对位置模式 关注相邻2-3个token
其他/混合 15% 特殊模式或未明确功能 标点符号处理等

有趣的是,这种分工并非设计者预先设定,而是模型在预训练过程中自发形成的特征。就像大脑不同区域会发展出不同功能,注意力头也通过梯度下降"找到"了自己的专业领域。

提示:在实际应用中,某些头可能对特定任务特别重要。例如在共指消解任务中,指代关注头的权重往往更大。

3. 可视化实战:追踪注意力头的关注模式

要真正理解多头注意力的工作方式,最直观的方法是可视化其注意力权重。以下是一个分析句子"The animal didn't cross the street because it was too wide"的典型流程:

  1. 选择目标token:比如代词"it"
  2. 提取相关注意力权重:从各层各头获取该token对其他token的注意力分数
  3. 识别显著模式
    • 第5层第3头:强烈关注"animal"(正确指代)
    • 第7层第1头:同时关注"street"(潜在错误关联)
  4. 对比分析
    # 可视化特定头的注意力权重
    import matplotlib.pyplot as plt
    
    def plot_attention(head_layer, head_index):
        attention = attentions[head_layer][0, head_index]
        plt.matshow(attention.detach().numpy())
        plt.xticks(range(len(inputs.tokens())), inputs.tokens(), rotation=90)
        plt.yticks(range(len(inputs.tokens())), inputs.tokens())
        plt.title(f"Layer {head_layer+1} Head {head_index+1}")
    

这种分析揭示了模型决策过程的"思考链条"——不同层级的头逐步构建对文本的理解:低层头更多处理局部语法,高层头则整合复杂语义。

4. 注意力头修剪:效率与性能的平衡艺术

既然不是所有头都同等重要,能否移除部分头来提升效率?研究表明:

  • 关键发现

    • 约30-40%的注意力头可以被修剪而不显著影响模型性能
    • 不同任务依赖的头不同(如句法密集型任务更需要语法关注头)
  • 修剪策略对比

方法 优点 缺点
基于幅值 简单高效 忽略头间依赖
基于任务损失 保留任务相关头 计算成本高
随机修剪 实现简单 性能下降风险大

实践中的平衡建议:

  1. 评估任务对各类头的依赖程度
  2. 优先修剪持续低幅值的头
  3. 采用渐进式修剪(每次移除5-10%)
  4. 微调修剪后的模型
# 简单的基于幅值的头修剪示例
def prune_heads(model, head_importance, prune_ratio=0.3):
    heads_to_prune = {}
    for layer in range(model.config.num_hidden_layers):
        importance = head_importance[layer]
        threshold = torch.sort(importance)[0][int(len(importance)*prune_ratio)]
        heads_to_prune[layer] = [i for i,imp in enumerate(importance) if imp < threshold]
    model.prune_heads(heads_to_prune)

5. 跨模型比较:BERT与GPT的注意力模式差异

虽然BERT和GPT都基于Transformer,但它们的注意力头展现出有趣的差异:

  • BERT(双向注意力)

    • 更多头专注于句法结构和指代消解
    • 高层头表现出更强的语义整合能力
    • 典型模式:约25%的头专门处理[CLS]特殊token
  • GPT(单向注意力)

    • 更多头专注于前缀模式(前文到当前token)
    • 更强的位置编码依赖性
    • 高层头发展出复杂的主题维持能力

这种差异源于它们不同的训练目标:

  • BERT的掩码语言建模促使头发展出更强的上下文分析能力
  • GPT的自回归特性则鼓励头建立更强大的前文依赖模型

在实际项目中,理解这些差异有助于:

  • 为特定任务选择合适的预训练模型
  • 设计更有效的微调策略
  • 优化模型解释性方案

更多推荐