你有哪些deep learning(rnn、cnn)调参的经验?

关注者
15,883
被浏览
1,991,404

76 个回答

训练技巧对深度学习来说是非常重要的,作为一门实验性质很强的科学,同样的网络结构使用不同的训练方法训练,结果可能会有很大的差异。这里我总结了近一年来的炼丹心得,分享给大家,也欢迎大家补充指正。

参数初始化。

下面几种方式,随便选一个,结果基本都差不多。但是一定要做。否则可能会减慢收敛速度,影响收敛结果,甚至造成Nan等一系列问题。

下面的n_in为网络的输入大小,n_out为网络的输出大小,n为n_in或(n_in+n_out)*0.5

Xavier初始法论文:jmlr.org/proceedings/pa

He初始化论文:arxiv.org/abs/1502.0185

  • 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有比较好的效果。参考论文:arxiv.org/abs/1312.6120

数据预处理方式

  • zero-center ,这个挺常用的.
    X -= np.mean(X, axis = 0) # zero-center
    X /= 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,可以参考这篇论文: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或者更大的值做初始化,可以取得更好的结果,来自这篇论文:jmlr.org/proceedings/pa, 我这里实验设成1.0,可以提高收敛速度.实际使用中,不同的任务,可能需要尝试不同的值.
  • Batch Normalization据说可以提升效果,不过我没有尝试过,建议作为最后提升模型的手段,参考论文:Accelerating Deep Network Training by Reducing Internal Covariate Shift
  • 如果你的模型包含全连接层(MLP),并且输入和输出大小一样,可以考虑将MLP替换成Highway Network,我尝试对结果有一点提升,建议作为最后提升模型的手段,原理很简单,就是给输出加了一个gate来控制信息的流动,详细介绍请参考论文: arxiv.org/abs/1505.0038
  • 来自@张馨宇的技巧:一轮加正则,一轮不加正则,反复进行。

Ensemble

Ensemble是论文刷结果的终极核武器,深度学习中一般有以下几种方式

  • 同样的参数,不同的初始化方式
  • 不同的参数,通过cross-validation,选取最好的几组
  • 同样的参数,模型训练的不同阶段,即不同迭代次数的模型。
  • 不同的模型,进行线性融合. 例如RNN和传统模型.

​ 更多深度学习技巧,请参见我的专栏:炼丹实验室 - 知乎专栏

总结一下我遇到的小朋友常犯的错:

1、一上来就自己动手写模型。建议首先用成熟的开源项目及其默认配置(例如 Gluon 对经典模型的各种复现、各个著名模型作者自己放出来的代码仓库)在自己的数据集上跑一遍,在等程序运行结束的时间里仔细研究一下代码里的各种细节,最后再自己写或者改代码。

2、训 RNN 不加 gradient clipping,导致训练一段时间以后 loss 突然变成 Nan。

3、tying input & output embedding(就是词向量层和输出 softmax 前的矩阵共享参数,在语言模型或机器翻译中常用)时学习率需要设置得非常小,不然容易 Nan。

4、在数据集很大的情况下,一上来就跑全量数据。建议先用 1/100、1/10 的数据跑一跑,对模型性能和训练时间有个底,外推一下全量数据到底需要跑多久。在没有足够的信心前不做大规模实验。

5、只喜欢漂亮的模型结构,瞧不起调参数的论文/实验报告,看论文时经常不看超参数设置等细节。举个例子,现在还有相当多的人不知道 BERT 的激活函数是 GELU 而不是 transformer 原论文中的 ReLU(虽然我觉得这一点很无厘头)。在自己没有太多资源实验的情况下,实验报告类文章简直是业界良心好不好!

NLP 领域主要推荐以下几篇:

Regularizing and Optimizing LSTM Language Models(LSTM 的训练技巧)

Massive Exploration of Neural Machine Translation Architectures(NMT 里各个超参的影响)

Training Tips for the Transformer Model(训练 Transformer 时会发生的各种现象)

RoBERTa: A Robustly Optimized BERT Pretraining Approach(BERT 预训练技巧,虽然跟大部分人没啥关系)

CV 我不算太熟,不过也可以勉强推荐几篇:

Training ImageNet in 1 Hour(大批量训练技巧)

Bag of Tricks for Image Classification with Convolutional Neural Networks(各种训练技巧集大成)

Bag of Freebies for Training Object Detection Neural Networks(同上)

EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks(当前对参数利用最有效的 CNN,类似地还有一篇 EfficientDet)

6、初始学习率:

有时受 batch size、sequence length 各种因素的影响,loss 很大(比如说好几万),对于这种数字人是没有数感的,建议首先计算一下 per token loss(如果是多任务,可以每个任务单独算;类似地,某些 CV 任务可以计算 per pixel loss),心里有点感觉。脱离损失函数的形式谈学习率没有意义(例如单是对 batch size 求和或者取平均这个差异就会使梯度差成百上千倍)。

在确定初始学习率的时候,从一个很小的值(例如 1e-7)开始,然后每一步指数增大学习率(例如扩大1.05 倍)进行训练。训练几百步应该能观察到损失函数随训练步数呈对勾形,选择损失下降最快那一段的学习率即可。

7、Adam 可以解决一堆奇奇怪怪的问题(有时 loss 降不下去,换 Adam 瞬间就好了),也可以带来一堆奇奇怪怪的问题(比如单词词频差异很大,当前 batch 没有的单词的词向量也被更新;再比如Adam和L2正则结合产生的复杂效果)。用的时候要胆大心细,万一遇到问题找各种魔改 Adam(比如 MaskedAdam, AdamW 啥的)抢救。

8、subword 总是会很稳定地涨点,只管用就对了。

9、GPU 上报错时尽量放在 CPU 上重跑,错误信息更友好。例如 GPU 报 "ERROR:tensorflow:Model diverged with loss = NaN" 其实很有可能是输入 ID 超出了 softmax 词表的范围。

10、要有耐心

这一条放在最后,是因为很多人不把它当一回事儿。可能是觉得这一条不需要写代码所以不重要?我见过太多人因为这条浪费时间了,所以专门强调一下。

有些指标是有滞后性的,需要等训练一段时间才开始动(例如 BN 训练和推断行为不一致,可能训练的准确率已经很高了,但测试准确率很低,这是因为 BN 的统计量还不准,再等等就好了)。很多人训练几步看没什么效果就把程序停掉开始 debug 了,但其实代码毫无问题。如此反复好几天甚至一两周都在原地踏步,其实需要做的仅仅是让程序自个儿安安静静地跑上几个小时或者一天……