调参技巧

一、参数和超参数

参数即是我们在过程中想要模型学习到的信息(模型自己能计算出来的),例如 $W^{[l]}$,$b^{[l]}$。

超参数(hyper parameters)即为控制参数的输出值的一些网络信息(需要人经验判断)。超参数的改变会导致最终得到的参数 $W^{[l]}$,$b^{[l]}$ 的改变。

典型的超参数有:
(1)学习速率:α
(2)迭代次数:N
(3)隐藏层的层数:L
(4)每一层的神经元个数:$n^{[1]}$,$n^{[2]}$,…
(5)激活函数 g(z) 的选择

当开发新应用时,预先很难准确知道超参数的最优值应该是什么。因此,通常需要尝试很多不同的值。应用深度学习领域是一个很大程度基于经验的过程。

比如常见的梯度下降问题,好的超参数能使你尽快收敛:

sgd_good
sgd_bad


二、超参数调试处理

1、重要程度排序

目前已经讲到过的超参数中,重要程度依次是(仅供参考):

  • 最重要:
    • 学习率 α;
  • 其次重要:
    • β:动量衰减参数,常设置为 0.9;
    • hidden units:各隐藏层神经元个数;
    • mini-batch 的大小;
  • 再次重要:
    • β1,β2,ϵ:Adam 优化算法的超参数,常设为 0.9、0.999、$10^{-8}$;
    • layers:神经网络层数;
    • decay_rate:学习衰减率;

2、调参技巧

系统地组织超参调试过程的技巧:

  • 随机选择点(而非均匀选取),用这些点实验超参数的效果。这样做的原因是我们提前很难知道超参数的重要程度,可以通过选择更多值来进行更多实验;
  • 由粗糙到精细:聚焦效果不错的点组成的小区域,在其中更密集地取值,以此类推;

3、选择合适的范围

  • 对于学习率 α,用对数标尺而非线性轴更加合理:0.0001、0.001、0.01、0.1 等,然后在这些刻度之间再随机均匀取值;
  • 对于 β,取 0.9 就相当于在 10 个值中计算平均值,而取 0.999 就相当于在 1000 个值中计算平均值。可以考虑给 1-β 取值,这样就和取学习率类似了。

上述操作的原因是当 β 接近 1 时,即使 β 只有微小的改变,所得结果的灵敏度会有较大的变化。例如,β 从 0.9 增加到 0.9005 对结果(1/(1-β))几乎没有影响,而 β 从 0.999 到 0.9995 对结果的影响巨大(从 1000 个值中计算平均值变为 2000 个值中计算平均值)。

4、一些建议

  • 深度学习如今已经应用到许多不同的领域。不同的应用出现相互交融的现象,某个应用领域的超参数设定有可能通用于另一领域。不同应用领域的人也应该更多地阅读其他研究领域的 paper,跨领域地寻找灵感;
  • 考虑到数据的变化或者服务器的变更等因素,建议每隔几个月至少一次,重新测试或评估超参数,来获得实时的最佳模型;
  • 根据你所拥有的计算资源来决定你训练模型的方式:
    • Panda(熊猫方式):在在线广告设置或者在计算机视觉应用领域有大量的数据,但受计算能力所限,同时试验大量模型比较困难。可以采用这种方式:试验一个或一小批模型,初始化,试着让其工作运转,观察它的表现,不断调整参数;
    • Caviar(鱼子酱方式):拥有足够的计算机去平行试验很多模型,尝试很多不同的超参数,选取效果最好的模型;

三、神经网络调参经验

结合最近的项目经历进行整理,待处理。。
使用谷歌最近开源的AutoML工具包 NNI
知乎:深度学习调参有哪些技巧?

一、参数初始化
下面几种方式,随便选一个,结果基本都差不多。但是一定要做。否则可能会减慢收敛速度,影响收敛结果,甚至造成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-centerX /= np.std(X, axis = 0) # normalize
PCA whitening,这个用的比较少.

三、训练技巧
要做梯度归一化,即算出来的梯度除以minibatch size
clip c(梯度裁剪): 限制最大梯度,其实是value = sqrt(w1^2+w2^2….),如果value超过了阈值,就算一个衰减系系数,让value的值等于阈值: 5,10,15

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之类的激活函数.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或者更大的值做初始化,可以取得更好的结果,来自这篇论文: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来控制信息的流动,详细介

五、Ensemble
Ensemble是论文刷结果的终极核武器,深度学习中一般有以下几种方式
同样的参数,不同的初始化方式
不同的参数,通过cross-validation,选取最好的几组
同样的参数,模型训练的不同阶段,即不同迭代次数的模型。
不同的模型,进行线性融合. 例如RNN和传统模型.