Pytorch——fine-tune相关经验总结

最近几天在参加AI研习社的一个美食识别比赛,比赛方提供了6140张图片的训练集,856张图片的测试集。其中测试集没有标签,只用来生成预测数据进行提交。
任务难度不是很高,但是在做的过程中还是遇到了一些问题,有一些经验值得总结,这里主要记录一下在模型fine-tune中的一些经验教训。

1.模型选择

由简单到复杂,先后选择了resnet50、resnet101、resnext50_32x4d、resnext101_32x8d。
这些模型中,前两个在验证集上的acc在到达94%后就基本上不去了(也可能是我超参不合适没有到最佳性能),resnext50_32x4d的acc能够到达95%,而resnext101_32x8d比较轻松的就到达了96%(还没有训练完,可能会更高)。
另外还没有测试google出品的SEnet,有结果来再来补充。

2. 全连接层的设置

通用方法

按照pytorch官方的fine-tune教程以及网上搜到的大多数教程,fine-tine的方法如下:

model_ft = models.resnet50(pretrained=True) # 这里自动下载官方的预训练模型,并且将所有的参数层进行冻结
num_fc_ftr = model_ft.fc.in_features #获取到fc层的输入
model_ft.fc = nn.Linear(num_fc_ftr, n_classes) # 定义一个新的FC层

替换掉原网络的FC层,然后自己接一个新的,输出由自己任务的分类数决定。
存在的问题:
原本以为这样足够了,但是发现这样构成的网络,在验证集上的acc最多只有93%,而且训练集的loss最终也只是收敛到了一个比较高的数值,可以确定是欠拟合了。分析网络结构以后,发现在最后一个卷积层后接了一个池化层,然后就是我们添加的FC层,因此后续改进时我主要提高FC层的复杂度。

我采用的解决方法

这样参考了Fastai中对模型fine-tune时默认添加的head模块,即将原模型的FC层替换为如下所示的模块:

num_fc_ftr = model.fc.in_features #获取到fc层的输入
add_block = nn.Sequential(nn.Flatten(),
                          nn.BatchNorm1d(num_fc_ftr, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True),
                          nn.Dropout(p=0.25, inplace=False),
                          nn.Linear(in_features=num_fc_ftr, out_features=2048, bias=True),
                          nn.ReLU(inplace=True),
                          nn.BatchNorm1d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True),
                          nn.Dropout(p=0.5, inplace=False),
                          nn.Linear(in_features=2048, out_features=n_classes, bias=True))
model.fc = add_block

如上所示,一个block包含(Flatten、BatchNormal1d、Dropout、Linear、ReLU)几个部分,可以添加多个block,其中Linear层的输出个数是超参。需要注意的是最后一个block的激活函数可以不要,或者改为log_softmax。
经过这样改造的FC层,能够显著提高模型在验证集上的性能。

3. 训练中出现的问题

fine-tune时需要固定FC层之前的所有参数,只训练FC层的参数。在这里一开始我应该是犯了一些错误。
最初我是这么实现的:

model_ft = models.resnet50(pretrained=True) # 这里自动下载官方的预训练模型,并且
# 将所有的参数层进行冻结
for param in model_ft.parameters():
    param.requires_grad = False
# 这里打印下全连接层的信息
num_fc_ftr = model_ft.fc.in_features #获取到fc层的输入

一个for循环把网络的所有参数求梯度的flag都设为了False,这里面也包含了FC层的。后来发现训练不太正常,loss下降的特别慢。可能就是这里FC层requires_grad = False的原因。
后来改为下面的实现方式:

model = models.resnext101_32x8d(pretrained=True)
for name, child in model.named_children():
    if 'fc' not in name:
        for param in child.parameters():
            param.requires_grad = False
    else:
        for param in child.parameters():
            param.requires_grad = True
num_fc_ftr = model.fc.in_features #获取到fc层的输入

实现略为繁琐,但是效果是达到了,训练也变得正常了。

未完待续。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值