Skip to content
Gains Summary
Main Navigation 首页 / Home
C++ 编程 / C++ Programming
系统与高性能 / Systems & Performance
Web 开发 / Web Development
人工智能 / Artificial Intelligence
工业软件 / Industrial Software
其他内容 / Other Topics
C++ 编程 / C++系统与性能 / SystemsWeb 开发 / Web人工智能 / AI工业软件 / Industrial

外观

Sidebar Navigation

← 人工智能 / Artificial Intelligence

大语言模型 / Large Language Models

1. LLM 前置知识学习路线 / A Prerequisite Learning Path for Large Language Models

2. 神经网络基础 - 从零理解 AI 的"计算单元" / Neural Network Fundamentals from Artificial Neurons

3. Token 与上下文窗口 - LLM 的计费与记忆单位 / Tokens and Context Windows as the Units of LLM Cost and Memory

4. 解码策略 - 控制 LLM 输出的艺术 / Decoding Strategies for Controlling LLM Output

5. 消息角色 - 构建 Agent 对话的基础 / Message Roles as the Foundation of Agent Conversations

6. 流式输出 - 实时交互的体验优化 / Streaming Output for Responsive Interaction

7. Prompt 工程基础 - 与 LLM 高效对话的技巧 / Prompt Engineering Fundamentals for Effective LLM Interaction

8. LLM 进化史 - 从词向量到 Transformer / The Evolution of LLMs from Word Embeddings to Transformers

9. Transformer 手动计算:从 Attention 到 Encoder-Decoder 完整数据流

10. Transformer 核心原理 - 现代 LLM 的基石 / Transformer Fundamentals Behind Modern LLMs

11. Decoder-Only LLM 深度解析:为什么扔掉 Encoder,以及 KV Cache 如何工作

12. 训练 vs 推理:同一个 Transformer,两条完全不同的执行路径

13. Transformer 训练阶段计算详解 - 手算每一行矩阵 / Transformer Training Computation Matrix by Matrix

14. Transformer 推理阶段详解 — 模型如何"思考"并生成回答 / Transformer Inference and Autoregressive Generation

15. 训练基础扫盲 - 理解 Fine-tune 在做什么 / A Training Primer for Understanding Fine-Tuning

16. LLM 预训练全景:数据管道、Scaling Laws 与训练稳定性

17. 训练基础设施 - 从单卡到千卡集群 / Training Infrastructure from One GPU to Thousand-GPU Clusters

18. Post-Training Pipeline - 从 Base Model 到可用助手 / The Post-Training Pipeline from Base Model to Assistant

19. SFT 深度解析:从 Base Model 到指令跟随——后训练第一步 / SFT Deep Dive: Teaching Base Models to Follow Instructions

20. RLHF 深度解析:从 Reward Model 到 PPO 的完整对齐流程

21. DPO 与对齐方法:从 RLHF 复杂度到直接偏好优化

22. 研究视角:DL/RL 理论到 LLM 训练的完整映射

本页目录

研究视角:DL/RL 理论到 LLM 训练的完整映射 ​

📅 创建时间:2026-07-29 🏷️ 标签:#DeepLearning #ReinforcementLearning #TrainingPipeline #FineTuning #Research 📚 前置知识:[[11-training-primer]] [[08-transformer-by-hand]] [[16-rlhf-deep-dive]]


📋 本章目标 ​

  • 理解课堂上学的 DL/RL 理论在 LLM 训练的哪个具体环节被使用
  • 理解每一行训练代码(loss.backward(), optimizer.step())对应的数学原理
  • 理解"模型代码"定义什么、"权重文件"存什么、训练脚本怎么串起一切
  • 理解预训练、SFT、RLHF 三个阶段各自用了哪些 DL/RL 知识
  • 能够从理论出发,读懂并修改一个训练脚本

第1部分:DL 理论 → 训练代码的精确映射 ​

很多同学学完 DL 课之后有一个困惑:"我知道反向传播、知道 Adam、知道交叉熵——但这些东西在训练 LLM 时,对应代码里的哪一行?"

这一部分就是这张"黑板公式到代码行"的映射表。以后你写训练脚本时,看到每一行都知道它在数学上对应什么。

1.1 一个训练 step 的完整拆解 ​

python
# ===== 一个训练 step,拆解每一行对应的 DL 理论 =====

# 数据:从 DataLoader 取一个 batch
batch = next(train_dataloader)
input_ids = batch["input_ids"].to("cuda")  # (batch_size, seq_len)
labels = batch["labels"].to("cuda")

# ─── 前向传播(课堂上的 Forward Propagation)───
# 数据流过 Embedding → N 层 Transformer → LM Head
# 每层内部依次:LayerNorm → Attention(Q,K,V) → +Residual →
#              LayerNorm → FFN(GELU) → +Residual
outputs = model(input_ids=input_ids, labels=labels)
# outputs.logits: (batch, seq_len, vocab_size) ← 每个位置对每个词的预测分数
# outputs.loss: 标量

loss = outputs.loss
# ↑ 这就是老师黑板上的 L(θ) = -1/N · Σᵢ Σⱼ yᵢⱼ·log(pᵢⱼ)
#   其中 yᵢⱼ 是 one-hot 的真实标签,pᵢⱼ 是 softmax(logits) 的预测概率

# ─── 反向传播(课堂上的 Backpropagation)───
# PyTorch 的 autograd 自动用链式法则算出每一个参数的 ∂L/∂W
# 你不需要手写 ∂L/∂W = ∂L/∂y · ∂y/∂h · ∂h/∂W
# autograd 替你做了——但原理就是你学的那套链式法则
loss.backward()
# 现在 model.layers[0].self_attn.q_proj.weight.grad 里存了 ∂L/∂W_q0
# 这个 .grad tensor 的形状和 weight 完全一样 (4096, 4096)

# ─── 梯度裁剪(防止梯度爆炸)───
# 如果某个参数的梯度模长 > max_norm,按比例缩小
# 为什么需要?长序列 + 深层网络 → 梯度可能指数级增长
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

# ─── 参数更新(课堂上的 Gradient Descent + Adam)───
# SGD:  W = W - lr × ∂L/∂W
# Adam: W = W - lr × m̂ / (√v̂ + ε)
#   其中 m̂ = β₁·m + (1-β₁)·∂L/∂W   ← 一阶矩(动量)
#        v̂ = β₂·v + (1-β₂)·(∂L/∂W)²  ← 二阶矩(自适应学习率)
optimizer.step()    # ← 就是这一行!所有 DL 课最终都归结于此

optimizer.zero_grad()  # 清空所有参数的 .grad,准备下一个 step
# 为什么必须清零?因为 .backward() 是累加的,不清零会混入上一步的梯度

# ─── 学习率更新 ──
scheduler.step()    # lr 按 Warmup+Cosine 曲线变化
# Warmup:前 N 步 lr 从 0 线性增长到 peak_lr(防止初期梯度不稳定)
# Cosine Decay:之后按余弦曲线衰减到接近 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

1.2 理论→代码 速查表 ​

┌─────────────────────────────────────────────────────────────┐
│        课堂上学的 DL 理论 ──→ 训练脚本里的哪一行              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  前向传播 (Forward)              outputs = model(batch)     │
│  ├─ Embedding 查表               embed_tokens(input_ids)   │
│  ├─ Self-Attention (Q,K,V)       self_attn(hidden_states)  │
│  │   └─ softmax(QK^T/√d_k)·V    ← 你手算过的那个           │
│  ├─ 残差连接 (Residual)          x = x + attn_output       │
│  ├─ 层归一化 (RMSNorm)           RMSNorm / LayerNorm       │
│  ├─ 激活函数 (GELU/SwiGLU)       F.gelu / F.silu           │
│  └─ Feed-Forward (FFN)           mlp(hidden_states)         │
│                                                             │
│  交叉熵损失 (Cross-Entropy)      loss = F.cross_entropy(   │
│  → 老师黑板上的 -Σ y·log(ŷ)      │   logits, labels)        │
│                                                             │
│  反向传播 (Backprop)             loss.backward()           │
│  → 链式法则计算所有 ∂L/∂W         ← 就是这一行!            │
│                                                             │
│  梯度下降 (Gradient Descent)      optimizer.step()          │
│  → SGD: W = W - lr × ∂L/∂W        ← 就是这一行!            │
│    Adam: W = W - lr × m̂/(√v̂+ε)                             │
│                                                             │
│  优化器 (Adam/AdamW)              optimizer = AdamW(        │
│  AdamW = Adam + 解耦 weight decay   model.parameters(),     │
│  weight_decay 直接作用于 W          lr=3e-4,               │
│  而非混入梯度计算中                  weight_decay=0.1)      │
│                                                             │
│  学习率调度 (LR Schedule)         scheduler = CosineWarmup  │
│  → Warmup + Cosine Decay          scheduler.step()         │
│                                                             │
│  梯度裁剪 (Gradient Clipping)     clip_grad_norm_(         │
│  → 防止梯度爆炸                     model.parameters(), 1.0)│
│                                                             │
│  混合精度 (Mixed Precision)       with autocast(dtype=bf16):│
│  → 前向用 BF16 省显存              outputs = model(batch)  │
│  → 权重更新用 FP32 保精度          ...                      │
│                                                             │
│  Weight Decay (L2 正则)           AdamW 的 weight_decay     │
│  → 防止过拟合                                                │
│                                                             │
│  Dropout                          nn.Dropout(0.1)           │
│  → 训练时随机丢弃神经元                                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

1.3 Transformer 各组件用到了哪些课上知识 ​

┌─────────────────────────────────────────────────────────────┐
│         Transformer 组件 ──→ 对应的 DL 基础概念               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Embedding 层      → 查表操作(不是矩阵乘法!)              │
│                      本质:把离散 ID 映射到稠密向量            │
│                      训练后语义相近的词向量接近               │
│                                                             │
│  Self-Attention    → 矩阵乘法 + Softmax + 加权求和           │
│  ├─ Q,K,V 投影     → 三个 nn.Linear(就是矩阵乘法)          │
│  ├─ Attention 分数 → QK^T 点积 = 线性代数里的内积           │
│  ├─ Softmax        → 概率论:把分数变成概率分布              │
│  └─ 加权输出       → 概率论:期望值 = Σ p_i × v_i            │
│                                                             │
│  Positional Enc.   → 正余弦函数(Sinusoidal)或              │
│  (RoPE)            → 旋转矩阵(RoPE=复数乘法,线性代数)     │
│                                                             │
│  LayerNorm(RMS)    → 统计学:减均值除标准差                  │
│                      让每层的激活值保持稳定分布               │
│                      LayerNorm ≠ BatchNorm!                 │
│                      LayerNorm 沿特征维度归一化              │
│                      BatchNorm 沿 batch 维度归一化           │
│                                                             │
│  Feed-Forward      → 两层全连接 + 激活函数                   │
│  (FFN)             → FFN(x) = W₂ · GELU(W₁·x + b₁) + b₂   │
│                      GELU/SwiGLU 是 ReLU 的平滑版            │
│                                                             │
│  残差连接           → x = F(x) + x                          │
│  (Residual)        → 没有残差,深层网络梯度消失,训不动      │
│                      (这就是 ResNet 论文的核心贡献)         │
│                                                             │
│  LM Head           → nn.Linear(hidden_dim, vocab_size)      │
│                      投影到词表维度 + Softmax = 概率分布     │
│                      取 argmax = 预测下一个 token             │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

第2部分:RL 理论 → RLHF 代码的精确映射 ​

2.1 RL 概念到 RLHF 的一一对应 ​

┌─────────────────────────────────────────────────────────────┐
│        课堂上学的 RL 理论 ──→ RLHF 具体实现                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  RL 概念                     在 RLHF 中对应什么              │
│  ────────                    ────────────────               │
│                                                             │
│  Agent(智能体)             LLM 本身 = Policy π_θ           │
│  → 感知环境、选择动作        → 给定 prompt,生成回答          │
│                                                             │
│  Environment(环境)         Reward Model + 对话上下文       │
│  → 接收动作、返回奖励        → 对完整回答输出一个标量分数     │
│                                                             │
│  State sₜ                  已生成的 token 序列               │
│  → 环境在时刻 t 的状态       → sₜ = [x₁, ..., xₜ]           │
│                                                             │
│  Action aₜ                 生成下一个 token                  │
│  → Agent 在状态 sₜ 的选择    → 从|V|个 token 中选一个        │
│                                                             │
│  Policy π_θ(a|s)           LLM 输出的 token 概率             │
│  → 给定状态选动作的概率      → P(xₜ₊₁ | x₁, ..., xₜ)        │
│  → 策略由参数 θ 决定        → θ = LLM 的 7B 个权重           │
│                                                             │
│  Reward R(s,a)             Reward Model 的标量输出           │
│  → 环境给的即时反馈          → RM(回答) → 一个分数            │
│                                                             │
│  Value Function V(s)       PPO 的 Value Model               │
│  → 状态 s 的期望累计奖励     → 一个小网络,输入 hidden_state  │
│                              输出 scalar:这个"局面"有多好   │
│                                                             │
│  Advantage A(s,a)          核心计算                          │
│  A = Q(s,a) - V(s)         A = 实际奖励 - Value 预测        │
│  → 这个动作比"平均水平"      → A > 0:这个 token 比预期好     │
│    好多少                    → A < 0:这个 token 比预期差     │
│                                                             │
│  Policy Gradient            PPO 的 surrogate loss           │
│  ∇J = E[∇log π·A]          L^CLIP = -min(ratio·A,          │
│  → 让"好动作"概率上升                   clip(ratio)·A)     │
│     "坏动作"概率下降         ratio = π_new/π_old             │
│                             clip 限制更新幅度(e.g. ±20%)   │
│                                                             │
│  KL 散度 D_KL(P||Q)         KL penalty in RLHF loss         │
│  → 衡量两个分布的距离         → β·KL(π_θ || π_SFT)           │
│  → 防止新策略偏离太远         防止模型为了刷分变成胡言乱语    │
│                                                             │
│  GAE (Generalized           PPO 的标准做法                   │
│  Advantage Estimation)      → 平衡偏差(bias)和方差(variance) │
│                             → λ 参数控制看多远               │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

2.2 RLHF 训练循环——RL 理论全景 ​

┌─────────────────────────────────────────────────────────────┐
│              RLHF 训练一个 PPO 迭代的全流程                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  for each PPO iteration:                                    │
│                                                             │
│  ┌────────────────────────────────────────────────────┐     │
│  │ 1. Rollout(采样)                                  │     │
│  │    prompt → Policy π_θ 生成完整回答(auto-regressive)│     │
│  │    每个 token = 一次 Action,策略 = LLM 的概率分布   │     │
│  │    → 这就是 RL 课上说的"Agent 在环境中采取行动"      │     │
│  └──────────────────────┬─────────────────────────────┘     │
│                         ↓                                   │
│  ┌────────────────────────────────────────────────────┐     │
│  │ 2. Reward(奖励打分)                               │     │
│  │    Reward Model(完整回答) → scalar reward             │     │
│  │    → 这就是 RL 课上说的 R(s,a)——环境给的反馈         │     │
│  │    → Reward Model 本身是 SFT 模型微调来的            │     │
│  └──────────────────────┬─────────────────────────────┘     │
│                         ↓                                   │
│  ┌────────────────────────────────────────────────────┐     │
│  │ 3. GAE(计算 Advantage)                            │     │
│  │    Value Model 预测每个 token 位置的价值 V(sₜ)       │     │
│  │    Aₜ = Rₜ + γ·V(sₜ₊₁) - V(sₜ) + γλ·Aₜ₊₁          │     │
│  │    → 如果 Advantage > 0,这个 token 强化             │     │
│  │    → 如果 Advantage < 0,这个 token 抑制             │     │
│  │    → 这就是 Policy Gradient 的核心——用 A 替代直接的 R│     │
│  └──────────────────────┬─────────────────────────────┘     │
│                         ↓                                   │
│  ┌────────────────────────────────────────────────────┐     │
│  │ 4. Update Policy(PPO Loss + KL 惩罚)              │     │
│  │                                                     │     │
│  │    ratio = π_new(token) / π_old(token)  ← 新旧策略比│     │
│  │                                                     │     │
│  │    L_CLIP = -min(ratio × A,                        │     │
│  │                  clip(ratio, 0.8, 1.2) × A)        │     │
│  │    ↑ clip 限制了单步更新的幅度(PPO 的关键创新)     │     │
│  │                                                     │     │
│  │    L_KL = β × KL(π_new || π_ref)                   │     │
│  │    ↑ 防止新策略偏离 SFT 太远                        │     │
│  │                                                     │     │
│  │    L_total = L_CLIP + L_KL                         │     │
│  │    → loss.backward()  ← 所有 RL 理论最终落地         │     │
│  │    → optimizer.step()                               │     │
│  └──────────────────────┬─────────────────────────────┘     │
│                         ↓                                   │
│  ┌────────────────────────────────────────────────────┐     │
│  │ 5. Update Value Model                               │     │
│  │    L_V = MSE(V(sₜ), R_target)                      │     │
│  │    → 让 Value Model 更好地预测"这个局面的期望奖励"   │     │
│  └────────────────────────────────────────────────────┘     │
│                                                             │
│  4 个模型同时加载在 GPU 上:                                  │
│  • Policy (Actor)    ← 正在训练(就是 LLM 本身)            │
│  • Reference (SFT)   ← 冻结,只用于计算 KL                  │
│  • Reward Model      ← 冻结,只用于打分                     │
│  • Value Model       ← 正在训练(预测期望奖励)              │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

2.3 三个训练阶段各自用到什么理论 ​

┌─────────────────────────────────────────────────────────────┐
│         Pre-training / SFT / RLHF 使用的理论对比              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  │               │ Pre-training │ SFT       │ RLHF           │
│  ├───────────────┼─────────────┼──────────┼───────────────┤
│  │ 核心任务       │ 预测下一词   │ 预测下一词 │ 最大化奖励     │
│  │ 损失函数       │ Cross-Entropy│ CE(仅assistant)│ PPO+KL+MSE│
│  │ 优化器         │ AdamW       │ AdamW     │ AdamW/PPO      │
│  │ 学习率         │ 3e-4→1e-5   │ 5e-6~2e-5 │ 1e-6~5e-6     │
│  │ 数据量         │ 万亿 token   │ 千~万条   │ 万~十万条      │
│  │ 训练时长       │ 数周~数月    │ 数小时~天 │ 数天~周        │
│  │ 起点权重       │ 随机初始化   │ 预训练权重 │ SFT 权重       │
│  │ DL 知识        │ 全部 DL 理论 │ 全部 DL   │ DL + RL 全部   │
│  │ RL 知识        │ 不需要       │ 不需要    │ Policy Gradient│
│  │                │              │           │ + PPO + GAE   │
│                                                             │
│  核心洞察:三个阶段的训练代码看起来几乎一样。                  │
│  Pre-training 和 SFT 只差在起点权重和数据量。                 │
│  RLHF 才真正用到强化学习的 Policy Gradient/PPO 那套理论。    │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

第2.5部分:拿到手的到底是什么——从裸代码到完整模型的光谱 ​

前面讲的是"训练时每行代码对应什么理论"。但在你开始训练之前,还有一个更实际的问题:你从别人那里拿到的东西,到底包含什么?能做什么?

这个问题没有唯一答案——取决于发布者给了你什么。

2.5.1 五种"拿到手"的形态 ​

┌─────────────────────────────────────────────────────────────┐
│         你拿到的到底是什么?——五种典型形态                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  形态1:只有论文 + 代码(无权重)                            │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 你拿到:                                             │   │
│  │ • 一篇 arXiv 论文(描述了架构和训练方法)             │   │
│  │ • GitHub 上的 model.py(定义了 nn.Module 架构)      │   │
│  │ • 可能有一个 train.py(训练脚本)                    │   │
│  │                                                     │   │
│  │ 你拿不到:                                           │   │
│  │ • 权重文件(.pt / .safetensors)——没有!             │   │
│  │                                                     │   │
│  │ 你能做什么:                                         │   │
│  │ • 读懂架构,复现论文                                  │   │
│  │ • 用自己的数据从头训练(需要大量 GPU)                │   │
│  │ • 从零开始:随机权重 + 你的数据 + 数周训练            │   │
│  │                                                     │   │
│  │ 举例:大多数学术论文的 GitHub 仓库                    │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  形态2:代码 + 预训练权重(最常见)                          │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 你拿到:                                             │   │
│  │ • config.json(架构参数:几层、多宽)                │   │
│  │ • modeling_xxx.py(模型代码)                        │   │
│  │ • pytorch_model.bin 或 model.safetensors(权重!)   │   │
│  │ • tokenizer.json / tokenizer_config.json            │   │
│  │                                                     │   │
│  │ 这就是 HuggingFace 上 "Model Card" 的典型内容。      │   │
│  │ 模型代码几百 KB,权重文件 14GB(7B 模型)。          │   │
│  │                                                     │   │
│  │ 你能做什么:                                         │   │
│  │ • 直接做推理(问答、生成文本)                        │   │
│  │ • 微调(在自己数据上继续训练几小时)                  │   │
│  │ • 不能:复现预训练过程(没有训练数据和脚本)          │   │
│  │                                                     │   │
│  │ 举例:meta-llama/Llama-2-7b-hf、Qwen/Qwen2-7B       │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  形态3:完整训练发布(代码 + 数据 + 权重 + 训练脚本)        │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 你拿到形态2的一切,外加:                              │   │
│  │ • 训练数据(或数据配方)                              │   │
│  │ • 完整的 train.py 脚本                               │   │
│  │ • 中间 checkpoint(保存了不同训练阶段的状态)         │   │
│  │ • 训练日志 / Wandb 曲线                              │   │
│  │ • 评估脚本和基准测试结果                              │   │
│  │                                                     │   │
│  │ 你能做什么:                                         │   │
│  │ • 完整复现训练过程                                    │   │
│  │ • 从 checkpoint 恢复训练                              │   │
│  │ • 修改训练过程并对比效果                              │   │
│  │                                                     │   │
│  │ 举例:LLaMA-1 论文发布(权+代码)、Pythia、OLMo      │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  形态4:量化权重(用于消费级推理)                            │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 你拿到的是形态2的"压缩版":                           │   │
│  │ • GGUF 文件(llama.cpp 格式)                        │   │
│  │ • 或 GPTQ/AWQ 量化权重                                │   │
│  │ • FP16 14GB → INT4 3.5GB(7B 模型)                  │   │
│  │                                                     │   │
│  │ 你能做什么:                                         │   │
│  │ • 在消费级硬件(RTX 3060 / MacBook)上做推理         │   │
│  │ • 通常不能微调(量化后的模型无法训练)                │   │
│  │                                                     │   │
│  │ 举例:TheBloke 在 HuggingFace 上发布的 GGUF 版本     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  形态5:只有 API(无代码,无权重)                            │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 你拿到:                                             │   │
│  │ • 一个 HTTP API 端点,按 token 计费                  │   │
│  │ • 看不到模型代码、看不到权重、看不到训练细节          │   │
│  │                                                     │   │
│  │ 你能做什么:                                         │   │
│  │ • 调用 API 做推理(问答、生成、工具调用)             │   │
│  │ • 不能微调、不能看内部结构、不能离线使用              │   │
│  │                                                     │   │
│  │ 举例:GPT-4o、Claude Sonnet、Gemini                  │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86

2.5.2 "预训练权重"到底是什么——最常见的误解 ​

很多人以为"下载一个模型"就是下载了一个可执行文件。不是的。

┌─────────────────────────────────────────────────────────────┐
│        HuggingFace 上一个典型模型仓库里到底有什么            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  meta-llama/Llama-2-7b-hf 这个仓库里的文件:                │
│                                                             │
│  ┌──────────────────────────────────────────────────┐       │
│  │ config.json              ← 超参数:hidden_size=4096│      │
│  │                               num_layers=32        │      │
│  │                               num_heads=32         │      │
│  │                          文件大小:~1KB             │      │
│  ├──────────────────────────────────────────────────┤       │
│  │ tokenizer.json           ← 词表:32000 个 token    │      │
│  │ tokenizer_config.json    文件大小:~2MB             │      │
│  ├──────────────────────────────────────────────────┤       │
│  │ model-00001-of-00002     ← 权重文件(7B 参数值)    │      │
│  │   .safetensors              文件大小:~10GB         │      │
│  │ model-00002-of-00002                              │      │
│  │   .safetensors              文件大小:~4GB          │      │
│  ├──────────────────────────────────────────────────┤       │
│  │ generation_config.json   ← 生成参数:temperature   │      │
│  │                          文件大小:~200B            │      │
│  └──────────────────────────────────────────────────┘       │
│                                                             │
│  注意:                                                     │
│  • 没有 modeling_llama.py 文件!                            │
│    → 模型代码在 transformers 库里(pip install 时安装的)    │
│    → from_pretrained() 时 HF 自动匹配 config 到正确的代码   │
│  • 权重是 FP16 的——每个参数 2 字节,7B = 14GB               │
│  • 分片是因为 Git 不支持 >5GB 的单文件                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

2.5.3 一个典型的工作流:从拿到权重到训练出自己的模型 ​

┌─────────────────────────────────────────────────────────────┐
│        研究生/开发者的典型工作流                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  第0天:拿到东西                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ pip install transformers torch peft                 │   │
│  │                                                     │   │
│  │ model = AutoModelForCausalLM.from_pretrained(       │   │
│  │     "meta-llama/Llama-2-7b-hf"                     │   │
│  │ )                                                   │   │
│  │ # ↑ 自动下载 config + (分片的)safetensors 权重       │   │
│  │ # ↑ 根据 config 的 architectures 字段匹配 model 代码 │   │
│  │                                                     │   │
│  │ 你现在有了可以推理的模型。但它只会续写文本。          │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  第1-3天:构造训练数据                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 收集/构造 500-5000 条 instruction-output 对          │   │
│  │ 格式化成 JSON:                                      │   │
│  │ {"instruction": "翻译:今天天气很好",                 │   │
│  │  "output": "The weather is nice today."}            │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  第4天:LoRA 微调                                           │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ lora_config = LoraConfig(r=16, ...)                 │   │
│  │ model = get_peft_model(base_model, lora_config)     │   │
│  │ trainer = Trainer(model, train_dataset, ...)        │   │
│  │ trainer.train()                                     │   │
│  │ model.save_pretrained("./my-lora-adapter")          │   │
│  │                                                     │   │
│  │ 产出:adapter_model.safetensors (~10MB)             │   │
│  │ 你没有修改原始的 14GB 权重                           │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  第5天:评估 + 发布                                          │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 在 test set 上跑评估,写论文/报告                     │   │
│  │ 发布你的 LoRA adapter 到 HuggingFace                 │   │
│  │ 其他人:下载 base model (14GB) + 你的 adapter (10MB) │   │
│  │         = 你的微调模型                                │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

2.5.4 "拿来即用"的能力光谱 ​

┌─────────────────────────────────────────────────────────────┐
│      拿到的东西 → 你能做的事 → 需要什么资源                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  只有 API (GPT-4/Claude)                                    │
│    → 问答、生成、调用工具                                    │
│    → 需要:网络 + API Key                                   │
│                                                             │
│  + 代码 + 权重 (HuggingFace 开源模型)                        │
│    → 以上 + 推理 + 微调 + 研究内部结构                       │
│    → 需要:GPU(推理 4-16GB,微调 16-80GB)                  │
│                                                             │
│  + 量化权重 (GGUF)                                           │
│    → 以上 + 在笔记本/手机上运行                               │
│    → 需要:消费级 GPU 或 Apple Silicon                       │
│                                                             │
│  + 训练脚本 + 数据 (完整发布)                                 │
│    → 以上 + 完全复现 + 修改训练过程                          │
│    → 需要:GPU 集群(数十到数百张卡)                         │
│                                                             │
│  每多拿到一层,你能做的事多一层,需要的资源也大一个数量级。   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

第3部分:物理实体——代码、权重、数据 ​

3.1 三类文件,三种角色 ​

┌─────────────────────────────────────────────────────────────┐
│              一个 LLM 项目的三类物理文件                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 模型代码(.py 文件)                                     │
│     ┌──────────────────────────────────────────────────┐    │
│     │ 定义了:架构——多少层、多少头、hidden_dim 多大     │    │
│     │ 没有定义:具体的权重数值                          │    │
│     │ → 建筑的蓝图(几层、每层多大)                    │    │
│     └──────────────────────────────────────────────────┘    │
│                                                             │
│  2. 权重文件(.safetensors / .pt)                          │
│     ┌──────────────────────────────────────────────────┐    │
│     │ 一个巨大的字典,300+ 个 tensor:                  │    │
│     │ {"model.layers.0.self_attn.q_proj.weight":       │    │
│     │      Tensor([4096, 4096], dtype=float16), ...}   │    │
│     │ → 建筑的实际材料(每块砖的具体位置)               │    │
│     └──────────────────────────────────────────────────┘    │
│                                                             │
│  3. 训练脚本(train.py)                                     │
│     ┌──────────────────────────────────────────────────┐    │
│     │ 组合 1+2+数据,循环 forward→loss→backward→update │    │
│     │ 产出新的权重文件                                  │    │
│     │ → 施工队(把建材按蓝图组装起来)                   │    │
│     └──────────────────────────────────────────────────┘    │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

3.2 打开权重文件看看 ​

python
from safetensors import safe_open

with safe_open("model-00001-of-00002.safetensors", framework="pt") as f:
    for key in f.keys():
        tensor = f.get_tensor(key)
        print(f"{key}: {tensor.shape}, dtype={tensor.dtype}")
        # 打印第一个元素
        print(f"  first value: {tensor[0,0].item():.6f}")
1
2
3
4
5
6
7
8

输出(LLaMA-7B 有约 300 个这样的 tensor):

model.embed_tokens.weight: (32000, 4096), dtype=float16
  first value: 0.003418
model.layers.0.input_layernorm.weight: (4096,), dtype=float16
  first value: 1.023438
model.layers.0.self_attn.q_proj.weight: (4096, 4096), dtype=float16
  first value: -0.014526  ← 这就是 08 那篇手算的 Wq 里(0,0)位置的值
model.layers.0.self_attn.k_proj.weight: (4096, 4096), dtype=float16
model.layers.0.self_attn.v_proj.weight: (4096, 4096), dtype=float16
model.layers.0.self_attn.o_proj.weight: (4096, 4096), dtype=float16
model.layers.0.mlp.gate_proj.weight: (11008, 4096), dtype=float16
... (重复 32 层)
lm_head.weight: (32000, 4096), dtype=float16
1
2
3
4
5
6
7
8
9
10
11
12

7B 参数 × 2 字节 (FP16) ≈ 14GB。训练时如果用 FP32 + Adam 优化器,一个 checkpoint 就要 140GB+。

3.3 加载模型 = 创建架构 + 填入权重 ​

python
from transformers import AutoModelForCausalLM

# from_pretrained 做了三件事:
# 1. 读 config.json → 确定架构(几层、多宽、多少头)
# 2. 创建 nn.Module 对象(此时权重随机)
# 3. 加载 .safetensors 覆盖随机权重 → 模型可用

model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf")
1
2
3
4
5
6
7
8

第4部分:微调——三种场景,三种策略 ​

┌─────────────────────────────────────────────────────────────┐
│              微调的三种方式                                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Full Fine-Tuning       LoRA                  QLoRA         │
│  ───────────────       ─────                 ──────         │
│  修改所有参数           只训增量矩阵          量化+增量矩阵    │
│                                                             │
│  7B: ~14GB 权重         7B: ~10MB adapter     7B: ~10MB     │
│  +Adam: ~100GB 显存     +Adam: ~16GB 显存     +Adam: ~8GB  │
│  需要 A100              RTX 4090 即可         RTX 3060 即可 │
│                                                             │
│  科研用不起 →           实验室标配 →           消费级显卡     │
│                                                             │
│  LoRA 原理:不修改原始 W,在旁边加可训练的 A 和 B             │
│  h = W·x + α/r · B·A·x     (A: 4096×r, B: r×4096)          │
│     ↑ 冻结                 ↑ r=16 时仅 13万参数              │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

第5部分:完整实验流程 ​

┌─────────────────────────────────────────────────────────────┐
│              一次科研微调的全流程                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 选底座:从 HuggingFace 下载 LLaMA/Mistral/Qwen/DeepSeek │
│  2. 准备数据:写 JSON 文件 {"instruction":..., "output":...} │
│  3. 选方法:显卡够 → Full FT / 不够 → LoRA / 很不够 → QLoRA │
│  4. 写 train.py(200行):forward→loss→backward→step        │
│  5. 训练:几小时到几天,Wandb 监控 loss                       │
│  6. 保存:LoRA adapter(~10MB)或完整权重                     │
│  7. 评测:test set 指标 + 人工评估                            │
│                                                             │
│  你不需要保存 base model——那是从 HF 下载的。                  │
│  你只需要保存你训练的增量部分。                                │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

核心总结 ​

  1. loss.backward() 就是反向传播:PyTorch 的 autograd 自动执行链式法则,算出每个参数的 ∂L/∂W。你不需要手写,但原理完全是你课堂上学的那一套。

  2. optimizer.step() 就是梯度下降:SGD 做 W = W - lr×∂L/∂W,Adam 在此基础上加了动量和自适应学习率。训练 LLM 几乎全用 AdamW。

  3. PPO 的 surrogate loss + KL penalty = RLHF 的核心:RL 课上学的 Policy Gradient、Advantage、GAE 全部在这里用上。Policy = LLM,Reward Model = 环境,Advantage 决定强化还是抑制每个 token。

  4. 三个训练阶段用的理论不同:Pre-training 和 SFT 只需要 DL 理论(CE loss + AdamW + Backprop)。RLHF 才需要 RL 理论(PPO + GAE + KL)。大部分科研微调只做到 SFT,用不到 RL。

  5. 权重文件 = 命名的 tensor 字典:打开一个 .safetensors,里面是 {layer_name: Tensor(shape, dtype)}。模型代码定义了 shape 和如何前向传播,权重文件提供了具体数值。


章节测试 ​

测试1:loss.backward() 在数学上执行了什么操作? ​

A. 计算 loss 的值 B. 用链式法则计算每个参数的 ∂L/∂W C. 更新模型权重 D. 清空梯度

测试2:Adam 优化器相对于 SGD 多了哪两个组件? ​

A. 学习率和 batch size B. 动量(一阶矩)和自适应学习率(二阶矩) C. Dropout 和 Weight Decay D. LayerNorm 和 Residual Connection

测试3:RLHF 训练时,以下哪个是"Agent"的角色? ​

A. Reward Model B. LLM 本身(Policy π_θ) C. 训练数据 D. Value Model

测试4:PPO 为什么比 REINFORCE 更稳定? ​

A. PPO 不需要 Reward Model B. PPO 用 clipped objective 限制策略更新幅度 C. PPO 不需要 Value Model D. PPO 比 REINFORCE 更简单

测试5:Pre-training、SFT、RLHF 三个阶段分别使用哪些损失函数?各自用到什么 DL/RL 理论? ​

测试6:打开一个 .safetensors 文件,你会看到什么数据结构? ​


参考答案 ​

测试1答案 ​

答案:B。loss.backward() 从 loss 开始,沿计算图反向传播,用链式法则自动算出模型每一个参数的梯度 ∂L/∂W,存入 .grad 属性。它不更新参数——那是 optimizer.step() 的工作。

测试2答案 ​

答案:B。Adam 维护两个状态:一阶矩 m(梯度的指数移动平均,提供动量效果)和二阶矩 v(梯度平方的指数移动平均,为每个参数提供自适应学习率)。AdamW 在此基础上把 weight decay 从梯度计算中解耦。

测试3答案 ​

答案:B。LLM 本身 = Policy π_θ = Agent。它"观察"prompt(状态),"选择"每个 token(动作),Reward Model 给它打分(奖励)。Value Model 预测"这个局势期望得多少分"。

测试4答案 ​

答案:B。PPO 的核心创新是 clipped surrogate objective:min(ratio·A, clip(ratio, 1-ε, 1+ε)·A)。当新旧策略差异过大时,clip 会截断梯度,防止单步更新"步子太大"。REINFORCE 没有这个机制。

测试5答案 ​

Pre-training:Cross-Entropy Loss(对所有位置计算)。DL 理论:Forward→Loss→Backward→AdamW update。不需要 RL。 SFT:Cross-Entropy Loss(仅计算 assistant token 位置)。DL 理论同上。不需要 RL。 RLHF:PPO surrogate loss + KL penalty + MSE(Value Model)。DL 理论用于三个模型的 forward/backward,RL 理论用于 Policy Gradient(PPO+GAE+KL)。这是 RL 理论真正被用到的地方。

测试6答案 ​

一个巨大的字典 {name: Tensor},约 300+ 个 key。每个 key 是参数名(如 model.layers.0.self_attn.q_proj.weight),value 是一个 PyTorch Tensor(shape 如 [4096, 4096],dtype 如 float16)。里面存的全部是具体的浮点数值(如 -0.014526)。没有任何架构信息——架构由 config.json 和 modeling_*.py 定义。


相关笔记 ​

  • [[11-training-primer]] — 训练循环的 DL 基础
  • [[08-transformer-by-hand]] — Transformer 手动计算
  • [[12-pretraining-panorama]] — 预训练全景
  • [[15-sft-deep-dive]] — SFT 监督微调
  • [[16-rlhf-deep-dive]] — RLHF 原理
  • [[17-dpo-and-alignment]] — DPO 与对齐

下一步学习 ​

  • [ ] 用 PyTorch 写一个 50 行的训练循环:model = ...; for batch: loss=model(batch); loss.backward(); optimizer.step()
  • [ ] 从 HuggingFace 下载 LLaMA-3-8B,用 LoRA 在自己的数据上跑一次 SFT
  • [ ] 打开一个 .safetensors 文件,打印前 20 个参数名和 shape

学习状态:🟡 开始学习

最后更新于:

Pager
上一篇21. DPO 与对齐方法:从 RLHF 复杂度到直接偏好优化

持续记录,持续成长

Copyright © Tidenflow