回答:现阶段深度学习已经进入稳定阶段,已经很少需要一些很小的技巧了,尤其是在工程实践方面。往往学习率等超参数依靠经验设置一下就能基本将模型训练好。此外精调学习率超参数能取得的提升也往往是较小的,而且很容易过拟合,在应用场景上影响不大。除非是一个开源的数据集或者比赛数据集,测试集基本不变,通过精调这些参能“过拟合”测试集,获得一些提升。
2.Code Pipeline
一般cv模型的代码都是由这几个模块构成,Data(dataset/dataloader),model本身,optimizer优化器,loss functions,再加上一个trainer其实就是组织整个框架的训练顺序,不太会出错)。如果要构建一个模型,就是依次构建这些地方。
作者:没有故事只有酒
链接:https://www.zhihu.com/question/25097993/answer/2812858376
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2.1 Data
dataset 构建基本不会有啥问题,构建好了后需要检查一下 输出的数据读取是否正确,同一个batch的数据是否符合需要的逻辑;
2.2 Model
模型这块其实出错的问题也不多,
- 模型代码:如果不是自己写新网络,用一些现有的ResNet, Transformer之类的,在对应的开源代码复制就行,需要魔改的话记得修改的部分bug free,基本就不会出现什么问题;
- 参数初始化:现在的模型初始化(model.init)pytorch 已经内置的挺好了,比如
kaming
初始化,xavier
初始化等,不太需要自己刻意专门的初始化,就能正常训; - 使用 pretrained 模型时记得把 load 过程打印出来,保证模型被 load 了。
- 如果有不懂的 code,请去开源的代码里找,然后复制过来学习参考过来。
2.3 Optimizer
优化器相对能说的点 会多一些
- 万能模板 Adam + Cos退火 构建基础:初始构建时选 Adam 优化器(也可以AdamW),超参数跟其他开源代码保持一致,学习率初始时可以设置
3.5e-4
,可以适当进行调整,基本就在5e-4 ~ 5e-5
之间,我这里说的也只是一个经验的参数。你要问为什么,很多大神就用的这个参数,而且这个参数也确实能训好,还是经验。学习率变化选择 warm-up + cos退火,训练epoch可以随便选,比如10,100,等等,具体得看你这个学习任务,选一个更优的就行。假如是10个,可以第1个epoch用warm-up逐步增大lr,然后1~9epoch就 cos 下降lr,终点是9,cos lr结束值设置为一个很小的数1e-7
等等。你要是想问为啥 warm刚结束就开始 cos 退火,因为不知道这个数据集咋样,所以就让 lr 缓慢下降就行。一般这样,就能保证模型收敛了。 - 如果在 上面基础上,想进一步精调,可以把 epoch 进行网格搜索,看看多少个epoch合适。可以每个epoch在测试集上test一下,不用非得设计的很精准,最终模型我们可以使用中间的checkpoint;
- 同样,lr也可以调调,适当放大缩小看看,cos 退化的起始点或者 warm-up的持续时长,也可以试试改改,但是有时候调了作用不大,需要精调的可以试试;
- 一般按上述过程,就能训一个收敛的模型,但你说还想进一步提升,可以试试阶梯下降(step delay)的lr衰减策略,因为通过上面过程,epoch,lr初始值基本就拿捏了,可以固定lr值,然后训,发现损失不咋下降了,设置一个 在这个时间点下降 0.1 倍(一般都是10倍下降,经验值,你要说下降一般,也行),然后设置后继续训练,不下降后继续缩小 0.1 倍,这样就大概知道需要的 step 步长了。如果觉得这样比较麻烦,可以固定下降步长为某一值,比如 2 epoch,然后试试 3 epoch, 4 epoch(挨个试的过程也被称为 网格搜索),试一个最优的。(突然想起来一件事,和我打比赛的学弟说你调参调的可以啊,咋调的弄这么高。。。我都不想说我他娘的是 一个个试出来的,不就是网格搜索,哪有啥大技巧(尴尬哈哈))这里提示一下,调参时一定要先 文字记录,然后跑模型 填空,一定要保证 自己知道自己这个实验的目的是什么,这很重要
- 还想进一步榨干模型,行,推荐使用 SGD(SGD+Momentum),SDG更难训,但一般最优模型也更好。SGD一般初始学习率为 0.1(具体还得调),然后选择Cos 或者 Step 下降,具体方法跟 Adam 类似,就网格搜索就行,Epoch 的总长度两个基本一致。
- 此外,要保证 model 的参数被加载到 optimizer 里了,比如你先后创建了 model1, optimizer, model2,结果你的 model2 的参数未添加到 optimizer里,这部分参数就没参与学习,有可能最后发现结果一直很差,其实是因为模型根本没训练。
2.4 Loss
loss是基本不会出错的地方,但loss也是最容易出错的地方。
- loss 函数本身基本不会出错,使用已有loss 一般要保证逻辑是无误的,不会出现一些 log(0) 的操作;
- 但模型训练时 总会 loss 值出错,如果 NaN 了,需要看一下,是训练过程中 NaN 了还是第一下就 NaN,一般 NaN 就是梯度爆炸,如果使用的是已有网络,并且 bug free,必须是 Bug free,对修改的地方要多检查,那么从以下考虑:
- 先定位到 NaN 出现的时候,在NaN 的时候依次排查
- data无误,即 model input 的原始数据 是没问题的,这样就排除了 data 影响,其实排除data 就是把 dataloader 空跑一遍,用 torch.isnan 或者别的方法进行检测,看看源数据是否正确;
- loss 无误,即这个 NaN 不是因为 Log(0) 等操作引其的,这个需要排查 loss 函数计算;
- model 无误,最后是 model 无误,这个比较难排查,你可以把修改部分的代码复原进行训练,控制变量法找到是不是model的原因。
- 如果实现无法解决,梯度 CLIP 防止 NaN 也行,但这样可能治标不治本,不知道为啥 NaN,但是有时候可以继续训。不过我有次经验表示,有时候 Clip 正常的模型,会导致性能下降。就是本来一个模型是正常收敛的,但是我也添加了 clip,最后导致性能不如之前,删除clip后又重新恢复原先的精度。
最后,说一个万能方法,也是我一直在践行的方法:如果你不知道怎么办,怎么训这个网络,怎么改,用什么 optimizer,学习率,数据增广,去看一些杰作是大佬的代码怎么搞的,比如 ViT,比如Clip,SimCLR,看看他们咋搞的,copy过来学习学习总是好的,说白了,调参里面很多经验的东西,学习好的code才是硬道理