Transformer核心原理
一、课程基本信息
- 课程主题:Transformer核心原理与自注意力机制
- 核心目标:理解Transformer整体结构、解决的问题、核心优势,掌握自注意力关键逻辑,衔接此前QKV手算案例
- 参考论文:Attention is All You Need (Vaswani et al., 2017, NeurIPS)
二、Transformer整体概述
1. 什么是Transformer?
Transformer是2017年由谷歌团队在论文 “Attention is All You Need” 中提出的深度学习模型架构,彻底摒弃了传统循环神经网络(RNN、LSTM)的串行计算模式,全程依靠自注意力机制(Self-Attention) 实现序列数据的建模与处理,是当下所有大语言模型(LLM)的核心基石。
技术突破点:
- 首次将注意力机制作为模型核心,而非RNN的辅助组件
- 实现了序列数据的完全并行处理
- 为后续预训练语言模型革命奠定了基础
代码示例:Transformer核心结构定义
import torch
import torch.nn as nn
class Transformer(nn.Module):
def __init__(self, d_model=512, nhead=8, num_layers=6):
super().__init__()
# 编码器
self.encoder = nn.TransformerEncoder(
nn.TransformerEncoderLayer(d_model, nhead),
num_layers=num_layers
)
# 解码器
self.decoder = nn.TransformerDecoder(
nn.TransformerDecoderLayer(d_model, nhead),
num_layers=num_layers
)
def forward(self, src, tgt):
memory = self.encoder(src)
output = self.decoder(tgt, memory)
return output
2. Transformer解决的核心问题
解决传统RNN/LSTM串行计算瓶颈:
- 问题本质:传统模型必须按顺序逐词处理,第t时刻的计算依赖第t-1时刻的输出,无法并行
- 影响:训练速度慢、处理长文本效率极低
- 解决方案:Transformer通过自注意力机制,所有位置的信息可同时计算
解决长距离依赖问题:
- 问题本质:传统模型处理长文本时,前文信息经过多步传递会逐步丢失(梯度消失/信息衰减)
- 影响:无法关联远距离语义,如"我昨天买了一本书,它的封面是红色的"中的"它"无法准确指代"书"
- 解决方案:Transformer通过自注意力直接计算任意两个位置的关联强度,无距离限制
实现通用序列建模:
- 问题本质:传统模型针对特定任务设计,迁移性差
- 解决方案:编码器-解码器架构灵活适配多种任务
3. Transformer的核心优势
对比表格:
| 计算方式 | 串行 | 并行 |
| 长距离依赖 | 差(信息衰减) | 优(直接关联) |
| 训练效率 | 低 | 高 |
| 语义理解 | 有限 | 精准 |
4. 基于Transformer的主流大模型应用
1. 仅使用编码器(Encoder-only)架构
- 代表模型:BERT、RoBERTa、ALBERT、ELECTRA
- 核心任务:文本分类、情感分析、实体识别、语义理解等理解类任务
- 特点:双向上下文编码,擅长从输入文本中提取特征
2. 仅使用解码器(Decoder-only)架构
- 代表模型:GPT系列、Llama、Claude、通义千问、文心一言
- 核心任务:文本生成、对话交互、代码生成、续写等生成类任务
- 特点:自回归生成,逐词预测下一个token
3. 编码器-解码器(Encoder-Decoder)架构
- 代表模型:T5、BART、Transformer原生模型
- 核心任务:机器翻译、文本摘要、对话生成等序列转换任务
- 特点:先理解输入,再生成输出
三、Transformer核心组件与整体结构
1. 整体结构:编码器-解码器架构
Transformer标准结构分为编码器(Encoder) 和解码器(Decoder) 两大部分:
- 编码器(Encoder):负责理解输入文本的语义,提取全局上下文特征
- 解码器(Decoder):负责生成输出序列,结合编码器信息逐词生成内容
2. 编码器核心组件(堆叠N层,每层结构一致)
1. 多头自注意力层(Multi-Head Self-Attention)
- 作用:实现全局上下文关联、一词多义区分
- 多头机制:多组QKV并行计算,捕捉不同类型的语义关系
- 单头vs多头:单头只能学习一种注意力模式,多头可学习多种
代码示例:多头自注意力
class MultiHeadAttention(nn.Module):
def __init__(self, d_model, nhead):
super().__init__()
self.nhead = nhead
self.d_k = d_model // nhead
# QKV投影矩阵
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 scaled_dot_product_attention(self, Q, K, V, mask=None):
# Q·K^T / sqrt(d_k)
scores = torch.matmul(Q, K.transpose(–2, –1)) / torch.sqrt(torch.tensor(self.d_k, dtype=torch.float32))
if mask is not None:
scores = scores.masked_fill(mask == 0, –1e9)
# Softmax归一化
attn_weights = torch.softmax(scores, dim=–1)
# 加权融合V
output = torch.matmul(attn_weights, V)
return output, attn_weights
def forward(self, x, mask=None):
batch_size = x.size(0)
# 生成QKV并拆分为多头
Q = self.W_q(x).view(batch_size, –1, self.nhead, self.d_k).transpose(1, 2)
K = self.W_k(x).view(batch_size, –1, self.nhead, self.d_k).transpose(1, 2)
V = self.W_v(x).view(batch_size, –1, self.nhead, self.d_k).transpose(1, 2)
# 计算注意力
output, attn_weights = self.scaled_dot_product_attention(Q, K, V, mask)
# 拼接多头输出
output = output.transpose(1, 2).contiguous().view(batch_size, –1, self.nhead * self.d_k)
# 最终线性变换
return self.W_o(output), attn_weights
2. 前馈神经网络(FFN)
- 结构:两层线性层 + ReLU激活 + Dropout
- 作用:对注意力输出做非线性变换,强化语义特征提取
- 公式:FFN(x) = max(0, xW1 + b1)W2 + b2
代码示例:FFN
class FeedForwardNetwork(nn.Module):
def __init__(self, d_model, d_ff=2048, dropout=0.1):
super().__init__()
self.fc1 = nn.Linear(d_model, d_ff)
self.fc2 = nn.Linear(d_ff, d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x):
return self.fc2(self.dropout(torch.relu(self.fc1(x))))
3. 残差连接+层归一化
- 残差连接:x + f(x),解决梯度消失问题
- 层归一化:对每个样本的特征维度做归一化,加速收敛
- 顺序:LayerNorm(x + SubLayer(x))
代码示例:残差连接与层归一化
class SublayerConnection(nn.Module):
def __init__(self, d_model, dropout=0.1):
super().__init__()
self.norm = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x, sublayer):
return x + self.dropout(sublayer(self.norm(x)))
3. 解码器核心组件(堆叠N层,每层结构一致)
1. 掩码多头自注意力层
- 关键机制:因果掩码(Causal Mask)
- 作用:防止生成时看到未来的词,保证自回归生成的合理性
- 掩码原理:上三角矩阵置为负无穷,Softmax后变为0
代码示例:生成因果掩码
def generate_causal_mask(sz):
mask = (torch.triu(torch.ones(sz, sz)) == 1).transpose(0, 1)
mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0))
return mask
2. 编码器-解码器注意力层
- 作用:关联编码器的输入语义,让生成内容贴合输入信息
- Q来源:解码器前一层输出
- K/V来源:编码器输出(memory)
3. 前馈神经网络+残差连接+层归一化:与编码器完全一致
4. 辅助组件
1. 词嵌入层(Embedding)
- 作用:将文本词语转化为数字向量
- 实现:可训练的查找表,维度通常为d_model
- 示例:“猫” → [0.2, 0.5, -0.3, …](512维向量)
代码示例:词嵌入
vocab_size = 10000
d_model = 512
embedding = nn.Embedding(vocab_size, d_model)
2. 位置编码(Positional Encoding)
- 作用:给向量添加位置信息,让模型知道词语的先后顺序
- Transformer论文方法:正弦余弦编码
- 公式:
- PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
- PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
代码示例:位置编码
class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_len=5000, dropout=0.1):
super().__init__()
self.dropout = nn.Dropout(p=dropout)
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):
x = x + self.pe[:x.size(0), :]
return self.dropout(x)
3. 线性层+Softmax层
- 线性层:将d_model维向量映射到词汇表大小
- Softmax:转化为概率分布,生成最终词语概率
四、自注意力缩放机制
为什么要除以 sqrt(d_k)?(梯度消失完整解释)
核心结论
除以 sqrt(d_k),核心目的只有一个:防止注意力点积结果过大,避免Softmax函数饱和进而引发梯度消失,保障模型训练过程稳定可控。这一步操作也被称为缩放点积注意力(Scaled Dot-Product Attention)。
1. 底层数学逻辑
设定基础条件:Q、K向量的维度为dkd_kdk,向量内部元素满足均值为0、方差为1,点积计算公式:
Q⋅K⊤=∑i=1dkqiki
Q \\cdot K^\\top = \\sum_{i=1}^{d_k} q_i k_i
Q⋅K⊤=i=1∑dkqiki
通过数学推导可得:点积结果的方差 = dkd_kdk
直观理解:假设Q和K的每个元素都是独立的随机变量,均值为0,方差为1,则它们乘积的期望为0,方差为1。d_k个独立随机变量之和的方差为d_k。
2. Softmax饱和问题详解
Softmax函数负责将点积分数转化为和为1的概率权重:
Softmax(zi)=ezi∑jezj
\\text{Softmax}(z_i) = \\frac{e^{z_i}}{\\sum_{j} e^{z_j}}
Softmax(zi)=∑jezjezi
饱和现象:当ziz_izi数值过大时,ezie^{z_i}ezi会指数爆炸,导致:
- 最大的ziz_izi对应的概率趋近于1
- 其他所有概率趋近于0
- 概率分布完全失去区分度
3. 梯度消失的成因与危害
Softmax梯度公式:
∂Softmax(zi)∂zj=Softmax(zi)⋅(δij−Softmax(zj))
\\frac{\\partial \\text{Softmax}(z_i)}{\\partial z_j} = \\text{Softmax}(z_i) \\cdot (\\delta_{ij} – \\text{Softmax}(z_j))
∂zj∂Softmax(zi)=Softmax(zi)⋅(δij−Softmax(zj))
当Softmax饱和时:
- 如果iii是最大值位置:Softmax(zi)≈1\\text{Softmax}(z_i) \\approx 1Softmax(zi)≈1,其他Softmax(zj)≈0\\text{Softmax}(z_j) \\approx 0Softmax(zj)≈0
- 梯度变为:∂Softmax(zi)∂zj≈δij−0=δij\\frac{\\partial \\text{Softmax}(z_i)}{\\partial z_j} \\approx \\delta_{ij} – 0 = \\delta_{ij}∂zj∂Softmax(zi)≈δij−0=δij
- 只有i=ji=ji=j时梯度为1,其余均为0,导致梯度无法传播
4. 缩放操作的解决原理
通过除以 sqrt(d_k) 进行缩放:
Scaled(Q,K)=Q⋅K⊤dk
\\text{Scaled}(Q,K) = \\frac{Q \\cdot K^\\top}{\\sqrt{d_k}}
Scaled(Q,K)=dkQ⋅K⊤
缩放后,点积结果的方差变为1:
Var(QK⊤dk)=dkdk=1
\\text{Var}\\left(\\frac{Q K^\\top}{\\sqrt{d_k}}\\right) = \\frac{d_k}{d_k} = 1
Var(dkQK⊤)=dkdk=1
效果:点积结果稳定在[-2, 2]区间,Softmax不会饱和,梯度正常流动。
五、课堂总结与回顾
→ 除以sqrt(d_k)缩放 → Softmax归一化 → 加权融合V向量输出
面试极简速记版
关键公式速查
| Q⋅K⊤/dkQ \\cdot K^\\top / \\sqrt{d_k}Q⋅K⊤/dk | 缩放点积 | 计算注意力分数 |
| Softmax(zi)=ezi/∑jezj\\text{Softmax}(z_i) = e^{z_i} / \\sum_j e^{z_j}Softmax(zi)=ezi/∑jezj | Softmax | 归一化为概率分布 |
| Output=Attention(Q,K,V)⋅V\\text{Output} = \\text{Attention}(Q,K,V) \\cdot VOutput=Attention(Q,K,V)⋅V | 注意力输出 | 加权融合值向量 |
| PE(pos,2i)=sin(pos/100002i/d)PE(pos, 2i) = \\sin(pos/10000^{2i/d})PE(pos,2i)=sin(pos/100002i/d) | 位置编码 | 注入位置信息 |





