You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PyTorch的TransformerEncoderLayer中获取各注意力头分数?

获取TransformerEncoderLayer各注意力头的注意力分数

PyTorch原生的nn.TransformerEncoderLayer默认只返回特征输出,不会直接给出注意力分数。要获取每个注意力头的注意力权重,有两种实用方法:

方法一:自定义编码器层,重写forward逻辑

继承TransformerEncoderLayer,修改forward方法,让内部的多头注意力层返回并保存注意力权重。

import torch
import torch.nn as nn

# 自定义Transformer编码器层,用于保存注意力分数
class AttentionCapturingEncoderLayer(nn.TransformerEncoderLayer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 存储当前层的注意力分数,形状为 [batch_size, nhead, seq_len, seq_len]
        self.attention_weights = None

    def forward(self, src, src_mask=None, src_key_padding_mask=None):
        # 调用多头注意力时开启need_weights=True,获取注意力权重
        src2, attn_weights = self.self_attn(src, src, src, attn_mask=src_mask,
                                            key_padding_mask=src_key_padding_mask,
                                            need_weights=True)
        self.attention_weights = attn_weights  # 保存注意力分数
        # 原层的剩余逻辑不变
        src = src + self.dropout1(src2)
        src = self.norm1(src)
        src2 = self.linear2(self.dropout(self.activation(self.linear1(src))))
        src = src + self.dropout2(src2)
        src = self.norm2(src)
        return src

# 定义输入张量:batch_size=2,sequence_length=16,embedding_dim=4
batch_size = 2
sequence_length = 16
input_size = 4  # 需与d_model保持一致
x = torch.randn(batch_size, sequence_length, input_size)

# 配置Transformer参数
d_model = 4  # 匹配输入的embedding维度
nhead = 2  # d_model必须能被nhead整除(4/2=2,每个头维度为2)
d_hid = 64
dropout = 0.1
nlayers = 2

# 使用自定义编码器层构建Transformer
encoder_layers = AttentionCapturingEncoderLayer(d_model=d_model, nhead=nhead, 
                                               dim_feedforward=d_hid, dropout=dropout)
transformer_encoder = nn.TransformerEncoder(encoder_layers, num_layers=nlayers)

# 前向传播
output = transformer_encoder(x)

# 提取各层的注意力分数
for idx, layer in enumerate(transformer_encoder.layers):
    attn_weights = layer.attention_weights
    print(f"第{idx+1}层注意力分数形状:", attn_weights.shape)
    # 可通过attn_weights[b, h]获取第b个样本、第h个注意力头的注意力矩阵

方法二:用钩子函数捕获注意力分数

无需修改原类,通过给多头注意力层注册前向钩子,动态捕获注意力权重,更灵活。

import torch
import torch.nn as nn

# 定义输入张量
batch_size = 2
sequence_length = 16
input_size = 4
x = torch.randn(batch_size, sequence_length, input_size)

# 配置Transformer参数
d_model = 4
nhead = 2
d_hid = 64
dropout = 0.1
nlayers = 2

encoder_layers = nn.TransformerEncoderLayer(d_model=d_model, nhead=nhead, 
                                           dim_feedforward=d_hid, dropout=dropout)
transformer_encoder = nn.TransformerEncoder(encoder_layers, num_layers=nlayers)

# 存储各层注意力分数的字典
attention_weights_dict = {}

# 定义钩子函数,捕获注意力权重
def save_attention(module, input, output):
    # 强制开启返回注意力权重
    module.need_weights = True
    attn_weights = output[1]
    # 定位当前模块所属的层
    for idx, layer in enumerate(transformer_encoder.layers):
        if layer.self_attn is module:
            attention_weights_dict[idx] = attn_weights
            break

# 给每一层的多头注意力模块注册钩子
for layer in transformer_encoder.layers:
    layer.self_attn.register_forward_hook(save_attention)

# 前向传播
output = transformer_encoder(x)

# 输出各层注意力分数信息
for layer_idx, attn_weights in attention_weights_dict.items():
    print(f"第{layer_idx+1}层注意力分数形状:", attn_weights.shape)

注意事项

  • d_model必须与输入的embedding维度(这里是4)完全一致,且nhead必须能整除d_model,否则会抛出维度不匹配的错误。
  • 注意力分数的形状为[batch_size, nhead, sequence_length, sequence_length],其中attn_weights[b, h, i, j]代表第b个样本中,第h个注意力头里,第i个token对第j个token的注意力权重。

内容的提问来源于stack exchange,提问作者pte

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 07:50:17