卷积神经网络的参数设置

这个举个例子来说是最好的,因为直接说会抽象一些,在网上找了一个caffe-ssd的参数设置配置文件solver.prototxt,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
train_net:"train.prototxt"
test_net:"test.prototxt"
test_iter: 299
test_interval: 200
base_lr: 0.01
display: 20
max_iter: 6720
lr_policy: "step"
gamma: 0.1
power: 0.75
momentum: 0.9
weight_decay: 0.0001
snapshot: 224
snapshot_prefix: "snapshot"
solver_mode: GPU
device_id:0
debug_info:false
snapshot_after_train:true
test_initialization:false
average_loss:10
iter_size:1
stepsize:2000
type: SGD
eval_type:"detection"
ap_version:"11point"
show_per_class_result:true

我们就一个个来解析一下。

  • train_net:训练的模型文件的路径。
  • test_net:测试的模型文件的路径。
  • test_iter:网络的迭代测试次数。网络一次测试batch_size张图片,因为为了可以将验证集中所有图片都测试一次,这个参数乘以batch_size应该等于验证集中的图片数。
  • test_interval:网络迭代多少次进行一次测试。一次迭代即是将一个batch_size的图片进行训练。这个文件中test_interval设为了200,也就是说每隔200次对网络的准确率进行一次验证。一般来讲,我们是需要将训练集合中的所有图片都跑一次再对准确率进行测试,也就是test_interval乘以batch_size不小于训练集中的图片总数。
  • base_lr:表示网络的基础学习率,学习率过高可能会无法梯度下降,loss保持不变,也可能loss不能收敛。而学习率过低会使网络收敛速度缓慢,也可能导致梯度消失。一般初始学习率设为0.01
  • display:每隔多少次显示一次。也就是在屏幕上打印一次loss和准确率。
  • max_iter:网络的最大迭代次数。训练集中的图片需要训练多次,所以这个参数一般比较大。
  • lr_policy:学习率变化策略,这里面又分为如下几类:
    • fixed:保持base_lr不变。
    • step:如果设置为step,则还需要设置一个stepsize, 返回 base_lr * gamma ^ (floor(iter / stepsize)),其中iter表示当前的迭代次数。
    • exp:返回base_lr * gamma^ iteriter表示当前的迭代次数。
    • inv: 如果设置为inv,还需要设置一个power, 返回base_lr * (1 + gamma * iter) ^ (- power)
    • multistep:如果设置为multistep,则还需要设置一个stepvalue。这个参数和step很相似,step是均匀等间隔变化,而multistep则是根据 stepvalue值变化。
    • poly:学习率进行多项式误差, 返回 base_lr (1 - iter/max_iter) ^ (power)
    • sigmoid:学习率进行sigmod衰减,返回 base_lr ( 1/(1 + exp(-gamma * (iter - stepsize))))
  • gamma:因为这里学习率衰减策略为step,所以需要设置gammapower两个参数。
  • power:同上。
  • momentum:上一次梯度更新的权重,一般取值在0.5--0.99之间。通常设为0.9momentum可以让使用SGD的深度学习方法更加稳定以及快速。
  • weight_decay:权重衰减项,防止过拟合的一个参数。在损失函数中,weight decay是放在正则项(regularization)前面的一个系数,正则项一般指示模型的复杂度,所以weight decay的作用是调节模型复杂度对损失函数的影响,若weight decay很大,则复杂的模型损失函数的值也就大。
  • sanpshot:每多少次保存一次学习的结果,在caffe框架下就是caffemodel
  • snapshot_prefix:模型保存的路径。
  • solver_model:设置使用GPU还是CPU进行学习训练。
  • device_id:GPU序列号,默认从开始。
  • debug_info:是否显示debug信息。
  • snapshot_after_train:是否在训练结束后保存一个snapshot文件。便于以后可以在此基础上继续训练。
  • test_initialization:确保内存可以用并输出loss的初始值。
  • average_loss:显示loss为之前average_lossloss的平均值。
  • iter_size:每处理iter_size*batch_size张图片后进行一次梯度计算。
  • stepsize:每多少次学习率递减。这里是迭代2000次学习率递减。
  • type:优化算法的选择,一共有六种可选:SGD、AdaDelta、AdaGrad、Adam、Nesterov和RMSProp。默认为SGD
  • eval_type:评价类型。
  • ap_version:计算平均准确率(map值)的方法,有11point、MaxIntegral、Integral三种,l1point是SSD在VOC2007中计算AP的方法,使用了简单的均值计算。MaxInterral是SSD在VOC2012中使用的最大值积分法。Integral是普通积分方法。
  • show_per_class_result:在终端输出每一类的AP(每一类的检测精度)信息。

提高卷积神经网络的泛化能力

在做工程的时候如何提高自己训练出来的模型的泛化能力是一项具有挑战性同时也是一件充满”玄学”的事情。大概可以总结为下面这几点。

  • 使用更多的数据。竟可能标注更多的训练数据,这是提高泛化能力最理想的方法,更多的数据让模型得到更充分的学习,自然提高了泛化能力,但实际场景中考虑到标注成本的问题,可能并不能无脑加数据。
  • 使用更大的batch_size。在相同迭代次数和学习率的条件下,每批次采用更多的数据将有助于模型更好的学习到正确的模式,模型输出结果也会更加稳定。
  • 数据过采样。很多情况下我们拿到手的数据都存在类别不均匀的情况,模型这个时候过多的拟合某类数量多的数据导致其输出结果偏向于该类数据,此时如果我们过采样其他类别的数据,使得数据量比较均衡可以一定程度提高泛化能力。
  • 数据增强。数据增强是指在数据有限的情况通过一些几何操作对图像进行变换,使得同类数据的表现形式更加丰富,以此提高模型的泛化能力。数据增强是一门比较大的学问,在分类,检测,分割中数据增强的方式都有区别,我们可以通过研究优秀的开源代码实现的数据增强策略来应用到我们自己的任务中。
  • 修改损失函数。这方面有大量的工作,如目标检测中的Focal Loss, GHM Loss,IOU Loss等都是为了提升模型的泛化能力。
  • 修改网络。如果网络过浅并且参数量过少往往会使得模型的泛化能力不足导致欠拟合,此时一般考虑使用简单的堆叠卷积层增加网络的参数,提高模型的特征提取能力。而如果网络过深且训练数据量比较少,那么就容易导致模型过拟合,此时一般需要简化网络结构减少网络层数或者使用resnet的残差结构以及bn层。
  • 权重惩罚。权重惩罚也即是正则化操作,一般是在损失函数中添加一项权重矩阵的正则项作为惩罚项,用来惩罚损失值较小时网络权重过大的情况,此时往往是网络权值过拟合了数据样本。
  • Dropout策略。如果网络最后有全连接层可以使用Dropout策略,相当于对深度学习模型做了Ensemble,有助于提高模型的泛化能力。

一些调参技巧

做工程

  • 3 x 3卷积是CNN的主流组件。平时有设计一些解决分类,回归任务的网络,里面的卷积核基本都设置为3 x 3,要说原因的话应该去问问VGG16吧。两个3 x 3的卷积核堆叠能获得5 x 5卷积核的感受野并且参数比5 x 5卷积核少,所以是大量推荐使用的。

  • 可以适当使用1 x N卷积。为什么要提这一点呢,这是因为1 X N卷积可以减少计算量,并且1 X N卷积可以在某个方向强调感受野,也就是说假如如果你要对一个长方形形状的目标进行分类,你可以使用1 X N的卷积核搭配3 X 3的卷积核对长边方向设定更大的感受野,或许可以获得泛化性能的提升。

  • ACNet结构。这个研究来自于ICCV2019,可以在3 X 3卷积的基础上加上1 X 3和3 X 1的旁路卷积核,最后在推理阶段把三个卷积核都fusion到3 X 3卷积核上,在许多经典CV任务上都可以获得大概1个点的提升。
  • 卷积核权重初始化方式。对于weight的初始化一般都是使用xavier初始化。当然也可以可以尝试何凯明大神的He初始化。对于bias的初始化全置于0。
  • Batch Normalization。这是一直在使用的技巧,可以很大程度的加快收敛速度。建议搭建自己网络的时候尽量加上BN,如果有BN了全连接层就没必要加Dropout了。
  • 目标检测不能盲目去掉fpn结构。在针对自己的数据调检测任务如yolov3的时候不能盲目砍掉fpn结构,尽管你分析出某个分支的Anchor基本不可能会对你预测的目标起作用,但如果你直接去掉分支很可能会带来漏检。
  • 优化器的选择。基本都是带动量的SGD。如果优化不动可以试试Adam
  • 激活函数。可以先用ReLU做一版,如果想再提升精度可以将ReLU改成PReLU试试。我更倾向于直接使用ReLU
  • batch_size:在不同类型的任务中,batch_size的影响也不同。
  • 初始学习率。一般是从0.01开始设置,个人认为这个学习率和学习率衰减策略是相关的,但不宜设置的过大过小,0.010.1应该是比较常用的。学习率衰减策略一般使用multistep方式,step_size的设置要看视你的的max_iter而定。
  • 数据与处理之zero-center。主要有2个步骤,第一个是减均值,第二个是除以方差。这样做下来最后的输入值域为[-1,1],一般减均值是最常用的,后面的除以方差用不用可能需要自己动手试验一下看看效果。
  • 残差结构和密集连接。resnet的残差结构和dense net密集连接结构,做工程的时候考虑到速度近乎不可能说完全使用完整版本的resnetdensenet的完整结构,但我们可以自己动手将我们网络的某些模块替换为残差结构和密集连接,替换的时候可以适当降低这俩结构的复杂度,类似于通道数减半,密集连接中只保留一半连接等等。这里需要做一些消融实验来验证改进后的精度。
  • 关于loss。优秀的loss一般是对模型的泛化性能有所改善的,但在用loss的时候往往并不是直接替换loss那么简单,需要仔细思考loss背后的数学原理,要用对地方才可有提升。
  • 找到模型调参时的可靠评价指标。在调整参数训练模型时一定要找到正确的评价指标,没调整一个参数就要记录一下模型的评价指标如准确率,map值,miou值等。并且在调参时建议将调整的参数和在测试集上的精度组合成一个字符串给模型重命令,方便之后快速review
  • 使用了带backbone的网络,如训练VGG16-SSD建议选择finetune的方式,从头训练不仅费时费力,甚至难以收敛。

做比赛

  • 特征提取。VGG16,VGG19,ResNet50,Xception是非常好用的几个特征提取模型。建议使用训练好的经典模型对数据集提取特征向量存储到本地,更方便使用,同时可以大幅度降低显存消耗。

  • ensemle:

    • 将不同的经典网络提取出的特征向量,假设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得到不同的特征,然后对结果做votingsoft-voting等多种处理,一般结果不会太差。
    • 可以使用不同的初始化方式训练出模型,然后做ensemble。
    • 可以使用用不同超参数(如学习率,batch_size,优化器)训练出不同模型,然后做ensemble。