Transformer

"Attention Is All You Need" — 改变了自然语言处理和人工智能的革命性架构

并行计算

抛弃RNN顺序处理,所有位置同时计算,GPU利用率从30%提升至90%+,训练速度大幅提升

🎯

全局依赖

自注意力让每个位置直接关注序列任意位置,有效捕获长距离依赖,解决RNN梯度消失问题

📈

可扩展性

架构简洁统一,易扩展到数十亿甚至万亿参数规模,GPT、BERT、LLaMA等模型的基础架构

🔄

通用性强

从机器翻译扩展到文本生成、图像识别(ViT)、语音处理、蛋白质结构预测等多领域

整体架构

输入
词嵌入
Token → Vector
+
位置编码
sin/cos编码
Encoder ×N
多头自注意力
Self-Attention
Add & Norm
残差+归一化
前馈网络 FFN
d→4d→d
Add & Norm
残差+归一化
Decoder ×N
Masked
掩码自注意力
因果掩码
Add & Norm
交叉注意力
Q:解码器 K,V:编码器
Add & Norm
前馈网络 FFN
Add & Norm
输出
线性层
d→vocab
Softmax
概率分布
下一个Token

输入序列嵌入+位置[自注意力 → Add&Norm → FFN → Add&Norm] ×N[掩码注意力 → Add&Norm → 交叉注意力 → Add&Norm → FFN → Add&Norm] ×NLinear → Softmax输出Token

自注意力机制详解

Transformer的核心创新

Attention(Q, K, V) = softmax(QKT / √dk) × V

Q (Query)

查询向量
「我想找什么?」

K (Key)

键向量
「我有什么特征?」

V (Value)

值向量
「我能提供什么?」

q₁
q₂
q₃
Query
×
k₁
k₂
k₃
KeyT
=
0.1
0.7
0.2
Attention Weights

多头注意力:将Q、K、V分成多个头(如8个),各自独立计算注意力后拼接,
使模型能从不同子空间学习不同的注意力模式

训练与推理

理解Transformer的工作流程

1

数据准备

将源序列和目标序列转换为token ID,添加特殊标记([BOS]、[EOS]、[PAD]),创建注意力掩码

# 示例:机器翻译数据 # 原始文本 → tokenizer分词 → 转换为词表中的ID source = "I love AI" source_ids = [101, 234, 567, 102] # 101=[BOS]开始标记, 234="I", 567="love", 102=[EOS]结束标记 target = "我爱人工智能" target_ids = [101, 876, 543, 210, 102] # 101=[BOS], 876="我", 543="爱", 210="人工智能", 102=[EOS]
2

前向传播

编码器处理源序列生成上下文表示,解码器基于上下文和已生成的token预测下一个token(Teacher Forcing)

# ========== 训练时使用 Teacher Forcing ========== # 编码器:将源语言序列编码为上下文向量 encoder_output = encoder(source_ids) # encoder_output: [batch_size, src_len, d_model] # 每个源语言token被编码为d_model维向量(如512维) # 解码器:基于编码器输出和目标序列预测 decoder_output = decoder( target_ids[:-1], # 输入: [BOS]我爱人工智能 (去掉EOS) encoder_output # 编码器的上下文表示 ) # decoder_output: [batch_size, tgt_len, d_model] # 线性层:将d_model维映射到词表大小,输出每个词的概率 logits = linear(decoder_output) # logits: [batch_size, tgt_len, vocab_size] # vocab_size可能是30000,表示预测30000个词的概率分布
3

计算损失

使用交叉熵损失计算预测分布与真实标签之间的差距

# 交叉熵损失:衡量预测概率分布与真实标签的差距 loss = CrossEntropyLoss( logits.view(-1, vocab_size), # 展平为 [batch*seq_len, vocab_size] target_ids[1:].view(-1) # 标签: 我爱人工智能[EOS] (去掉BOS,右移一位) ) # 为什么右移?因为输入[BOS]时应预测"我",输入"我"时应预测"爱"... # 这样模型学会"根据已有序列预测下一个token"
4

反向传播与优化

计算梯度,使用Adam优化器(配合warmup学习率调度)更新模型参数

# 反向传播:计算损失对每个参数的梯度 loss.backward() # 梯度裁剪:防止梯度爆炸,将梯度范数限制在1.0以内 clip_grad_norm_(model.parameters(), max_norm=1.0) # Adam优化器:根据梯度更新模型参数 optimizer.step() # 学习率调度:Transformer使用warmup策略 # 先线性增加学习率,再逐渐衰减,防止训练初期不稳定 lr_scheduler.step()
1

编码源序列

将输入文本编码为向量表示,这一步只需执行一次

# ========== 编码阶段(只执行一次)========== source = "I love AI" # 分词:将文本拆分为token并转换为ID source_ids = tokenize(source) # source_ids: [101, 234, 567, 890, 102] # 编码器前向传播:生成源语言的上下文表示 encoder_output = encoder(source_ids) # encoder_output: [1, 5, 512] (batch=1, 5个token, 512维) # 这个输出会被缓存,在整个解码过程中复用
2

自回归生成

从[BOS]开始,逐个生成token,每次将已生成的序列作为解码器输入

# ========== 自回归解码循环 ========== generated = [BOS_TOKEN] # 从开始标记开始 for step in range(max_length): # 1. 解码器处理已生成的序列 + 编码器输出 decoder_output = decoder(generated, encoder_output) # decoder_output: [1, len(generated), 512] # 2. 只取最后一个位置的输出(预测下一个token) next_token_logits = decoder_output[:, -1, :] # next_token_logits: [1, vocab_size] 即30000个词的得分 # 3. 从logits中选择下一个token(贪婪或采样) next_token = sample(next_token_logits) # 4. 将新token追加到已生成序列 generated.append(next_token) # 5. 遇到结束标记则停止 if next_token == EOS_TOKEN: break # 最终: generated = [BOS, 猫, 坐, 在, 垫, 子, 上, EOS]
3

解码策略

选择合适的解码策略影响生成质量

# ========== 方法1: 贪婪解码 ========== # 每次选择概率最高的token,确定性但可能生成重复内容 next_token = logits.argmax(dim=-1) # ========== 方法2: 温度采样 ========== # temperature越高越随机,越低越确定 temperature = 0.7 # 常用0.7-1.0 probs = softmax(logits / temperature) next_token = torch.multinomial(probs, num_samples=1) # ========== 方法3: Top-k 采样 ========== # 只从概率最高的k个token中采样 top_k = 50 probs = top_k_filter(logits, k=top_k) # ========== 方法4: Top-p (Nucleus) 采样 ========== # 从累积概率达到p的最小token集合中采样 top_p = 0.9 # 保留累积概率90%的token
4

KV Cache优化

缓存已计算的Key和Value,避免重复计算,将推理时间从O(n²)降到O(n)

# ========== 无KV Cache(低效)========== # 每次生成新token都要重新计算所有位置的K、V # 生成n个token需要 1+2+3+...+n = O(n²) 次计算 # ========== 使用KV Cache(高效)========== # 缓存已计算的K、V,只计算新token的K、V cached_k = concat(cached_k, new_k) # 追加新的Key cached_v = concat(cached_v, new_v) # 追加新的Value # 只用新token的Query与所有缓存的K、V计算注意力 attention = attention(new_q, cached_k, cached_v) # new_q: [1, 1, d] 只有1个token # cached_k/v: [1, n, d] 包含所有历史token # 生成n个token只需 O(n) 次计算,大幅加速!

实际示例

以机器翻译为例理解Transformer工作流程

🌐

英译中:机器翻译任务

输入(英文)
The cat sits on the mat
输出(中文)
猫坐在垫子上

处理流程可视化

Step 1: 分词(Tokenization)

[BOS]
The
cat
sits
on
the
mat
[EOS]

Step 2: 词嵌入 + 位置编码

[0.12, -0.34, 0.56, ...]

词嵌入

+
[sin(pos), cos(pos), ...]

位置编码

=
[0.15, -0.28, 0.61, ...]

输入表示

Step 3: 自注意力计算

"cat" 在计算注意力时会给 "sits" 较高的权重(主谓关系),
"mat" 会给 "on" 和 "the" 较高的权重(介词短语)

The cat sits on the mat

↑ "cat" 的注意力分布(颜色越深权重越高)

Step 4: 自回归解码生成 — 详细流程

t=1: [BOS]
t=2: [BOS]
t=3: [BOS]
最终: [EOS]

🔍 每个时间步t,解码器内部发生什么?

掩码多头自注意力

输入 [BOS, 猫, 坐] → 每个位置只能看到自己和之前的token(因果掩码防止"偷看"未来)
输出:融合了历史信息的向量表示

Add & Norm(残差连接 + 层归一化)

残差连接:output = input + attention_output(让梯度直接流过,训练更稳定)
层归一化:将数值标准化到均值0、方差1,加速收敛、防止数值爆炸

编码器-解码器注意力

Q来自解码器("坐"的表示),K和V来自编码器("The cat sits..."的表示)
这是翻译的关键:让中文token"看到"对应的英文信息

Add & Norm

再次残差连接 + 层归一化,稳定训练

前馈神经网络(FFN)

两层线性变换 + ReLU/GELU激活:FFN(x) = W₂ · ReLU(W₁ · x + b₁) + b₂
将512维扩展到2048维再压回512维,增加模型的表达能力(存储知识)

Add & Norm

第三次残差 + 归一化,输出送入下一个解码器层或最终输出层

线性层 + Softmax → 输出概率分布

线性层:将512维向量映射到词表大小(如30000维),每个维度是一个词的"得分"
Softmax:将得分转换为概率分布(所有概率和为1)
例如:P(在)=0.85, P(上)=0.08, P(里)=0.03... → 选择"在"作为下一个token

输入token → 掩码自注意力Add&Norm交叉注意力Add&NormFFNAdd&NormLinear+Softmax → 概率分布 → 采样 → 下一个token

GPT系列模型规模与训练

从GPT-1到ChatGPT:参数规模、矩阵维度与设计原理

模型 参数量 层数 (L) 隐藏维度 (d_model) 注意力头数 (h) FFN维度 上下文长度
GPT-1 117M 12 768 12 3072 512
GPT-2 1.5B 48 1600 25 6400 1024
GPT-3 175B 96 12288 96 49152 2048
GPT-4 ~1.8T (MoE) 120 ~16384 128 ~65536 8K-128K

核心参数矩阵规模(以GPT-3为例)

W

词嵌入矩阵

50257 × 12288

vocab_size × d_model
将token ID映射为向量
6.17亿参数

QKV

注意力权重

12288 × 12288 × 3

每层3个矩阵(Q,K,V)
96层共288个
每层4.53亿参数

FFN

前馈网络

12288 × 49152 × 2

W1: d→4d, W2: 4d→d
96层共192个
每层12.08亿参数

O

输出投影

12288 × 12288

多头注意力输出投影
合并多头结果
每层1.51亿参数

ChatGPT 三阶段训练流程

1

预训练 (Pre-training)

目标:学习语言的通用知识

数据:互联网文本 (~45TB,约3000亿token)

任务:下一个token预测 (Causal LM)

损失:交叉熵 L = -Σ log P(x_t | x_

计算:数千GPU,数周至数月

2

监督微调 (SFT)

目标:学习对话格式和指令遵循

数据:人工标注的对话数据 (~10万条)

任务:给定prompt生成高质量回复

方法:在预训练模型上继续训练

效果:模型学会按指令回答

3

RLHF 强化学习

目标:让模型输出符合人类偏好

步骤1:训练奖励模型(RM),学习人类偏好

步骤2:用PPO算法优化策略

损失:L = E[r(x,y)] - β·KL(π||π_ref)

效果:更有帮助、无害、诚实

为什么这样设计?

为什么层数要深?(96-120层)

更深的网络能学习更抽象的特征层次:浅层学词法、中层学句法、深层学语义和推理。研究表明模型能力随深度对数增长。

为什么d_model要大?(12288)

更大的隐藏维度提供更丰富的表示空间,能存储更多知识。Scaling Law显示:参数量翻倍,损失下降约0.05。

为什么FFN是4倍?(49152)

FFN是主要的知识存储层,4×扩展提供足够容量。研究表明FFN类似于键值记忆,存储事实知识。

为什么用多头注意力?(96头)

多头让模型同时关注不同类型的依赖:语法关系、语义相似、位置模式等。每个头d_k=128维,总计=96×128=12288。

为什么需要RLHF?

预训练只学"像人说话",不保证"说人话"。RLHF通过人类反馈校准,让模型学会有用、无害、诚实(HHH)。

为什么GPT-4用MoE?

Mixture of Experts让参数量增大但计算量不变。每次推理只激活部分专家(如8/16),实现"大而不贵"。

改进方向

Transformer架构的演进与优化

注意力效率优化

  • Flash Attention:IO感知算法,减少显存访问
  • 稀疏注意力:Longformer、BigBird,O(n)复杂度
  • 线性注意力:Performer、Linear Transformer
  • 分组查询注意力(GQA):减少KV缓存
📍

位置编码改进

  • RoPE:旋转位置编码,更好的外推性
  • ALiBi:注意力线性偏置,无需学习
  • 相对位置编码:T5、DeBERTa
  • NTK-aware插值:扩展上下文长度
🏗️

架构变体

  • Pre-Norm:层归一化前置,训练更稳定
  • SwiGLU/GeGLU:门控线性单元激活
  • MoE:混合专家,稀疏激活扩展容量
  • 仅解码器架构:GPT系列、LLaMA
🚀

训练优化

  • DeepNorm:更深网络的稳定训练
  • 混合精度训练:FP16/BF16加速
  • 梯度检查点:以计算换显存
  • μP参数化:超参迁移到大模型
📏

长上下文

  • 位置插值:扩展已训练模型的长度
  • 滑动窗口:Mistral的局部注意力
  • Ring Attention:分布式长序列处理
  • 压缩记忆:Transformer-XL风格
🌐

多模态扩展

  • Vision Transformer (ViT):图像处理
  • CLIP:视觉-语言对齐
  • Whisper:语音识别
  • Sora:视频生成

发展时间线

2017

Transformer诞生

Google发布"Attention Is All You Need",开创性地使用纯注意力机制

2018

GPT & BERT

预训练+微调范式确立,NLP进入大模型时代

2020

GPT-3 & ViT

1750亿参数展示涌现能力,Transformer进入视觉领域

2022-2023

ChatGPT & LLaMA

大语言模型爆发,开源生态繁荣,Flash Attention等优化成熟

2024+

多模态与Agent

GPT-4V、Gemini等多模态模型,Transformer成为AGI基础架构