前言
很多人都说训练神经网络和炼金术师炼药的过程相像,难以破解其中的黑盒子。其实不然,在训练的时候我们依然可以通过大量的技巧去最大化我们的训练效果,从而帮助我们的任务取得不错的精度,这些技巧是训练神经网络不可缺少的一环。
本文尽可能说明训练过程中所需要的各种小技巧,会有不完善的地方,限于篇幅每个点不会说很细,但是这些观念是我们都需要掌握的,牢记这几点,在训练神经网络的过程中就可以得心应手,让炼丹不再没有头绪~
避免overfitting
overfitting
即过拟合,典型的表现为训练集损失远远小于验证集损失。而欠拟合则表现为训练集损失大于验
证集损失。
我们要清楚远远大于的概念,如果训练集损失只比验证集损失多一点点的话,同等数量级(例如0.8与0.9)这种情况下并不是过拟合的表现。我们一般遇到的过拟合应该是0.8(训练集损失)与2.0(验证集损失)这种不在一个量级的损失比。
dropout与overfitting的关系
Dropout类似于bagging ensemble减少variance。也就是投通过投票来减少可变性。通常我们在全连接层部分使用dropout,在卷积层则不使用。但要声明,dropout并不适合所有的情况,请大家不要无脑上Dropout
。
Dropout
一般适合于全连接层部分,而卷积层由于其参数并不是很多,所以不需要dropout,加上的话对模型的泛化能力并没有太大的影响。如下图:
我们一般在网络的最开始和结束的时候使用全连接层,而hidden layers则是网络中的卷积层。所以一般情况,在全连接层部分,采用较大概率的dropout而在卷积层采用低概率或者不采用dropout。
进一步请看相关讨论
- https://www.reddit.com/r/MachineLearning/comments/42nnpe/why_do_i_never_see_dropout_applied_in/
- https://blog.csdn.net/stdcoutzyx/article/details/49022443
数据集是否损坏?
数据集的好坏是算法泛化好坏的一个非常重要的前提条件,我们通常在构建深度学习的任务中,所得到的数据集一般不会是非常完美的(不论是自己搭建还是利用他人的数据,在数据集量过大的时候,数据集中难免会有图像损坏或者错误的个例)。
如果有损坏的图像,如果我们不知情,一般来说,在我们使用代码读取的时候就会报错,举个例子:
raise IOError("image file is truncated) "
。
在Kaggle的一个比赛中就存在这样的情况,训练的数据中有10w+的图像数据,但是存在10余张的图像内容缺失(图像大小明显小于其他正常图像)或者直接被损坏无法读取。这个时候就需要我们自己去手动编写代码将那些错误且无法参与训练的图像挑选出来。
那么如果正确过滤这些图像呢?
- 找到损坏图像的特点,例如无法读取,编写程序去除无法读取的图像
- 找到内容缺失的图像,这样的图像大小往往比普通图像小一些,通过过滤文件大小小于某一阈值去除
当然,数据集损坏的形式还有很多,需要我们自己去发掘。在一开始得到数据集的时候最好不要直接开始训练,应该去仔细检查自己的数据集是否有问题,这样可以避免很多之后训练时需要的处理的麻烦。
难例挖掘 hard-negative-mining
在任何一个深度学习任务中,我们都会遇到一些比较“棘手”的数据,这些数据相比较于其他的普通数据更难识别,这种特比容易识别错误的例子就称为hard-negative
。
举个栗子。
比如Kaggle比赛中的一个识别遥感图像中船只的任务,使用的图像集是从一张大的遥感图中裁剪出来的,每张图的大小为768*768,在简单地对图像进行分类时(仅仅分类图像中有无船只),在validation中发现最容易识别出错的图如下:
在观察到这些最难办的图像的特征后,我们可以针对这些难办的图像采取一些方法来解决这些问题。我们先用初始的正负样本(一般是正样本+与正样本同规模的负样本的一个子集)训练分类器, 然后再用训练出的分类器对样本进行分类, 把其中负样本中错误分类的那些样本(hard negative)放入负样本集合, 再继续训练分类器, 如此反复, 直到达到停止条件(比如分类器性能不再提升)。也就是不停滴将困难样本拿去训练,让分类器更好地学习到难以学习的特征,简单来说就是熟能生巧嘛。
而在fast-rcnn中,这个方法也被使用了:
在 Fast-RCNN 中将与 groud-truth 的 IoU 在 [0.1, 0.5) 之间的图像标记为负例,[0, 0.1)的example 用于hard negative mining.在训练时一般输入为N=2张图片, 选择128个RoI,即每张图片64个RoI。对于每张图片, 按照1:3的比例来抽取RoI, 抽取正负样本的比例为1:3,要在负例中抽取48个。
相关的两篇文章可以看一下
[17] P. Felzenszwalb, R. Girshick, D. McAllester, and D. Ramanan. Object detection with discriminatively trained part based models. TPAMI, 2010.
[37] K. Sung and T. Poggio. Example-based learning for viewbased human face detection. Technical Report A.I. Memo No. 1521, Massachussets Institute of Technology, 1994.
关于学习率
寻找合适的学习率(learning rate)
学习率是一个非常非常重要的超参数,这个参数呢,面对不同规模、不同batch-size、不同优化方式、不同数据集,其最合适的值都是不确定的,我们无法光凭经验来准确地确定lr
的值,我们唯一可以做的,就是在训练中不断寻找最合适当前状态的学习率。
比如下图利用fastai中的lr_find()函数寻找合适的学习率,根据下方的学习率-损失曲线得到此时合适的学习率为1e-2
。
如果想要了解更多,这里推荐一篇fastai首席设计师Sylvain Gugger的一篇博客:How Do You Find A Good Learning Rate
以及相关的论文Cyclical Learning Rates for Training Neural Networks。
learning-rate与batch-size的关系
一般来说,越大的batch-size使用越大的学习率。
原理很简单,越大的batch-size
意味着我们学习的时候,收敛方向的confidence
越大,我们前进的方向更加坚定,而小的batch-size
则显得比较杂乱,毫无规律性,因为相比批次大的时候,批次小的情况下无法照顾到更多的情况,所以需要小的学习率来保证不至于出错。
可以看下图损失Loss
与学习率Lr
的关系:
上图来源于这篇文章:Visualizing Learning rate vs Batch size
当然我们也可以从上图中看出,当batchsize变大后,得到好的测试结果所能允许的lr范围在变小,也就是说,当batchsize很小时,比较容易找打一个合适的lr达到不错的结果,当batchsize变大后,可能需要精细地找一个合适的lr才能达到较好的结果,这也给实际的large batch分布式训练带来了困难。
所以说,在显存足够的条件下,最好采用较大的batch-size进行训练,找到合适的学习率后,可以加快收敛速度。另外,较大的batch-size可以避免batch normalization出现的一些小问题:https://github.com/pytorch/pytorch/issues/4534
更多类似的问题可以在知乎找到相关答案:https://www.zhihu.com/question/64134994/answer/216895968
差分学习率与迁移学习
首先说下迁移学习,迁移学习是一种很常见的深度学习技巧,我们利用很多预训练的经典模型直接去训练我们自己的任务。虽然说领域不同,但是在学习权重的广度方面,两个任务之间还是有联系的。
由上图,我们拿来model A训练好的模型权重去训练我们自己的模型权重(Model B),其中,modelA可能是ImageNet的预训练权重,而ModelB则是我们自己想要用来识别猫和狗的预训练权重。
那么差分学习率和迁移学习有什么关系呢?我们直接拿来其他任务的训练权重,在进行optimize的时候,如何选择适当的学习率是一个很重要的问题。
一般地,我们设计的神经网络(如下图)一般分为三个部分,输入层,隐含层和输出层,随着层数的增加,神经网络学习到的特征越抽象。因此,下图中的卷积层和全连接层的学习率也应该设置的不一样,一般来说,卷积层设置的学习率应该更低一些,而全连接层的学习率可以适当提高。
这就是差分学习率的意思,在不同的层设置不同的学习率,可以提高神经网络的训练效果,具体的介绍可以查看下方的连接。
上面的示例图来自:https://towardsdatascience.com/transfer-learning-using-differential-learning-rates-638455797f00
余弦退火(cosine annealing)和热重启的随机梯度下降
余弦就是类似于余弦函数的曲线,退火就是下降,余弦退火就是学习率类似余弦函数慢慢下降。
热重启就是在学习的过程中,学习率慢慢下降然后突然再回弹(重启)然后继续慢慢下降。
两个结合起来就是下方的学习率变化图:
更多详细的介绍可以查看知乎机器学习算法如何调参?这里有一份神经网络学习速率设置指南
以及相关论文SGDR: Stochastic Gradient Descent with Warm Restarts
权重初始化
权重初始化相比于其他的trick来说在平常使用并不是很频繁。
为什么呢?
原因很简单,因为大部分人使用的模型都是预训练模型,使用的权重都是在大型数据集上训练好的模型,当然不需要自己去初始化权重了。只有没有预训练模型的领域会自己初始化权重,或者在模型中去初始化神经网络最后那几个全连接层的权重。
那么大家喜欢用什么初始化权重算法?
当然是kaiming_normal或者xavier_normal。
相关论文:
Delving deep into rectifiers: Surpassing human-level performance on ImageNet classification
Understanding the difficulty of training deep feedforward neural networks
多尺度训练
多尺度训练是一种直接有效的方法,通过输入不同尺度的图像数据集,因为神经网络卷积池化的特殊性,这样可以让神经网络充分地学习不同分辨率下图像的特征,可以提高机器学习的性能。
也可以用来处理过拟合效应,在图像数据集不是特别充足的情况下,可以先训练小尺寸图像,然后增大尺寸并再次训练相同模型,这样的思想在Yolo-v2的论文中也提到过:
需要注意的是:多尺度训练并不是适合所有的深度学习应用,多尺度训练可以算是特殊的数据增强方法,在图像大小这一块做了调整。如果有可能最好利用可视化代码将多尺度后的图像近距离观察一下,看看多尺度会对图像的整体信息有没有影响,如果对图像信息有影响的话,这样直接训练的话会误导算法导致得不到应有的结果。
关于优化算法
按理说不同的优化算法适合于不同的任务,不过我们大多数采用的优化算法还是是adam和SGD+monmentum。
这两个为什么好用?
经验呗~
尝试过拟合一个小数据集。
这是一个经典的小trick了,但是很多人并不这样做,可以尝试一下。
关闭正则化/随机失活/数据扩充,使用训练集的一小部分,让神经网络训练几个周期。确保可以实现零损失,如果没有,那么很可能什么地方出错了。
Cross Validation 交叉验证
在李航的统计学方法中说到,交叉验证往往是对实际应用中数据不充足而采用的,基本目的就是重复使用数据。在平常中我们将所有的数据分为训练集和验证集就已经是简单的交叉验证了,可以称为1折交叉验证。注意,交叉验证和测试集没关系,测试集是用来衡量我们的算法标准的,不参与到交叉验证中来。
交叉验证只针对训练集和验证集。
交叉验证是Kaggle比赛中特别推崇的一种技巧,我们经常使用的是5-折(5-fold)交叉验证,将训练集分成5份,随机挑一份做验证集其余为训练集,循环5次,这种比较常见计算量也不是很大。还有一种叫做leave-one-out cross validation
留一交叉验证,这种交叉验证就是n-折交叉,n表示数据集的容量,这种方法只适合数据量比较小的情况,计算量非常大的情况很少用到这种方法。
吴恩达有一节课The nuts and bolts of building applications using deep learning中也提到了。
关于数据集
如果数据集极其不平衡,假如我们有个检测船只的任务,在100000图中只有30000张图中含有船只,其余图像都是不含船只的图像,这时候如果对此直接进行训练效果应该会很差。为此,首先可以挑选出只有船只的图像集合进行初步训练。其次,使用训练好的模型去检测没有训练的部分(不含船只),挑选出检测失败的false positive(也就是在没有船的图像集中检测出船只了)加入之前的训练集再次进行训练。
数据增强
数据集增强是一个老生常谈的话题了,我们为什么需要那么多数据?为什么需要数据增强技术?
可以看这篇文章来了解一下:深度学习为什么需要那么多的数据?
这里只简单说下我们常用的数据增强的Transform
。
大部分我们使用的图像增强技术一般是随机旋转,水平翻转,高斯模糊和尺度变化还有什么拉伸等blabla的操作。这些图像变化对大部分的任务是比较适合的,但针对特定的任务,存在某一簇特殊的图像增强技术可以达到比普通图像增强技术更好的效果。
例如夜视图或者光照:
但也需要提个醒,并不是所有的图像增强都可以提升模型的泛化能力。
而且有些图像增强技术会对原始图像造成损失从而导致神经网络学习到错误的信息,这点是我们比较容易忽视的问题,同样重要,相关内容可以查看fastai中的图像增强技术为什么相对比较好。
TTA(Test Time Augmentation)
最初这个概念是在fastai课程中看到的,这个过程在训练阶段不会参与,是通过在验证和测试阶段进行的。具体过程是,对所要处理的图像进行几种随机的图像增强变化,然后对每种图像增强后的图像进行预测,对预测结果取平均值。
原理类似于模型平均,牺牲推断速度来实现推断精度的提升。
当然,这个技术也有好有坏,在我自己跑的卫星图数据集中采用TTA的精确度比不采用低了0.03个百分点。
结语
大概先说这么多,训练神经网络真的很像炼丹,而我们所有使用的技巧tricks,也只是增加一些炼丹的成功率,而究竟炼丹能不能成功,最终还是要取决于我们设计的算法合不合理。
参考文章:
https://www.cnblogs.com/nowgood/p/Hardexamplemining.html
https://towardsdatascience.com/kaggle-planet-competition-how-to-land-in-top-4-a679ff0013ba