使用LeNet訓練自己的手寫圖片數據


一、前言

本文主要嘗試將自己的數據集制作成lmdb格式,送進lenet作訓練和測試,參考了http://blog.csdn.net/liuweizj12/article/details/52149743http://blog.csdn.net/xiaoxiao_huitailang/article/details/51361036這兩篇博文

二、從訓練模型到使用模型預測圖片分類

(1)自己准備的圖像數據

由於主要是使用lenet模型訓練自己的圖片數據,我的圖像數據共有10個類別,分別是0~9,相應地保存在名為0~9的文件夾,在/homg/您的用戶名/下新建一文件夾char_images,用於保存圖像數據,在/homg/您的用戶名/char_images/下新建兩個文件夾,名字分別為train和val,各自都包含了名為0~9的文件夾,例如文件夾0內存放的是字符”0”的圖像,我的文件夾 如下:

(2)對圖像數據作統一縮放至28*28,並生成txt標簽

 

為了計算均值文件,需要將所有圖片縮放至統一的尺寸,在train和val文件夾所在路徑下創建python文件,命名getPath.py,並寫入以下內容:

 

[python]  view plain  copy
 
  1. #coding:utf-8  
  2.   
  3. import cv2  
  4. import os  
  5.   
  6. def IsSubString( SubStrList , Str):  #判斷SubStrList的元素  
  7.     flag = True                  #是否在Str內  
  8.     for substr in SubStrList:  
  9.         if not ( substr in Str):  
  10.             flag = False            
  11.    
  12.     return flag  
  13.   
  14. def GetFileList(FindPath,FlagStr=[]):  #搜索目錄下的子文件路徑  
  15.     FileList=[]    
  16.     FileNames=os.listdir(FindPath)    
  17.     if len(FileNames)>0:    
  18.         for fn in FileNames:    
  19.             if len(FlagStr)>0:    
  20.                 if IsSubString(FlagStr,fn): #不明白這里判斷是為了啥  
  21.                     fullfilename=os.path.join(FindPath,fn)    
  22.                     FileList.append(fullfilename)    
  23.             else:    
  24.                 fullfilename=os.path.join(FindPath,fn)    
  25.                 FileList.append(fullfilename)    
  26.         
  27.     if len(FileList)>0:    
  28.         FileList.sort()    
  29.             
  30.     return FileList  
  31.   
  32.   
  33. train_txt = open('train.txt' , 'w') #制作標簽數據  
  34. classList =['0','1','2','3','4','5','6','7','8','9']  
  35. for idx in range(len(classList)) :  
  36.     imgfile=GetFileList('train/'+ classList[idx])#將數據集放在與.py文件相同目錄下       
  37.     for img in imgfile:   
  38.         srcImg = cv2.imread( img);  
  39.                 resizedImg = cv2.resize(srcImg , (28,28))  
  40.                 cv2.imwrite( img  ,resizedImg)   
  41.         strTemp=img+' '+classList[idx]+'\n'        #用空格代替轉義字符 \t     
  42.             train_txt.writelines(strTemp)    
  43. train_txt.close()   
  44.   
  45.   
  46. test_txt = open('val.txt' , 'w') #制作標簽數據  
  47. for idx in range(len(classList)) :  
  48.     imgfile=GetFileList('val/'+ classList[idx])  
  49.     for img in imgfile:    
  50.                 srcImg = cv2.imread( img);  
  51.                 resizedImg = cv2.resize(srcImg , (28,28))  
  52.                 cv2.imwrite( img  ,resizedImg)  
  53.         strTemp=img+' '+classList[idx]+'\n'        #用空格代替轉義字符 \t     
  54.             test_txt.writelines(strTemp)    
  55. test_txt.close()  
  56.   
  57. print("成功生成文件列表")  

 

運行該py文件,可將所有圖片縮放至28*28大小,並且在rain和val文件夾所在路徑下生成訓練和測試圖像數據的標簽txt文件,文件內容為:
         

 

(3)生成lmdb格式的數據集

首先於caffe路徑下新建一文件夾My_File,並在My_File下新建兩個文件夾Build_lmdb和Data_label,將(2)中生成文本文件train.txt和val.txt搬至Data_label下

  

將caffe路徑下 examples/imagenet/create_imagenet.sh 復制一份到Build_lmdb文件夾下

打開create_imagenet.sh ,修改內容如下:

 

[python]  view plain  copy
 
  1. #!/usr/bin/env sh  
  2. # Create the imagenet lmdb inputs  
  3. # N.B. set the path to the imagenet train + val data dirs  
  4. set -e  
  5.   
  6. EXAMPLE=My_File/Build_lmdb         #生成的lmdb格式數據保存地址  
  7. DATA=My_File/Data_label                 #兩個txt標簽文件所在路徑  
  8. TOOLS=build/tools                            #caffe自帶工具,不用管  
  9.   
  10. TRAIN_DATA_ROOT=/home/zjy/char_images/    #預先准備的訓練圖片路徑,該路徑和train.txt上寫的路徑合起來是圖片完整路徑  
  11. VAL_DATA_ROOT=/home/zjy/char_images/         #預先准備的測試圖片路徑,...  
  12.   
  13. # Set RESIZE=true to resize the images to 256x256. Leave as false if images have  
  14. # already been resized using another tool.  
  15. RESIZE=false  
  16. if $RESIZE; then  
  17.   RESIZE_HEIGHT=28  
  18.   RESIZE_WIDTH=28  
  19. else  
  20.   RESIZE_HEIGHT=0  
  21.   RESIZE_WIDTH=0  
  22. fi  
  23.   
  24. if [ ! -d "$TRAIN_DATA_ROOT" ]; then  
  25.   echo "Error: TRAIN_DATA_ROOT is not a path to a directory: $TRAIN_DATA_ROOT"  
  26.   echo "Set the TRAIN_DATA_ROOT variable in create_imagenet.sh to the path" \  
  27.        "where the ImageNet training data is stored."  
  28.   exit 1  
  29. fi  
  30.   
  31. if [ ! -d "$VAL_DATA_ROOT" ]; then  
  32.   echo "Error: VAL_DATA_ROOT is not a path to a directory: $VAL_DATA_ROOT"  
  33.   echo "Set the VAL_DATA_ROOT variable in create_imagenet.sh to the path" \  
  34.        "where the ImageNet validation data is stored."  
  35.   exit 1  
  36. fi  
  37.   
  38. echo "Creating train lmdb..."  
  39.   
  40. GLOG_logtostderr=1 $TOOLS/convert_imageset \  
  41.     --resize_height=$RESIZE_HEIGHT \  
  42.     --resize_width=$RESIZE_WIDTH \  
  43.     --shuffle \  
  44.     --gray \        #灰度圖像加上這個  
  45.     $TRAIN_DATA_ROOT \  
  46.     $DATA/train.txt \  
  47.     $EXAMPLE/train_lmdb                   #生成的lmdb格式訓練數據集所在的文件夾  
  48.   
  49. echo "Creating val lmdb..."  
  50.   
  51. GLOG_logtostderr=1 $TOOLS/convert_imageset \  
  52.     --resize_height=$RESIZE_HEIGHT \  
  53.     --resize_width=$RESIZE_WIDTH \  
  54.     --shuffle \  
  55.     --gray \        #灰度圖像加上這個  
  56.     $VAL_DATA_ROOT \  
  57.     $DATA/val.txt \  
  58.     $EXAMPLE/val_lmdb              #生成的lmdb格式訓練數據集所在的文件夾  
  59.   
  60. echo "Done."  

 

 

 

以上只是為了說明修改的地方才添加漢字注釋,實際時sh文件不要出現漢字,運行該sh文件,可在Build_lmdb文件夾內生成2個文件夾train_lmdb和val_lmdb,里面各有2個lmdb格式的文件

 

(4)更改lenet_solver.prototxt和lenet_train_test.prototxt
將caffe/examples/mnist下的 train_lenet.sh 、lenet_solver.prototxt 、lenet_train_test.prototxt 這三個文件復制至 My_File,首先修改train_lenet.sh 如下,只改了solver.prototxt的路徑

 

[python]  view plain  copy
 
  1. #!/usr/bin/env sh  
  2. set -e  
  3.   
  4. ./build/tools/caffe train --solver=My_File/lenet_solver.prototxt $@    #改路徑  

然后再更改lenet_solver.prototxt,如下:

 

[python]  view plain  copy
 
  1. # The train/test net protocol buffer definition  
  2. net: "My_File/lenet_train_test.prototxt"            #改這里  
  3. # test_iter specifies how many forward passes the test should carry out.  
  4. # In the case of MNIST, we have test batch size 100 and 100 test iterations,  
  5. # covering the full 10,000 testing images.  
  6. test_iter: 100  
  7. # Carry out testing every 500 training iterations.  
  8. test_interval: 500  
  9. # The base learning rate, momentum and the weight decay of the network.  
  10. base_lr: 0.01  
  11. momentum: 0.9  
  12. weight_decay: 0.0005  
  13. # The learning rate policy  
  14. lr_policy: "inv"  
  15. gamma: 0.0001  
  16. power: 0.75  
  17. # Display every 100 iterations  
  18. display: 100  
  19. # The maximum number of iterations  
  20. max_iter: 10000  
  21. # snapshot intermediate results  
  22. snapshot: 5000  
  23. snapshot_prefix: "My_File/"         #改這里  
  24. # solver mode: CPU or GPU  
  25. solver_mode: GPU  

 

最后修改lenet_train_test.prototxt ,如下:

 

[python]  view plain  copy
 
  1. name: "LeNet"  
  2. layer {  
  3.   name: "mnist"  
  4.   type: "Data"  
  5.   top: "data"  
  6.   top: "label"  
  7.   include {  
  8.     phase: TRAIN  
  9.   }  
  10.   transform_param {  
  11.     scale: 0.00390625  
  12.   }  
  13.   data_param {  
  14.     source: "My_File/Build_lmdb/train_lmdb"       #改成自己的  
  15.     batch_size: 64  
  16.     backend: LMDB  
  17.   }  
  18. }  
  19. layer {  
  20.   name: "mnist"  
  21.   type: "Data"  
  22.   top: "data"  
  23.   top: "label"  
  24.   include {  
  25.     phase: TEST  
  26.   }  
  27.   transform_param {  
  28.     scale: 0.00390625  
  29.   }  
  30.   data_param {  
  31.     source: "My_File/Build_lmdb/val_lmdb"        #改成自己的  
  32.     batch_size: 100  
  33.     backend: LMDB  
  34.   }  
  35. }  
  36. layer {  
  37.   name: "conv1"  
  38.   type: "Convolution"  
  39.   bottom: "data"  
  40.   top: "conv1"  
  41.   param {  
  42.     lr_mult: 1  
  43.   }  
  44.   param {  
  45.     lr_mult: 2  
  46.   }  
  47.   convolution_param {  
  48.     num_output: 20  
  49.     kernel_size: 5  
  50.     stride: 1  
  51.     weight_filler {  
  52.       type: "xavier"  
  53.     }  
  54.     bias_filler {  
  55.       type: "constant"  
  56.     }  
  57.   }  
  58. }  
  59. layer {  
  60.   name: "pool1"  
  61.   type: "Pooling"  
  62.   bottom: "conv1"  
  63.   top: "pool1"  
  64.   pooling_param {  
  65.     pool: MAX  
  66.     kernel_size: 2  
  67.     stride: 2  
  68.   }  
  69. }  
  70. layer {  
  71.   name: "conv2"  
  72.   type: "Convolution"  
  73.   bottom: "pool1"  
  74.   top: "conv2"  
  75.   param {  
  76.     lr_mult: 1  
  77.   }  
  78.   param {  
  79.     lr_mult: 2  
  80.   }  
  81.   convolution_param {  
  82.     num_output: 50  
  83.     kernel_size: 5  
  84.     stride: 1  
  85.     weight_filler {  
  86.       type: "xavier"  
  87.     }  
  88.     bias_filler {  
  89.       type: "constant"  
  90.     }  
  91.   }  
  92. }  
  93. layer {  
  94.   name: "pool2"  
  95.   type: "Pooling"  
  96.   bottom: "conv2"  
  97.   top: "pool2"  
  98.   pooling_param {  
  99.     pool: MAX  
  100.     kernel_size: 2  
  101.     stride: 2  
  102.   }  
  103. }  
  104. layer {  
  105.   name: "ip1"  
  106.   type: "InnerProduct"  
  107.   bottom: "pool2"  
  108.   top: "ip1"  
  109.   param {  
  110.     lr_mult: 1  
  111.   }  
  112.   param {  
  113.     lr_mult: 2  
  114.   }  
  115.   inner_product_param {  
  116.     num_output: 500  
  117.     weight_filler {  
  118.       type: "xavier"  
  119.     }  
  120.     bias_filler {  
  121.       type: "constant"  
  122.     }  
  123.   }  
  124. }  
  125. layer {  
  126.   name: "relu1"  
  127.   type: "ReLU"  
  128.   bottom: "ip1"  
  129.   top: "ip1"  
  130. }  
  131. layer {  
  132.   name: "ip2"  
  133.   type: "InnerProduct"  
  134.   bottom: "ip1"  
  135.   top: "ip2"  
  136.   param {  
  137.     lr_mult: 1  
  138.   }  
  139.   param {  
  140.     lr_mult: 2  
  141.   }  
  142.   inner_product_param {  
  143.     num_output: 10  
  144.     weight_filler {  
  145.       type: "xavier"  
  146.     }  
  147.     bias_filler {  
  148.       type: "constant"  
  149.     }  
  150.   }  
  151. }  
  152. layer {  
  153.   name: "accuracy"  
  154.   type: "Accuracy"  
  155.   bottom: "ip2"  
  156.   bottom: "label"  
  157.   top: "accuracy"  
  158.   include {  
  159.     phase: TEST  
  160.   }  
  161. }  
  162. layer {  
  163.   name: "loss"  
  164.   type: "SoftmaxWithLoss"  
  165.   bottom: "ip2"  
  166.   bottom: "label"  
  167.   top: "loss"  
  168. }  

 

運行 My_File/train_lenet.sh ,得到最后的訓練結果,在My_File下生成訓練的caffemodel和solverstate。

 

(5)生成均值文件
均值文件主要用於圖像預測的時候,由caffe/build/tools/compute_image_mean生成,在My_File文件夾下新建一文件夾Mean,用於存放均值文件,在caffe/下執行:
build/tools/compute_image_mean My_File/Build_lmdb/train_lmdb My_File/Mean/mean.binaryproto
可在My_File/Mean/下生成均值文件mean.binaryproto 
(6)生成deploy.prototxt
deploy.prototxt是在lenet_train_test.prototxt的基礎上刪除了開頭的Train和Test部分以及結尾的Accuracy、SoftmaxWithLoss層,並在開始時增加了一個data層描述,結尾增加softmax層,可以參照博文http://blog.csdn.net/lanxuecc/article/details/52474476 使用python生成,也可以直接由train_val.prototxt上做修改,在My_File文件夾下新建一文件夾Deploy,將 lenet_train_test.prototxt復制至文件夾Deploy下,並重命名為deploy.prototxt ,修改里面的內容如下:

 

[python]  view plain  copy
 
  1. name: "LeNet"  
  2. layer {                   #刪去原來的Train和Test部分,增加一個data層  
  3.   name: "data"  
  4.   type: "Input"  
  5.   top: "data"  
  6.   input_param { shape: { dim: 1 dim: 1 dim: 28 dim: 28 } }  
  7. }  
  8. layer {  
  9.   name: "conv1"  
  10.   type: "Convolution"  
  11.   bottom: "data"  
  12.   top: "conv1"  
  13.   param {  
  14.     lr_mult: 1  
  15.   }  
  16.   param {  
  17.     lr_mult: 2  
  18.   }  
  19.   convolution_param {  
  20.     num_output: 20  
  21.     kernel_size: 5  
  22.     stride: 1  
  23.     weight_filler {  
  24.       type: "xavier"  
  25.     }  
  26.     bias_filler {  
  27.       type: "constant"  
  28.     }  
  29.   }  
  30. }  
  31. layer {  
  32.   name: "pool1"  
  33.   type: "Pooling"  
  34.   bottom: "conv1"  
  35.   top: "pool1"  
  36.   pooling_param {  
  37.     pool: MAX  
  38.     kernel_size: 2  
  39.     stride: 2  
  40.   }  
  41. }  
  42. layer {  
  43.   name: "conv2"  
  44.   type: "Convolution"  
  45.   bottom: "pool1"  
  46.   top: "conv2"  
  47.   param {  
  48.     lr_mult: 1  
  49.   }  
  50.   param {  
  51.     lr_mult: 2  
  52.   }  
  53.   convolution_param {  
  54.     num_output: 50  
  55.     kernel_size: 5  
  56.     stride: 1  
  57.     weight_filler {  
  58.       type: "xavier"  
  59.     }  
  60.     bias_filler {  
  61.       type: "constant"  
  62.     }  
  63.   }  
  64. }  
  65. layer {  
  66.   name: "pool2"  
  67.   type: "Pooling"  
  68.   bottom: "conv2"  
  69.   top: "pool2"  
  70.   pooling_param {  
  71.     pool: MAX  
  72.     kernel_size: 2  
  73.     stride: 2  
  74.   }  
  75. }  
  76. layer {  
  77.   name: "ip1"  
  78.   type: "InnerProduct"  
  79.   bottom: "pool2"  
  80.   top: "ip1"  
  81.   param {  
  82.     lr_mult: 1  
  83.   }  
  84.   param {  
  85.     lr_mult: 2  
  86.   }  
  87.   inner_product_param {  
  88.     num_output: 500  
  89.     weight_filler {  
  90.       type: "xavier"  
  91.     }  
  92.     bias_filler {  
  93.       type: "constant"  
  94.     }  
  95.   }  
  96. }  
  97. layer {  
  98.   name: "relu1"  
  99.   type: "ReLU"  
  100.   bottom: "ip1"  
  101.   top: "ip1"  
  102. }  
  103. layer {  
  104.   name: "ip2"  
  105.   type: "InnerProduct"  
  106.   bottom: "ip1"  
  107.   top: "ip2"  
  108.   param {  
  109.     lr_mult: 1  
  110.   }  
  111.   param {  
  112.     lr_mult: 2  
  113.   }  
  114.   inner_product_param {  
  115.     num_output: 10  
  116.     weight_filler {  
  117.       type: "xavier"  
  118.     }  
  119.     bias_filler {  
  120.       type: "constant"  
  121.     }  
  122.   }  
  123. }  
  124. layer {                   #增加softmax層  
  125.   name: "prob"  
  126.   type: "Softmax"  
  127.   bottom: "ip2"  
  128.   top: "prob"  
  129. }  

 

(7)預測圖片
在My_File文件夾下創建一文件夾Pic,用於存放測試的圖片;在My_File文件夾下創建另一文件夾Synset,在其中新建synset_words.txt文件,之后在里面輸入:
0
1
2
3
4
5
6
7
8
9

 

看看My_File文件夾都有啥了

 

最后使用caffe/build/examples/cpp_classification/classification.bin對圖片作預測,在終端輸入:

三、結束語

真是篇又臭又長的博文,高手自行忽略,剛剛入門的可以看看!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM