Transformer详解:从原理到实现的完整指南
目录
引言
Transformer是由Vaswani等人在2017年论文《Attention Is All You Need》中提出的革命性架构。它完全基于注意力机制,摒弃了传统的循环神经网络(RNN)和卷积神经网络(CNN),在自然语言处理任务中取得了突破性的成果。
Transformer的主要优势:
- 并行化训练,提高训练效率
- 长距离依赖建模能力强
- 可解释性好
- 为后续的BERT、GPT等模型奠定了基础
Transformer架构概览
Transformer采用编码器-解码器(Encoder-Decoder)架构,主要组件包括:
核心组件
- 多头自注意力机制 (Multi-Head Self-Attention)
- 位置编码 (Positional Encoding)
- 前馈神经网络 (Feed Forward Network)
- 残差连接 (Residual Connection)
- 层归一化 (Layer Normalization)
注意力机制详解
什么是注意力机制?
注意力机制允许模型在处理序列的每个位置时,关注序列中的所有位置。这解决了RNN中信息瓶颈的问题。
缩放点积注意力 (Scaled Dot-Product Attention)
这是Transformer中使用的注意力机制的核心。
数学公式
Attention ( Q , K , V ) = softmax ( Q K T d k ) V \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V Attention(Q,K,V)=softmax(dkQKT)V
其中:
- Q Q Q:查询矩阵 (Query)
- K K K:键矩阵 (Key)
- V V V:值矩阵 (Value)
- d k d_k dk:键向量的维度
详细推导
-
计算注意力分数:
scores = Q K T \text{scores} = QK^T scores=QKT -
缩放:
scaled_scores = Q K T d k \text{scaled\_scores} = \frac{QK^T}{\sqrt{d_k}} scaled_scores=dkQKT缩放的目的是防止softmax函数进入饱和区域。
-
应用softmax:
attention_weights = softmax ( scaled_scores ) \text{attention\_weights} = \text{softmax}(\text{scaled\_scores}) attention_weights=softmax(scaled_scores) -
加权求和:
output = attention_weights ⋅ V \text{output} = \text{attention\_weights} \cdot V output=attention_weights⋅V
注意力机制的直观理解
想象你在阅读一个句子:“The animal didn’t cross the street because it was too tired”。当模型处理"it"这个词时,注意力机制帮助模型将注意力集中在"animal"上,而不是"street"。
多头注意力机制
为什么需要多头注意力?
单一的注意力头可能只能捕获一种类型的关系。多头注意力允许模型同时关注不同位置的不同表示空间的信息。
数学表示
MultiHead ( Q , K , V ) = Concat ( head 1 , . . . , head h ) W O \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, ..., \text{head}_h)W^O MultiHead(Q,K,V)=Concat(head1,...,headh)WO
其中每个注意力头计算为:
head
i
=
Attention
(
Q
W
i
Q
,
K
W
i
K
,
V
W
i
V
)
\text{head}_i = \text{Attention}(QW_i^Q, KW_i^K, VW_i^V)
headi=Attention(QWiQ,KWiK,VWiV)
参数矩阵
- W i Q ∈ R d m o d e l × d k W_i^Q \in \mathbb{R}^{d_{model} \times d_k} WiQ∈Rdmodel×dk:第i个头的查询权重矩阵
- W i K ∈ R d m o d e l × d k W_i^K \in \mathbb{R}^{d_{model} \times d_k} WiK∈Rdmodel×dk:第i个头的键权重矩阵
- W i V ∈ R d m o d e l × d v W_i^V \in \mathbb{R}^{d_{model} \times d_v} WiV∈Rdmodel×dv:第i个头的值权重矩阵
- W O ∈ R h d v × d m o d e l W^O \in \mathbb{R}^{hd_v \times d_{model}} WO∈Rhdv×dmodel:输出权重矩阵
维度关系
通常设置: d k = d v = d m o d e l / h d_k = d_v = d_{model}/h dk=dv=dmodel/h,其中 h h h是头的数量。
位置编码
为什么需要位置编码?
注意力机制本身是排列不变的(permutation invariant),即改变输入序列的顺序不会影响输出。但在序列建模中,位置信息至关重要。
正弦位置编码
Transformer使用固定的正弦和余弦函数来编码位置信息:
P E ( p o s , 2 i ) = sin ( p o s 1000 0 2 i / d m o d e l ) PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d_{model}}}\right) PE(pos,2i)=sin(100002i/dmodelpos)
P E ( p o s , 2 i + 1 ) = cos ( p o s 1000 0 2 i / d m o d e l ) PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d_{model}}}\right) PE(pos,2i+1)=cos(100002i/dmodelpos)
其中:
- p o s pos pos:位置
- i i i:维度索引
- d m o d e l d_{model} dmodel:模型维度
位置编码的性质
- 唯一性:每个位置都有唯一的编码
- 相对位置信息:模型可以学习到相对位置关系
- 外推性:可以处理比训练时更长的序列
前馈神经网络
结构
每个Transformer层都包含一个位置级的前馈网络:
FFN ( x ) = max ( 0 , x W 1 + b 1 ) W 2 + b 2 \text{FFN}(x) = \max(0, xW_1 + b_1)W_2 + b_2 FFN(x)=max(0,xW1+b1)W2+b2
特点
- 两个线性变换,中间使用ReLU激活
- 内部维度通常是模型维度的4倍
- 应用于序列的每个位置
残差连接与层归一化
残差连接
每个子层都使用残差连接:
output
=
LayerNorm
(
x
+
Sublayer
(
x
)
)
\text{output} = \text{LayerNorm}(x + \text{Sublayer}(x))
output=LayerNorm(x+Sublayer(x))
层归一化
与批量归一化不同,层归一化在特征维度上进行:
LayerNorm ( x ) = γ x − μ σ + β \text{LayerNorm}(x) = \gamma \frac{x - \mu}{\sigma} + \beta LayerNorm(x)=γσx−μ+β
其中:
- μ = 1 d ∑ i = 1 d x i \mu = \frac{1}{d}\sum_{i=1}^{d} x_i μ=d1∑i=1dxi
- σ = 1 d ∑ i = 1 d ( x i − μ ) 2 \sigma = \sqrt{\frac{1}{d}\sum_{i=1}^{d}(x_i - \mu)^2} σ=d1∑i=1d(xi−μ)2
编码器-解码器架构
编码器 (Encoder)
- 6个相同的层堆叠
- 每层包含:多头自注意力 + 前馈网络
- 残差连接和层归一化
解码器 (Decoder)
- 6个相同的层堆叠
- 每层包含:
- 掩码多头自注意力
- 编码器-解码器注意力
- 前馈网络
掩码机制
在解码器的自注意力中,使用掩码防止模型看到未来的信息:
mask = torch.triu(torch.ones(seq_len, seq_len), diagonal=1)
scores = scores.masked_fill(mask == 1, -1e9)
数学推导详解
注意力分数的计算复杂度
对于序列长度为 n n n的输入:
- 计算 Q K T QK^T QKT: O ( n 2 d k ) O(n^2 d_k) O(n2dk)
- Softmax: O ( n 2 ) O(n^2) O(n2)
- 乘以V: O ( n 2 d v ) O(n^2 d_v) O(n2dv)
- 总复杂度: O ( n 2 d m o d e l ) O(n^2 d_{model}) O(n2dmodel)
梯度推导
对于注意力权重的梯度:
∂ L ∂ A i j = ∑ k ∂ L ∂ O i k V j k \frac{\partial L}{\partial A_{ij}} = \sum_k \frac{\partial L}{\partial O_{ik}} V_{jk} ∂Aij∂L=k∑∂Oik∂LVjk
其中 A A A是注意力权重矩阵, O O O是输出。
Softmax的梯度
∂ softmax ( x i ) ∂ x j = softmax ( x i ) ( δ i j − softmax ( x j ) ) \frac{\partial \text{softmax}(x_i)}{\partial x_j} = \text{softmax}(x_i)(\delta_{ij} - \text{softmax}(x_j)) ∂xj∂softmax(xi)=softmax(xi)(δij−softmax(xj))
代码实现
基础注意力机制
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
class ScaledDotProductAttention(nn.Module):
def __init__(self, d_k):
super().__init__()
self.d_k = d_k
def forward(self, Q, K, V, mask=None):
# 计算注意力分数
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
# 应用掩码
if mask is not None:
scores = scores.masked_fill(mask == 0, -1e9)
# Softmax
attention_weights = F.softmax(scores, dim=-1)
# 加权求和
output = torch.matmul(attention_weights, V)
return output, attention_weights
多头注意力
class MultiHeadAttention(nn.Module):
def __init__(self, d_model, num_heads):
super().__init__()
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 = 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)
self.attention = ScaledDotProductAttention(self.d_k)
def forward(self, query, key, value, mask=None):
batch_size = query.size(0)
# 线性变换并重塑为多头形式
Q = self.W_q(query).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
K = self.W_k(key).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
V = self.W_v(value).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
# 应用注意力
attn_output, attn_weights = self.attention(Q, K, V, mask)
# 连接多头
attn_output = attn_output.transpose(1, 2).contiguous().view(
batch_size, -1, self.d_model)
# 最终线性变换
output = self.W_o(attn_output)
return output, attn_weights
位置编码
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).transpose(0, 1)
self.register_buffer('pe', pe)
def forward(self, x):
return x + self.pe[:x.size(0), :]
前馈网络
class FeedForward(nn.Module):
def __init__(self, d_model, d_ff):
super().__init__()
self.linear1 = nn.Linear(d_model, d_ff)
self.linear2 = nn.Linear(d_ff, d_model)
self.relu = nn.ReLU()
def forward(self, x):
return self.linear2(self.relu(self.linear1(x)))
编码器层
class EncoderLayer(nn.Module):
def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
super().__init__()
self.self_attn = MultiHeadAttention(d_model, num_heads)
self.feed_forward = FeedForward(d_model, d_ff)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x, mask=None):
# 自注意力
attn_output, _ = self.self_attn(x, x, x, mask)
x = self.norm1(x + self.dropout(attn_output))
# 前馈网络
ff_output = self.feed_forward(x)
x = self.norm2(x + self.dropout(ff_output))
return x
完整的Transformer编码器
class TransformerEncoder(nn.Module):
def __init__(self, vocab_size, d_model, num_heads, num_layers, d_ff, max_len=5000):
super().__init__()
self.d_model = d_model
self.embedding = nn.Embedding(vocab_size, d_model)
self.pos_encoding = PositionalEncoding(d_model, max_len)
self.layers = nn.ModuleList([
EncoderLayer(d_model, num_heads, d_ff)
for _ in range(num_layers)
])
def forward(self, x, mask=None):
# 词嵌入 + 位置编码
x = self.embedding(x) * math.sqrt(self.d_model)
x = self.pos_encoding(x)
# 通过编码器层
for layer in self.layers:
x = layer(x, mask)
return x
训练与优化
学习率调度
Transformer使用特殊的学习率调度策略:
lrate = d m o d e l − 0.5 ⋅ min ( step_num − 0.5 , step_num ⋅ warmup_steps − 1.5 ) \text{lrate} = d_{model}^{-0.5} \cdot \min(\text{step\_num}^{-0.5}, \text{step\_num} \cdot \text{warmup\_steps}^{-1.5}) lrate=dmodel−0.5⋅min(step_num−0.5,step_num⋅warmup_steps−1.5)
class TransformerLRScheduler:
def __init__(self, d_model, warmup_steps=4000):
self.d_model = d_model
self.warmup_steps = warmup_steps
def __call__(self, step):
arg1 = step ** (-0.5)
arg2 = step * (self.warmup_steps ** -1.5)
return (self.d_model ** -0.5) * min(arg1, arg2)
标签平滑
class LabelSmoothing(nn.Module):
def __init__(self, size, padding_idx, smoothing=0.1):
super().__init__()
self.criterion = nn.KLDivLoss(reduction='sum')
self.padding_idx = padding_idx
self.confidence = 1.0 - smoothing
self.smoothing = smoothing
self.size = size
def forward(self, x, target):
true_dist = x.data.clone()
true_dist.fill_(self.smoothing / (self.size - 2))
true_dist.scatter_(1, target.data.unsqueeze(1), self.confidence)
true_dist[:, self.padding_idx] = 0
mask = torch.nonzero(target.data == self.padding_idx)
if mask.dim() > 0:
true_dist.index_fill_(0, mask.squeeze(), 0.0)
return self.criterion(x, true_dist)
应用与扩展
主要应用领域
- 机器翻译:原始论文的主要任务
- 文本摘要:BART, T5等模型
- 问答系统:BERT的主要应用之一
- 代码生成:CodeT5, Codex等
- 图像处理:Vision Transformer (ViT)
重要的Transformer变体
BERT (Bidirectional Encoder Representations from Transformers)
- 只使用编码器
- 双向上下文建模
- 预训练 + 微调范式
GPT (Generative Pre-trained Transformer)
- 只使用解码器
- 自回归生成
- 大规模语言模型的基础
T5 (Text-to-Text Transfer Transformer)
- 编码器-解码器架构
- 所有任务都转换为文本到文本
效率优化
Sparse Attention
减少注意力计算的复杂度:
- Longformer:滑动窗口 + 全局注意力
- BigBird:随机 + 窗口 + 全局
- Linformer:线性复杂度
知识蒸馏
将大模型的知识转移到小模型:
def distillation_loss(student_logits, teacher_logits, labels, temperature=3.0, alpha=0.7):
soft_loss = F.kl_div(
F.log_softmax(student_logits / temperature, dim=-1),
F.softmax(teacher_logits / temperature, dim=-1),
reduction='batchmean'
) * (temperature ** 2)
hard_loss = F.cross_entropy(student_logits, labels)
return alpha * soft_loss + (1 - alpha) * hard_loss
总结
Transformer架构的成功在于其优雅的设计和强大的表示能力:
关键创新点
- 完全基于注意力:摒弃了RNN和CNN
- 并行化训练:显著提高训练效率
- 长距离依赖:有效捕获远程关系
- 可扩展性:为大规模模型奠定基础
数学美感
- 注意力机制的矩阵乘法形式
- 位置编码的周期性设计
- 残差连接的梯度传播
- 多头注意力的信息整合
实践意义
Transformer不仅改变了NLP领域,还在计算机视觉、语音识别、强化学习等领域展现出巨大潜力。从BERT到GPT,从ViT到DALL-E,Transformer架构持续推动着人工智能的发展。
参考文献
- Vaswani, A., et al. (2017). Attention is all you need. In NIPS.
- Devlin, J., et al. (2018). BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding.
- Radford, A., et al. (2019). Language Models are Unsupervised Multitask Learners.
- Dosovitskiy, A., et al. (2020). An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale.
本文深入探讨了Transformer的各个方面,从基础概念到高级应用。希望这篇详细的介绍能帮助你更好地理解这一重要的深度学习架构。