【debug】pytorch损失/梯度出现NaN

排查顺序

最常见的就是出现了除0或者log0这种,看看代码中在这种操作的时候有没有加一个很小的数,但是这个数数量级要和运算的数的数量级要差很多。一般是1e-8。
在optim.step()之前裁剪梯度。

optim.zero_grad()
loss.backward()
nn.utils.clip_grad_norm(model.parameters, max_norm, norm_type=2)
optim.step()

max_norm一般是1,3,5。

前面两条还不能解决nan的话,就按照下面的流程来判断。
...

loss = model(input)
1. 先看loss是不是nan,如果loss是nan,那么说明可能是在forward的过程中出现了第一条列举的除0或者log0的操作

assert torch.isnan(loss).sum() == 0, print(loss)

2. 如果loss不是nan,那么说明forward过程没问题,可能是梯度爆炸,所以用梯度裁剪试试

optim.zero_grad()
loss.backward()
nn.utils.clip_grad_norm(model.parameters, max_norm, norm_type=2)

3.1 在step之前,判断参数是不是nan, 如果不是判断step之后是不是nan

assert torch.isnan(model.mu).sum() == 0, print(model.mu)
optim.step()

3.2 在step之后判断,参数和其梯度是不是nan,如果3.1不是nan,而3.2是nan,特别是梯度出现了Nan,考虑学习速率是否太大,调小学习速率或者换个优化器试试。

assert torch.isnan(model.mu).sum() == 0, print(model.mu)
assert torch.isnan(model.mu.grad).sum() == 0, print(model.mu.grad)

4. 如果还有问题的话,尝试定位出问题的tensor

梯度NaN定位方法

使用如下代码设置,在出现NaN异常时程序会报错,便于定位错误代码

import torch
# 正向传播时:开启自动求导的异常侦测
torch.autograd.set_detect_anomaly(True)

# 反向传播时:在求导时开启侦测
with torch.autograd.detect_anomaly():
    loss.backward()

常见原因

  • 使用了非法的操作如log(0)、÷0等
  • 使用FP16
    • FP16越界,如使用操作softmax、矩阵乘、EPS>1e-7等,解决方案就是先转换为FP32在运算
    • loss scale设置不合适,解决方案是使用‘dynamic’自动搜索
  • 很多网友提到,pytorch的求标准差的函数STD可能有问题。如果使用了类似会调用STD函数的各种Norm层就可能导致NAN问题。
  • 还有bfloat16 (BF16) 进行混合精度训练时,通常不需要使用 GradScaler,因为BF16拥有与FP32相同的指数范围,能够有效避免梯度下溢的问题,使用scaler反而会导致梯度爆炸。

 

 ​

posted @ 2021-07-16 11:58  TinaSmile  阅读(100)  评论(0)    收藏  举报