caffe + win10基於CaffeNet網路框架訓練自己的圖片進行分類(實踐篇)
接觸caffe一段時間了 ,一直沒有自己完整的跑過自己的資料,現在使用在win10系統上配置好的caffe環境,使用caffeNet網路框架,對自己準備的圖片資料集進行訓練,並使用生成的模型對圖片進行類別預測。接觸時間比較短,有的地方理解不到位,現在整理下我處理的流程以及過程之中遇到過的問題,希望可以與大家互享經驗,不足之處請大家多多指教。
在caffe目錄examples下新建my_classify資料夾,其餘檔案均在該目錄下;
一、製作資料集
1、準備圖片
使用Corel資料集中的400圖片,4類每類選取100張,其中90張作為訓練集,10張作為測試集。所以訓練集train中有360張圖片,測試集test中有40張圖片,分別存放在train與test資料夾中。
2、生成帶標籤的列表list.txt
分別在train與test檔案中生成list.txt列表,切記標籤要從0開始。我使用python指令碼分別對每個類別影象生成標籤 ,然後和在一起的。(方法有點笨,附上基本的生成列表的程式碼,可以寫個迴圈,直接生成train的list.txt的)
import os
def generate(dir,label):
files = os.listdir(dir)
files.sort()
print '****************'
print 'input :',dir
print 'start...'
listText = open(dir+'\\'+'list.txt','w')
for file in files:
fileType = os.path.split(file)
if fileType[1] == '.txt':
continue
name = file + ' ' + str(int(label)) +'\n'
listText.write(name)
listText.close()
print 'down!'
print '****************'
if __name__ == '__main__':
generate('D:\\caffe\\caffe-master\\examples\\my_classify\\Test',1)
生成的list.txt
注:如果label標記不從0開始,可能會導致 label_value < num_labels 問題:
3、生成lmdb格式資料集,並生成二進位制imagemean.binaryproto的均值檔案(size 256 256)
採用windows批處理格式的檔案.bat,檔名為:convert_and_computeMean.bat
D:\caffe\caffe-master\Build\x64\Debug\convert_imageset.exe --resize_height=256 --resize_width=256 --shuffle --backend="lmdb" train/ train/list.txt trainlmdb
D:\caffe\caffe-master\Build\x64\Debug\convert_imageset.exe --resize_height=256 --resize_width=256 --shuffle --backend="lmdb" test/ test/list.txt testlmdb
D:\caffe\caffe-master\Build\x64\Debug\compute_image_mean.exe trainlmdb image_mean.binaryproto
pause
執行結果:
生成trainlmdb、testlmdb和image_mean.binaryproto三個檔案
注:如果資料集有改動,要將原來生成的trainlmdb和testlmdb刪除,否則會造成如下問題:
至此資料集製作部分完成。
二、新建網路模型train_val.prototxt和solver.prototxt兩個檔案
這裡直接使用caffeNet的網路架構模型
需要做如下更改:
1、訓練集與測試集的路徑,直接改成了絕對路徑;
2、batch_size改成了4,crop_size改成了與圖片一般大小的256;
3、最後一層的num_output設定成4,因為只有4類。
name: "CaffeNet"
layer {
name: "data"
type: "Data"
top: "data"
top: "label"
include {
phase: TRAIN
}
transform_param {
mirror: true
crop_size: 256
mean_file: "D:/caffe/caffe-master/examples/my_classify/image_mean.binaryproto"
}
data_param {
source: "D:/caffe/caffe-master/examples/my_classify/trainlmdb"
batch_size: 4
backend: LMDB
}
}
layer {
name: "data"
type: "Data"
top: "data"
top: "label"
include {
phase: TEST
}
transform_param {
mirror: false
crop_size: 256
mean_file: "D:/caffe/caffe-master/examples/my_classify/image_mean.binaryproto"
}
data_param {
source: "D:/caffe/caffe-master/examples/my_classify/testlmdb"
batch_size: 4
backend: LMDB
}
}
layer {
name: "conv1"
type: "Convolution"
bottom: "data"
top: "conv1"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 96
kernel_size: 11
stride: 4
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
name: "relu1"
type: "ReLU"
bottom: "conv1"
top: "conv1"
}
layer {
name: "pool1"
type: "Pooling"
bottom: "conv1"
top: "pool1"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer {
name: "norm1"
type: "LRN"
bottom: "pool1"
top: "norm1"
lrn_param {
local_size: 5
alpha: 0.0001
beta: 0.75
}
}
layer {
name: "conv2"
type: "Convolution"
bottom: "norm1"
top: "conv2"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 256
pad: 2
kernel_size: 5
group: 2
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 1
}
}
}
layer {
name: "relu2"
type: "ReLU"
bottom: "conv2"
top: "conv2"
}
layer {
name: "pool2"
type: "Pooling"
bottom: "conv2"
top: "pool2"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer {
name: "norm2"
type: "LRN"
bottom: "pool2"
top: "norm2"
lrn_param {
local_size: 5
alpha: 0.0001
beta: 0.75
}
}
layer {
name: "conv3"
type: "Convolution"
bottom: "norm2"
top: "conv3"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 384
pad: 1
kernel_size: 3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
name: "relu3"
type: "ReLU"
bottom: "conv3"
top: "conv3"
}
layer {
name: "conv4"
type: "Convolution"
bottom: "conv3"
top: "conv4"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 384
pad: 1
kernel_size: 3
group: 2
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 1
}
}
}
layer {
name: "relu4"
type: "ReLU"
bottom: "conv4"
top: "conv4"
}
layer {
name: "conv5"
type: "Convolution"
bottom: "conv4"
top: "conv5"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 256
pad: 1
kernel_size: 3
group: 2
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 1
}
}
}
layer {
name: "relu5"
type: "ReLU"
bottom: "conv5"
top: "conv5"
}
layer {
name: "pool5"
type: "Pooling"
bottom: "conv5"
top: "pool5"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer {
name: "fc6"
type: "InnerProduct"
bottom: "pool5"
top: "fc6"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 4096
weight_filler {
type: "gaussian"
std: 0.005
}
bias_filler {
type: "constant"
value: 1
}
}
}
layer {
name: "relu6"
type: "ReLU"
bottom: "fc6"
top: "fc6"
}
layer {
name: "drop6"
type: "Dropout"
bottom: "fc6"
top: "fc6"
dropout_param {
dropout_ratio: 0.5
}
}
layer {
name: "fc7"
type: "InnerProduct"
bottom: "fc6"
top: "fc7"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 4096
weight_filler {
type: "gaussian"
std: 0.005
}
bias_filler {
type: "constant"
value: 1
}
}
}
layer {
name: "relu7"
type: "ReLU"
bottom: "fc7"
top: "fc7"
}
layer {
name: "drop7"
type: "Dropout"
bottom: "fc7"
top: "fc7"
dropout_param {
dropout_ratio: 0.5
}
}
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: 4
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"
}
solver.prototxt檔案
net: "D:/caffe/caffe-master/examples/my_classify/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
snapshot: 200
snapshot_prefix: "D:/caffe/caffe-master/examples/my_classify/train"
至此網路搭建部分結束,接下來可以開始訓練
三、訓練
新建train.bat檔案
caffe.exe train --solver=solver.prototxt --gpu=all
pause
訓練結果,其中accuracy=0.625挺低的,首先訓練集不夠多,其次迭代次數較少
訓練結束會生成xxx.caffemodel和xxx.solverstate分別儲存訓練過程的引數和中段的引數資訊
四、利用生成的模型使用python介面測試自己的資料
1、通過指令碼將image_mean.binaryproto轉換成python可以識別的mean.npy檔案
import caffe
import numpy as np
MEAN_PROTO_PATH = 'image_mean.binaryproto'
MEAN_NPY_PATH = 'mean.npy'
blob = caffe.proto.caffe_pb2.BlobProto()
data = open(MEAN_PROTO_PATH, 'rb' ).read()
blob.ParseFromString(data)
array = np.array(caffe.io.blobproto_to_array(blob))
mean_npy = array[0]
np.save(MEAN_NPY_PATH ,mean_npy)
2、編寫與train_val.prototxt對應的deploy.prototxt用於測試的網路模型
name: "CaffeNet"
layer {
name: "data"
type: "Input"
top: "data"
input_param { shape: { dim: 10 dim: 3 dim: 256 dim: 256 } }
}
layer {
name: "conv1"
type: "Convolution"
bottom: "data"
top: "conv1"
convolution_param {
num_output: 96
kernel_size: 11
stride: 4
}
}
layer {
name: "relu1"
type: "ReLU"
bottom: "conv1"
top: "conv1"
}
layer {
name: "pool1"
type: "Pooling"
bottom: "conv1"
top: "pool1"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer {
name: "norm1"
type: "LRN"
bottom: "pool1"
top: "norm1"
lrn_param {
local_size: 5
alpha: 0.0001
beta: 0.75
}
}
layer {
name: "conv2"
type: "Convolution"
bottom: "norm1"
top: "conv2"
convolution_param {
num_output: 256
pad: 2
kernel_size: 5
group: 2
}
}
layer {
name: "relu2"
type: "ReLU"
bottom: "conv2"
top: "conv2"
}
layer {
name: "pool2"
type: "Pooling"
bottom: "conv2"
top: "pool2"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer {
name: "norm2"
type: "LRN"
bottom: "pool2"
top: "norm2"
lrn_param {
local_size: 5
alpha: 0.0001
beta: 0.75
}
}
layer {
name: "conv3"
type: "Convolution"
bottom: "norm2"
top: "conv3"
convolution_param {
num_output: 384
pad: 1
kernel_size: 3
}
}
layer {
name: "relu3"
type: "ReLU"
bottom: "conv3"
top: "conv3"
}
layer {
name: "conv4"
type: "Convolution"
bottom: "conv3"
top: "conv4"
convolution_param {
num_output: 384
pad: 1
kernel_size: 3
group: 2
}
}
layer {
name: "relu4"
type: "ReLU"
bottom: "conv4"
top: "conv4"
}
layer {
name: "conv5"
type: "Convolution"
bottom: "conv4"
top: "conv5"
convolution_param {
num_output: 256
pad: 1
kernel_size: 3
group: 2
}
}
layer {
name: "relu5"
type: "ReLU"
bottom: "conv5"
top: "conv5"
}
layer {
name: "pool5"
type: "Pooling"
bottom: "conv5"
top: "pool5"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer {
name: "fc6"
type: "InnerProduct"
bottom: "pool5"
top: "fc6"
inner_product_param {
num_output: 4096
}
}
layer {
name: "relu6"
type: "ReLU"
bottom: "fc6"
top: "fc6"
}
layer {
name: "drop6"
type: "Dropout"
bottom: "fc6"
top: "fc6"
dropout_param {
dropout_ratio: 0.5
}
}
layer {
name: "fc7"
type: "InnerProduct"
bottom: "fc6"
top: "fc7"
inner_product_param {
num_output: 4096
}
}
layer {
name: "relu7"
type: "ReLU"
bottom: "fc7"
top: "fc7"
}
layer {
name: "drop7"
type: "Dropout"
bottom: "fc7"
top: "fc7"
dropout_param {
dropout_ratio: 0.5
}
}
layer {
name: "fc8"
type: "InnerProduct"
bottom: "fc7"
top: "fc8"
inner_product_param {
num_output: 4
}
}
layer {
name: "prob"
type: "Softmax"
bottom: "fc8"
top: "prob"
}
3、編寫python介面
import caffe
import numpy as np
root = 'D:/caffe/caffe-master/examples/my_classify/'
#設定測試網路
deploy = root + 'deploy.prototxt'
#新增訓練的網路權重引數
caffe_model = root + 'train_iter_500.caffemodel'
#測試圖片
img = root + '4.jpg'
#標籤檔案
label_file = root + 'label.txt'
#均值檔案
mean_file = root + 'mean.npy'
#設定使用GPU
caffe.set_model_gpu()
#構造一個net
net = caffe.Net(deploy,caffe_model,caffe.TEST)
# 得到data的形狀,這裡的圖片是預設matplotlib底層載入的
transformer = caffe.io.Transformer({'data':net.blobs['data'].data.shape})
# matplotlib載入的image是畫素[0-1],圖片的資料格式[weight,high,channels],RGB
# caffe載入的圖片需要的是[0-255]畫素,資料格式[channels,weight,high],BGR,那麼就需要轉換
transformer.set_transpose('data', (2,0,1))
transformer.set_mean('data', np.load(mean_file).mean(1).mean(1))
# 圖片畫素放大到[0-255]
transformer.set_raw_scale('data', 255)
# RGB-->BGR 轉換
transformer.set_channel_swap('data', (2,1,0))
#設定輸入的圖片shape,1張,3通道,長寬都是256
net.blobs['data'].reshape(1,3,256,256)
#載入圖片
im = caffe.io.load_image(img)
net.blobs['data'].data[...] = transformer.preprocess('data', im)
#輸出每層網路的name和shape
for layer_name,blob in net.blobs.iteritems():
print layer_name + '\t' + str(blob.data.shape)
#網路向前傳播
out = net.forward()
labels = np.loadtxt(label_file,str,delimiter = '\t')
prob = net.blobs['prob'].data[0].flatten()
print prob
order = prob.argsort()[-1]
#輸出類別
print 'the class is:',labels[order]
測試圖片為:
分類結果為: