Transformer 自注意力机制:从 QKV 向量到多头注意力 8 头并行计算全解析
在自然语言处理领域,Transformer 架构凭借其强大的并行计算能力和高效的注意力机制,彻底改变了序列建模的范式。本文将深入剖析 Transformer 中最核心的自注意力机制,特别是 QKV 向量的生成、点积计算、缩放、Softmax 以及多头注意力(以 8 头为例)的并行计算流程。通过数学原理推导和 PyTorch 代码实现,帮助机器学习工程师和研究者掌握这一革命性技术的底层实现细节。
1. 自注意力机制的核心概念
自注意力机制的本质是让模型能够动态地关注输入序列中不同位置的信息。与传统循环神经网络(RNN)逐词处理的模式不同,自注意力机制可以同时处理整个序列,并通过权重分配来决定每个词对其他词的重要性。
关键术语解析:
- Query(Q):当前需要计算注意力的词向量表示
- Key(K):用于与 Query 计算相似度的参考向量
- Value(V):实际参与加权求和的词向量表示
这三个向量的命名来源于信息检索系统:给定一个查询(Query),系统会计算它与键(Key)的相似度,然后返回对应的值(Value)。在 Transformer 中,这种机制被抽象为可学习的矩阵运算。
2. QKV 矩阵的生成与计算
在标准的 Transformer 实现中,Q、K、V 矩阵是通过对输入嵌入进行线性变换得到的。假设输入序列的维度为(batch_size, seq_len, d_model),其中d_model是模型维度(通常为 512),则 QKV 的计算过程如下:
import torch import torch.nn as nn class SelfAttention(nn.Module): def __init__(self, d_model=512): super().__init__() self.d_model = d_model # 初始化Q、K、V的线性变换矩阵 self.W_q = nn.Linear(d_model, d_model) self.W_k = nn.Linear(d_model, d_model) self.W_v = nn.Linear(d_model, d_model) def forward(self, x): # x shape: (batch_size, seq_len, d_model) Q = self.W_q(x) # (batch_size, seq_len, d_model) K = self.W_k(x) # (batch_size, seq_len, d_model) V = self.W_v(x) # (batch_size, seq_len, d_model) return Q, K, V数学表达: 对于输入矩阵 X ∈ ℝ^(n×d),其中 n 是序列长度,d 是模型维度:
Q = XW^Q, K = XW^K, V = XW^V
其中 W^Q, W^K, W^V ∈ ℝ^(d×d) 是可学习的参数矩阵。
3. 单头注意力计算流程
单头注意力的完整计算过程可以分为以下几个步骤:
- 点积计算:计算 Q 和 K 的点积相似度
- 缩放处理:除以 √d_k 防止梯度消失
- Softmax归一化:得到注意力权重
- 加权求和:用权重对 V 进行加权
PyTorch 实现:
def scaled_dot_product_attention(Q, K, V, mask=None): d_k = Q.size(-1) # 获取key的维度 scores = torch.matmul(Q, K.transpose(-2, -1)) / torch.sqrt(torch.tensor(d_k)) if mask is not None: scores = scores.masked_fill(mask == 0, -1e9) attention_weights = torch.softmax(scores, dim=-1) output = torch.matmul(attention_weights, V) return output, attention_weights数学公式表达:
Attention(Q, K, V) = softmax(QK^T/√d_k)V
其中 d_k 是 key 向量的维度(通常为 d_model / num_heads)。
4. 多头注意力机制(8头并行)
多头注意力的核心思想是将高维的 Q、K、V 矩阵分割到多个子空间(头)中,让每个头学习不同的注意力模式。以 8 头注意力为例:
分割过程:
- 将 Q、K、V 从
(batch_size, seq_len, d_model)重塑为(batch_size, seq_len, num_heads, d_model/num_heads) - 转置为
(batch_size, num_heads, seq_len, d_model/num_heads)以便并行计算
PyTorch 实现:
class MultiHeadAttention(nn.Module): def __init__(self, d_model=512, num_heads=8): super().__init__() assert d_model % num_heads == 0, "d_model must be divisible by num_heads" self.d_model = d_model self.num_heads = num_heads self.depth = d_model // num_heads self.W_q = nn.Linear(d_model, d_model) self.W_k = nn.Linear(d_model, d_model) self.W_v = nn.Linear(d_model, d_model) self.W_o = nn.Linear(d_model, d_model) # 输出变换矩阵 def split_heads(self, x): batch_size = x.size(0) return x.view(batch_size, -1, self.num_heads, self.depth).transpose(1, 2) def forward(self, Q, K, V, mask=None): batch_size = Q.size(0) # 线性变换并分割头 Q = self.split_heads(self.W_q(Q)) # (batch_size, num_heads, seq_len, depth) K = self.split_heads(self.W_k(K)) V = self.split_heads(self.W_v(V)) # 计算缩放点积注意力 scaled_attention, attention_weights = scaled_dot_product_attention(Q, K, V, mask) # 合并头 scaled_attention = scaled_attention.transpose(1, 2).contiguous() concat_attention = scaled_attention.view(batch_size, -1, self.d_model) # 最终线性变换 output = self.W_o(concat_attention) return output, attention_weights多头注意力的数学表达:
MultiHead(Q, K, V) = Concat(head_1, ..., head_h)W^O 其中 head_i = Attention(QW_i^Q, KW_i^K, VW_i^V)
5. 并行计算与性能优化
Transformer 的并行计算优势主要体现在以下几个方面:
- 矩阵运算并行化:QKV 的线性变换和注意力计算都可以表示为矩阵乘法,非常适合 GPU 并行计算
- 多头注意力并行:8 个注意力头可以完全独立计算
- 序列级别并行:整个序列的注意力计算可以同时进行,无需像 RNN 那样顺序处理
性能对比表:
| 特性 | RNN/LSTM | Transformer |
|---|---|---|
| 序列处理方式 | 顺序处理 | 并行处理 |
| 长距离依赖 | 容易丢失 | 直接建模 |
| 计算复杂度 | O(n) | O(n²) |
| 并行度 | 低 | 高 |
| 内存占用 | 较低 | 较高 |
注意:虽然 Transformer 的计算复杂度是 O(n²),但由于其高度并行性,在实际硬件上的执行时间可能比 O(n) 的 RNN 更快。
6. 工程实现中的关键细节
在实际工程实现中,有以下几个关键点需要特别注意:
- 位置编码:由于自注意力机制本身不包含位置信息,需要添加位置编码
- 残差连接:防止深层网络梯度消失
- Layer Normalization:稳定训练过程
- Mask 机制:在解码器中防止信息泄露
位置编码实现示例:
class PositionalEncoding(nn.Module): def __init__(self, d_model, max_len=5000): super().__init__() pe = torch.zeros(max_len, d_model) position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1) div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model)) pe[:, 0::2] = torch.sin(position * div_term) pe[:, 1::2] = torch.cos(position * div_term) pe = pe.unsqueeze(0) self.register_buffer('pe', pe) def forward(self, x): return x + self.pe[:, :x.size(1)]7. 现代 Transformer 的变体与改进
随着研究的深入,出现了多种 Transformer 的改进架构:
- 稀疏注意力:降低计算复杂度(如 Longformer)
- 混合架构:结合 CNN 和 Transformer(如 ConvBERT)
- 高效注意力:线性注意力机制(如 Linformer)
- 相对位置编码:替代绝对位置编码(如 T5)
这些改进都在不同程度上保留了原始 Transformer 的核心——自注意力机制,同时解决了其在长序列处理、计算效率等方面的问题。