Transformer 与训练原理专项
这是一篇面向小白但不偷懒的专项篇。我们不只讲“Transformer 很厉害”,而是一步一步拆开:文本怎么进模型、注意力到底怎么算、为什么要多头、为什么要残差和层归一化、训练时 loss 到底在优化什么、反向传播如何更新参数,以及一个大模型通常经历哪些训练阶段。
为什么这一篇值得你花时间啃?
如果你以后要做大模型测试、评测、效果分析、提示词优化、RAG、Agent,甚至只是想看懂研发在说什么,Transformer 和训练原理都是绕不过去的底层认知。它不要求你一上来会推导整篇论文,但你至少要知道:模型为什么能看上下文、为什么会预测下一个 token、为什么训练这么贵、为什么同样是大模型却行为差很多。 这篇不会写成“论文摘要”,而是尽量用教学方式拆开:先讲直觉,再讲结构,再手撕 Attention,再讲训练,再用一个极简代码骨架把前面说的东西落地。
图 1:把 Transformer 先看成一个“逐步读上下文再预测下一个 token 的机器” 输入文本先被切成 token ↓ token 变成向量,再加上位置信息 ↓ 多层 Transformer Block 反复处理上下文关系 ↓ 输出每个位置对“下一个 token”的概率分布 ↓ 采样或贪心选择下一个 token,继续往后生成
测试同学为什么也要学这一层?
因为很多“效果问题”其实都能在底层找到更清楚的解释:上下文一长效果下降、召回内容明明给了模型但它没用、长 prompt 成本高、流式首包慢、幻觉和训练数据偏差有关、微调后某类任务变好但泛化变差。你越懂底层,越不容易把现象全归结成“模型玄学”。
02. 为什么是 Transformer
在 Transformer 之前,序列建模常见的是 RNN / LSTM 一类模型。它们也能处理序列,但随着序列变长,问题越来越明显:并行性差、长距离依赖难学、训练慢。Transformer 的核心突破,是把“序列里谁该关注谁”这件事交给 Attention 机制来做。
| 模型家族 | 优点 | 短板 |
|---|---|---|
| RNN / LSTM | 天然处理序列,早年很主流 | 难并行,长距离依赖难学 |
| CNN 序列模型 | 局部模式提取强、并行较好 | 表达长距离关系不够自然 |
| Transformer | 可并行、可显式建模任意位置关系 | 长上下文计算和显存成本高 |
2.1 什么叫“长距离依赖”
比如这句话:“小王把奖杯递给了小李,因为他今天拿了第一名。”这里的“他”指谁,其实要看前面很远的上下文关系。Transformer 的强项之一,就是能让当前位置去关注序列中和自己最相关的那些位置,而不必像早期 RNN 一样一步一步艰难传递信息。
并行性更好
训练时同一段序列里很多位置可以一起算,而不是严格按时间步串行。
关系表达更直接
通过 Attention,当前位置可以直接“看”到其他位置。
可扩展性强
堆层数、堆参数、堆数据后,效果能继续往上走,这对大模型时代特别关键。
03. 输入到输出全链路
很多初学者对 Transformer 的理解停在“一个黑盒模型”。更好的方式是把它拆成完整的输入到输出链路。你先把链路记住,再回头看每一段为什么存在。
| 步骤 | 发生了什么 | 为什么需要它 |
|---|---|---|
| 1. Tokenization | 把文本切成 token | 模型不能直接吃原始字符串 |
| 2. Embedding | 把 token id 变成向量 | 神经网络处理的是连续数值 |
| 3. Positional Info | 给向量加位置信息 | 否则模型不知道词序 |
| 4. Transformer Blocks | 反复做 Attention + FFN | 建模上下文关系并提炼特征 |
| 5. Logits | 输出词表上每个 token 的分数 | 为下一个 token 选择做准备 |
| 6. Softmax / Sampling | 把分数转成概率并选出结果 | 真正得到下一个 token |
图 2:从一句话到下一个 token 的基本链路
文本 → token ids → embeddings + positions → Transformer blocks → logits / next token
3.1 Token 为什么不是“一个字一个 token”
因为 token 是“模型分词器定义出来的最小单元”,它可能是一个字、半个词、一个完整词、一个标点,甚至是一个常见短语片段。不同模型的 tokenizer 不一样,所以同一句话在不同模型里 token 数也可能不同。
3.2 位置为什么要单独处理
如果你只给模型一堆 token 向量,不告诉它顺序,那么“我爱你”和“你爱我”在集合意义上几乎没区别。位置编码的意义,就是告诉模型:这些 token 不仅是什么,还排在第几位。
04. Self-Attention 手撕拆解
这是整篇最关键的一节。你不一定要把所有矩阵细节背下来,但至少要能回答:Attention 到底在算什么?为什么它能让一个位置“看见”别的位置?
4.1 Self-Attention 的一句话定义
Self-Attention = 当前 token 根据自己和其他 token 的相关性,为不同位置分配不同关注权重,再把这些位置信息加权汇总。
4.2 Q、K、V 是什么
| 符号 | 直觉理解 |
|---|---|
| Q(Query) | “我现在在找什么信息?” |
| K(Key) | “我这里能提供什么信息给别人匹配?” |
| V(Value) | “如果别人最终关注我,我真正贡献出去的内容是什么?” |
4.3 手撕一个极简例子
我们用 3 个 token 来做最简化示意,不追求工业级严谨,只追求你能算明白。
| token | 向量表示 |
|---|---|
| x1 | [1, 0] |
| x2 | [1, 1] |
| x3 | [0, 1] |
为了手算简单,我们先假设:
Wq = I,所以Q = XWk = I,所以K = XWv = I,所以V = X
现在我们关心第 2 个 token,也就是 x2 = [1, 1]。它的 query 向量是:
q2 = [1, 1]接着分别与每个 key 做点积:
| 匹配对象 | 计算 | 分数 |
|---|---|---|
| q2 · k1 | [1,1] · [1,0] | 1 |
| q2 · k2 | [1,1] · [1,1] | 2 |
| q2 · k3 | [1,1] · [0,1] | 1 |
这说明:对第 2 个 token 来说,它和自己最相关,对第 1、3 个 token 的相关性相同。
4.4 为什么还要除以 sqrt(dk)
因为维度一大,点积值容易变得很大,Softmax 会过于尖锐,训练不稳定。这里 dk = 2,所以除以 sqrt(2) 约等于 1.414:
scaled scores = [1/1.414, 2/1.414, 1/1.414]
≈ [0.71, 1.41, 0.71]4.5 Softmax 后得到注意力权重
把上面的分数做 Softmax,近似可以得到:
attention weights ≈ [0.25, 0.50, 0.25]这表示:第 2 个 token 在整合信息时,大约 50% 关注自己,另外各 25% 关注第 1 和第 3 个 token。
4.6 最终输出怎么得到
用注意力权重去加权 Value:
output2 = 0.25 * v1 + 0.50 * v2 + 0.25 * v3
= 0.25 * [1,0] + 0.50 * [1,1] + 0.25 * [0,1]
= [0.75, 0.75]这就是 Self-Attention 的核心:当前位置的输出,不再只来自自己,而是来自对全局相关位置的加权汇总。
4.7 Decoder Only 里还要加 Mask
大语言模型做下一个 token 预测时,当前位置不能偷看未来位置,所以要加因果 Mask。还是看上面的第 2 个 token,如果它不能看第 3 个 token,那么分数变成:
[1, 2, -inf]做 Softmax 后,大约是:
[0.33, 0.67, 0.00]也就是说第 2 个 token 现在只能看见自己和前面的位置,这就是 Decoder-Only 模型能做“自回归生成”的关键之一。
把这一节真正学懂的标志。
你不必现场推一大堆矩阵,但你至少应该能口头讲清楚:Q 和 K 负责算相关性,Softmax 给出关注权重,V 提供被加权汇总的内容,Mask 负责阻止当前位置偷看未来。
05. Multi-Head / FFN / 残差
单个 Attention 头已经能工作,但 Transformer 并不只靠一个头。真正的 Transformer Block 还包括 Multi-Head Attention、Feed Forward Network(FFN)、残差连接和 LayerNorm。
| 组件 | 作用 | 直觉解释 |
|---|---|---|
| Multi-Head Attention | 并行从多个子空间看关系 | 像从多个角度同时观察一句话 |
| FFN | 对每个位置再做一层非线性变换 | 把注意力整合后的表示再加工一遍 |
| Residual | 保留原输入信息,便于深层训练 | 避免网络太深后信息完全丢失 |
| LayerNorm | 稳定训练、规范数值分布 | 让每层的数值别飘太狠 |
图 3:一个典型 Transformer Block 的骨架 输入表示 ↓ Multi-Head Attention ↓ Residual + LayerNorm ↓ Feed Forward Network ↓ Residual + LayerNorm
5.1 为什么要有多个头
因为一句话里的关系不止一种。一个头可能更关注主谓关系,另一个头可能更关注指代关系,另一个头可能更关注位置模式。多个头并行,相当于让模型能从不同投影空间同时观察序列。
5.2 FFN 为什么不能省
Attention 负责“看别人”,但不代表它已经把当前位置的表示加工得够充分。FFN 的作用,是在每个位置上单独做更丰富的非线性变换,相当于把“看完别人之后”的信息再提炼一遍。
06. 为什么大模型常用 Decoder Only
Transformer 家族里常见有三种形态:Encoder Only、Encoder-Decoder、Decoder Only。今天的大语言模型大量采用 Decoder Only,不是因为另外两种没用,而是因为它特别适合“根据已有上下文继续往后生成”。
| 结构 | 典型任务 | 特点 |
|---|---|---|
| Encoder Only | 分类、判别、表示学习 | 更擅长理解整段输入 |
| Encoder-Decoder | 翻译、摘要、序列到序列 | 输入输出分工清楚 |
| Decoder Only | 自回归文本生成 | 特别适合 next-token prediction |
大模型聊天、本质上还是在不断做“给定前文,预测下一个 token”。Decoder Only 结构天然贴合这个训练目标,所以成为主流。
07. 训练全流程总览
模型不是天生就会说话的。训练的本质,是让参数在海量样本上反复调整,逐渐把“给定上下文应该预测什么”这件事学会。 图 4:训练流程总览
数据准备 → 分词与打包 → 前向计算 → 计算损失 → 反向传播与参数更新
| 阶段 | 在做什么 |
|---|---|
| 数据准备 | 清洗语料、去重、过滤脏数据、格式整理 |
| Tokenize | 把文本变成 token ids |
| Batching | 组成 batch,放进 GPU 并行训练 |
| Forward | 模型算出每个位置对下一个 token 的预测 |
| Loss | 比较预测和正确答案差多远 |
| Backward | 把误差往回传,算每个参数该怎么调 |
| Optimizer Step | 真正更新参数 |
7.1 训练和推理的最大区别
| 维度 | 训练 | 推理 |
|---|---|---|
| 目标 | 学参数 | 用参数生成结果 |
| 是否反向传播 | 要 | 不要 |
| 成本 | 极高 | 相对低,但大模型也不便宜 |
| 数据处理 | 海量样本、多轮迭代 | 单次或批量输入即可 |
08. Loss、反向传播、参数更新
训练里最核心的一件事,就是让模型“越来越愿意把更高概率给正确答案”。Loss 是对“差多远”的量化,反向传播则是告诉每个参数:你该往哪个方向调,调多少。
8.1 用一个最小例子理解交叉熵
假设当前位置真实下一个 token 是“你”,模型给出的概率分布是:
["我": 0.10, "爱": 0.70, "你": 0.20]因为真实答案是“你”,所以我们只关心模型给“你”的概率 0.20。交叉熵损失大致就是:
loss = -log(0.20) ≈ 1.609如果模型把“你”的概率提到 0.80,那么:
loss = -log(0.80) ≈ 0.223这就说明:模型越把高概率分配给正确答案,loss 越小。
8.2 反向传播到底在做什么
你可以先不用把链式求导全推完,但要知道这件事的本质:根据 loss 对每个参数的影响,算出每个参数应该往哪个方向微调,才能让下次 loss 更小。
8.3 参数更新为什么还需要 Optimizer
理论上有了梯度就能更新,但实际训练里要考虑学习率、历史梯度、数值稳定性,所以常用 AdamW 一类优化器,而不是最朴素的“减去梯度乘学习率”。
09. 预训练、SFT、对齐
今天大家说“大模型训练”,通常不是单一阶段,而是多个阶段叠起来。最常见的理解方式,是分成预训练、监督微调、对齐。
| 阶段 | 在学什么 | 常见结果 |
|---|---|---|
| 预训练 | 海量语料上的通用语言模式 | 模型具备广泛语言能力和知识先验 |
| SFT | 按示例学习更像助手地回答 | 输出更像人希望的格式和风格 |
| 对齐 | 让模型更符合偏好、安全和规则 | 更听指令、更少危险输出 |
9.1 RLHF 和 DPO 要怎么理解
你不一定要马上掌握算法细节,但至少要知道:它们都属于“对齐”范畴,目标是让模型更符合人类偏好。RLHF 更像先训练奖励模型再做强化学习,DPO 更像直接基于偏好对进行优化。对测试同学来说,记住它们会影响模型风格、服从性和安全性就很有用了。
10. 训练为什么这么贵
很多人知道“训练大模型很贵”,但不知道贵在哪里。真正的成本,不只是买几张卡那么简单,而是多种成本叠在一起。
| 成本项 | 为什么贵 |
|---|---|
| 算力 | 参数多、层数多、batch 大,前向和反向都要大量矩阵计算 |
| 显存 | 训练不仅要存参数,还要存梯度、优化器状态和中间激活 |
| 数据 | 清洗、去重、标注、对齐数据构建都很贵 |
| 通信 | 多卡、多机训练时,同步和并行开销很大 |
| 试错 | 超参、数据配比、训练策略一旦试错,烧的是真金白银 |
上下文变长会更贵
Attention 计算和序列长度关系非常敏感,长上下文不是白来的。
训练比推理贵得多
因为训练要反向传播,还要反复迭代很多轮。
并行不是免费午餐
多卡能加速,但通信和工程复杂度也会上来。
11. 新手最容易误解的点
| 误解 | 为什么不对 |
|---|---|
| 模型真的“理解”了语义 | 更准确地说,是在大量数据中学到了统计模式和表示关系 |
| Attention 就是解释性真相 | 它能反映一部分关注关系,但不等于全部因果解释 |
| Loss 降了就什么都好了 | 训练目标变好不等于所有下游任务都自动最优 |
| 微调就是教模型记住几条知识 | 微调会影响整体行为分布,不只是加几条记忆 |
| 模型输出像人说话,就说明它像人一样思考 | 生成像人,不代表内部机制就是人类思维 |
12. 手撕一个极简训练循环
下面这段代码不是工业级训练器,而是帮助你把前面所有概念真正串起来:Embedding、位置编码、Masked Self-Attention、预测下一个 token、计算 loss、反向传播、更新参数。
import torch
import torch.nn as nn
import torch.nn.functional as F
class TinyDecoderBlock(nn.Module):
def __init__(self, dim, heads):
super().__init__()
self.attn = nn.MultiheadAttention(dim, heads, batch_first=True)
self.ln1 = nn.LayerNorm(dim)
self.ffn = nn.Sequential(
nn.Linear(dim, dim * 4),
nn.ReLU(),
nn.Linear(dim * 4, dim),
)
self.ln2 = nn.LayerNorm(dim)
def forward(self, x, attn_mask):
attn_out, _ = self.attn(x, x, x, attn_mask=attn_mask)
x = self.ln1(x + attn_out)
ffn_out = self.ffn(x)
x = self.ln2(x + ffn_out)
return x
class TinyLM(nn.Module):
def __init__(self, vocab_size, dim=32, heads=4, max_len=32):
super().__init__()
self.token_emb = nn.Embedding(vocab_size, dim)
self.pos_emb = nn.Embedding(max_len, dim)
self.block = TinyDecoderBlock(dim, heads)
self.lm_head = nn.Linear(dim, vocab_size)
def forward(self, input_ids):
bsz, seq_len = input_ids.shape
pos = torch.arange(seq_len, device=input_ids.device).unsqueeze(0)
x = self.token_emb(input_ids) + self.pos_emb(pos)
# 上三角为 True,表示不能看未来位置
causal_mask = torch.triu(
torch.ones(seq_len, seq_len, device=input_ids.device),
diagonal=1
).bool()
x = self.block(x, causal_mask)
logits = self.lm_head(x)
return logits
# 伪造一点 toy 数据:每个样本是一串 token id
input_ids = torch.tensor([
[1, 5, 9, 4],
[2, 6, 3, 8],
], dtype=torch.long)
# 训练 next-token prediction:用前 n-1 个 token 预测后 n-1 个 token
x = input_ids[:, :-1]
y = input_ids[:, 1:]
model = TinyLM(vocab_size=20)
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3)
for step in range(100):
logits = model(x)
loss = F.cross_entropy(
logits.reshape(-1, logits.size(-1)),
y.reshape(-1)
)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if step % 10 == 0:
print(step, float(loss))12.1 这段极简代码里,对应了哪些概念
| 代码片段 | 对应概念 |
|---|---|
| Embedding | token id → 向量表示 |
| pos_emb | 位置编码 / 位置嵌入 |
| MultiheadAttention | 多头自注意力 |
| causal_mask | 阻止当前位置偷看未来 |
| lm_head | 把隐藏状态映射回词表分数 |
| cross_entropy | 计算 next-token prediction 的损失 |
| backward() | 反向传播算梯度 |
| optimizer.step() | 更新参数 |
12.2 为什么说这是“极简骨架”
因为真实大模型训练远比这个复杂:多层 block、更大的维度、更大的 batch、混合精度、分布式训练、学习率调度、梯度裁剪、数据打包策略、检查点保存、评估集监控……但核心逻辑,就是你现在看到的这条主线。
如果你能把这段骨架讲顺,说明已经入门了。
你不需要手写出工业级大模型,但你至少应该能解释:输入怎么进、Attention 怎么算、loss 怎么来、梯度怎么回、参数怎么更新。
13. 训练中的常见现象怎么理解
很多底层知识真正有价值的地方,不在于考试,而在于你以后看到现象时知道该往哪一层想。下面这些现象,在模型训练、微调、应用评测里都很常见。
| 现象 | 可能和哪一层有关 | 你该怎么理解 |
|---|---|---|
| 长上下文效果变差 | Attention 计算复杂度、位置泛化、上下文污染 | 不是“模型突然变傻”,而是长序列本来就难 |
| 微调后某任务变好,通用能力变差 | SFT 数据分布偏移 | 模型更贴近新任务,但也可能牺牲泛化 |
| 训练 loss 降了,但业务效果没明显提升 | 训练目标与业务目标不完全一致 | loss 好看不等于下游任务一定最优 |
| 同一问题多次输出差异大 | 采样策略、温度、概率分布尾部 | 生成本身带随机性,不一定是训练坏了 |
| RAG 明明给了文档,模型还是不用 | Prompt 组织、注意力分配、模型偏好 | “给了上下文”不等于“模型一定使用” |
13.1 为什么会出现“loss 好了但业务没感觉”
因为训练 loss 通常只是一个代理目标,它描述的是“在训练数据上 next-token prediction 做得更好了”。但业务世界关心的可能是:摘要是否更稳、分类边界是否更准、幻觉是否更少、是否更听话。这些不是单靠一个 loss 值就能完整表示的。
13.2 为什么长上下文不是白来的
你以后会经常听到“支持 128K 上下文”“支持更长窗口”。这不是简单把数字改大。上下文一变长,注意力计算量、显存、训练数据覆盖、位置泛化问题都会一起上来。所以长上下文能力通常也伴随更高成本和更多工程技巧。
14. 测试同学怎么用这些知识
这套底层知识,对测试同学非常有用。它的价值不在于让你去训练模型,而在于帮助你把现象拆得更准、问题提得更实、测试点抓得更稳。
看上下文问题
你会更容易理解为什么长 prompt、长对话、多文档拼接容易出问题。
看成本问题
你会知道为什么长输入、长输出、流式、批量评测都和成本直接相关。
看效果问题
你会知道哪些问题更像检索问题,哪些更像 Prompt 组织问题,哪些更像训练分布问题。
14.1 大模型测试里最常见的 5 类底层关联
| 测试现象 | 底层关联 |
|---|---|
| 长会话里模型开始忘前文 | 上下文窗口、注意力分配、历史污染 |
| 流式首包慢 | 推理路径、prefill 成本、输入长度 |
| 输出格式飘 | 训练分布、提示约束不足、采样波动 |
| 检索内容没被采用 | 上下文组织、模型偏好、长上下文竞争 |
| 微调后安全风格变化 | SFT / 对齐数据分布改变 |
14.2 你以后提问题会更专业
比如你不再只写“模型不准”,而会写:
- 长上下文拼接后后半段知识被忽略,怀疑与上下文组织或注意力竞争有关。
- 同样问题在高温参数下结果不稳,建议区分采样波动与真实能力回退。
- 微调版本在客服分类上更好,但开放问答泛化下降,怀疑 SFT 数据分布偏窄。
这就是底层知识对测试的真正价值。
它不一定让你立刻会训练模型,但会让你把问题讲得更准确,把测试设计得更有针对性,把和研发的沟通拉到同一个语境里。
15. 练习题与手算题
下面这组题,既有概念题,也有手算题。你不一定一次全答对,但只要能自己尝试答一遍,理解就会比“看过一遍”深很多。
15.1 概念题
| 问题 | 你至少应该答到什么程度 |
|---|---|
| 为什么 Transformer 比 RNN 更适合大模型时代? | 因为更容易并行,更擅长建模长距离依赖,也更适合大规模扩展 |
| Q、K、V 各自是什么? | Q 表示当前想找什么,K 表示我能提供什么,V 表示真正提供的内容 |
| 为什么 Decoder Only 要加 Mask? | 因为做 next-token prediction 时不能偷看未来位置 |
| 为什么训练这么贵? | 算力、显存、数据、通信、试错成本一起叠加 |
15.2 手算题
已知某个 query 对 3 个 key 的未缩放点积分数分别是 [1, 2, 1],请回答:
- 如果不考虑 mask,谁最受关注?
- 如果第 3 个位置被 mask 成
-inf,最终权重会如何变化? - 为什么除以
sqrt(dk)会让训练更稳定?
15.3 自测标准
本页自测标准
- 你能不能不用看笔记,口头讲完整个“文本到下一个 token”的流程。
- 你能不能手算一个极简 attention 例子。
- 你能不能区分预训练、SFT、对齐各在解决什么问题。
- 你能不能从测试视角解释长上下文、流式首包慢、微调漂移这些现象。
16. 练习建议与延伸阅读
这类底层内容,最怕“看懂了,但讲不出来”。所以最有效的复习方式,不是继续堆新名词,而是反复做这几件事:
练习 1
不用看笔记,自己口头讲一遍“文本如何变成下一个 token”。
练习 2
手算一遍 3 个 token 的 Self-Attention 分数和权重。
练习 3
自己解释清楚 Q、K、V、Mask、Softmax 各自负责什么。
练习 4
把预训练、SFT、对齐三阶段各用一句话讲给别人听。
练习 5
照着极简代码骨架,用自己的话解释每一段在干什么。
练习 6
从测试视角思考:长上下文、幻觉、流式延迟、微调漂移分别和哪一层相关。
最后的学习建议。
别追求一次把论文推到极致。对大模型测试来说,先把“结构、机制、训练目标、成本来源、常见误解”这五件事真正讲明白,已经比只会背术语强太多。等你后面再去看 LangChain、LangGraph、RAG、评测,就会发现很多现象突然不神秘了。
补充参考答案要点
- 本篇练习的核心答案应围绕三件事:Attention 如何建模依赖、训练目标如何驱动能力形成、测试为什么要理解这些底层原理。
- 如果让你解释训练退化或能力变化,答案不能只说“模型变差了”,要能落到数据、目标函数、对齐或推理配置层。
- 真正合格的答案,应能把原理和测试策略对应起来,例如为什么长依赖、位置编码和注意力瓶颈会影响长文本场景。