分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
控制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
设置为step
和step_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_prefix
为snapshot/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的卷积神经网络模型。下次实验,我们会利用这个模型去开发一个图片字母识别程序,让我们的模型真正的能够发挥作用。
本次实验,我们学习了:
- 让学习速率随着训练的过程逐渐变小可以使最终的参数更接近理想点。
- 正则化是保证模型获得较高的泛化性能的重要手段之一。
四、课后作业
solver.prototxt
中的超参数我已经提前设置好了,请你自己尝试不同的超参数设置,观察超参数的变化对模型训练过程的影响。