Skip to content

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 = X
  • Wk = I,所以 K = X
  • Wv = 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,那么分数变成:

json
[1, 2, -inf]

做 Softmax 后,大约是:

json
[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 是“你”,模型给出的概率分布是:

json
["我": 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、反向传播、更新参数。

python
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 这段极简代码里,对应了哪些概念

代码片段对应概念
Embeddingtoken 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],请回答:

  1. 如果不考虑 mask,谁最受关注?
  2. 如果第 3 个位置被 mask 成 -inf,最终权重会如何变化?
  3. 为什么除以 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 如何建模依赖、训练目标如何驱动能力形成、测试为什么要理解这些底层原理。
  • 如果让你解释训练退化或能力变化,答案不能只说“模型变差了”,要能落到数据、目标函数、对齐或推理配置层。
  • 真正合格的答案,应能把原理和测试策略对应起来,例如为什么长依赖、位置编码和注意力瓶颈会影响长文本场景。