用Caffe在MNIST上训练LeNet
我们假设你已经成功编译了Caffe。如果没有,请参阅安装页面。在本教程中,我们将假设您的Caffe安装位于CAFFE_ROOT
。
准备数据集
您首先需要从MNIST网站下载并转换数据格式。为此,只需运行以下命令:
cd $CAFFE_ROOT
./data/mnist/get_mnist.sh
./examples/mnist/create_mnist.sh
如果它抱怨wget或未gunzip安装,则需要分别安装它们。运行脚本后,应该有两个数据集mnist_train_lmdb,和mnist_test_lmdb。
image.png在window上这就是然并卵啊!!! 没事有招..
在windows下使用caffe时,如果先前没有啥经验,会考虑按照官方文档中的例子跑一跑。比如mnist手写数字识别。
然后就会遇到这个问题:windows下怎么执行/examples/mnist/create_mnist.sh呢?
当然,你需要先编译了convert_mnist_data子项目,以及下载了mnist数据集。
ok,要执行的脚本create_mnist.sh是shell语法,不妨转写为python
# create_mnist.py
import os
import shutil
EXAMPLE='F:\\py\\mnist'
DATA='F:\\py\\mnist'
BUILD='G:\\caffe\\bin'
BACKEND='lmdb'
print "Createing "+BACKEND+"..."
#rm -rf $EXAMPLE/mnist_train_${BACKEND}
#rm -rf $EXAMPLE/mnist_test_${BACKEND}
path1=EXAMPLE+"\\mnist_train_"+BACKEND
path2=EXAMPLE+"\\mnist_test_"+BACKEND
if os.path.exists(path1):
shutil.rmtree(path1)
if os.path.exists(path2):
shutil.rmtree(path2)
s1=BUILD+"\\convert_mnist_data.exe"
s2=DATA+"\\train-images.idx3-ubyte"
s3=DATA+"\\train-labels.idx1-ubyte"
s4=EXAMPLE+"\\mnist_train_"+BACKEND
s5="--backend="+BACKEND
cmd=s1+" "+s2+" "+s3+" "+s4+" "+s5
print cmd
os.system(cmd)
t1=BUILD+"\\convert_mnist_data.exe"
t2=DATA+"\\t10k-images.idx3-ubyte"
t3=DATA+"\\t10k-labels.idx1-ubyte"
t4=EXAMPLE+"\\mnist_test_"+BACKEND
t5="--backend="+BACKEND
cmd=t1+" "+t2+" "+t3+" "+t4+" "+t5
print "cmd2="+cmd
os.system(cmd)
PS G:\Projects\caffe\examples\mnist> py ./create_mnist.py
Createing lmdb...
G:\caffe\bin\convert_mnist_data.exe F:\py\mnist\train-images.idx3-ubyte F:\py\mnist\train-labels.idx1-ubyte F:\py\mnist\
mnist_train_lmdb --backend=lmdb
I0302 14:46:29.084498 7608 common.cpp:36] System entropy source not available, using fallback algorithm to generate see
d instead.
I0302 14:46:29.086498 7608 db_lmdb.cpp:40] Opened lmdb F:\py\mnist\mnist_train_lmdb
I0302 14:46:29.086498 7608 convert_mnist_data.cpp:93] A total of 60000 items.
I0302 14:46:29.087498 7608 convert_mnist_data.cpp:94] Rows: 28 Cols: 28
I0302 14:46:40.301141 7608 convert_mnist_data.cpp:113] Processed 60000 files.
cmd2=G:\caffe\bin\convert_mnist_data.exe F:\py\mnist\t10k-images.idx3-ubyte F:\py\mnist\t10k-labels.idx1-ubyte F:\py\mni
st\mnist_test_lmdb --backend=lmdb
I0302 14:46:40.795168 14096 common.cpp:36] System entropy source not available, using fallback algorithm to generate see
d instead.
I0302 14:46:40.798168 14096 db_lmdb.cpp:40] Opened lmdb F:\py\mnist\mnist_test_lmdb
I0302 14:46:40.798168 14096 convert_mnist_data.cpp:93] A total of 10000 items.
I0302 14:46:40.798168 14096 convert_mnist_data.cpp:94] Rows: 28 Cols: 28
I0302 14:46:41.637217 14096 convert_mnist_data.cpp:113] Processed 10000 files.
PS G:\Projects\caffe\examples\mnist>
生成好训练的数据了
LeNet:MNIST分类模型
在我们实际运行培训计划之前,让我们先解释一下会发生什么。我们将使用已知在数字分类任务中运作良好的LeNet网络。我们将使用与原始LeNet实施方案略有不同的版本,用神经元的整流线性单位(ReLU)激活代替S形激活。
LeNet的设计包含了CNN的本质,这些CNN仍然用于ImageNet等大型模型中。一般来说,它由一个卷积层,一个汇集层,另一个卷积层,然后是一个汇集层,然后是两个完全连接的层,类似于传统的多层感知器。我们已经定义了图层$CAFFE_ROOT/examples/mnist/lenet_train_test.prototxt
。
定义MNIST网络
本节介绍了lenet_train_test.prototxt
指定MNIST手写数字分类的LeNet模型的模型定义。我们假设您熟悉Google Protobuf,并且假设您已经阅读了Caffe使用的protobuf定义,可以在这里找到它$CAFFE_ROOT/src/caffe/proto/caffe.proto
。
具体来说,我们将编写一个caffe::NetParameter
(或python caffe.proto.caffe_pb2.NetParameter
)protobuf。我们将首先给网络一个名字:
name: "LeNet"
编写数据层
目前,我们将从我们之前在演示中创建的lmdb中读取MNIST数据。这由数据层定义:
layer {
name: "mnist"
type: "Data"
transform_param {
scale: 0.00390625
}
data_param {
source: "mnist_train_lmdb"
backend: LMDB
batch_size: 64
}
top: "data"
top: "label"
}
具体来说,该图层具有名称mnist,类型data,并从给定的lmdb源读取数据。我们将使用64的批处理大小,并缩放输入像素,使其位于[0,1)范围内。为什么0.00390625?它是1除以256.最后,这一层产生两个斑点,一个是data斑点,另一个是label斑点。
编写卷积层
我们来定义第一个卷积层:
layer {
name: "conv1"
type: "Convolution"
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"
}
}
bottom: "data"
top: "conv1"
}
该图层采用dataBlob(由数据层提供)并生成conv1图层。它产生20个通道的输出,卷积核大小为5,执行步长为1。
填充符允许我们随机初始化权重和偏差的值。对于加权填充器,我们将使用xavier基于输入和输出神经元数自动确定初始化规模的算法。对于偏置填充器,我们将简单地将其初始化为常量,默认填充值为0。
lr_mults是图层可学习参数的学习速率调整。在这种情况下,我们将设置权重学习率与求解器在运行时给出的学习率相同,并且偏差学习率为此的两倍 - 这通常会导致更好的收敛率。
编写池图层
唷。合并图层实际上更容易定义:
layer {
name: "pool1"
type: "Pooling"
pooling_param {
kernel_size: 2
stride: 2
pool: MAX
}
bottom: "conv1"
top: "pool1"
}
这就是说,我们将执行池大小为2的最大池和跨度为2(所以相邻池区之间不重叠)。
同样,你可以写出第二个卷积和合并图层。检查$CAFFE_ROOT/examples/mnist/lenet_train_test.prototxt细节。
编写完全连接的层
编写完全连接的图层也很简单:
layer {
name: "ip1"
type: "InnerProduct"
param { lr_mult: 1 }
param { lr_mult: 2 }
inner_product_param {
num_output: 500
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
bottom: "pool2"
top: "ip1"
}
这定义了InnerProduct具有500个输出的完全连接的层(在Caffe中称为层)。所有其他线看起来很熟悉,对吧?
编写ReLU层
ReLU层也很简单:
layer {
name: "relu1"
type: "ReLU"
bottom: "ip1"
top: "ip1"
}
由于ReLU是一种按元素操作,我们可以进行就地操作以节省一些内存。这是通过简单地给底部和顶部斑点赋予相同的名称来实现的。当然,不要为其他图层类型使用重复的斑点名称!
在ReLU层之后,我们将编写另一个内部产品层:
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"
}
编写损失层
最后,我们会写下损失!
layer {
name: "loss"
type: "SoftmaxWithLoss"
bottom: "ip2"
bottom: "label"
}
该softmax_loss层实现softmax和多项logistic损失(这节省了时间并提高了数值稳定性)。它需要两个blob,第一个是预测,第二个是label数据层提供的(记住它?)。它不会产生任何输出 - 它只是计算损失函数值,在反向传播开始时报告它,并针对其启动梯度ip2。这是所有魔法开始的地方。
其他注意事项:编写层规则
图层定义可以包含是否以及何时包含在网络定义中的规则,如下所示:
layer {
// ...layer definition...
include: { phase: TRAIN }
}
这是一个规则,它根据当前网络的状态控制网络中的层包含。您可以参考$CAFFE_ROOT/src/caffe/proto/caffe.proto关于图层规则和模型模式的更多信息。
在上面的例子中,这一层将只包含在TRAIN阶段中。如果我们改变TRAIN了TEST,那么这个图层将仅用于测试阶段。默认情况下,没有图层规则,图层始终包含在网络中。因此,lenet_train_test.prototxt有DATA两层定义(不同batch_size),一个用于训练阶段,另一个用于测试阶段。此外,还有一个Accuracy仅包含在TEST阶段中的层,用于每100次迭代报告模型精度,如定义lenet_solver.prototxt。
定义MNIST求解器
查看解释原型文件中每一行的注释$CAFFE_ROOT/examples/mnist/lenet_solver.prototxt:
# The train/test net protocol buffer definition
net: "examples/mnist/lenet_train_test.prototxt"
# test_iter specifies how many forward passes the test should carry out.
# In the case of MNIST, we have test batch size 100 and 100 test iterations,
# covering the full 10,000 testing images.
test_iter: 100
# Carry out testing every 500 training iterations.
test_interval: 500
# The base learning rate, momentum and the weight decay of the network.
base_lr: 0.01
momentum: 0.9
weight_decay: 0.0005
# The learning rate policy
lr_policy: "inv"
gamma: 0.0001
power: 0.75
# Display every 100 iterations
display: 100
# The maximum number of iterations
max_iter: 10000
# snapshot intermediate results
snapshot: 5000
snapshot_prefix: "examples/mnist/lenet"
# solver mode: CPU or GPU
solver_mode: GPU
培训和测试模型
在编写网络定义protobuf和求解器protobuf文件后,训练模型很简单。train_lenet.sh直接运行,或直接执行以下命令:
cd $CAFFE_ROOT
./examples/mnist/train_lenet.sh
train_lenet.sh是一个简单的脚本,但这里是一个快速解释:训练的主要工具是caffe动作train和解算器protobuf文本文件作为其参数。
当你运行代码时,你会看到很多像这样飞行的消息:
I1203 net.cpp:66] Creating Layer conv1
I1203 net.cpp:76] conv1 <- data
I1203 net.cpp:101] conv1 -> conv1
I1203 net.cpp:116] Top shape: 20 24 24
I1203 net.cpp:127] conv1 needs backward computation.
这些消息告诉你关于每一层的细节,它的连接和它的输出形状,这可能对调试有帮助。初始化后,培训将开始:
I1203 net.cpp:142] Network initialization done.
I1203 solver.cpp:36] Solver scaffolding done.
I1203 solver.cpp:44] Solving LeNet
基于求解器设置,我们将每100次迭代打印一次训练损失函数,并且每500次迭代测试一次网络。你会看到这样的消息:
I1203 solver.cpp:204] Iteration 100, lr = 0.00992565
I1203 solver.cpp:66] Iteration 100, loss = 0.26044
...
I1203 solver.cpp:84] Testing net
I1203 solver.cpp:111] Test score #0: 0.9785
I1203 solver.cpp:111] Test score #1: 0.0606671
对于每次训练迭代,lr该迭代的学习率loss是训练函数。对于测试阶段的输出,得分0是准确度,得分1是测试损失函数。
几分钟后,你就完成了!
I1203 solver.cpp:84] Testing net
I1203 solver.cpp:111] Test score #0: 0.9897
I1203 solver.cpp:111] Test score #1: 0.0324599
I1203 solver.cpp:126] Snapshotting to lenet_iter_10000
I1203 solver.cpp:133] Snapshotting solver state to lenet_iter_10000.solverstate
I1203 solver.cpp:78] Optimization Done.
存储为二进制protobuf文件的最终模型存储在
lenet_iter_10000
如果您正在对现实世界的应用程序数据集进行培训,那么您可以在应用程序中将其部署为训练有素的模型。
呃... GPU训练如何?
你刚刚做到了!所有的培训都是在GPU上进行的。事实上,如果您想对CPU进行培训,您可以简单地更改一行lenet_solver.prototxt:
# solver mode: CPU or GPU
solver_mode: CPU
并且您将使用CPU进行培训。这不容易吗?
MNIST是一个小型数据集,因此使用GPU进行培训并不会因通信开销而带来太多好处。在更复杂模型的大型数据集上,如ImageNet,计算速度差异将更为显着。
训练跑起来了
image.png模型也训练好了
image.png现在的文件目录结构
image.png前面又是 sh.改成cmd.
train_lenet.cmd
G:\caffe\bin\caffe train --solver=lenet_solver.prototxt
lenet_solver.prototxt
lenet_train_test.prototxt
这两个文件从demo里面就有..自己复制一下.
需要打开文件,修改路径
image.png
然后就可以训练了。
预测
生成均值文件
compute_image_mean.cmd
G:\caffe\bin\compute_image_mean mnist_train_lmdb mean.binaryproto
pause
跑一下就出来均值文件了
test_lenet-one.cmd
G:\caffe\bin\classification lenet.prototxt ./lenet/_iter_10000.caffemodel mean.binaryproto label.txt ./test/3.png
pause
image.png
python调用caffe模型
import sys
import caffe
classifier = caffe.Classifier(
'lenet.prototxt',
'lenet/_iter_10000.caffemodel',
image_dims=(28, 28),
mean=None,
input_scale=None,
raw_scale=255,
channel_swap=None)
img = 'test/3.png'
inputs = [caffe.io.load_image(img, False)] # rgb to gray
scores = classifier.predict(inputs, False).argmax()
scores
网友评论