首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【深度学习 混合精度训练】只看训练过程就够了

【深度学习 混合精度训练】只看训练过程就够了

作者头像
flos chen
发布2026-01-23 17:44:49
发布2026-01-23 17:44:49
1640
举报

混合精度训练:只看训练过程就够了

一句话说清楚混合精度训练

在训练时,用半精度(FP16)算得快,用单精度(FP32)存得稳,两者结合就是混合精度训练。


一、训练时发生了什么变化?

代码语言:javascript
复制
传统训练(FP32全精度):
输入数据 → 模型计算 → 损失计算 → 反向传播 → 更新参数
   ↓           ↓          ↓           ↓          ↓
 FP32        FP32       FP32        FP32       FP32
(稳但慢) (稳但慢)  (稳但慢)   (稳但慢)  (稳但慢)

混合精度训练(FP16+FP32):
输入数据 → 模型计算 → 损失计算 → 反向传播 → 更新参数
   ↓           ↓          ↓           ↓          ↓
 FP16        FP16       FP16        FP16       FP32
(快点)    (快2-3倍) (快点)    (快点)   (这里必须用FP32!)
         ↑                        ↑
     用Tensor Core加速       损失要放大再传回去

二、训练代码怎么改?

PyTorch:就加4行代码
代码语言:javascript
复制
# 原来正常的训练循环
for data, target in dataloader:
    optimizer.zero_grad()
    output = model(data)          # 前向
    loss = criterion(output, target)
    loss.backward()               # 反向
    optimizer.step()              # 更新
代码语言:javascript
复制
# 启用混合精度后(只改4处!)
from torch.cuda.amp import autocast, GradScaler  # 1. 导入

scaler = GradScaler()  # 2. 创建缩放器

for data, target in dataloader:
    optimizer.zero_grad()
    
    with autocast():  # 3. 包住前向计算(自动用FP16算)
        output = model(data)
        loss = criterion(output, target)
    
    scaler.scale(loss).backward()  # 4. 用scaler包装loss
    scaler.step(optimizer)         #   用scaler包装step
    scaler.update()                #   更新缩放因子

关键: autocast() 自动决定哪里用FP16算,GradScaler 自动处理梯度太小的问题。


三、为什么训练时要分FP16和FP32?

简单比喻:
  • FP16(半精度):计算器(算得快,但数字大了小了会出错)
  • FP32(单精度):草稿纸(算得慢,但什么数都能记)
训练中的分工:
  1. 前向传播(算预测值):用FP16,因为只是算数,快点好
  2. 反向传播(算梯度):用FP16,因为梯度只是中间值
  3. 更新参数(存权重):用FP32,因为权重要长期保存,不能出错
核心问题解决:梯度太小怎么办?
代码语言:javascript
复制
# 假设梯度是 0.000001(FP16存不了,会变成0)
# GradScaler做的事:
1. 把损失放大:loss * 1024 = 变大的loss
2. 反向传播:得到放大的梯度
3. 更新前缩小:梯度 / 1024 = 正确的梯度
4. 用FP32更新参数

四、训练参数该怎么调?

参数

建议值

为什么

学习率

原来的 1.5-2倍

FP16梯度更稳定,可以大一点

批量大小

原来的 2倍左右

省下来的内存可以放更多数据

热身(Warmup)

必须用

前几个epoch慢慢提高学习率

示例:

代码语言:javascript
复制
# 原来:batch_size=32, lr=0.001
# 混合精度后:batch_size=64, lr=0.002
# 并且加学习率热身

五、训练时要注意什么?

1. 监控训练曲线
代码语言:javascript
复制
# 正常情况:
# 损失稳步下降 → 训练正常
# 准确率逐步上升 → 训练正常

# 有问题的情况:
# 损失突然变成NaN → 梯度爆炸了(调小学习率)
# 损失完全不变 → 梯度消失了(检查缩放器)
2. 检查GPU使用
代码语言:javascript
复制
# 运行训练时,另一个终端运行:
watch -n 1 nvidia-smi

# 应该看到:
# - GPU内存占用减少40-50%
# - GPU利用率更高
3. 验证精度
代码语言:javascript
复制
# 每个epoch结束后:
model.eval()  # 重要!验证时不要用autocast
with torch.no_grad():  # 重要!不要计算梯度
    for data, target in val_loader:
        output = model(data)  # 这里用FP32算
        # 计算准确率...

六、最常见的训练问题

问题1:损失变成NaN了

解决:

代码语言:javascript
复制
# 1. 降低学习率(最有效)
optimizer = Adam(model.parameters(), lr=0.001)  # 原来是0.002

# 2. 改缩放器设置
scaler = GradScaler(init_scale=65536.0)  # 默认是65536.0

# 3. 加梯度裁剪
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
问题2:训练变慢了

解决:

代码语言:javascript
复制
# 检查是否是这些问题:
1. 数据加载是瓶颈(用DataLoader num_workers>0)
2. 模型有些层不支持FP16(如某些自定义层)
3. GPU太旧(需要Pascal架构以上)
问题3:精度下降了0.5%

解决:

代码语言:javascript
复制
# 1. 试试BF16(如果GPU支持)
# 训练命令加:--bf16

# 2. 调整优化器
optimizer = AdamW(model.parameters(), lr=0.002, weight_decay=0.05)

# 3. 延长训练时间(有时需要多练几个epoch)

七、训练脚本示例

代码语言:javascript
复制
import torch
import torch.nn as nn
from torch.cuda.amp import autocast, GradScaler

def train_one_epoch(model, dataloader, optimizer, epoch):
    """一个epoch的训练"""
    model.train()
    scaler = GradScaler()  # 每个epoch都可以新建
    
    for batch_idx, (data, target) in enumerate(dataloader):
        data, target = data.cuda(), target.cuda()
        
        # 1. 清空梯度
        optimizer.zero_grad()
        
        # 2. 前向计算(自动混合精度)
        with autocast():
            output = model(data)
            loss = nn.CrossEntropyLoss()(output, target)
        
        # 3. 反向传播(自动梯度缩放)
        scaler.scale(loss).backward()
        
        # 4. 参数更新(自动取消缩放)
        scaler.step(optimizer)
        scaler.update()
        
        # 5. 打印进度
        if batch_idx % 100 == 0:
            print(f'Epoch {epoch}, Batch {batch_idx}, Loss: {loss.item():.4f}')
    
    return loss.item()

# 使用:直接调用这个函数训练

八、检查清单

  1. ✅ GPU是否支持?(2017年后的N卡基本都支持)
  2. ✅ PyTorch是否≥1.6?(pip list | grep torch 查看)
  3. ✅ 代码中加了autocast()GradScaler吗?
  4. ✅ 学习率调大了吗?(乘1.5-2倍)
  5. ✅ 批量大小能加大吗?(内存够就加大)
  6. ✅ 验证时关闭了autocast吗?

总结:训练时记住这3句话

  1. 前向/反向用FP16算 → 加速
  2. 参数更新用FP32存 → 稳定
  3. 梯度太小就放大GradScaler自动做

最后建议: 先用小数据跑1-2个epoch,确保不报错,再正式开始训练。

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=ky8c508by5

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-01-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 混合精度训练:只看训练过程就够了
    • 一句话说清楚混合精度训练
    • 一、训练时发生了什么变化?
    • 二、训练代码怎么改?
      • PyTorch:就加4行代码
    • 三、为什么训练时要分FP16和FP32?
      • 简单比喻:
      • 训练中的分工:
      • 核心问题解决:梯度太小怎么办?
    • 四、训练参数该怎么调?
    • 五、训练时要注意什么?
      • 1. 监控训练曲线
      • 2. 检查GPU使用
      • 3. 验证精度
    • 六、最常见的训练问题
      • 问题1:损失变成NaN了
      • 问题2:训练变慢了
      • 问题3:精度下降了0.5%
    • 七、训练脚本示例
    • 八、检查清单
    • 总结:训练时记住这3句话
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档