1. 程式人生 > >用自己的圖片訓練和預測LeNet模型

用自己的圖片訓練和預測LeNet模型

學習過mnist手寫數字識別的例程之後,想用自己的字元圖片進行訓練和預測。整個過程分為:1、收集影象資料;2、轉換資料格式;3、訓練網路;4、生成均值檔案;5、修改網路模型;6、預測圖片的類別。

1、收集影象資料

由於主要是使用lenet模型訓練自己的圖片資料,我的影象資料共有10個類別,分別是0~9。

  1. 在路徑/home/liu/caffe/data/下新建資料夾,命名為cheh_data,用於儲存影象資料。
  2. 在路徑/home/liu/caffe/data/cheh_data/下新建資料夾,命名為train,用於儲存訓練資料,裡邊有10個資料夾,命名為0~9,分別存放字元‘0’~‘9’的字元樣本;
  3. 在路徑/home/liu/caffe/data/cheh_data/
    下新建資料夾,命名為val,用於儲存驗證資料,裡邊有10個資料夾,命名為0~9,分別存放字元‘0’~‘9’的字元樣本;
  4. 對所有樣本進行縮放處理並生成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.txtval.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 

結果如下:

入門小白所做,高手可自行忽略。