最近在用PyTorch复现一个Transformer做文本分类,模型不大,训练集也就几万条。前几个epoch loss正常下降,但到第5个epoch左右loss突然变成NaN,之后就一直NaN了。试过调小学习率(从1e-3降到1e-5)、加梯度裁剪(clip_grad_norm_设为1.0)、检查数据里有没有空值,都没什么用。网络里用了LayerNorm和GELU,优化器是AdamW,batch size=32。有没有人遇到过类似情况?一般这种后期突然NaN还可能是什么原因?感觉快被折磨疯了,求指点。
PyTorch训练时loss突然变NaN,调了几天还是找不到原因,大家帮我看看?
全部回复
共 11 条查下是不是某条样本太长导致attention爆了,我上次也是后期NaN,加个长度过滤就好了。
前几个epoch正常、后面突然NaN,这种“延迟爆炸”我也踩过,最后发现是某一批数据里出现了极长序列,attention的softmax直接把梯度干炸了。你可以试试打印每步的grad norm,看NaN前哪个参数层先异常,通常能定位到具体模块。另外AdamW的eps太小(默认1e-8)在fp16下也容易炸,换成1e-6或者直接上bf16会稳很多。还有个偏方是在LayerNorm前加个极小值防止除零,虽然治标但能救急。
第5个epoch才炸,感觉不像单纯学习率的问题。我之前也遇到过类似的,最后发现是某个batch里出现了极长文本,attention的softmax直接爆了。建议你打印一下每个step的grad norm,看看到底是哪一步开始飘的,顺便查查有没有异常长的样本。另外AdamW的weight decay也确认下,如果设太大后期也会把参数推飞。
第5个epoch才炸,前几个epoch正常,这个时间点挺关键的。我之前也遇到过类似的,最后发现是某条样本的label越界了,前面没抽到而已,建议把embedding输入和label的范围都扫一遍。另外AdamW的eps默认1e-8,如果某些层梯度本身很小,后期累积也可能出问题,可以试试调成1e-6。还有个偏方是开torch.autograd.set_detect_anomaly(True)跑一下,虽然慢但能直接定位到出问题的算子。
试试把AdamW的eps从默认1e-8改成1e-6,后期NaN经常是它数值不稳定导致的。
试试把AdamW的eps从默认1e-8调到1e-6,后期NaN经常是它引起的。
第5个epoch才炸,前几个epoch正常,这个时间点挺关键的。我之前也遇到过类似的,最后发现是某条样本的label越界了,前几个epoch没轮到它,后来shuffle到就炸了。建议你打印一下每个step的loss,看是突然炸还是慢慢涨上去的,如果是慢慢涨可能就是梯度累积的问题。另外可以试试把GELU换成ReLU看看还炸不炸,有些实现里GELU在fp16下容易出问题。
查下学习率调度器,warmup后步进太大很容易炸,我上次就是这原因。
你这个现象还挺典型的,前几个epoch正常后面突然炸,大概率不是数据里那几个空值的问题。我之前复现BERT类模型时也遇到过几乎一样的,最后发现是某层attention的softmax在长序列上溢出,导致梯度直接爆掉。建议你在forward里挂个hook,逐层打印激活值的min/max,看看到底是哪一层先出问题的。另外AdamW的weight decay如果作用到了LayerNorm的gamma上,后期也容易把数值搞崩,可以试试把no_decay组单独拆出来。还有一点容易被忽略,就是混合精度训练时GradScaler没配好,loss scale涨到一定程度也会突然NaN,如果你开了amp先关掉排除一下。要是这些都试了还不行,可以在loss变成NaN那个step前保存一下模型输入,手动跑一遍前向看具体哪个值先变inf。这种后期才炸的情况一般跟初始化没关系,基本都是数值累积到某个临界点才触发的。
这种前几个epoch正常、后面突然NaN的情况,我遇到过一回,最后发现是某条样本的标签越界了,前面没轮到它,后面shuffle才被抽到。你可以查一下每个epoch的loss曲线,看是不是在某个batch突然炸的,如果是就重点看那个batch的数据。另外AdamW的weight decay如果作用在LayerNorm参数上,训练久了也可能把数值搞崩,可以试试把norm层的参数排除掉。还有混合精度的话,GradScaler那块也容易埋雷,可以关掉amp跑几个epoch验证一下。
试试把AdamW的eps从默认1e-8改成1e-6,后期NaN经常是它导致的。