大模型如何"学习":从梯度下降到AdamW优化器
引言:什么是"学习"?
在前面的章节中,我们学习了Transformer的各个组件:注意力机制、MLP、残差连接、LM Head等。但有一个核心问题我们还没有回答:
这些参数( WQ、 W1、 γ、 β、Embedding等)是怎么得到的?
答案是:通过训练(Training)学习得到的!
类比:
想象你在学习投篮:
- 初始状态:随便投,命中率很低(随机初始化)
- 观察结果:投偏了,偏左边10厘米(计算误差)
- 调整姿势:下次往右边调整一点(参数更新)
- 重复练习:不断投篮、观察、调整(迭代训练)
- 最终:命中率很高(模型收敛)
深度学习的训练过程就是这样:通过不断调整参数,让模型的输出越来越接近正确答案。
前向传播(Forward Propagation)
定义
前向传播是指数据从输入层经过各层计算,最终得到输出的过程。
输入计算 输出
Transformer的前向传播
让我们用一个具体例子看完整的前向传播流程:
输入 :"今天天气" 目标:预测下一个词(应该是"很好"、"不错"等)
步骤1:Token Embedding
输入Token IDs:查表:1234,5678X=Etoken\[1234,5678]∈R2×768
步骤2:位置编码
Xpos=X+PE∈R2×768
步骤3:通过第1层Transformer
注意力:MLP:X1′=Xpos+Attention(LN(Xpos))X1=X1′+MLP(LN(X1′))
步骤4:通过第2-12层
X2,X3,...,X12
步骤5:LM Head
logits=X12−1⋅Wlm∈R50257
取最后一个位置的隐藏状态,映射到词表。
步骤6:Softmax
P(wi)=∑jelogitsjelogitsi
得到每个词的概率。
假设输出:
| 词 | 概率 |
|---|---|
| 很好 | 45% |
| 不错 | 30% |
| 真棒 | 15% |
| 差 | 5% |
| ... | ... |
关键点
前向传播的特点:
- 确定性:给定输入和参数,输出是确定的
- 单向性:只能从输入到输出,不能反过来
- 快速:主要是矩阵乘法,GPU加速很快
用途:
- 训练时:计算输出,然后计算Loss
- 推理时:直接用来生成文本
损失函数(Loss Function)
前向传播得到了输出(概率分布),但如何知道这个输出好不好?这就需要损失函数。
什么是损失函数?
**损失函数(Loss Function)**衡量模型输出与真实答案之间的差距:
Loss=f(模型输出,真实答案)
Loss越小,说明模型越好!
交叉熵损失(Cross-Entropy Loss)
对于语言模型,最常用的是交叉熵损失:
L=−logP(正确答案)
直观理解:
- 如果模型给正确答案的概率很高(接近1),则 −logP≈0(损失小)
- 如果模型给正确答案的概率很低(接近0),则 −logP→∞(损失大)
具体例子
输入 :"今天天气" 真实答案:"很好"(Token ID: 9527)
模型输出(前向传播后):
| 词 | 概率 P(w) | −logP(w) |
|---|---|---|
| 很好 | 0.45 | 0.80 |
| 不错 | 0.30 | 1.20 |
| 真棒 | 0.15 | 1.90 |
| 差 | 0.05 | 3.00 |
| ... | ... | ... |
计算Loss:
L=−logP("很好")=−log(0.45)=0.80
如果模型预测得更准(概率0.9):
L=−log(0.9)=0.11(损失更小!)
如果模型预测得很差(概率0.01):
L=−log(0.01)=4.61(损失很大!)
数学形式
更正式地,对于词表大小为 V 的分类问题:
L=−i=1∑Vyilog(pi)
其中:
- yi:真实标签的one-hot编码(正确答案为1,其余为0)
- pi:模型预测的概率
由于 y 是one-hot,只有正确答案那一项不为0,所以简化为:
L=−log(pcorrect)
训练目标
训练的目标就是最小化Loss:
θminE(x,y)∼DataL(fθ(x),y)
其中:
- θ:所有模型参数( WQ、 W1、Embedding等)
- fθ(x):模型的输出
- E:对所有训练数据的期望
通俗理解:找到一组参数,让模型在所有训练样本上的平均Loss最小。
反向传播(Backward Propagation)
知道了Loss,如何调整参数让Loss变小?这就需要反向传播。
核心思想
**梯度(Gradient)**告诉我们:参数往哪个方向调整,Loss会下降最快。
梯度=∂θ∂L
直观理解:
想象你在山上迷雾中,想下山(最小化Loss):
- 梯度:指向上坡最陡的方向
- 负梯度:指向下坡最陡的方向
- 沿着负梯度走:最快下山
链式法则(Chain Rule)
反向传播的数学基础是链式法则:
∂W1∂L=∂y∂L⋅∂h∂y⋅∂W1∂h
从后往前传:
- 计算 ∂y∂L(Loss对输出的梯度)
- 计算 ∂h∂y(输出对中间层的梯度)
- 计算 ∂W1∂h(中间层对参数的梯度)
- 相乘得到 ∂W1∂L(Loss对参数的梯度)
具体例子:简单MLP的反向传播
假设一个简单的MLP:
hhactyL=W1⋅x+b1=ReLU(h)=W2⋅hact+b2=(y−ytrue)2
各步骤说明:
- 第一层线性变换
- ReLU激活函数
- 第二层线性变换(输出)
- 计算损失(均方误差)
前向传播 (假设 x=2, ytrue=5):
hhactyL=3×2+1=7=ReLU(7)=7=2×7+0=14=(14−5)2=81
反向传播:
∂y∂L∂W2∂L∂b2∂L∂hact∂L∂h∂L∂W1∂L∂b1∂L=2(y−ytrue)=2(14−5)=18=∂y∂L⋅∂W2∂y=18×hact=18×7=126=∂y∂L⋅∂b2∂y=18×1=18=∂y∂L⋅∂hact∂y=18×W2=18×2=36=∂hact∂L⋅∂h∂hact=36×1=36=∂h∂L⋅∂W1∂h=36×x=36×2=72=∂h∂L⋅∂b1∂h=36×1=36
注意:ReLU的导数在 h>0 时为1,在 h≤0 时为0。这里 h=7>0,所以导数为1。
得到梯度:
- ∂W1∂L=72
- ∂b1∂L=36
- ∂W2∂L=126
- ∂b2∂L=18
自动微分(Automatic Differentiation)
好消息:我们不需要手动计算梯度!
现代深度学习框架(PyTorch、TensorFlow)会自动计算梯度:
python
import torch
# 定义参数(requires_grad=True表示需要计算梯度)
W1 = torch.tensor([[3.0]], requires_grad=True)
b1 = torch.tensor([1.0], requires_grad=True)
W2 = torch.tensor([[2.0]], requires_grad=True)
b2 = torch.tensor([0.0], requires_grad=True)
# 前向传播
x = torch.tensor([2.0])
y_true = torch.tensor([5.0])
h = W1 @ x + b1
h_act = torch.relu(h)
y = W2 @ h_act + b2
loss = (y - y_true) ** 2
# 反向传播(自动计算梯度)
loss.backward()
# 查看梯度
print(f"∂L/∂W1 = {W1.grad}") # tensor([[72.]])
print(f"∂L/∂b1 = {b1.grad}") # tensor([36.])
print(f"∂L/∂W2 = {W2.grad}") # tensor([[126.]])
print(f"∂L/∂b2 = {b2.grad}") # tensor([18.])
完全自动!我们只需要定义前向传播,反向传播由框架完成。
Transformer的反向传播
对于一个12层的GPT-2模型:
- 前向传播:输入 → Embedding → Layer1 → ... → Layer12 → LM Head → Loss
- 反向传播 :Loss → ∂Wlm∂L → ∂W12∂L → ... → ∂W1∂L → ∂Etoken∂L
计算图:
css
前向:
x → E_token → Layer1 → Layer2 → ... → Layer12 → LM_Head → logits → softmax → loss
↓ ↓ ↓ ↓ ↓ ↓ ↓
反向: ← ← ... ← ← ← ← ←
∂L/∂E ∂L/∂W1 ∂L/∂W12 ∂L/∂Wlm ∂L/∂logits ∂L/∂P ∂L/∂L
每个参数都会得到一个梯度,告诉我们如何更新它。
梯度下降(Gradient Descent)
有了梯度,就可以更新参数了。
基本思想
沿着负梯度方向更新参数:
θnew=θold−η⋅∂θ∂L
其中:
- θ:参数(如 W1、 b1 等)
- η:学习率(Learning Rate)
- ∂θ∂L:梯度
直观理解:
- 梯度正:参数增大会让Loss增大 → 减小参数
- 梯度负:参数增大会让Loss减小 → 增大参数
具体例子
继续上面的MLP例子:
当前参数 : W1=3,梯度 ∂W1∂L=72
选择学习率 : η=0.01
更新参数:
W1new=W1old−η⋅∂W1∂L=3−0.01×72=2.28
同理:
b1newW2newb2new=1−0.01×36=0.64=2−0.01×126=0.74=0−0.01×18=−0.18
验证:用新参数再做一次前向传播,Loss应该变小了。
三种梯度下降
1. Batch Gradient Descent(批量梯度下降)
每次使用所有训练数据计算梯度:
θ=θ−η⋅N1i=1∑N∇Li
优点:
- 梯度准确,收敛稳定
缺点:
- 计算量大(N可能有百万级)
- 内存不够(无法一次加载所有数据)
- 更新慢(每个epoch只更新一次)
2. Stochastic Gradient Descent (SGD,随机梯度下降)
每次只用一个样本计算梯度:
θ=θ−η⋅∇Li
优点:
- 更新快(每个样本都更新)
- 内存友好
缺点:
- 梯度噪声大,不稳定
- 可能震荡,难以收敛到最优点
3. Mini-Batch Gradient Descent(小批量梯度下降)
每次用一小批样本(如32、64、128个)计算梯度:
θ=θ−η⋅B1i=1∑B∇Li
其中 B 是batch size(批量大小)。
优点:
- 平衡了Batch GD和SGD的优缺点
- 梯度相对稳定
- 可以利用GPU并行计算
缺点:
- 需要调整batch size
这是现代深度学习的标准做法!
训练循环
完整的训练过程:
python
for epoch in range(num_epochs): # 遍历多个epoch
for batch in dataloader: # 遍历所有batch
# 1. 前向传播
outputs = model(batch['input'])
loss = loss_fn(outputs, batch['target'])
# 2. 反向传播
loss.backward() # 计算梯度
# 3. 参数更新
optimizer.step() # 更新参数
# 4. 梯度清零(为下一个batch准备)
optimizer.zero_grad()
学习率(Learning Rate)
学习率 η 是梯度下降中最重要的超参数。
学习率的影响
θnew=θold−η⋅∂θ∂L
学习率太大(如 η=1.0)
- 参数更新幅度太大
- 可能"跳过"最优点
- Loss震荡或发散
javascript
Loss
^
| /\ /\
| / \ / \
| / \/ \
| /
+-----------------> Iteration
学习率太小(如 η=0.0001)
- 参数更新幅度太小
- 收敛极其缓慢
- 可能卡在局部最优
markdown
Loss
^
|
| ___
| /
| /
|/_______________
+-----------------> Iteration
很长时间才下降一点
学习率合适(如 η=0.001)
- 稳定下降
- 收敛速度合理
lua
Loss
^
|
|\
| \
| \___
| ----____
+-----------------> Iteration
典型的学习率值
| 模型规模 | 典型学习率 |
|---|---|
| 小模型(<100M) | 1e-3 ~ 1e-4 |
| 中型模型(100M-1B) | 5e-4 ~ 1e-4 |
| 大模型(1B-100B+) | 1e-4 ~ 1e-5 |
为什么大模型用更小的学习率?
- 参数量大,梯度累积效应强
- 需要更细致的调整
- 避免训练不稳定
学习率调度(Learning Rate Scheduling)
问题:固定学习率不是最优的。
解决方案:在训练过程中动态调整学习率。
1. Warmup(预热)
训练初期使用很小的学习率,然后逐渐增大:
η(t)=ηmax⋅min(1,Twarmupt)
原因:
- 训练初期,参数是随机的,梯度可能很大
- 小学习率避免参数被"破坏"
- 通常warmup 1000-10000步
可视化:
lua
Learning Rate
^
| /----------
| /
| /
| /
| /
+----+--------------> Step
warmup
2. Cosine Annealing(余弦退火)
学习率按余弦曲线衰减:
η(t)=ηmin+21(ηmax−ηmin)(1+cos(Ttπ))
可视化:
lua
Learning Rate
^
| ___
| / \___
| / \___
| / \___
| / ---
+----------------------->\__Step
warmup cosine decay
3. 组合:Warmup + Cosine Decay
GPT-3等大模型常用的策略:
python
def get_lr(step, warmup_steps, max_steps, lr_max, lr_min):
if step < warmup_steps:
# Warmup阶段:线性增长
return lr_max * step / warmup_steps
else:
# Cosine衰减阶段
progress = (step - warmup_steps) / (max_steps - warmup_steps)
return lr_min + 0.5 * (lr_max - lr_min) * (1 + math.cos(math.pi * progress))
AdamW优化器
虽然梯度下降很简单,但实践中有很多问题。AdamW是目前训练大模型的标准优化器。
SGD的问题
标准的梯度下降(SGD):
θt+1=θt−η⋅gt
其中 gt=∂θ∂L 是当前梯度。
问题1:不同参数的梯度尺度差异大
- 某些参数的梯度很大(如1000)
- 某些参数的梯度很小(如0.001)
- 用相同的学习率无法兼顾
问题2:梯度噪声
- Mini-batch的梯度有噪声
- 可能在最优点附近震荡
问题3:高维空间的"峡谷"
- 某些方向梯度大,某些方向梯度小
- SGD在峡谷中会"之字形"前进,效率低
Adam优化器
Adam(Adaptive Moment Estimation) 通过维护梯度的一阶矩 (均值)和二阶矩(方差)来自适应调整每个参数的学习率。
Adam的核心思想
- 动量(Momentum):累积历史梯度的指数移动平均
- 自适应学习率:根据梯度的历史方差调整学习率
Adam算法(简化版)
初始化:
m0v0=0=0
其中:
- m0:一阶矩初始值(梯度的均值)
- v0:二阶矩初始值(梯度的方差)
每次迭代:
gtmtvtm^tv^tθt+1=∂θt∂L=β1⋅mt−1+(1−β1)⋅gt=β2⋅vt−1+(1−β2)⋅gt2=1−β1tmt=1−β2tvt=θt−η⋅v^t +ϵm^t
各步骤说明:
- gt:计算当前梯度
- mt:更新一阶矩(梯度的指数移动平均)
- vt:更新二阶矩(梯度平方的指数移动平均)
- m^t:偏差修正后的一阶矩
- v^t:偏差修正后的二阶矩
- θt+1:根据修正后的矩更新参数
参数解释:
- β1=0.9:一阶矩的衰减率(通常)
- β2=0.999:二阶矩的衰减率(通常)
- ϵ=10−8:防止除零的小常数
- m^t:偏差修正后的一阶矩
- v^t:偏差修正后的二阶矩
直观理解
一阶矩 mt(动量):
- 累积历史梯度的加权平均
- 让参数更新更平滑,减少震荡
- 类似物理中的"惯性"
二阶矩 vt(方差):
- 累积历史梯度平方的加权平均
- 反映梯度的波动程度
- 梯度波动大的参数,学习率自动减小
自适应学习率:
effective_lr=v^t +ϵη
- 梯度波动大( v^t大):学习率小
- 梯度波动小( v^t小):学习率大
AdamW:Adam + Weight Decay
AdamW 是Adam的改进版本,专门针对深度学习中的权重衰减(Weight Decay)。
什么是权重衰减?
问题:如果不加限制,神经网络的参数可能会变得非常大。
python
# 举个例子
# 假设模型学到了这样的参数:
W = [[100, -200],
[300, -400]]
# 过大的参数会导致:
1. 过拟合:模型对训练数据"记住"而不是"理解"
2. 数值不稳定:计算时容易溢出
3. 泛化能力差:在新数据上表现不好
权重衰减是一种正则化技术,通过惩罚大参数来防止上述问题:
Ltotal=L+2λ∥θ∥2
其中:
- L:原始损失函数
- λ:权重衰减系数(如0.01、0.1)
- ∥θ∥2=θ12+θ22+...+θn2:参数的L2范数
直观理解:
ini
不用权重衰减:
Loss = 预测错误程度
使用权重衰减:
Loss = 预测错误程度 + λ × (参数大小的惩罚)
模型需要在"预测准确"和"参数不要太大"之间取得平衡
Adam中的权重衰减问题
在标准Adam中,权重衰减是通过把 λθ 加到梯度上实现的:
gt=∂θ∂L+λθ
然后用这个"带权重衰减的梯度"来更新动量和方差:
mtvt=β1⋅mt−1+(1−β1)⋅gt=β2⋅vt−1+(1−β2)⋅gt2
注意:这里的 gt 包含了权重衰减项 λθ,因此 mt 和 vt 都会受到权重衰减的影响。
问题在哪里?
权重衰减项 λθ 被混入了自适应学习率的计算中:
python
# 假设某个参数 θ = 10(比较大)
# 真实梯度 g = 0.1(比较小)
# λ = 0.01
# Adam中:
g_with_decay = 0.1 + 0.01 × 10 = 0.2
# 这个0.2会被用来计算 v_t(梯度方差)
v_t = 0.999 × v_{t-1} + 0.001 × 0.2² = 0.999 × v_{t-1} + 0.00004
# 因为参数大 → 权重衰减项大 → g_with_decay大 → v_t变大
# 而实际学习率 = lr / √v_t
# v_t变大 → 实际学习率变小 → 权重衰减效果被削弱!
# 结果:权重衰减的效果被自适应学习率"对冲"了
本质问题:权重衰减应该独立于梯度大小,但在Adam中它却受到自适应学习率的影响。
AdamW的解决方案
AdamW将权重衰减从梯度中解耦,直接在参数更新时应用:
标准Adam的做法(错误):
arduino
步骤1:g_t = ∂L/∂θ + λθ (梯度混入权重衰减)
步骤2:m_t = β₁×m_{t-1} + (1-β₁)×g_t
步骤3:v_t = β₂×v_{t-1} + (1-β₂)×g_t²
步骤4:θ_{t+1} = θ_t - lr × m̂_t/(√v̂_t + ε)
↑
权重衰减效果被自适应学习率削弱
AdamW的做法(正确):
arduino
步骤1:g_t = ∂L/∂θ
(纯梯度,不含权重衰减)
步骤2:m_t = β₁×m_{t-1} + (1-β₁)×g_t
步骤3:v_t = β₂×v_{t-1} + (1-β₂)×g_t²
步骤4:θ_{t+1} = θ_t - lr × [m̂_t/(√v̂_t + ε) + λ×θ_t]
↑ ↑
Adam更新 权重衰减(独立)
数学形式:
θt+1=θt−η⋅(v^t +ϵm^t+λθt)
或者分两步写更清楚:
θt+1′θt+1=θt−η⋅v^t +ϵm^t=θt+1′−η⋅λ⋅θt
其中:
- 第一步是标准的Adam更新
- 第二步是权重衰减,独立应用
具体例子对比:
python
# 继续上面的例子
# θ = 10, 真实梯度 g = 0.1, λ = 0.01
# AdamW中:
g_t = 0.1 # 只用真实梯度
v_t = 0.999 × v_{t-1} + 0.001 × 0.1² # 不受权重衰减影响
# Adam更新部分:
Δθ_adam = lr × 0.1 / √v_t
# 权重衰减部分(独立应用):
Δθ_decay = lr × λ × θ = lr × 0.01 × 10 = lr × 0.1
# 总更新:
θ_{t+1} = θ - Δθ_adam - Δθ_decay
# 关键:权重衰减不受自适应学习率影响!
# 参数越大,衰减越强,完全符合正则化的本意
可视化对比:
arduino
Adam (标准权重衰减):
┌─────────────────────────────────────┐
│ 梯度 g + 权重衰减 λθ │
└──────────┬──────────────────────────┘
↓
┌──────────────┐
│ 计算 m_t │ ← 权重衰减混入动量
│ 计算 v_t │ ← 权重衰减影响方差
└──────┬───────┘
↓
自适应学习率 lr/√v_t
↓
参数更新 ← 权重衰减效果被削弱 ✗
AdamW (解耦权重衰减):
┌─────────────────────────────────────┐
│ 纯梯度 g (不含权重衰减) │
└──────────┬──────────────────────────┘
↓
┌──────────────┐
│ 计算 m_t │ ← 只用真实梯度
│ 计算 v_t │ ← 只用真实梯度
└──────┬───────┘
↓
┌──────────────┐ ┌──────────────┐
│ Adam更新 │ + │ 权重衰减独立 │
│ lr×m̂/√v̂ │ │ lr×λ×θ │
└──────┬───────┘ └──────┬───────┘
└──────────┬───────────┘
↓
参数更新 ← 权重衰减效果完整 ✓
效果对比:
| 对比项 | Adam | AdamW |
|---|---|---|
| 权重衰减位置 | 混入梯度中 | 独立于梯度 |
| 权重衰减是否受自适应学习率影响 | 是(被削弱) | 否(独立) |
| 大参数的衰减效果 | 较弱 | 强(符合预期) |
| 大模型训练效果 | 较差 | 更好 |
| 泛化能力 | 一般 | 更好 |
| 是否被广泛采用 | 较少 | GPT-3、LLaMA标准 |
总结:
- Adam : gt=∂θ∂L+λθ,然后用 gt 更新 mt 和 vt → 权重衰减效果被自适应学习率削弱
- AdamW : gt=∂θ∂L,更新 mt 和 vt 后,再独立应用权重衰减 θ←θ−ηλθ → 权重衰减效果完整保留
这就是为什么现代大模型训练都使用AdamW而不是Adam!
AdamW的超参数
| 参数 | 典型值 | 说明 |
|---|---|---|
| η(学习率) | 1e-4 ~ 1e-5 | 最重要的超参数 |
| β1 | 0.9 | 一阶矩衰减率 |
| β2 | 0.999 或 0.95 | 二阶矩衰减率 |
| λ(weight decay) | 0.01 ~ 0.1 | 权重衰减系数 |
| ϵ | 1e-8 | 数值稳定性常数 |
GPT-3的设置:
- Learning rate: 6e-5(with warmup and cosine decay)
- β1 = 0.9
- β2 = 0.95
- Weight decay: 0.1
PyTorch实现
python
import torch
import torch.optim as optim
# 定义模型
model = GPT2Model(...)
# 创建AdamW优化器
optimizer = optim.AdamW(
model.parameters(),
lr=6e-5, # 学习率
betas=(0.9, 0.95), # (β₁, β₂)
weight_decay=0.1, # 权重衰减
eps=1e-8 # ε
)
# 训练循环
for batch in dataloader:
# 前向传播
outputs = model(batch['input'])
loss = loss_fn(outputs, batch['target'])
# 反向传播
loss.backward()
# 梯度裁剪(防止梯度爆炸)
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
# 参数更新(AdamW)
optimizer.step()
# 梯度清零
optimizer.zero_grad()
完整的训练流程
让我们把所有概念串起来,看一个完整的训练流程:
伪代码
python
# 1. 初始化
model = Transformer(...) # 随机初始化参数
optimizer = AdamW(model.parameters(), lr=1e-4, weight_decay=0.1)
lr_scheduler = CosineAnnealingWarmup(optimizer, warmup_steps=2000)
# 2. 训练循环
for epoch in range(num_epochs):
for batch in dataloader:
# (1) 前向传播
input_ids = batch['input'] # [batch_size, seq_len]
target_ids = batch['target'] # [batch_size, seq_len]
logits = model(input_ids) # [batch_size, seq_len, vocab_size]
# (2) 计算Loss
loss = cross_entropy(logits, target_ids)
# (3) 反向传播
loss.backward() # 自动计算所有参数的梯度
# (4) 梯度裁剪(可选,防止梯度爆炸)
clip_grad_norm_(model.parameters(), max_norm=1.0)
# (5) 参数更新(AdamW)
optimizer.step()
# (6) 学习率调整
lr_scheduler.step()
# (7) 梯度清零
optimizer.zero_grad()
# (8) 打印进度
if step % 100 == 0:
print(f"Epoch {epoch}, Step {step}, Loss: {loss.item():.4f}, LR: {lr_scheduler.get_last_lr()[0]:.6f}")
完整流程图
scss
初始化模型参数(随机)
↓
┌─────────────────────── Training Loop ───────────────────────┐
│ │
│ 加载一个batch的数据 │
│ ↓ │
│ 前向传播:input → Transformer → logits │
│ ↓ │
│ 计算Loss:Cross-Entropy(logits, target) │
│ ↓ │
│ 反向传播:计算梯度 ∂L/∂θ │
│ ↓ │
│ 梯度裁剪:防止梯度爆炸 │
│ ↓ │
│ 参数更新:θ ← θ - AdamW(∂L/∂θ) │
│ ↓ │
│ 学习率调整:Warmup + Cosine Decay │
│ ↓ │
│ 梯度清零:准备下一个batch │
│ ↓ │
│ [循环] │
│ │
└───────────────────────────────────────────────────────────────┘
↓
训练完成,得到优化后的参数
小结
-
前向传播:
- 数据从输入到输出的计算过程
- 得到模型的预测结果
-
损失函数:
- 衡量模型输出与真实答案的差距
- 语言模型常用交叉熵损失: L=−logP(正确答案)
-
反向传播:
- 利用链式法则计算梯度 ∂θ∂L
- 现代框架自动完成(AutoGrad)
-
梯度下降:
- 沿着负梯度方向更新参数: θ←θ−η∇L
- 实践中使用Mini-Batch GD
-
学习率:
- 控制参数更新的步长
- 需要careful tuning
- 通常使用Warmup + Cosine Decay
-
AdamW优化器:
- 结合动量和自适应学习率
- 独立的权重衰减
- 是训练大模型的标准选择
训练的本质:通过不断的"前向计算-计算误差-反向传播-更新参数"循环,让模型的输出越来越接近正确答案,最终学会预测下一个词。
这就是大模型"学习"的秘密!