loss不下降怎么办

本文最后更新于 2025年3月27日 晚上

Loss不下降怎么办

在训练神经网络时,如果发现 Loss 长时间不下降,可以从以下两个方面进行检查。

1. 检查参数设置

  • 学习率(Learning Rate)是否合适
    • 过大可能导致 Loss 震荡甚至发散。
    • 过小可能导致收敛速度过慢甚至停滞。
    • 观察 Loss 变化趋势,并尝试调整学习率。
  • 优化器(Optimizer)设置
    • 是否正确初始化优化器?
    • 是否正确使用 zero_grad() 清除梯度?
    • 是否尝试了不同的优化器(AdamW、SGD 等)?
  • Batch Size 影响
    • 过小的 batch size 可能导致梯度噪声较大。
    • 过大的 batch size 可能导致泛化能力下降。
    • 适当调整 batch size 观察效果。
  • 数据是否正确
    • 目标值范围是否正确?
    • 是否存在数据泄漏?
    • 归一化或标准化是否一致?

2. 检查 param 和 param.grad

  • param是否被正确初始化!!!
    • 检查param是否被初始化为0,如果有梯度初始化为0的层一定要注意!!!最后的loss看起来正常但是可能实际上在反传的时候是nan
      1
      2
      3
      4
      5
      6
      for name, param in self.named_parameters():
      if (param == 0).all():
      print(f"Parameter {name} is all zeros!")
      if param.grad is not None:
      self.log(f"grad_norm/{name}", param.grad.norm()) # 记录梯度范数
      print(f"Layer {name} | Grad Norm: {param.grad.norm().item()}")
    • 如果检查到param初始化有0的,用别的初始化方法代替
      1
      2
      3
      4
      5
      6
      7
      # 初始化参数为0的代码
      param = nn.Parameter(torch.zeros(param_shape))
      param.fill_(0)
      # 初始化为正态分布
      nn.init.normal_(param, mean=0.0, std=0.02)
      # 如果param是2维的,可以用Xavier 初始化
      nn.init.xavier_uniform_(param) # Xavier 初始化
  • 梯度是否正确计算
    • 通过 on_after_backward()on_before_optimizer_step() 检查参数的梯度是否为 None
    • 可能原因:参数未注册、未计算梯度或梯度被截断。
  • 输入是否有 requires_grad=True
    • 使用 input.requires_grad 检查输入是否可计算梯度。
    • 如果 requires_grad=False,则反向传播不会计算梯度。
  • 检查梯度是否过大或过小(Lightning PyTorch)
    • 如果你使用的是lighting pytorch 包,可以在 on_after_backward() 里打印梯度范数:
      1
      2
      3
      4
      5
      def on_after_backward(self):
      for name, param in self.named_parameters():
      if param.grad is not None:
      self.log(f"grad_norm/{name}", param.grad.norm())
      print(f"Layer {name} | Grad Norm: {param.grad.norm().item()}")
    • 如果你在 DeepSpeed / FSDP / DDP 之类的分布式环境下训练,梯度可能在 on_after_backward 之外的地方被同步,导致 .grad 一直是 None,上面的方法会不适用。因此你可以用hook来检查
      1
      2
      3
      4
      5
      6
      def hook_fn(self, grad):
      print(f"Gradient: {grad}")
      # 在你的training_step中加入
      for name, param in self.named_parameters():
      if param.requires_grad:
      param.register_hook(hook_fn)
  • 计算图是否正确构建
    • 确保 loss 依赖于所有参数,否则 loss.backward() 不会正确计算梯度。
    • 检查 loss.requires_grad 是否为 True
    • 确保没有错误使用 .detach()torch.no_grad(),否则会截断梯度。

3. 其他可能原因

  • 梯度裁剪(Gradient Clipping)
    • 在梯度爆炸时使用 self.clip_gradients(optimizer, gradient_clip_val=1.0) 进行裁剪。
  • 检查是否正确使用 manual_backward()(Lightning)
    • 仅在 automatic_optimization=False 时,才需要手动调用 manual_backward(loss)

如果按照这些步骤排查,应该能找到 Loss 不下降的原因并加以修正。


loss不下降怎么办
http://example.com/2025/03/27/loss不下降怎么办/
作者
Artimis
发布于
2025年3月27日
许可协议