深度学习调参技巧
训练技巧对深度学习来说是非常重要的,作为一门实验性质很强的科学,同样的网络结构使用不同的训练方法训练,结果可能会有很大的差异。
1.参数初始化
下面几种方式,随便选一个,结果基本都差不多。但是一定要做。否则可能会减慢收敛速度,影响收敛结果,甚至造成Nan等一系列问题。
下面的n_in为网络的输入大小,n_out为网络的输出大小,n为n_in或(n_in+n_out)*0.5
uniform均匀分布初始化: w = np.random.uniform(low=-scale, high=scale, size=[n_in,n_out])
Xavier初始法,适用于普通激活函数(tanh,sigmoid):scale = np.sqrt(3/n)
He初始化,适用于ReLU:scale = np.sqrt(6/n)
normal高斯分布初始化: w = np.random.randn(n_in,n_out) * stdev # stdev为高斯分布的标准差,均值设为0
Xavier初始法,适用于普通激活函数 (tanh,sigmoid):stdev = np.sqrt(n)
He初始化,适用于ReLU:stdev = np.sqrt(2/n)
2.数据预处理方式
zero-center ,这个挺常用的. X -= np.mean(X, axis = 0) # zero-center X /= np.std(X, axis = 0) # normalize
PCA whitening,这个用的比较少.
3.训练技巧
要做梯度归一化,即算出来的梯度除以minibatch size
clip c(梯度裁剪): 限制最大梯度,其实是value = sqrt(w12+w22….),如果value超过了阈值,就算一个衰减系系数,让value的值等于阈值: 5,10,15
dropout对小数据防止过拟合有很好的效果,值一般设为0.5,小数据上dropout+sgd在大部分实验中,效果提升都非常明显。因此可能的话,建议一定要尝试一下。 dropout的位置比较有讲究, 对于RNN,建议放到输入->RNN与RNN->输出的位置。关于RNN如何用dropout。
adam,adadelta等,在小数据上,这里实验的效果不如sgd, sgd收敛速度会慢一些,但是最终收敛后的结果,一般都比较好。如果使用sgd的话,可以选择从1.0或者0.1的学习率开始,隔一段时间,在验证集上检查一下,如果cost没有下降,就对学习率减半。 看过很多论文都这么搞,自己实验的结果也很好。 当然,也可以先用ada系列先跑,最后快收敛的时候,更换成sgd继续训练。同样也会有提升。据说adadelta一般在分类问题上效果比较好,adam在生成问题上效果比较好。
除了gate之类的地方,需要把输出限制成0-1之外,尽量不要用sigmoid,可以用tanh或者relu之类的激活函数。1. sigmoid函数在-4到4的区间里,才有较大的梯度。之外的区间,梯度接近0,很容易造成梯度消失问题。2. 输入0均值,sigmoid函数的输出不是0均值的。
rnn的dim和embdding size,一般从128上下开始调整。 batch size,一般从128左右开始调整。batch size合适最重要,并不是越大越好。
word2vec初始化,在小数据上,不仅可以有效提高收敛速度,也可以可以提高结果。
尽量对数据做shuffle
LSTM 的forget gate的bias,用1.0或者更大的值做初始化,可以取得更好的结果。
Batch Normalization据说可以提升效果,建议作为最后提升模型的手段。
如果你的模型包含全连接层(MLP),并且输入和输出大小一样,可以考虑将MLP替换成Highway Network,尝试对结果有一点提升,建议作为最后提升模型的手段,原理很简单,就是给输出加了一个gate来控制信息的流动。
4.常见问题
问题1:网络错误没有正确训练,损失完全不收敛。
可能两种原因:1,错误的input data,网络无法学习。2,错误的网络,网络无法学习。解决办法:(1)请检测自己的数据是否存在可以学习的信息,这个数据集中的数值是否泛化(防止过大或过小的数值破坏学习)。(2)如果是错误的数据则你需要去再次获得正确的数据,如果是数据的数值异常可以使用zscore函数来解决这个问题(3)如果是网络的错误,则希望调整网络,包括:网络深度,非线性程度,分类器的种类等等。
问题2:部分收敛。
可能原因:1.underfitting,就是网络的分类太简单了没办法去分类,因为没办法分类就是没办法学到正确的知识。2.overfitting,就是网络的分类太复杂了以至于它可以学习数据中的每一个信息甚至是错误的信息他都可以学习。
解决办法:(1)underfitting: 增加网络的复杂度(深度),降低learning rate,优化数据集,增加网络的非线性度(ReLu),采用batch normalization。(2)overfitting: 丰富数据,增加网络的稀疏度,降低网络的复杂度(深度),L1 regularization,L2 regulariztion,添加Dropout,Early stopping,适当降低Learning rate,适当减少epoch的次数。
问题3:全部收敛但效果不好。这是个好的开始,接下来要做的就是微调一些参数。解决办法:调整方法就是保持其他参数不变,只调整一个参数。这里需要调整的参数会有:learning rate,minibatch size,epoch,filter size,number of filter。
5.调参技巧
好的实验环境是成功的一半:(1)将各个参数的设置部分集中在一起。如果参数的设置分布在代码的各个地方,那么修改的过程想必会非常痛苦。(2)可以输出模型的损失函数值以及训练集和验证集上的准确率。(3)可以考虑设计一个子程序,可以根据给定的参数,启动训练并监控和周期性保存评估结果。再由一个主程序,分配参数以及并行启动一系列子程序。
画图:画图是一个很好的习惯,一般是训练数据遍历一轮以后,就输出一下训练集和验证集准确率。同时画到一张图上。这样训练一段时间以后,如果模型一直没有收敛,那么就可以停止训练,尝试其他参数了,以节省时间。如果训练到最后,训练集,测试集准确率都很低,那么说明模型有可能欠拟合。那么后续调节参数方向,就是增强模型的拟合能力。例如增加网络层数,增加节点数,减少dropout值,减少L2正则值等等。如果训练集准确率较高,测试集准确率比较低,那么模型有可能过拟合,这个时候就需要向提高模型泛化能力的方向,调节参数。
从粗到细分阶段调参:(1)建议先参考相关论文,以论文中给出的参数作为初始参数。至少论文中的参数,是个不差的结果。(2)如果找不到参考,那么只能自己尝试了。可以先从比较重要,对实验结果影响比较大的参数开始,同时固定其他参数,得到一个差不多的结果以后,在这个结果的基础上,再调其他参数。例如学习率一般就比正则值,dropout值重要的话,学习率设置的不合适,不仅结果可能变差,模型甚至会无法收敛。(3)如果实在找不到一组参数,可以让模型收敛。那么就需要检查,是不是其他地方出了问题,例如模型实现,数据等等。
提高速度:调参只是为了寻找合适的参数,而不是产出最终模型。一般在小数据集上合适的参数,在大数据集上效果也不会太差。因此可以尝试对数据进行精简,以提高速度,在有限的时间内可以尝试更多参数。(1)对训练数据进行采样。例如原来100W条数据,先采样成1W,进行实验看看。(2)减少训练类别。例如手写数字识别任务,原来是10个类别,就可以先在2个类别上训练,看看结果如何。
超参数范围:建议优先在对数尺度上进行超参数搜索。比较典型的是学习率和正则化项,可以从诸如0.001 0.01 0.1 1 10,以10为阶数进行尝试。因为他们对训练的影响是相乘的效果。不过有些参数,还是建议在原始尺度上进行搜索,例如dropout值: 0.3 0.5 0.7)。
经验参数:
o learning rate: 1 0.1 0.01 0.001,一般从1开始尝试。很少见learning rate大于10的。学习率一般要随着训练进行衰减。衰减系数一般是0.5。衰减时机,可以是验证集准确率不再上升时,或固定训练多少个周期以后。不过更建议使用自适应梯度的办法,例如adam,adadelta,rmsprop等,这些一般使用相关论文提供的默认值即可,可以避免再费劲调节学习率。对RNN来说,有个经验,如果RNN要处理的序列比较长,或者RNN层数比较多,那么learning rate一般小一些比较好,否则有可能出现结果不收敛,甚至Nan等问题。
o 网络层数: 先从1层开始。
o 每层结点数: 16 32 128,超过1000的情况比较少见。超过1W的从来没有见过。
o batch size: 128上下开始。batch size值增加,的确能提高训练速度。但是有可能收敛结果变差。如果显存大小允许,可以考虑从一个比较大的值开始尝试。因为batch size太大,一般不会对结果有太大的影响,而batch size太小的话,结果有可能很差。
o clip c(梯度裁剪): 限制最大梯度,其实是value = sqrt(w1^2+ w2^2….),如果value超过了阈值,就算一个衰减系系数,让value的值等于阈值: 5,10,15
o dropout: 0.5
o L2正则:1.0,超过10的很少见。
o 词向量embedding大小:128,256
o 正负样本比例: 这个是非常忽视,但是在很多分类问题上,又非常重要的参数。很多人往往习惯使用训练数据中默认的正负类别比例,当训练数据非常不平衡的时候,模型很有可能会偏向数目较大的类别,从而影响最终训练结果。除了尝试训练数据默认的正负类别比例之外,建议对数目较小的样本做过采样,例如进行复制。提高他们的比例,看看效果如何,这个对多分类问题同样适用。在使用mini-batch方法进行训练的时候,尽量让一个batch内,各类别的比例平衡,这个在图像识别等多分类任务上非常重要。
· 自动调参:
o Gird Search。这个是最常见的。具体说,就是每种参数确定好几个要尝试的值,然后像一个网格一样,把所有参数值的组合遍历一下。优点是实现简单暴力,如果能全部遍历的话,结果比较可靠。缺点是太费时间了,特别像神经网络,一般尝试不了太多的参数组合。
o Random Search。Bengio在Random Search for Hyper-Parameter Optimization中指出,Random Search比Gird Search更有效。实际操作的时候,一般也是先用Gird Search的方法,得到所有候选参数,然后每次从中随机选择进行训练。
o Bayesian Optimization。贝叶斯优化,考虑到了不同参数对应的实验结果值,因此更节省时间。和网络搜索相比简直就是老牛和跑车的区别。具体原理可以参考这个论文: Practical Bayesian Optimization of Machine Learning Algorithms ,这里同时推荐两个实现了贝叶斯调参的Python库,可以上手即用:
o jaberg/hyperopt,比较简单。
o fmfn/BayesianOptimization, 比较复杂,支持并行调参。
6.注意事项
· 刚开始,先上小规模数据,模型往大了放,只要不爆显存,能用256个filter你就别用128个。直接奔着过拟合去。没错,就是训练过拟合网络,连测试集验证集这些都可以不用。如果小数据量下,这么粗暴的大网络奔着过拟合去都没效果,那么有可能是:模型的输入输出是不是有问题? 代码错误? 模型解决的问题定义是不是有问题? 你对应用场景的理解是不是有错?
· Loss设计要合理。一般来说分类就是Softmax,回归就是L2的loss。但是要注意loss的错误范围(主要是回归),你预测一个label是10000的值,模型输出0,你算算这loss多大,这还是单变量的情况下。一般结果都是难。所以不仅仅输入要做normalization,输出也要这么弄。多任务情况下,各loss想法限制在一个量级上,或者最终限制在一个量级上,初期可以着重一个任务的loss。
· 观察loss胜于观察准确率。准确率虽然是评测指标,但是训练过程中还是要注意loss的。你会发现有些情况下,准确率是突变的,原来一直是0,可能保持上千迭代,然后突然变1。要是因为这个你提前中断训练了,只有老天替你惋惜了。而loss是不会有这么诡异的情况发生的,毕竟优化目标是loss。给NN一点时间,要根据任务留给NN的学习一定空间。不能说前面一段时间没起色就不管了。有些情况下就是前面一段时间看不出起色,然后开始稳定学习。
· 确认分类网络学习充分。分类网络就是学习类别之间的界限。你会发现,网络就是慢慢的从类别模糊到类别清晰的。怎么发现? 看Softmax输出的概率的分布。如果是二分类,你会发现,刚开始的网络预测都是在0.5上下,很模糊。随着学习过程,网络预测会慢慢的移动到0,1这种极值附近。所以,如果你的网络预测分布靠中间,再学习学习。
· Learning Rate设置合理。太大: loss爆炸,或者nan。太小: 半天loss没反映。需要进一步降低了: loss在当前LR下一路降了下来,但是半天不再降了。如果上面的Loss设计那块你没法合理,初始情况下容易爆,先上一个小LR保证不爆,等loss降下来了,再慢慢升LR,之后当然还会慢慢再降LR。
· 对比训练集和验证集的loss。判断过拟合,训练是否足够,是否需要early stop的依据。
· 清楚receptive field的大小。CV的任务,context window是很重要的。所以你对自己模型的receptive field的大小要心中有数。这个对效果的影响还是很显著的。特别是用FCN,大目标需要很大的receptive field。
7.简短的注意事项
· 预处理:-mean/std zero-center就够了,PCA,白化什么的都用不上。
· shuffle,shuffle,shuffle(洗牌、打乱)。
· 网络原理的理解最重要,CNN的conv这块,你得明白sobel算子的边界检测。
· Dropout,Dropout,Dropout(不仅仅可以防止过拟合,其实这相当于做人力成本最低的Ensemble,当然,训练起来会比没有Dropout的要慢一点,同时网络参数你最好相应加一点,对,这会再慢一点)。
· CNN更加适合训练回答是否的问题,如果任务比较复杂,考虑先用分类任务训练一个模型再finetune。
· 无脑用ReLU(CV领域)。
· 无脑用3x3。
· 无脑用xavier。
· LRN一类的,其实可以不用。不行可以再拿来试试看。
· filter数量2^n
· 多尺度的图片输入(或者网络内部利用多尺度下的结果)有很好的提升效果。
· 第一层的filter,数量不要太少。否则根本学不出来(底层特征很重要)。
· sgd adam 这些选择上,看你个人选择。一般对网络不是决定性的。反正无脑用sgd + momentum。
· batch normalization要鼓励使用batch normalization。
· 不要完全相信论文里面的东西。结构什么的觉得可能有效果,可以拿去试试。
· 你有95%概率不会使用超过40层的模型。
· shortcut的联接是有作用的。