minimind/course/notes/07_amp_gradient_notes.md

142 lines
2.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 第7节:混合精度 + 梯度累积 + 梯度裁剪
> 训练流程核心机制笔记
---
## 📚 本节目录
1. [整体训练流程](#1-整体训练流程核心顺序)
2. [每一步在干什么](#2-每一步在干什么)
3. [三大核心机制总结](#3-三大核心机制总结)
4. [一句话总理解](#4-一句话总理解)
---
## 1. 整体训练流程(核心顺序)
```python
scaler.scale(loss).backward()
if step % accumulation_steps == 0:
scaler.unscale_(optimizer)
clip_grad_norm_(model.parameters(), max_norm)
scaler.step(optimizer)
scaler.update()
optimizer.zero_grad(set_to_none=True)
```
---
## 2. 每一步在干什么
### (1) `scaler.scale(loss).backward()`
**作用**:AMP 防止 FP16 下溢
- loss 先乘一个 scale(放大)
- backward 计算梯度
**目的**:防止 FP16 数值太小 → 梯度变 0
---
### (2) 梯度累积(accumulation)
**每 N 步才更新一次参数**
**作用**:
- 模拟大 batch training
- 节省显存
---
### (3) `scaler.unscale_(optimizer)`
**作用**:梯度"还原"
因为之前 loss 被放大了:`grad = grad × scale`
所以这里要把梯度恢复到真实数值(为了梯度裁剪)
---
### (4) `clip_grad_norm_()`
**作用**:防止梯度爆炸
**数学本质**:
先算整体梯度范数:
$$\|g\| = \sqrt{\sum_i g_i^2}$$
- 如果 `norm ≤ max_norm`:不变
- 如果 `norm > max_norm`:缩放 → `g := g × max_norm / norm`
**直觉**:不改变方向,只限制"更新步子大小"
---
### (5) `scaler.step(optimizer)`
**作用**:真正更新参数
- 如果检测到 NaN/Inf → 自动跳过
- 否则正常 optimizer.step()
---
### (6) `scaler.update()`
**作用**:动态调整 loss scale
| 情况 | 动作 |
|------|------|
| 出现 NaN/Inf | scale ↓(变小,更保守) |
| 训练稳定 | scale ↑(变大,提高精度) |
**本质**:自动调节 FP16 "放大倍率",保证稳定 + 高效
---
### (7) `optimizer.zero_grad(set_to_none=True)`
**作用**:清空梯度
`set_to_none=True` → 更省显存 + 更快
---
## 3. 三大核心机制总结
### AMP(scaler)
👉 解决 FP16 数值问题
- scale loss(防 underflow)
- 自动调 scale(防 overflow)
### 梯度累积
👉 解决显存限制
- 多 step 累积梯度
- 再统一更新
### 梯度裁剪
👉 解决梯度爆炸
- 控制 norm 上限
- 保持方向不变
---
## 4. 一句话总理解
**这套流程本质是在做三件事**:
1. 用 AMP 提速
2. 用 accumulation 放大 batch
3. 用 clipping 保稳定