# Transformer 架构原理详解
1. 概述
Transformer 是一种完全基于注意力机制(Attention)来建模序列依赖的模型结构,最早在《Attention Is All You Need》中提出。它摒弃了传统的循环(RNN)或卷积(CNN)结构,仅使用 **自注意力**、**多头注意力**、**位置编码** 与 **前馈网络**,配合残差连接和层归一化,实现高效并行训练,并成为大语言模型(LLM)的基石。
---
2. 自注意力机制(Self‑Attention)
\[
\text{Attention}(Q,K,V)=\operatorname{softmax}\!\left(\frac{QK^{\top}}{\sqrt{d_k}}\right)V
\]
- \(Q, K, V\):分别由输入 \(X\) 经过线性投影得到,维度为 \((n, d)\)。
- \(\sqrt{d_k}\) 为缩放因子,防止点积值过大导致梯度饱和。
---
3. 多头注意力(Multi‑Head Attention)
1. 将 \(Q,K,V\) 分别通过 \(h\) 组独立的线性投影得到 \(h\) 组 \((Q_i,K_i,V_i)\),每组维度为 \((n, d/h)\)。
2. 对每组并行计算注意力得到 \( \text{head}_i = \text{Attention}(Q_i,K_i,V_i) \),形状 \((n, d/h)\)。
3. 将所有 head 拼接后再次线性投影得到最终输出 \( \text{MultiHead}(Q,K,V) = \text{Concat}(\text{head}_1,\dots,\text{head}_h)W^O\)。
---
4. 位置编码(Positional Encoding)
\[
\begin{aligned}
PE_{(pos,2i)} &= \sin\!\left(\frac{pos}{10000^{2i/d}}\right)\\
PE_{(pos,2i+1)} &= \cos\!\left(\frac{pos}{10000^{2i/d}}\right)
\end{aligned}
\]
- 其中 \(pos\) 为 token 在序列中的位置,\(i\) 为维度索引。生成的编码与词向量直接相加或拼接。
---
5. 前馈网络(FFN)与残差连接
\[
\text{FFN}(x)=\operatorname{GELU}(xW_1+b_1)W_2+b_2
\]
- 通常第一层维度为 \(d_{\text{model}} \rightarrow d_{\text{ff}}\)(如 3072),第二层恢复到 \(d_{\text{model}}\)。
\[
x' = x + \text{SubLayer}(x)
\]
- 使得梯度能够直接回传,帮助深层网络稳定训练。
\[
\text{output} = \text{LayerNorm}(x + \text{SubLayer}(x))
\]
---
6. PyTorch 实现要点
下面给出一个 **简化但完整** 的实现示例,演示多头注意力、位置编码以及一个完整的 Transformer Encoder Block。
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_len=5000):
super().__init__()
# 生成 (max_len, d_model) 的位置编码
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, dtype=torch.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) # (1, max_len, d_model)
self.register_buffer('pe', pe)
def forward(self, x):
# x shape: (batch, seq_len, d_model)
x = x + self.pe[:, :x.size(1), :]
return x
class MultiHeadAttention(nn.Module):
def __init__(self, d_model, num_heads):
super().__init__()
assert d_model % num_heads == 0
self.d_k = d_model // num_heads
self.num_heads = 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):
# x: (batch, seq_len, d_model)
batch, seq_len, _ = x.size()
x = x.view(batch, seq_len, self.num_heads, self.d_k).transpose(1, 2)
return x # (batch, heads, seq_len, d_k)
def forward(self, query, key, value, mask=None):
# 线性投影
Q = self.split_heads(self.W_q(query))
K = self.split_heads(self.W_k(key))
V = self.split_heads(self.W_v(value))
# 注意力分数
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)
attn_weights = F.softmax(scores, dim=-1)
# 加权求和
attn_output = torch.matmul(attn_weights, V) # (batch, heads, seq_len,