1. LeNet简介:MNIST分类模型
LeNet网络能很好地执行数字分类任务。本文使用了一个轻量级的不同于原始LeNet的版本实现,在神经网络中,使用线性修正单元激活函数ReLU取代sigmoid函数。
LeNet的设计包括CNNS。概括来说,它的组成包含一个卷积层,紧跟一个池化层,另一个卷积层紧跟一个池化层,然后是两个全连接层类似于卷积多层感知器。层的定义是在$CAFFE_ROOT/examples/mnist/lenet_train_test.prototxt里。
2. 准备数据集
从MNIST网站下载和转换数据格式。
cd $CAFFE_ROOT
./data/mnist/get_mnist.sh
./examples/mnist/create_mnist.sh
脚本执行后,生成两个数据集 mnist_train_lmdb和 mnist_test_lmdb
3. 定义MNIST网络
lenet_train_test.prototxt 的模型定义指定了MNIST手写体分类的LeNet模型。Caffe使用的protobuf定义文件在$CAFFE_ROOT/src/caffe/proto/caffe.proto。写一个 caffe::NetParameter的protobuf,给网络起一个名字,然后开始:
name: “LeNet”
3.1 数据层
这个例子,从之前创建的lmdb读取MNIST数据,一个数据层的定义如下:
“`shell
layer {
name: “mnist”
type: “Data”
top: “data”
top: “label”
include {
phase: TRAIN
}
transform_param {
scale: 0.00390625
}
data_param {
source: “./mnist_train_lmdb”
batch_size: 64
backend: LMDB
}
}
这个层的名字是mnist,类型是data。从给定的lmdb源中读取数据。使用的批尺寸是64,对传入的像素点进行缩放使之在[0,1)范围内。
这层只包含于TRAIN阶段,如果修改TRAIN为TEST,这层就会只用于test阶段。
## 3.2 卷积层
定义第一个卷积层:
```shell
layer {
name: "conv1"
type: "Convolution"
bottom: "data"
top: "conv1"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
convolution_param {
num_output: 20
kernel_size: 5
stride: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
<div class="se-preview-section-delimiter"></div>
这一层输入data blob(由数据层提供),产生conv1层。产生20通道的输出,卷积核尺寸是5,步长是1。
填充器允许任意初始化权重和偏置值。对于权重填充,使用基于输入和输出神经元数量的xavier算法自动确定初始化的scale。对于偏置填充器,简单的初始化为常数,默认的填充值是 0。
lr_mult是学习率调整图层的学习参数。在这种情况下,我们将把权值学习速率设置为与运行时解决器所给出的学习速率相同,并且偏置学习速率是该算法的两倍,这通常会导致更好的收敛速度。
3.3 池化层
定义第一个池化层:
layer {
name: "pool1"
type: "Pooling"
bottom: "conv1"
top: "pool1"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
<div class="se-preview-section-delimiter"></div>
执行一个最大池化,池化核尺寸为2步长为2(相邻池化区域间没有重叠)。
类似的,可以写第二个卷积和池化层。具体可以查看
$CAFFE_ROOT/examples/mnist/lenet_train_test.prototxt 文件。
3.4 全连接层
定义一个500个输出的全连接层(在Caffe中为InnerProduct层)。
layer {
name: "ip1"
type: "InnerProduct"
bottom: "pool2"
top: "ip1"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 500
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
<div class="se-preview-section-delimiter"></div>
3.5 ReLU激活层
layer {
name: "relu1"
type: "ReLU"
bottom: "ip1"
top: "ip1"
}
<div class="se-preview-section-delimiter"></div>
ReLU在in-place操作节省一些内存。这是通过简单地给底部和顶部的blobs提供相同的名称来实现的。
ReLU层之后,写另外的innerproduct 层:
layer {
name: "ip2"
type: "InnerProduct"
bottom: "ip1"
top: "ip2"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 10
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
<div class="se-preview-section-delimiter"></div>
3.6 Accuracy层
输出分类(预测)精确度,只有test阶段才有,因此需要加入include参数。
layer {
name: "accuracy"
type: "Accuracy"
bottom: "ip2"
bottom: "label"
top: "accuracy"
include {
phase: TEST
}
}
<div class="se-preview-section-delimiter"></div>
3.7 损失层
layer {
name: "loss"
type: "SoftmaxWithLoss"
bottom: "ip2"
bottom: "label"
top: "loss"
}
<div class="se-preview-section-delimiter"></div>
softmax_loss层实现了softmax和多项式对数损失(节省时间和提高数值计算的稳定性)。它需要两个blobs,第一个是预测,第二个是数据层提供的标签。它不产生任何输出,只是计算损失函数值。
4. MNIST solver定义
solver配置文件的主要作用就是交替调用前向(forward)算法和后向(backward)算法来更新参数,从而最小化loss,实际上就是一种迭代的优化算法。结合loss,用gradient更新weights。
caffe提供了六种优化算法来求解最优参数,在solver配置文件中,通过设置type类型来选择。
lenet_solver.prototxt代码
<div class="se-preview-section-delimiter"></div>
# The train/test net protocol buffer definition
net: "./lenet_train_test.prototxt"
<div class="se-preview-section-delimiter"></div>
# test_iter specifies how many forward passes the test should carry out.
<div class="se-preview-section-delimiter"></div>
# In the case of MNIST, we have test batch size 100 and 100 test iterations,
<div class="se-preview-section-delimiter"></div>
# covering the full 10,000 testing images.
test_iter: 100
<div class="se-preview-section-delimiter"></div>
# Carry out testing every 500 training iterations.
test_interval: 500
<div class="se-preview-section-delimiter"></div>
# The base learning rate, momentum and the weight decay of the network.
base_lr: 0.01
momentum: 0.9
weight_decay: 0.0005
<div class="se-preview-section-delimiter"></div>
# The learning rate policy
lr_policy: "inv"
gamma: 0.0001
power: 0.75
<div class="se-preview-section-delimiter"></div>
# Display every 100 iterations
display: 100
<div class="se-preview-section-delimiter"></div>
# The maximum number of iterations
max_iter: 10000
<div class="se-preview-section-delimiter"></div>
# snapshot intermediate results
snapshot: 5000
snapshot_prefix: "model/lenet"
<div class="se-preview-section-delimiter"></div>
# solver mode: CPU or GPU
solver_mode: CPU
<div class="se-preview-section-delimiter"></div>
5.训练和测试模型
在写完了lenet_solver.prototxt和lenet_train_test.prototxt文件后,运行caffe训练模型命令,solver.prototxt文本文件作为参数
./caffe/build/tools/caffe train --solver=./lenet_solver.prototxt
当运行代码时,会看到很多诸如下面的信息:
这些信息告诉你每层的细节,它的连接,输出维度,这会在调试时很有用。初始化之后,训练将会开始:
基于solver的设置,每100迭代会打印出训练的损失函数,每500迭代测试网络。将会看到诸如下面的信息:
…
对于每个训练迭代,lr是学习率,loss是训练函数。对于测试阶段的输出,score 0是准确率,score 1 是测试损失函数。
数分钟后,训练结束!
最后的模型,保存为一个二进制protobuf文件,保存在lenet_iter_10000.caffemodel里。
更多精彩原创文章,详见红象云腾社区