接触caffe时日不多,基本上把官网上的example玩了一遍,只是走马观花的过一遍。
昨天晚上想在自己的数据集上做分类。于是就开始了下面的一系列事情。
1、挑选适合自己任务的modal
caffe zoo中有很多已经训练好的modal,那么根据自己的任务需求找到适合自己的modal,由于我现在想做的是classification的事情,所以我就从http://dl.caffe.berkeleyvision.org/bvlc_reference_caffenet.caffemodeldownload了bvlc_reference_caffenet。下载后的modal放到caffe根目录下的 models/bvlc_reference_caffenet/ 文件夹下面,如果是安装官网给的方法下载的话,
# sudo ./scripts/download_model_binary.py models/bvlc_reference_caffenet
# sudo sh ./data/ilsvrc12/get_ilsvrc_aux.sh
至此我们第一步的任务就over了。
2、准备自己的数据集
我使用的数据集是http://www.vision.caltech.edu/Image_Datasets/Caltech101/Caltech101.html,你用什么样的数据集根据你自己要求,我用这个数据集是因为手边刚好有这个数据,因为该数据集一共有101类,我只是为了练习,所以从中挑选了两类做实验,airplanes和Faces。我把数据集分成了两个部分,train和test。
至此我们第二步的任务就over了。
3、数据集的预处理
当你准备好你自己的数据集后,我们开始预处理数据集。我将数据分成了4个文件夹,如图所示:
这里最好将你自己的图片都resize成网络所需的,比如说我的都resize成227*227的了,因为这个是caffeNet网络所需要的大小。我这里给出一个简单的程序可以批处理文件夹中的文件大小。
clear;clc;close all;
tic; % 用于计算程序运行时间,和toc搭配使用
CoreNum=4; % cpu核的数量
% 下面代码块为并行处理检测与开启程序,需要在执行结束后予以关闭
if matlabpool('size')<=0
matlabpool('open','local',CoreNum);
else
disp('Already initialized');
end
imagePath = '/home/zf/caffe/data/own_data/val_faces/'; % 图片存放路径
imageFiles = dir(imagePath);
numFiles = length(imageFiles);
parfor i=3:numFiles % 从3开始,因为前两个是当前路径‘.’和上一级路径‘..’
j = i-2;
disp(j);
imageFile = strcat(imagePath,imageFiles(i).name);
A = imread(imageFile);
B = imresize(A,[227 227]); % resize为227
imwrite(B,imageFile); % 覆盖原始图片,若需要另存为,则修改此处的imageFile为新的存储路径
end
matlabpool close % 关闭并行
toc; % 显示运行时间
来自网上的代码
3.1、数据集label文件生成
到这一个步骤,我们的数据彻底准备好了,需要把我们的数据以及相应的label写进一个txt中,作为我们下一步工作的输入。最不需要智商的方法就是手动生成txt,但是我个人觉得这个工作量微大,我推荐代码实现,几乎任何语言都可以实现这个功能。我这里给出我使用的代码,网上一搜罗一大筐这种代码。
pos_folder='/home/zf/caffe/data/own_data/airplans/';
neg_folder = '/home/zf/caffe/data/own_data/Faces/';
image_width = 227; %图像小块的宽度
image_height = 227; %图像小块的高度
pixel_total = image_width * image_height*3; %图像中像素点的数目
pos = dir(pos_folder);
[pos_row,pos_line] = size(pos);
neg = dir(neg_folder);
[neg_row,neg_line] = size(neg);
fid=fopen('/home/zf/caffe/data/myfile/train.txt','w');
for i=3:pos_row
img_name = [pos_folder,pos(i).name];
[a,b]=strtok(img_name,pos_folder);
d=strcat(a,b);
d=strcat('train_pos/',d);
fprintf(fid,'%s %d\n',d,0);
end
for i=3:neg_row
img_name = [neg_folder,neg(i).name];
[a,b]=strtok(img_name,neg_folder);
d=strcat(a,b);
d=strcat('train_neg/',d);
fprintf(fid,'%s %d\n',d,1);
end
fclose(fid);
通过上面的代码,修改几次路径后,我就生成了我所需要的label文件,如下图所示:
到此基本上我们这一个步骤的工作就完成了。
3.2、将自己的图片数据集转换成lmdb
又耽搁了好几天,继续骂我的心路历程。
caffe一共支持三种格式(我的知识理解范畴),LMDB,leveldb以及最原始的image格式。我们这里选择LMDB格式,他们三者的优缺点caffe官网上讲解的很是清楚,这里就不罗嗦了。
经过上面的步骤后,我的图片以及相应的图片对应的txt文件也准备好了,使用
/home/zf/caffe/build/tools/convert_imageset /home/zf/caffe/data/own_data/ /home/zf/caffe/data/own_data/val.txt test_lmdb
-resize_width=227 -resize_height=227 -check_size -shuffle true
可以将你上一步骤准备好的图片转换成相应的格式。其中将
/home/zf/caffe/
/home/zf/caffe/data/own_data/val.txt
改成自己的路径,
test_lmdb
表示的是生成的LMDB的名字,
-shuffle true
表示的是是否进行打乱的操作。由于我当时准备的数据集有训练集和测试集,这样我修改上面的对应部分,生成了两个文件,其中每个文件夹中包含两个文件,如下图所示。
3.3、计算数据集的mean iamge
现在我们的数据集已经做好了,接下来我们就计算mean image,为什么一定要计算这个,这个在caffe后面进行training的阶段,会把每个数据图片减去这个mean image,这样据说是可以达到去除背景的功能(据说,其实我不明白这个步骤的具体原因)。使用下面的代码就可以完成这个功能。
sudo /home/zf/caffe/build/tools/compute_image_mean /home/zf/caffe/data/test_lmdb/ /home/zf/caffe/mean_test.binaryproto
调用tools文件夹下面的compute_image_mean 对之前生成的lmdb文件进行求解,求出来的结果保存在一个二进制文件中。网上有其他人将这个图片imshow出来了,啥也没有。这个函数其实我们自己也可以实现,它就是将你准备的数据集中的所有图片的三个图片对应像素点的像素求了一个均值。
OK,That is all!!
4、开始fineturn
剩下的事情就是fineturn了。万事俱备只欠一条指令。使用下面的指令我们就完成了。
./build/tools/caffe train --solver ./models/bvlc_reference_caffenet/solver.prototxt
--weights ./models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel --gpu 0
当然如果不指定下面的命令
--weights ./models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel
我们进行的就是利用我们准备的数据集以及给定的网络结构,生成自己的网络权重。两者之间的差距其实就是网络权重初始化的区别。fineturn的网络权重的初始化是利用我们已经有的caffemodel进行初始化我们网络的权重,而不加weight的意思是随机初始化整个网络的权重,重头开始train一个新的每层权重。
4.1、修改prototxt文件
哈哈,有没有发现上面单额代码并不能让你很好的fineturn以及train,为什么了,因为solver对应的网络结构prototxt文件和我们所做的任务不相同。举个简单的例子,我用的是caffenet做fineturn,caffenet中最后一层softmax可以分类1000类别,而我准备的数据只是二分类,因此这是个需要修改的地方。
同时由于我们的train以及test的数据source和caffenet的肯定不一样,所以需要修改。同时mean image的文件位置也有可能需要修改一下,基本上需要需改的地方也就是这三个地方。
4.2、开始train
./build/tools/caffe train --solver ./models/bvlc_reference_caffenet/solver.prototxt
--weights ./models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel --gpu 0
OKKKKK!!!!5、done
5.1、查看loss
运行train的时候shell界面出现很多行不知道是啥的东西,我们可以在train的时候将他们保存起来。
./examples/mnist/train_lenet.sh|& tee train_loss.log
这样我们就把运行的log文件保存下来了。利用tools目录下面的parse_log.sh可以将我们保存的log文件parse成两个文件,.test文件和.train文件。
./parse_log.sh train_loss.log
生成如下两个文件。
通过修改plot_log.gnuplot.example(依然在tools文件夹下),可以画出Training loss vs. training iterations曲线、Training loss vs. training time曲线等等各种曲线。下图是我在我自己数据集上训练误差曲线。
5.2、查看fineturn过程中生成的snapshot
基本上我们的任务就完成了。剩下的是我们可以使用我们train生成的model进行单张图片测试等任务。
利用下面的代码可以实现利用我们生成的caffemodel进行单张图片分类。
./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/basketball.jpg
到此关于fineturn以及如何自我训练就完成了。以上都是我亲测完的。欢迎大家补充和纠正。谢谢!