1.背景
训练时忽然发现某几项loss变成了nan。
2.nan的含义
nan值在python往往可以直接与无穷大,无穷小等价。
常见根本来源:
a/0
log(0)
空索引
第一个常见例子: l o s s / p o s n u m loss / posnumloss/posnum,希望根据正例个数平分loss,却忽视pos_num可能为0,也就是图片中没有正例,常见解决l o s s / ( 0.0001 + n u m ) loss/(0.0001 + num)loss/(0.0001+num)或者if判断;
第二个常见例子:loss计算中带有log( p)时,p却为0,后果就是反传梯度无穷大;
第三个常见例子:mask = gt>0, 然后索引 pos_p = pred[mask],却不知此时的pos_p为空;
3.解决
原因:上面第二条
debug
当出现nan后在模型forward函数上打断点,结果发现在第一步时就出现了nan,正常的x输入后出来就有nan值。
def forward(self, x):
x = self.base_layer(x)
...
而self.base模块只是简单的卷积模块:
self.base_layer = nn.Sequential(
nn.Conv2d(3,self.channels[0], kernel_size=7, stride=1,padding=3,bias=False),
nn.BatchNorm2d(self.channels[0],momentum=0.1),
nn.ReLU(inplace=True)
)
再打印出它的学习参数:
for p in self.base_layer.parameters():
print(p)
结果发现大部分参数已经是nan了,而这里只是整个模型第一层…
显然梯度爆炸了,自然检查去loss。
重新开始,在总的loss处debug:
if(torch.isnan(loss).sum()>0):
print("here!")
当首次出现loss时在此处断点,结果发现,nan来源某一项loss,我这里是focal loss,显然是来自里面的log(p ),而p,也就是网络预测为0了,本该p ∈ [ 0 , 1 ] p\in[0, 1]p∈[0,1],怎么会有0?
最后想起是Sigmoid函数:
z['hm'] = z['hm'].sigmoid() * out_branch
最后调整为:
z['hm'] = torch.clamp(z['hm'].sigmoid(),min=1e-4,max=1-1e-4) * out_branch
版权声明:本文为qq_31787603原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。