本来该比赛博主卷到写上一篇博文0.97123是不打算在卷了的,事情的起因也很简单博主4月底参加这个比赛的时候提交了结果,成绩0.96689很一般。不料,到了月底有人(202105-202204月份的全时段第一名)又把3月份的结果(成绩0.96954)提交了上来,让博主的排名下来一位。于是,博主在5月份力争在5月份把结果做到0.97。于是,在5月9号有了这篇博文(常规赛:PALM眼底彩照视盘探测与分割202105-202205全时段第二名(得分0.97123)方案_万里鹏程转瞬至的博客-CSDN博客)。当时的标题是全时段第一名,但是几天后又被上月的第一名卷下去了。于是,把博文标题改成了第二名。看来是博主当时得意的太早了。
于是,在好几天的卷王争锋中,top1的得分从0,971到0.972。在这阶段中博主基于HardNet 最多也只能把得分做到0.972,而对手把分数干到了0.97337。对于HardNet和该比赛基本上放弃了。后来又是手闲,把模型换成了SegFormer系列,权衡系列性能与flop后选用了SegFormerB2。SegFormerB2的训练比HardNet 更加费时,因此在有限的训练资源下一直没出啥效果。最佳的得分也不过是第二名,本来欲放弃挣扎了。结果,在5月20几好不容易整来的全时段第二名,在5.30日又被人搞成了第三名。于是,争一口。博主不配搞个第一名么?因此选择模型,增加分辨率,继续训练,终于一举突破0.97337,干到了全时段的第一名。这篇博客写出来后,也许在以后的时间会被人超越,但起码在202105-202205全时段是第一名(因为,现在离5月份结束只剩最后3小时了)。
话不多说,开始分享得分方案。博主一直觉得,模型精度要高,必须得自己写代码训练模型,不能使用现成的二次封装框架(比如:paddleseg)。想必这也是前面11个月里面一直没有人能把成绩赶到0.97的原因吧。
1、基本训练配置
1.1 基本训练配置
选用的网络:SegFormer
数据集划分:8:2
学习率:0.0005
优化器:AdamW
loss:[loss_CrossEntropyLoss,loss_LovaszSoftmaxLoss,loss_DiceLoss],loss_weights=[0.3,0.3,0.4]
在多loss的训练配置下,博主发现loss的权重对训练效果的影响较小。同时,在进行分辨率多阶段迭代时,学习率应该按阶段减少。
1.2 训练涨点技巧
1、训练时random size:提升模型泛化能力
2、为模型添加dropout与dropblock:提升模型拟合能力,同时降低过拟合
3、使用cutmix进行数据增强:提升模型拟合能力
4、逐步增强的数据增强方式:加快训练过程
5、保存多个最优模型:避免单一指标下错失最优模型
6、多尺度预测试(非数据融合,得出最佳测试尺寸):避免单一分辨率下错失最佳测试尺寸
7、多分辨率迭代(逐步增大分辨率):不断提升模型精度上限
8、MCdropout:多次测试取平均值
2、具体得分记录
使用SegFormerB2最大的感受就是,模型收敛慢,而且训练周期较长。同时针对于逐步增强的数据增强方式,需要更加细腻的考虑。博主一开始设置的是各阶段的学习周期都是一样的,后面考虑到数据的泛化能力,增加了norm、diff两个阶段的训练周期(其中norm指一般的数据增强【仿射变化、色彩变化】、diff【仿射变化、色彩变化、扭曲抖动】)。
if epoch==Epochs_frozen*1:
trainer.init_dataloader("norm","norm",mix_mode="cutmix",random_size=random_size)
elif epoch==Epochs_frozen*3:
trainer.init_dataloader("random_size","easy",mix_mode="cutmix",random_size=random_size)
#trainer.reset_head()
elif epoch==Epochs_frozen*4:
trainer.init_dataloader("random_mask","easy",mix_mode="cutmix",random_size=random_size)
elif epoch==Epochs_frozen*5:
trainer.init_dataloader("norm","easy",mix_mode="cutmix",random_size=random_size)
elif epoch==Epochs_frozen*7:
trainer.init_dataloader("diff","easy",mix_mode="None",random_size=random_size)
#trainer.reset_head()
elif epoch==Epochs_frozen*9:
trainer.init_dataloader("easy","easy",mix_mode="None",random_size=random_size)
2.1 0.97265时记录
该得分的训练尺寸为800*800,模型为SegFormer_B2。训练了近乎12个小时,得到了多个模型,后面不知怎么滴只剩下这两个。在这里博主并没有通过多尺度测试的方式选择最佳模型和size,而是随意测试。最后模型n800_PALM_SegFormer_B2_ep214_0.0234_0.9593.pdparams在size为896的情况下得到了0.97265的最佳成绩。此时,才将成绩突破到第二名。
n800_PALM_SegFormer_B2_ep214_0.0234_0.9593.pdparams n800_PALM_SegFormer_B2_ep180_0.0223_0.9593.pdparams
但是,30号下午一看,第二名变成了第三名。于此开始了第二阶段训练
2.2 0.97387时记录
以n800_PALM_SegFormer_B2_ep214_0.0234_0.9593.pdparams为基本模型,在896的size上继续训练数据。同样训练了近乎14个小时。得到了多个模型,具体如下图所示
然后将选中的4个模型进行稳定性测试,具体代码细节如下所示
shape_list=[x for x in range(896,1056,32)]
modes=["easy","test"]#"n3_PALM_HarDNet_ep230_0.0240_0.9564.pdparams",
model_paths=[
#train on 864
"n896_PALM_SegFormer_B2_ep189_0.0185_0.9663.pdparams",
"n896_PALM_SegFormer_B2_ep258_0.0189_0.9645.pdparams",
"n896_PALM_SegFormer_B2_ep273_0.0183_0.9649.pdparams",
"n896_PALM_SegFormer_B2_ep280_0.0190_0.9632.pdparams",
]
for path in model_paths:
tag="mymodel/"
model_path=tag+path
trainer.load_model(model_path,eval_model=False)
log_print("\n\n\n"+model_path,log_name)
for shape in shape_list:
for mode in modes:
trainer.input_shape=(shape,shape)
times=5
if mode=="test":
times=1
for i in range(times):
trainer.init_dataloader(mode,mode,mix_mode="none")
trainer.eval_model(log=False)
log_print("shape:%s mode:%s Val loss: %.4f, map: %.4f, iou: %.4f"%(shape,mode,trainer.val_loss,trainer.val_acc,trainer.val_iou),log_name)
经过测试后得到一堆性能数据,从数据中分析发现是ep280模型的性能最为稳定,size 896时最稳定。因此,就以该模型进行测试,得分数据如下所示。
928->(null-18)->96.997 896(null-17)->97.164 1024(null-16)->0.97257 1088(null-15)0.97381 1120(null-15)0.97356
load_weight = paddle.load("mymodel/n896_PALM_SegFormer_B2_ep280_0.0190_0.9632.pdparams");
为了更上一步,使用了MCDropout(测试13次取平均值)继续进行测试,得分数据如下所示。
1088(null-15)0.97387 1088(null-15)0.97373 1152(null-14)0.97277 1184(null-15)0.97027 1088(null-15,7次)0.97387
此时,成绩达到了最高点0.97387。
不过,从效果上看,MCDropout的性能并不稳定,对于比分提升有限。