点击上方“算法猿的成长”,选择“加为星标”
第一时间关注 AI 和 Python 知识
来源:知乎问题
深度学习中调参其实是一个比较重要的技巧,但很多时候都需要多尝试多积累经验,因此算法工程师也被调侃为调参工程师。
这里分享来自知乎上的关于调参的经验的问题的几个回答,希望给大家在调参方面提供一些参考和建议。
作者:萧瑟
回答时间:2017-03-20
回答:
训练技巧对深度学习来说是非常重要的,作为一门实验性质很强的科学,同样的网络结构使用不同的训练方法训练,结果可能会有很大的差异。这里我总结了近一年来的炼丹心得,分享给大家,也欢迎大家补充指正。参数初始化。下面几种方式,随便选一个,结果基本都差不多。但是一定要做。否则可能会减慢收敛速度,影响收敛结果,甚至造成Nan等一系列问题。
参数初始化
下面几种方式,随便选一个,结果基本都差不多。但是一定要做。否则可能会减慢收敛速度,影响收敛结果,甚至造成Nan等一系列问题。下面的n_in 为网络的输入大小,n_out 为网络的输出大小,n 为 n_in 或 (n_in+n_out)*0.5
Xavier 初始法论文:http://jmlr.org/proceedings/papers/v9/glorot10a/glorot10a.pdf
He 初始化论文:https://arxiv.org/abs/1502.01852
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)svd初始化:对RNN有比较好的效果。参考论文:https://arxiv.org/abs/1312.6120
数据预处理方式
zero-center ,这个挺常用的. X -= np.mean(X, axis = 0) # zero-center X /= np.std(X, axis = 0) # normalize
PCA whitening, 这个用的比较少.
训练技巧
要做梯度归一化,即算出来的梯度除以 mini batch size
dropout 对小数据防止过拟合有很好的效果,值一般设为0.5,小数据上 dropout+sgd 在我的大部分实验中,效果提升都非常明显.因此可能的话,建议一定要尝试一下。dropout 的位置比较有讲究, 对于RNN,建议放到输入->RNN与RNN->输出的位置.关于RNN如何用dropout,可以参考这篇论文:http://arxiv.org/abs/1409.2329
adam,adadelta 等,在小数据上,我这里实验的效果不如 sgd, sgd 收敛速度会慢一些,但是最终收敛后的结果,一般都比较好。如果使用 sgd 的话,可以选择从1.0或者0.1的学习率开始,隔一段时间,在验证集上检查一下,如果 cost 没有下降,就对学习率减半. 我看过很多论文都这么搞,我自己实验的结果也很好. 当然,也可以先用ada系列先跑,最后快收敛的时候,更换成 sgd 继续训练.同样也会有提升.据说 adadelta 一般在分类问题上效果比较好,adam 在生成问题上效果比较好。
除了 gate 之类的地方,需要把输出限制成 0-1 之外,尽量不要用 sigmoid,可以用 tanh 或者 relu 之类的激活函数.
sigmoid 函数在-4到4的区间里,才有较大的梯度。之外的区间,梯度接近0,很容易造成梯度消失问题。
输入 0 均值,sigmoid 函数的输出不是 0 均值的。
rnn 的 dim 和 embdding size,一般从 128 上下开始调整. batch size,一般从128左右开始调整.batch size合适最重要,并不是越大越好.word2vec 初始化,在小数据上,不仅可以有效提高收敛速度,也可以可以提高结果.尽量对数据做 shuffle
LSTM 的 forget gate 的 bias,用1.0或者更大的值做初始化,可以取得更好的结果,来自这篇论文:http://jmlr.org/proceedings/papers/v37/jozefowicz15.pdf, 我这里实验设成1.0,可以提高收敛速度.实际使用中,不同的任务,可能需要尝试不同的值.
Batch Normalization 据说可以提升效果,不过我没有尝试过,建议作为最后提升模型的手段,参考论文:Accelerating Deep Network Training by Reducing Internal Covariate Shift
如果你的模型包含全连接层(MLP),并且输入和输出大小一样,可以考虑将MLP替换成Highway Network,我尝试对结果有一点提升,建议作为最后提升模型的手段,原理很简单,就是给输出加了一个gate来控制信息的流动,详细介绍请参考论文: http://arxiv.org/abs/1505.00387来自@张馨宇的技巧:一轮加正则,一轮不加正则,反复进行。
Ensemble
Ensemble 是论文刷结果的终极核武器,深度学习中一般有以下几种方式
同样的参数,不同的初始化方式
不同的参数,通过cross-validation,选取最好的几组
同样的参数,模型训练的不同阶段,即不同迭代次数的模型。
不同的模型,进行线性融合. 例如RNN和传统模型.
Ying Zhang
回答时间:2016-04-10
回答:
补充一点,adam 收敛虽快但是得到的解往往没有 sgd+momentum 得到的解更好,如果不考虑时间成本的话还是用 sgd 吧。
再补充一个 rnn trick,仍然是不考虑时间成本的情况下,batch size=1 是一个很不错的 regularizer, 起码在某些 task 上,这也有可能是很多人无法复现 alex graves 实验结果的原因之一,因为他总是把 batch size 设成 1。。。
罗浩.ZJU
回答时间:2018-05-15
回答:
其实我发现现在深度学习越来越成熟,调参工作比以前少了很多,绝大多数情况自己设计的参数都不如教程和框架的默认参数好,不过有一些技巧我一直都在用的。
(1)relu+bn。这套好基友组合是万精油,可以满足95%的情况,除非有些特殊情况会用 identity,比如回归问题,比如 resnet 的 shortcut 支路,sigmoid 什么的都快从我世界里消失了。
(2)dropout。分类问题用 dropout,只需要最后一层 softmax 前用基本就可以了,能够防止过拟合,可能对 accuracy 提高不大,但是 dropout 前面的那层如果是之后要使用的 feature 的话,性能会大大提升。
(3)数据的 shuffle 和 augmentation。这个没啥好说的,aug 也不是瞎加,比如行人识别一般就不会加上下翻转,因为不会碰到头朝下的异型种。
(4)降学习率。随着网络训练的进行,学习率要逐渐降下来,如果你有 tensorboard,你有可能发现,在学习率下降的一瞬间,网络会有个巨大的性能提升,同样的 fine-tuning 也要更加模型的性能设置合适的学习率,比如一个训练的已经非常好的模型,你上来就 1e-3 的学习率,那之前就白训练了,就是说网络性能越好,学习率要越小。
(5)tensorboard。以前不怎么用,用了之后发现太有帮助,帮助你监视网络的状态,来调整网络参数。
(6)随时存档模型,要有 validation。这就跟打游戏一样存档,把每个 epoch 和其对应的 validation 结果存下来,可以分拆出开始 overfitting 的时间点,方便下次加载 fine-tuning。
(7)网络层数,参数量什么的都不是大问题,在性能不丢的情况下,减到最小。
(8)batchsize 通常影响没那么大,塞满卡就行,除了特殊的算法需要 batch 大一点。
(9)输入减不减 mean 归一化在有了 bn 之后已经不那么重要了。
上面那些都是大家所知道的常识,也是外行人觉得深度学习一直在做的就是这些很 low 的东西,其实网络设计上博大精深,这也远超过我的水平范畴,只说一些很简单的
(1)卷积核的分解。从最初的 5×5 分解为两个 3×3,到后来的 3×3 分解为 1×3 和 3×1,再到 resnet 的 1×1, 3×3,1×1,再 xception 的 3×3 channel-wise conv + 1×1,网络的计算量越来越小,层数越来越多,性能越来越好,这些都是设计网络时可以借鉴的。
(2)不同尺寸的 feature maps 的 concat,只用一层的 feature map 一把梭可能不如 concat 好,pspnet 就是这种思想,这个思想很常用。
(3)resnet 的 shortcut 确实会很有用,重点在于 shortcut 支路一定是 identity,主路是什么 conv 都无所谓,这是我亲耳听 resnet 作者所述
(4)针对于 metric learning,对 feature 加个 classification 的约束通常可以提高性能加快收敛。
京东白条
回答时间:2019-08-06
回答:
相信很多刚开始接触深度学习的朋友,会感觉深度学习调参就像玄学一般,有时候参数调得好,模型会快速收敛,参数没调好,可能迭代几次 loss 值就直接变成 Nan 了。
记得刚开始研究深度学习时,做过两个小例子。一个是用 tensorflow 构建了一个十分简单的只有一个输入层和一个 softmax 输出层的 Mnist 手写识别网络,第一次我对权重矩阵 W 和偏置 b 采用的是正态分布初始化,一共迭代了 20 个 epoch,当迭代完第一个 epoch 时,预测的准确度只有 10% 左右(和随机猜一样,Mnist 是一个十分类问题),当迭代完二十个 epoch,精度也仅仅达到了 60% 的样子。然后我仅仅是将权重矩阵 W 初始化方法改成了全为 0 的初始化,其他的参数均保持不变,结果在训练完第一个 epoch 后预测精度就达到了 85% 以上,最终 20 个 epoch 后精度达到 92%。另一个例子是回归问题的预测,当时采用的 SGD 优化器,一开始学习率设定的 0.1,模型可以正常训练,只是训练速度有些慢,我试着将学习率调整到 0.3,希望可以加快训练速度,结果没迭代几轮 loss 就变成 Nan 了。于是从那时起我就深刻的感受到参数调节在深度学习模型训练中的重要意义。
其实上述问题产生的原因也很好理解,对于参数初始化,因为我们学习的本来就是权重 W 与偏置 b,如果初始化足够好,直接就初始化到最优解,那都不用进行训练了。良好的初始化,可以让参数更接近最优解,这可以大大提高收敛速度,也可以防止落入局部极小。对于学习率,学习率如果取太大,会使模型训练非常震荡,可以想象我们最小化一个二次抛物线,选取一个很大的学习率,那么迭代点会一直在抛物线的两边震荡,收敛不到最小值,甚至还有螺旋上升迭代点的可能。
下面对深度学习调参技巧谈些心得,虽说不能让你通过以下阅读成为一个调参高手,但最起码可以提供一些调参的思路。
1.激活函数选择:
常用的激活函数有 relu、leaky-relu、sigmoid、tanh 等。对于输出层,多分类任务选用 softmax 输出,二分类任务选用 sigmoid 输出,回归任务选用线性输出。而对于中间隐层,则优先选择 relu 激活函数(relu 激活函数可以有效的解决 sigmoid 和 tanh 出现的梯度弥散问题,多次实验表明它会其他激活函数以更快的速度收敛)。另外,构建序列神经网络(RNN)时要优先选用 tanh 激活函数
2.学习率设定:
一般学习率从 0.1 或 0.01 开始尝试。学习率设置太大会导致训练十分不稳定,甚至出现 Nan,设置太小会导致损失下降太慢。学习率一般要随着训练进行衰减。衰减系数设 0.1,0.3,0.5 均可,衰减时机,可以是验证集准确率不再上升时,或固定训练多少个周期以后自动进行衰减。
3.防止过拟合:
一般常用的防止过拟合方法有使用 L1 正则项,L2 正则项,dropout,提前终止、数据集扩充等。如果模型在训练集上表现比较好,但在测试集上表现欠佳可以选择增大 L1 或 L2 正则的惩罚力度(L2正则经验上首选1.0,超过10很少见)或者增加 dropout 的随机失活概率(p=0.5);或者当随着模型的持续在测试集上不增反减时,使用提前终止训练的方法。当然最有效的还是增大训练集的规模,实在难以获得新数据也可以使用数据集增强的方法,比如 CV 任务可以对数据集进行裁剪、翻转、平移等方法进行数据集增强,这种方法往往都会提高最后模型的测试精度。
4. 优化器选择:
如果数据是稀疏的,就用自适应方法,即 Adagrad,Adadelta,RMSprop,Adam。整体来讲,Adam 是最好的选择。SGD 虽然能达到极小值,但是比其他算法用的时间长,而且可能会被困在鞍点。如果需要更快的收敛,或者是训练更深更复杂的神经网络,需要用一种自适应的算法。
5. 残差块与BN层:
如果你希望训练一个更深更复杂的网络,那么残差块绝对是一个重要的组件,它可以让你的网络训练得更深。
BN 层具有加快训练速度,有效防止梯度消失与梯度爆炸,具有过拟合的效果,所以构建网络时最好要加上这个组件。
6. 自动调参方法:
(1)Grid Search:网格搜索,在所有候选的参数选择中,通过循环遍历,尝试每一种可能性,表现最好的参数就是最终的结果。其原理就像是在数组里找最大值。缺点是太费时间了,特别像神经网络,一般尝试不了太多的参数组合。
(2)Random Search:经验上,Random Search 比 Grid Search 更有效。实际操作的时候,一般也是先用 Grid Search 的方法,得到所有候选参数,然后每次从中随机选择进行训练。另外 Random Search 往往会和由粗到细的调参策略结合使用,即在效果比较好的参数附近进行更加精细的搜索。
(3)Bayesian Optimization:贝叶斯优化,考虑到了不同参数对应的实验结果值,因此更节省时间,贝叶斯调参比 Grid Search 迭代次数少,速度快;而且针对非凸问题依然稳健。
7.参数随机数初始化与数据预处理:
参数初始化很重要,它决定了模型的训练速度与是否可以躲开局部极小。relu 激活函数初始化推荐使用 He normal,tanh 初始化推荐使用 Glorot normal,其中 Glorot normal 也称作 Xavier normal 初始化;
数据预处理方法一般也就采用数据归一化即可。
Necther
回答时间:2019-08-10
回答:
1、要注意不同的网络结构学习率是不一样的,甚至差上数量级, 如果碰见网络不收敛,检查数据标注是正确的,就要考虑学习率是否过大
2、先训练几轮看一下情况 观察loss下降的速度,如果震荡,就要调小学习率
3、注意数据多少和学习率衰减的关系 尤其是ocr这种需要大量样本的场景,太少数据量相当于字都没有看全不能够马上迭代到下一个批次
分享的几个关于调参的回答,这些回答有些方法其实都是比较常见的,比如学习率、优化器、参数初始化等,当然这些经验并不一定适用于所有的情况,还是需要具体问题具体分析,多做一些实验,验证一下这些调参方法的效果。
欢迎关注我的微信公众号--算法猿的成长,或者扫描下方的二维码,大家一起交流,学习和进步!
如果觉得不错,在看、转发就是对小编的一个支持!