MiniMind 学习笔记(十):优化器、学习率和数据设置------训练稳定性的三块基石
大模型的训练不是简单的"用个 Adam 就行了",而是需要对优化器、学习率调度、数据设置等多个方面进行细致调整和理解,才能保证训练的稳定性。这一节是 Pretrain 中最偏实践的部分:从权重初始化、Warmup、Cosine Decay,一路讲到 Adam/AdamW 的构造、Lion/Muon 新方向、数据质量、Scaling Law,以及 batch size / epoch / tokens seen 这套训练计量体系。
文章目录
- [MiniMind 学习笔记(十):优化器、学习率和数据设置------训练稳定性的三块基石](#MiniMind 学习笔记(十):优化器、学习率和数据设置——训练稳定性的三块基石)
-
- 前言:这一节要回答的问题
- 一、Q2:权重初始化为什么会影响训练?
-
- [Q2.1:为什么不用全 0 初始化?](#Q2.1:为什么不用全 0 初始化?)
- [二、Q3:Warmup 是什么?为什么训练初期需要它?](#二、Q3:Warmup 是什么?为什么训练初期需要它?)
- [三、Q4:Learning Rate 调整与 Cosine Decay](#三、Q4:Learning Rate 调整与 Cosine Decay)
-
- [3.1 MiniMind 的 cosine decay 实现](#3.1 MiniMind 的 cosine decay 实现)
- [3.2 Q4.1:常见的学习率调度方式一览](#3.2 Q4.1:常见的学习率调度方式一览)
- [3.3 Q4.2:如何判断学习率是否合适?](#3.3 Q4.2:如何判断学习率是否合适?)
- [四、Q5:Adam 和 AdamW 是如何被构造出来的?](#四、Q5:Adam 和 AdamW 是如何被构造出来的?)
-
- [4.1 Q5.1:GD 族优化器](#4.1 Q5.1:GD 族优化器)
- [4.2 Q5.2:Adam](#4.2 Q5.2:Adam)
- [Q5.2.1: v t v_t vt 是严格意义上的二阶矩吗?](#Q5.2.1: v t v_t vt 是严格意义上的二阶矩吗?)
- [4.3 Q5.3:AdamW](#4.3 Q5.3:AdamW)
- [五、Q6:Weight Decay 是什么?](#五、Q6:Weight Decay 是什么?)
- [六、Q7:Lion 和 Muon 这类新型优化器是什么?](#六、Q7:Lion 和 Muon 这类新型优化器是什么?)
-
- [6.1 Lion](#6.1 Lion)
- [6.2 Muon](#6.2 Muon)
- [七、Q8:Pretrain 数据集需要注意什么?](#七、Q8:Pretrain 数据集需要注意什么?)
-
- Q8.1:模型在训练和推理时能接受多长的句子?
- Q8.2:支持长上下文的模型一般怎么训练?
- [Q8.3:下一个 token 的预测是否受限于最后一个 hidden state?](#Q8.3:下一个 token 的预测是否受限于最后一个 hidden state?)
- [八、Q9:Scaling Law 是什么?它说明了什么?](#八、Q9:Scaling Law 是什么?它说明了什么?)
-
- [8.1 Kaplan (OpenAI, 2020)](#8.1 Kaplan (OpenAI, 2020))
- [8.2 Chinchilla (DeepMind, 2022)](#8.2 Chinchilla (DeepMind, 2022))
- [九、Q10:Batch Size、Epoch 和 Tokens Seen 应该怎么理解?](#九、Q10:Batch Size、Epoch 和 Tokens Seen 应该怎么理解?)
-
- [9.1 Q10.1:Batch size、梯度累积和分布式训练的关系](#9.1 Q10.1:Batch size、梯度累积和分布式训练的关系)
- [9.2 Q10.2:Epoch、tokens seen 和 training compute](#9.2 Q10.2:Epoch、tokens seen 和 training compute)
- [9.3 Q10.3:如何决定 effective batch size?](#9.3 Q10.3:如何决定 effective batch size?)
- [十、Q11:为什么不同实验的 loss 曲线不能随便比较?](#十、Q11:为什么不同实验的 loss 曲线不能随便比较?)
- [十一、Q12:一个 CheckList](#十一、Q12:一个 CheckList)
- 总结
- 参考
前言:这一节要回答的问题
模型训练的最基本更新形式是:
θ t + 1 = θ t − η ∇ θ L ( θ t ) \theta_{t+1} = \theta_t - \eta \nabla_\theta \mathcal{L}(\theta_t) θt+1=θt−η∇θL(θt)
其中 θ t \theta_t θt 是第 t t t 步的模型参数, η \eta η 是学习率, ∇ θ L ( θ t ) \nabla_\theta \mathcal{L}(\theta_t) ∇θL(θt) 是 loss 对参数的梯度。
这个公式看起来很简单,但实际训练中会马上遇到一串问题:
- 参数一开始应该怎么初始化?
- 训练刚开始时梯度不稳定怎么办?(→ Warmup)
- 学习率应该一直不变,还是随着训练变化?(→ LR Schedule)
- 参数会不会变得太大?(→ Weight Decay)
- 梯度更新要不要考虑历史方向和历史尺度?(→ Adam/AdamW)
- 一个 batch 里应该放多少数据?(→ Batch Size)
- 数据集本身是否足够大、足够干净、分布是否合理?(→ 数据设置 / Scaling Law)
这一节按训练流程展开,组织如下:
#mermaid-svg-IIuwzMI5t5rCM4H3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IIuwzMI5t5rCM4H3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IIuwzMI5t5rCM4H3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IIuwzMI5t5rCM4H3 .error-icon{fill:#552222;}#mermaid-svg-IIuwzMI5t5rCM4H3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IIuwzMI5t5rCM4H3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IIuwzMI5t5rCM4H3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IIuwzMI5t5rCM4H3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IIuwzMI5t5rCM4H3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IIuwzMI5t5rCM4H3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IIuwzMI5t5rCM4H3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IIuwzMI5t5rCM4H3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IIuwzMI5t5rCM4H3 .marker.cross{stroke:#333333;}#mermaid-svg-IIuwzMI5t5rCM4H3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IIuwzMI5t5rCM4H3 p{margin:0;}#mermaid-svg-IIuwzMI5t5rCM4H3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-IIuwzMI5t5rCM4H3 .cluster-label text{fill:#333;}#mermaid-svg-IIuwzMI5t5rCM4H3 .cluster-label span{color:#333;}#mermaid-svg-IIuwzMI5t5rCM4H3 .cluster-label span p{background-color:transparent;}#mermaid-svg-IIuwzMI5t5rCM4H3 .label text,#mermaid-svg-IIuwzMI5t5rCM4H3 span{fill:#333;color:#333;}#mermaid-svg-IIuwzMI5t5rCM4H3 .node rect,#mermaid-svg-IIuwzMI5t5rCM4H3 .node circle,#mermaid-svg-IIuwzMI5t5rCM4H3 .node ellipse,#mermaid-svg-IIuwzMI5t5rCM4H3 .node polygon,#mermaid-svg-IIuwzMI5t5rCM4H3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IIuwzMI5t5rCM4H3 .rough-node .label text,#mermaid-svg-IIuwzMI5t5rCM4H3 .node .label text,#mermaid-svg-IIuwzMI5t5rCM4H3 .image-shape .label,#mermaid-svg-IIuwzMI5t5rCM4H3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-IIuwzMI5t5rCM4H3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IIuwzMI5t5rCM4H3 .rough-node .label,#mermaid-svg-IIuwzMI5t5rCM4H3 .node .label,#mermaid-svg-IIuwzMI5t5rCM4H3 .image-shape .label,#mermaid-svg-IIuwzMI5t5rCM4H3 .icon-shape .label{text-align:center;}#mermaid-svg-IIuwzMI5t5rCM4H3 .node.clickable{cursor:pointer;}#mermaid-svg-IIuwzMI5t5rCM4H3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IIuwzMI5t5rCM4H3 .arrowheadPath{fill:#333333;}#mermaid-svg-IIuwzMI5t5rCM4H3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IIuwzMI5t5rCM4H3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IIuwzMI5t5rCM4H3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IIuwzMI5t5rCM4H3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IIuwzMI5t5rCM4H3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IIuwzMI5t5rCM4H3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IIuwzMI5t5rCM4H3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IIuwzMI5t5rCM4H3 .cluster text{fill:#333;}#mermaid-svg-IIuwzMI5t5rCM4H3 .cluster span{color:#333;}#mermaid-svg-IIuwzMI5t5rCM4H3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-IIuwzMI5t5rCM4H3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IIuwzMI5t5rCM4H3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-IIuwzMI5t5rCM4H3 .icon-shape,#mermaid-svg-IIuwzMI5t5rCM4H3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IIuwzMI5t5rCM4H3 .icon-shape p,#mermaid-svg-IIuwzMI5t5rCM4H3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IIuwzMI5t5rCM4H3 .icon-shape .label rect,#mermaid-svg-IIuwzMI5t5rCM4H3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IIuwzMI5t5rCM4H3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IIuwzMI5t5rCM4H3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IIuwzMI5t5rCM4H3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 训练开始
Q2 权重初始化
决定优化起点
Q3 Warmup
初期缓冲
Q4 学习率调度
cosine decay
Q5 优化器
SGD → Adam → AdamW
Q6 Weight Decay
参数衰减
Q7 新方向
Lion / Muon
Q8 数据设置
质量 / 分布 / 长度
Q9 Scaling Law
参数量×数据量×算力
Q10 计量体系
batch / epoch / tokens seen
Q11 实验比较
loss 曲线可比性
Q12 CheckList
一、Q2:权重初始化为什么会影响训练?
权重初始化不是"随便随机一下"。它会影响前向传播时激活值的尺度 ,也会影响反向传播时梯度的尺度:
- 初始化太大 → 激活值和梯度迅速变大,训练容易不稳定;
- 初始化太小 → 信号在层与层之间传播时变得很弱,学习速度变慢。
经典初始化方法的本质都是维持不同层之间的方差稳定:
| 方法 | 公式 | 适用场景 |
|---|---|---|
| 小方差正态 | W i j ∼ N ( 0 , σ 2 ) W_{ij} \sim \mathcal{N}(0, \sigma^2) Wij∼N(0,σ2),如 σ = 0.02 \sigma=0.02 σ=0.02 | Transformer/LLM 常用,embedding、attention/MLP projection |
| Xavier normal | W i j ∼ N ( 0 , 2 d i n + d o u t ) W_{ij} \sim \mathcal{N}\left(0, \frac{2}{d_{in}+d_{out}}\right) Wij∼N(0,din+dout2) | tanh、sigmoid 等对称激活;同时看 fan_in/fan_out |
| Kaiming normal | W i j ∼ N ( 0 , 2 d i n ) W_{ij} \sim \mathcal{N}\left(0, \frac{2}{d_{in}}\right) Wij∼N(0,din2) | ReLU、LeakyReLU、GELU 等截断型激活 |
在 LLM 里情况更复杂:Transformer 大量使用残差连接、LayerNorm、Attention 和 MLP,很多初始化策略还会考虑模型深度------例如对残差分支相关参数使用更小的初始化尺度,避免多层残差叠加后激活值过大。
有些参数的初始化规则不同:
- bias 通常初始化为 0;
- LayerNorm 的 scale/weight 初始化为 1,bias 为 0;
- embedding 矩阵用正态分布初始化,但角色和普通线性层不完全一样;
- RoPE 这类位置编码缓存不是可训练参数,不涉及初始化。
一个好的初始化不保证训练成功,但一个不合适的初始化很容易让训练一开始就出问题。MiniMind 里这部分封装在模型定义中,训练脚本里不显眼。
Q2.1:为什么不用全 0 初始化?
如果方差已经很小了,为什么不直接初始化为全 0?关键区别在于------对称性无法打破。
假设一层里有多个神经元 h i = f ( W i x + b i ) h_i = f(W_i x + b_i) hi=f(Wix+bi)。如果所有 W i W_i Wi 都是 0,所有 b i b_i bi 也一样,那么这些神经元一开始的输出完全一样;反向传播时收到的梯度也完全一样,于是更新后仍然一样------这一层里虽然有很多神经元,但行为上像是同一个神经元的复制品,模型容量被浪费了。
小方差随机初始化 W i j ∼ N ( 0 , σ 2 ) W_{ij} \sim \mathcal{N}(0, \sigma^2) Wij∼N(0,σ2) 是一种折中:
- 随机:打破对称性;
- 均值接近 0:避免整体偏移;
- 方差较小:避免一开始数值爆炸。
bias 倒是可以初始化为 0,因为只要权重已经随机,神经元之间就已经被区分开了。
二、Q3:Warmup 是什么?为什么训练初期需要它?
Warmup 指的是:训练刚开始时,不直接使用目标学习率,而是先从一个很小的学习率逐步升高到目标学习率。
最常见的线性 warmup (从 0 升到 η m a x \eta_{max} ηmax):
η t = η m a x ⋅ t T w a r m u p \eta_t = \eta_{max} \cdot \frac{t}{T_{warmup}} ηt=ηmax⋅Twarmupt
更一般的形式(从 η m i n \eta_{min} ηmin 升到 η m a x \eta_{max} ηmax,当 η m i n = 0 \eta_{min}=0 ηmin=0 时退化为上式):
η t = η m i n + ( η m a x − η m i n ) ⋅ t T w a r m u p \eta_t = \eta_{min} + (\eta_{max} - \eta_{min}) \cdot \frac{t}{T_{warmup}} ηt=ηmin+(ηmax−ηmin)⋅Twarmupt
为什么需要它? 训练刚开始时,模型参数还很随机,激活值和梯度的尺度可能不稳定。如果一开始就用较大的学习率,参数更新可能过猛,导致 loss 剧烈震荡,甚至直接出现 NaN。Warmup 相当于给训练一个缓冲期:先小步走,等模型进入相对稳定的区域后,再使用正常学习率。
实际训练里,warmup 后面通常会接一个衰减策略(比如 cosine decay),完整调度是两段:前面 warmup,后面 decay。
MiniMind 当前的 pretrain 脚本没有单独实现 warmup,但大模型训练里 warmup 非常常见。是否需要 warmup,通常和模型规模、batch size、初始化方式、优化器和学习率大小有关。
三、Q4:Learning Rate 调整与 Cosine Decay
学习率决定每一步参数更新走多远:
- 太大:更新越过较好的区域,loss 震荡甚至发散;
- 太小:训练稳定但下降太慢,有限时间内学不到足够的东西。
3.1 MiniMind 的 cosine decay 实现
python
# src/minimind_learning/trainer/trainer_utils.py
def get_lr(current_step, total_steps, lr):
return lr / 10 + 0.5 * lr * (1 + math.cos(math.pi * current_step / total_steps))
# src/minimind_learning/trainer/train_pretrain.py
lr = get_lr(epoch * iters + step, args.epochs * iters, args.learning_rate)
for param_group in optimizer.param_groups:
param_group['lr'] = lr
用公式写出来:
η t = η 0 10 + 1 2 η 0 ( 1 + cos ( π t T ) ) \eta_t = \frac{\eta_0}{10} + \frac{1}{2}\eta_0\left(1 + \cos\left(\frac{\pi t}{T}\right)\right) ηt=10η0+21η0(1+cos(Tπt))
直觉:训练前期学习率较高,帮助模型快速学习;训练后期逐渐降低,让参数更新变得更细。
小细节:MiniMind 这个实现不是从 η 0 \eta_0 η0 严格衰减到 0,而是从大约 1.1 η 0 1.1\eta_0 1.1η0 衰减到 0.1 η 0 0.1\eta_0 0.1η0。不影响理解,但读代码时需要注意。
3.2 Q4.1:常见的学习率调度方式一览
| 方式 | 公式 | 特点 |
|---|---|---|
| Constant | η t = η 0 \eta_t = \eta_0 ηt=η0 | 最简单,大模型 pretrain 不够灵活 |
| Step decay | η 0 ⋅ γ ⌊ t / S ⌋ \eta_0 \cdot \gamma^{\lfloor t/S \rfloor} η0⋅γ⌊t/S⌋ | 阶梯状下降,下降点突兀 |
| Exponential decay | η 0 ⋅ γ t \eta_0 \cdot \gamma^t η0⋅γt | 连续平滑,但 γ 不好调 |
| Linear decay | η m i n + ( η 0 − η m i n ) ( 1 − t / T ) \eta_{min} + (\eta_0 - \eta_{min})(1 - t/T) ηmin+(η0−ηmin)(1−t/T) | 常配 warmup 使用 |
| Cosine decay | η m i n + 1 2 ( η 0 − η m i n ) ( 1 + cos ( π t / T ) ) \eta_{min} + \frac{1}{2}(\eta_0 - \eta_{min})(1 + \cos(\pi t / T)) ηmin+21(η0−ηmin)(1+cos(πt/T)) | 平滑,后期自然小步更新,LLM 最常见 |
| Warmup + decay | 先升后降组合 | 大模型训练的标准形态 |
| ReduceLROnPlateau | 按 val loss 动态降 | 传统 DL 常用;大规模 pretrain 验证成本高,少用 |
一个典型的 warmup + cosine decay 实现:
python
import math
def warmup_cosine_lr(step, total_steps, warmup_steps, lr_max, lr_min=0.0):
if step < warmup_steps:
return lr_max * step / warmup_steps
progress = (step - warmup_steps) / (total_steps - warmup_steps)
progress = min(max(progress, 0.0), 1.0)
return lr_min + 0.5 * (lr_max - lr_min) * (1 + math.cos(math.pi * progress))
#mermaid-svg-CnrRy69YMbAmrRgg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CnrRy69YMbAmrRgg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CnrRy69YMbAmrRgg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CnrRy69YMbAmrRgg .error-icon{fill:#552222;}#mermaid-svg-CnrRy69YMbAmrRgg .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CnrRy69YMbAmrRgg .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CnrRy69YMbAmrRgg .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CnrRy69YMbAmrRgg .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CnrRy69YMbAmrRgg .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CnrRy69YMbAmrRgg .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CnrRy69YMbAmrRgg .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CnrRy69YMbAmrRgg .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CnrRy69YMbAmrRgg .marker.cross{stroke:#333333;}#mermaid-svg-CnrRy69YMbAmrRgg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CnrRy69YMbAmrRgg p{margin:0;}#mermaid-svg-CnrRy69YMbAmrRgg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CnrRy69YMbAmrRgg .cluster-label text{fill:#333;}#mermaid-svg-CnrRy69YMbAmrRgg .cluster-label span{color:#333;}#mermaid-svg-CnrRy69YMbAmrRgg .cluster-label span p{background-color:transparent;}#mermaid-svg-CnrRy69YMbAmrRgg .label text,#mermaid-svg-CnrRy69YMbAmrRgg span{fill:#333;color:#333;}#mermaid-svg-CnrRy69YMbAmrRgg .node rect,#mermaid-svg-CnrRy69YMbAmrRgg .node circle,#mermaid-svg-CnrRy69YMbAmrRgg .node ellipse,#mermaid-svg-CnrRy69YMbAmrRgg .node polygon,#mermaid-svg-CnrRy69YMbAmrRgg .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CnrRy69YMbAmrRgg .rough-node .label text,#mermaid-svg-CnrRy69YMbAmrRgg .node .label text,#mermaid-svg-CnrRy69YMbAmrRgg .image-shape .label,#mermaid-svg-CnrRy69YMbAmrRgg .icon-shape .label{text-anchor:middle;}#mermaid-svg-CnrRy69YMbAmrRgg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CnrRy69YMbAmrRgg .rough-node .label,#mermaid-svg-CnrRy69YMbAmrRgg .node .label,#mermaid-svg-CnrRy69YMbAmrRgg .image-shape .label,#mermaid-svg-CnrRy69YMbAmrRgg .icon-shape .label{text-align:center;}#mermaid-svg-CnrRy69YMbAmrRgg .node.clickable{cursor:pointer;}#mermaid-svg-CnrRy69YMbAmrRgg .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CnrRy69YMbAmrRgg .arrowheadPath{fill:#333333;}#mermaid-svg-CnrRy69YMbAmrRgg .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CnrRy69YMbAmrRgg .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CnrRy69YMbAmrRgg .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CnrRy69YMbAmrRgg .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CnrRy69YMbAmrRgg .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CnrRy69YMbAmrRgg .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CnrRy69YMbAmrRgg .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CnrRy69YMbAmrRgg .cluster text{fill:#333;}#mermaid-svg-CnrRy69YMbAmrRgg .cluster span{color:#333;}#mermaid-svg-CnrRy69YMbAmrRgg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CnrRy69YMbAmrRgg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CnrRy69YMbAmrRgg rect.text{fill:none;stroke-width:0;}#mermaid-svg-CnrRy69YMbAmrRgg .icon-shape,#mermaid-svg-CnrRy69YMbAmrRgg .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CnrRy69YMbAmrRgg .icon-shape p,#mermaid-svg-CnrRy69YMbAmrRgg .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CnrRy69YMbAmrRgg .icon-shape .label rect,#mermaid-svg-CnrRy69YMbAmrRgg .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CnrRy69YMbAmrRgg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CnrRy69YMbAmrRgg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CnrRy69YMbAmrRgg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 训练开始
Warmup 阶段
η 从 0/η_min 线性升到 η_max
(参数还很随机, 先小步走)
高学习率阶段
快速下降
Cosine/Linear Decay
平滑衰减
训练后期
小步精修, 稳定收敛
3.3 Q4.2:如何判断学习率是否合适?
最直接的观察对象是 loss 曲线,但loss 曲线只能给信号,不能单独证明某个学习率是最优的:
| 学习率太大 | 学习率太小 |
|---|---|
| loss 一开始不下降,或下降一点后剧烈震荡 | loss 很稳定,但下降非常慢 |
| 曲线有很多尖峰,甚至越来越高 | 长时间几乎没有改善 |
出现 NaN / Inf |
单位时间/单位 token 学习效率很低 |
| 同配置多次跑差异很大 | 提大 LR 后 loss 仍稳定下降且更快 → 原来偏小 |
| grad norm 出现明显尖峰 |
比较合适的学习率:warmup 后 loss 较快下行,曲线允许 batch-level 抖动,但整体趋势稳定下降,没有频繁大尖峰,更不会 NaN。
一个常用的低成本方法是 LR range test------不是严格控制变量实验,而是预实验,粗略测试当前训练系统能承受多大的学习率:
#mermaid-svg-wDbBe0Rb7cGQ7Tjj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .error-icon{fill:#552222;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .marker.cross{stroke:#333333;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj p{margin:0;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .cluster-label text{fill:#333;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .cluster-label span{color:#333;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .cluster-label span p{background-color:transparent;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .label text,#mermaid-svg-wDbBe0Rb7cGQ7Tjj span{fill:#333;color:#333;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .node rect,#mermaid-svg-wDbBe0Rb7cGQ7Tjj .node circle,#mermaid-svg-wDbBe0Rb7cGQ7Tjj .node ellipse,#mermaid-svg-wDbBe0Rb7cGQ7Tjj .node polygon,#mermaid-svg-wDbBe0Rb7cGQ7Tjj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .rough-node .label text,#mermaid-svg-wDbBe0Rb7cGQ7Tjj .node .label text,#mermaid-svg-wDbBe0Rb7cGQ7Tjj .image-shape .label,#mermaid-svg-wDbBe0Rb7cGQ7Tjj .icon-shape .label{text-anchor:middle;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .rough-node .label,#mermaid-svg-wDbBe0Rb7cGQ7Tjj .node .label,#mermaid-svg-wDbBe0Rb7cGQ7Tjj .image-shape .label,#mermaid-svg-wDbBe0Rb7cGQ7Tjj .icon-shape .label{text-align:center;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .node.clickable{cursor:pointer;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .arrowheadPath{fill:#333333;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .cluster text{fill:#333;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .cluster span{color:#333;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj rect.text{fill:none;stroke-width:0;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .icon-shape,#mermaid-svg-wDbBe0Rb7cGQ7Tjj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .icon-shape p,#mermaid-svg-wDbBe0Rb7cGQ7Tjj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .icon-shape .label rect,#mermaid-svg-wDbBe0Rb7cGQ7Tjj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wDbBe0Rb7cGQ7Tjj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 从很小的 LR 开始
(1e-7 ~ 1e-6)
每个 step 按指数增大 LR
ηₜ = η_min(η_max/η_min)^(t/T)
记录每步 smoothed loss
loss 明显发散/震荡/NaN → 停止
选 '开始快速下降之后、
明显发散之前' 的区间作为候选
再从同一 checkpoint
开 2~4 个短跑实验验证
为什么说它不是严格实验?因为模型每一步都在更新,不同学习率对应的 loss 并不是在同一个模型状态下测出来的。严格做法应该从同一 checkpoint 出发、多个 LR 分别训练一小段再比较,但资源消耗高得多。
所以 LR range test 主要回答的不是"最优学习率是多少",而是:学习率太小时 loss 是不是几乎不动?进入哪个区间后 loss 开始明显下降?大到什么程度后开始震荡/上升/NaN?正式训练应该避开哪些危险区域? 注意:模型、数据、batch size、optimizer、混合精度任一变化,这个区间也会变。
四、Q5:Adam 和 AdamW 是如何被构造出来的?
4.1 Q5.1:GD 族优化器
从最基础的 SGD 看起:
θ t + 1 = θ t − η g t \theta_{t+1} = \theta_t - \eta g_t θt+1=θt−ηgt
其中 g t g_t gt 是当前 mini-batch 上估计出来的梯度。
深度学习语境下的 SGD 实际上通常指 mini-batch SGD。本质上都是 Gradient Descent 的一族变种,区别只在用多少样本估计梯度的期望:
| 名称 | 含义 | 对应 |
|---|---|---|
| Batch(全量批次) | 用整个训练集计算一次梯度 | Batch Gradient Descent |
| Mini-batch(小批量) | 用一小部分样本(如 32、64) | Mini-batch SGD(现代默认) |
| SGD(随机) | 用单个样本计算梯度 | Stochastic Gradient Descent |
SGD 有两个问题:
- 当前 batch 的梯度可能噪声很大,更新方向会抖动;
- 所有参数使用同一个学习率,不会根据不同参数的梯度尺度自适应调整。
为了缓解问题 1,加入 momentum ------维护一个速度项 v t v_t vt:
v t = μ v t − 1 + g t , θ t + 1 = θ t − η v t v_t = \mu v_{t-1} + g_t, \qquad \theta_{t+1} = \theta_t - \eta v_t vt=μvt−1+gt,θt+1=θt−ηvt
直觉:momentum 累积过去梯度的方向,让更新方向更平滑,减少 batch 噪声带来的来回震荡。
4.2 Q5.2:Adam
Adam 可以看成在 Momentum SGD 方向上的继续发展:SGD 主要看当前梯度 ,Momentum SGD 看历史梯度方向 ,而 Adam 同时维护梯度的一阶矩和二阶矩。
Adam(Adaptive Moment Estimation)对每个参数维护两类状态:
- 一阶矩 m t m_t mt:梯度的指数滑动平均,"带 momentum 的梯度方向";
- 二阶矩 v t v_t vt:梯度平方的指数滑动平均,"梯度尺度"的估计。
完整更新流程:
m t = β 1 m t − 1 + ( 1 − β 1 ) g t m_t = \beta_1 m_{t-1} + (1-\beta_1) g_t mt=β1mt−1+(1−β1)gt
v t = β 2 v t − 1 + ( 1 − β 2 ) g t 2 v_t = \beta_2 v_{t-1} + (1-\beta_2) g_t^2 vt=β2vt−1+(1−β2)gt2
因为 m 0 m_0 m0、 v 0 v_0 v0 都初始化为 0,训练初期的 m t m_t mt、 v t v_t vt 会偏向 0,所以用 bias correction 修正:
m ^ t = m t 1 − β 1 t , v ^ t = v t 1 − β 2 t \hat{m}_t = \frac{m_t}{1-\beta_1^t}, \qquad \hat{v}_t = \frac{v_t}{1-\beta_2^t} m^t=1−β1tmt,v^t=1−β2tvt
最后更新参数:
θ t = θ t − 1 − η m ^ t v ^ t + ϵ \theta_t = \theta_{t-1} - \eta \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon} θt=θt−1−ηv^t +ϵm^t
常见默认值 β 1 = 0.9 \beta_1 = 0.9 β1=0.9、 β 2 = 0.999 \beta_2 = 0.999 β2=0.999、 ϵ ≈ 10 − 8 \epsilon \approx 10^{-8} ϵ≈10−8(避免除 0)。
#mermaid-svg-iVaosFmMr3r57C6Z{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-iVaosFmMr3r57C6Z .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-iVaosFmMr3r57C6Z .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-iVaosFmMr3r57C6Z .error-icon{fill:#552222;}#mermaid-svg-iVaosFmMr3r57C6Z .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-iVaosFmMr3r57C6Z .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-iVaosFmMr3r57C6Z .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-iVaosFmMr3r57C6Z .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-iVaosFmMr3r57C6Z .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-iVaosFmMr3r57C6Z .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-iVaosFmMr3r57C6Z .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-iVaosFmMr3r57C6Z .marker{fill:#333333;stroke:#333333;}#mermaid-svg-iVaosFmMr3r57C6Z .marker.cross{stroke:#333333;}#mermaid-svg-iVaosFmMr3r57C6Z svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-iVaosFmMr3r57C6Z p{margin:0;}#mermaid-svg-iVaosFmMr3r57C6Z .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-iVaosFmMr3r57C6Z .cluster-label text{fill:#333;}#mermaid-svg-iVaosFmMr3r57C6Z .cluster-label span{color:#333;}#mermaid-svg-iVaosFmMr3r57C6Z .cluster-label span p{background-color:transparent;}#mermaid-svg-iVaosFmMr3r57C6Z .label text,#mermaid-svg-iVaosFmMr3r57C6Z span{fill:#333;color:#333;}#mermaid-svg-iVaosFmMr3r57C6Z .node rect,#mermaid-svg-iVaosFmMr3r57C6Z .node circle,#mermaid-svg-iVaosFmMr3r57C6Z .node ellipse,#mermaid-svg-iVaosFmMr3r57C6Z .node polygon,#mermaid-svg-iVaosFmMr3r57C6Z .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-iVaosFmMr3r57C6Z .rough-node .label text,#mermaid-svg-iVaosFmMr3r57C6Z .node .label text,#mermaid-svg-iVaosFmMr3r57C6Z .image-shape .label,#mermaid-svg-iVaosFmMr3r57C6Z .icon-shape .label{text-anchor:middle;}#mermaid-svg-iVaosFmMr3r57C6Z .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-iVaosFmMr3r57C6Z .rough-node .label,#mermaid-svg-iVaosFmMr3r57C6Z .node .label,#mermaid-svg-iVaosFmMr3r57C6Z .image-shape .label,#mermaid-svg-iVaosFmMr3r57C6Z .icon-shape .label{text-align:center;}#mermaid-svg-iVaosFmMr3r57C6Z .node.clickable{cursor:pointer;}#mermaid-svg-iVaosFmMr3r57C6Z .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-iVaosFmMr3r57C6Z .arrowheadPath{fill:#333333;}#mermaid-svg-iVaosFmMr3r57C6Z .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-iVaosFmMr3r57C6Z .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-iVaosFmMr3r57C6Z .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iVaosFmMr3r57C6Z .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-iVaosFmMr3r57C6Z .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iVaosFmMr3r57C6Z .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-iVaosFmMr3r57C6Z .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-iVaosFmMr3r57C6Z .cluster text{fill:#333;}#mermaid-svg-iVaosFmMr3r57C6Z .cluster span{color:#333;}#mermaid-svg-iVaosFmMr3r57C6Z div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-iVaosFmMr3r57C6Z .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-iVaosFmMr3r57C6Z rect.text{fill:none;stroke-width:0;}#mermaid-svg-iVaosFmMr3r57C6Z .icon-shape,#mermaid-svg-iVaosFmMr3r57C6Z .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iVaosFmMr3r57C6Z .icon-shape p,#mermaid-svg-iVaosFmMr3r57C6Z .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-iVaosFmMr3r57C6Z .icon-shape .label rect,#mermaid-svg-iVaosFmMr3r57C6Z .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iVaosFmMr3r57C6Z .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-iVaosFmMr3r57C6Z .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-iVaosFmMr3r57C6Z :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 梯度 gₜ
一阶矩更新
mₜ = β₁mₜ₋₁ + (1−β₁)gₜ
(方向: 带动量的梯度)
二阶矩更新
vₜ = β₂vₜ₋₁ + (1−β₂)gₜ²
(尺度: 梯度大小)
Bias Correction
m̂ₜ = mₜ/(1−β₁ᵗ), v̂ₜ = vₜ/(1−β₂ᵗ)
更新
θₜ = θₜ₋₁ − η·m̂ₜ/(√v̂ₜ+ε)
这个更新式的直觉:
- m ^ t \hat{m}_t m^t 决定主要更新方向;
- v ^ t \sqrt{\hat{v}_t} v^t 根据历史梯度尺度对更新做归一化;
- 梯度长期较大的参数,更新会被缩小;梯度长期较小的参数,更新相对不会被压得太厉害。
所以 Adam 会根据每个参数自己的梯度历史,自适应地调整更新尺度:梯度波动大的参数更新更谨慎,梯度稳定的参数更新更顺滑。
Adam 的注意点:
- 状态开销比 SGD 大(每个参数额外保存 m t m_t mt 和 v t v_t vt);
- 对学习率仍然敏感,不是用了 Adam 就不需要调学习率;
- 如果直接把 L2 regularization 加进梯度,它会和自适应缩放混在一起------这正是 AdamW 要解决的问题。
Q5.2.1: v t v_t vt 是严格意义上的二阶矩吗?
这个问题很关键,它揭示了 Adam 本质上还是一阶优化算法,而不是严格意义上的二阶优化算法(没有使用二阶 Hessian 矩阵)。
严格说, v t v_t vt 不是完整意义上的二阶矩矩阵,也不是协方差矩阵。它更准确地说是逐元素梯度平方的指数滑动平均 ------对每个参数维度的 second raw moment estimate。 g t , m t , v t ∈ R d g_t, m_t, v_t \in \mathbb{R}^d gt,mt,vt∈Rd,与参数同 shape,更新是逐元素的:
v t = β 2 v t − 1 + ( 1 − β 2 ) ( g t ⊙ g t ) v_t = \beta_2 v_{t-1} + (1-\beta_2)(g_t \odot g_t) vt=β2vt−1+(1−β2)(gt⊙gt)
它不是 g t g t ⊤ g_t g_t^\top gtgt⊤ 这种完整矩阵。完整二阶矩矩阵会包含不同参数维度之间的相关性,计算和存储代价都非常高。Adam 只保留每个参数位置自己的梯度平方历史,是一种对角近似:估计每个参数维度自己的梯度尺度,但不建模维度间的相关性。
4.3 Q5.3:AdamW
AdamW = Adam + decoupled weight decay,即把 weight decay 从 Adam 的梯度自适应更新中解耦出来。
先看普通 Adam 加入 L2 正则的做法------把正则项加进 loss:
L ′ ( θ ) = L ( θ ) + λ 2 ∥ θ ∥ 2 ⇒ g t ′ = g t + λ θ t − 1 \mathcal{L}'(\theta) = \mathcal{L}(\theta) + \frac{\lambda}{2}\|\theta\|^2 \quad \Rightarrow \quad g't = g_t + \lambda\theta{t-1} L′(θ)=L(θ)+2λ∥θ∥2⇒gt′=gt+λθt−1
如果把 g t ′ g't gt′ 送进 Adam,那么 λ θ t − 1 \lambda\theta{t-1} λθt−1 也会进入一阶矩、二阶矩,并被自适应缩放处理------weight decay 的效果会和参数自己的梯度尺度纠缠在一起。
AdamW 的做法:自适应梯度更新仍然只根据 g t g_t gt 计算,然后在参数更新时单独加上 weight decay:
θ t = θ t − 1 − η m ^ t v ^ t + ϵ − η λ θ t − 1 = ( 1 − η λ ) θ t − 1 − η m ^ t v ^ t + ϵ \theta_t = \theta_{t-1} - \eta\frac{\hat{m}t}{\sqrt{\hat{v}t}+\epsilon} - \eta\lambda\theta{t-1} = (1-\eta\lambda)\theta{t-1} - \eta\frac{\hat{m}_t}{\sqrt{\hat{v}_t}+\epsilon} θt=θt−1−ηv^t +ϵm^t−ηλθt−1=(1−ηλ)θt−1−ηv^t +ϵm^t
MiniMind 的 pretrain 代码:
python
# src/minimind_learning/trainer/train_pretrain.py
optimizer = optim.AdamW(model.parameters(), lr=args.learning_rate)
在 Transformer/LLM 训练里 AdamW 是非常常见的默认选择。使用时注意:
- learning rate 仍然需要调,AdamW 不是免调参优化器;
- weight decay 通常不会施加到所有参数------很多设置会排除 bias、LayerNorm weight、embedding 等参数;
- weight decay 和 learning rate 共同决定衰减强度(衰减项里有 η λ \eta\lambda ηλ);
- 状态开销仍然较大(和 Adam 一样要保存 m t m_t mt、 v t v_t vt)。
五、Q6:Weight Decay 是什么?
有一个很容易和 Learning Rate Decay 混淆的概念------Weight Decay。它们名字里都有 decay,但完全是两个东西。Weight decay 来自 L2 正则项:
L ′ ( θ ) = L ( θ ) + λ 2 ∥ θ ∥ 2 ⇒ ∇ θ L ′ = ∇ θ L + λ θ \mathcal{L}'(\theta) = \mathcal{L}(\theta) + \frac{\lambda}{2}\|\theta\|^2 \quad \Rightarrow \quad \nabla_\theta \mathcal{L}' = \nabla_\theta \mathcal{L} + \lambda\theta L′(θ)=L(θ)+2λ∥θ∥2⇒∇θL′=∇θL+λθ
在 SGD 中,更新展开后:
θ t + 1 = θ t − η g t − η λ θ t = ( 1 − η λ ) θ t − η g t \theta_{t+1} = \theta_t - \eta g_t - \eta\lambda\theta_t = (1-\eta\lambda)\theta_t - \eta g_t θt+1=θt−ηgt−ηλθt=(1−ηλ)θt−ηgt
这里的 − η λ θ t -\eta\lambda\theta_t −ηλθt 就是 weight decay 项:每一步都把参数按比例往 0 的方向拉一点,避免参数规模无限变大。
#mermaid-svg-f8HZ7UDF9SbvOAmu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-f8HZ7UDF9SbvOAmu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-f8HZ7UDF9SbvOAmu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-f8HZ7UDF9SbvOAmu .error-icon{fill:#552222;}#mermaid-svg-f8HZ7UDF9SbvOAmu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-f8HZ7UDF9SbvOAmu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-f8HZ7UDF9SbvOAmu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-f8HZ7UDF9SbvOAmu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-f8HZ7UDF9SbvOAmu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-f8HZ7UDF9SbvOAmu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-f8HZ7UDF9SbvOAmu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-f8HZ7UDF9SbvOAmu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-f8HZ7UDF9SbvOAmu .marker.cross{stroke:#333333;}#mermaid-svg-f8HZ7UDF9SbvOAmu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-f8HZ7UDF9SbvOAmu p{margin:0;}#mermaid-svg-f8HZ7UDF9SbvOAmu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-f8HZ7UDF9SbvOAmu .cluster-label text{fill:#333;}#mermaid-svg-f8HZ7UDF9SbvOAmu .cluster-label span{color:#333;}#mermaid-svg-f8HZ7UDF9SbvOAmu .cluster-label span p{background-color:transparent;}#mermaid-svg-f8HZ7UDF9SbvOAmu .label text,#mermaid-svg-f8HZ7UDF9SbvOAmu span{fill:#333;color:#333;}#mermaid-svg-f8HZ7UDF9SbvOAmu .node rect,#mermaid-svg-f8HZ7UDF9SbvOAmu .node circle,#mermaid-svg-f8HZ7UDF9SbvOAmu .node ellipse,#mermaid-svg-f8HZ7UDF9SbvOAmu .node polygon,#mermaid-svg-f8HZ7UDF9SbvOAmu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-f8HZ7UDF9SbvOAmu .rough-node .label text,#mermaid-svg-f8HZ7UDF9SbvOAmu .node .label text,#mermaid-svg-f8HZ7UDF9SbvOAmu .image-shape .label,#mermaid-svg-f8HZ7UDF9SbvOAmu .icon-shape .label{text-anchor:middle;}#mermaid-svg-f8HZ7UDF9SbvOAmu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-f8HZ7UDF9SbvOAmu .rough-node .label,#mermaid-svg-f8HZ7UDF9SbvOAmu .node .label,#mermaid-svg-f8HZ7UDF9SbvOAmu .image-shape .label,#mermaid-svg-f8HZ7UDF9SbvOAmu .icon-shape .label{text-align:center;}#mermaid-svg-f8HZ7UDF9SbvOAmu .node.clickable{cursor:pointer;}#mermaid-svg-f8HZ7UDF9SbvOAmu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-f8HZ7UDF9SbvOAmu .arrowheadPath{fill:#333333;}#mermaid-svg-f8HZ7UDF9SbvOAmu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-f8HZ7UDF9SbvOAmu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-f8HZ7UDF9SbvOAmu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-f8HZ7UDF9SbvOAmu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-f8HZ7UDF9SbvOAmu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-f8HZ7UDF9SbvOAmu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-f8HZ7UDF9SbvOAmu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-f8HZ7UDF9SbvOAmu .cluster text{fill:#333;}#mermaid-svg-f8HZ7UDF9SbvOAmu .cluster span{color:#333;}#mermaid-svg-f8HZ7UDF9SbvOAmu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-f8HZ7UDF9SbvOAmu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-f8HZ7UDF9SbvOAmu rect.text{fill:none;stroke-width:0;}#mermaid-svg-f8HZ7UDF9SbvOAmu .icon-shape,#mermaid-svg-f8HZ7UDF9SbvOAmu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-f8HZ7UDF9SbvOAmu .icon-shape p,#mermaid-svg-f8HZ7UDF9SbvOAmu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-f8HZ7UDF9SbvOAmu .icon-shape .label rect,#mermaid-svg-f8HZ7UDF9SbvOAmu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-f8HZ7UDF9SbvOAmu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-f8HZ7UDF9SbvOAmu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-f8HZ7UDF9SbvOAmu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Adam 中: 两者不再等价
SGD 中: L2 正则 ⟺ weight decay
L2 项加进 loss
梯度多出 λθ
更新含 −ηλθ
(等价, 干净)
L2 项加进 loss
λθ 进入 mₜ, vₜ
衰减被自适应缩放扭曲
(纠缠)
AdamW: 解耦
θ ← θ − η·m̂/(√v̂+ε) − ηλθ
在普通 SGD 中,L2 正则和 weight decay 等价 ;但在 Adam 中,如果直接把 L2 加到 loss 里, λ θ t \lambda\theta_t λθt 会进入自适应缩放过程,参数衰减不再是简单的"按比例往 0 拉"。AdamW 的意义就是解耦这两件事。
关于 λ \lambda λ:通常 weight decay 系数本身是固定超参数 (常见 0.01、0.1 一类),但实际每一步的衰减强度里还有学习率------即使 λ \lambda λ 固定,只要 LR schedule 在变,实际衰减幅度也会跟着变化。
六、Q7:Lion 和 Muon 这类新型优化器是什么?
AdamW 很常见,但不是优化器发展的终点。
6.1 Lion
Lion 来自论文 Symbolic Discovery of Optimization Algorithms,名字是 EvoLved Sign Momentum。
和 Adam 相比,Lion 更轻量:Adam 要保存一阶矩和二阶矩两个状态,Lion 主要保存 momentum,内存开销更小;而且更新使用了 sign operation------更新方向由符号决定,而不是直接使用连续梯度值:
c t = β 1 m t − 1 + ( 1 − β 1 ) g t c_t = \beta_1 m_{t-1} + (1-\beta_1)g_t ct=β1mt−1+(1−β1)gt
θ t = θ t − 1 − η ( sign ( c t ) + λ θ t − 1 ) \theta_t = \theta_{t-1} - \eta\left(\operatorname{sign}(c_t) + \lambda\theta_{t-1}\right) θt=θt−1−η(sign(ct)+λθt−1)
m t = β 2 m t − 1 + ( 1 − β 2 ) g t m_t = \beta_2 m_{t-1} + (1-\beta_2)g_t mt=β2mt−1+(1−β2)gt
常见默认 β ≈ ( 0.9 , 0.99 ) \beta \approx (0.9, 0.99) β≈(0.9,0.99)。
关键区别 :Adam 用 v ^ t \sqrt{\hat{v}_t} v^t 做逐元素尺度归一化,Lion 不维护二阶矩,只用 sign ( c t ) \operatorname{sign}(c_t) sign(ct) 决定每个参数位置的更新方向。
Lion 不是"无脑替换 AdamW":sign update 的更新范数通常更大,Lion 往往需要比 AdamW 更小的学习率 ;为保持 η λ \eta\lambda ηλ 的衰减强度,weight decay 有时也要相应调大。它在不同任务上的收益并不总是稳定。
6.2 Muon
Muon 的主实现来自 KellerJordan/Muon,全称可以理解为 MomentUm Orthogonalized by Newton-Schulz。
核心思想:对神经网络隐藏层中的 2D 权重矩阵,先用 momentum 得到更新方向,再对这个更新矩阵做近似正交化(实现上用 Newton-Schulz iteration 高效近似)。
符号约定: W t W_t Wt 第 t t t 步的 2D 权重矩阵, G t G_t Gt 梯度矩阵, M t M_t Mt momentum 状态, U t U_t Ut 正交化前的候选更新, O t O_t Ot 正交化后的更新矩阵。
更新流程:
M t = μ M t − 1 + G t ( 类似 SGD momentum ) M_t = \mu M_{t-1} + G_t \qquad (\text{类似 SGD momentum}) Mt=μMt−1+Gt(类似 SGD momentum)
U t = μ M t + G t ( Nesterov 形式;不用则 U t = M t ) U_t = \mu M_t + G_t \qquad (\text{Nesterov 形式;不用则 } U_t = M_t) Ut=μMt+Gt(Nesterov 形式;不用则 Ut=Mt)
O t = Ortho ( U t ) O_t = \operatorname{Ortho}(U_t) Ot=Ortho(Ut)
理想情况下,如果 U t = A Σ B ⊤ U_t = A\Sigma B^\top Ut=AΣB⊤(SVD),那么正交化结果为 Ortho ( U t ) = A B ⊤ \operatorname{Ortho}(U_t) = AB^\top Ortho(Ut)=AB⊤------保留更新矩阵的"方向结构",但把奇异值压到接近 1。最后更新参数:
W t = W t − 1 − η O t ( 带 decay: W t = W t − 1 − η ( O t + λ W t − 1 ) ) W_t = W_{t-1} - \eta O_t \qquad (\text{带 decay: } W_t = W_{t-1} - \eta(O_t + \lambda W_{t-1})) Wt=Wt−1−ηOt(带 decay: Wt=Wt−1−η(Ot+λWt−1))
Muon 的使用方式和 AdamW 不完全一样:通常只用于隐藏层里的矩阵参数,embedding、输出层、bias、gain 等参数仍然建议用 AdamW。它更像一种"针对矩阵参数结构的优化器",而 AdamW/Lion 是逐元素更新。
#mermaid-svg-6SdWLi8ijcq6Y16b{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6SdWLi8ijcq6Y16b .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6SdWLi8ijcq6Y16b .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6SdWLi8ijcq6Y16b .error-icon{fill:#552222;}#mermaid-svg-6SdWLi8ijcq6Y16b .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6SdWLi8ijcq6Y16b .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6SdWLi8ijcq6Y16b .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6SdWLi8ijcq6Y16b .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6SdWLi8ijcq6Y16b .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6SdWLi8ijcq6Y16b .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6SdWLi8ijcq6Y16b .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6SdWLi8ijcq6Y16b .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6SdWLi8ijcq6Y16b .marker.cross{stroke:#333333;}#mermaid-svg-6SdWLi8ijcq6Y16b svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6SdWLi8ijcq6Y16b p{margin:0;}#mermaid-svg-6SdWLi8ijcq6Y16b .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6SdWLi8ijcq6Y16b .cluster-label text{fill:#333;}#mermaid-svg-6SdWLi8ijcq6Y16b .cluster-label span{color:#333;}#mermaid-svg-6SdWLi8ijcq6Y16b .cluster-label span p{background-color:transparent;}#mermaid-svg-6SdWLi8ijcq6Y16b .label text,#mermaid-svg-6SdWLi8ijcq6Y16b span{fill:#333;color:#333;}#mermaid-svg-6SdWLi8ijcq6Y16b .node rect,#mermaid-svg-6SdWLi8ijcq6Y16b .node circle,#mermaid-svg-6SdWLi8ijcq6Y16b .node ellipse,#mermaid-svg-6SdWLi8ijcq6Y16b .node polygon,#mermaid-svg-6SdWLi8ijcq6Y16b .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6SdWLi8ijcq6Y16b .rough-node .label text,#mermaid-svg-6SdWLi8ijcq6Y16b .node .label text,#mermaid-svg-6SdWLi8ijcq6Y16b .image-shape .label,#mermaid-svg-6SdWLi8ijcq6Y16b .icon-shape .label{text-anchor:middle;}#mermaid-svg-6SdWLi8ijcq6Y16b .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6SdWLi8ijcq6Y16b .rough-node .label,#mermaid-svg-6SdWLi8ijcq6Y16b .node .label,#mermaid-svg-6SdWLi8ijcq6Y16b .image-shape .label,#mermaid-svg-6SdWLi8ijcq6Y16b .icon-shape .label{text-align:center;}#mermaid-svg-6SdWLi8ijcq6Y16b .node.clickable{cursor:pointer;}#mermaid-svg-6SdWLi8ijcq6Y16b .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6SdWLi8ijcq6Y16b .arrowheadPath{fill:#333333;}#mermaid-svg-6SdWLi8ijcq6Y16b .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6SdWLi8ijcq6Y16b .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6SdWLi8ijcq6Y16b .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6SdWLi8ijcq6Y16b .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6SdWLi8ijcq6Y16b .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6SdWLi8ijcq6Y16b .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6SdWLi8ijcq6Y16b .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6SdWLi8ijcq6Y16b .cluster text{fill:#333;}#mermaid-svg-6SdWLi8ijcq6Y16b .cluster span{color:#333;}#mermaid-svg-6SdWLi8ijcq6Y16b div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6SdWLi8ijcq6Y16b .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6SdWLi8ijcq6Y16b rect.text{fill:none;stroke-width:0;}#mermaid-svg-6SdWLi8ijcq6Y16b .icon-shape,#mermaid-svg-6SdWLi8ijcq6Y16b .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6SdWLi8ijcq6Y16b .icon-shape p,#mermaid-svg-6SdWLi8ijcq6Y16b .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6SdWLi8ijcq6Y16b .icon-shape .label rect,#mermaid-svg-6SdWLi8ijcq6Y16b .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6SdWLi8ijcq6Y16b .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6SdWLi8ijcq6Y16b .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6SdWLi8ijcq6Y16b :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} SGD
只看当前梯度
Momentum SGD
-
历史方向
Adam -
二阶矩(逐元素尺度自适应)
AdamW -
解耦 weight decay
(LLM 默认)
Lion
sign update, 更少状态
Muon
矩阵正交化, 利用参数结构
对这类新优化器的态度:先把它们当作了解方向------优化器设计正在从"通用自适应更新"往"利用参数结构、减少状态开销、提高训练效率"发展。但在 MiniMind 这种学习项目里,AdamW 仍然是最稳定、最容易理解的选择。
七、Q8:Pretrain 数据集需要注意什么?
Pretrain 的本质是拟合数据分布。模型学到什么,很大程度上取决于训练数据长什么样。 至少要关注几件事:
| 关注点 | 影响 |
|---|---|
| 数据质量 | 低质量文本、乱码、重复模板会直接被模型学进去 |
| 数据分布 | 代码/百科/小说/问答/网页的比例不同,模型能力偏向不同方向 |
| 重复数据 | 浪费训练步数,增加记忆风险 |
| 数据污染 | eval 数据混进训练集会让评估失真 |
| 领域覆盖 | 训练数据过窄,其他领域泛化弱 |
MiniMind 读取 JSONL 的 text 字段,随后每条样本被 tokenizer 编码、截断和 padding:
python
encoding = self.tokenizer(
str(sample['text']),
max_length=self.max_length,
padding='max_length',
truncation=True,
return_tensors='pt'
)
max_seq_len 不只是显存参数:它决定了单条样本最多保留多少 token,也影响模型能学习多长范围内的上下文依赖 。较短序列训练更快更省显存,但学不到长文本结构;数据集缺乏长句子时,过长的 max_seq_len 会浪费显存;数据集有很多长文本时,过短又会丢信息。
Q8.1:模型在训练和推理时能接受多长的句子?
受四类因素共同限制:① 模型结构里的最大位置长度;② 训练时见过的序列长度;③ attention 计算和显存成本;④ 位置编码的外推能力。
如果使用传统的 learned absolute position embedding( x t = e t + p t x_t = e_t + p_t xt=et+pt,位置表 P ∈ R L m a x × d P \in \mathbb{R}^{L_{max} \times d} P∈RLmax×d),训练时 L m a x = 1024 L_{max}=1024 Lmax=1024,推理输入超过 1024 就没有 p t p_t pt 可查------上下文长度被位置表硬限制。强行扩展(随机初始化新位置)效果通常不可靠,因为模型没学过这些新位置。
后来很多 LLM 使用 RoPE、ALiBi 等相对位置编码。以 RoPE 为例,它不是查固定长度的表,而是根据位置 t t t 对 query 和 key 做旋转,理论上 t t t 可以继续变大------但 RoPE 解决的是"位置编码能不能算到更远",没有解决"模型会不会用这么远的信息"。模型学到的 attention 行为和长距离依赖方式,主要来自训练长度范围。
所以训练长度、位置编码、推理长度之间不是简单等号:位置编码决定"能不能表示位置",训练长度决定"模型有没有学过这种长度范围内的行为",推理资源决定"实际能不能跑得动"。
Q8.2:支持长上下文的模型一般怎么训练?
不能只靠推理时把长度硬拉长,常见做法有三类:
- 从预训练阶段就使用长序列 (4k/8k/32k)------但非常贵,标准 attention 复杂度 O ( L 2 ) O(L^2) O(L2);
- 先短上下文预训练,再做长上下文继续训练------最常见路线。先用 2k/4k 训练基础语言能力,再升到 8k/16k/32k,补的是:更长距离的位置泛化、长文档结构、跨段落依赖、长上下文检索能力、注意力在长序列里的稳定性;
- RoPE scaling / position interpolation 后继续训练------通过缩放位置编码把位置分布映射到更长范围,但通常仍需继续训练让模型适应新尺度。
一句话:RoPE scaling 让模型"能算",长上下文训练让模型"会用"。
配套的工程和数据策略:FlashAttention(降显存加速)、gradient checkpointing(减少保存激活)、sequence packing(提高 token 利用率)、document-level 数据(保留真实长文档结构)、长文档 QA/retrieval-style 数据(学习使用远处信息)。
训练显存压力比推理更大:训练要保存 forward 激活、梯度、optimizer state( m t m_t mt、 v t v_t vt)、混合精度额外状态;推理不需要 backward,主要只额外保存 KV cache。同样 32k context,推理可能跑得动,训练贵很多。
因此长上下文能力可以拆成三层:
#mermaid-svg-DLF7rnfzQFMQBZsS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DLF7rnfzQFMQBZsS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DLF7rnfzQFMQBZsS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DLF7rnfzQFMQBZsS .error-icon{fill:#552222;}#mermaid-svg-DLF7rnfzQFMQBZsS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DLF7rnfzQFMQBZsS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DLF7rnfzQFMQBZsS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DLF7rnfzQFMQBZsS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DLF7rnfzQFMQBZsS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DLF7rnfzQFMQBZsS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DLF7rnfzQFMQBZsS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DLF7rnfzQFMQBZsS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DLF7rnfzQFMQBZsS .marker.cross{stroke:#333333;}#mermaid-svg-DLF7rnfzQFMQBZsS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DLF7rnfzQFMQBZsS p{margin:0;}#mermaid-svg-DLF7rnfzQFMQBZsS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DLF7rnfzQFMQBZsS .cluster-label text{fill:#333;}#mermaid-svg-DLF7rnfzQFMQBZsS .cluster-label span{color:#333;}#mermaid-svg-DLF7rnfzQFMQBZsS .cluster-label span p{background-color:transparent;}#mermaid-svg-DLF7rnfzQFMQBZsS .label text,#mermaid-svg-DLF7rnfzQFMQBZsS span{fill:#333;color:#333;}#mermaid-svg-DLF7rnfzQFMQBZsS .node rect,#mermaid-svg-DLF7rnfzQFMQBZsS .node circle,#mermaid-svg-DLF7rnfzQFMQBZsS .node ellipse,#mermaid-svg-DLF7rnfzQFMQBZsS .node polygon,#mermaid-svg-DLF7rnfzQFMQBZsS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DLF7rnfzQFMQBZsS .rough-node .label text,#mermaid-svg-DLF7rnfzQFMQBZsS .node .label text,#mermaid-svg-DLF7rnfzQFMQBZsS .image-shape .label,#mermaid-svg-DLF7rnfzQFMQBZsS .icon-shape .label{text-anchor:middle;}#mermaid-svg-DLF7rnfzQFMQBZsS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DLF7rnfzQFMQBZsS .rough-node .label,#mermaid-svg-DLF7rnfzQFMQBZsS .node .label,#mermaid-svg-DLF7rnfzQFMQBZsS .image-shape .label,#mermaid-svg-DLF7rnfzQFMQBZsS .icon-shape .label{text-align:center;}#mermaid-svg-DLF7rnfzQFMQBZsS .node.clickable{cursor:pointer;}#mermaid-svg-DLF7rnfzQFMQBZsS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DLF7rnfzQFMQBZsS .arrowheadPath{fill:#333333;}#mermaid-svg-DLF7rnfzQFMQBZsS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DLF7rnfzQFMQBZsS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DLF7rnfzQFMQBZsS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DLF7rnfzQFMQBZsS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DLF7rnfzQFMQBZsS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DLF7rnfzQFMQBZsS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DLF7rnfzQFMQBZsS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DLF7rnfzQFMQBZsS .cluster text{fill:#333;}#mermaid-svg-DLF7rnfzQFMQBZsS .cluster span{color:#333;}#mermaid-svg-DLF7rnfzQFMQBZsS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DLF7rnfzQFMQBZsS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DLF7rnfzQFMQBZsS rect.text{fill:none;stroke-width:0;}#mermaid-svg-DLF7rnfzQFMQBZsS .icon-shape,#mermaid-svg-DLF7rnfzQFMQBZsS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DLF7rnfzQFMQBZsS .icon-shape p,#mermaid-svg-DLF7rnfzQFMQBZsS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DLF7rnfzQFMQBZsS .icon-shape .label rect,#mermaid-svg-DLF7rnfzQFMQBZsS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DLF7rnfzQFMQBZsS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DLF7rnfzQFMQBZsS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DLF7rnfzQFMQBZsS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 长上下文能力
① 位置编码是否支持更长位置
(RoPE / ALiBi / scaling)
② 模型是否通过训练
学会使用长距离信息
③ 训练和推理资源
是否支持这么长的序列
能算 ≠ 会用
三层缺一不可
Q8.3:下一个 token 的预测是否受限于最后一个 hidden state?
对于 causal LM,预测下一个 token 时通常确实只使用最后一层、最后一个位置的 hidden state:
logits t + 1 = h t ( L ) W lm + b lm , p ( x t + 1 ∣ x ≤ t ) = softmax ( logits t + 1 ) \text{logits}{t+1} = h_t^{(L)} W{\text{lm}} + b_{\text{lm}}, \qquad p(x_{t+1} \mid x_{\le t}) = \operatorname{softmax}(\text{logits}_{t+1}) logitst+1=ht(L)Wlm+blm,p(xt+1∣x≤t)=softmax(logitst+1)
这个直觉是对的:整个历史上下文中与当前预测有关的信息,最后都需要被压缩到 h t ( L ) ∈ R d m o d e l h_t^{(L)} \in \mathbb{R}^{d_{model}} ht(L)∈Rdmodel 这个固定维度的向量里 ------一种隐含的信息瓶颈。上下文可以变长,attention 可以看到更多 token,但最后参与预测的表示维度不会随上下文长度线性增长。序列非常长时,模型必须学会选择性地保留、压缩和聚合信息。
不过 h t ( L ) h_t^{(L)} ht(L) 并不是一个静态的"全文摘要"------它是每一层 attention 根据当前位置的预测需求逐层构造出来的表示。模型不需要记住前面所有 token 的每个细节,只需要保留对预测下一个 token 有用的信息。
所以长上下文能力并不只是 position embedding 或显存问题:即使结构上能接收很长的输入,模型仍要通过 attention 和 hidden state,把"长上下文里真正有用的部分"压缩成当前预测所需的表示------这个压缩过程做得好不好,也是长上下文能力的重要限制之一。
八、Q9:Scaling Law 是什么?它说明了什么?
Scaling law 讨论的是模型性能如何随模型参数量、训练数据量和计算量变化 。它不是严格的理论定理,而是从大量训练实验里拟合出来的经验规律。
先定义符号: L L L = cross entropy loss, N N N = 模型参数量(Kaplan 里不含 embedding), D D D = 训练 token 数, C C C = 训练 compute(FLOPs)。对 dense Transformer,常用近似:
C ≈ 6 N D C \approx 6ND C≈6ND
8.1 Kaplan (OpenAI, 2020)
Scaling Laws for Neural Language Models 观察到:当其他因素不成为瓶颈时,loss 随 N N N、 D D D、 C C C 呈幂律下降:
L ( N ) = ( N c N ) α N , α N ≈ 0.076 ; L ( D ) = ( D c D ) α D , α D ≈ 0.095 L(N) = \left(\frac{N_c}{N}\right)^{\alpha_N},\ \alpha_N \approx 0.076; \qquad L(D) = \left(\frac{D_c}{D}\right)^{\alpha_D},\ \alpha_D \approx 0.095 L(N)=(NNc)αN, αN≈0.076;L(D)=(DDc)αD, αD≈0.095
L ( N , D ) = ( N c N ) α N α D + D c D α D L(N, D) = \left\\left(\\frac{N_c}{N}\\right)\^{\\frac{\\alpha_N}{\\alpha_D}} + \\frac{D_c}{D}\\right^{\alpha_D} L(N,D)=(NNc)αDαN+DDcαD
模型太小会带来损失,数据太少也会带来损失,最终 loss 是两类限制共同作用的结果。据此得到的 compute-optimal 趋势:
N opt ∝ C 0.73 , D opt ∝ C 0.27 N_{\text{opt}} \propto C^{0.73}, \qquad D_{\text{opt}} \propto C^{0.27} Nopt∝C0.73,Dopt∝C0.27
Kaplan 版本的建议更偏向优先增大模型参数量,训练 token 数增长得相对慢。
8.2 Chinchilla (DeepMind, 2022)
Training Compute-Optimal Large Language Models 重新研究了这个问题:固定 compute 预算 C C C 下,预算应该更多分给模型参数量 N N N,还是训练 token 数 D D D?
参数化 loss 形式:
L ( N , D ) = E + A N α + B D β L(N, D) = E + \frac{A}{N^\alpha} + \frac{B}{D^\beta} L(N,D)=E+NαA+DβB
- E E E:理想生成过程在数据分布上的不可约 loss(数据本身的熵下界);
- A N α \frac{A}{N^\alpha} NαA:模型参数量不够带来的额外 loss;
- B D β \frac{B}{D^\beta} DβB:训练 token 数不够带来的额外 loss。
拟合得到 E = 1.69 , A = 406.4 , B = 410.7 , α = 0.34 , β = 0.28 E=1.69, A=406.4, B=410.7, \alpha=0.34, \beta=0.28 E=1.69,A=406.4,B=410.7,α=0.34,β=0.28。在 6 N D = C 6ND = C 6ND=C 预算约束下优化,得到 compute-optimal frontier:
N opt ( C ) = G ( C 6 ) a , D opt ( C ) = G − 1 ( C 6 ) b N_{\text{opt}}(C) = G\left(\frac{C}{6}\right)^a, \qquad D_{\text{opt}}(C) = G^{-1}\left(\frac{C}{6}\right)^b Nopt(C)=G(6C)a,Dopt(C)=G−1(6C)b
其中 a = β α + β ≈ 0.45 a = \frac{\beta}{\alpha+\beta} \approx 0.45 a=α+ββ≈0.45, b = α α + β ≈ 0.55 b = \frac{\alpha}{\alpha+\beta} \approx 0.55 b=α+βα≈0.55,大致:
N opt ∝ C 0.5 , D opt ∝ C 0.5 N_{\text{opt}} \propto C^{0.5}, \qquad D_{\text{opt}} \propto C^{0.5} Nopt∝C0.5,Dopt∝C0.5
Chinchilla 的结论和 Kaplan 不太一样:compute 增加时,模型参数量和训练 token 数应该接近同比例增长。 这就是常说的 Chinchilla scaling:模型变大时,训练 token 数也应该同步变多。
#mermaid-svg-ca5GmWunjbln8hxm{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ca5GmWunjbln8hxm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ca5GmWunjbln8hxm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ca5GmWunjbln8hxm .error-icon{fill:#552222;}#mermaid-svg-ca5GmWunjbln8hxm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ca5GmWunjbln8hxm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ca5GmWunjbln8hxm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ca5GmWunjbln8hxm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ca5GmWunjbln8hxm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ca5GmWunjbln8hxm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ca5GmWunjbln8hxm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ca5GmWunjbln8hxm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ca5GmWunjbln8hxm .marker.cross{stroke:#333333;}#mermaid-svg-ca5GmWunjbln8hxm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ca5GmWunjbln8hxm p{margin:0;}#mermaid-svg-ca5GmWunjbln8hxm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ca5GmWunjbln8hxm .cluster-label text{fill:#333;}#mermaid-svg-ca5GmWunjbln8hxm .cluster-label span{color:#333;}#mermaid-svg-ca5GmWunjbln8hxm .cluster-label span p{background-color:transparent;}#mermaid-svg-ca5GmWunjbln8hxm .label text,#mermaid-svg-ca5GmWunjbln8hxm span{fill:#333;color:#333;}#mermaid-svg-ca5GmWunjbln8hxm .node rect,#mermaid-svg-ca5GmWunjbln8hxm .node circle,#mermaid-svg-ca5GmWunjbln8hxm .node ellipse,#mermaid-svg-ca5GmWunjbln8hxm .node polygon,#mermaid-svg-ca5GmWunjbln8hxm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ca5GmWunjbln8hxm .rough-node .label text,#mermaid-svg-ca5GmWunjbln8hxm .node .label text,#mermaid-svg-ca5GmWunjbln8hxm .image-shape .label,#mermaid-svg-ca5GmWunjbln8hxm .icon-shape .label{text-anchor:middle;}#mermaid-svg-ca5GmWunjbln8hxm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ca5GmWunjbln8hxm .rough-node .label,#mermaid-svg-ca5GmWunjbln8hxm .node .label,#mermaid-svg-ca5GmWunjbln8hxm .image-shape .label,#mermaid-svg-ca5GmWunjbln8hxm .icon-shape .label{text-align:center;}#mermaid-svg-ca5GmWunjbln8hxm .node.clickable{cursor:pointer;}#mermaid-svg-ca5GmWunjbln8hxm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ca5GmWunjbln8hxm .arrowheadPath{fill:#333333;}#mermaid-svg-ca5GmWunjbln8hxm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ca5GmWunjbln8hxm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ca5GmWunjbln8hxm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ca5GmWunjbln8hxm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ca5GmWunjbln8hxm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ca5GmWunjbln8hxm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ca5GmWunjbln8hxm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ca5GmWunjbln8hxm .cluster text{fill:#333;}#mermaid-svg-ca5GmWunjbln8hxm .cluster span{color:#333;}#mermaid-svg-ca5GmWunjbln8hxm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ca5GmWunjbln8hxm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ca5GmWunjbln8hxm rect.text{fill:none;stroke-width:0;}#mermaid-svg-ca5GmWunjbln8hxm .icon-shape,#mermaid-svg-ca5GmWunjbln8hxm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ca5GmWunjbln8hxm .icon-shape p,#mermaid-svg-ca5GmWunjbln8hxm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ca5GmWunjbln8hxm .icon-shape .label rect,#mermaid-svg-ca5GmWunjbln8hxm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ca5GmWunjbln8hxm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ca5GmWunjbln8hxm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ca5GmWunjbln8hxm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 重新研究后修正
Chinchilla (2022)
N_opt ∝ C^0.5
D_opt ∝ C^0.5
参数量和 token 数
同比例增长
D_seen ≈ 20N
Kaplan (2020)
N_opt ∝ C^0.73
D_opt ∝ C^0.27
优先增大模型参数量
token 增长慢
重要后果: 很多早期大模型
其实是 undertrained 的
(参数大但 token 不够)
这个结论的重要后果:很多早期大模型其实是 undertrained 的------参数量很大但训练 token 不够,没有在给定 compute 下达到最优。Chinchilla 本身就是典型例子:比 Gopher 小很多,但训练 token 更多,相近 compute 下效果更好。
对 MiniMind 这样的学习项目,不需要直接套工业级公式(它们是在特定数据集、模型结构、大规模训练设置下拟合的),但它们给了重要直觉:
- 参数量、数据量、计算量要一起看;
- 只增大模型、不增加训练 token,模型可能 undertrained;
- 只增加数据、不增加模型容量,模型可能吃不下更复杂的分布;
- pretrain 里不能只看 epoch,更应该关注 tokens seen;
- 数据质量和数据规模本身就是 pretrain 的核心变量。
九、Q10:Batch Size、Epoch 和 Tokens Seen 应该怎么理解?
这一节回答的是一个训练计量问题:一次参数更新到底看了多少数据?整个训练一共看了多少 token?这些量和显存、epoch、compute 有什么关系?
9.1 Q10.1:Batch size、梯度累积和分布式训练的关系
代码里的 batch_size 通常不是整个训练系统的总 batch size ,而是每张卡、每次 forward/backward 实际处理的样本数(micro-batch size):
python
parser.add_argument("--batch_size", type=int, default=32)
parser.add_argument("--accumulation_steps", type=int, default=8)
显存不够时最直接的办法是把 batch_size 设小;但 batch 太小,单次梯度估计会嘈杂。于是常用 gradient accumulation :连续做多次 forward/backward,梯度累积在参数上,暂时不执行 optimizer.step();累积指定次数后再统一做一次参数更新。
分布式训练再多一个维度:多张 GPU 各自处理不同数据,通过 all-reduce 把梯度求平均,相当于一起组成更大的 batch。
所以 effective batch size:
B eff = B micro × A × W B_{\text{eff}} = B_{\text{micro}} \times A \times W Beff=Bmicro×A×W
( B micro B_{\text{micro}} Bmicro:每卡 batch size; A A A:accumulation steps; W W W:world size/GPU 数)
MiniMind 默认单卡: B eff = 32 × 8 × 1 = 256 B_{\text{eff}} = 32 \times 8 \times 1 = 256 Beff=32×8×1=256;4 卡则 = 32 × 8 × 4 = 1024 = 32 \times 8 \times 4 = 1024 =32×8×4=1024。
如果每条样本长度是 L L L,每次参数更新处理的 token 数:
T update = B eff × L T_{\text{update}} = B_{\text{eff}} \times L Tupdate=Beff×L
对 LLM 训练来说, T update T_{\text{update}} Tupdate 往往比"多少条样本"更直观。
effective batch size 是不是越大越好?不一定。
好处:梯度估计更平滑、训练曲线更稳;多卡吞吐更高;每次更新看的数据更多、噪声更小。
代价:总 token 固定时,batch 越大参数更新次数越少;batch 变大后 LR、warmup、weight decay 往往都要重调;batch 太大梯度噪声太小,泛化不一定更好;继续增大到某个程度后收益明显变小。
所以 batch size 是在显存、吞吐、训练稳定性、更新次数之间做平衡,而不是单纯追求越大越好。
9.2 Q10.2:Epoch、tokens seen 和 training compute
Epoch 表示完整遍历训练集的次数。传统深度学习里数据集不大,经常训练几十上百个 epoch;但 LLM pretrain 的数据集大到完整训练一个 epoch 就已经需要巨大的计算量 ,所以大家更常用的计量单位是 tokens seen------训练过程中模型实际处理过的 token 总数。
D seen = E ⋅ D dataset , C ≈ 6 N D seen D_{\text{seen}} = E \cdot D_{\text{dataset}}, \qquad C \approx 6ND_{\text{seen}} Dseen=E⋅Ddataset,C≈6NDseen
数据集固定时 C ≈ 6 N E D dataset C \approx 6NE D_{\text{dataset}} C≈6NEDdataset:模型大小和数据集大小固定,训练更多 epoch 线性增加 compute。反向可以估算最多训多少 epoch: E ≈ C 6 N D dataset E \approx \frac{C}{6ND_{\text{dataset}}} E≈6NDdatasetC。
如果不知道 compute 是多少,Chinchilla 论文的一个经验值是总 token 数约为模型参数量的 20 倍:
D seen ≈ 20 N ⇒ E ≈ 20 N D dataset D_{\text{seen}} \approx 20N \quad \Rightarrow \quad E \approx \frac{20N}{D_{\text{dataset}}} Dseen≈20N⇒E≈Ddataset20N
一个容易混淆的问题:数据有限时,第二遍、第三遍重复同一批数据,算不算"增加数据"?
从训练计数上看,它确实增加了 tokens seen 和 compute;但不等价于增加新的数据多样性。需要区分两个概念:
- D seen D_{\text{seen}} Dseen:训练过程中实际处理过的 token 总数;
- D unique D_{\text{unique}} Dunique:去重后真正不同的 token 或文档规模。
同一批样本看三遍, D seen D_{\text{seen}} Dseen 按三倍计,但 D unique D_{\text{unique}} Dunique 没变。第二、三遍仍能帮助优化(模型继续更新参数),但信息增量通常下降,且可能增加过拟合和记忆风险。
这也是 LLM pretrain 和传统小数据深度学习的重要区别:传统深度学习默认"一个 epoch 太少";LLM pretrain 面对巨大的 token 流,很多时候一个或少数几个 epoch 就已经足够昂贵。相比"把同一批数据刷很多遍",更有价值的往往是扩大高质量、去重后的数据覆盖。
所以对 pretrain,更自然的观察单位是:
- steps:优化器更新了多少次;
- tokens seen:模型总共处理了多少 token;
- unique tokens / unique documents:训练数据本身有多少去重后的信息;
- effective batch size:每次参数更新大约用了多少样本或 token;
- learning rate schedule:这些更新发生在什么学习率下。
9.3 Q10.3:如何决定 effective batch size?
麻烦在于:effective batch size 和 learning rate 是强耦合的 ,都会影响 loss 曲线稳定性,有时会造成相似现象。所以实践里不是先拍脑袋定 batch 再单独定 LR,而是把它们当成一组训练配置一起调。一个比较稳妥的流程:
#mermaid-svg-WW7dGFxCLyhogdJ6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WW7dGFxCLyhogdJ6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WW7dGFxCLyhogdJ6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WW7dGFxCLyhogdJ6 .error-icon{fill:#552222;}#mermaid-svg-WW7dGFxCLyhogdJ6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WW7dGFxCLyhogdJ6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WW7dGFxCLyhogdJ6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WW7dGFxCLyhogdJ6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WW7dGFxCLyhogdJ6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WW7dGFxCLyhogdJ6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WW7dGFxCLyhogdJ6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WW7dGFxCLyhogdJ6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WW7dGFxCLyhogdJ6 .marker.cross{stroke:#333333;}#mermaid-svg-WW7dGFxCLyhogdJ6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WW7dGFxCLyhogdJ6 p{margin:0;}#mermaid-svg-WW7dGFxCLyhogdJ6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-WW7dGFxCLyhogdJ6 .cluster-label text{fill:#333;}#mermaid-svg-WW7dGFxCLyhogdJ6 .cluster-label span{color:#333;}#mermaid-svg-WW7dGFxCLyhogdJ6 .cluster-label span p{background-color:transparent;}#mermaid-svg-WW7dGFxCLyhogdJ6 .label text,#mermaid-svg-WW7dGFxCLyhogdJ6 span{fill:#333;color:#333;}#mermaid-svg-WW7dGFxCLyhogdJ6 .node rect,#mermaid-svg-WW7dGFxCLyhogdJ6 .node circle,#mermaid-svg-WW7dGFxCLyhogdJ6 .node ellipse,#mermaid-svg-WW7dGFxCLyhogdJ6 .node polygon,#mermaid-svg-WW7dGFxCLyhogdJ6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WW7dGFxCLyhogdJ6 .rough-node .label text,#mermaid-svg-WW7dGFxCLyhogdJ6 .node .label text,#mermaid-svg-WW7dGFxCLyhogdJ6 .image-shape .label,#mermaid-svg-WW7dGFxCLyhogdJ6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-WW7dGFxCLyhogdJ6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-WW7dGFxCLyhogdJ6 .rough-node .label,#mermaid-svg-WW7dGFxCLyhogdJ6 .node .label,#mermaid-svg-WW7dGFxCLyhogdJ6 .image-shape .label,#mermaid-svg-WW7dGFxCLyhogdJ6 .icon-shape .label{text-align:center;}#mermaid-svg-WW7dGFxCLyhogdJ6 .node.clickable{cursor:pointer;}#mermaid-svg-WW7dGFxCLyhogdJ6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-WW7dGFxCLyhogdJ6 .arrowheadPath{fill:#333333;}#mermaid-svg-WW7dGFxCLyhogdJ6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-WW7dGFxCLyhogdJ6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-WW7dGFxCLyhogdJ6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WW7dGFxCLyhogdJ6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-WW7dGFxCLyhogdJ6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WW7dGFxCLyhogdJ6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-WW7dGFxCLyhogdJ6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-WW7dGFxCLyhogdJ6 .cluster text{fill:#333;}#mermaid-svg-WW7dGFxCLyhogdJ6 .cluster span{color:#333;}#mermaid-svg-WW7dGFxCLyhogdJ6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-WW7dGFxCLyhogdJ6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-WW7dGFxCLyhogdJ6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-WW7dGFxCLyhogdJ6 .icon-shape,#mermaid-svg-WW7dGFxCLyhogdJ6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WW7dGFxCLyhogdJ6 .icon-shape p,#mermaid-svg-WW7dGFxCLyhogdJ6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-WW7dGFxCLyhogdJ6 .icon-shape .label rect,#mermaid-svg-WW7dGFxCLyhogdJ6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WW7dGFxCLyhogdJ6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-WW7dGFxCLyhogdJ6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-WW7dGFxCLyhogdJ6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 不 work 就回到第二步
重新选 batch 再选 LR
第一步: 显存定 micro-batch
固定模型和 seq_len,
从 1,2,4,8... 往上试到不 OOM
第二步: 梯度累积+多卡
得到候选 B_eff
B_eff = B_micro × A × W
第三步: 在候选 B_eff 上
重新找 LR
η_new ≈ η_old(B_new/B_old)^p, p∈0.5,1
第四步: 用 loss 曲线区分
batch 问题 vs LR 问题
第五步: 按 tokens seen
而不是只按 step 比较实验
第四步的判读是关键,最容易误判的是前两种(都可能表现为 loss 抖动):
| 现象 | 诊断 |
|---|---|
| 单 step loss 抖动明显,但滑动平均仍稳定下降 | batch 太小(有噪声地下降) |
| loss 不只抖动,出现 spike、持续升高甚至 NaN | LR 太大(更新走过头) |
| 曲线很平滑,但按 tokens seen 比较时下降很慢 | batch 大但 LR 偏小(慢慢挪) |
| 前期 loss spike,或 warmup 结束附近突然不稳定 | batch 变大但 LR/warmup 没跟上 |
起步参考(不是理论最优值): L = 512 L=512 L=512 时可试 B eff = 64 ∼ 512 B_{\text{eff}}=64\sim512 Beff=64∼512; L = 1024 L=1024 L=1024 时可试 B eff = 32 ∼ 256 B_{\text{eff}}=32\sim256 Beff=32∼256------对应每次更新约 3.2 × 10 4 ∼ 2.6 × 10 5 3.2\times10^4 \sim 2.6\times10^5 3.2×104∼2.6×105 tokens。更大的训练常把 T update T_{\text{update}} Tupdate 提到 10 5 ∼ 10 6 10^5 \sim 10^6 105∼106 甚至更高,但需要更仔细的 LR、warmup 和稳定性调参。
一句话总结:先由显存确定 micro-batch 的可行范围,再用梯度累积和多卡得到适中的候选 B eff B_{\text{eff}} Beff,然后根据候选 batch 重新调 LR;不 work 就重选 batch 再选 LR。评估时除了 step 级曲线,还要按 tokens seen 的 loss 曲线判断模型更新得是否足够快。loss 抖动不一定只说明 batch 小,也可能是 LR 太大------这两个旋钮必须一起看。
十、Q11:为什么不同实验的 loss 曲线不能随便比较?
比如,一个实验 padding 很多,另一个实验有效 token更多,它们虽然都在算平均 loss,但实际参与统计的 token 分布并不一样。再比如,effective batch size 变大后,loss 曲线可能更平滑,但这不一定代表模型能力更强------它的更新次数、梯度噪声和 LR 适配关系都变了。
训练指标不是孤立数字。每一条 loss 曲线背后,都绑定着一整套数据和训练配置。 比较不同实验时,至少需要检查(大致按重要性排列):
- 数据集是否一致(来源、清洗、去重、过滤、混合比例)
- tokenizer 是否一致(token 数、切分方式、loss 统计单位都受影响)
- tokens seen 是否一致(按训练 token 数比较,而不是只按 step/epoch)
max_seq_len是否一致- packing、padding、truncation 规则是否一致
- loss mask 是否一致(padding/特殊/prompt token 是否参与 loss)
- 模型结构是否一致(参数量、层数、hidden size、heads、位置编码、tie embedding)
- optimizer 是否一致
- learning rate 和 schedule 是否一致
- effective batch size 是否一致
- weight decay、gradient clipping 是否一致
- mixed precision 设置是否一致
- 随机性是否一致(seed、shuffle、dataloader worker)
- eval 设置是否一致
实际训练时可以同时看的曲线:
- train step loss (最原始,噪声最大)/ smoothed train loss(滑动平均,看趋势);
- train loss vs tokens seen(比 vs step 更适合比较不同 batch size 的实验);
- eval loss (固定验证集,判断泛化)/ eval perplexity ( PPL = exp ( L ) \text{PPL} = \exp(L) PPL=exp(L));
- learning rate curve(确认 warmup/decay 按预期执行);
- gradient norm curve(判断梯度爆炸、LR 过大);
- parameter norm / update norm 、loss scale / NaN count(混合精度数值稳定性);
- tokens/sec 或 step time(吞吐,不然可能 loss 降得快但效率很差);
- train loss 和 eval loss 的 gap(train 降 eval 不降 → 过拟合/数据污染/分布不匹配)。
不要只说"第 N step 的 loss 更低"。当 B eff B_{\text{eff}} Beff、
seq_len或accumulation_steps不同时,第 N step 背后对应的 tokens seen 可能完全不同。更稳妥:用 loss vs tokens seen 作为主轴,再结合 eval loss、gradient norm、LR curve 和吞吐一起判断。
十一、Q12:一个 CheckList
整个训练流程可以按如下步骤检查规划:
#mermaid-svg-gbwchcyHkF3DlGql{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gbwchcyHkF3DlGql .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gbwchcyHkF3DlGql .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gbwchcyHkF3DlGql .error-icon{fill:#552222;}#mermaid-svg-gbwchcyHkF3DlGql .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gbwchcyHkF3DlGql .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gbwchcyHkF3DlGql .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gbwchcyHkF3DlGql .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gbwchcyHkF3DlGql .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gbwchcyHkF3DlGql .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gbwchcyHkF3DlGql .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gbwchcyHkF3DlGql .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gbwchcyHkF3DlGql .marker.cross{stroke:#333333;}#mermaid-svg-gbwchcyHkF3DlGql svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gbwchcyHkF3DlGql p{margin:0;}#mermaid-svg-gbwchcyHkF3DlGql .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gbwchcyHkF3DlGql .cluster-label text{fill:#333;}#mermaid-svg-gbwchcyHkF3DlGql .cluster-label span{color:#333;}#mermaid-svg-gbwchcyHkF3DlGql .cluster-label span p{background-color:transparent;}#mermaid-svg-gbwchcyHkF3DlGql .label text,#mermaid-svg-gbwchcyHkF3DlGql span{fill:#333;color:#333;}#mermaid-svg-gbwchcyHkF3DlGql .node rect,#mermaid-svg-gbwchcyHkF3DlGql .node circle,#mermaid-svg-gbwchcyHkF3DlGql .node ellipse,#mermaid-svg-gbwchcyHkF3DlGql .node polygon,#mermaid-svg-gbwchcyHkF3DlGql .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gbwchcyHkF3DlGql .rough-node .label text,#mermaid-svg-gbwchcyHkF3DlGql .node .label text,#mermaid-svg-gbwchcyHkF3DlGql .image-shape .label,#mermaid-svg-gbwchcyHkF3DlGql .icon-shape .label{text-anchor:middle;}#mermaid-svg-gbwchcyHkF3DlGql .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gbwchcyHkF3DlGql .rough-node .label,#mermaid-svg-gbwchcyHkF3DlGql .node .label,#mermaid-svg-gbwchcyHkF3DlGql .image-shape .label,#mermaid-svg-gbwchcyHkF3DlGql .icon-shape .label{text-align:center;}#mermaid-svg-gbwchcyHkF3DlGql .node.clickable{cursor:pointer;}#mermaid-svg-gbwchcyHkF3DlGql .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gbwchcyHkF3DlGql .arrowheadPath{fill:#333333;}#mermaid-svg-gbwchcyHkF3DlGql .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gbwchcyHkF3DlGql .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gbwchcyHkF3DlGql .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gbwchcyHkF3DlGql .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gbwchcyHkF3DlGql .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gbwchcyHkF3DlGql .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gbwchcyHkF3DlGql .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gbwchcyHkF3DlGql .cluster text{fill:#333;}#mermaid-svg-gbwchcyHkF3DlGql .cluster span{color:#333;}#mermaid-svg-gbwchcyHkF3DlGql div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-gbwchcyHkF3DlGql .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gbwchcyHkF3DlGql rect.text{fill:none;stroke-width:0;}#mermaid-svg-gbwchcyHkF3DlGql .icon-shape,#mermaid-svg-gbwchcyHkF3DlGql .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gbwchcyHkF3DlGql .icon-shape p,#mermaid-svg-gbwchcyHkF3DlGql .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gbwchcyHkF3DlGql .icon-shape .label rect,#mermaid-svg-gbwchcyHkF3DlGql .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gbwchcyHkF3DlGql .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gbwchcyHkF3DlGql .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gbwchcyHkF3DlGql :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1. 目标和算力
→ 模型参数量和规模
(layers / hidden / heads / 预算)
2. 目标上下文能力
→ 数据集和序列长度
(max_seq_len / packing / truncation)
3. 模型量×数据规模
→ 估算训练 token 数
(tokens seen / epoch / 重复数据?)
4. optimizer / decay / precision
→ 估算训练状态开销
(显存 / mixed precision / checkpointing)
5. 实际硬件显存
→ batch_size / 累积步数 / 分布式
(B_eff / tokens per update / 吞吐)
6. 稳定性和收敛速度
→ 调 LR / warmup / decay
7. Eval 监控
(loss / eval loss / PPL / grad norm / LR curve / tokens per sec)
8. 实验管理
(完整配置记录, 可复现可比较, 可视化清晰)
总结
这一节从"一行更新公式"出发,把训练稳定性的三块基石铺开了:
| 主题 | 一句话要点 |
|---|---|
| 初始化 | 维持层间方差稳定;全 0 无法打破对称性;不同参数类有不同规则 |
| Warmup | 初期小步走,防 loss 震荡/NaN;MiniMind 未实现但大模型常见 |
| LR Schedule | cosine decay 是主流;LR range test 摸稳定区间;太大=spike/NaN,太小=慢慢挪 |
| Adam | 一阶矩(方向)+二阶矩(尺度)+bias correction; v t v_t vt 是对角近似,本质仍一阶算法 |
| AdamW | decoupled weight decay:衰减不进 m t / v t m_t/v_t mt/vt,更新时单独作用;LLM 默认选择 |
| Lion / Muon | 新方向:sign update 省状态 / 矩阵正交化利用参数结构;学习项目仍首选 AdamW |
| 数据 | 本质是拟合数据分布;质量、分布、重复、污染、覆盖都要管 |
| 长上下文 | 位置编码"能算" ≠ 训练"会用";最后一个 hidden state 是信息瓶颈 |
| Scaling Law | Kaplan 偏增大模型;Chinchilla 参数与 token 同比例( D ≈ 20 N D\approx20N D≈20N);很多早期大模型 undertrained |
| 计量体系 | B eff = B micro × A × W B_{\text{eff}}=B_{\text{micro}}\times A\times W Beff=Bmicro×A×W;tokens seen 比 epoch 更本质; D seen ≠ D unique D_{\text{seen}} \neq D_{\text{unique}} Dseen=Dunique |
| 实验比较 | loss 曲线绑定整套配置;按 tokens seen 比,别只看 step |
下一篇进入训练细节专题:混合精度、梯度缩放、梯度累积与裁剪、checkpoint 与断点恢复------训练中最容易踩坑的工程部分。
参考
- llm-notes-all-in-one: 优化器、学习率和数据设置
- Scaling Laws for Neural Language Models (Kaplan, 2020)
- Training Compute-Optimal Large Language Models (Chinchilla, 2022)
- Symbolic Discovery of Optimization Algorithms (Lion, 2023)
- Muon: An optimizer for hidden layers in neural networks
- Adam: A Method for Stochastic Optimization (2014)
- Decoupled Weight Decay Regularization (AdamW, 2019)