一、CNN训练注意事项

神经网络训练采用的方法是SGD,严格意义上是Mini-batch SGD。过程如下:

1、SGD过程

不断循环如下过程:

(1)采样一个 batch 数据(比如 32 张 , 可以做镜像对称):即每次是在这32张图片里求梯度,不是全局也不是一张。在采样数据的过程中可以对图像做镜像对称,镜像对称并不影响图像内容。

(2)前向计算得到损失loss。

(3)反向传播计算一个batch上的梯度。为什么是一个batch上的梯度而不是一张图片上的梯度呢?因为一张一张的做非常耗时间,而且损失函数并不是严格意义上的下降,而是振荡下降,如果只用一张图片则振荡会非常大甚至会上升一段时间再下降。

(4)用这部分梯度迭代更新权重参数。

CNN 训练指标 cnn训练次数_方差


如上图所示除了SGD之外还有一些其它方法,SGD是一阶的,采用高阶的方法可能会快一些,但是在CNN卷积神经网络上做最优化常用的方法还是SGD。

2、两种去均值方式

1、提取整张图像的均值:把这张图片的所有像素点求平均。
2、提取每个通道的均值:对每张图片RGB三个颜色通道分别求均值,最后拿到的数字是3个。

3、权重初始化

3.1、初始化为0

我们思考当给每个权重初始值W=0会发生什么呢?

CNN 训练指标 cnn训练次数_深度学习_02


当W=0时不管中间有没有加激励函数,最后拿到的4个神经元的输入即输出层的值都一摸一样,再往后推一层,因为后面权重W还是为0,所以输出层的值还是一样。

3.2、以很小的随机数来初始化W

我们发现给W初始化都为0的方法不可行,所以我们想到可以给W一个很小的随机数。例如从均值为0,方差为0.01的高斯分布中取值即从0到0.01之间随机选取一些值去初始化网络结构。目的是让激励过后的结果可以符合正态分布。

CNN 训练指标 cnn训练次数_神经网络_03


这种方法对于层次不深的神经网络是可行的,但是一旦作用在深层网络中很容易带来整个网络的不对称性。

假设将此方法作用在10层神经网络上,这10层神经网络共有500个神经元,我们使用tanh作为激励函数。过程如下:

CNN 训练指标 cnn训练次数_CNN 训练指标_04


输出:

CNN 训练指标 cnn训练次数_深度学习_05


CNN 训练指标 cnn训练次数_深度学习_06


打印出来的第一个部分是层次,第二个部分是当前隐层经过激励函数拿到的均值。,只看均值是不够的,还要看方差即波动的情况。做完一遍前向运算后均值如上图上侧左边部分所示,下侧是将均值变化函数画成图的样子。我们发现从正态分布取出的值去初始化权重这种方法在第一个隐层没有问题,应为W是正态分布过来的,人为的保持了对称性。均值为-0.000117,方差为0.213081,有波动;但是当传到第二层时均值减小了,方差的变化也很小;再往后传的话会发现在所有场景下拿到的均值和方差都一样,都接近0,这时再做反向传播时就学不到东西了。

3.3、取输入层与输出层结点个数之间的一个值

基于上述方法无法应用到多层神经网络, 后来又提出了另一个取W的方式,即取输入层和输出层结点个数之间的一个值。

CNN 训练指标 cnn训练次数_方差_07


从上图中我们看到取的值较大也会有问题,当值较大时经过激励函数tanh之后拿到输出结果的均值和方差后会发现均值和方差在频繁的跳,要么是1,要么是-1,基本饱和了。

3.4、Xavier initialization

后来又提出一种方法是W取输入层与输出层结点个数再除以输入层结点个数的平方根。

CNN 训练指标 cnn训练次数_深度学习_08


结果如下:

CNN 训练指标 cnn训练次数_深度学习_09


采用这种方式使用tanh作为激励函数时正常。我们希望输出的方差和输入的方差处在同样的大小,即波动范围一样,但是当我们使用ReLU激励函数后会出现中断。

如下图所示:

CNN 训练指标 cnn训练次数_方差_10


当我们将除以输入个数的平方根换成输入个数除以2的平方根就正常了。

如下图所示:

CNN 训练指标 cnn训练次数_CNN 训练指标_11


CNN 训练指标 cnn训练次数_深度学习_12


上图所示我们会发现直到最后一层都能维持住方差的波动。

CNN 训练指标 cnn训练次数_神经网络_13


上图是损失函数逐渐衰减的过程。

3.5、Batch Normalization

我们发现输出结果受W的影响,但是又不能每次去手动调W,所以我们希望有一套规则不管W怎么样都能约束住最后的结果值。只要每次用这个规则对输出结果做一遍处理,就能让结果值处于一个可控的高斯分布状态。

CNN 训练指标 cnn训练次数_神经网络_14


BN通常在全连接层,全连接层两两之间都会有一个权重组合,后一层的其中一个神经元是由前一层所有神经元的W和x线性组合而成,所以会导致次神经元的波动性可能会更大,由此会把约束这一层放在FC后面。

BN学习过程:

CNN 训练指标 cnn训练次数_cnn_15


BN的输入是全连接层的输出,要学习CNN 训练指标 cnn训练次数_CNN 训练指标_16CNN 训练指标 cnn训练次数_cnn_17两个参数。由上图我们看到有m个输入,对m个输入求均值得到CNN 训练指标 cnn训练次数_深度学习_18,有了CNN 训练指标 cnn训练次数_深度学习_18又求得方差。然后根据根据均值和方差把x化到一个符合正态分布的输入,CNN 训练指标 cnn训练次数_CNN 训练指标_16CNN 训练指标 cnn训练次数_cnn_17的作用就是对做过正态分布约束的输出做一个线性的组合作为tanh的输入。

Batch Normalization的优点:
(1)梯度计算更为顺畅;
(2)学习率设高一点也没关系;
(3)对于初始值的依赖减少了;
(4)也可以看做一种正则化 , 减少了对 dropout的需求 。
注意在训练中途要去看一个神经网络是否工作正常,先取一个非常小的神经网络,先跑一下看看能不能收敛到准确度为1的程度,因为如果在一小部分中都没有收敛到1的话那么一定是代码的实现有问题。

4、正则化(Dropout)

神经网络正则化方式:别一次开启所有学习单元。

CNN 训练指标 cnn训练次数_方差_22


如上图所示,dropout做的事情就是在做一次前向运算时,会给每一个神经元都加一个开关,会随机的把里面一部分开关关掉。

注意:在实际工程中可以按上述方法来做,但是在做预测时会很麻烦。所以在实际工程中不会中途把开关打开或者关上,而是给所有x乘以概率p。后来想到一个更好的方法就是在训练时将所有x都除以概率p,这时做预测时就不用做任何处理了。

CNN 训练指标 cnn训练次数_深度学习_23


正则化方法可以防止过拟合,因为神经元的记忆力非常好,如果记的太清楚的话会出现问题。假如猫有一只耳朵,神经元记住了有耳朵的就是猫,但是狗也有一只耳朵。这样就会出现识别错误的现象。所以需要有泛化能力。

二、caffe框架使用

1、特点

(1)来源于 Berkeley 的开源框架;
(2)由C++ 完成;
(3)有 python 和 mathlab 的接口;
(4)对于卷积神经网络的训练和 fine-tuning 非常方便。

2、主要的类\模块

(1)Blob:主要用来表示网络中的数据,包括训练数据,网络各层自身的参数(包括权值、偏置以及它们的梯度),网络之间传递的数据都是通过 Blob 来实现的,同时 Blob 数据也支持在 CPU 与 GPU 上存储,能够在两者之间做同步。
(2)Layer:是对神经网络中各种层的一个抽象,包括我们熟知的卷积层和下采样层,还有全连接层和各种激活函数层等等。同时每种 Layer 都实现了前向传播和反向传播,并通过 Blob 来传递数据。
(3)Net:是对整个网络的表示,由各种 Layer 前后连接组合而成,也是我们所构建的网络模型。
(4)Solver:定义了针对 Net 网络模型的求解方法,记录网络的训练过程,保存网络模型参数,中断并恢复网络的训练过程。自定义 Solver 能够实现不同的网络求解方式。

3、使用方法

3.1 Resize 图片 ,转换存储格式

CNN 训练指标 cnn训练次数_cnn_24

3.2 定义网络结构(编辑 prototxt )

CNN 训练指标 cnn训练次数_CNN 训练指标_25

3.3 定义 solver (编辑另一个 prototxt )

CNN 训练指标 cnn训练次数_方差_26

3.4 训练(可以基于已有的权重赋值 , 跑一个脚本 )

CNN 训练指标 cnn训练次数_神经网络_27

4、关于fine-tuning

如果层次不变,只需修改输入和输出。

CNN 训练指标 cnn训练次数_方差_28


如果层次改变,则需要手动添加\删减层次:

CNN 训练指标 cnn训练次数_cnn_29


fine-tuning技巧/注意点:

(1)优先学习权放在新加层:每一层都有控制学习率的参数: blobs_lr,通常情况下我们 一般会把前面层学习率调低,最后新加层调高,甚至可以freeze前面的层次不动。一般fine-tuning的前期loss下降非常快,中间有个瓶颈期,要有耐心。

(2)在solver处调整学习率:调低solver处的学习率,记住存储一下中间结果,以免出现意外。