文章目录
0. 前言
- 相关资料
- B站视频(字幕是自动生成的,但也差不多够用)
- 为什么要聊聊深度学习调试(Troubleshotting)
- 80%以上的时间在调试,剩下一点点时间在尝试新内容。
- 主要内容:
- 为什么深度学习调试很难?
- 怎样调试呢?
- 如何评估模型的性能?
- 基础性能评估后,我们需要思考可以从哪些方面改进我们的模型呢?
- 调超参有哪些技巧呢?
1. 为什么深度学习调试很难?
-
深度学习模型出错的表现形式不是抛出异常,而是模型效果不好。
-
模型效果不好的原因有很多
- 实现出错。调试非常困难,因为是invisible,无法通过错误直接通过抛出异常。
- 超参数选择的不对。学习率是最常见的。
- 数据与模型不符。感觉意思是测试集与训练集不在同一分布上。
- 可能与模型完全没关系,而是数据层面有问题。
- 常见的原因有:数据不足,不均衡,标签有问题,训练集与测试集不在同一分布。
- Alex Karpathy 对比了他在读博以及Tesla工作期间,熬夜原因的分布……真实……
-
总结来看,调试困难的原因在于:
- 无法判断是不是有BUG
- 性能不好的原因太多了
- 结果可能由于一点点参数的改变而大幅度下降
2. 调试策略与过程
-
调试的策略
- 总体策略:过程从简单到难
- 前提:
- 假设初步有个测试集
- 确定了一个需要提升的性能指标
- 以人类水准/公开结果/之前的baseline确定目标
-
基本过程(后文就是介绍下面每一步)
2.1. Start Simple
- 因为可能出问题的地方很多,所以可以先从简单的开始,一点点排查。
- 所谓“简单的开始”,分为下面四步
- 第一步:选择一个简单的结构,比如lenet,之后再考虑ResNet
- 如果有多重类型的输入,比如文字+图片,那就先统一将这些内容转换为低纬度特征,然后concat
- 第二步:使用一些明智的(sensible)默认参数,比如使用3e-4的lr以及adam/Relu/he_normal,不使用正则化。
- 第三步:输入数据归一化,即减去均值除以方差,比如不用ImageNet均值方差,而是直接将输入归一化到[0, 1]或[-0.5, 0.5]中。
- 第四步:简化问题。比如使用训练集的自己,使用固定的物体数量、类别数量、图像尺寸,使用简单的生成的训练数据。
- 第一步:选择一个简单的结构,比如lenet,之后再考虑ResNet
- 举例:对行人检测数据集,可以10000张训练,500张测试,先训练一个分类的二分类网络
2.2. Implement & Debug
- 总结如下
- 最常见的几种BUG(都碰到过,真的惨)
- tensor的shape不对
- 数据预处理有问题
- 损失函数的输入不对
- 忘记将网络设置为训练模式(主要是BN层)
- 数字不稳定(可能出现inf或nan)
- 搭建模型的建议
- 使用简单的实现方法:第一版模型尽量少些代码,
- 使用现成的工具,如Keras。
- 先构建简单的输入处理流程,复杂的之后再说。
- 推荐的debug工具
- PyTorch 推荐使用 ipdb
- Tensorflow 推荐使用 trickier 或 tfdb
- 简单来说,分为三步:
- 让模型能够成功运行,常见问题包括
- Shape不匹配以及数据类型不同:建议通过debugger解决
- OOM:手动一个一个处理对显存敏感的操作
- 其他:如忘记初始化、忘记在BN中取消bias等
- 在一个batch上过拟合,常见问题包括
- loss增加,可能是忘记负号、lr太大、softmax选错dim
- loss爆炸(变为inf或nan):一些数学问题(比如分母为0),或lr太大
- loss震荡:可能是标签有问题,也可能是loss太大
- loss太早进入平滑期:lr太小、梯度没有更新整个模型、正则化太多、损失函数的输入不对、数据有问题
- 与已知结果进行对比
- 有各种官方实现的源码,且数据集与我们自己的类似
- 最高优先级的参考项目,不仅可以比较结果,还可以比较代码(模型结构)
- 有各种官方实现的源码,且数据集与我们自己的类似
- 让模型能够成功运行,常见问题包括
2.3. 评估模型
- 本节主要就是掌握测试误差的来源,主要分为5个部分
- irreducible error:不可简化的误差。毕竟数据是人标注的,人的标注误差是无法避免的。
- avoidable bias:训练集与人相比的性能差距(如果训练集效果比人的要查,那就是有问题),一般来说这一点是可以避免的,一般都是欠拟合问题。
- variance:验证集误差与训练集误差之间的插值,过拟合引起的
- distribution shift:要区分 train val error 和 test val error,大概理解就是模型从实验室到业务场景有一个迁移
- val overfitting:验证集与测试集之间的误差。可以这么理解,我们选择的都是验证集上过拟合的模型,所以验证集到测试集,其实一般都会有性能下降,因为存在过拟合。
2.4. 改进模型与数据集
- 模型已经评估好了,那就要进行改进了,一般分为四步,下面分别介绍:
- 第一步:处理欠拟合
- 也就是减少bias
- 方法有:模型结构不变但更大(比如增加layer、channel等)、减少正则化、换模型(如从lenet到resnet)、调参、增加额外特征
- 第二步:处理过拟合
- 很多东西需要尝试,最好的办法就是增加数据、数据增强以及norm。
- 不推荐进行early stopping(QA的时候又说是不是不推荐,而是不应该依赖 early stopping 作为正则化方法)、去掉特征、减少模型容量,
- 第三步:处理 distribution shift
- 分析 test-val 的所有错误样例,归纳,并收集更多相关的数据。
- 用模拟的方式造一些与错误样本类似的数据。
- 使用 domain adaption 技术,之后会单独介绍一下
- 第四步:重新平衡数据集
- 如果验证集比测试集效果好得多,那肯定是在验证集上过拟合了
- 在验证集很小时容易出现
- 如果是这样,验证集要重新构建
- Domain Adaption 技术简介:
- 是什么?Techniques to train on “source” distribution and generalize to another “target” using only nlabeled data or limited labeled data
- 什么时候用?测试集数据难以获取,但类似的数据容易获取。
- 常用方法包括:
2.5. 超参数调节
- 有哪些超参数?
- 模型相关:层数、初始化方法、卷积核尺寸
- 优化器相关:优化器类型、学习率、优化器参数
- 如果选择要调节的超参数?
- 选择敏感、与模型相关性大、rules of thumb(这是啥)永远是ok
- 调节方法一:手动调节
- 理解算法,训练并验证模型,猜测更合适的超参数重新训练并评估模型,
- 对于有经验的人来说,获取好结果的时间最短,但需要深入理解算法,刚开始非常耗时。
- 调节方法二:Grid Search,实现容易,能获得较好效果,但耗时长,需要有一定先验知识(即知道超参数怎么选)
- 调参方法三:Random Search,实现容易,经常比Grid Search结果更好,但不具备可解释性,需要先验知识来获得更好的效果。
- 调参方法四:Corase-to-fine,即先随机测试,找性能还不错的子区域再搜索,依次缩小超参数范围,得到好结果。
- 调参方法五:Bayesian hyperparam opt,这个我看不懂,不需要手动处理,有工具,但要使用工具也不是特别容易。