问题来源:多标签的Pytorch实现
最近关注多标签学习,发现网上的开源代码基本都是keras实现的,比如TinyMind的多标签竞赛方法等,由于本人习惯于Pytorch,希望能够使用Pytorch实现,因此复现Pytorch版本,至此困惑即开始了。
问题:学习不收敛
根据自己的经验,搭建网络来学习TinyMind竞赛数据,并参考了网上的建议,采用二值交叉熵损失(Pytorch中BCELoss底层调用的是binary_cross_entropy),不幸的是网络始终不收敛,且预测F1指标在百分之十几不动了,所有图像的预测输出始终是固定的几个值。
解决:
1、分析数据,发现标签正负样本分布极其不均横,个人分析可能是数据分布不均的问题;但是,在这之前我用(竞赛第2名方案)复现,发现他们的方法是收敛的,且非常好。这就说明和数据是无关的。
2、我对比了个人实现代码和竞赛第2名方案,并逐一修改,使过程一致,但是始终学习训练不收敛,这就很尴尬了。最后,对损失前的输出打印发现两个基本都是一样的。最后我就怀疑上了损失函数的不同,因此,从Pytorch损失库中把所有可能的损失都拿出来,发现只有BCELoss对应的是binary_cross_entropy,与Keras调用tensorflow中的binary_crossentropy应该是一致的。本着怀疑的态度,我搜了一个google,竟然有人说tf.binary_crossentropy不是原始的方法,所以我直接怀疑BCELoss中的binary_cross_entropy估计也是不一样的,所以索性对着损失函数自己写,实验证明BCELoss还真的不是想象中的那样,真心愤怒啊!
3、更尴尬的是,发现有人论证了Pytorch的torch.nn.BCELoss()=F.binary_cross_entropy()是没错的,这就实在是尴尬的不行了。最后,对比发现BCELoss中有个参数reduction,其求平均的方法是对所有正式标签个数求平均,而非对样本个数求平均,因此,找到解决问题的关键所在——即令BCELoss中参数reduction=‘none’,然后自己对损失求和算样本平均。