最近在学习caffe,因此将自己的训练和测试的过程及代码记录下来。
(1)处理数据集,理论上caffe的训练集和测试集的比例为3:1,首先将我们将自己的数据图片先分为训练集和测试集,然后分别对测试集和训练集的图片进行分类,此次我的数据集分类两类,一类c,一类m,如下所示
训练集
测试集
每个文件夹中都是对应的图片。
(2)制作标签文件,标签文件应该是如下格式(下面是训练集的标签文件)
标签文件格式应当是“ 路径\文件名 标签”(标签要从0开始)
制作标签文件可使用python完成,python代码如下:
import os
root="D:/caffe/caffe-master/caffe-master/data/mineral_data" #此处为根路径
#制作训练集文件标签
i=-7 #i的数字根据下面输出的i来改,直到改为输出从0开始
with open(root+"/train/train.txt","w") as f: #训练集标签文件生成地址(下同)
for root,dirs,files in os.walk(root):
for dir in dirs:
for root,dirs,files in os.walk("D:/caffe/caffe-master/caffe-master/data/mineral_data/train/"+str(dir)):
for file in files:
image_file=str(dir)+"\\"+str(file)
label=image_file+" "+str(i)+"\n"
f.writelines(label)
print(i) #此处输出为标签数字,要从0开始,若不从0开始修改上方i初始化
i+=1
#制作测试集标签文件
i=-5
with open("D:/caffe/caffe-master/caffe-master/data/mineral_data/test/test.txt","w") as f:
for root,dirs,files in os.walk("D:/caffe/caffe-master/caffe-master/data/mineral_data"):
for dir in dirs:
for root,dirs,files in os.walk("D:/caffe/caffe-master/caffe-master/data/mineral_data/test/"+str(dir)):
for file in files:
image_file=str(dir)+"\\"+str(file)
label=image_file+" "+str(i)+"\n"
f.writelines(label)
print(i)
i+=1
(3)生成caffe使用的格式文件(lmdb格式),该处使用.bat文件,脚本文件内容如下:
生成训练集lmdb格式文件:
D:/caffe/caffe-master/caffe-master/Build/x64/Release/convert_imageset.exe --resize_height=100 --resize_width=100 --shuffle --backend=lmdb D:/caffe/caffe-master/caffe-master/data/mineral_data/train/ D:/caffe/caffe-master/caffe-master/data/mineral_data/label/train.txt train_leveldb
pause
测试集lmdb格式文件:
D:/caffe/caffe-master/caffe-master/Build/x64/Release/convert_imageset.exe --resize_height=100 --resize_width=100 --shuffle --backend=lmdb D:/caffe/caffe-master/caffe-master/data/mineral_data/test/ D:/caffe/caffe-master/caffe-master/data/mineral_data/label/test.txt test_leveldb
pause
生成格式文件如下:
上面所用脚本文件说明:
(4)生成均值文件(使用.bat文件):(mean.binaryproto)
脚本文件如下:
D:\caffe\caffe-master\caffe-master\Build\x64\Release\compute_image_mean D:\caffe\caffe-master\caffe-master\data\mineral_data\train_leveldb D:\caffe\caffe-master\caffe-master\data\mineral_data\mean.binaryproto
脚本文件说明:
(5)修改train_val.prototxt文件,该文件为训练网络结构
本次我们采用mobilenet,如果你想用其他网络也可以,可以从github上下载相应网络结构,不过注意下载下来的网络模型只有deploy.prototxt(测试所用网络结构)和已经训练好的caffemodel(识别测试时所用),因此此时你需要修改deploy.prototxt文件来生成train_val.prototxt文件,下面说明具体步骤。
首先将下载的模型文件复制到自己test和train文件夹所在的目录如下:
mobilenet为所用网络模型,test和train分别为训练集和测试集文件,另外两个为前面所生成的lmdb格式文件
接下来取出mobilenet文件夹中的deploy.prototoxt文件,复制到该目录下:
再将该文件复制一份,命名为 train_val.prototxt
打开该文件,如下:
接下里修改该文件,删除最后一个层(prob)和前面的数据维度描述:
然后添加数据层和accuracy层,全连接层以及loss层,如下:
#数据层添加在最开始:
name: "MOBILENET"
layer {
name: "data"
type: "Data"
top: "data"
top: "label"
include {
phase: TRAIN
}
transform_param {
mirror: true
crop_size: 100
#均值文件所在目录
mean_file: "D:/caffe/caffe-master/caffe-master/data/mineral_data/mean.binaryproto"
}
data_param {
#训练集lmdb格式文件目录
source: "D:/caffe/caffe-master/caffe-master/data/mineral_data/train_leveldb"
batch_size: 50
backend: LMDB
}
}
layer {
name: "data"
type: "Data"
top: "data"
top: "label"
include {
phase: TEST
}
transform_param {
mirror: false
crop_size: 100
mean_file: "D:/caffe/caffe-master/caffe-master/data/mineral_data/mean.binaryproto"
}
data_param {
#测试集lmdb格式文件目录
source: "D:/caffe/caffe-master/caffe-master/data/mineral_data/test_leveldb"
batch_size: 10
backend: LMDB
}
}
#以下三层添加在最后
layer {
name: "fc8"
type: "InnerProduct"
bottom: "fc7" #注意此处要与上一层连接起来
top: "fc8"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 3 #此处为输出类别数,建议比实际输出类别大,我实际分类2,此处写为3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
name: "accuracy"
type: "Accuracy"
bottom: "fc8"
bottom: "label"
top: "accuracy"
include {
phase: TEST
}
}
layer {
name: "loss"
type: "SoftmaxWithLoss"
bottom: "fc8"
bottom: "label"
top: "loss"
}
修改完成后的train_val文件如下所示(仅展示开头和结尾):
(6)修改超参数文件solver.prototxt
该文件定义了网络训练过程中的重要参数如学习率,batch_size等,文件参数在此不再赘述,详情参看其他博客,我们只说一个参数,该参数定义了caffemodel所在路径。
建议在该目录建一个model文件夹保存训练所得model,solver_mode定义了该训练是gpu还是cpu。
(7)至此我们的准备工作全部完成,接下来便是训练了,创建训练脚本train.bat,脚本内容如下:
D:\caffe\caffe-master\caffe-master\Build\x64\Release\caffe.exe train -solver=D:\caffe\caffe-master\caffe-master\data\mineral_data\solver.prototxt
pause
脚本文件说明如下:
双击即可开始训练。如下:
最后即可得到caffemodel和准确率,至此训练阶段全部完成,如果想提高准确率的话可以修改超参数文件中的参数。
(如果你想绘制训练过程中的loss曲线和accuracy曲线欢迎参看我的其他博客)
(8)进行识别。
训练的目的就是为了识别图片,我们完成了训练得到了model,接下来进行识别。
首先制作标签,该标签为识别时的判定标签(labels.txt):
前面提到我的网络为对两个类别识别分类,且为c,m两类,因此此处写为该格式,你可根据自己的网络酌情修改。
接下来为deploy.prototxt文件:
修改如下地方:
箭头处应与train_val文件数据层的crop_size相同。
结尾在softmax层前添加全连接层,全连接层同前面train_val.prototxt文件中添加的,修改完后如下所示:
然后找到训练的caffemodel:
测试时使用训练次数最多保存的模型文件如上红圈所示(2000是因为我设置了最大迭代次数为2000,你也可以为其他)。
准备工作完成,我们开始识别测试,此处我们测试一个文件夹的图片,为了方便我放置了8张,首先你需要修改文件夹中不同类别字母的后缀以便最后和识别输出对比看是否识别准确。我修改如下:
最后一个数字0和1代表不同类别,此处数字应为前面设置标签时的数字。
我们使用python接口来进行识别,由于前面使用了均值文件,而python接口识别时也需要均值文件,但python处理的文件为.npy文件,因此需要转换mean文件的格式。代码如下:
import caffe
import numpy as np
MEAN_PROTO_PATH = r'D:\caffe\caffe-master\caffe-master\data\mineral_data\mean.binaryproto' # 待转换的pb格式图像均值文件路径
MEAN_NPY_PATH = r'D:\caffe\caffe-master\caffe-master\data\mineral_data\mean.npy' # 转换后的numpy格式图像均值文件路径
blob = caffe.proto.caffe_pb2.BlobProto() # 创建protobuf blob
data = open(MEAN_PROTO_PATH, 'rb' ).read() # 读入mean.binaryproto文件内容
blob.ParseFromString(data) # 解析文件内容到blob
array = np.array(caffe.io.blobproto_to_array(blob))# 将blob中的均值转换成numpy格式,array的shape (mean_number,channel, hight, width)
mean_npy = array[0] # 一个array中可以有多组均值存在,故需要通过下标选择其中一组均值
np.save(MEAN_NPY_PATH ,mean_npy)
转换后的均值文件如下:
接下来识别,python代码如下:
#coding=utf-8
import os,sys
import caffe
import numpy as np
root='D:/caffe/caffe-master/caffe-master/data/mineral_data' #根目录
deploy=root + '/mobilenet_deploy.prototxt' #deploy文件
caffe_model=root + '/model/mobile93%/_iter_2000.caffemodel' #训练好的 caffemodel
labels_filename = root + '/label.txt' #类别名称文件,将数字标签转换回类别名称
mean_file=root+"/mean.npy"
net = caffe.Net(deploy,caffe_model,caffe.TEST) #加载model和network
transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape}) #设定图片的shape格式(1,3,28,28)
transformer.set_transpose('data', (2,0,1)) #改变维度的顺序,由原始图片(28,28,3)变为(3,28,28)
transformer.set_mean('data', np.load(mean_file).mean(1).mean(1)) #减去均值,前面训练模型时没有减均值,这儿就不用
transformer.set_raw_scale('data', 255) # 缩放到【0,255】之间
transformer.set_channel_swap('data', (2,1,0)) #交换通道,将图片由RGB变为BGR
path=r"D:\caffe\caffe-master\caffe-master\data\mineral_data\test\shiyan"#此处为测试图片目录
mulu=os.listdir(path)
test=[] #创建空列表存放每个图片的预测值
for i in mulu:
im=caffe.io.load_image(path+"\\"+i)
net.blobs['data'].data[...] = transformer.preprocess('data',im)
out = net.forward()
labels = np.loadtxt(labels_filename, str, delimiter='\t')
prob= net.blobs['prob'].data[0].flatten()
order=prob.argsort()[-1]
test.append(labels[order])
train=[]#创建空列表存放每个图片的真实标记
for s in mulu:
train.append(os.path.basename(s)[-5])
for i in range(0,len(test)):
print(train[i] , test[i],"\n")
输出结果如下:
上图为图像识别输出,第一列为实际图像所属类别,第二列为模型识别分类(c,m代表两种不同类别的图像)
可以看到识别完全正确,如果你们识别有错则是网络模型准确率太低,重新调试超参数文件和训练以求达到好的识别准确率。
至此一个模型训练识别已经完成,你已经可以用来训练自己的项目了。