一般把数据源放在 $CAFFE_ROOT/data 文件夹下面。 处理后的数据和模型文件等放在 $CAFFE_ROOT/examples文件夹下。($CAFFE_ROOT 表示你电脑上的caffe代码路径。)
- 数据库格式 (LEVELDB or LMDB) $CAFFEROOT/build/tools/convert_imageset 可以用来做把原始图片转换为LevelDB或者 Lmdb格式。
Image Data - read raw images.
Database - read data from LEVELDB or LMDB.
HDF5 Input - read HDF5 data, allows data of arbitrary dimensions.
HDF5 Output - write data as HDF5.
Input - typically used for networks that are being deployed.
Window Data - read window data file.
Memory Data - read data directly from memory.
Dummy Data - for static data and debugging.
MNIST数据: $CAFFE_ROOT/data/mnist/get_mnist.sh
运行这个脚本就可以帮我们下载MNIST数据。
cd $CAFFE_ROOT
./data/mnist/get_mnist.sh #得到MNIST数据
运行成功后,在 data/mnist/
目录下有四个文件:
文件名 | 内容 | 大小 |
---|---|---|
train-images-idx3-ubyte | 训练集样本 | 9912422bytes |
train-labels-idx1-ubyte | 训练集对应标注 | 28881bytes |
t10k-images-idx3-ubyte | 测试集图片 | 1648877bytes |
t10k-labels-idx1-ubyte | 测试集对应标注 | 4542bytes |
但是这些数据不能在 Caffe 中直接使用,需要转换成 LMDB 数据
./examples/mnist/create_mnist.sh #官方给出的专门转换MNIST数据格式的脚本
#这条命令必须从$CAFFE_ROOT这个目录下执行,否则会出现找不到文件的错误:build/examples/mnist/convert_mnist_data.bin: not found
如果想运行 leveldb 数据,请运行 examples/siamese/ 文件夹下面的程序。 examples/mnist/ 文件夹是运行 lmdb 数据
转换成功后,会在 `examples/mnist/` 目录下,生成两个文件夹
官网提供了定义好的网络文件 $CAFFE_ROOT/examples/mnist/lenet_train_test.prototxt
内容如下:
name: "LeNet"
layer {
name: "mnist"
type: "Data" #数据层
top: "data"
top: "label"
include {
phase: TRAIN #训练时才加载
}
transform_param {
scale: 0.00390625 #每个像素乘以改值做归一化(1/255 = 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
}
}
layer {
name: "conv1"
type: "Convolution" #卷积层
bottom: "data"
top: "conv1"
param {
lr_mult: 1 #weights学习率
}
param {
lr_mult: 2 #bias学习率
}
convolution_param {
num_output: 20 #输出多少个特征图(对应卷积核数量)
kernel_size: 5 #卷积核大小
stride: 1 #步长
weight_filler {
type: "xavier" #权重初始化算法
}
bias_filler {
type: "constant" #基值初始化算法
}
}
}
layer {
name: "pool1"
type: "Pooling" #池化层
bottom: "conv1"
top: "pool1"
pooling_param {
pool: MAX #池化方法
kernel_size: 2
stride: 2
}
}
layer {
name: "conv2"
type: "Convolution"
bottom: "pool1"
top: "conv2"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
convolution_param {
num_output: 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
}
}
layer {
name: "ip1"
type: "InnerProduct" #全链接层
bottom: "pool2"
top: "ip1"
param {
lr_mult: 1 #weights学习率
}
param {
lr_mult: 2 #bias学习率
}
inner_product_param {
num_output: 500
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "relu1"
type: "ReLU" #relu层
bottom: "ip1"
top: "ip1"
}
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"
}
}
}
layer {
name: "accuracy"
type: "Accuracy" #输出精度
bottom: "ip2"
bottom: "label"
top: "accuracy"
include {
phase: TEST
}
}
layer {
name: "loss"
type: "SoftmaxWithLoss" #输出损失
bottom: "ip2"
bottom: "label"
top: "loss"
}
可以用 官方自带的python绘图工具绘制出网络图:
~/caffe/python/draw_net.py ~/caffe/examples/mnist/lenet_train_test.prototxt ~/lenet_train_test.png
#最后一个参数是输出图片的路径
官网给出了一个求解文件:$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. 设置每500次测试一下网络 精度 损失
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 设置每100次迭代训练显示当前状态 lr loss
display: 100
# The maximum number of iterations
max_iter: 10000
# snapshot intermediate results 中间结果快照每5000次保存一次
snapshot: 5000
snapshot_prefix: "examples/mnist/lenet"
# solver mode: CPU or GPU
solver_mode: GPU
cd $CAFFE_ROOT
./examples/mnist/train_lenet.sh
其内容如下:
`#!/usr/bin/env sh
set -e
./build/tools/caffe train --solver=examples/mnist/lenet_solver.prototxt $@`
可见只是调用命令行接口。
然后得到一堆的输出信息:
#solver文件设置每100次迭代训练显示当前状态 lr loss
I1203 solver.cpp:204] Iteration 100, lr = 0.00992565 #学习率
I1203 solver.cpp:66] Iteration 100, loss = 0.26044 #损失
...
#solver文件设置每500次测试一下网络 精度 损失
I1203 solver.cpp:84] Testing net
I1203 solver.cpp:111] Test score #0: 0.9785 #精度
I1203 solver.cpp:111] Test score #1: 0.0606671 #损失
训练结束后,输出信息可以看到最终的精度和损失。 在 $CAFFE_ROOT/examples/mnist 文件夹下可以看到 如下文件。 迭代5000次的中间状态快照 (.solverstate文件,可用于恢复网络训练)和模型 (.caffemodel文件,可用于下一步测试),迭代10000次的中间状态快照和模型
备注:由于我们在solver中设置了每500做一下测试。所以实际上上面 ./build/tools/caffe train –solver=examples/mnist/lenet_solver.prototxt 的过程包含了训练和间隔测试。这样做有助于我们对网络训练的中间过程有直观感受。
参数包括: 网络结构模型文件(.prototxt 注意不是求解文件solover) 训练好的模型参数(.caffemodel) 迭代测试100次(前面训练和间隔测试时是在solver文件中定义的 此处则用命令行写明)
cd $CAFFE_ROOT
build/tools/caffe test -model examples/mnist/lenet_train_test.prototxt -weights examples/mnist/lenet_iter_10000.caffemodel -iterations 100
cifar10 数据训练样本50000张,测试样本10000张,每张为32*32的彩色三通道图片,共分为10类。
下载数据:
sh data/cifar10/get_cifar10.sh
运行成功后,会在 data/cifar10/
文件夹下生成一堆 bin 文件
转换数据格式为 lmdb:
sh examples/cifar10/create_cifar10.sh
注意:这些命令必须在caffe 主目录下执行,否则会报错
转换成功后,会在 examples/cifar10/
文件夹下生成两个文件夹, cifar10_train_lmdb
和 cifar10_test_lmdb
里面的文件就是我们需要的文件。
为了节省时间,我们进行快速训练 (train_quick),训练分为两个阶段,第一个阶段(迭代4000次)调用配置文件 cifar10_quick_solver.prototxt
, 学习率 (base_lr) 为0.001。第二阶段(迭代1000次)调用配置文件 cifar10_quick_solver_lr1.prototxt
, 学习率 (base_lr) 为0.0001。
前后两个配置文件就是学习率 (base_lr) 和最大迭代次数 (max_iter) 不一样,其它都是一样。如果你对配置文件比较熟悉以后,实际上是可以将两个配置文件合二为一的,设置 lr_policy
为 multistep
就可以了。
base_lr: 0.001
momentum: 0.9
weight_decay: 0.004
lr_policy: "multistep"
gamma: 0.1
stepvalue: 4000
stepvalue: 5000
运行例子:
sh examples/cifar10/train_quick.sh
net: "examples/mnist/lenet_train_test.prototxt"
test_iter: 100
test_interval: 500
base_lr: 0.01
momentum: 0.9
type: SGD
weight_decay: 0.0005
lr_policy: "inv"
gamma: 0.0001
power: 0.75
display: 100
max_iter: 20000
snapshot: 5000
snapshot_prefix: "examples/mnist/lenet"
solver_mode: CPU
net: "examples/mnist/lenet_train_test.prototxt"
设置网络模型。每一个模型就是一个net,需要在一个专门的配置文件中对net进行配置,每个net由许多的layer所组成。
需要注意的是:文件的路径要从caffe的根目录开始,其它的所有配置都是这样。
test_iter: 100
测试集迭代次数。
这个要与 test layer
中的 batch_size
结合起来理解。mnist数据中测试样本总数为10000,一次性执行全部数据效率很低,因此我们将测试数据分成几个批次来执行,每个批次的数量就是 batch_size
。假设我们设置 batch_size
为100,则需要迭代100次才能将10000个数据全部执行完。因此 test_iter
设置为100。执行完一次全部数据,称之为一个 epoch
。
test_interval: 500
测试间隔。也就是每训练500次,才进行一次测试。
base_lr: 0.01
lr_policy: "inv"
gamma: 0.0001
power: 0.75
这四行可以放在一起理解,用于学习率的设置。只要是梯度下降法来求解优化,都会有一个学习率,也叫步长。
base_lr
用于设置基础学习率
lr_policy
设置学习率的调整策略。
lr_policy
可以设置为下面这些值,相应的学习率的计算为:
- fixed: 保持base_lr不变.
- step: 如果设置为step,则还需要设置一个stepsize, 返回 base_lr * gamma ^ (floor(iter / stepsize)),其中iter表示当前的迭代次数
- exp: 返回base_lr * gamma ^ iter, iter为当前迭代次数
- inv: 如果设置为inv,还需要设置一个power, 返回base_lr * (1 + gamma * iter) ^ (- power)
- multistep: 如果设置为multistep,则还需要设置一个stepvalue。这个参数和step很相似,step是均匀等间隔变化,而multistep则是根据 stepvalue值变化
- poly: 学习率进行多项式误差, 返回 base_lr (1 - iter/max_iter) ^ (power)
- sigmoid: 学习率进行sigmod衰减,返回 base_lr ( 1/(1 + exp(-gamma * (iter - stepsize))))
multistep示例:
base_lr: 0.01
momentum: 0.9
weight_decay: 0.0005
# The learning rate policy
lr_policy: "multistep"
gamma: 0.9
stepvalue: 5000
stepvalue: 7000
stepvalue: 8000
stepvalue: 9000
stepvalue: 9500
接下来的参数:
momentum :0.9
指上一次梯度更新的权重
type: SGD
优化算法选择。这一行可以省掉,因为默认值就是SGD。
caffe提供了六种优化算法来求解最优参数,在solver配置文件中,通过设置 type
类型来选择
- Stochastic Gradient Descent (type:”SGD”),
- AdaDelta (type:”AdaDelta”),
- Adaptive Gradient (type:”AdaGrad”),
- Adam (type: “Adam”),
- Nesterov’s Accelerated Gradient (type: “Nesterov”) and
- RMSprop (type:”RMSProp”)
weight_decay: 0.0005
权重衰减项,防止过拟合的一个参数。
display: 100
每训练100次,在屏幕上显示一次。如果设置为0,则不显示。
max_iter: 20000
最大迭代次数。这个数设置太小,会导致没有收敛,精确度很低。设置太大,会导致震荡,浪费时间
snapshot: 5000
snapshot_prefix: "examples/mnist/lenet"
快照。将训练出来的model和solver状态进行保存,`snapshot`用于设置训练多少次后进行保存,默认为0,不保存。
snapshot_prefix
设置保存路径。
还可以设置`snapshot_diff`,是否保存梯度值,默认为 false
,不保存。
也可以设置 snapshot_format
,保存的类型。有两种选择: HDF5
和 BINARYPROTO
,默认为 BINARYPROTO
solver_mode: CPU
设置运行模式。默认为GPU,如果你没有GPU,则需要改成CPU,否则会出错。
注意:以上的所有参数都是可选参数,都有默认值。根据solver方法(type)的不同,还有一些其它的参数,在此不一一列举。
Caffe的 C++ 主程序(caffe.cpp) 放在根目录下的 tools 文件夹内, 当然还有一些其它的功能文件,如: convert_imageset.cpp
, train_net.cpp
, test_net.cpp
等也放在这个文件夹内。经过编译后,这些文件都被编译成了可执行文件,放在了 ./build/tools/
文件夹内。因此我们要执行caffe程序,都需要加 ./build/tools/
前缀。
如: sh ./build/tools/caffe train --solver=examples/mnist/train_lenet.sh
caffe <command> <args>
其中<command>有四种
command | 功能 |
---|---|
train | 训练或微调模型 |
test | 测试模型 |
time | 显示程序执行时间 |
其中的<args>参数有
command | required | 功能 |
---|---|---|
-solver | 必选参数 | 指定一个模型的配置文件 |
-gpu | 可选参数 | 指定执行 Caffe 的 GPU 编号 |
-snapshot | 可选参数 | 从快照文件(*.solverstate)恢复训练 |
-weights | 可选参数 | 从权重文件 (*.caffemodel)微调模型 |
-iterations | 可选参数 | 设定迭代次数 |
-model | 可选参数 | 指定模型结构描述文件 |
-sighup_effect | 可选参数 | 用来设定当程序发生挂起事件时,执行的操作 |
-sigint_effect | 可选参数 | 用来设定当程序发生键盘中止事件时(Ctrl + C ) |
- -solver:一个 protocol buffer 类型的文件,即模型的配置文件。
./build/tools/caffe train -solver examples/mnist/lenet_solver.prototxt
- -gpu: 该参数用来指定用哪一块 GPU 运行,根据 GPU 的 ID 进行选择,如果设置为 -gpu all 则使用所有的 GPU 运行。如使用第二块 GPU 运行:
./build/tools/caffe train -solver examples/mnist/lenet_solver.prototxt -gpu 1
- -snapshot: 该参数用来从快照 (snapshot)中恢复训练。可以在 solver 配置文件设置快照,保存 solverstate。如:
./build/tools/caffe train -solver examples/mnist/lenet_solver.prototxt -snapshot examples/mnist/lenet_iter_5000.solverstate
- -weights: 用预先训练好的权重来 fine-tuning 模型,需要一个 caffemodel,不能和 -snapshot 同时使用。如:
./build/tools/caffe train -solver examples/finetuning_on_flickr_style/solver.prototxt -weights models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel
- -iterations: 指定迭代次数,默认为50。如果在配置文件文件中没有设定迭代次数,则默认迭代50次。
- -model: 指定 protocol buffer 文件中的模型。也可以在 solver 配置文件中指定。
- -sighup_effect:用来设定当程序发生挂起事件时,执行的操作,可以设置为 snapshot, stop 或 none, 默认为 snapshot
- -sigint_effect: 用来设定当程序发生键盘中止事件时 (Ctrl + C), 执行的操作,可以设置为 snapshot, stop 或 none, 默认为 stop
刚才举例了一些 `train` 参数的例子,现在我们来看看其它三个`<command>`:
- test
test
参数用在测试阶段,用于最终结果的输出,要模型配置文件中我们可以设定需要输入 accuracy 还是 loss. 假设我们要在验证集中验证已经训练好的模型,就可以这样写
./build/tools/caffe test -model examples/mnist/lenet_train_test.prototxt -weights examples/mnist/lenet_iter_10000.caffemodel -gpu 0 -iterations 100
这个例子比较长,不仅用到了 test 参数,还用到了-model, -weights, -gpu 和 -iterations 四个参数。意思是利用训练好了的权重 (-weight),输入到测试模型中 (-model),用编号为0的 gpu (-gpu) 测试100次 (-iteration)。
- time time 参数用来在屏幕上显示程序运行时间。如
./build/tools/caffe time -model examples/mnist/lenet_train_test.prototxt -iterations 10
#这个例子用来在屏幕上显示 lenet 模型迭代10次所使用的时间。包括每次迭代的 forward 和 backward 所用的时间,也包括每层 forward 和 backward 所用的平均时间。
./build/tools/caffe time -model examples/mnist/lenet_train_test.prototxt -gpu 0
#这个例子用来在屏幕上显示 lenet 模型用 gpu 迭代50次所使用的时间。
./build/tools/caffe time -model examples/mnist/lenet_train_test.prototxt -weights examples/mnist/lenet_iter_10000.caffemodel -gpu 0 -iterations 10
#利用给定的权重,利用第一块 gpu,迭代10次 lenet 模型所用的时间。
- device_query device_query 参数用来诊断 gpu 信息。
./build/tools/caffe device_query -gpu 0
- 关于GPU的例子
./build/tools/caffe train -solver examples/mnist/lenet_solver.prototxt -gpu 0,1
./build/tools/caffe train -solver examples/mnist/lenet_solver.prototxt -gpu all
这两个例子表示: 用两块或多块 GPU 来平行运算,这样速度会快很多。但是如果你只有一块或没有 GPU, 就不要加 -gpu 参数了,加了反而慢。 最后,在 linux 下,本身就有一个 time 命令,因此可以结合进来使用,因此我们运行mnist例子的最终命令是(一块 gpu ):
time ./build/toos/caffe train -solver examples/mnist/lenet_solver.prototxt
#!/usr/bin/en sh
# /usr/bin/env sh
echo "Create train.txt..."
rm -rf caffemodel/train.txt
find caffemodel/train/cardboard -name *.jpg | cut -d '/' -f3-4 | sed "s/$/ 0/" >> caffemodel/train.txt
find caffemodel/train/glass -name *.jpg | cut -d '/' -f3-4 | sed "s/$/ 1/" >> caffemodel/train.txt
find caffemodel/train/metal -name *.jpg | cut -d '/' -f3-4 | sed "s/$/ 2/" >> caffemodel/train.txt
find caffemodel/train/paper -name *.jpg | cut -d '/' -f3-4 | sed "s/$/ 3/" >> caffemodel/train.txt
find caffemodel/train/plastic -name *.jpg | cut -d '/' -f3-4 | sed "s/$/ 4/" >> caffemodel/train.txt
find caffemodel/train/trash -name *.jpg | cut -d '/' -f3-4 | sed "s/$/ 5/" >> caffemodel/train.txt
echo "Create val.txt..."
rm -rf caffemodel/val.txt
find caffemodel/val/cardboard -name *.jpg | cut -d '/' -f3-4 | sed "s/$/ 0/" >> caffemodel/val.txt
find caffemodel/val/glass -name *.jpg | cut -d '/' -f3-4 | sed "s/$/ 1/" >> caffemodel/val.txt
find caffemodel/val/metal -name *.jpg | cut -d '/' -f3-4 | sed "s/$/ 2/" >> caffemodel/val.txt
find caffemodel/val/paper -name *.jpg | cut -d '/' -f3-4 | sed "s/$/ 3/" >> caffemodel/val.txt
find caffemodel/val/plastic -name *.jpg | cut -d '/' -f3-4 | sed "s/$/ 4/" >> caffemodel/val.txt
find caffemodel/val/trash -name *.jpg | cut -d '/' -f3-4 | sed "s/$/ 5/" >> caffemodel/val.txt
echo "All Done.."
rm -rf caffemodel/img_train_lmdb
build/tools/convert_imageset --shuffle \
--resize_height=224 --resize_width=224 \
caffemodel/train/ caffemodel/train.txt caffemodel/img_train_lmdb/
rm -rf caffemodel/img_val_lmdb
build/tools/convert_imageset --shuffle \
--resize_height=224 --resize_width=224 \
caffemodel/val/ caffemodel/val.txt caffemodel/img_val_lmdb/
在 Caffe 中,作者为我们提供了这样一个文件:`convert_imageset.cpp`,存放在根目录下的 `tools` 文件夹下。编译之后,生成对应的可执行文件放在 `buile/tools/` 下面,这个文件的作用就是用于将图片文件转换成 Caffe 框架中能直接使用的 db 文件。
convert_imageset [FLAGS] ROOTFOLDER/ LISTFILE DB_NAME
需要带四个参数:
- FLAGS: 图片参数组
- ROOTFOLDER/: 图片存放的绝对路径,从 linux 系统根目录开始
- LISTFILE: 图片文件列表清单,一般为一个 txt 文件,一行一张图片
- DB_NAME: 最终生成的 db 文件存放目录
如果图片已经下载到本地电脑上了,那么我们首先需要创建一个图片列表清单,保存为 txt。本文以 Caffe 程序中自带的图片为例,进行讲解,图片目录是
example/images/
, 两张图片,一张为cat.jpg
, 另一张为fish_bike.jpg
,表示两个类别。
我们创建一个 sh 脚本文件,调用 linux 命令来生成图片清单:
vim examples/images/create_filelist.sh
编辑这个文件,输入下面的代码并保存
# /usr/bin/env sh
DATA=examples/images
echo "Create train.txt..."
rm -rf $DATA/train.txt
find $DATA -name *cat.jpg | cut -d '/' -f3 | sed "s/$/ 1/" >> $DATA/train.txt
find $DATA -name *bike.jpg | cut -d '/' -f3 | sed "s/$/ 2/" >> $DATA/tmp.txt
cat $DATA/tmp.txt >> $DATA/train.txt
rm -rf $DATA/tmp.txt
echo "Done.."
这个脚本文件中,用到了 rm, find, cut, sed, cat
等 linux 命令。
- rm: 删除文件
- find: 寻找文件
- cut: 截取路径
- sed: 在每行的最后面加上标注。本例中将找到的 *cat.jpg 文件加入标注为1,找到的 *bike.jpg 文件加入标注为2
- cat: 将两个类别合并在一个文件里。
最终生成如下的一个 train.txt 文件:
cat.jpg 1
fish-bike.jpg 2
当然,图片很少的时候,手动编写这个列表清单文件就行了。但图片很多的情况,就需要用脚本文件来自动生成了。在以后的实际应用中,还需要生成相应的 `val.txt` 和 `test.txt` 文件,方法是一样的。生成的这个 `train.txt` 文件,就可以作为第三个参数,直接使用了。 接下来,我们来了解一下 `FLAGS` 这个参数组,有些什么内容:
- -gray: 是否以灰度图的方式打开图片。程序调用 opencv 库中的 imread() 函数来打开图片,默认为 false
- -shuffle: 是否随机打乱图片顺序。默认为 false
- -backend: 需要转换成的 db 文件格式,可选为 leveldb 或 lmdb,默认为 lmdb
- -resize_width/resize_height: 改变图片的大小。在运行中,要求所有图片的尺寸一致,因此需要改变图片大小。 程序调用 opencv 库的 resize() 函数来对图片放大缩小,默认为0,不改变
- -check_size: 检查所有的数据是否有相同的尺寸。默认为 false, 不检查
- -encoded: 是否将原图片编码放入最终的数据中,默认为false
- -encode_type: 与前一个参数对应,将图片编码为哪一个格式:‘png’, ‘jpg’……
知道这些参数后,我们就可以调用命令来生成最终的 lmdb 格式数据了。由于参数比较多,因此我们可以编写一个 sh 脚本来执行命令: 首先,创建 sh 脚本文件:
vim examples/images/create_lmdb.sh
编辑,输入下面的代码并保存
#!/usr/bin/en sh
DATA=examples/images
rm -rf $DATA/img_train_lmdb
build/tools/convert_imageset --shuffle \
--resize_height=256 --resize_width=256 \
$CAFFE_ROOT/examples/images/ $DATA/train.txt $DATA/img_train_lmdb
设置参数 `-shuffle`,打乱图片顺序。设置参数 -resize_height
和 -resize_width
将所有图片尺寸都变为256*256。 $CAFFE_ROOT/examples/images/
为图片保存的绝对路径($CAFFE_ROOT 是 Caffe 工程的根目录)。
最后,运行这个脚本文件
sh examples/images/create_lmdb.sh
就会在 examples/images/
目录下生成一个名为 img_train_lmdb
的文件夹,里面的文件就是我们需要的 db 文件了。
Caffe 程序是由 C++ 语言写的,本身是不带数据可视化功能的。只能借助其它的库或接口,如 opencv, python 或 matlab。大部分人使用 python 接口来进行可视化,因为 python 出了个比较强大的东西: ipython notebook
, 现在的最新版本改名叫 jupyter notebook
,它能将python代码搬到浏览器上去执行,以富文本方式显示,使得整个工作可以以笔记的形式展现、存储,对于交互编程、学习非常方便。
图片减去均值后,再进行训练和测试,会提高速度和精度。因此,一般在各种模型中都会有这个操作。这个均值实际上就是计算所有训练样本的平均值,计算出来后,保存为一个均值文件,在以后的测试中,就可以直接使用这个均值来相减,而不需要对测试图片重新计算。
Caffe 中使用的均值数据格式是 binaryproto
, 作者为我们提供了一个计算均值的文件 compute_image_mean.cpp
,放在 Caffe 根目录下的 tools
文件夹里面。编译后的可执行体放在 build/tools/
下面,我们直接调用就可以了。
./build/tools/compute_image_mean examples/mnist/mnist_train_lmdb examples/mnist/mean.binaryproto
参数解释:
- 第一个参数:examples/mnist/mnist_train_lmdb, 表示需要计算均值的数据,格式为lmdb的训练数据。
- 第二个参数:examples/mnist/mean.binaryproto,计算出来的结果保存文件。
如果我们要使用 python 接口,或者我们要进行特征可视化,可能就要用到 python 格式的均值文件了。首先,我们用 lmdb 格式的数据,计算出二进制格式的均值,然后,再转换成 python 格式的均值。 我们可以编写一个 python 脚本来实现:
#!/usr/bin/env python
import numpy as np
import sys,caffe
if len(sys.argv)!=3:
print "Usage: python convert_mean.py mean.binaryproto mean.npy"
sys.exit()
blob = caffe.proto.caffe_pb2.BlobProto()
bin_mean = open( sys.argv[1] , 'rb' ).read()
blob.ParseFromString(bin_mean)
arr = np.array( caffe.io.blobproto_to_array(blob) )
npy_mean = arr[0]
np.save( sys.argv[2] , npy_mean )
将这个脚本保存为 convert_mean.py。调用格式为:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
“`bash python convert_mean.py mean.binaryproto mean.npy “`
其中的 `mean.binaryproto` 就是经过前面步骤计算出来的二进制均值。`mean.npy` 就是我们需要的python格式的均值。
通过前面的学习,我们已经能够正常训练各种数据了。设置好 `solver.prototxt` 后,我们可以把训练好的模型保存起来,如 `lenet_iter_10000.caffemodel`。训练多少次就自动保存一下,这个是通过 `snapshot` 进行设置的,保存文件的路径及文件名前缀是由 `snapshot_prefix` 来设定的。 `*.caffemodel` 文件里面存放的就是各层的参数,即 `net.params`,里面没有数据 `net.blobs`。 顺带还生成了一个相应的 `*.solverstate` 文件,这个和 `*.caffemodel` 差不多,但它多了一些数据,如模型名称、当前迭代次数等。两者的功能不一样,训练完后保存起来的 `*.caffemodel`,是在测试阶段用来分类的,而 `.*solverstate` 是用来恢复训练的,防止意外终止而保存的快照。
既然我们知道了 `*.caffemodel` 里面保存的就是模型各层的参数,因此我们可以把这些参数提取出来,进行可视化,看一看究竟长什么样。 参数有两种类型:权值参数和偏置项,分别用 params[“conv1”][0] 和 params[“conv1”][1] 表示。
`python/draw_net.py` 这个文件,就是用来绘制网络模型的,也就是将网络模型由 prototxt 变成一张图片。 draw_net.py 执行的时候带三个参数
- 第一个参数:网络模型的 prototxt 文件
- 第二个参数:保存的图片路径及名字
- 第二个参数:–rankdir=x , x 有四种选项,分别是 LR, RL, TB, BT 。用来表示网络的方向,分别是从左到右,从右到左,从上到小,从下到上。默认为LR。
[绘制 loss 和 accuracy 曲线](http://blog.leanote.com/post/braveapple/Caffe-%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7)
- 下载caffemodel文件 可以手动下载,也可以使用脚本下载
“`bash ./scripts/download_model_binary.py models/bvlc_reference_caffenet “`
- 下载均值文件 有了 caffemodel 文件,就需要对应的均值文件,在测试阶段,需要把测试数据减去均值。这个文件我们用脚本来下载,在 caffe 根目录下执行:
“`bash sh ./data/ilsvrc12/get_ilsvrc_aux.sh “`
执行并下载后,均值文件放在 data/ilsvrc12/ 文件夹里。
- synset_words.txt 文件 在调用脚本文件下载均值的时候,这个文件也一并下载好了。里面放的是1000个类的名称。数据准备好了,我们就可以开始分类了。这里提供两个版本的分类方法: 3.1 C++ 方法 在 Caffe 根目录下的 `examples/cpp-classification/` 文件夹下面,有个 `classification.cpp`文件,就是用来分类的。当然编译后,放在 `/build/examples/cpp_classification/` 下面。
我们就直接运行命令:
“`bash ./build/examples/cpp_classification/classification.bin \ models/bvlc_reference_caffenet/deploy.prototxt \ models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel \ data/ilsvrc12/imagenet_mean.binaryproto \ data/ilsvrc12/synset_words.txt \ examples/images/cat.jpg “`
命令很长,用了很多的 \ 符号来换行。可以看出,从第二行开始就是参数,每行一个,共需要4个参数运行成功后,输出 top-5 结果:
“`bash ---------- Prediction for examples/images/cat.jpg ---------- 0.3134 - “n02123045 tabby, tabby cat” 0.2380 - “n02123159 tiger cat” 0.1235 - “n02124075 Egyptian cat” 0.1003 - “n02119022 red fox, Vulpes vulpes” 0.0715 - “n02127052 lynx, catamount” “`
即有0.3134的概率为 `tabby cat`, 有0.2380的概率为 `tiger cat`。 3.2、python 方法 python 接口可以使用 jupyter notebook 来进行可视化操作,因此推荐使用这种方法。编写一个 py 文件,命名为 py-classify.py
“`python *coding=utf-8 *加载必要的库 import numpy as np import sys,os *设置当前目录 caffe_root = ’home/xxx/caffe’ sys.path.insert(0, caffe_root + ‘python’) import caffe os.chdir(caffe_root) net_file=caffe_root + ‘models/bvlc_reference_caffenet/deploy.prototxt’ caffe_model=caffe_root + ‘models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel’ mean_file=caffe_root + ‘python/caffe/imagenet/ilsvrc_2012_mean.npy’ net = caffe.Net(net_file,caffe_model,caffe.TEST) transformer = caffe.io.Transformer({‘data’: net.blobs[‘data’].data.shape}) transformer.set_transpose(‘data’, (2,0,1)) transformer.set_mean(‘data’, np.load(mean_file).mean(1).mean(1)) transformer.set_raw_scale(‘data’, 255) transformer.set_channel_swap(‘data’, (2,1,0)) im=caffe.io.load_image(caffe_root+’examples/images/cat.jpg’) net.blobs[‘data’].data[…] = transformer.preprocess(‘data’,im) out = net.forward() imagenet_labels_filename = caffe_root + ‘data/ilsvrc12/synset_words.txt’ labels = np.loadtxt(imagenet_labels_filename, str, delimiter=’\t’) top_k = net.blobs[‘prob’].data[0].flatten().argsort()[-1:-6:-1] for i in np.arange(top_k.size): print top_k[i], labels[top_k[i]] “`
执行这个文件,输出:
“`bash 281 n02123045 tabby, tabby cat 282 n02123159 tiger cat 285 n02124075 Egyptian cat 277 n02119022 red fox, Vulpes vulpes 287 n02127052 lynx, catamount “`
Caffe开发团队实际上也编写了一个 python 版本的分类文件,路径为 `python/classify.py`。运行这个文件必需两个参数,一个输入图片文件,一个输出结果文件。而且运行必须在 `python` 目录下。假设当前目录是 caffe 根目录,则运行:
“`bash cd python python classify.py ../examples/images/cat.jpg result.npy “`
分类的结果保存为当前目录下的 `result.npy`文件里面,是看不见的。而且这个文件有错误,运行的时候,会提示 `Mean shape incompatible with input shape`的错误。因此,要使用这个文件,我们还得进行修改:
- 修改均值计算: 定位到
“`bash mean = np.load(args.mean_file) “`
这一行,在下面加上一行:
“`bash mean=mean.mean(1).mean(1) “`
则可以解决报错的问题。
- 修改文件,使得结果显示在命令行下: 定位到
“`python
start = time.time() predictions = classifier.predict(inputs, not args.center_only) print(“Done in %.2f s.” % (time.time() - start)) “`
这个地方,在后面加上几行,如下所示:
“`python
start = time.time() predictions = classifier.predict(inputs, not args.center_only) print(“Done in %.2f s.” % (time.time() - start)) imagenet_labels_filename = ‘../data/ilsvrc12/synset_words.txt’ labels = np.loadtxt(imagenet_labels_filename, str, delimiter=’\t’) top_k = predictions.flatten().argsort()[-1:-6:-1] for i in np.arange(top_k.size): print top_k[i], labels[top_k[i]] “`
就样就可以了。运行不会报错,而且结果会显示在命令行下面。
我们把最后一层的输出类别改一下,然后把层的名称改一下,最后用别人的参数、修改后的 network 和我们自己的数据,再进行训练,使得参数适应我们的数据,这样一个过程,通常称之为微调 (fine tuning)
- 下载`.caffemodel`文件
- 准备数据
> 这些数据共有500张图片,分为大巴车、恐龙、大象、鲜花和马五个类,每个类100张。编号分别以0,1,2,3,4开头,各为一类。我从其中每类选出20张作为测试,其余80张作为训练。因此最终训练图片400张(放在 train 文件夹内,每个类一个子文件夹),测试图片100张(放在 test 文件夹内,每个类一个子文件夹)。
caffenet的网络配置文件,放在 caffe/models/bvlc_reference_caffenet/ 这个文件夹里面,名字叫 train_val.prototxt。
修改 train 阶段的 data 层为:
“`c layer { name: “data” type: “Data” top: “data” top: “label” include { phase: TRAIN } transform_param { mean_file: “examples/finetune/mean.binaryproto” mirror: true crop_size: 227 } data_param { source: “examples/finetune/finetune_train_lmdb” batch_size: 100 backend: LMDB } } “`
把均值文件(mean_file)、数据源文件 (source)、批次大小 (batch_size)和数据源格式 (backend) 这四项作相应的修改。 修改 test 阶段的 data 层:
“`c layer { name: “data” type: “Data” top: “data” top: “label” include { phase: TEST } transform_param { mean_file: “examples/finetune/mean.binaryproto” mirror: false crop_size: 227 } data_param { source: “examples/finetune/finetune_test_lmdb” batch_size: 100 backend: LMDB } } “`
修改最后一个全连接层 (fc8):
“`c layer { name: “fc8-my” *原来为”fc8” type: “InnerProduct” bottom: “fc7” top: “fc8” param { lr_mult: 1.0 decay_mult: 1.0 } param { lr_mult: 2.0 decay_mult: 0.0 } inner_product_param { num_output: 5 *原来为”1000” weight_filler { type: “gaussian” std: 0.01 } bias_filler { type: “constant” value: 0.0 } } } “`
看注释的地方,就只有两个地方修改,其它不变。设置好后,就可以开始微调了(fine tuning)。训练结果就是一个新的 model,可以用来单张图片和多张图片测试。
[Caffe 使用教程](http://blog.leanote.com/post/braveapple/Caffe-%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7) [caffe学习系列:训练自己的图片集](https://blog.csdn.net/qq_27923041/article/details/54139887) [caffe 有关prototxt文件的设置解读](https://blog.csdn.net/greenlight_74110/article/details/78640916)
我用的模型是[deploy_resnet10-1x32d.prototxt](https://github.com/soeaver/caffe-model/tree/master/cls/resnet) 这个模型的输入数据格式为
“`bash input_shape { dim: 1 dim: 3 dim: 224 dim: 224 } “`
我用的数据集合是[这个](https://pan.baidu.com/s/1nuqlTnN) 这个数据集有500张图片,分为大巴车、恐龙、大象、鲜花和马五个类,每个类100张。 编号分别以3,4,5,6,7开头,各为一类。我从其中每类选出20张作为测试,其余80张作为训练。因此最终训练图片400张,测试图片100张,共5类。 首先在examples下面创建一个myfile的文件夹,来用存放配置文件和脚本文件。 我将图片放在`/root/caffe/examples/myfile/re`下。即训练图片目录:`/root/caffe/examples/myfile/re/train/`,测试图片目录:`/root/caffe/examples/myfile/re/test/` 然后编写一个脚本create_filelist.sh,用来生成train.txt和test.txt清单文件 编辑create_filelist.sh文件,并写入如下代码,并保存
“`bash *!/usr/bin/env sh DATA=/root/caffe/examples/myfile/re MY=/root/caffe/examples/myfile
echo “Create train.txt…” rm -rf $MY/train.txt for i in 3 4 5 6 7 do j=`expr $i - 3` find $DATA/train -name $i*.jpg | cut -d ’’ -f7-8 | sed “s/$ $j/”>>$MY/train.txt done echo “Create test.txt…” rm -rf $MY/test.txt for i in 3 4 5 6 7 do j=`expr $i - 3` find $DATA/test -name $i*.jpg | cut -d ’’ -f7-8 | sed “s/$ $j/”>>$MY/test.txt done “`
然后,运行此脚本 在这个脚本里,我们通过查找替换功能,将train.txt文件和test.txt文件里面的图片编号依次由3,4,5,6,7改为0,1,2,3,4。之所以要改编号的原因是,编号从3~7会影响训练精度。修改完后,train.txt内容如下图所示 打开train.txt可以看到如下内容,同样test.txt文件里面的内容也是如此,二者仅数量不同。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20191224203626639.png) 接着再编写一个脚本文件create_lmdb.sh,调用convert_imageset命令来转换数据格式
“`bash *!/usr/bin/en sh My=/root/caffe/examples/myfile echo “Create train lmdb..” rm -rf $My/img_train_lmdb /root/caffe/build/tools/convert_imageset –shuffle \ –resize_height=256 –resize_width=256 \ $My/re/train/ $My/train.txt $My/img_train_lmdb
echo “Create test lmdb..” rm -rf $My/img_test_lmdb /root/caffe/build/tools/convert_imageset –shuffle \ –resize_height=256 –resize_width=256 \ $My/re/test/ $My/test.txt $My/img_test_lmdb
echo “All Done” “`
代码中的shuffle为,打乱图片顺序。`/root/caffe/examples/myfile/re`为下载的图像数据保存的绝对路径。 执行该脚本即可。运行成功后,会在`examples/myfile`下面生成两个`img_test_lmdb`和`img_train_lmdb`,分别用于保存图片转换后的lmdb文件。
图片减去均值再训练,会提高训练速度和精度。因此,一般都会有这个操作。 caffe程序提供了一个计算均值的文件`compute_image_mean.cpp`,我们直接使用就可以了。
“`bash /root/caffe/build/tools/compute_image_mean /root/caffe/examples/myfile/img_train_lmdb /root/caffe/examples/myfile/mean.binaryproto “`
第一个参数为上一步得到的`img_train_lmdb`,第二个参数为转换后的文件保存位置。
这一步我失败了
创建模型并编写配置文件 配置文件`solver.prototxt`的内容为
“`bash net: “/root/caffe/examples/myfile/deploy_resnet10-1x32d.prototxt” test_iter: 2 test_interval: 50 base_lr: 0.01 lr_policy: “step” gamma: 0.1 stepsize: 100 display: 20 max_iter: 500 momentum: 0.9 weight_decay: 0.0005 solver_mode: GPU “`
100个测试数据,batch_size为50,因此test_iter设置为2,就能全覆盖了。在训练过程中,调整学习率,逐步变小。