caffe示例实现之4在MNIST手写数字数据集上训练与测试LeNet
2016-07-28 21:39
906 查看
http://blog.csdn.net/liumaolincycle/article/details/47336921
本文主要来自Caffe作者Yangqing Jia网站给出的examples。
2
3
4
5
6
首先从MNIST网站上下载数据集,运行:
2
下载到四个文件,从左至右依次是测试集图像、测试集标签、训练集图像、训练集标签:
转换数据格式:
在 examples/mnist下出现两个新的文件夹:
create_mnist.sh这个脚本是将训练集和测试集分别转换成了lmdb格式。
本实验用的网络模型是LeNet,它是公认在数字分类任务上效果很好的网络。实验中在原始 LeNet基础上做了一点改动,对于神经元的激活,用ReLU替换了sigmoid。
LeNet的设计包括一个卷积层,后随一个pooling层,再一个卷积层,后随一个pooling层,再两个全连接层,类似于传统的多层感知器。经典LeNet如图:
这些层的定义在examples/mnist/lenet_train_test.prototxt中。
在定义自己的网络之前可以运行示例中给出的代码训练网络:
过程与CIFAR-10中的一样,所用solver是examples/mnist/lenet_solver.prototxt,在这个solver中可以看到对训练与测试的简单设置,所用的网络定义就是examples/mnist/lenet_train_test.prototxt。
下面详细学习examples/mnist/lenet_train_test.prototxt的模型定义,学习的基础是对Google
Protobuf比较熟悉,可参考Google Protocol Buffer的使用和原理,还要读过Caffe使用的protobuf定义,这个定义在src/caffe/proto/caffe.proto中。
为了更深入地了解网络是怎么定义的,我们自己写一个caffe网络参数的protobuf。首先新建一个prototxt文件,我这里的命名是lenet_train_lml.prototxt。给网络取个名字:
现在要从之前创建的lmdb中读取MNIST数据,定义如下的数据层:
2
3
4
5
6
7
8
9
10
11
12
定义第一个卷积层:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
pooling层就好定义多了:
2
3
4
5
6
7
8
9
10
11
然后就可以自己写第二个卷积层和pooling层了,细节参考examples/mnist/lenet_train_test.prototxt。
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
27
28
29
30
全连接层也比较简单:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
因为ReLU是元素级操作,所以可以用一种叫做in-place(猜测可以翻译为在原位置,也就是不开辟新内存)的操作来节省内存,这是通过简单地把bottom blob和top blob设成同样的名字来实现,当然了,不要在其他类型的层中这么干。
然后再写一个全连接层:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
最后写一个loss层:
2
3
4
5
6
softmax_loss层实现了Softmax和多项Logistic损失(节省了时间,同时提高了数据稳定性)。它需要两个blob,第一个是预测,第二个是数据层生成的label。该层不产生输出,只是计算loss函数的值,在反向传播的时候使用,并初始化关于ip2的梯度。
层次定义包含的规则是这些层是否以及什么时候包含在网络定义中,像这样:
2
3
4
这个规则基于现有网络状态,控制网络中的层次包含关系,可以查看src/caffe/proto/caffe.proto来获取层次规则及模型概要的更多信息。
在上面的例子中,这个层只会包含在TRAIN阶段中,如果把TRAIN改为TEST,这个层就只会被用于TEST阶段。如果不写TRAIN或TEST的话,那么这个层TRAIN阶段和TEST阶段都会被用到,所以lenet_train_test.prototxt中定义了两个DATA层,我们参考它也分别定义两个DATA层:
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
27
28
29
30
31
32
33
34
另外,TEST阶段有一个Accuracy层,它是用来每100次迭代报告一次模型准确率的,lenet_solver.prototxt中给出了定义,我们同样也把它加上:
2
3
4
5
6
7
8
9
10
再看一下lenet_solver.prototxt中都定义了些什么:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
在写完网络定义和solver之后,就可以训练模型了,运行:
会在终端看到这样的消息,这些消息显示了每一层的细节,即连接关系与输出的形状,在调试的时候是很有用的:
初始化以后就开始训练了:
solver中设置每100次迭代打印出训练的loss,每1000次迭代打印出测试的loss:
迭代完结果就出来了:
最后的模型存储在一个二进制的protobuf文件lenet_iter_10000.caffemodel中,在训练其他数据集的时候可以把它作为基础模型。
通过这个实验,终于知道网络要怎么设置了,还有其他不同的solver值得一试。例如autoencoder网络,运行命令:
或者:
或者:
本文主要来自Caffe作者Yangqing Jia网站给出的examples。
@article{jia2014caffe, Author = {Jia, Yangqing and Shelhamer, Evan and Donahue, Jeff and Karayev, Sergey and Long, Jonathan and Girshick, Ross and Guadarrama, Sergio and Darrell, Trevor}, Journal = {arXiv preprint arXiv:1408.5093}, Title = {Caffe: Convolutional Architecture for Fast Feature Embedding}, Year = {2014} }1
2
3
4
5
6
1.准备数据集
首先从MNIST网站上下载数据集,运行:cd $CAFFE_ROOT ./data/mnist/get_mnist.sh1
2
下载到四个文件,从左至右依次是测试集图像、测试集标签、训练集图像、训练集标签:
转换数据格式:
./examples/mnist/create_mnist.sh1
在 examples/mnist下出现两个新的文件夹:
create_mnist.sh这个脚本是将训练集和测试集分别转换成了lmdb格式。
2.LeNet: MNIST分类模型
本实验用的网络模型是LeNet,它是公认在数字分类任务上效果很好的网络。实验中在原始 LeNet基础上做了一点改动,对于神经元的激活,用ReLU替换了sigmoid。 LeNet的设计包括一个卷积层,后随一个pooling层,再一个卷积层,后随一个pooling层,再两个全连接层,类似于传统的多层感知器。经典LeNet如图:
这些层的定义在examples/mnist/lenet_train_test.prototxt中。
3.定义MNIST网络
在定义自己的网络之前可以运行示例中给出的代码训练网络:sh examples/mnist/train_lenet.sh1
过程与CIFAR-10中的一样,所用solver是examples/mnist/lenet_solver.prototxt,在这个solver中可以看到对训练与测试的简单设置,所用的网络定义就是examples/mnist/lenet_train_test.prototxt。
下面详细学习examples/mnist/lenet_train_test.prototxt的模型定义,学习的基础是对Google
Protobuf比较熟悉,可参考Google Protocol Buffer的使用和原理,还要读过Caffe使用的protobuf定义,这个定义在src/caffe/proto/caffe.proto中。
为了更深入地了解网络是怎么定义的,我们自己写一个caffe网络参数的protobuf。首先新建一个prototxt文件,我这里的命名是lenet_train_lml.prototxt。给网络取个名字:
name: "LeNet"1
3.1 写数据层
现在要从之前创建的lmdb中读取MNIST数据,定义如下的数据层:layer { name: "mnist" #该层的名字 type: "Data" #输入的类型 data_param { #数据参数 source: "mnist_train_lmdb" #数据来源,从 mnist_train_lmdb中读入数据 backend: LMDB batch_size: 64 #批次大小,即一次处理64条数据 scale: 0.00390625 #像素灰度归一化参数,1/256 } top: "data" #该层生成两个blob,分别是data和label top: "label" }1
2
3
4
5
6
7
8
9
10
11
12
3.2 写卷积层
定义第一个卷积层:layer { name: "conv1" #该层的名字 type: "Convolution" #该层的类型 param { lr_mult: 1 } #学习率,权重的和solver的一样 param { lr_mult: 2 } #偏移的是solver的两倍 convolution_param { num_output: 20 #卷积核个数 kernel_size: 5 #卷积核大小 stride: 1 #步长 weight_filler { type: "xavier" #用xavier算法基于输入输出神经元数自动初始化权重 } bias_filler { type: "constant" #简单初始化偏移为常数,默认为0 } } bottom: "data" #该层的上一层是 data top: "conv1" #该层生成conv1 blob }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
3.3 写pooling层
pooling层就好定义多了:layer { name: "pool1" #该层的名字 type: "Pooling" #该层的类型 pooling_param { kernel_size: 2 #pooling的核是2×2 stride: 2 #步长是2,也就是说相邻pooling区域之间没有重叠 pool: MAX #pooling的方式 } bottom: "conv1" #该层的上一层是conv1 top: "pool1" #该层生成pool1 blob }1
2
3
4
5
6
7
8
9
10
11
然后就可以自己写第二个卷积层和pooling层了,细节参考examples/mnist/lenet_train_test.prototxt。
layer { name: "conv2" type: "Convolution" bottom: "pool1" top: "conv2" param { lr_mult: 1 } param { lr_mult: 2 } convolution_param { num_output: 50 #卷积核变成了50个 kernel_size: 5 stride: 1 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { name: "pool2" type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { pool: MAX kernel_size: 2 stride: 2 } }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
27
28
29
30
3.4 写全连接层
全连接层也比较简单:layer { name: "ip1" #全连接层的名字 type: "InnerProduct" #全连接层的类型 param { lr_mult: 1 } param { lr_mult: 2 } inner_product_param { num_output: 500 #输出500个节点 weight_filler { type: "xavier" } bias_filler { type: "constant" } } bottom: "pool2" top: "ip1" }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
3.5 写ReLU层
layer { name: "relu1" type: "ReLU" bottom: "ip1" top: "ip1" }1
2
3
4
5
6
因为ReLU是元素级操作,所以可以用一种叫做in-place(猜测可以翻译为在原位置,也就是不开辟新内存)的操作来节省内存,这是通过简单地把bottom blob和top blob设成同样的名字来实现,当然了,不要在其他类型的层中这么干。
然后再写一个全连接层:
layer { name: "ip2" type: "InnerProduct" param { lr_mult: 1 } param { lr_mult: 2 } inner_product_param { num_output: 10 weight_filler { type: "xavier" } bias_filler { type: "constant" } } bottom: "ip1" top: "ip2" }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
3.6 写loss层
最后写一个loss层:layer { name: "loss" type: "SoftmaxWithLoss" bottom: "ip2" bottom: "label" }1
2
3
4
5
6
softmax_loss层实现了Softmax和多项Logistic损失(节省了时间,同时提高了数据稳定性)。它需要两个blob,第一个是预测,第二个是数据层生成的label。该层不产生输出,只是计算loss函数的值,在反向传播的时候使用,并初始化关于ip2的梯度。
3.7 写层次规则
层次定义包含的规则是这些层是否以及什么时候包含在网络定义中,像这样:layer { #...layer definition... include: { phase: TRAIN } }1
2
3
4
这个规则基于现有网络状态,控制网络中的层次包含关系,可以查看src/caffe/proto/caffe.proto来获取层次规则及模型概要的更多信息。
在上面的例子中,这个层只会包含在TRAIN阶段中,如果把TRAIN改为TEST,这个层就只会被用于TEST阶段。如果不写TRAIN或TEST的话,那么这个层TRAIN阶段和TEST阶段都会被用到,所以lenet_train_test.prototxt中定义了两个DATA层,我们参考它也分别定义两个DATA层:
layer { name: "mnist" type: "Data" top: "data" top: "label" include { phase: TRAIN } transform_param { scale: 0.00390625 } data_param { source: "examples/mnist/mnist_train_lmdb" batch_size: 64 backend: LMDB } } layer { name: "mnist" type: "Data" top: "data" top: "label" include { phase: TEST } transform_param { scale: 0.00390625 } data_param { source: "examples/mnist/mnist_test_lmdb" batch_size: 100 backend: LMDB } }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
27
28
29
30
31
32
33
34
另外,TEST阶段有一个Accuracy层,它是用来每100次迭代报告一次模型准确率的,lenet_solver.prototxt中给出了定义,我们同样也把它加上:
layer { name: "accuracy" type: "Accuracy" bottom: "ip2" bottom: "label" top: "accuracy" include { phase: TEST } }1
2
3
4
5
6
7
8
9
10
4.定义MNIST的solver文件
再看一下lenet_solver.prototxt中都定义了些什么:# 训练/检测网络的protobuf定义 net: "examples/mnist/lenet_train_lml.prototxt" # test_iter指的是测试的迭代次数,这里是100,测试批次大小也是100,这样就覆盖了10000个测试图像 test_iter: 100 # 每训练迭代500次就测试一次 test_interval: 500 # 学习率,动量,权重下降 base_lr: 0.01 momentum: 0.9 weight_decay: 0.0005 # 学习率规则 lr_policy: "inv" gamma: 0.0001 power: 0.75 # 每迭代100次显示一次 display: 100 # 最大迭代次数 max_iter: 10000 # 每5000次迭代存储一次快照 snapshot: 5000 snapshot_prefix: "examples/mnist/lenet" # 选择CPU还是GPU模式 solver_mode: GPU1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
5.训练与测试模型
在写完网络定义和solver之后,就可以训练模型了,运行:./examples/mnist/train_lenet.sh1
会在终端看到这样的消息,这些消息显示了每一层的细节,即连接关系与输出的形状,在调试的时候是很有用的:
初始化以后就开始训练了:
solver中设置每100次迭代打印出训练的loss,每1000次迭代打印出测试的loss:
迭代完结果就出来了:
最后的模型存储在一个二进制的protobuf文件lenet_iter_10000.caffemodel中,在训练其他数据集的时候可以把它作为基础模型。
6.其他
通过这个实验,终于知道网络要怎么设置了,还有其他不同的solver值得一试。例如autoencoder网络,运行命令:./examples/mnist/train_mnist_autoencoder.sh1
或者:
./examples/mnist/train_mnist_autoencoder_adagrad.sh1
或者:
./examples/mnist/train_mnist_autoencoder_nesterov.sh
相关文章推荐
- AngularJS 事件
- javaScript基础语法(上)
- jQuery对象与DOM对象
- D3.js 之面包圈图
- Html与CSS快速入门03-CSS基础应用
- jsp页面引入js文件报错
- SharedPreferences封装类SPUtils
- Java源码阅读-StringBuffer
- 项目中 poi 导出 出现html特殊符号的实体 (已解决)
- Jsoup
- javascript 之DOM篇
- js细节
- html img图片等比例缩放
- jQuery 绑定 select 联动 手动触发
- jquery中选择器的用法
- 页面跳转
- jQuery里鼠标事件的一些简单实现
- 【插件-前端-bootstrap】快速上手布局web页面用什么?快速制表用什么?bootstrap及其插件(1)
- 一个利用Bootstrap写的的小作品
- 单词语音音标正则式查询分析JavaScript应用