学习率是神经网络训练中最重要的超参数之一,针对学习率的技巧有很多。Warm up是在ResNet论文[26]中提到的一种学习率预热的方法。由于刚开始训练时模型的权重(weights)是随机初始化的(全部置为0是一个坑,原因见[27]),此时选择一个较大的学习率,可能会带来模型的不稳定。学习率预热就是在刚开始训练的时候先使用一个较小的学习率,训练一些epoches或iterations,等模型稳定时再修改为预先设置的学习率进行训练。论文[1]中使用一个110层的ResNet在cifar10上训练时,先用0.01的学习率训练直到训练误差低于80%(大概训练了400个iterations),然后使用0.1的学习率进行训练。
上述的方法是constant warmup,18年Facebook又针对上面的warmup进行了改进[28],因为从一个很小的学习率一下变为比较大的学习率可能会导致训练误差突然增大。论文[28]提出了gradual warmup来解决这个问题,即从最开始的小学习率开始,每个iteration增大一点,直到最初设置的比较大的学习率。
Gradual warmup代码如下:
from torch.optim.lr_scheduler import_LRScheduler
class GradualWarmupScheduler(_LRScheduler):
"""
Args:
optimizer (Optimizer): Wrapped optimizer.
multiplier: target learning rate = base lr * multiplier
total_epoch: target learning rate is reached at total_epoch, gradually
after_scheduler: after target_epoch, use this scheduler(eg. ReduceLROnPlateau)
"""
def __init__(self, optimizer, multiplier, total_epoch, after_scheduler=None):
self.multiplier = multiplier
if
self.multiplier <= 1.:
raise ValueError('multiplier should be greater than 1.')
self.total_epoch = total_epoch
self.after_scheduler = after_scheduler
self.finished = False
super().__init__(optimizer)
def get_lr(self):
if self.last_epoch > self.total_epoch:
if self.after_scheduler:
if not self.finished:
self.after_scheduler.base_lrs = [base_lr * self.multiplier for base_lr in self.base_lrs]
self.finished = True
return self.after_scheduler.get_lr()
return[base_lr * self.multiplier for base_lr in self.base_lrs]
return [base_lr * ((self.multiplier - 1.) * self.last_epoch / self.total_epoch + 1.) for base_lr in self.base_lrs]
def step(self, epoch=None):
if self.finished and self.after_scheduler:
return self.after_scheduler.step(epoch)
else:
return super(GradualWarmupScheduler, self).step(epoch)
学习率是一个非常非常重要的超参数,这个参数呢,面对不同规模、不同batch-size、不同优化方式、不同数据集,其最合适的值都是不确定的,我们无法光凭经验来准确地确定lr的值,我们唯一可以做的,就是在训练中不断寻找最合适当前状态的学习率。
比如下图利用fastai中的lr_find()函数寻找合适的学习率,根据下方的学习率-损失曲线得到此时合适的学习率为1e-2。
推荐一篇fastai首席设计师「Sylvain Gugger」的一篇博客:How Do You Find A Good Learning Rate[1]
以及相关的论文Cyclical Learning Rates for Training Neural Networks[2]。
一般来说,越大的batch-size使用越大的学习率。
原理很简单,越大的batch-size意味着我们学习的时候,收敛方向的confidence越大,我们前进的方向更加坚定,而小的batch-size则显得比较杂乱,毫无规律性,因为相比批次大的时候,批次小的情况下无法照顾到更多的情况,所以需要小的学习率来保证不至于出错。
可以看下图损失Loss与学习率Lr的关系:
在显存足够的条件下,最好采用较大的batch-size进行训练,找到合适的学习率后,可以加快收敛速度。
另外,较大的batch-size可以避免batch normalization出现的一些小问题,参考如下Pytorch库Issue[3]。
Linear scaling learning rate是在论文[28]中针对比较大的batch size而提出的一种方法。
在凸优化问题中,随着批量的增加,收敛速度会降低,神经网络也有类似的实证结果。随着batch size的增大,处理相同数据量的速度会越来越快,但是达到相同精度所需要的epoch数量越来越多。也就是说,使用相同的epoch时,大batch size训练的模型与小batch size训练的模型相比,验证准确率会减小。
上面提到的gradual warmup是解决此问题的方法之一。另外,linear scaling learning rate也是一种有效的方法。在mini-batch SGD训练时,梯度下降的值是随机的,因为每一个batch的数据是随机选择的。增大batch size不会改变梯度的期望,但是会降低它的方差。也就是说,大batch size会降低梯度中的噪声,所以我们可以增大学习率来加快收敛。
具体做法很简单,比如ResNet原论文[26]中,batch size为256时选择的学习率是0.1,当我们把batch size变为一个较大的数b时,学习率应该变为 0.1 × b/256。
在分类问题中,我们的最后一层一般是全连接层,然后对应标签的one-hot编码,即把对应类别的值编码为1,其他为0。这种编码方式和通过降低交叉熵损失来调整参数的方式结合起来,会有一些问题。这种方式会鼓励模型对不同类别的输出分数差异非常大,或者说,模型过分相信它的判断。但是,对于一个由多人标注的数据集,不同人标注的准则可能不同,每个人的标注也可能会有一些错误。模型对标签的过分相信会导致过拟合。
标签平滑(Label-smoothing regularization,LSR)是应对该问题的有效方法之一,它的具体思想是降低我们对于标签的信任,例如我们可以将损失的目标值从1稍微降到0.9,或者将从0稍微升到0.1。标签平滑最早在inception-v2[29]中被提出,它将真实的概率改造为:
其中,ε是一个小的常数,K是类别的数目,y是图片的真正的标签,i代表第i个类别,q_i是图片为第i类的概率。
总的来说,LSR是一种通过在标签y中加入噪声,实现对模型约束,降低模型过拟合程度的一种正则化方法。
LSR代码如下:
import torch
import torch.nn as nn
class LSR(nn.Module):
def __init__(self, e=0.1, reduction='mean'):
super().__init__()
self.log_softmax = nn.LogSoftmax(dim=1)
self.e = e
self.reduction = reduction
def _one_hot(self, labels, classes, value=1):
"""
Convert labels to one hot vectors
Args:
labels: torch tensor in format [label1, label2, label3,...]
classes: int, number of classes
value: label value in one hot vector, default to 1
Returns:
return one hot format labels in shape [batchsize, classes]
"""
one_hot = torch.zeros(labels.size(0), classes)
#labels and value_added size must match
labels = labels.view(labels.size(0), -1)
value_added = torch.Tensor(labels.size(0), 1).fill_(value)
value_added = value_added.to(labels.device)
one_hot = one_hot.to(labels.device)
one_hot.scatter_add_(1, labels, value_added)
return one_hot
def
_smooth_label(self, target, length, smooth_factor):
"""convert targets to one-hot format, and smooth them.
Args:
target: target in form with [label1, label2, label_batchsize]
length: length of one-hot format(number of classes)
smooth_factor: smooth factor for label smooth
Returns:
smoothed labels in one hot format
"""
one_hot = self._one_hot(target, length, value=1- smooth_factor)
one_hot += smooth_factor / length
return one_hot.to(target.device)
权重初始化相比于其他的trick来说在平常使用并不是很频繁。
因为大部分人使用的模型都是预训练模型,使用的权重都是在大型数据集上训练好的模型,当然不需要自己去初始化权重了。只有没有预训练模型的领域会自己初始化权重,或者在模型中去初始化神经网络最后那几个全连接层的权重。
常用的权重初始化算法是kaiming_normal或者xavier_normal。
相关论文:
dropout是指在深度学习网络的训练过程中,对于神经网络单元,按照一定的概率将其暂时从网络中丢弃。注意是「暂时」,对于随机梯度下降来说,由于是随机丢弃,故而每一个mini-batch都在训练不同的网络。
Dropout类似于bagging ensemble减少variance。也就是投通过投票来减少可变性。通常我们在全连接层部分使用dropout,在卷积层则不使用。但「dropout」并不适合所有的情况,不要无脑上Dropout。
Dropout一般适合于全连接层部分,而卷积层由于其参数并不是很多,所以不需要dropout,加上的话对模型的泛化能力并没有太大的影响。图片
我们一般在网络的最开始和结束的时候使用全连接层,而hidden layers则是网络中的卷积层。所以一般情况,在全连接层部分,采用较大概率的dropout而在卷积层采用低概率或者不采用dropout。
主要有「数据筛选」 以及 「数据增强」
fastai中的图像增强技术为什么相对比较好[9]
分析模型难以预测正确的样本,给出针对性方法。
Ensemble是论文刷结果的终极核武器,深度学习中一般有以下几种方式
提高模型性能和鲁棒性大法:probs融合 和 投票法。
假设这里有model 1, model 2, model 3,可以这样融合:
- model1 probs + model2 probs + model3 probs ==> final label
- model1 label , model2 label , model3 label ==> voting ==> final label
- model1_1 probs + … + model1_n probs ==> mode1 label, model2 label与model3获取的label方式与1相同 ==> voting ==> final label
第三个方式的启发来源于,如果一个model的随机种子没有固定,多次预测得到的结果可能不同。
以上方式的效果要根据label个数,数据集规模等特征具体问题具体分析,表现可能不同,方式无非是probs融合和投票法的单独使用or结合。
在李航的统计学方法中说到,交叉验证往往是对实际应用中「数据不充足」而采用的,基本目的就是重复使用数据。在平常中我们将所有的数据分为训练集和验证集就已经是简单的交叉验证了,可以称为1折交叉验证。「注意,交叉验证和测试集没关系,测试集是用来衡量我们的算法标准的,不参与到交叉验证中来。」
交叉验证只针对训练集和验证集。
交叉验证是Kaggle比赛中特别推崇的一种技巧,我们经常使用的是5-折(5-fold)交叉验证,将训练集分成5份,随机挑一份做验证集其余为训练集,循环5次,这种比较常见计算量也不是很大。还有一种叫做leave-one-out cross validation留一交叉验证,这种交叉验证就是n-折交叉,n表示数据集的容量,这种方法只适合数据量比较小的情况,计算量非常大的情况很少用到这种方法。
吴恩达有一节课The nuts and bolts of building applications using deep learning[13]中也提到了。
按理说不同的优化算法适合于不同的任务,不过我们大多数采用的优化算法还是是adam和SGD+monmentum。
Adam 可以解决一堆奇奇怪怪的问题(有时 loss 降不下去,换 Adam 瞬间就好了),也可以带来一堆奇奇怪怪的问题(比如单词词频差异很大,当前 batch 没有的单词的词向量也被更新;再比如Adam和L2正则结合产生的复杂效果)。用的时候要胆大心细,万一遇到问题找各种魔改 Adam(比如 MaskedAdam[14], AdamW 啥的)抢救。
但看一些博客说adam的相比SGD,收敛快,但泛化能力差,更优结果似乎需要精调SGD。
adam,adadelta等, 在小数据上,我这里实验的效果不如sgd, sgd收敛速度会慢一些,但是最终收敛后的结果,一般都比较好。
如果使用sgd的话,可以选择从1.0或者0.1的学习率开始,隔一段时间,在验证集上检查一下,如果cost没有下降,就对学习率减半. 我看过很多论文都这么搞,我自己实验的结果也很好. 当然,也可以先用ada系列先跑,最后快收敛的时候,更换成sgd继续训练.同样也会有提升.据说adadelta一般在分类问题上效果比较好,adam在生成问题上效果比较好。
adam收敛虽快但是得到的解往往没有sgd+momentum得到的解更好,如果不考虑时间成本的话还是用sgd吧。
adam是不需要特别调lr,sgd要多花点时间调lr和initial weights。
[1] How Do You Find A Good Learning Rate: https://sgugger.github.io/how-do-you-find-a-good-learning-rate.html
[2] Cyclical Learning Rates for Training Neural Networks: https://arxiv.org/abs/1506.01186
[3] Pytorch库Issue: https://github.com/pytorch/pytorch/issues/4534
[4] Delving deep into rectifiers: Surpassing human-level performance on ImageNet classification: https://www.cv-foundation.org/openaccess/content_iccv_2015/papers/He_Delving_Deep_into_ICCV_2015_paper.pdf
[5] Understanding the difficulty of training deep feedforward neural networks: http://proceedings.mlr.press/v9/glorot10a.html
[6] Xavier初始化论文: http://proceedings.mlr.press/v9/glorot10a/glorot10a.pdf
[7] He初始化论文: https://arxiv.org/abs/1502.01852
[8] https://arxiv.org/abs/1312.6120: https://link.zhihu.com/?target=https%3A//arxiv.org/abs/1312.6120
[9]
fastai中的图像增强技术为什么相对比较好: https://oldpan.me/archives/fastai-1-0-quick-study
[10] towardsdatascience.com/transfer-le…: https://towardsdatascience.com/transfer-learning-using-differential-learning-rates-638455797f00
[11] 机器学习算法如何调参?这里有一份神经网络学习速率设置指南: https://zhuanlan.zhihu.com/p/34236769
[12] SGDR: Stochastic Gradient Descent with Warm Restarts: https://arxiv.org/abs/1608.03983
[13] The nuts and bolts of building applications using deep learning: https://www.youtube.com/watch?v=F1ka6a13S9I
[14] MaskedAdam: https://www.zhihu.com/question/265357659/answer/580469438
[15] http://arxiv.org/abs/1409.2329: https://link.zhihu.com/?target=http%3A//arxiv.org/abs/1409.2329
[16] http://jmlr.org/proceedings/papers/v37/jozefowicz15.pdf: https://link.zhihu.com/?target=http%3A//jmlr.org/proceedings/papers/v37/jozefowicz15.pdf
[17] http://arxiv.org/abs/1505.00387: https://link.zhihu.com/?target=http%3A//arxiv.org/abs/1505.00387
[18] 关于训练神经网路的诸多技巧Tricks(完全总结版): https://juejin.im/post/5be5b0d7e51d4543b365da51
[19] 你有哪些deep learning(rnn、cnn)调参的经验?: https://www.zhihu.com/question/41631631
[20] Bag of Tricks for Image Classification with Convolutional Neural Networks: https://link.zhihu.com/?target=https%3A//arxiv.org/abs/1812.01187
[21] Must Know Tips/Tricks in Deep Neural Networks: https://link.zhihu.com/?target=http%3A//lamda.nju.edu.cn/weixs/project/CNNTricks/CNNTricks.html
[22] 33条神经网络训练秘技: https://zhuanlan.zhihu.com/p/63841572
[23] 26秒单GPU训练CIFAR10: https://zhuanlan.zhihu.com/p/79020733
[24] Batch Normalization: https://link.zhihu.com/?target=https%3A//arxiv.org/abs/1502.03167%3Fcontext%3Dcs
[25] Searching for Activation Functions: https://link.zhihu.com/?target=https%3A//arxiv.org/abs/1710.05941
[26] Deep Residual Learning for Image Recognition(https://arxiv.org/pdf/1512.03385.pdf
[27] http://cs231n.github.io/neural-networks-2/
[28] Accurate, Large Minibatch SGD:Training ImageNet in 1 Hour
https://arxiv.org/pdf/1706.02677v2.pdf
[29] Rethinking the Inception Architecture for Computer Vision
https://arxiv.org/pdf/1512.00567v3.pdf