如何提高卷积神经网络模型的泛化能力
在做工程的时候如何提高自己训练出来的模型的泛化能力是一项具有挑战性同时也是一件充满”玄学”的事情。回想我这一年半载训练的那么几个任务的调参来讲,大概可以总结为下面这几点。
1、使用更多的数据。竟可能标注更多的训练数据,这是提高泛化能力最理想的方法,更多的数据让模型得到更充分的学习,自然提高了泛化能力,但实际场景中考虑到标注成本的问题,可能并不能无脑加数据。
2、使用更大的batch_size。在相同迭代次数和学习率的条件下,每批次采用更多的数据将有助于模型更好的学习到正确的模式,模型输出结果也会更加稳定。
3、数据过采样。很多情况下我们拿到手的数据都存在类别不均匀的情况,模型这个时候过多的拟合某类数量多的数据导致其输出结果偏向于该类数据,此时如果我们过采样其他类别的数据,使得数据量比较均衡可以一定程度提高泛化能力。
4、数据增强。数据增强是指在数据有限的情况通过一些几何操作对图像进行变换,使得同类数据的表现形式更加丰富,以此提高模型的泛化能力。数据增强是一门比较大的学问,在分类,检测,分割中数据增强的方式都有区别,我们可以通过研究优秀的开源代码实现的数据增强策略来应用到我们自己的任务中。
5、修改损失函数。这方面有大量的工作,如目标检测中的Focal Loss, GHM Loss,IOU Loss等都是为了提升模型的泛化能力。
修改网络。如果网络过浅并且参数量过少往往会使得模型的泛化能力不足导致欠拟合,此时一般考虑使用简单的堆叠卷积层增加网络的参数,提高模型的特征提取能力。而如果网络过深且训练数据量比较少,那么就容易导致模型过拟合,此时一般需要简化网络结构减少网络层数或者使用resnet的残差结构以及bn层。
6、权重惩罚。权重惩罚也即是正则化操作,一般是在损失函数中添加一项权重矩阵的正则项作为惩罚项,用来惩罚损失值较小时网络权重过大的情况,此时往往是网络权值过拟合了数据样本。
7、Dropout策略。如果网络最后有全连接层可以使用Dropout策略,相当于对深度学习模型做了Ensemble,有助于提高模型的泛化能力。
一些调参技巧
本节建立在CV方向。
做工程
1、3×3卷积是CNN的主流组件。平时有设计一些解决分类,回归任务的网络,里面的卷积核基本都设置为,要说原因的话应该去问问VGG16吧。两个的卷积核堆叠能获得卷积核的感受野并且参数比卷积核少,所以是大量推荐使用的。
2、可以适当使用1xN卷积。为什么要提这一点呢,这是因为1xN卷积可以减少计算量,并且1xN卷积可以在某个方向强调感受野,也就是说假如如果你要对一个长方形形状的目标进行分类,你可以使用的1xN卷积核搭配的3×3卷积核对长边方向设定更大的感受野,或许可以获得泛化性能的提升。
3、ACNet结构。这个研究来自于ICCV2019,可以在卷积的基础上加上和的旁路卷积核,最后在推理阶段把三个卷积核都fusion到卷积核上,在许多经典CV任务上都可以获得大概1个点的提升。
4、卷积核权重初始化方式。对于weight的初始化我一般都是使用xavier初始化。当然也可以可以尝试何凯明大神的He初始化。对于bias的初始化全置于0。
5、Batch Normalization。这是我一直在使用的技巧,可以很大程度的加快收敛速度。建议搭建自己网络的时候尽量加上BN,如果有BN了全连接层就没必要加Dropout了。
6、目标检测不能盲目去掉fpn结构。在针对自己的数据调检测任务如yolov3的时候不能盲目砍掉fpn结构,尽管你分析出某个分支的Anchor基本不可能会对你预测的目标起作用,但如果你直接去掉分支很可能会带来漏检。
7、优化器的选择。我基本都是带动量的SGD。如果优化不动可以试试Adam。
8、激活函数。可以先用ReLU做一版,如果想再提升精度可以将ReLU改成PReLU试试。我更倾向于直接使用ReLU。
9、batch_size:在不同类型的任务中,batch_size的影响也不同。
10、初始学习率。一般我是从0.01开始设置,我个人认为这个学习率和学习率衰减策略是相关的,但不宜设置的过大过小,0.01和0.1应该是比较常用的。学习率衰减策略我一般使用multistep方式,step_size的设置要看视你的的max_iter而定。
11、数据与处理之zero-center。第一次见到这个词是在看cs231n的视频上。主要有2个步骤,第一个是减均值,第二个是除以方差。这样做下来最后的输入值域为[-1,1],一般减均值是最常用的,后面的除以方差用不用可能需要自己动手试验一下看看效果。
12、残差结构和密集连接。resnet的残差结构和dense net密集连接结构,做工程的时候考虑到速度近乎不可能说完全使用完整版本的resnet和densenet的完整结构,但我们可以自己动手将我们网络的某些模块替换为残差结构和密集连接,替换的时候可以适当降低这俩结构的复杂度,类似于通道数减半,密集连接中只保留一半连接等等。这里需要做一些消融实验来验证改进后的精度。
13、关于loss。优秀的loss一般是对模型的泛化性能有所改善的,但在用loss的时候往往并不是直接替换loss那么简单,需要仔细思考loss背后的数学原理,要用对地方才可有提升。例如,如何将Focal Loss用到YOLOv3中提升map(https://mp.weixin.qq.com/s?__biz=MzI1NjQ0Mzc1Mw==&mid=2247496764&idx=1&sn=ea372deaf47a6fe8e824299387a95890&source=41#wechat_redirect)。
14、找到模型调参时的可靠评价指标。在调整参数训练模型时一定要找到正确的评价指标,没调整一个参数就要记录一下模型的评价指标如准确率,map值,miou值等。并且在调参时建议将调整的参数和在测试集上的精度组合成一个字符串给模型重命令,方便之后快速review。
15、使用了带backbone的网络,如训练VGG16-SSD建议选择finetune的方式,从头训练不仅费时费力,甚至难以收敛。
做比赛
特征提取。VGG16,VGG19,ResNet50,Xception是非常好用的几个特征提取模型。建议使用训练好的经典模型对数据集提取特征向量存储到本地,更方便使用,同时可以大幅度降低显存消耗。
ensemle:
将不同的经典网络提取出的特征向量,假设VGG16提取出的特征向量维度是[N,c1],ResNet50提取的特征向量维度是[N,c2],Xception提取的特征向量维度是[N, c3],那么我们可以使用三个系数a、b、c将其组合为形状为[N, ac1+bc2+c*c3],其中a、b、c三个参数的取值代表我们使用哪个模型的特征多一些,如果是分类回归比赛,我们在后面接特征处理网络就可以了。可以取不同的a、b、c得到不同的特征,然后对结果做voting,soft-voting等多种处理,一般结果不会太差啦。
可以使用不同的初始化方式训练出模型,然后做ensemble。
可以使用用不同超参数(如学习率,batch_size,优化器)训练出不同模型,然后做ensemble。