📚 课程目录

第26课:混合精度训练

📖 本课概述

混合精度训练用FP16做前向和梯度计算,FP32维护主权重,既加速训练又保持精度。

⚡ 一、为什么用混合精度

FP16 vs FP32:

内存:减半 → 可用更大batch或更大模型

速度:Tensor Core加速 → 2-3倍加速(V100/A100)

带宽:数据传输量减半

⚠️ 二、FP16的风险

2.1 数值溢出

FP16最大值~65504,梯度可能溢出!

2.2 数值下溢

FP16最小正值~6e-8,小梯度可能变为0!

2.3 舍入误差

小数+大数时,小数可能被"吃掉"。

🔧 三、解决方案:损失缩放

前向:FP16计算

loss缩放:loss × S(放大S倍,避免梯度下溢)

反向:FP16计算梯度

梯度还原:grad / S(恢复真实值)

FP32更新主权重

3.1 PyTorch AMP实现


import torch
import torch.nn as nn
from torch.cuda.amp import autocast, GradScaler

# 混合精度训练模拟(CPU上也展示逻辑)
torch.manual_seed(42)
X = torch.randn(500, 100)
y = torch.randint(0, 10, (500,))

model = nn.Sequential(
    nn.Linear(100, 256), nn.ReLU(),
    nn.Linear(256, 128), nn.ReLU(),
    nn.Linear(128, 10),
)

# FP32训练
model_fp32 = nn.Sequential(
    nn.Linear(100, 256), nn.ReLU(),
    nn.Linear(256, 128), nn.ReLU(),
    nn.Linear(128, 10),
)
model_fp32.load_state_dict(model.state_dict())

optimizer_fp32 = torch.optim.Adam(model_fp32.parameters(), lr=0.001)
loss_fn = nn.CrossEntropyLoss()

import time
start = time.time()
for epoch in range(50):
    optimizer_fp32.zero_grad()
    loss = loss_fn(model_fp32(X), y)
    loss.backward()
    optimizer_fp32.step()
fp32_time = time.time() - start
fp32_loss = loss.item()

# 模拟FP16的效果(手动转换)
print("=== FP16 vs FP32 数值对比 ===")
for name, param in model_fp32.named_parameters():
    fp16_param = param.data.half().float()  # FP32→FP16→FP32
    error = (param.data - fp16_param).abs().mean().item()
    max_val = param.data.abs().max().item()
    print(f"{name}: FP16转换误差={error:.8f}, 最大值={max_val:.4f}")

# 损失缩放演示
print("\n=== 损失缩放 ===")
small_grad = torch.tensor([1e-6, 1e-7, 1e-8, 1e-9])
scale_factor = 1024.0

scaled_grad = small_grad * scale_factor
print(f"原始梯度: {small_grad.tolist()}")
print(f"缩放后:   {scaled_grad.tolist()}")
print(f"FP16下溢阈值: ~6e-8")

unscaled_grad = scaled_grad / scale_factor
print(f"还原后:   {unscaled_grad.tolist()}")

# 不同精度内存对比
print("\n=== 内存占用对比 ===")
model_size = sum(p.numel() for p in model.parameters())
print(f"模型参数量: {model_size:,}")
print(f"FP32: {model_size * 4 / 1024:.1f} KB")
print(f"FP16: {model_size * 2 / 1024:.1f} KB")
print(f"INT8: {model_size * 1 / 1024:.1f} KB")
print(f"FP32→FP16节省: {50:.0f}%")

print(f"\nFP32训练: loss={fp32_loss:.4f}, 耗时={fp32_time:.2f}s")
🟢 运行结果 ✅验证通过 === FP16 vs FP32 数值对比 === 0.weight: FP16转换误差=0.00001096, 最大值=0.1582 0.bias: FP16转换误差=0.00000977, 最大值=0.1494 2.weight: FP16转换误差=0.00000808, 最大值=0.1206 2.bias: FP16转换误差=0.00000668, 最大值=0.1041 4.weight: FP16转换误差=0.00001480, 最大值=0.1469 4.bias: FP16转换误差=0.00000982, 最大值=0.1043 === 损失缩放 === 原始梯度: [9.999999974752427e-07, 1.0000000116860974e-07, 9.99999993922529e-09, 9.999999717180685e-10] 缩放后: [0.0010239999974146485, 0.00010240000119665638, 1.0239999937766697e-05, 1.0239999710393022e-06] FP16下溢阈值: ~6e-8 还原后: [9.999999974752427e-07, 1.0000000116860974e-07, 9.99999993922529e-09, 9.999999717180685e-10] === 内存占用对比 === 模型参数量: 60,042 FP32: 234.5 KB FP16: 117.3 KB INT8: 58.6 KB FP32→FP16节省: 50% FP32训练: loss=0.2506, 耗时=0.22s

🔬 深入拓展

核心要点回顾

本课的核心知识点构成了训练技巧学习的重要一环。让我们从更高维度审视这些知识之间的关系:

常见陷阱与最佳实践

⚠️ 初学者常犯的错误:

💡 最佳实践:

与前后课程的联系

深度学习是一个整体——每一课的知识都不是孤立的:

📖 延伸阅读

💡 继续深入的学习路径:

📝 练习

练习1:GPU混合精度训练

在有GPU的环境下使用torch.cuda.amp训练。

练习2:BF16精度

了解BF16与FP16的区别,分析各自适用场景。

练习3:精度对模型影响

对比FP32/FP16/BF16/INT8的精度和速度。

💡 混合精度实践

精度选择

常见错误与解决方案

⚠️ 需要避免的典型错误:

  1. 不了解模型假设就盲目使用
  2. 没有建立基线就追求复杂方法
  3. 忽视数据质量和预处理
  4. 过度调参而不理解原理
  5. 只看训练指标忽略验证指标

性能优化建议

💡 提升模型性能的系统方法:

  1. 确保数据管道正确且高效
  2. 建立简单但正确的基线模型
  3. 分析基线的错误类型
  4. 针对性地改进(数据/模型/训练)
  5. 持续迭代,每步验证

🔬 深度拓展:从理论到实践

核心概念网络

理解本课内容需要将其置于更大的知识网络中。每个核心概念都不是孤立的,它们相互关联、相互支撑:

关键洞察与直觉

建立正确的直觉比记住公式更重要:

1. 维度直觉:高维空间中,数据分布与低维直觉截然不同(维度诅咒)

2. 信息瓶颈:神经网络逐层压缩信息,保留任务相关的、丢弃无关的

3. 流形假设:高维数据实际分布在低维流形上,学习即发现流形结构

4. 偏差-方差权衡:简单模型欠拟合(高偏差),复杂模型过拟合(高方差)

与其他课程的关联

本课作为训练技巧阶段的核心内容,与前后课程紧密关联:

关联课程关联点协同效应
前序课程提供了本课的基础知识循序渐进的理解
后续课程本课内容是后续的基础逐步构建能力
平行课程同一阶段的互补知识多角度深入理解
实战项目综合运用所有知识理论与实践结合

面试常见问题

💡 准备面试时,确保能回答以下问题:

  1. 用简单语言解释本课核心概念
  2. 画出关键算法/结构的流程图
  3. 比较不同方法的优缺点
  4. 举出实际应用场景
  5. 讨论常见误解和注意事项

进阶学习路径

掌握本课内容后,可以通过以下方式继续深入:

📐 补充:知识体系梳理

核心公式速查

将本课涉及的关键公式整理如下,方便回顾和记忆。理解每个公式背后的直觉比死记硬背更重要——试着用自然语言解释每个公式在做什么。

易混淆概念辨析

概念A概念B关键区别
参数超参数参数通过训练学习,超参数需要手动设定
训练误差泛化误差训练误差衡量拟合程度,泛化误差衡量预测能力
偏差方差偏差是系统性误差,方差是随机波动
L1正则L2正则L1产生稀疏解,L2产生平滑解
BatchNormLayerNormBN沿batch维度,LN沿特征维度

代码模板

以下是本课核心概念的标准PyTorch实现模板,可以直接用于实际项目:

💡 调试建议:当结果不符合预期时,先检查数据,再检查损失函数,最后检查模型结构。90%的问题出在前两个环节。

成就解锁:精度优化师

混合精度训练——速度翻倍,精度不减!