深度学习调参体验(一)
基本原则:
快速试错
一.一些大的注意事项:
1.先上小规模数据, 模型往大了放,
只要不爆显存, 能用256个filter就别用128个。直接奔着过拟合去。就是训练过拟合网络, 连测试集验证集这些都可以不用。
为什么?
要验证自己的训练脚本的流程对不对。这一步小数据量, 生成速度快, 但是所有的脚本都是和未来大规模训练一致的(除了少跑点循环)。
如果小数据量下, 这么粗暴的大网络奔着过拟合去都没效果。要开始反思自己了, 模型的输入输出是不是有问题? 要不要检查自己的代码(永远不要怀疑工具库, 除非动过代码)?
模型解决的问题定义是不是有问题? 对应用场景的理解是不是有错?
不要怀疑NN的能力, 不要怀疑NN的能力, 不要怀疑NN的能力。调参狗能遇到的问题, NN没法拟合的, 这概率是有多小?
可以不这么做, 但是等数据准备了两天, 结果发现有问题要重新生成的时候,时间没有了。
2. Loss设计要合理.
一般来说分类就是Softmax, 回归就是L2的loss.
但是要注意loss的错误范围(主要是回归), 预测一个label是10000的值, 模型输出0, 算算这loss多大, 这还是单变量的情况下。一般结果都是nan. 所以不仅仅输入要做normalization, 输出也要这么弄。
多任务情况下, 各loss想法限制在一个量级上, 或者最终限制在一个量级上, 初期可以着重一个任务的loss。
3. 观察loss胜于观察准确率
准确率虽然是评测指标, 但是训练过程中还是要注意loss的。会发现有些情况下, 准确率是突变的, 原来一直是0, 可能保持上千迭代, 然后突然变1。要是因为这个提前中断训练了, 只有老天替惋惜了。而loss是不会有这么诡异的情况发生的, 毕竟优化目标是loss。
给NN一点时间, 要根据任务留给NN的学习一定空间。不能说前面一段时间没起色就不管了。有些情况下就是前面一段时间看不出起色, 然后开始稳定学习。
4. 确认分类网络学习充分
分类网络就是学习类别之间的界限。网络就是慢慢的从类别模糊到类别清晰的。怎么发现? 看Softmax输出的概率的分布。如果是二分类,刚开始的网络预测都是在0.5上下, 很模糊。随着学习过程, 网络预测会慢慢的移动到0,1这种极值附近。如果的网络预测分布靠中间, 再学习学习。
5. Learning Rate设置合理
太大: loss爆炸, 或者nan。
太小: 半天loss没反映(但是, LR需要降低的情况也是这样, 这里可视化网络中间结果, 不是weights, 有效果, 俩者可视化结果是不一样的, 太小的话中间结果有点水波纹或者噪点的样子, 因为filter学习太慢的原因, 试过就会知道很明显)。
需要进一步降低了: loss在当前LR下一路降了下来, 但是半天不再降了。
如果有个复杂点的任务,需要人肉盯着调LR的。后面熟悉这个任务网络学习的特性后, 可以扔一边跑去了。
如果上面的Loss设计那块没法合理, 初始情况下容易爆, 先上一个小LR保证不爆, 等loss降下来了, 再慢慢升LR, 之后当然还会慢慢再降LR, 虽然这很痛苦。
LR在可以工作的最大值下往小收一收, 免得ReLU把神经元弄死了。
6 对比训练集和验证集的loss
判断过拟合, 训练是否足够, 是否需要early stop的依据, 这都是中规中矩的原则。
7 清楚receptive
field的大小
CV的任务, context window是很重要的。所以对自己模型的receptive field的大小要心中有数。这个对效果的影响还是很显著的。特别是用FCN,
大目标需要很大的receptive field。不像有fully
connection的网络, 好歹有个fc兜底, 全局信息都有。
二. 简短的注意事项:
1. 预处理: -mean/std zero-center就够了, PCA, 白化什么的都用不上。反正CNN能学习encoder, PCA用不用其实关系不大, 大不了网络里面自己学习出来一个。
2. shuffle, shuffle, shuffle。
3. 网络原理的理解最重要, CNN的conv这块, 得明白sobel算子的边界检测。
4. Dropout, Dropout, Dropout(不仅仅可以防止过拟合, 其实这相当于做人力成本最低的Ensemble, 当然, 训练起来会比没有Dropout的要慢一点, 同时网络参数最好相应加一点, 对, 这会再慢一点)。
5. CNN更加适合训练回答是否的问题, 如果任务比较复杂, 考虑先用分类任务训练一个模型再finetune。
6. 不要用ReLU(CV领域)。
7. 不要用3x3。
8. 不要用xavier。
9. LRN一类的, 其实可以不用。不行可以再拿来试试看。
10. filter数量2^n。
11. 多尺度的图片输入(或者网络内部利用多尺度下的结果)有很好的提升效果。
12. 第一层的filter, 数量不要太少。否则根本学不出来(底层特征很重要)。
13. sgd adam 这些选择上,一般对网络不是决定性的。反正不要用sgd momentum。
14. batch normalization,要鼓励使用batch normalization。
15. 不要完全相信论文里面的东西。结构什么的觉得可能有效果, 可以拿去试试。
16. 有95%概率不会使用超过40层的模型。
17. shortcut的联接是有作用的。
18. 暴力调参最可取,调完这个模型说不定过两天这模型就扔掉了。
19. 机器, 机器, 机器。
20. Google的inception论文, 结构要好好看看。
21. 一些传统的方法, 要了解。自己的程序就用过1x14的手写filter, 写过之后看看inception里面的1x7, 7x1。
三.产品开发
· 3*3卷积是CNN的主流组件。平时有设计一些解决分类,回归任务的网络,里面的卷积核基本都设置为3*3,要说原因的话应该去问问VGG16吧。两个3*3的卷积核堆叠能获得5*5卷积核的感受野并且参数比5*5卷积核少,所以是大量推荐使用的。
· 可以适当使用1*N卷积。为什么要提这一点呢,这是因为1*N卷积可以减少计算量,并且1*N卷积可以在某个方向强调感受野,也就是说假如如果要对一个长方形形状的目标进行分类,可以使用1*N的卷积核搭配3*3的卷积核对长边方向设定更大的感受野,或许可以获得泛化性能的提升。
· ACNet结构。这个研究来自于ICCV2019,可以在3*3卷积的基础上加上1*3和3*1的旁路卷积核,最后在推理阶段把三个卷积核都fusion到3*3卷积核上,在许多经典CV任务上都可以获得大概1个点的提升。
非对称卷积通常用于逼近现有的正方形卷积以进行模型压缩和加速,先前的一些工作表明,可以将标准的d*d卷积分解为d*1和1*d卷积,以减少参数量。其背后的理论相当简单:如果二维卷积核的秩为1,则运算可等价地转换为一系列一维卷积。由于深度网络中下学习到的核具有分布特征值,其内在秩比实际中的高,因此直接将变换应用于核会导致显著的信息损失。Denton等人基于SVD分解找到一个低秩逼近,然后对上层进行精细化以恢复性能。Jaderberg等人通过最小化重构误差,成功学习了水平核和垂直核。Jin等人应用结构约束使二维卷积可分离,在获得相当精度的条件下时间加速了2倍。另一方面,非堆成卷积也被广泛的用来做网络结构设计,例如Inception-v3中,7*7卷积被1*7卷积和7*1卷积代替。语义分割ENet网络也采用这种方法来设计高效的语义分割网络,虽然精度略有下降,但降低了33%的参数量。
关注3x3卷积,这在现代CNN体系结构中大量使用。在给定的体系结构下,通过简单地将每个3x3卷积层替换为ACB来构建ACNet,该ACB模块包含三个并行层,内核大小分别为3x3,1x3,和3x1。和标准CNN的常规做法类似,在三层中的每一层之后都进行批量归一化,并将三个分支的输出综合作为ACB的输出。可以使用与原始模型相同的配置来训练ACNet,而无需调整任何额外的超参数。可以训练ACNet达到更高的精度。训练完成后,尝试将每个ACB转换为产生相同输出的标准卷积层这样,与经过常规训练的对等方相比,可以获得不需要任何额外计算的功能更强大的网络。该转换通过两个步骤完成,即BN融合和分支融合。
· 卷积核权重初始化方式。对于weight的初始化一般都是使用xavier初始化。对于bias的初始化全置于0。
· Batch Normalization。这是一直在使用的技巧,可以很大程度的加快收敛速度。建议搭建自己网络的时候尽量加上BN,如果有BN了全连接层就没必要加Dropout了。
· 目标检测不能盲目去掉fpn结构。在针对自己的数据调检测任务如yolov3的时候不能盲目砍掉fpn结构,尽管分析出某个分支的Anchor基本不可能会对预测的目标起作用,但如果直接去掉分支很可能会带来漏检。
· 优化器的选择。基本都是带动量的SGD。如果优化不动可以试试Adam。
· 激活函数。可以先用ReLU做一版,如果想再提升精度可以将ReLU改成PReLU试试。更倾向于直接使用ReLU。
·
batch_size:在不同类型的任务中,batch_size的影响也不同, Batch_size是怎么影响模
型性能的。
· 初始学习率。一般是从0.01开始设置,这个学习率和学习率衰减策略是相关的,但不宜设置的过大过小,0.01和0.1应该是比较常用的。学习率衰减策略一般使用multistep方式,step_size的设置要看视的的max_iter而定。
· 数据与处理之zero-center。第一次见到这个词是在看cs231n的视频上。主要有2个步骤,第一个是减均值,第二个是除以方差。这样做下来最后的输入是满足均值为0方差为1的概率分布的,一般减均值是最常用的,后面的除以方差用不用可能需要自己动手试验一下看看效果。
· 残差结构和密集连接。resnet的残差结构和dense net密集连接结构,做工程的时候考虑到速度近乎不可能说完全使用完整版本的resnet和densenet的完整结构,但可以自己动手将网络的某些模块替换为残差结构和密集连接,替换的时候可以适当降低这俩结构的复杂度,类似于通道数减半,密集连接中只保留一半连接等等。这里需要做一些消融实验来验证改进后的精度。
· 关于loss。优秀的loss一般是对模型的泛化性能有所改善的,但在用loss的时候往往并不是直接替换loss那么简单,需要仔细思考loss背后的数学原理,要用对地方才可有提升。例如,如何将Focal Loss用到YOLOv3中提升map,大家可以看看这个帖子。https://www.zhihu.com/question/293369755。
· 找到模型调参时的可靠评价指标。在调整参数训练模型时一定要找到正确的评价指标,没调整一个参数就要记录一下模型的评价指标如准确率,map值,miou值等。并且在调参时建议将调整的参数和在测试集上的精度组合成一个字符串给模型重命令,方便之后快速review。
· 使用了带backbone的网络,如训练VGG16-SSD建议选择finetune的方式,从头训练不仅费时费力,甚至难以收敛。
· 在做分割实验的时候发现用upsamling 加1*1卷积代替反卷积做上采样得到的结果更平滑,并且miou差距不大,所以认为这两者都是都可以使用的。
· 一些Anchor-based目标检测算法为了提高精度,都是疯狂给框,ap值确实上去了,但也导致了fp会很多,并且这部分fp没有回归,在nms阶段也滤不掉。相比于ap提升而言,工程上减少fp更加重要。Gaussian yolov3的fp相比于yolov3会减少40%,Anchor-free算法暂时接触得不多,就不太了解了。
· 特征提取。VGG16,VGG19,ResNet50,Xception是非常好用的几个特征提取模型。建议使用训练好的经典模型对数据集提取特征向量存储到本地,更方便使用,同时可以大幅度降低显存消耗。
· ensemble:
o 将不同的经典网络提取出的特征向量,假设VGG16提取出的特征向量维度是N,c1],ResNet50提取的特征向量维度是[N,c2],Xception提取的特征向量维度是[N, c3],那么可以使用三个系数a、b、c将其组合为形状为[N, a*c1+b*c2+c*c3],其中a、b、c三个参数的取值代表使用哪个模型的特征多一些,如果是分类回归比赛,在后面接特征处理网络就可以了。可以取不同的a、b、c得到不同的特征,然后对结果做voting,soft-voting等多种处理,一般结果不会太差啦。
o 可以使用不同的初始化方式训练出模型,然后做ensemble。
o 可以使用用不同超参数(如学习率,batch_size,优化器)训练出不同模型,然后做ensemble。