caffe的使用方法
Caffe学习总结(更新中)
主要参考一个大牛的caffe学习系列博客:http://www.cnblogs.com/denny402/tag/caffe/
这里说明一下:下面的测试分别在我自己的电脑和服务器上进行(服务器速度快),
1. 关于caffe的简介
caffe是一个清晰而高效的深度学习框架,纯粹的C++/CUDA架构,支持命令行、Python和MATLAB接口,可以在CPU和GPU直接无缝切换;
caffe的主要优势:
(1)CPU与GPU的无缝切换;
(2)模型与优化都是通过配置文件来设置,无需代码;
简洁好用不多说。
2. Caffe环境的搭建,参考另一篇博客:Ubuntu14.04安装caffe。
3. Caffe总体框架介绍
主要参考:深度学习框架Caffe源码解析;
若想详细了解caffe源码,参考官方教程Tutorial Documentation或者看《caffe官方教程中译本》。
还有一个博客写的很好,caffe源码解析:http://www.cnblogs.com/louyihang-loves-baiyan/
Caffe主要由Blob,Layer,Net 和 Solver这几个部分组成。
1)Blob
主要用来表示网络中的数据,包括训练数据,网络各层自身的参数(包括权值、偏置以及它们的梯度),网络之间传递的数据都是通过 Blob 来实现的,同时 Blob 数据也支持在 CPU 与 GPU 上存储,能够在两者之间做同步。
2)Layer
是对神经网络中各种层的一个抽象,包括我们熟知的卷积层和下采样层,还有全连接层和各种激活函数层等等。同时每种 Layer 都实现了前向传播和反向传播,并通过 Blob 来传递数据。
3) Net
是对整个网络的表示,由各种 Layer 前后连接组合而成,也是我们所构建的网络模型。
4) Solver
定义了针对 Net 网络模型的求解方法,记录网络的训练过程,保存网络模型参数,中断并恢复网络的训练过程。自定义 Solver 能够实现不同的网络求解方式。
完整巧妙不多说。
4. 总流程(重点)
完成一个简单的自己的网络模型训练预测,主要包含几个步骤:(以一个实例介绍)
1) 准备数据;
例1,学习系列博客上的数据,后面会用到,这里简单说明一下:
可以从这里下载:http://pan.baidu.com/s/1nuqlTnN
共有500张图片,分为大巴车、恐龙、大象、鲜花和马五个类,每个类100张。编号分别以3,4,5,6,7开头,各为一类。从其中每类选出20张作为测试,其余80张作为训练。
因此最终训练图片400张,测试图片100张,共5类。我将图片放在caffe根目录下的 data文件夹下面。即训练图片目录:data/re/train/ ,测试图片目录: data/re/test/。
2) 数据格式处理,也就是把我们jpg,jpeg,png,tif等格式的图片(可能存在大小不一致的问题),处理转换成caffe中能够运行的db(leveldb/lmdb)文件。
参考学习系列博客 Caffe学习系列(11):图像数据转换成db(leveldb/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文件存放目录。
其中第二个和第四个目录是自己决定的,不多说;
先说第三个,所谓图片列表清单,也叫标签文件,一般该文件存放图片文件路径,以及该图片的标签(属于哪个类);一般来说,标签文件有两个,一个描述训练集合-train.txt,一个描述测试集合-test.txt,(可能还有描述验证的val.txt),
例1中测试图片转换后的标签文件格式(test.txt)如下:
那怎么由图片生成标签文件呢?
当然图片很少的时候,直接手动输入就好了,但图片很多的情况,就需要用脚本文件来自动生成了。之前对脚本的编写不是很熟,有个快速上手的文章:
http://www.cnblogs.com/handsomecui/p/5869361.html(其实非常简单( ̄▽ ̄))
对于例1,在examples下面创建一个myfile的文件夹,来用存放配置文件和脚本文件。然后编写一个脚本create_filelist.sh,用来生成train.txt和test.txt清单文件。脚本(/examples/myfile/create_filelist.sh)编写如下:
#!/usr/bin/env sh DATA=data/re/ MY=examples/myfile echo "Create train.txt..." rm -rf $MY/train.txt for i in 3 4 5 6 7 do find $DATA/train -name $i*.jpg | cut -d \'/\' -f4-5 | sed "s/$/ $i/">>$MY/train.txt done echo "Create test.txt..." rm -rf $MY/test.txt for i in 3 4 5 6 7 do find $DATA/test -name $i*.jpg | cut -d \'/\' -f4-5 | sed "s/$/ $i/">>$MY/test.txt done echo "All done"
生成的.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\’……
继续看例1,于是将训练数据转换成lmdb文件命令如下:
注:由于图片大小不一,因此这里统一转换成256*256大小。
类似的,将测试数据转换成lmdb文件命令如下:
当然,由于参数比较多,依然可以写脚本(examples/myfile/create_lmdb.sh):
#!/usr/bin/env sh MY=examples/myfile echo "Create train lmdb.." rm -rf $MY/img_train_lmdb build/tools/convert_imageset \ --shuffle \ --resize_height=256 \ --resize_width=256 \ /home/xxx/caffe/data/re/ \ $MY/train.txt \ $MY/img_train_lmdb echo "Create test lmdb.." rm -rf $MY/img_test_lmdb build/tools/convert_imageset \ --shuffle \ --resize_width=256 \ --resize_height=256 \ /home/xxx/caffe/data/re/ \ $MY/test.txt \ $MY/img_test_lmdb echo "All Done.."
这里的xxx是你具体的路径。
运行成功后,会在 examples/myfile下面生成两个文件夹img_train_lmdb和img_test_lmdb,分别用于保存图片转换后的lmdb文件。
3) 计算均值并保存
图片减去均值再训练,会提高训练速度和精度。因此,一般都会有这个操作。
caffe程序提供了一个计算均值的文件compute_image_mean.cpp,我们直接使用就可以了。
对于例1,命令如下:
1 sudo build/tools/compute_image_mean examples/myfile/img_train_lmdb examples/myfile/mean.binaryproto
compute_image_mean带两个参数,第一个参数是lmdb训练数据位置,第二个参数设定均值文件的名字及保存路径。
运行成功后,会在 examples/myfile/ 下面生成一个mean.binaryproto的均值文件。
4) 创建模型
在caffe中是通过.prototxt配置文件来定义网络模型。
例如,可以打开caffe自带的手写数字库MNIST例子的网络结构文件:
sudo vim examples/mnist/lenet_train_test.prototxt
这个网络模型是非常有名的LeNet模型,网络结构如下图。
可以看到各个网络层是如何定义的:
l 数据层(也叫输入层)
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 //数据类型 } }
关于数据层及参数的编写,参看学习系列(2):Caffe学习系列(2):数据层及参数
简单介绍一下其中参数:
name: 表示该层的名称,可随意取
type: 层类型,如果是Data,表示数据来源于LevelDB或LMDB。根据数据的来源不同,数据层的类型也不同(后面会详细阐述)。一般在练习的时候,我们都是采 用的LevelDB或LMDB数据,因此层类型设置为Data。
top或bottom: 每一层用bottom来输入数据,用top来输出数据。如果只有top没有bottom,则此层只有输出,没有输入。反之亦然。如果有多个 top或多个bottom,表示有多个blobs数据的输入和输出。
data 与 label: 在数据层中,至少有一个命名为data的top。如果有第二个top,一般命名为label。 这种(data,label)配对是分类模型所必需的。
include: 一般训练的时候和测试的时候,模型的层是不一样的。该层(layer)是属于训练阶段的层,还是属于测试阶段的层,需要用include来指定。如果没有include参数,则表示该层既在训练模型中,又在测试模型中。
Transformations: 数据的预处理,可以将数据变换到定义的范围内。如设置scale为0.00390625,实际上就是1/255, 即将输入数据由0-255归一化到0-1之间。
l 视觉层
视觉层包括Convolution, Pooling, Local Response Normalization (LRN), im2col等层。
关于视觉层及参数的编写,参看:Caffe学习系列(3):视觉层(Vision Layers)及参数
仍然以mnist为例,
mnist卷积层定义如下:
layer { name: "conv1" type: "Convolution" bottom: "data" //输入是data top: "conv1" //输出是卷积特征 param { lr_mult: 1 //权重参数w的学习率倍数 } param { lr_mult: 2 //偏置参数b的学习率倍数 } convolution_param { num_output: 20 kernel_size: 5 stride: 1 weight_filler { //权重参数w的初始化方案,使用xavier算法 type: "xavier" } bias_filler { type: "constant" //偏置参数b初始化化为常数,一般为0 } } }
简单介绍一下其中参数:
层类型:Convolution
lr_mult: 学习率的系数,最终的学习率是这个数乘以solver.prototxt配置文件中的base_lr。如果有两个lr_mult, 则第一个表示权值的学习率,第二个表示偏置项的学习率。一般偏置项的学习率是权值学习率的两倍。
在后面的convolution_param中,我们可以设定卷积层的特有参数。
必须设置的参数:
num_output: 卷积核(filter)的个数
kernel_size: 卷积核的大小。如果卷积核的长和宽不等,需要用kernel_h和kernel_w分别设定
其它参数:
stride: 卷积核的步长,默认为1。也可以用stride_h和stride_w来设置。
pad: 扩充边缘,默认为0,不扩充。 扩充的时候是左右、上下对称的,比如卷积核的大小为5*5,那么pad设置为2,则四个边缘都扩充2个像素,即宽度和高度都扩充了4个像素,这样卷积运算之后的特征图就不会变小。也可以通过pad_h和pad_w来分别设定。
weight_filler: 权值初始化。 默认为“constant”,值全为0,很多时候我们用”xavier”算法来进行初始化,也可以设置为”gaussian”
bias_filler: 偏置项的初始化。一般设置为”constant”,值全为0。
bias_term: 是否开启偏置项,默认为true, 开启
group: 分组,默认为1组。如果大于1,我们限制卷积的连接操作在一个子集内。如果我们根据图像的通道来分组,那么第i个输出分组只能与第i个输入分组进行连接。
输入:n*c0*w0*h0
输出:n*c1*w1*h1
其中,c1就是参数中的num_output,生成的特征图个数
w1=(w0+2*pad-kernel_size)/stride+1;
h1=(h0+2*pad-kernel_size)/stride+1;
如果设置stride为1,前后两次卷积部分存在重叠。如果设置pad=(kernel_size-1)/2,则运算后,宽度和高度不变。
mnist的Pooling层,也叫池化层,为了减少运算量和数据维度而设置的一种层。
LeNet总共有两个,其中一个定义如下:
layer { name: "pool1" type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { pool: MAX kernel_size: 2 stride: 2 } }
层类型:Pooling
必须设置的参数:
kernel_size: 池化的核大小。也可以用kernel_h和kernel_w分别设定。
其它参数:
pool: 池化方法,默认为MAX。目前可用的方法有MAX, AVE, 或STOCHASTIC
pad: 和卷积层的pad的一样,进行边缘扩充。默认为0
stride: 池化的步长,默认为1。一般我们设置为2,即不重叠。也可以用stride_h和stride_w来设置。
还有Local Response Normalization (LRN)层和im2col层等,在LeNet中没有定义,可以参看AlexNet或GoogLenet等更复杂的模型。
l 激活层
关于激活层及参数的编写,参看Caffe学习系列(4):激活层(Activiation Layers)及参数
在LeNet中,用到的激活函数是ReLU / Rectified-Linear and Leaky-ReLU,
ReLU是目前使用最多的激活函数,主要因为其收敛更快,并且能保持同样效果。标准的ReLU函数为max(x, 0),当x>0时,输出x; 当x<=0时,输出0。
f(x)=max(x,0)。
定义如下:
layer { name: "relu1" type: "ReLU" bottom: "ip1" top: "ip1" }
层类型:ReLU
可选参数:
negative_slope:默认为0. 对标准的ReLU函数进行变化,如果设置了这个值,那么数据为负数时,就不再设置为0,而是用原始数据乘以negative_slope。
RELU层支持in-place计算,这意味着bottom的输出和输入相同以避免内存的消耗。
l 其他层
包括:softmax_loss层,Inner Product层,accuracy层,reshape层和dropout层等,参看Caffe学习系列(5):其它常用层及参数,不多说。
l 关于模型编写的一个总结:
Caffe学习系列(6):Blob,Layer and Net以及对应配置文件的编写
一个完整网络例子:
第一层:name为mnist, type为Data,没有输入(bottom),只有两个输出(top),一个为data,一个为label
第二层:name为ip,type为InnerProduct, 输入数据data, 输出数据ip
第三层:name为loss, type为SoftmaxWithLoss,有两个输入,一个为ip,一个为label,有一个输出loss,没有画出来。
对应的配置文件prototxt就可以这样写:
name: "LogReg" layer { name: "mnist" type: "Data" top: "data" top: "label" data_param { source: "input_leveldb" batch_size: 64 } } layer { name: "ip" type: "InnerProduct" bottom: "data" top: "ip" inner_product_param { num_output: 2 } } layer { name: "loss" type: "SoftmaxWithLoss" bottom: "ip" bottom: "label" top: "loss" }
5) 编写配置文件
主要参看Caffe学习系列(7):solver及其配置 和Caffe学习系列(8):solver优化方法
先看一下mnist中配置文件(一般是..solver.prototxt文件):
1 sudo vim examples/mnist/lenet_solver.prototxt
配置文件如下:
sudo vim 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 //这个要与test layer中的batch_size结合起来理解。mnist数据中测试样本总数为10000,一次性执行全部数据效率很低,因此我们将测试数据分成几个批次来执行,每个批次的数量就是batch_size。假设我们设置batch_size为100,则需要迭代100次才能将10000个数据全部执行完。因此test_iter设置为100。执行完一次全部数据,称之为一个epoch # Carry out testing every 500 training iterations. test_interval: 500 // 测试间隔。也就是每训练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 //这几个参数用于学习率的设置。只要是梯度下降法来求解优化,都会有一个学习率,也叫步长。base_lr用于设置基础学习率,在迭代的过程中,可以对基础学习率进行调整。怎么样进行调整,就是调整的策略,由lr_policy来设置。 # Display every 100 iterations display: 100 //每100次迭代显示一次 # The maximum number of iterations max_iter: 10000 //最大迭代次数。这个数设置太小,会导致没有收敛,精确度很低。设置太大,会导致震荡,浪费时间。 # snapshot intermediate results snapshot: 5000 snapshot_prefix: "examples/mnist/lenet" //快照。将训练出来的model和solver状态进行保存,snapshot用于设置训练多少次后进行保存,默认为0,不保存。snapshot_prefix设置保存路径。 # solver mode: CPU or GPU solver_mode: GPU //设置运行模式。默认为GPU,如果你没有GPU,则需要改成CPU,否则会出错。
其实英文注释将参数的意思说的很清楚,我自己也在后面注释了,具体详细的解释还是参看前面提到的两个链接。
优化的方法不是很清楚,这里不多说。
继续看例1,简单一点,直接用程序自带的caffenet模型,位置在 models/bvlc_reference_caffenet/文件夹下, 将需要的两个配置文件,复制到myfile文件夹内:
sudo cp models/bvlc_reference_caffenet/solver.prototxt examples/myfile/ sudo cp models/bvlc_reference_caffenet/train_val.prototxt examples/myfile/
修改其中的solver.prototxt
1 sudo vi examples/myfile/solver.prototxt
修改如下:
sudo cp models/bvlc_reference_caffenet/solver.prototxt examples/myfile/ sudo cp models/bvlc_reference_caffenet/train_val.prototxt examples/myfile/ 修改其中的solver.prototxt sudo vi examples/myfile/solver.prototxt 修改如下: net: "examples/myfile/train_val.prototxt" test_iter: 2 test_interval: 50 base_lr: 0.001 lr_policy: "step" gamma: 0.1 stepsize: 100 display: 20 max_iter: 500 momentum: 0.9 weight_decay: 0.005 solver_mode: GPU
说明:100个测试数据,batch_size为50,因此test_iter设置为2,就能全cover了。在训练过程中,调整学习率,逐步变小。
修改train_val.protxt,只需要修改两个阶段的data层就可以了,其它可以不用管。
name: "CaffeNet" layer { name: "data" type: "Data" top: "data" top: "label" include { phase: TRAIN } transform_param { mirror: true crop_size: 227 mean_file: "examples/myfile/mean.binaryproto" } data_param { source: "examples/myfile/img_train_lmdb" batch_size: 256 backend: LMDB } } layer { name: "data" type: "Data" top: "data" top: "label" include { phase: TEST } transform_param { mirror: false crop_size: 227 mean_file: "examples/myfile/mean.binaryproto" } data_param { source: "examples/myfile/img_test_lmdb" batch_size: 50 backend: LMDB } }
实际上就是修改两个data layer的mean_file和source这两个地方,其它都没有变化 。
6) 训练和测试
caffe的运行提供三种接口:c++接口(命令行)、python接口和matlab接口。
这里选择命令行,参看Caffe学习系列(10):命令行解析。
回到例1,命令很简单:
1 sudo build/tools/caffe train -solver examples/myfile/solver.prototxt
运行时间和最后的精确度,会根据机器配置,参数设置的不同而不同。gpu+cudnn的配置运行500次,大约8分钟,精度为95%。
其他的例子,参看Caffe学习系列(9):运行caffe自带的两个简单例子
运行结果大致(我自己机子上跑的结果)如下:
mnist:
精度99%左右。
cifar10:
精度75%左右。
7) 用训练好的model用到自己数据上
参看Caffe学习系列(23):如何将别人训练好的model用到自己的数据上
l 下载caffemodel
参看:Model-Zoo或model zoo documentation;
下载地方:https://github.com/BVLC/caffe/tree/master/models
简单点,直接运行脚本来下载:
sudo ./scripts/download_model_binary.py <dirname>
<dirname>可以是:
models/bvlc_reference_caffenet
models/bvlc_alexnet
models/bvlc_reference_rcnn_ilsvrc13
models/bvlc_googlenet
例2,这里我们以Alexnet为例,
sudo ./scripts/download_model_binary.py models/bvlc_alexnet
下载后,可以在models/bvlc_alexnet/下看到.caffemodel文件。
l 小插曲,先做一个简单的测试,单图的测试,也就是分类。
参考Caffe学习系列(20):用训练好的caffemodel来进行分类
在caffe根目录下的 examples/cpp-classification/ 文件夹下面,有个classification.cpp文件,就是用来分类的。
使用该文件的格式如下:
./build/examples/cpp_classification/classification.bin \
网络结构文件:xx/xx/deploy.prototxt \
训练的模型文件:xx/xx/xx.caffemodel \
训练的图像的均值文件:xx/xx/xx.binaryproto \
类别名称标签文件:xx/xx/synset_words.txt \
待测试图像:xx/xx/xx.jpg
在例2中,即AlexNet模型中,
网络结构文件:models/bvlc_alexnet/deploy.prototxt
训练的模型文件:models/bvlc_alexnet/bvlc_alexnet.caffemodel
训练的图像的均值文件:
直接用脚本下载:sudo sh ./data/ilsvrc12/get_ilsvrc_aux.sh
下载后的文件是data/ilsvrc12/imagenet_mean.binaryproto
类别名称标签文件:
在调用脚本文件下载均值的时候,这个文件也一并下载好了。
文件:data/ilsvrc12/synset_words.txt
待测试图像:
就以自带的测试图片吧:examples/images/cat.jpg
于是完整的命令是:
./build/examples/cpp_classification/classification.bin \ models/bvlc_alexnet/deploy.prototxt \ models/bvlc_alexnet/bvlc_alexnet.caffemodel \ data/ilsvrc12/imagenet_mean.binaryproto \ data/ilsvrc12/synset_words.txt \ examples/images/cat.jpg
运行结果如下:
(不要在意细节= =)
换VGG16试试(在服务器上跑):
(好吧,结果还是好奇怪。。)
l 继续,第二步依然是准备数据并处理啦,前面提过了,不在赘述。
还是用例1的数据。
l 接下来就是测试
依旧跟前面说的一样,要修改.prototxt文件:
先拷贝一份到myfile,顺便把.caffemodel也拷贝过来吧:
cp models/bvlc_alexnet/bvlc_alexnet.caffemodel examples/myfile/ cp models/bvlc_alexnet/train_val.prototxt examples/myfile/ vim examples/myfile/train_val.prototxt
还是修改两个data layer的mean_file和source这两个地方,其它都没有变化 。
接着按照命令行(C++)的测试命令格式,(不清楚的话参考博客)输入测试命令就好了:
1 ./build/tools/caffe test -model examples/myfile/train_val.prototxt -weights examples/myfile/bvlc_alexnet.caffemodel -gpu 0 -iterations 1000
这个测试在服务器上完成的,结果如下:
额,这个结果很僵。。
应该是数据集的问题,用服务器上处理好的数据集吧,
(服务器上的数据是imagenet12百度云下载链接:https://pan.baidu.com/s/1pLt83J9(该测试数据集较大)
修改train_val.prototxt:
再输入测试命令:
1 ./build/tools/caffe test -model examples/myfile/train_val.prototxt -weights examples/myfile/bvlc_alexnet.caffemodel -gpu 0 -iterations 1000
换个VGG试试:
按前面步骤(服务器上没有_train_val.prototxt文件,我自己找了一个,复制到examples/myfile/,按前面的修改),测试:
运行结果如下:
更新中。。。
8) 额外的例子
更新中。。。
注意事项:
1.在caffe中运行的所有程序,都必须在根目录下进行,否则会有下面类似的报错:
例子:报not found的错。
2.遇到问题网上相关文章很多,多看,多问,多学。
更新中。。。