对卷积网络来说,所学习的就是数据集的数据分布,你的卷积核参数最后形成的也是对数据集中特征分布的认知。
写这篇文章就是因为up主的邀请,然后分享一下自己工作时候总结的一些经验和技巧,不一定适用别的网络,有的还可能会有反作用,所以也就是给大家提供一个思路,欢迎拍砖吧,因为都是公司数据,分享试验结果也比较麻烦,所以大家看个思路就好。
1、预处理技巧分享
图像预处理部分调参的主要目的是对输入数据进行增强,使得网络模型在训练的过程中能更专注于目标特征部分的学习。常用的方式是图像的随机旋转、裁剪以及翻转等方式,这些方式的预处理其本质其实是为了让你的数据集更丰富,让网络能够学习到更多的分布情况,这个网上已经有很多博客了,笔者就不赘述了;另一种调整的trick是在图像上叠加信息,例如在输入数据上增加高斯噪声,椒盐噪声,从而提升网络对有干扰和成像较差情况下的目标检测能力。本文将对图像上叠加信息的调参技巧进行一定的扩展讲解。
1、为什么在原始图像上叠加信息会管用,不会破坏原有的图像信息么?
在图像上叠加信息分成两类,一类是叠加噪声,这种操作的目的是为了让网络能够适应图像质量不佳情况下的图像检测任务,在板端实测的结果,这种叠加噪声的方式也可以一定程度提高网络对输入图像中待检测目标的仿射变换的适应能力。笔者通过在输入图像上叠加一定量的高斯噪声,在Hi3516CV500上完成基于yolov3-tiny的车牌检测任务时,提高了0.5%的精度。
另一类信息的叠加是对尝试对图像上某一特定特征进行增强,该增强的目的是突出该方面的图像特征,使得网络能够首先注意到该种特征并更专注于此类特征的学习,因为这种方式只是对图像中的指定特征或位置有变动,并不会整体上对图像的结构有巨大的改变,所以并不会破坏图像的信息可读性。例如:利用canny算子对图像中的边缘特征进行增强。
2、上面说的两种调参技巧是怎么想出来的?
我们都知道,网络学习的是数据中的参数分布,何凯明大神也在retinanet的论文里提到过,数据的不平衡是影响检测网络性能的主要因素,而focal loss的提出就是让网络在学习时更专注于漏检和误分的样本。那么我们接着这个思路想,如何使得网络更专注于目标区,从而获得尽量多的价值更高的误捡和漏检样本呢?花朵吸引蜜蜂靠的是自己的香味和更鲜艳的外表,所以我们也要让目标区域更“显眼”,而平时在训练检测网络时,发现对数据集进行标注时,anchor base类算法,目标标注框比实际的物体紧缩框大大概几个像素时得到的检测结果统计精度和定位框的稳定性都会好,那么我们是不是可以认为,对anchor base的检测网络来说,目标物体的边缘也是很重要的,所以就想到了通过canny算子增强边缘的方式来增强训练数据。
3、如何在实际的网络训练中应用以上的技巧?
实际使用过程如下:
1)通过对输入数据的手动查验或自动化统计,确定较好的canny阈值
2)利用阈值对训练样本中10-20个batch的数据进行canny边缘增强。
3)增强方式为:原图转灰度提取到的canny边缘所对应的原图像素位置进行对比度增强或直接涂黑。加深程度可以由自定义的超参数alpha来指定。
4)用这10-20个batch的数据进行几个epoch的训练后再换成普通数据进行训练。
2、模型训练参数调整
讲了预训练时候的数据增强,接下来是模型训练参数部分。其实这部分网上讲的trick很多了,大家平时注意搜集一下或者github上找一找,就有很多人的练手的仓库可以跟着学。我也就不多讲了,因为我也不敢说学全了。我讲讲我自己实际跑模型时候的一些想法。
1、BFEnet特征擦除网络
这个网络是reid方向的,先讲这个是因为,这个特征擦除和上面讲到的噪声本质上有相似的地方,都是通过在训练时遮蔽一部分特征值,来让网络习惯一定量的噪声干扰,从而增强性能。这个技巧可以用在应对有遮挡的场景下的模型。
1、anchor的调整
在yolo的代码里大家肯定都看了,作者是根据你给的数据集里面,标定的目标的长和宽进行k-means的聚类,然后确定在当前这个数据集上的anchor的。我这里的经验就是,我发现有人问过我为啥我只训练一类的检测,然后重新计算的anchor6个或者9个anchor尺寸差的都不大,但是在实际检测的时候,却检测不到东西。我的结论是:对anchor的设计应该是基于模型作者默认的anchor进行微调而不是完全的重新计算。
原因:大家都知道,yolov3来说,输出是三个特征图,分别对应小目标,中目标和大目标。比如我们要检测的目标在图像中占比我们人眼感觉应该是比较大的,然后我们统计的框也都是比较大的尺寸,但是在实际训练的时候,并不是说大目标就一定由yolov3的最初设计的大目标输出层输出的。很可能就是由中间目标层输出的,而因为anchor的设计过大,导致训练的网络不收敛的有之,明明收敛了,却检测不到目标的情况也有之。
解决办法:在设计anchor的时候,首先统计目标框的分布,然后进行聚类,聚类后替换或修改原有的9个anchor中和你计算的anchor相近的几个原有的anchor值。然后再训练,如果框还是不够紧缩,再对某几个框进行精调就可以了,核心思路就是:anchor的分布也要满足对全集的稀疏覆盖而不仅仅是你的当前数据集。
2、后处理的优化
后处理的优化部分严格来说不算是网络训练的trick了,应该是部署的trick,比如海思的NPU部署的时候,会限制比较大的pool核,所以最好训练的时候就把大的pooling切换为几个小的连续pooling,实测虽然理念上两者应该是差不多的,但是实际上还是差了0.3%的精度。(指的是直接多层的pooling转换到板子和训练时是一个大的pooling,到转换时候再改结构成几个小的pooling)
还有一个就是nms部分,这部分也有同学问过我说因为我的数据集有遮挡,可能两个离的比较近的,nms就把有遮挡的那个小目标去掉了。这部分分享一个小技巧就是,你在算nms的时候,也关注一下两个框的中心点距离,可以设置中心点距离超过多少的两个框,不做nms。这样就能避免nms的一部分武断删除检测结果bbox。
3、大模型训练时的一个训练技巧
有一位同学问过我,就是为啥同样的模型,用比较少的数据训练的时候很快到了97%的MAP,但是换300w的大数据集的训练以后,卡在93%上不去了。这里面有一个技巧叫warm up,也就是说在大数据下训练模型的时候,可以先从大数据集上取一部分数据训练模型,然后以这个训练的模型为预训练模型,在大数据集上,增大batch_size再进行训练,至少没卡在93%这个问题上了。
4、学习率手动修正策略
我们训练的时候,一般都会设置学习率的衰减,有很多的方式,按已迭代步长的,按当前损失值的,按训练集当前损失值和测试集计算的损失值的gap差值做修正项的。我这里提到的技巧就是比如以步长调整学习率为例,什么时候可以靠自动化的修正学习率,什么时候要手动调整一下。
我们在训练模型的时候,一般都会关注损失函数变化曲线图,在曲线图中,数据集的稀疏程度能通过损失曲线的震荡情况有一定的反映,如果有个别的跳点,多为数据集中的坏数据(标记错误数据),当我们的损失图呈现为震荡--阶跃--在另一个损失值附近震荡时,就要注意了,此时多半是因为你的数据集在做打乱的时候数据并没有打的很散,可以在这个位置先停止训练并记录当前状态,再降低学习率,继续训练,等训练数据再次开始恢复之前的震荡位置时,再恢复学习率训练。
这样操作的原因是为了避免在参数中引入过大的噪声,噪声分两种,一种就是错误的数据,比如背景啊,像目标但是不是目标的东西,还有就是多类别训练的时候,对每个类别来说,其余类别也算是噪声的一种。所以采用要么把数据集弄好(这个很难,我也没看过谁的文章里真的能说清把训练集弄好是啥样的),要么加大batch,要么就训练时候注意。
3、总结
模型的调参训练技巧其实说白了就是怎么让模型得到的是数据全集的稀疏分布,且和别的类别有比较好的区分,也就是类内差小类间差则尽量大。以这个为核心,告诉模型应该关注什么,少关注什么,既然是数据的科学,多关注数据的分布和呈现的状态,祝大家在训练的时候都得到自己满意的结果。