No.1
总结一下在旷视实习两年来的炼丹经验,我主要做了一些 RL,图像质量,图像分类,GAN 相关的任务,日常大概占用 5 - 10 张卡。
可复现性和一致性
有的同学在打比赛的时候,从头到尾只维护若干份丹方(代码),每次载入前一次的训练参数,调一下丹方再炼,俗称老丹。这样会有几个问题:某次引入一个 bug,过了很久才发现,然后不知道影响范围;炼出一个金丹,但是不知道它是怎么来的;忘了自己的 baseline,不知道改动是正面还是负面。
要尽可能确保每一个丹有可复现性,实践上建议丹方不应该在炼丹后改动,炼新的丹时,把旧的丹方复制一遍。得到的实验结果要开个文档记下来以便日后总结,避免遗忘。我经常通过阅读自己和别人的记录来得到灵感。
实验一致性上也要多做努力,理想状态是有合理的基准来测丹的性能,同一个丹方不应该由于超参的微小改动而有显著结果差异。出现这种情况可能是数据太少或基准设置不当。
资源利用
对于新入行的同学,不要试图在玩具级别的数据集或任务上做靠谱的研究,比如 MNIST。
不是每一个实验都要出一个金丹,实验是为了验证结论的。如果每个实验都要 8 张卡跑两个星期,人力物力都耗不起。尽力把实验控制在单卡一天以内,理想状态是半天得一次结论。理论上来说,水多加面面多加水(加数据加计算量)的做法无限涨点。建议先设一个目标,比如说就是在一天的训练时间下做对比实验。
我的实践经验是,首先用小图小模型,比如 128 x 128 输入的 ResNet18;用 cProfile 来找性能瓶颈,比如我发现某个丹,炼的时候有一大半时间耗费在等数据,数据处理中一大半时间在调用 numpy 的 round 函数,前期把精力集中在提高做实验的效率上。
模型不 work
先把锦上添花的东西去掉,比如数据增广,玄学学习率和超参,魔幻损失函数,异形模型。如果世界上有一个非要加旋转增广和 1.96e-4 学习率 42 batchsize,配上四种混合损失函数才能炼好的丹,它应该存在于灵能文明。可以先造一些尽量玩具的模型,验证代码正确性。
需要进一步改进
先确认影响模型性能的组件。感性认识就是,数据是否需要增加或增广。模型是大了还是小了,再根据速度和精度期望开始寻找合适的模型。能用全卷积的任务,少用全连接层,参数量小。基本模型上 ResNet, Unet 结构还是主流。
当你的模型有 Batch Normalization,初始化通常不需要操心,激活函数默认 Relu 即可(某引用数万的大佬说的)。一般顺序是 Conv - BN - Relu。如果没有 BN(很多任务上,BN降低训练难度,但是可能影响最终性能 ),试着要做一些数据归一化。
虽然有至少十种激活函数,但在 Relu 外只推荐试一下 Swish。炼丹术只推荐 Momentum 和 Adam。在这些方面做尝试意义不大,如果性能提升反倒可能说明模型不成熟。不推荐做人肉模型设计,比如把某层卷积改大一点,或者微调一下通道数。除非有特别 insight,不要自己乱设计新组件。
超参上,learning rate 最重要,推荐了解 cosine learning rate 和 cyclic learning rate,其次是 batchsize 和 weight decay。当你的丹还不错的时候,可以试着做数据增广和改损失函数锦上添花了。
祝读到这里的各位模型涨点!
参考文献
作者:hzwer
https://www.zhihu.com/question/41631631/answer/859040970
No.2
训练技巧对深度学习来说是非常重要的,作为一门实验性质很强的科学,同样的网络结构使用不同的训练方法训练,结果可能会有很大的差异。这里我总结了近一年来的炼丹心得,分享给大家,也欢迎大家补充指正。
参数初始化
下面几种方式,随便选一个,结果基本都差不多。但是一定要做。否则可能会减慢收敛速度,影响收敛结果,甚至造成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
数据预处理方式
训练技巧
Ensemble
Ensemble是论文刷结果的终极核武器,深度学习中一般有以下几种方式
作者:萧瑟 https://www.zhihu.com/question/41631631/answer/94816420
No.3
总结一下我遇到的小朋友常犯的错:
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(各种训练技巧集大成)
EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks(当前对参数利用最有效的 CNN)
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、要有耐心!
这一条放在最后,是因为很多人不把它当一回事儿。可能是觉得这一条不需要写代码所以不重要?我见过太多人因为这条浪费时间了,所以专门强调一下。
有些指标是有滞后性的,需要等训练一段时间才开始动。很多人训练几步看没什么效果就把程序停掉开始 debug 了,但其实代码毫无问题。如此反复好几天甚至一两周都在原地踏步,其实需要做的仅仅是让程序自个儿安安静静地跑上几个小时或者一天……
作者:Towser
https://www.zhihu.com/question/41631631/answer/862075836