使用卷积神经网络进行图片分类 3

来源:互联网 发布:java连接数据库的框架 编辑:程序博客网 时间:2024/05/29 18:56

控制caffe模型的训练过程

一、实验介绍

1.1 实验内容

上次实验,我们已经构建好了卷积神经网络,我们的模型已经蓄势待发,准备接受训练了。为了控制训练进程,记录训练过程中的各种数据,caffe还需要定义另一个solver.prototxt文件,这次实验我们就来完成它,并开始激动人心的训练过程。

1.2 实验知识点

  • 可变的学习速率
  • 正则化

1.3 实验环境

  • caffe 1.0.0

二、实验步骤

2.1 指定网络定义文件

solver.prototxt中,我们需要先指定网络定义文件的位置,我们通过下面的语句指定:

net: "network.prototxt"

2.2 可变的学习速率

在课程814中,有这么一张图:

此处输入图片的描述
当学习速率固定时,每次参数更新的“步长”会慢慢变短。我将为什么步长会变短作为一个选做课后作业留在了那里,其实不难思考,因为学习速率固定,而损失函数图形的“倾斜”程度一直在变小,即损失函数对参数的梯度的数值一直在变小,所以最后更新的“步长”会越来越短。

这个特性有利于我们的模型训练过程,因为越接近损失函数最低点我们希望更新的步长越小,这样才能使参数更逼近最低点。但为了保证每次更新的步长越来越小,也可以在训练的过程中减小学习速率的数值,在solver.prototxt中可以像下面这样定义:

base_lr: 0.001lr_policy: "step"stepsize: 4000gamma: 0.1max_iter: 10000

其中base_lr指定在开始训练时的学习速率,这里设置为0.001, lr_policy设置为stepstep_size设置为4000就指定了学习速率每隔4000次训练周期(epoch)就自动减小。而gamma的数值就是每次减小学习速率时乘以的数值,这里设置为0.1就代表每次将学习速率减小到原来的十分之一。

max_iter指定训练的最大迭代周期数,这里设置成10000次。

2.3 测试周期

在前一次实验中,我们的测试数据层(phase设置为TEST的数据层)中的batch_size被设置成了100,而我们的测试数据总共有10000张图片,为了每次测试将所有图片都测试一次,这里需要如下设置:

test_iter: 100test_interval:500

test_iter为100即测试的迭代次数为100次,这样100x100等于10000,刚好把所有图片都测试一次。

同时,这里设置test_interval为500表明每训练500个周期执行一次测试。

2.4 正则化

其实早在课程814中,我就非常想讲解正则化,但是一直担心一次性介绍太多内容会让人难以消化,这里我终于迎来了必须讲解正则化的机会。
为了理解正则化在深度学习中的作用,我们以回归问题为例讲解。

此处输入图片的描述

如图,假设我们现在要根据图中的六个点拟合出数据的分布曲线,用于预测其他x坐标对应的y值,我们使用如下的多项式进行拟合:
y=a0+a1*x+a2*x^2+a3*x^3+...

假如一开始,只有a0和a1不为0,其他系数a都为0,拟合函数就变成了:y=a0+a1*x, 拟合曲线如下:

此处输入图片的描述
由于此时的系数比较少,曲线不够灵活,所以此时拟合的误差较大,损失函数较大。

如果我们再增加一个不为0的系数a2, 拟合函数变成了:y=a0+a1*x+a2*x^2, 拟合曲线如下:

此处输入图片的描述

这时的拟合效果已经非常好了,但注意仍然有一些数据点的中心不在拟合曲线上,即损失函数值大于0。
假如此时再增加一个不为0的系数a3, 拟合函数变成了:y=a0+a1*x+a2*x^2+a3*x^3, 注意每次增加一个不为0的系数,相当于是拟合函数变得更加的灵活。这时拟合曲线可能如下:

此处输入图片的描述
此时拟合函数经过每一个数据点的中心,损失函数为0。但拟合函数的形状已经有些奇怪了。
如果我们继续增加更多的不为0的系数,拟合函数曲线甚至可能变成这样:

此处输入图片的描述
拟合函数非常精确的经过了每一个数据点且此时的损失函数值为0,但使用这个拟合函数来预测新的x点对应的y的值可能不会取得非常好的结果。联想之前我们学过的泛化性能,这里的泛化性能会非常差。或者说,这里出现了过拟合(overfitting)

我们的深度神经网络模型就可能会出现这个问题,虽然在训练集上的损失函数值已经非常低,甚至为0,但可能仍然无法在验证/测试集上获得很好的泛化性能。

为了避免过拟合,就需要我们这里的正则化(regularization), 在solver.prototxt里像下面这样设置正则化参数:

regularization_type: "L2"weight_decay:0.0001

那么具体正则化是如何防止过拟合发生的呢?实际上,这里的正则化,是通过在前向计算的过程中,将网络中所有的参数的平方与weight_decay相乘,再加到损失函数值上;而反向传递梯度时,则仍然根据链式法则对参数进行更新。
比如这里的weight_decay为0.0001,假设只有一个参数a=3,则损失函数值计算时就变成了:L=L0+0.0001*3^2=L0+0.0009,这里的L0是不添加正则化时的损失函数值。反向传递梯度时,对参数的更新就变成了a=a-lr*(d0+2*0.0001*3), 其中d0是不添加正则化时的梯度值,而2*0.0001*3是误差函数中的正则化项对参数求导(梯度)的结果。实际上,由于这里的a值就是3,之前的式子可以直接改成:a=(1-lr*2*0.0001)*a-lr*d0。合理的设置学习速率lr和正则化参数weight_decay的值,可以使(1-lr*2*weight_decay)的值小于1,这样的效果实际上是使得参数a值每次都以一定的比例缩小, 防止参数变得过大。这样可以在一定程度上使高次项的系数变小,从而防止高次项对整个模型的影响过大。从而最终达到防止过拟合的目的。

正则化在这里属于稍难的内容,如果你不需要非常深刻的理解深度学习而只是想能够实际上手,可以暂时不去深究正则化的原理,只需要记住caffe里的这两个参数是用来进行正则化,从而提高模型效果就行了。

regularization_type设置为L2就是代表对损失函数加上参数的平方项,还可以将其设置为L1,这样加上的就是参数的绝对值。(实际上,这里的L2代表的是2-范数,而L1代表的是1-范数)

2.5 其他设置

我们的solver.prototxt还剩下下面这些设置项:

display: 100snapshot: 2000snapshot_prefix: "snapshot/alpha"solver_mode: CPU

其中display: 100代表训练是每隔100隔训练周期显示一次损失函数值
snapshlt: 2000代表每隔2000个训练周期将当前模型的参数caffemodel和训练过程中的其他数据solverstate存入快照,快照存入的位置由snapshot_prefix指定
solver_mode指定训练是在CPU还是GPU上进行,这里是CPU

2.6 开始训练

终于,我们的solver.prototxt也写好了,可以开始我们的训练了。训练前,你先要确保存放快照文件的文件夹存在,由于这里snapshot_prefixsnapshot/alpha,所以我们的快照最终后保存在snapshot目录中,我们运行以下命令创建这个目录:

mkdir snapshot

通过以下命令执行训练过程:

caffe train -solver solver.prototxt

train代表了现在我们是要进行训练,-solver solver.prototxt指定了我们的solver.prototxt文件的位置。

如果没有出错的话,你会先看到很长的一串输出(caffe创建模型时的日志输出),接着就是类似这样的损失函数值输出:

此处输入图片的描述

可以看到,每隔100个训练周期,会有损失函数值的输出,损失函数值大致是呈递减的趋势。每隔500个训练周期,会有测试准确率输出,准确率大致呈递增的趋势。

整个训练过程10000次迭代大致需要三分钟,训练结束后,差不多能够达到0.994的准确率。

就这样,我们使用caffe训练出了第一个卷积神经网络模型(鼓掌)。在snapshot文件夹下面,你可以找到训练完毕的模型参数文件。

此处输入图片的描述

三、实验总结

至此,模型的构建和训练过程已经全部完成了,我们拥有了一个准确率超过0.99的卷积神经网络模型。下次实验,我们会利用这个模型去开发一个图片字母识别程序,让我们的模型真正的能够发挥作用。

本次实验,我们学习了:

  • 让学习速率随着训练的过程逐渐变小可以使最终的参数更接近理想点。
  • 正则化是保证模型获得较高的泛化性能的重要手段之一。

四、课后作业

  1. solver.prototxt中的超参数我已经提前设置好了,请你自己尝试不同的超参数设置,观察超参数的变化对模型训练过程的影响。
原创粉丝点击