前言
之前有用OpenCv的SUFT特征提取和SVM、BOW做過按圖像里的內(nèi)容進(jìn)行分類的相關(guān)項(xiàng)目,耗時(shí)長,準(zhǔn)確率又不是很高,各種優(yōu)化之后準(zhǔn)確率也只有百分七十到八十,所以一直想用caffe試試。
一、系統(tǒng)環(huán)境
1.windows 7 64位
之前一直在linux下(Ubuntu 16.04 64位)使用過caffe,然后也有在win7 32位試過,能編譯,但是在訓(xùn)練過程中出現(xiàn)各種小問題,所有就換64位系統(tǒng),在訓(xùn)練沒有遇到什么問題。
2.Anaconda3
安裝Anaconda3盡量裝3.4,這樣就不用再把python的版本降到3.5。
3.caffe CPU
caffe我使用的是CPU版本。
二、數(shù)據(jù)收集與處理
1.收集數(shù)據(jù)
圖像數(shù)據(jù)是從ZOL壁紙網(wǎng)站下載,里面有分類好的壁紙,可以整個(gè)系列下載。下載之后新建文件夾放同類型的圖像,我收集了四個(gè)類型的然后手工分類放到相關(guān)的文件夾里,每個(gè)種類收集了差不多150張圖像。
比如我這里把動(dòng)漫人物放到這個(gè)文件夾下:
2.更改文件名
但下載下來的文件的文件名很混亂,所以要更改成與文件夾對應(yīng)的文件名,方便之后訓(xùn)練使用,編寫python腳本更改整個(gè)文件夾的文件名,每個(gè)類型的文件夾運(yùn)行一次
rename.ipynb
import os
def rename(): path="E:/caffe/4/" #文件路徑ex = 4filelist = os.listdir(path) #該文件夾下的所有文件count = 0for file in filelist: #遍歷所有文件 包括文件夾Olddir = os.path.join(path,file)#原來文件夾的路徑if os.path.isdir(Olddir):#如果是文件夾,則跳過continuefilename = os.path.splitext(file)[0] #文件名filetype = ".jpg"#os.path.splitext(file)[1] 文件擴(kuò)展名p = str(count).zfill(3)Newdir = os.path.join(path,str(ex)+p+filetype) #新的文件路徑os.rename(Olddir,Newdir) #重命名count += 1
rename()
得到統(tǒng)一遞增的文件名,文件名前綴是當(dāng)前的文件夾名,生成訓(xùn)練文件名列表是以文件名前綴打上標(biāo)簽。
3.統(tǒng)一圖像大小
下載下來的圖像文件大小有很多用類型的,編寫python腳本把每個(gè)文件夾下的圖像改成統(tǒng)一大小的像素的,該腳本把所有圖像改成寬384和高256的圖像。
resize.ipynb
from PIL import Image
import glob, os
w,h = 384,256 #更改成的分辨率
def timage():for files in glob.glob('E:/caffe/5/*.jpg'): #原文件路徑filepath,filename = os.path.split(files)filterame,exts = os.path.splitext(filename)opfile = r'E:/caffe/data/5/' #保存的文件路徑if (os.path.isdir(opfile)==False):os.mkdir(opfile)im=Image.open(files)im_ss=im.resize((int(w), int(h)))try:im_ss.save(opfile+filterame+'.jpg')except:print (filterame)os.remove(opfile+filterame+'.jpg')if __name__=='__main__':timage()
4.可以從這里下載我分好類的正樣本和測試樣本,下載地址:https://download.csdn.net/download/matt45m/11044661
三、準(zhǔn)備訓(xùn)練
1.創(chuàng)建數(shù)據(jù)文件夾
(1)在caffe-windows/data路徑下創(chuàng)建一個(gè)自己存放數(shù)據(jù)的文件夾,這里起名為classify,在classify創(chuàng)建兩個(gè)文件夾,分別為train和test,如下圖:
(2)把要訓(xùn)練的圖像文件放到train文件夾下,這里每個(gè)類別選了120張照片放進(jìn)來,剩下的圖像放到test文件夾里面,如下圖:
(3)test文件夾里放著測試用的圖像,如下圖:
2.得到數(shù)據(jù)集文件名列表
(1)編寫python代碼,得到train與test文件夾下的文件列表并標(biāo)記
getFileNameList.ipynb
import osif __name__ == "__main__":data_dir = 'E:/LIB/caffe-windows/data/classify/test/' #要遍歷的文件夾fid = open("E:/LIB/caffe-windows/data/classify/test.txt","w") #保存的文件列表files = os.listdir(data_dir)index = 0for ii, file in enumerate(files,1):fid.write("{0}{1} {2}\n".format("",file, int(file[0])-2))index = index + 1if index%100 == 0:print("{0} images processed!".format(index))print("All images processed!")fid.close()
運(yùn)行之后在classify文件夾生成兩個(gè)train.txt和test.txt
(2)得到的train.txt和test.txt文件內(nèi)容如下:
test.txt的一部分內(nèi)容,后面數(shù)字為類型標(biāo)記
train.txt的內(nèi)容,后面數(shù)字為類型標(biāo)記
四、轉(zhuǎn)換數(shù)據(jù)
在caffe-windows/data/classify文件夾下編寫腳本,把圖像數(shù)據(jù)改成Leveldb格式
data_convention.bat
E:/LIB/caffe-windows/build/tools/Release/convert_imageset.exe --shuffle --resize_height=256 --resize_width=256 --shuffle --backend=leveldb E:/LIB/caffe-windows/data/classify/train/ E:/LIB/caffe-windows/data/classify/train.txt E:/LIB/caffe-windows/data/classify/train_leveldb
E:/LIB/caffe-windows/build/tools/Release/convert_imageset.exe --shuffle --resize_height=256 --resize_width=256 --shuffle --backend=leveldb E:/LIB/caffe-windows/data/classify/test/ E:/LIB/caffe-windows/data/classify/test.txt E:/LIB/caffe-windows/data/classify/test_leveldb
pause
其中resize_height和resize_width表示將原圖像更改為相應(yīng)的大小,這里改成256是因?yàn)檫x取的網(wǎng)絡(luò)(ImageNet)的要求,shuffle是將數(shù)據(jù)隨機(jī)打亂的意思,backend表示將數(shù)據(jù)轉(zhuǎn)換的格式,這里選擇Leveldb。
出現(xiàn)下面的窗口代表轉(zhuǎn)換成功
注:Caffe生成的數(shù)據(jù)分為2種格式:Lmdb和Leveldb,它們都是鍵/值對(Key/Value Pair)嵌入式數(shù)據(jù)庫管理系統(tǒng)編程庫。lmdb的內(nèi)存消耗是leveldb的1.1倍,但是lmdb的速度比leveldb快10%至15%,更重要的是lmdb允許多種訓(xùn)練模型同時(shí)讀取同一組數(shù)據(jù)集。因此之后lmdb取代了leveldb成為Caffe默認(rèn)的數(shù)據(jù)集生成格式。但上面還是使用Leveldb數(shù)據(jù)類型。
2.運(yùn)行之后在caffe-windows/data/classify生成兩個(gè)文件夾,test_leveldb和train_leveldb兩個(gè)文件夾:
test_leveldb文件夾下內(nèi)容
train_leveldb文件夾下內(nèi)容
五、生成均值文件
在caffe-windows/data/classify文件夾下編寫腳本,點(diǎn)擊運(yùn)行,生成均值文件
data_mean.bat
E:/LIB/caffe-windows/build/tools/Release/compute_image_mean.exe E:/LIB/caffe-windows/data/classify/train_leveldb --backend=leveldb E:/LIB/caffe-windows/data/classify/train_mean.binaryproto
E:/LIB/caffe-windows/build/tools/Release/compute_image_mean.exe E:/LIB/caffe-windows/data/classify/test_leveldb --backend=leveldb E:/LIB/caffe-windows/data/classify/test_mean.binaryproto
pause
其中backend的參數(shù)要與上面轉(zhuǎn)換時(shí)的格式保持一致,運(yùn)行完成后,會在caffe-windows/data/classify文件夾下生成train_mean.binaryproto和test_mean.binaryproto文件
出現(xiàn)以下窗口代表生成成功
在caffe-windows/data/classify生成兩個(gè)均值文件,如下:
六、訓(xùn)練數(shù)據(jù)
1.將caffe-windows/models/bvlc_reference_caffenet文件夾下的deploy.prototxt、solver.prototxt和train_val.prototxt拷貝到caffe-windows/data/classify下。
bvlc_reference_caffenet文件夾:
復(fù)制到classify文件夾下:
2.更改solver.prototxt
#訓(xùn)練樣本為480張圖像,batch_size = 60,480 / 60 = 8 那么test_interval(測試間隔)的值要大于或者等于8,即處理完一次所有的訓(xùn)練數(shù)據(jù)后,才去進(jìn)行測試.
#如果想訓(xùn)練100代,max_iter 則最大迭代次數(shù)為800。
#測試數(shù)據(jù)為100張圖像,batch_size = 25,100 / 25 = 4 那么test_interval(測試間隔)的值要大于或者等于4,即需要4次才能完整的測試一次。
#stepsize(學(xué)習(xí)率變化規(guī)律)置為隨著迭代次數(shù)的增加,慢慢變低。總共迭代800次,我們將變化5次,所以stepsize設(shè)置為800/5=160,即每迭代160次,就要降低一次學(xué)習(xí)率。
net: "data/classify/train_val.prototxt" #訓(xùn)練或者測試配置文件
test_iter:4 #完成一次測試需要的迭代次數(shù)
test_interval: 8 #測試間隔
base_lr: 0.001 #基礎(chǔ)學(xué)習(xí)率
lr_policy: "step" #學(xué)習(xí)率變化規(guī)律
gamma: 0.1 #學(xué)習(xí)率變化指數(shù)
stepsize: 160 #學(xué)習(xí)率變化頻率 (stepsize不能太小,如果太小會導(dǎo)致學(xué)習(xí)率再后來越來越小,達(dá)不到充分收斂的效果)
display: 20 #屏幕顯示間隔
max_iter: 800 #最大迭代次數(shù)
momentum: 0.9 #動(dòng)量
weight_decay: 0.0005 #權(quán)重衰減
snapshot: 5000 #保存模型間隔
snapshot_prefix: "data/classify/caffenet_train" #保存模型的前綴
solver_mode: CPU #使用GPU或者CPU
3.更改train_val
對trian_val文件進(jìn)行修改,更改source路徑,batch_size,backend和mean_file,其中batch_size看計(jì)算機(jī)的配置,計(jì)算機(jī)配置較高,可以設(shè)大一點(diǎn),訓(xùn)練的結(jié)果準(zhǔn)確率會有些提升。
name: "CaffeNet"
layer {name: "data"type: "Data"top: "data" #輸出數(shù)據(jù)top: "label" #輸出標(biāo)簽include {phase: TRAIN #訓(xùn)練階段}transform_param {mirror: true #映射是否開啟crop_size: 227 #圖的尺寸mean_file: "data/classifyCPP/train_mean.binaryproto" #均值文件路徑}
# mean pixel / channel-wise mean instead of mean image
# transform_param {
# crop_size: 227
# mean_value: 104
# mean_value: 117
# mean_value: 123
# mirror: true
# }data_param {source: "data/classifyCPP/train_lmdb" #訓(xùn)練集的lmdb數(shù)據(jù)路徑batch_size: 60 #每一批的大小backend: leveldb #數(shù)據(jù)格式leveldb}
}
layer {name: "data"type: "Data"top: "data"top: "label"include {phase: TEST #測試階段}transform_param {mirror: false #映射是否開啟crop_size: 227 #測試圖的尺寸mean_file: "data/classifyCPP/test_mean.binaryproto" #測試集的均值文件}
# mean pixel / channel-wise mean instead of mean image
# transform_param {
# crop_size: 227
# mean_value: 104
# mean_value: 117
# mean_value: 123
# mirror: false
# }data_param {source: "data/classifyCPP/test_lmdb" #測試集的lmdb數(shù)據(jù)路徑batch_size: 25 #測試圖像個(gè)數(shù)backend: LMDB}
}
layer {name: "conv1"type: "Convolution"bottom: "data"top: "conv1"param {lr_mult: 1decay_mult: 1}param {lr_mult: 2decay_mult: 0}convolution_param {num_output: 96kernel_size: 11stride: 4weight_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: MAXkernel_size: 3stride: 2}
}
layer {name: "norm1"type: "LRN"bottom: "pool1"top: "norm1"lrn_param {local_size: 5alpha: 0.0001beta: 0.75}
}
layer {name: "conv2"type: "Convolution"bottom: "norm1"top: "conv2"param {lr_mult: 1decay_mult: 1}param {lr_mult: 2decay_mult: 0}convolution_param {num_output: 256pad: 2kernel_size: 5group: 2weight_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: MAXkernel_size: 3stride: 2}
}
layer {name: "norm2"type: "LRN"bottom: "pool2"top: "norm2"lrn_param {local_size: 5alpha: 0.0001beta: 0.75}
}
layer {name: "conv3"type: "Convolution"bottom: "norm2"top: "conv3"param {lr_mult: 1decay_mult: 1}param {lr_mult: 2decay_mult: 0}convolution_param {num_output: 384pad: 1kernel_size: 3weight_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: 1decay_mult: 1}param {lr_mult: 2decay_mult: 0}convolution_param {num_output: 384pad: 1kernel_size: 3group: 2weight_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: 1decay_mult: 1}param {lr_mult: 2decay_mult: 0}convolution_param {num_output: 256pad: 1kernel_size: 3group: 2weight_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: MAXkernel_size: 3stride: 2}
}
layer {name: "fc6"type: "InnerProduct"bottom: "pool5"top: "fc6"param {lr_mult: 1decay_mult: 1}param {lr_mult: 2decay_mult: 0}inner_product_param {num_output: 4096weight_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: 1decay_mult: 1}param {lr_mult: 2decay_mult: 0}inner_product_param {num_output: 4096weight_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: 1decay_mult: 1}param {lr_mult: 2decay_mult: 0}inner_product_param {num_output: 4 #訓(xùn)練的種類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"
}
4.編寫訓(xùn)練腳本
trainSc.bat
cd ../../
"E:/LIB/caffe-windows/build/tools/Release/caffe.exe" train --solver=data/classify/solver.prototxt
pause
點(diǎn)擊運(yùn)行
等待運(yùn)行結(jié)束,在classify會多出兩個(gè)訓(xùn)練好的模型
所有訓(xùn)練完成,之后就是如何測試和使用模型。
七.測試模型
1.修改caffe-windows/data/classify/deploy.prototxt文件,訓(xùn)練是4個(gè)類型的數(shù)據(jù),那么這里要改成4,注意看行數(shù),不要改前面。
2.編寫腳本data_test.bat,運(yùn)行classification.exe,如果報(bào)錯(cuò),手動(dòng)查找classification.exe這個(gè)文件,腳本里改成它所在的位置,運(yùn)行。
E:\LIB\caffe-windows\build\examples\cpp_classification\Release\classification.exe ..\..\data\classifyCPP\deploy.prototxt ..\..\data\classifyCPP\caffenet_train_iter_800.caffemodel ..\..\data\classifyCPP\test_mean.binaryproto ..\..\data\classifyCPP\labels.txt ..\..\data\classifyCPP\test\5136.jpg
pause
3.運(yùn)行結(jié)果,有些特征類似的圖像還是不能很好的判斷,這個(gè)要去更改相關(guān)配置,重新訓(xùn)練。
(1)測試圖像
運(yùn)行結(jié)果:
(2)測試圖像
運(yùn)行結(jié)果:
(3)測試圖像
運(yùn)行結(jié)果:
這個(gè)結(jié)果是判斷錯(cuò)誤的。
后記:
1.以上只是訓(xùn)練成模型的部分,是于如何在win7下編譯caffe的辦法,現(xiàn)在沒有時(shí)間去整理,如果有需要問的可以私信我一起探討。
2.關(guān)于python的幾個(gè)腳本,不熟悉python的,也可用C++實(shí)現(xiàn),C++要使用boost庫讀取文件操作相對簡單一些。
3.之后有時(shí)間會寫caffe的fine tuning和使用opnecv調(diào)用caffe訓(xùn)練好的模型。
4.有興趣討論學(xué)習(xí)可以加群:487350510。
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀
總結(jié)
以上是生活随笔為你收集整理的Windows下Caffe的学习与应用(一)——训练自己的数据模型(GoogleNet)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。