深度学习训练模型损失Loss为NaN或者无穷大(INF)原因及解决办法

文章目录

  • 一、可能原因
    • ==1. 学习率过高==
    • ==2. batch size过大==
    • 3. 梯度爆炸
    • 4. 损失函数不稳定
    • 5. 数据预处理问题
    • 6. 数据标签与输入不匹配
    • 7. 模型初始化问题
    • 8. 优化器设置问题
    • 9. 数值问题
    • ==10. 模型结构设计缺陷==
  • 二、调试步骤
  • 三、常见预防措施

一、可能原因

1. 学习率过高

  • 原因:学习率过高可能导致梯度爆炸,权重更新幅度过大,导致模型参数变为无穷大或 NaN。学习率设置过大是常见问题,它会让参数更新步幅太大,模型难以收敛,导致梯度在更新过程中迅速增大,使损失值变为无穷大或者NaN。

  • 如果在迭代的100轮以内,出现NaN,一般情况下的原因是因为你的学习率过高,需要降低学习率。可以不断降低学习率直至不出现NaN为止,一般来说低于现有学习率1-10倍即可。如果为了排除是不是学习率的原因,可以直接把学习率设置为0,然后观察loss是否出现Nan,如果还是出现就不是学习率的原因。需要注意的是,即使使用 adam 之类的自适应学习率算法进行训练,也有可能遇到学习率过大问题,而这类算法,一般也有一个学习率的超参,可以把这个参数改的小一些。

  • 解决方法

    • 降低学习率,例如从 1e-3 调整到 1e-4 或更低。尝试降低学习率,采用学习率衰减策略,如torch.optim.lr_scheduler.StepLR,让学习率随着训练轮次逐步变小,找到合适的学习率区间。降低初始学习率,并且设置合适的学习速率和学习率衰减,至少降低一个数量级
    • 使用学习率调度器(如 torch.optim.lr_scheduler)。
    • 梯度裁剪,设置gradient clipping,用于限制过大的 diff
    • 数据量纲不一致,也会导致梯度爆炸,数据归一化方法(减均值,除方差,或者加入normalization,例如BN、L2 norm等)
    • 注意每个batch前梯度要清零,optimizer.zero_grad()
    • 如果模型中有多个loss层,就需要找到梯度爆炸的层,然后降低该层的loss weight

2. batch size过大

batch size过小,会导致模型后期摇摆不定,迟迟难以收敛,而过大时,模型前期由于梯度的平均,导致收敛速度过慢。
epoch在100内损失出现NaN可能原因以及解决

  • 原因:如果在迭代的100轮以内,出现NaN,一般情况下的原因是因为你的学习率过高,需要降低学习率,也可能是batch size过大。
  • 解决方案:减小学习率、减小batch size

3. 梯度爆炸

  • 原因:梯度过大,尤其是在深层网络中,可能导致数值溢出。
  • 解决方法
    • 使用梯度裁剪 (torch.nn.utils.clip_grad_norm_torch.nn.utils.clip_grad_value_)。
    • 检查模型结构,避免过深或过大的全连接层和激活函数。
      梯度消失不会导致模型出现 nan 和 inf ,只会导致模型 loss 不会下降,精度无法在训练过程中提升。而梯度爆炸则有可能导致模型在训练过程中出现 inf 。

4. 损失函数不稳定

  • 原因:损失函数可能产生过大的中间值(例如,log 函数输入为 0,或平方误差值过大)。所选的损失函数与任务不契合。比如,在回归任务中用分类的交叉熵损失函数,由于计算逻辑不符,会得出不合理的结果。
  • 解决方法
    • 在交叉熵损失中,确保模型输出经过 softmax(如果未使用 CrossEntropyLoss 的内置函数)。依据任务特性,正确选用损失函数。回归任务常用均方误差损失(MSELoss),分类任务常用交叉熵损失(CrossEntropyLoss )等。
    • 对输入数据或标签进行平滑处理(如标签平滑技术)。
    • 损失函数应该考虑到是否可以正常地backward。
    • 其次对输入的Tensor是否进行了类型转化,保证计算中保持同一类型。
    • 最后考虑在除数中加入微小的常数保证计算稳定性。
    • 尝试重现该错误,在loss layer中加入一些输出以进行调试。找到可能出现的错误的地方,增加一个bias

5. 数据预处理问题

  • 原因
    • 数据中存在异常值(如极大或极小的数值)。如果训练数据本身就有缺失值或者无穷大的异常数据点,在模型计算过程中,很容易产生不正常的梯度,进而导致损失值异常。例如,在图像数据处理时,若像素值由于错误的归一化变成无穷大,后续计算就会受影响。
    • 输入数据未标准化或归一化,导致输入范围过大。
  • 解决方法
    • 检查数据分布,使用归一化或标准化(如 MinMaxScalerStandardScaler)。对数据集进行全面检查,使用numpy.isnan()torch.isnan() 函数定位包含NaN的样本,去除或者修正这些异常数据。对于无穷大的数据点,也可用类似方法检测,再重新做数据的归一化等预处理。
    • 过滤异常值,确保数据质量。

6. 数据标签与输入不匹配

  • 原因脏数据。标签的数量、维度与输入数据不对应,使得模型在计算损失函数时出错。例如,分类任务中,标签数量与类别数不一致,会让交叉熵损失函数无法正确计算。
  • 解决办法:仔细核对数据加载与预处理流程,保证标签与输入数据在形状、数量等维度上完全匹配。

7. 模型初始化问题

  • 原因:模型参数初始化不当,可能导致训练初期梯度消失或爆炸。不合适的权重初始化可能导致模型在前向传播或反向传播时出现梯度爆炸或梯度消失。例如,若把所有权重初始化为 0,反向传播时各层神经元学到的梯度完全相同,很容易让梯度异常增大,损失值变为无穷大。
  • 解决方法
    • 使用 PyTorch 提供的初始化方法(如 torch.nn.init)。
    • 使用良好的初始化策略(如 Xavier 初始化、He 初始化)。采用合理的初始化方法,如nn.init.kaiming_normal_用于激活函数为 ReLU 类的网络层,nn.init.xavier_normal_ 也适用于多种常见网络结构,替换原来的初始化方式。

8. 优化器设置问题

  • 原因:某些优化器参数(如权重衰减、动量)设置不合理。
  • 解决方法
    • 调整优化器参数,例如降低权重衰减(weight_decay)或动量(momentum)。
    • 尝试不同的优化器(如从 SGD 转换为 Adam)。

9. 数值问题

  • 原因:某些计算操作导致数值溢出或除以零。
  • 解决方法
    • 检查是否有除以零操作(如 1/x 中的 x 是否可能为 0)。
    • 在计算中使用 torch.clamp 限制值的范围。

10. 模型结构设计缺陷

  • 原因:一些过于复杂或者设计不合理的网络架构,像层数极深且没有合适的归一化层,容易造成梯度问题。例如,深层循环神经网络(RNN),若没有梯度裁剪等机制,梯度很容易累积爆炸,使得损失值趋向无穷。
  • 如果你的网络中BatchNorm层很多,而且充当比较重要的角色,那么可以适当地检查一下Tensor在输入BatchNorm层后有没有可能变为nan,如果恰好发生这种情况,BatchNorm层中的移动均值(running_mean)和移动方差(running_var)也很有可能都是nan,而且这种情况很有可能发生在预测阶段注:BatchNorm不能输入单个图像,也就是batch里只有一个样本。
  • 当然上述现象出现的原因大部分在当我们使用model.eval()(Pytorch)之后发生。如果你在预测阶段也将模型model设置为model.train(True),那么问题可能就不会出现。
  • 解决办法:简化模型结构,合理添加批归一化层(Batch Normalization)、层归一化层(Layer Normalization )来稳定梯度;还可以设置梯度裁剪,限制梯度的最大范数,避免梯度爆炸,在优化器更新参数前,使用torch.nn.utils.clip_grad_norm_函数。

二、调试步骤

  1. 检查数据

    • 打印训练数据的最大值、最小值、均值等。
    • 可视化数据分布(例如使用直方图)。
  2. 观察损失变化

    • 打印每轮或每批次的损失值,观察损失在出错前是否剧烈波动。
  3. 梯度检查

    • 检查梯度是否出现异常(如过大或为 NaN):
      for name, param in model.named_parameters():
          if param.grad is not None:
              print(name, param.grad.abs().max())
      
  4. 单步运行

    • 在训练过程中暂停并打印中间变量,确认数据流在何处出现问题。
  5. 降低复杂度

    • 简化模型或使用更小的数据集,逐步定位问题。

三、常见预防措施

  1. 使用混合精度训练

    • 可以避免数值溢出问题,尤其是在 GPU 上训练大模型时:
      from torch.cuda.amp import GradScaler, autocast
      scaler = GradScaler()
      for data, target in train_loader:
          with autocast():
              output = model(data)
              loss = criterion(output, target)
      
          scaler.scale(loss).backward()
          scaler.step(optimizer)
          scaler.update()
      
  2. 使用正则化

    • 添加 L2 正则化或 Dropout 来稳定模型训练。
  3. 动态调整学习率

    • 使用学习率调度器,例如 ReduceLROnPlateau:
      scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, 'min')
      scheduler.step(val_loss)
      

参考文章:
Pytorch训练模型损失Loss为Nan或者无穷大(INF)原因
神经网络训练时损失(loss)不下降常见解决办法以及训练时损失出现nan可能原因以及解决

你可能感兴趣的:(学习笔记,机器学习,深度学习,pytorch,深度学习,人工智能,机器学习)