用自己的圖片訓練和預測LeNet模型
學習過mnist手寫數字識別的例程之後,想用自己的字元圖片進行訓練和預測。整個過程分為:1、收集影象資料;2、轉換資料格式;3、訓練網路;4、生成均值檔案;5、修改網路模型;6、預測圖片的類別。
1、收集影象資料
由於主要是使用lenet模型訓練自己的圖片資料,我的影象資料共有10個類別,分別是0~9。
- 在路徑/home/liu/caffe/data/下新建資料夾,命名為cheh_data,用於儲存影象資料。
- 在路徑/home/liu/caffe/data/cheh_data/下新建資料夾,命名為train,用於儲存訓練資料,裡邊有10個資料夾,命名為0~9,分別存放字元‘0’~‘9’的字元樣本;
- 在路徑/home/liu/caffe/data/cheh_data/
- 對所有樣本進行縮放處理並生成txt格式的類別標籤檔案:在路徑/home/liu/caffe/data/cheh_data/下建立python檔案,命名為get_path.py,並寫入以下內容:
# coding:utf-8
import cv2 import os
def IsSubString(SubStrList, Str): flag = True for substr in SubStrList: if not (substr in Str): flag = False
return flag
def GetFileList(FindPath, FlagStr=[]): FileList = [] FileNames = os.listdir(FindPath) if len(FileNames) > 0: for fn in FileNames: if len(FlagStr) > 0: if IsSubString(FlagStr, fn): fullfilename = os.path.join(FindPath, fn) FileList.append(fullfilename) else: fullfilename = os.path.join(FindPath, fn) FileList.append(fullfilename)
if len(FileList) > 0: FileList.sort()
return FileList
train_txt = open('train.txt', 'w')classList = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] for idx in range(len(classList)): imgfile = GetFileList('train/' + classList[idx]) for img in imgfile: srcImg = cv2.imread(img) resizedImg = cv2.resize(srcImg, (28, 28)) cv2.imwrite(img, resizedImg) strTemp = img + ' ' + classList[idx] + '\n' train_txt.writelines(strTemp) train_txt.close()
test_txt = open('val.txt', 'w') for idx in range(len(classList)): imgfile = GetFileList('val/' + classList[idx]) for img in imgfile: srcImg = cv2.imread(img) resizedImg = cv2.resize(srcImg, (28, 28)) cv2.imwrite(img, resizedImg) strTemp = img + ' ' + classList[idx] + '\n' test_txt.writelines(strTemp) test_txt.close()
print("success!")
注意:在語句 strTemp = img + ' ' + classList[idx] + '\n' 中用空格代替'\t'
在終端中將路徑切換到py檔案所在路徑並執行命令python get_path.py以執行該檔案,可將所有圖片縮放至28*28大小並得到train.txt和val.txt
2、轉換資料格式
首先,在路徑/home/liu/caffe/examples/下新建資料夾並命名為cheh,並在路徑/home/liu/caffe/examples/cheh/下新建一個資料夾命名為cheh_lmdb,用於存放生成的lmdb檔案。
其次,將caffe路徑下 examples/imagenet/create_imagenet.sh 複製一份到路徑/home/liu/caffe/examples/cheh/下,並修改如下:
#!/usr/bin/env sh # Create the imagenet lmdb inputs # N.B. set the path to the imagenet train + val data dirs set -e
EXAMPLE=/home/liu/caffe/examples/cheh/cheh_lmdb DATA=/home/liu/caffe/data/cheh_data TOOLS=/home/liu/caffe/build/tools
TRAIN_DATA_ROOT=/home/liu/caffe/data/cheh_data/ VAL_DATA_ROOT=/home/liu/caffe/data/cheh_data/
# Set RESIZE=true to resize the images to 256x256. Leave as false if images have # already been resized using another tool. RESIZE=false if $RESIZE; then RESIZE_HEIGHT=28 RESIZE_WIDTH=28 else RESIZE_HEIGHT=0 RESIZE_WIDTH=0 fi
if [ ! -d "$TRAIN_DATA_ROOT" ]; then echo "Error: TRAIN_DATA_ROOT is not a path to a directory: $TRAIN_DATA_ROOT" echo "Set the TRAIN_DATA_ROOT variable in create_imagenet.sh to the path" \ "where the ImageNet training data is stored." exit 1 fi
if [ ! -d "$VAL_DATA_ROOT" ]; then echo "Error: VAL_DATA_ROOT is not a path to a directory: $VAL_DATA_ROOT" echo "Set the VAL_DATA_ROOT variable in create_imagenet.sh to the path" \ "where the ImageNet validation data is stored." exit 1 fi
echo "Creating train lmdb..."
GLOG_logtostderr=1 $TOOLS/convert_imageset \ --resize_height=$RESIZE_HEIGHT \ --resize_width=$RESIZE_WIDTH \ --shuffle \ --gray \ $TRAIN_DATA_ROOT \ $DATA/train.txt \ $EXAMPLE/cheh_train_lmdb
echo "Creating val lmdb..."
GLOG_logtostderr=1 $TOOLS/convert_imageset \ --resize_height=$RESIZE_HEIGHT \ --resize_width=$RESIZE_WIDTH \ --shuffle \ --gray \ $VAL_DATA_ROOT \ $DATA/val.txt \ $EXAMPLE/cheh_val_lmdb
echo "Done."
注意:需要將所有的路徑都對應正確,需要設定樣本的尺寸,如果不是拷貝caffe下的create_imagenet.sh 檔案進行修改,可能會發生錯誤,注意python程式碼的縮排,要麼使用4個空格要麼使用\t,要保持一致。此外為防止發生錯誤,可以將所有路徑補全,不再使用相對路徑。
再次,在路徑/home/liu/caffe/examples/cheh/下建立資料夾cheh_lmdb
最後,執行指令sh examples/cheh/create_imagenet.sh後,在路徑/home/liu/caffe/examples/cheh/cheh_lmdb/下生成cheh_train_lmdb和cheh_val_lmdb兩個子資料夾,在每個資料夾裡都有data.mdb和lock.mdb兩個檔案
3、訓練網路
將caffe/examples/mnist下的 train_lenet.sh 、lenet_solver.prototxt 、lenet_train_test.prototxt 這三個檔案複製到路徑/home/liu/caffe/examples/cheh/下,
首先,修改train_lenet.sh :
#!/usr/bin/env sh set -e
./build/tools/caffe train --solver=examples/cheh/lenet_solver.prototxt [email protected]
然後,再更改lenet_solver.prototxt :
# The train/test net protocol buffer definition net: "examples/cheh/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 # Carry out testing every 500 training iterations. test_interval: 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 # Display every 100 iterations display: 100 # The maximum number of iterations max_iter: 10000 # snapshot intermediate results snapshot: 5000 snapshot_prefix: "examples/cheh/" # solver mode: CPU or GPU solver_mode: CPU
再然後,修改lenet_train_test.prototxt
name: "LeNet" layer { name: "mnist" type: "Data" top: "data" top: "label" include { phase: TRAIN } transform_param { scale: 0.00390625 } data_param { source: "examples/cheh/cheh_lmdb/cheh_train_lmdb" batch_size: 64 backend: LMDB } } layer { name: "mnist" type: "Data" top: "data" top: "label" include { phase: TEST } transform_param { scale: 0.00390625 } data_param { source: "examples/cheh/cheh_lmdb/cheh_val_lmdb" batch_size: 100 backend: LMDB } } layer { name: "conv1" type: "Convolution" bottom: "data" top: "conv1" param { lr_mult: 1 } param { lr_mult: 2 } convolution_param { num_output: 20 kernel_size: 5 stride: 1 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { name: "pool1" type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { pool: MAX kernel_size: 2 stride: 2 } } layer { name: "conv2" type: "Convolution" bottom: "pool1" top: "conv2" param { lr_mult: 1 } param { lr_mult: 2 } convolution_param { num_output: 50 kernel_size: 5 stride: 1 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { name: "pool2" type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { pool: MAX kernel_size: 2 stride: 2 } } layer { name: "ip1" type: "InnerProduct" bottom: "pool2" top: "ip1" param { lr_mult: 1 } param { lr_mult: 2 } inner_product_param { num_output: 500 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { name: "relu1" type: "ReLU" bottom: "ip1" top: "ip1" } layer { name: "ip2" type: "InnerProduct" bottom: "ip1" top: "ip2" param { lr_mult: 1 } param { lr_mult: 2 } inner_product_param { num_output: 10 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { name: "accuracy" type: "Accuracy" bottom: "ip2" bottom: "label" top: "accuracy" include { phase: TEST } } layer { name: "loss" type: "SoftmaxWithLoss" bottom: "ip2" bottom: "label" top: "loss" }
執行命令sh examples/cheh/train_lenet.sh得到最後的訓練結果,在/home/liu/caffe/examples/cheh/下生成訓練的caffemodel和solverstate。
注意:如果遇到以下問題請提升許可權:sudo sh examples/cheh/train_lenet.sh
F0919 15:49:21.687918 16837 io.cpp:69] Check failed: proto.SerializeToOstream(&output)
*** Check failure stack trace: ***
@ 0x7f12912575cd google::LogMessage::Fail()
@ 0x7f1291259433 google::LogMessage::SendToLog()
@ 0x7f129125715b google::LogMessage::Flush()
@ 0x7f1291259e1e google::LogMessageFatal::~LogMessageFatal()
@ 0x7f1291795835 caffe::WriteProtoToBinaryFile()
@ 0x7f129171fa06 caffe::Solver<>::SnapshotToBinaryProto()
@ 0x7f129171fb0a caffe::Solver<>::Snapshot()
@ 0x7f129172352e caffe::Solver<>::Step()
@ 0x7f1291723f7a caffe::Solver<>::Solve()
@ 0x40a064 train()
@ 0x406fa0 main
@ 0x7f12901c7830 __libc_start_main
@ 0x4077c9 _start
@ (nil) (unknown)
Aborted (core dumped)
訓練完成後:
4、生成均值檔案
均值檔案主要用於影象預測的時候,由caffe/build/tools/compute_image_mean生成,在caffe/下執行命令
build/tools/compute_image_mean \/home/liu/caffe/examples/cheh/cheh_lmdb/cheh_train_lmdb /home/liu/caffe/examples/cheh/mean.binaryproto
可在/home/liu/caffe/examples/cheh/下生成均值檔案mean.binaryproto
5、修改網路模型
deploy.prototxt是在lenet_train_test.prototxt的基礎上刪除了開頭的Train和Test部分以及結尾的Accuracy、SoftmaxWithLoss層,並在開始時增加了一個data層描述,結尾增加softmax層,可以參照博文http://blog.csdn.net/lanxuecc/article/details/52474476 使用python生成,也可以直接由train_val.prototxt上做修改,將 lenet_train_test.prototxt複製到/home/liu/caffe/examples/cheh/下,並重命名為deploy.prototxt ,修改裡面的內容如下:
name: "LeNet"layer { name: "data" type: "Input" top: "data" input_param { shape: { dim: 1 dim: 1 dim: 28 dim: 28 } } } layer { name: "conv1" type: "Convolution" bottom: "data" top: "conv1" param { lr_mult: 1 } param { lr_mult: 2 } convolution_param { num_output: 20 kernel_size: 5 stride: 1 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { name: "pool1" type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { pool: MAX kernel_size: 2 stride: 2 } } layer { name: "conv2" type: "Convolution" bottom: "pool1" top: "conv2" param { lr_mult: 1 } param { lr_mult: 2 } convolution_param { num_output: 50 kernel_size: 5 stride: 1 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { name: "pool2" type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { pool: MAX kernel_size: 2 stride: 2 } } layer { name: "ip1" type: "InnerProduct" bottom: "pool2" top: "ip1" param { lr_mult: 1 } param { lr_mult: 2 } inner_product_param { num_output: 500 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { name: "relu1" type: "ReLU" bottom: "ip1" top: "ip1" } layer { name: "ip2" type: "InnerProduct" bottom: "ip1" top: "ip2" param { lr_mult: 1 } param { lr_mult: 2 } inner_product_param { num_output: 10 weight_filler { type: "xavier" } bias_filler { type: "constant" } } }layer { name: "prob" type: "Softmax" bottom: "ip2" top: "prob" }
6、預測圖片
首先,在路徑/home/liu/caffe/data/cheh_data/下建立一資料夾並命名為test,用於存放測試的圖片;
然後,在/home/liu/caffe/examples/cheh/下新建synset_words.txt檔案,之後在裡面輸入: 0 1 2 3 4 5 6 7 8 9
此時,cheh資料夾的狀態:
最後,執行以下命令對圖片作預測
./build/examples/cpp_classification/classification.bin \ /home/liu/caffe/examples/cheh/deploy.prototxt \ /home/liu/caffe/examples/cheh/lenet_solver_iter_10000.caffemodel \ /home/liu/caffe/examples/cheh/mean.binaryproto \ /home/liu/caffe/examples/cheh/synset_words.txt \ /home/liu/caffe/data/cheh_data/1.jpg
結果如下:
入門小白所做,高手可自行忽略。