4. 训练过程错误:显存不足(OOM)、学习率设置不当、梯度爆炸/消失、训练集与验证集划分错误

训练模型的时候,我敢说你一定遇到过这几个“老朋友”。显存爆了、Loss飞了、模型不收敛…… 说实话,我刚开始用Reality AI那会儿,几乎每个项目都要跟它们打一架。今天咱们就把这几个坑一个一个填平。

核心要点:训练过程的四个常见错误,本质上是资源管理、超参数调优、梯度稳定性和数据划分的问题。每个问题都有对应的诊断方法和解决方案。

4.1 显存不足(OOM)

OOM,全称Out of Memory。说白了就是显存放不下了。我在项目中遇到过最夸张的一次,一个3D点云模型直接把24G显存吃满,然后程序直接崩了。嗯,当时我盯着黑屏愣了三秒。

为什么会OOM?

  • Batch Size太大——这是最常见的原因。你想想看,一个batch里塞太多样本,显存自然扛不住。
  • 输入尺寸过大——比如图像分辨率太高,或者序列长度太长。
  • 模型本身太大——参数数量多,中间激活值也大。
  • 梯度累积——有些框架默认会保留所有中间梯度。

怎么解决?

我个人习惯按这个顺序排查:

  1. 减小Batch Size——从32降到16,甚至8。别怕,小batch也能训练,就是慢点。
  2. 降低输入尺寸——比如图像从512x512降到256x256。
  3. 使用梯度累积——模拟大batch的效果,但显存占用小。
  4. 开启混合精度训练——FP16比FP32省一半显存。
# Reality AI 中开启混合精度训练
from reality_ai import Trainer, MixedPrecision

trainer = Trainer(
    model=my_model,
    mixed_precision=MixedPrecision.FP16,  # 开启FP16
    batch_size=16,
    gradient_accumulation_steps=4  # 梯度累积
)

小技巧:如果你不确定显存够不够,可以先跑一个batch试试。我经常用这个办法快速验证,比等半天再崩要好得多。

4.2 学习率设置不当

学习率,说白了就是模型每次更新的步长。步子迈大了容易扯着蛋(Loss震荡),步子迈小了又走不动(收敛太慢)。

学习率太高:

  • Loss曲线像过山车,上下乱跳
  • 模型可能直接发散,Loss变成NaN
  • 我见过有人设了0.1,结果Loss直接飞到1e10

学习率太低:

  • Loss下降缓慢,训练半天没动静
  • 容易陷入局部最优

怎么选学习率?

我建议用学习率范围测试(LR Range Test)。简单说就是让学习率从小变大,看Loss什么时候开始上升,那个拐点附近就是最佳学习率。

# Reality AI 中的学习率范围测试
from reality_ai import LRFinder

lr_finder = LRFinder(model, optimizer)
lr_finder.range_test(train_loader, start_lr=1e-7, end_lr=1, num_iter=100)
lr_finder.plot()  # 画出Loss vs 学习率曲线

# 根据曲线选择最佳学习率,比如 1e-3

注意:学习率不是一成不变的。我习惯用余弦退火或者阶梯式衰减,让学习率在训练过程中逐渐降低。这样前期学得快,后期学得稳。

4.3 梯度爆炸/消失

梯度爆炸和消失,是深度学习的“老毛病”了。尤其是深层网络,比如ResNet-50、Transformer这些。

梯度爆炸:

  • Loss突然变成NaN
  • 模型参数变得非常大
  • 训练直接崩掉

梯度消失:

  • Loss下降非常慢,甚至不动
  • 浅层网络的参数几乎不更新
  • 模型学不到东西

怎么诊断?

我一般会打印梯度的范数(gradient norm)。如果范数突然变得很大(比如超过100),那就是梯度爆炸。如果范数一直很小(比如小于1e-6),那就是梯度消失。

# 监控梯度范数
for name, param in model.named_parameters():
    if param.grad is not None:
        grad_norm = param.grad.norm().item()
        print(f"{name}: grad_norm = {grad_norm:.6f}")

解决方案:

问题 解决方案 说明
梯度爆炸 梯度裁剪(Gradient Clipping) 限制梯度的最大值,比如设为1.0
梯度消失 使用ReLU、残差连接、BatchNorm 这些技巧能缓解梯度消失
两者都有 调整初始化方式 比如用Xavier或He初始化
# Reality AI 中启用梯度裁剪
from reality_ai import Trainer

trainer = Trainer(
    model=my_model,
    gradient_clip_val=1.0,  # 梯度裁剪阈值
    gradient_clip_algorithm='norm'  # 按范数裁剪
)

避坑指南:我曾经在一个LSTM项目里遇到梯度爆炸,折腾了两天才发现是初始化的问题。后来改用正交初始化,问题就解决了。所以,遇到梯度问题先检查初始化。

4.4 训练集与验证集划分错误

这个错误看起来简单,但后果很严重。我见过有人把验证集和训练集混在一起,结果模型在验证集上表现“完美”,一到真实场景就拉胯。

常见错误:

  • 数据泄露——验证集里混入了训练集的数据
  • 划分比例不当——比如验证集太小,评估结果不可靠
  • 时间序列乱序——用未来的数据预测过去
  • 类别分布不均——训练集和验证集的类别比例差异大

正确的做法:

  1. 随机划分——用sklearn的train_test_split,设置random_state保证可复现
  2. 分层采样——对于分类任务,保持训练集和验证集的类别比例一致
  3. 时间序列划分——按时间顺序切分,不能用未来的数据
  4. 交叉验证——数据量小的时候,用K折交叉验证更可靠
# 正确的数据划分方式
from sklearn.model_selection import train_test_split

# 随机划分,保持类别分布
X_train, X_val, y_train, y_val = train_test_split(
    X, y, 
    test_size=0.2, 
    random_state=42, 
    stratify=y  # 分层采样
)

# 时间序列划分
train_size = int(len(data) * 0.8)
train_data = data[:train_size]
val_data = data[train_size:]

注意:划分完数据后,一定要检查一下验证集的分布。我习惯画个直方图对比一下训练集和验证集的标签分布,确保它们差不多。如果差异太大,说明划分有问题。

知识体系总览

下面这张图把本章的四个错误和对应的解决方案串起来了。你可以把它当作一个快速排查指南。

训练过程错误排查指南 显存不足 (OOM) 学习率设置不当 梯度爆炸/消失 数据划分错误 解决方案 • 减小Batch Size • 降低输入尺寸 • 混合精度训练 解决方案 • LR Range Test • 余弦退火衰减 • 阶梯式衰减 解决方案 • 梯度裁剪 • 残差连接 • 合理初始化 解决方案 • 随机划分+分层 • 时间序列切分 • 交叉验证 核心原则 1. 先诊断,后治疗 —— 用监控工具定位问题 2. 从简单方案开始 —— 比如先减小Batch Size 3. 保持数据纯洁 —— 验证集不能泄露训练信息

好了,这四个错误基本上覆盖了训练过程中80%的坑。下次遇到问题,别慌,按这个流程排查一遍,大概率能解决。如果还不行……嗯,那就看看是不是代码写错了(笑)。

专注资料整理