14.3 网络训练中的超参调整策略
14.3.1 如何调试模型
在讨论如何调试模型之前,我们先来纠正一个误区。通常理解如何调试模型的时候,我们想到一系列优秀的神经网络模型以及调试技巧。但这里需要指出的是数据才是模型的根本,如果有一批质量优秀的数据,或者说你能将数据质量处理的很好的时候,往往比挑选或者设计模型的收益来的更大。那在这之后才是模型的设计和挑选以及训练技巧上的事情。
1、探索和清洗数据。
探索数据集是设计算法之前最为重要的一步,以图像分类为例,我们需要重点知道给定的数据集样本类别和各类别样本数量是否平衡,图像之间是否存在跨域问题(例如网上爬取的图像通常质量各异,存在噪声)。
若是类别数远远超过类别样本数(比如类别10000,每个类别却只有10张图像),那通常的方法可能效果并不显著,这时候few-shot learning或者对数据集做进一步增强可能是你比较不错的选择。
再如目标检测,待检测目标在数据集中的尺度范围是对检测器对性能有很大影响的部分。因此重点是检测大目标还是小目标、目标是否密集完全取决于数据集本身。所以,探索和进一步清洗数据集一直都是深度学习中最重要对一步。这是很多新手通常会忽略的一点。
2、探索模型结果。
探索模型的结果,通常是需要对模型在验证集上的性能进一步的分析,这是如何进一步提升模型性能很重要的步骤。将模型在训练集和验证集都进行结果的验证和可视化,可直观的分析出模型是否存在较大偏差以及结果的正确性。
以图像分类为例,若类别间样本数量很不平衡时,我们需要重点关注少样本类别在验证集的结果是否和训练集的出入较大,对出错类别可进一步进行模型数值分析以及可视化结果分析,进一步确认模型的行为。
3、监控训练和验证误差。
首先很多情况下,我们忽略代码的规范性和算法撰写正确性验证,这点上很容易产生致命的影响。
在训练和验证都存在问题时,首先请确认自己的代码是否正确。其次,根据训练和验证误差进一步追踪模型的拟合状态。若训练数据集很小,此时监控误差则显得格外重要。
确定了模型的拟合状态对进一步调整学习率的策略的选择或者其他有效超参数的选择则会更得心应手。
4、反向传播数值的计算。
这种情况通常适合自己设计一个新操作的情况。目前大部分流行框架都已包含自动求导部分,但并不一定是完全符合你的要求的。
验证求导是否正确的方式是比较自动求导的结果和有限差分计算结果是否一致。所谓有限差分即导数的定义,使用一个极小的值近似导数。
14.3.2 为什么要做学习率调整
学习率可以说是模型训练最为重要的超参数。通常情况下,一个或者一组优秀的学习率既能加速模型的训练,又能得到一个较优甚至最优的精度。过大或者过小的学习率都会直接影响到模型的收敛。
我们知道,当模型训练到一定程度的时候,损失将不再减少,这时候模型的一阶梯度接近零,对应Hessian矩阵通常是两种情况:
一、正定,即所有特征值均为正,此时通常可以得到一个局部极小值,若这个局部极小值接近全局最小则模型已经能得到不错的性能了,但若差距很大,则模型性能还有待于提升,通常情况下后者在训练初最常见。
二、特征值有正有负,此时模型很可能陷入了鞍点,若陷入鞍点,模型性能表现就很差。
以上两种情况在训练初期以及中期,此时若仍然以固定学习率,会使模型陷入左右来回的震荡或者鞍点,无法继续优化。所以学习率衰减或者增大能帮助模型有效的减少震荡或者逃离鞍点。
14.3.3学习率调整策略有哪些
通常情况下,大部分学习率调整策略都是衰减学习率,但有时若增大学习率也同样起到奇效。这里结合TensorFlow的内置方法来举例。
1、exponential_decay 和 natural_exp_decay
代码语言:javascript复制exponential_decay(learning_rate, global_step, decay_steps, decay_rate,
staircase=False, name=None)
natural_exp_decay(learning_rate, global_step, decay_steps, decay_rate,
staircase=False, name=None)
指数衰减是最常用的衰减方式,这种方式简单直接,在训练初期衰减较大利于收敛,在后期衰减较小利于精调。以上两种均为指数衰减,区别在于后者使用以自然指数下降。
2、piecewise_constant
代码语言:javascript复制piecewise_constant(x, boundaries, values, name=None)
分段设置学习率法,跟指数型类似,区别在于每个阶段的衰减并不是按指数调整。可在不同阶段设置手动不同的学习率。这种学习率重点在有利于精调。
3、polynomial_decay
代码语言:javascript复制polynomial_decay(learning_rate, global_step, decay_steps,
end_learning_rate=0.0001, power=1.0,
cycle=False, name=None)
多项式衰减,计算如下:
有别于上述两种,多项式衰减则是在每一步迭代上都会调整学习率。主要看power参数,若power为1,则是下图中的红色直线;若power小于1,则是开1/power次方,为蓝色线;绿色线为指数,power大于1。
此外,需要注意的是参数cycle,cycle对应的是一种周期循环调整的方式。这种cycle策略主要目的在后期防止在一个局部极小值震荡,若跳出该区域或许能得到更有度结果。这里说明cycle的方式不止可以在多项式中应用,可配合类似的周期函数进行衰减。
4、inverse_time_decay
代码语言:javascript复制inverse_time_decay(learning_rate, global_step, decay_steps, decay_rate,
staircase=False, name=None)
逆时衰减,这种方式和指数型类似。
5、cosine_decay
代码语言:javascript复制cosine_decay(learning_rate, global_step, decay_steps, alpha=0.0,
name=None)
余弦衰减,即按余弦函数的方式衰减学习率。
6、cosine_decay_restarts
代码语言:javascript复制cosine_decay_restarts(learning_rate, global_step, first_decay_steps,
t_mul=2.0, m_mul=1.0, alpha=0.0, name=None)
余弦衰减,即余弦版本的cycle策略,作用与多项式衰减中的cycle相同。区别在于余弦重启衰减会重新回到初始学习率,拉长周期,而多项式版本则会逐周期衰减。
7、linear_cosine_decay
代码语言:javascript复制linear_cosine_decay(learning_rate, global_step, decay_steps,
num_periods=0.5, alpha=0.0, beta=0.001,
name=None)
线性余弦衰减,主要应用于增强学习领域。
8、noisy_linear_cosine_decay 噪声线性余弦衰减,即在线性余弦衰减中加入随机噪声,增大寻优的随机性。
14.3.4 极端批样本数量下,如何训练网络
极端批样本情况一般是指batch size为1或者batch size在6000以上的情况。这两种情况,在使用不合理情况下都会导致模型最终性能无法达到最优甚至是崩溃的情况。
在目标检测、分割或者3D图像等输入图像尺寸较大的场景,通常batch size会非常小。而在14.2.4中,我们已经讲到这种情况会导致梯度的不稳定以及batchnorm统计的不准确。针对梯度不稳定等问题,通常不会太致命,若训练中发现梯度不稳定导致性能等严重降低时可采用累计梯度的策略,即每次计算完不反向更小,而是累计多次的误差后进行一次更新,这是一种在内存有限情况下实现有效梯度更新的一个策略。
batch size过小通常对batchnorm的影响是最大的,若网络模型中存在batchnorm,batch size若只为1或者2时会对训练结果产生非常大的影响。这时通常有两种策略:
一、若模型使用了预训练网络,可冻结预训练网络中batchnorm的模型参数,有效降低batch size引起的统计量变化的影响。
二、在网络不是过深或者过于复杂时可直接移除batchnorm或者使用groupnorm代替batchnorm,前者不多阐释,后者是有FAIR提出的一种用于减少batch对batchnorm影响,其主要策略是先将特征在通道上进行分组,然后在组内进行归一化。即归一化操作上完全与batch size无关。
这种groupnorm的策略被证实在极小批量网络训练上能达到较优秀的性能。当然这里也引入了group这个超参数,一般情况下建议不宜取group为1或者各通道单独为组的group数量,可结合实际网络稍加调试。
为了降低训练时间的成本,多机多卡的分布式系统通常会使用超大的batch size进行网络训练。同样在14.2.4中,我们提到了超大batch size会带来梯度方向过于一致而导致的精度大幅度降低的问题。这时通常可采用层自适应速率缩放(LARS)算法。
从理论认知上,将batch size增大会减少反向传播的梯度更新次数,但为了达到相同的模型效果,需要增大学习率。但学习率一增大,又会引起模型的不收敛。为了解决这一矛盾。LARS算法就在各层上自适应的计算一个本地学习率用于更新本层的参数,这样能有效的提升训练的稳定性。
目前利用LARS算法,腾讯公司使用65536的超大 batch size 能将 ResNet50 在ImageNet在4分钟完成训练,而谷歌使用32768的 batch size使用TPU能将该时间缩短至2分钟。
14.4 合理使用预训练网络
14.4.1 什么是微调(fine-tune)
微调(fine-tune),顾名思义指稍微调整参数即可得到优秀的性能,是迁移学习的一种实现方式。
微调和从头训练(train from scratch)的本质区别在于模型参数的初始化,train from scratch通常指对网络各类参数进行随机初始化(当然随机初始化也存在一定技巧),随机初始化模型通常不具有任何预测能力,通常需要大量的数据或者特定域的数据进行从零开始的训练,这样需要训练到优秀的模型通常是稍困难的。
而微调的网络,网络各类参数已经在其他数据集(例如ImageNet数据集)完成较好调整的,具备了较优秀的表达能力。因此,我们只需要以较小的学习速率在自己所需的数据集领域进行学习即可得到较为优秀的模型。
微调通常情况下,无须再重新设计网络结构,预训练模型提供了优秀的结构,只需稍微修改部分层即可。在小数据集上,通常微调的效果比从头训练要好很多,原因在于数据量较小的前提下,训练更多参数容易导致过度拟合。
14.4.2 微调有哪些不同方法
以图像分类为例,通常情况下由于不同数据集需要的类别数不同,我们需要修改网络的输出顶层。这种情况下有两种微调方式:
- 不冻结网络模型的任何层,对最后的改动层使用较大的学习率,对未改动层以较小的学习率进行全模型训练,进行多轮训练即可。即一步完成训练。
- 冻结除了顶部改动层以外的所有层参数,即不对冻结部分对层进行参数训练更新,进行若干轮的微调训练后,放开顶部层以下的若干层或者全部放开所有层的参数,再次进行若干轮训练即可。即分多步训练。
以上两种都属于微调。目前由于存在大量优秀的预训练模型,如何确定哪个模型适合自己的任务并能得到最佳性能需要花大量的时间探索。
此时,上述的前者是种不错的训练方式,你无须进行过多分步的操作。而当探索到一个比较合适的模型时,你不妨可以再次重新尝试下以第二种方式进行训练,或许能得到相比于前者稍高些的性能,因为小数据集上调整过多的参数过拟合的几率也会增大,当然这并不是绝对的。
14.4.3 微调先冻结底层,训练顶层的原因
14.4.2中第二种冻结多步训练的方式。首先冻结除了顶部改动层以外的所有层参数,对顶层进行训练,这个过程可以理解为顶层的域适应训练,主要用来训练适应模型的现有特征空间,防止顶层糟糕的初始化,对已经具备一定表达能力的层的干扰和破坏,影响最终的性能。之后,在很多深度学习框架教程中会使用放开顶层往下一半的层数,继续进行微调。
这样的好处在于越底层的特征通常是越通用的特征,越往上其整体的高层次语义越完备,这通过感受野很容易理解。所以,若预训练模型的数据和微调训练的数据语义差异越大(例如ImageNet的预模型用于医学图像的训练),那越往顶层的特征语义差异就越大,因此通常也需要进行相应的调整。
14.4.4 不同数据集特性下如何微调
数据集数据量少,数据和原数据集类似。这是通常做法只需修改最后的输出层,训练即可,训练过多参数容易过拟合。
数据集数据量少,数据和原数据集差异较大。由于数据差异较大,可以在完成输出顶层的微调后,微调顶层往下一半的层数,进行微调。
数据集数据量大,数据与原数据集差异较大。这种情况下,通常已经不需要用预训练模型进行微调,通常直接重新训练即可。
数据集数据量大,数据与原数据相似。这时预训练模型的参数是个很好的初始化,可利用预训练模型放开所有层以较小的学习率微调即可。
14.4.5 目标检测中使用预训练模型的优劣
目标检测中无论是一阶段的YOLO、SSD或者RetinaNet,还是二阶段的Faster R-CNN、R-FCN和FPN都是基于ImageNet上预训练好的分类模型。
优势在于:
1、正如大部分微调的情况一样,使用预训练网络已拥有优秀的语义特征,能有效的加快训练速度。
2、其次,对于大部分二阶段的模型来说,并未实现严格意义上的完全端对端的训练,所以使用预训练模型能直接提取到语义特征,能使两个阶段的网络更容易实现模型的优化。
劣势在于:
1、分类模型大部分训练于单目标数据,对同时进行多目标的捕捉能力稍弱,且不关注目标的位置,在一定程度上让模型损失部分空间信息,这对检测模型通常是不利的。
2、域适应问题,若预训练模型(ImageNet)和实际检测器的使用场景(医学图像,卫星图像)差异较大时,性能会受到影响。
3、使用预训练模型就意味着难以自由改变网络结构和参数限制了应用场合。
14.4.6 目标检测中如何从零开始训练(train from scratch)
结合FAIR相关的研究,我们可以了解目标检测和其他任务从零训练模型一样,只要拥有足够的数据以及充分而有效的训练,同样能训练出不亚于利用预训练模型的检测器。这里我们提供如下几点建议:
1、数据集不大时,同样需要进行数据集增强。
2、预训练模型拥有更好的初始化,train from scratch需要更多的迭代次数以及时间训练和优化检测器。而二阶段模型由于并不是严格的端对端训练,此时可能需要更多的迭代次数以及时间,而一阶段检测模型训练会相对更容易些(例如DSOD以ScratchDet及)。
3、目标检测中train from scratch最大的问题还是batch size过小。所以可采取的策略是增加GPU使用异步batchnorm增大batch size,若条件限制无法使用更多GPU时,可使用groupnorm代替batchnorm
4、由于分类模型存在对多目标的捕捉能力弱以及对物体空间位置信息不敏感等问题,可借鉴DetNet训练一个专属于目标检测的模型网络,增强对多目标、尺度和位置拥有更强的适应性。