cafe-ssd數據集訓練

 

 

訓練方式::https://blog.csdn.net/xiao_lxl/article/details/79106837

caffe-ssd訓練本身的數據集

https://blog.csdn.net/lukaslong/article/details/81509855css

錯誤::

SSD(Single Shot MultiBox Detector):create_list.sh io.cpp:187 Could not open or find file

https://blog.csdn.net/10km/article/details/70144925

 

 https://www.jianshu.com/p/ef5509af1af3html

這大https://blog.csdn.net/u010167269/article/details/52851667python

 

 

第一部分,訓練VOC數據集ios

1. 預訓練模型c++

caffe-ssd做者提供了預訓練模型,下載地址https://gist.github.com/weiliu89/2ed6e13bfd5b57cf81d6git

下載完成後保存在 caffe/models/VGGNet 中github

2. 下載VOC2007和VOC2012數據集:shell

在home根目錄下建立文件夾data,即/home/×××/data/數據庫

cd data

wget http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar
wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtrainval_06-Nov-2007.tar
wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtest_06-Nov-2007.tar

tar -xvf VOCtrainval_11-May-2012.tar
tar -xvf VOCtrainval_06-Nov-2007.tar
tar -xvf VOCtest_06-Nov-2007.tar
若是終端下載速度太慢,也能夠到相應網站自行下載,而後放到data/文件夾內,進行解壓。bash

能夠獲得home/.../data/VOCdevkit/文件夾,包含VOC07十二、VOC200七、VOC2012三個子文件夾

3. 建立lmdb文件

VOC的數據及標註是不能直接拿來訓練的,先要進行格式轉換,建立train和test的lmdb文件:

cd caffe

# Create the trainval.txt, test.txt, and test_name_size.txt in data/VOC0712/

./data/VOC0712/create_list.sh

# You can modify the parameters in create_data.sh if needed.
# It will create lmdb files for trainval and test with encoded original image:
# - $HOME/data/VOCdevkit/VOC0712/lmdb/VOC0712_trainval_lmdb
# - $HOME/data/VOCdevkit/VOC0712/lmdb/VOC0712_test_lmdb
# and make soft links at examples/VOC0712/

./data/VOC0712/create_data.sh
4. 開始訓練

python examples/ssd/ssd_pascal.py
該訓練文件中默認使用的是4塊GPU,若是隻用一塊GPU,則須要打開文件修改

gpus = "0,1,2,3"
修改成:

gpus = "0"
 

第二部分:訓練本身的數據集

其實存在兩種方案, 第一:保持原來的文件目錄結構及文件名不變, 只替換裏面的數據。第二:從新新建一個與以前相似的目錄結構,改爲本身命名的文件夾,須要修改程序裏涉及數據路徑的代碼,可是方法更重要,所以這裏採用第二種方案。

1. 準備工做

1.1 建立文件夾

cd caffe/data/
mkdir mydataset
把caffe/data/VOC0712/目錄下create_list.sh 、create_data.sh、labelmap_voc.prototxt 這三個文件拷到caffe/data/mydataset/下,可手動複製也能夠經過命令進行:

cp VOC0712/create* ./mydataset
cp VOC0712/label* ./mydataset
1.2 修改label類別

labelmap_voc.prototxt文件定義了檢測中的label,打開進行標籤的設置,label0要保留,從label1開始修改本身要檢測的label名稱,刪除用不着的。注意這裏的name必定要和xml中的標註徹底相同。

item {
name: "none_of_the_above"
label: 0
display_name: "background"
}
item {
name: "aeroplane"
label: 1
display_name: "person"
}
...
1.3 數據集準備

在/data/VOCdevkit目錄下建立mydataset,而後:

cd home/.../data/VOCdevkit
mkdir mydataset
cd mydataset
mkdir Annotations
mkdir ImageSets
mkdir JPEGImages
cd ImageSets
mkdir Layout
mkdir Main
mkdir Segmentation
① 將標註文件所有放入Annotations文件夾(博主用的label_image標註的xml文件)

② 將與標註文件一一對應的jpg圖像文件所有放入JPEG文件夾

③ ImageSets/Main/文件夾中放入train.txt, val.txt, trainval.txt, test.txt四個文件,txt文件內容爲圖片的文件名(不帶後綴),

train.txt——用於訓練的圖片文件的文件名列表;

val.txt——訓練過程用來驗證的圖片文件的文件名列表,通常保持和train中圖片數量相近;

trianval.txt——用來訓練和驗證的圖片文件的文件名列表,數量爲train和val中圖片數量之和;

test.txt——用來測試已訓練的模型的文件名列表,數量通常和trainval中圖片數量相近。

關於如何生成以上四個文件見文章最後第三部分

④ 在/examples下建立mydataset文件夾,存放後續生成的lmdb文件

cd example/
mkdir mydataset
1.4 生成lmdb文件

上述文件夾建立好後, 開始生成lmdb文件, 在建立以前須要修改相關路徑:

打開create_list.sh,把「for name in VOC2007 VOC2012",修改成」for name in mydataset「

 

打開create_data.sh,把dataset_name的值修改成mydataset

 

1.5 開始訓練

打開example/ssd/ssd_pascal.py進行修改:

# 82行:train_data路徑; Created by data/VOC0712/create_data.sh
train_data = "examples/mydataset/mydataset_test_lmdb"

# 84行:test_data路徑;Created by data/VOC0712/create_data.sh
test_data = "examples/mydataset/mydataset_trainval_lmdb"

237-246行:model_name、save_dir、snapshot_dir、job_dir、output_result_dir路徑;

259-263行:name_size_file、label_map_file路徑;

266行:num_classes修改成1 + 類別數,本文中爲3+1=4;

344行:gpus = "0,1,2,3"根據本身需求修改,博主只有一塊GPU即gpus = "0"

360行:num_test_image:測試集圖片數目
374行:max_iter:最大迭代次數
修改後的文件見:

修改完成後,運行

python ./examples/ssd/ssd_pascal.py
 
---------------------
做者:lukaslong
來源:CSDN
原文:https://blog.csdn.net/lukaslong/article/details/81509855
版權聲明:本文爲博主原創文章,轉載請附上博文連接!

  D 單張圖片測試,python版本

    點開ssd_detect.ipynb,複製並保存爲ssd_detect.py,而後修改裏面的路徑(包括$caffe_root和測試圖片的路徑),並在最後加上plt.show()

    而後命令行運行該代碼便可

 
 

   2、訓練並測試本身的數據

    1.生成訓練和測試數據

     咱們本身的數據基本是jpeg或者其餘圖片格式的,而caffe輸入的通常是LMDB的數據,因此咱們要進行轉換。咱們轉換的方法是

     A.將圖像用工具進行標註(工具這裏先不介紹),獲得txt標註文件

     B.將txt文件和圖片轉換成VOC格式(用腳本)

     C.將VOC格式轉換爲LMDB格式,利用SSD示例代碼提供的轉換腳本。

 (1) 在 $caffe_root/data/VOCdevkit目錄下建立ljy_test目錄,該目錄中存放本身轉換完成的VOC數據集;
(2) $CAFFE_ROOT/examples目錄下建立ljy_test目錄;
(3) $CAFFE_ROOT/data目錄下建立ljy_test目錄,同時將data/VOC0712下的create_list.sh,create_data.sh,labelmap_voc.prototxt
這三個文件copy到ljy_test目錄下,分別重命名爲create_list_ljy_test.sh,create_data_ljy_test.sh, labelmap_ljy_test.prototxt
(4) 對上面新生成的兩個create文件進行修改,主要修改是將VOC0712相關的信息替換成ljy_test
修改後的兩個文件分別以下:


而後修改
labelmap_indoor.prototxt,將該文件中的類別修改爲和本身的數據集相匹配,注意須要保留一個label 0 , background類別

      完成上面步驟的修改後,能夠開始LMDB數據數據的製做,在$CAFFE_ROOT目錄下分別運行:

  ./data/ljy_test/create_list_indoor.sh

  ./data/ljy_test/create_data_indoor.sh

  命令執行完畢後,能夠在$CAFFE_ROOT/examples/ljy_test目錄下查看轉換完成的LMDB數據數據。

   2.訓練

  A.將預訓練好的模型放在$CAFFE_ROOT/models/VGGNet下(咱們這裏在運行SSD示例代碼的4已經放過了,能夠省略)

 B.將$caffe_root/examples/ssd/ssd_pascal.py拷貝到本身的文件夾$caffe_root/examples/ljy_test/下,並重命名爲ssd_pascal_ljy.py

 C.修改ssd_pascal_ljy.py爲本身的各個路徑,其中要在$caffe_root/models/VGGNet/下創建ljy_test文件夾,修改以下:

  

   

   

   

  D.執行訓練代碼。在$caffe_root下打開命令行,並輸入

python examples/ljy_test/ssd_pascal_ljy.py
等待訓練就能夠了...
有可能遇到loss=nan的狀況,這個待議,正常狀況下是下面醬紫的:

  3.測試

  A.c++版本的測試

   跟上面ssd示例測試的差很少,改一下路徑便可

  B.python版本的測試

  同最上面

  4.參考:http://blog.csdn.net/u014696921/article/details/53353896,https://github.com/weiliu89/caffe.git

 

 

 

如何從頭至尾的用caffe-ssd訓練本身的數據集並進行目標識別

caffe-ssd是一種很是適合新手的end to end 目標識別框架。也是我在學習了深度學習和目標識別理論之後第一個上手跑的程序。具體步驟以下:

 

 

一 SSD的安裝

  • 下載caffe,若是沒有配置過能夠參考:https://blog.csdn.net/baobei0112/article/details/77996369
  • 在home目錄下,獲取SSD的代碼,下載完成後有一個caffe文件夾
    1.  
      1 git clone https://github.com/weiliu89/caffe.git
    2.  
      2 cd caffe
    3.  
      3 git checkout ssd(出現「分支」則說明copy-check成功)

 

  • 進入下載好的caffe目錄,複製配置文件
  1.  
    1 cd /home/usrname/caffe
  2.  
    2 cp Makefile.config.example Makefile.config
3     而後修改Makefile.config,照以前caffe文件下修改便可
  • 編譯caffe

1 make all -j4

2 make test

3 make runtest

 

  • 編譯Python wrapper

make pycaffe

不報錯說明成功。

 

二 數據集的準備

  • 準備好標註好的圖片數據
  • 我是用labelImage(使用方法自行百度)來標註圖片,標註好的圖片會有與其名稱對應的xml文件保存識別框位置,具體以下:
  • 在data文件夾下新建本身訓練數據的VOC格式文件(VOC_knife):
  • 在voc文件夾下新建4個文件夾:
  • 將標註好的全部xml文件放入Annotations中,將全部圖片放入PNGImages中。
  • 在PNGImages新建兩個文件夾trainval和test,將PNGImages中的圖片按4:1(具體多少不必定,我是這麼分的)放入trainval和test中(也就是說全部圖片在PNGImages中有,在trainval和test中還有一份,如此是爲了方便接下來腳本方便生成數據集):
  • 在ImageSets中應有以下4個txt文檔:
  • 首先是labelmap.prototxt按實際項目寫成以下格式,如有多個分類依次往下手動編寫便可,須要注意的是都須要編寫背景類0:
  • 其次是trainval和test兩個txt文本,用以下腳本編寫便可,只需改變腳本中相應圖片與xml文件的路徑即可生成:
  • #! /usr/bin/python  
    # -*- coding:UTF-8 -*-  
      
    import os, sys  
    import glob  
    #訓練集和測試集路徑 
    trainval_dir = "/home/u809-1/caffe-ssd-clh/data/VOC_knife/PNGImages/trainval"  
    test_dir = "/home/u809-1/caffe-ssd-clh/data/VOC_knife/PNGImages/test"  
      
    trainval_img_lists = glob.glob(trainval_dir + '/*.png')    #獲取trainval中全部.png的文件 
    trainval_img_names = []       #獲取名稱  
    for item in trainval_img_lists:  
        temp1, temp2 = os.path.splitext(os.path.basename(item))  
        trainval_img_names.append(temp1)  
      
    test_img_lists = glob.glob(test_dir + '/*.png')   #獲取test中全部.png文件  
    test_img_names = []  
    for item in test_img_lists:  
        temp1, temp2 = os.path.splitext(os.path.basename(item))  
        test_img_names.append(temp1)  
    #圖片路徑和xml路徑  
    dist_img_dir = "data/VOC_knife/PNGImages"  #須要寫入txt的trainval和test路徑,由於咱們在PNGImges目錄下除了有trainval和test文件夾外還有全部圖片,因此只用寫到PNGImages  
    dist_anno_dir = "/data/VOC_knife/Annotations"  #須要寫入的xml路徑    !!!從caffe跟目錄下第一個文件開始寫
      
    trainval_fd = open("/home/u809-1/caffe-ssd-clh/data/VOC_knife/ImageSets/trainval.txt", 'w')  #存到哪裏,及存儲的名稱
    test_fd = open("/home/u809-1/caffe-ssd-clh/data/VOC_knife/ImageSets/test.txt", 'w')  
       
    for item in trainval_img_names:  
        trainval_fd.write(dist_img_dir + '/' + str(item) + '.png' + ' ' + dist_anno_dir + '/' + str(item) + '.xml\n')  
      
    for item in test_img_names:  
        test_fd.write(dist_img_dir + '/' + str(item) + '.png' + ' ' + dist_anno_dir + '/' + str(item) + '.xml\n')
  • 最後是test_name_size.txt文檔,一樣用腳本生成:
  • #! /usr/bin/python
    # -*- coding:UTF-8 -*- 
    import os, sys
    import glob
    from PIL import Image #讀圖

    #圖的路徑
    img_dir = "/home/u809-1/caffe-ssd-clh/data/VOC_knife/PNGImages/test"

    #獲取制定路徑下的全部png圖片的名稱
    img_lists = glob.glob(img_dir + '/*.png')

    #在指定路徑下建立文件
    test_name_size = open('/home/u809-1/caffe-ssd-clh/data/VOC_knife/test_name_size_knife.txt', 'w')

    for item in img_lists:
        img = Image.open(item)
        width, height = img.size
        temp1, temp2 = os.path.splitext(os.path.basename(item))
        test_name_size.write(temp1 + ' ' + str(height) + ' ' + str(width) + '\n')
  • 這時已經準備好VOC格式的數據了,最後一步將VOC轉換成LMDB格式的數據就能夠用做訓練了:
  • cur_dir=$(cd $( dirname ${BASH_SOURCE[0]} ) && pwd )  
    root_dir='/home/u809-1/caffe-ssd-clh'

    cd $root_dir  
      
    redo=1  
    data_root_dir="$HOME/caffe-ssd-clh"
    dataset_name="VOC_knife/ImageSets"  #上下相連到最終VOC
    mapfile="/home/u809-1/caffe-ssd-clh/data/VOC_knife/ImageSets/labelmap_knife.prototxt" #次文件定義了背景層0,以及分類層,下次直接定義labelmap.prototxt的直接路徑便可
    anno_type="detection"  
    db="lmdb"  
    min_dim=0  
    max_dim=0  
    width=0  
    height=0  
      
    extra_cmd="--encode-type=png --encoded"  
    if [ $redo ]  
    then  
      extra_cmd="$extra_cmd --redo"  
    fi  
    for subset in test trainval 
    do   #下面的路徑須要根據本身的狀況修改,咱們的就是這樣   
      python $root_dir/scripts/create_annoset.py --anno-type=$anno_type --label-map-file=$mapfile --min-dim=$min_dim --max-dim=$max_dim --resize-width=$width --resize-height=$height --check-label $extra_cmd $data_root_dir $root_dir/data/$dataset_name/$subset.txt $data_root_dir/$dataset_name/$db/$dataset_name"_"$subset"_"$db examples/$dataset_name  
    done  
  • 須要注意這個腳本是shell腳本,修改路徑時請仔細修改。
  • 生成後的lmdb以下:
  • 此時lmdb格式數據集合已經準備完了,開始訓練!

    三  訓練本身的caffe.model

    • 用以下腳本進行訓練,需修改路徑和其中solver.prototxt:
    • from __future__ import print_function  
      import caffe  
      from caffe.model_libs import *  
      from google.protobuf import text_format  
        
        
      import math  
      import os  
      import shutil  
      import stat  
      import subprocess  
      import sys  
        
        
      # 給基準網絡後面增長額外的卷積層(爲了不此處的卷積層的名稱和基準網絡卷積層的名稱重複,這裏能夠用基準網絡最後一個層的名稱進行開始命名),這一部分的具體實現方法能夠對照文件~/caffe/python/caffe/model_libs.py查看,SSD的實現基本上就是ssd_pascal.py和model_libs.py兩個文件在控制,剩下的則是caffe底層代碼中編寫各個功能模塊。  
      def AddExtraLayers(net, use_batchnorm=True, lr_mult=1):  
          use_relu = True  
        
        
          # Add additional convolutional layers.  
          # 19 x 19  
          ######################################生成附加網絡的第一個卷積層,卷積核的數量爲256,卷積核的大小爲1*1,pad的尺寸爲0,stride爲1.  
          from_layer = net.keys()[-1] #得到基準網絡的最後一層,做爲conv6-1層的輸入  
        
        
          # TODO(weiliu89): Construct the name using the last layer to avoid duplication.  
          # 10 x 10  
          out_layer = "conv6_1"  
          ConvBNLayer(net, from_layer, out_layer, use_batchnorm, use_relu, 256, 1, 0, 1,  
              lr_mult=lr_mult)  
          ########################################conv6_1生成完畢  
          ######################################生成附加網絡的第一個卷積層,卷積核的數量爲512,卷積核的大小爲3*3,pad的尺寸爲1,stride爲2.  
          from_layer = out_layer  
          out_layer = "conv6_2"  
          ConvBNLayer(net, from_layer, out_layer, use_batchnorm, use_relu, 512, 3, 1, 2,  
              lr_mult=lr_mult)  
          #########################################conv6_2生成完畢  
          # 5 x 5  
          from_layer = out_layer  
          out_layer = "conv7_1"  
          ConvBNLayer(net, from_layer, out_layer, use_batchnorm, use_relu, 128, 1, 0, 1,  
            lr_mult=lr_mult)  
          #########################################conv7_1生成完畢  
          from_layer = out_layer  
          out_layer = "conv7_2"  
          ConvBNLayer(net, from_layer, out_layer, use_batchnorm, use_relu, 256, 3, 1, 2,  
            lr_mult=lr_mult)  
          #########################################conv7_2生成完畢  
          # 3 x 3  
          from_layer = out_layer  
          out_layer = "conv8_1"  
          ConvBNLayer(net, from_layer, out_layer, use_batchnorm, use_relu, 128, 1, 0, 1,  
            lr_mult=lr_mult)  
          #########################################conv8_1生成完畢  
          from_layer = out_layer  
          out_layer = "conv8_2"  
          ConvBNLayer(net, from_layer, out_layer, use_batchnorm, use_relu, 256, 3, 0, 1,  
            lr_mult=lr_mult)  
          #########################################conv8_2生成完畢  
          # 1 x 1  
          from_layer = out_layer  
          out_layer = "conv9_1"  
          ConvBNLayer(net, from_layer, out_layer, use_batchnorm, use_relu, 128, 1, 0, 1,  
            lr_mult=lr_mult)  
          #########################################conv9_1生成完畢  
          from_layer = out_layer  
          out_layer = "conv9_2"  
          ConvBNLayer(net, from_layer, out_layer, use_batchnorm, use_relu, 256, 3, 0, 1,  
            lr_mult=lr_mult)  
          #########################################conv9_2生成完畢  
          return net  
        
        
        
        
      ### 相應地修改一下參數 ###  
      # 包含caffe代碼的路徑  
      # 咱們假設你是在caffe跟目錄下運行代碼  
      caffe_root = os.getcwd() #獲取caffe的根目錄  
        
        
      # 若是你想在生成全部訓練文件以後就開始訓練,這裏run_soon給予參數Ture.  
      run_soon = True      
      #若是你想接着上次的訓練,繼續進行訓練,這裏的參數爲Ture,(這個就是說可能你訓練通常中止了,從新啓動的時候,這裏的Ture保證繼續接着你上次的訓練進行訓練)  
      #不然爲False,表示咱們將從下面定義的預訓練模型處進行加載。(這個表示就是無論你上次訓練一半的模型了,咱們直接從預訓練好的基準模型哪裏開始訓練)  
      resume_training = True  
      # 若是是Ture的話,表示咱們要移除舊的模型訓練文件,不然是不移除的。  
        
      remove_old_models = False  
        
      # 訓練數據的數據庫文件. Created by data/VOC0712/create_data.sh  
      train_data = "examples/VOC0712/VOC0712_trainval_lmdb"  
      # 測試數據的數據庫文件. Created by data/VOC0712/create_data.sh  
      test_data = "examples/VOC0712/VOC0712_test_lmdb"  
      # 指定批量採樣器。  
      resize_width = 300  
      resize_height = 300  
      resize = "{}x{}".format(resize_width, resize_height)  
      batch_sampler = [  
              {  
                      'sampler': {  
                              },  
                      'max_trials': 1,  
                      'max_sample': 1,  
              },  
              {  
                      'sampler': {  
                              'min_scale': 0.3,  
                              'max_scale': 1.0,  
                              'min_aspect_ratio': 0.5,  
                              'max_aspect_ratio': 2.0,  
                              },  
                      'sample_constraint': {  
                              'min_jaccard_overlap': 0.1,  
                              },  
                      'max_trials': 50,  
                      'max_sample': 1,  
              },  
              {  
                      'sampler': {  
                              'min_scale': 0.3,  
                              'max_scale': 1.0,  
                              'min_aspect_ratio': 0.5,  
                              'max_aspect_ratio': 2.0,  
                              },  
                      'sample_constraint': {  
                              'min_jaccard_overlap': 0.3,  
                              },  
                      'max_trials': 50,  
                      'max_sample': 1,  
              },  
              {  
                      'sampler': {  
                              'min_scale': 0.3,  
                              'max_scale': 1.0,  
                              'min_aspect_ratio': 0.5,  
                              'max_aspect_ratio': 2.0,  
                              },  
                      'sample_constraint': {  
                              'min_jaccard_overlap': 0.5,  
                              },  
                      'max_trials': 50,  
                      'max_sample': 1,  
              },  
              {  
                      'sampler': {  
                              'min_scale': 0.3,  
                              'max_scale': 1.0,  
                              'min_aspect_ratio': 0.5,  
                              'max_aspect_ratio': 2.0,  
                              },  
                      'sample_constraint': {  
                              'min_jaccard_overlap': 0.7,  
                              },  
                      'max_trials': 50,  
                      'max_sample': 1,  
              },  
              {  
                      'sampler': {  
                              'min_scale': 0.3,  
                              'max_scale': 1.0,  
                              'min_aspect_ratio': 0.5,  
                              'max_aspect_ratio': 2.0,  
                              },  
                      'sample_constraint': {  
                              'min_jaccard_overlap': 0.9,  
                              },  
                      'max_trials': 50,  
                      'max_sample': 1,  
              },  
              {  
                      'sampler': {  
                              'min_scale': 0.3,  
                              'max_scale': 1.0,  
                              'min_aspect_ratio': 0.5,  
                              'max_aspect_ratio': 2.0,  
                              },  
                      'sample_constraint': {  
                              'max_jaccard_overlap': 1.0,  
                              },  
                      'max_trials': 50,  
                      'max_sample': 1,  
              },  
        
              ]  
        
      #以上這一部分就是文中所說的數據加強部分,抱歉的是這一部分我也沒太看懂。具體可查看~/caffe/src/caffe/util/sampler.cpp文件中的詳細定義。  
        
      #如下是轉換參數設置,具體意思可在caffe底層代碼中查看參數的定義。路徑爲~/caffe/src/caffe/proto/caffe.proto  
        
      train_transform_param = {  
              'mirror': True,  
              'mean_value': [104, 117, 123],############均值  
              'resize_param': {  #################存儲數據轉換器用於調整大小策略的參數的消息。  
                      'prob': 1,  ###############使用這個調整策略的可能性  
                      'resize_mode': P.Resize.WARP, ########重定義大小的模式,caffe.proto中定義的是枚舉類型  
                      'height': resize_height,  
                      'width': resize_width,  
                      'interp_mode': [ ###########插值模式用於調整大小,定義爲枚舉類型  
                              P.Resize.LINEAR,  
                              P.Resize.AREA,  
                              P.Resize.NEAREST,  
                              P.Resize.CUBIC,  
                              P.Resize.LANCZOS4,  
                              ],  
                      },  
              'distort_param': {##########################存儲數據轉換器用於失真策略的參數的消息  
                      'brightness_prob': 0.5,  ###########調整亮度的機率,默認爲1。  
                      'brightness_delta': 32,  ###########要添加到[-delta,delta]內的像素值的數量。可能的值在[0,255]以內。 推薦32。  
                      'contrast_prob': 0.5, #######調整對比度的機率。  
                      'contrast_lower': 0.5, #######隨機對比因子的下界。 推薦0.5。  
                      'contrast_upper': 1.5, #######隨機對比因子的上界。 推薦1.5。  
                      'hue_prob': 0.5, ##########調整色調的機率。  
                      'hue_delta': 18, ##########添加到[-delta,delta]內的色調通道的數量。 可能的值在[0,180]以內。 推薦36。  
                      'saturation_prob': 0.5, ########調整飽和的機率。  
                      'saturation_lower': 0.5, ########隨機飽和因子的下界。 推薦0.5。  
                      'saturation_upper': 1.5, ########隨機飽和因子的上界。 推薦1.5。  
                      'random_order_prob': 0.0, ########隨機排列圖像通道的機率。  
                      },  
              'expand_param': {   ##################存儲數據轉換器用於擴展策略的參數的消息  
                      'prob': 0.5,   ###############使用這個擴展策略的可能性  
                      'max_expand_ratio': 4.0,   ######擴大圖像的比例。  
                      },  
              'emit_constraint': {    ########給定註釋的條件。  
                  'emit_type': caffe_pb2.EmitConstraint.CENTER,    ##############類型定義爲枚舉,此處選定爲CENTER  
                  }  
              }  
      test_transform_param = {    ###############測試轉換參數,相似於訓練轉換參數。  
              'mean_value': [104, 117, 123],  
              'resize_param': {  
                      'prob': 1,  
                      'resize_mode': P.Resize.WARP,  
                      'height': resize_height,  
                      'width': resize_width,  
                      'interp_mode': [P.Resize.LINEAR],  
                      },  
        
              }  
        
      # 若是爲true,則對全部新添加的圖層使用批量標準。  
      # 目前只有非批量規範版本已通過測試。  
      use_batchnorm = False   ###############是否使用批量標準  
      lr_mult = 1    #############基礎學習率設定爲1,用於下面的計算以改變初始學習率。  
      # 使用不一樣的初始學習率。  
      if use_batchnorm:  
          base_lr = 0.0004  
      else:  
          # 當batch_size = 1, num_gpus = 1時的學習率。  
          base_lr = 0.00004   ############因爲上面use_batchnorm = false,因此咱們通常調整初始學習率時只需更改這一部分,目前爲0.001。  

      #你改你的model
        # Modify the job name if you want.
      job_name = "SSD_{}".format(resize)
      # The name of the model. Modify it if you want.
      model_name = "VGG_VOC0712_{}".format(job_name)


      #存儲模型.prototxt文件的目錄。  
      save_dir = "models/VGGNet/VOC0712/{}".format(job_name)  
      # 存儲模型快照的目錄。  
      snapshot_dir = "models/VGGNet/VOC0712/{}".format(job_name)  
      # 存儲做業腳本和日誌文件的目錄。  
      job_dir = "jobs/VGGNet/VOC0712/{}".format(job_name)  
      # 存儲檢測結果的目錄。  
      output_result_dir = "{}/data/VOCdevkit/results/VOC2007/{}/Main".format(os.environ['HOME'], job_name)  
        
      # 模型定義文件。  
      train_net_file = "{}/train.prototxt".format(save_dir)  
      test_net_file = "{}/test.prototxt".format(save_dir)  
      deploy_net_file = "{}/deploy.prototxt".format(save_dir)  
      solver_file = "{}/solver.prototxt".format(save_dir)  
      # 快照前綴。  
      snapshot_prefix = "{}/{}".format(snapshot_dir, model_name)  
      # 做業腳本路徑。  
      job_file = "{}/{}.sh".format(job_dir, model_name)  
        
      # 存儲測試圖像的名稱和大小。 Created by data/VOC0712/create_list.sh  
      name_size_file = "data/VOC0712/test_name_size.txt"  
      # 預訓練模型。 咱們使用完卷積截斷的VGGNet。  
      pretrain_model = "models/VGGNet/VGG_ILSVRC_16_layers_fc_reduced.caffemodel"  
      # 存儲LabelMapItem。  
      label_map_file = "data/VOC0712/labelmap_voc.prototxt"  
        
      # 多框損失層MultiBoxLoss的參數。在~/caffe/src/caffe/proto/caffe.proto可查找具體定義  
      num_classes = 21  ##########要預測的類的數量。你的分類數+1 
      share_location = True   #########位置共享,若是爲true,邊框在不一樣的類中共享。  
      background_label_id=0   ########是否使用先驗匹配,通常爲true。  
      train_on_diff_gt = True    ########是否考慮困難的ground truth,默認爲true。  
      normalization_mode = P.Loss.VALID    ######如何規範跨越批次,空間維度或其餘維度彙集的損失層的損失。 目前只在SoftmaxWithLoss和SigmoidCrossEntropyLoss圖層中實現。按照批次中的示例數量乘以空間維度。 在計算歸一化因子時,不會忽略接收忽略標籤的輸出。定義爲枚舉,四種類型分別是:FULL,除以不帶ignore_label的輸出位置總數。 若是未設置ignore_label,則表現爲FULL;VALID;BATCH_SIZE,除以批量大小;NONE,不要規範化損失。  
      code_type = P.PriorBox.CENTER_SIZE     #########bbox的編碼方式。此參數定義在PriorBoxParameter參數定義解釋中,爲枚舉類型,三種類型爲:CORNER,CENTER_SIZE和CORNER_SIZE。  
      ignore_cross_boundary_bbox = False    ########若是爲true,則在匹配期間忽略跨邊界bbox。 跨界bbox是一個在圖像區域以外的bbox。即將超出圖像的預測邊框剔除,這裏咱們不踢除,不然特徵圖邊界點產生的先驗框就沒有任何意義。  
      mining_type = P.MultiBoxLoss.MAX_NEGATIVE   訓練期間的挖掘類型。定義爲枚舉,分別爲三種類型: 若爲NONE則表示什麼都不使用,這樣會致使正負樣本的嚴重不均衡;若爲MAX_NEGATIVE則根據分數選擇底片;若爲HARD_EXAMPLE則選擇基於「在線硬示例挖掘的基於訓練區域的對象探測器」的硬實例,此類型即爲SSD原文中所使用的Hard_negative_mining(負硬挖掘)策略。  
      neg_pos_ratio = 3.  #####負/正比率,即文中所說的1:3。  
      loc_weight = (neg_pos_ratio + 1.) / 4.    #########位置損失的權重,  
      multibox_loss_param = {        ############存儲MultiBoxLossLayer使用的參數的消息  
          'loc_loss_type': P.MultiBoxLoss.SMOOTH_L1,   ###########位置損失類型,定義爲枚舉,有L2和SMOOTH_L1兩種類型。  
          'conf_loss_type': P.MultiBoxLoss.SOFTMAX,   #########置信損失類型,定義爲枚舉,有SOFTMAX和LOGISTIC兩種。  
          'loc_weight': loc_weight,  
          'num_classes': num_classes,  
          'share_location': share_location,  
          'match_type': P.MultiBoxLoss.PER_PREDICTION,   #########訓練中的匹配方法。定義爲枚舉,有BIPARTITE和PER_PREDICTION兩種。若是match_type爲PER_PREDICTION(即每張圖預測),則使用overlap_threshold來肯定額外的匹配bbox。  
          'overlap_threshold': 0.5,   #########閥值大小。即咱們所說的IoU的大小。  
          'use_prior_for_matching': True,   ########是否使用先驗匹配,通常爲true。  
          'background_label_id': background_label_id,   ##########背景標籤的類別編號,通常爲0。  
          'use_difficult_gt': train_on_diff_gt,  ########是否考慮困難的ground truth,默認爲true。  
          'mining_type': mining_type,    #######訓練期間的挖掘類型。定義爲枚舉,分別爲三種類型: 若爲NONE則表示什麼都不使用,這樣會致使正負樣本的嚴重不均衡;若爲MAX_NEGATIVE則根據分數選擇底片;若爲HARD_EXAMPLE則選擇基於「在線硬示例挖掘的基於訓練區域的對象探測器」的硬實例,此類型即爲SSD原文中所使用的Hard_negative_mining(負硬挖掘)策略。  
          'neg_pos_ratio': neg_pos_ratio,   #####負/正比率,即文中所說的1:3。  
          'neg_overlap': 0.5,   ####對於不匹配的預測,上限爲負的重疊。即若是重疊小於0.5則定義爲負樣本,Faster R-CNN設置爲0.3。  
          'code_type': code_type,   #########bbox的編碼方式。此參數定義在PriorBoxParameter參數定義解釋中,爲枚舉類型,三種類型爲:CORNER,CENTER_SIZE和CORNER_SIZE。  
          'ignore_cross_boundary_bbox': ignore_cross_boundary_bbox,  ########若是爲true,則在匹配期間忽略跨邊界bbox。 跨界bbox是一個在圖像區域以外的bbox。即將超出圖像的預測邊框剔除,這裏咱們不踢除,不然特徵圖邊界點產生的先驗框就沒有任何意義。  
          }  
      loss_param = {   ###存儲由損失層共享的參數的消息  
          'normalization': normalization_mode,    ######如何規範跨越批次,空間維度或其餘維度彙集的損失層的損失。 目前只在SoftmaxWithLoss和SigmoidCrossEntropyLoss圖層中實現。按照批次中的示例數量乘以空間維度。 在計算歸一化因子時,不會忽略接收忽略標籤的輸出。定義爲枚舉,四種類型分別是:FULL,除以不帶ignore_label的輸出位置總數。 若是未設置ignore_label,則表現爲FULL;VALID;BATCH_SIZE,除以批量大小;NONE,不要規範化損失。  
          }  
        
      #參數生成先驗。  
      #輸入圖像的最小尺寸  
      min_dim = 300   #######維度  
      # conv4_3 ==> 38 x 38  
      # fc7 ==> 19 x 19  
      # conv6_2 ==> 10 x 10  
      # conv7_2 ==> 5 x 5  
      # conv8_2 ==> 3 x 3  
      # conv9_2 ==> 1 x 1  
      mbox_source_layers = ['conv4_3', 'fc7', 'conv6_2', 'conv7_2', 'conv8_2', 'conv9_2'] #####prior_box來源層,能夠更改。不少改進都是基於此處的調整。  
      # in percent %  
      min_ratio = 20 ####這裏便是論文中所說的Smin=0.2,Smax=0.9的初始值,通過下面的運算便可獲得min_sizes,max_sizes。具體如何計算以及二者表明什麼,請關注個人博客SSD詳解。這裏產生不少改進。  
      max_ratio = 90  
      ####math.floor()函數表示:求一個最接近它的整數,它的值小於或等於這個浮點數。  
      step = int(math.floor((max_ratio - min_ratio) / (len(mbox_source_layers) - 2)))####取一個間距步長,即在下面for循環給ratio取值時起一個間距做用。能夠用一個具體的數值代替,這裏等於17。  
      min_sizes = []  ###通過如下運算獲得min_sizes和max_sizes。  
      max_sizes = []  
      for ratio in xrange(min_ratio, max_ratio + 1, step):  ####從min_ratio至max_ratio+1每隔step=17取一個值賦值給ratio。注意xrange函數的做用。  
      ########min_sizes.append()函數即把括號內部每次獲得的值依次給了min_sizes。  
        min_sizes.append(min_dim * ratio / 100.)  
        max_sizes.append(min_dim * (ratio + step) / 100.)  
      min_sizes = [min_dim * 10 / 100.] + min_sizes  
      max_sizes = [min_dim * 20 / 100.] + max_sizes  
      steps = [8, 16, 32, 64, 100, 300]  ###這一步要仔細理解,即計算卷積層產生的prior_box距離原圖的步長,先驗框中心點的座標會乘以step,至關於從feature map位置映射回原圖位置,好比conv4_3輸出特徵圖大小爲38*38,而輸入的圖片爲300*300,因此38*8約等於300,因此映射步長爲8。這是針對300*300的訓練圖片。  
      aspect_ratios = [[2], [2, 3], [2, 3], [2, 3], [2], [2]]  #######這裏指的是橫縱比,六種尺度對應六個產生prior_box的卷積層。具體可查看生成的train.prototxt文件一一對應每層的aspect_ratio參數,此參數在caffe.proto中有定義,關於aspect_ratios如何把其內容傳遞給了aspect_ratio,在model_libs.py文件中有詳細定義。  
      ##在此咱們要說明一個事實,就是文中的長寬比是如何產生的,這裏請讀者必定要參看博主博文《SSD詳解(一)》中的第2部份內容,關於prior_box的產生。  
      # L2 normalize conv4_3.  
      normalizations = [20, -1, -1, -1, -1, -1]  ##對卷積層conv4_3作歸一化。model_libs.py裏產生了normallize層,具體的層定義,參看底層代碼~/caffe/src/layers/Normalize_layer.cpp,爲何這裏設置conv4_3爲20我也沒看懂,原諒C++太渣,這裏每一個數對應每一個先驗層,只要哪一個層對應的數不爲-1則產生normal。  
      # 用於對以前的bbox進行編碼/解碼的方差。  
      if code_type == P.PriorBox.CENTER_SIZE:  ########兩種選擇,根據參數code_type的選擇決定,因爲上面已經將code_type選定。有人理解爲變量variance用來對bbox的迴歸目標進行放大,從而加快對應濾波器參數的收斂。除以variance是對預測box和真實box的偏差進行放大,從而增長loss,增大梯度,加快收斂。另外,top_data += top[0]->offset(0, 1);已經使指針指向新的地址,因此variance不會覆蓋前面的結果。prior_variance在model_libs.py中傳遞給了variance變量,而後利用prior_box_layer.cpp將其運算定義至priorbox_layer層中,具體可查看train.prototxt中的每個先驗卷積層層中產生先驗框的層中,即**_mbox_priorbox。  
        prior_variance = [0.1, 0.1, 0.2, 0.2]  
      else:  
        prior_variance = [0.1]  
      flip = True   ###若是爲true,則會翻轉每一個寬高比。例如,若是有縱橫比「r」,咱們也會產生縱橫比「1.0 / r」。故產生{1,2,3,1/2,1/3}。  
      clip = False  ###作clip操做是爲了讓prior的候選座標位置保持在[0,1]範圍內。在caffe.proto文件中有關於參數clip的解釋,爲」若是爲true,則將先驗框裁剪爲[0,1]「。  
      #以上兩個參數所產生的結果均在prior_box_layer.cpp中實現。  
        
      # 求解參數。  
      # 定義要使用的GPU。  
      gpus = "0,1,2,3"  #多塊GPU的編號,若是隻有一塊,這裏只需保留0,不然會出錯。  
      gpulist = gpus.split(",") #獲取GPU的列表。  
      num_gpus = len(gpulist) #獲取GPU編號。  
        
      # 將小批量分紅不一樣的GPU.  
      batch_size = 32  #設置訓練樣本輸入的數量,不要超出內存就好。  
      accum_batch_size = 32 #這裏與batch_size相搭配產生下面的iter_size。在看了下一行你就知道它的做用了。  
      iter_size = accum_batch_size / batch_size #若是iter_size=1,則前向傳播一次後進行一次反向傳遞,若是=2,則兩次前傳後進行一次反傳,這樣作是減小每次傳播所佔用的內存空間,有的硬件不行的話就沒法訓練,可是增長iter會使訓練時間增長,可是總的迭代次數不變。  
      solver_mode = P.Solver.CPU  
      device_id = 0  
      batch_size_per_device = batch_size #批次傳遞,沒什麼好講的。  
      if num_gpus > 0:  
        batch_size_per_device = int(math.ceil(float(batch_size) / num_gpus))  #這裏指若是你有多塊GPU則能夠將這些訓練任務均分給多塊GPU訓練,從而加快訓練速度。  
        iter_size = int(math.ceil(float(accum_batch_size) / (batch_size_per_device * num_gpus))) #多塊GPU的iter_size大小計算,上面的是一塊的時候。  
        solver_mode = P.Solver.GPU  
        device_id = int(gpulist[0])  
        
      if normalization_mode == P.Loss.NONE: ##若是損失層的參數NormalizationMode選擇NONE,即沒有歸一化模式,則基礎學習率爲本文件之上的base_lr=0.0004除以batch_size_per_device=32獲得新的base_lr=1.25*10^(-5)。  
        base_lr /= batch_size_per_device  
      elif normalization_mode == P.Loss.VALID: ##同理,根據不一樣的歸一化模式選擇不一樣的base_lr。在本文件上面咱們看到了normalization_mode = P.Loss.VALID,而loc_weight = (neg_pos_ratio + 1.) / 4==1,因此新的base_lr=25*0.0004=0.001,這就是爲何咱們最後生成的solver.prototxt文件中的base_lr=0.001的緣由,因此若是訓練發散想經過減少base_lr來實驗,則要更改最上面的base_lr=0.0004才能夠。  
        base_lr *= 25. / loc_weight  
      elif normalization_mode == P.Loss.FULL:  #同上理。  
        # 每幅圖像大概有2000個先驗bbox。  
        # TODO(weiliu89): 估計確切的先驗數量。  
        base_lr *= 2000. #base_lr=2000*0.0004=0.8。  
        
      # 評估整個測試集。  
      num_test_image = 4952 #整個測試集圖像的數量。  
      test_batch_size = 8 #測試時的batch_size。  
      # 理想狀況下,test_batch_size應該被num_test_image整除,不然mAP會略微偏離真實值。  
      test_iter = int(math.ceil(float(num_test_image) / test_batch_size)) #這裏計算每測試迭代多少次能夠覆蓋整個測試集,和分類網絡中的是一致的。這裏4952/8=619,若是你的測試圖片除以你的test_batch_size不等於整數,那麼這裏會取一個近似整數。  
        
      solver_param = { ##solver.prototxt文件中的各參數的取值,這裏相信作過caffe訓練的人應該大體有了解。  
          # 訓練參數  
          'base_lr': base_lr, #把上面的solver拿下來。  
          'weight_decay': 0.0005,  
          'lr_policy': "multistep",  
          'stepvalue': [80000, 100000, 120000], #多步衰減  
          'gamma': 0.1,  
          'momentum': 0.9,  
          'iter_size': iter_size,  
          'max_iter': 120000,  
          'snapshot': 80000,  
          'display': 10,  
          'average_loss': 10,  
          'type': "SGD",  
          'solver_mode': solver_mode,  
          'device_id': device_id,  
          'debug_info': False,  
          'snapshot_after_train': True,  
          # 測試參數  
          'test_iter': [test_iter],  
          'test_interval': 10000, #測試10000次輸出一次測試結果  
          'eval_type': "detection",  
          'ap_version': "11point",  
          'test_initialization': False,  
          }  
        
      # 生成檢測輸出的參數。  
      det_out_param = {  
          'num_classes': num_classes,  #類別數目  
          'share_location': share_location,  #位置共享。  
          'background_label_id': background_label_id, #背景類別編號,這裏爲0。  
          'nms_param': {'nms_threshold': 0.45, 'top_k': 400}, #非最大抑制參數,閥值爲0.45,top_k表示最大數量的結果要保留,文中介紹,非最大抑制的做用就是消除多餘的框,就是使評分低的框剔除。參數解釋在caffe.proto中有介紹。  
          'save_output_param': {  #用於保存檢測結果的參數,這一部分參數在caffe.proto中的SaveOutputParameter有定義。  
              'output_directory': output_result_dir,  #輸出目錄。 若是不是空的,咱們將保存結果。前面咱們有定義結果保存的路徑。  
              'output_name_prefix': "comp4_det_test_", #輸出名稱前綴。  
              'output_format': "VOC", #輸出格式。VOC  -  PASCAL VOC輸出格式。COCO  -  MS COCO輸出格式。  
              'label_map_file': label_map_file, #若是要輸出結果,還必須提供如下兩個文件。不然,咱們將忽略保存結果。標籤映射文件。這在前面中有給label_map_file附文件,也就是咱們在訓練的時候所作的labelmap.prototxt文件的位置,詳情參看博主博文《基於caffe使用SSD訓練本身的數據》。  
              'name_size_file': name_size_file, #即咱們在訓練時定義的test_name_size.txt文件的路徑。該文件表示測試圖片的大小。  
              'num_test_image': num_test_image, #測試圖片的數量。  
              },  
          'keep_top_k': 200, ##nms步以後每一個圖像要保留的bbox總數。-1表示在nms步以後保留全部的bbox。  
          'confidence_threshold': 0.01, #只考慮可信度大於閾值的檢測。 若是沒有提供,請考慮全部的框。  
          'code_type': code_type,  #bbox的編碼方式。  
          }  
        
      # 評估檢測結果的參數。  
      det_eval_param = {  #位於caffe.proto文件中的DetectionEvaluateParameter定義。  
          'num_classes': num_classes, #類別數  
          'background_label_id': background_label_id, #背景編號,爲0。  
          'overlap_threshold': 0.5, #重疊閥值,0.5。  
          'evaluate_difficult_gt': False, #若是爲true,也要考慮難以評估的grountruth。  
          'name_size_file': name_size_file, #test_name_size.txt路徑。  
          }  
        
      ###但願你不須要改變如下###  
      # 檢查文件。這一部分是檢查你的全部訓練驗證過程必須有的文件與數據提供。  
      check_if_exist(train_data)  
      check_if_exist(test_data)  
      check_if_exist(label_map_file)  
      check_if_exist(pretrain_model)  
      make_if_not_exist(save_dir)  
      make_if_not_exist(job_dir)  
      make_if_not_exist(snapshot_dir)  
        
      # 建立訓練網絡。這一部分主要是在model_libs.py中完成的。  
      net = caffe.NetSpec()  
      ##調用model_libs.py中的CreateAnnotatedDataLayer()函數,建立標註數據傳遞層,將括號中的參數傳遞進去。model_libs.py文件中提供了四種基礎網絡,即VGG、ZF、ResNet101和ResNet152。  
      net.data, net.label = CreateAnnotatedDataLayer(train_data, batch_size=batch_size_per_device,  
              train=True, output_label=True, label_map_file=label_map_file,  
              transform_param=train_transform_param, batch_sampler=batch_sampler)  
      #調用model_libs.py中的VGGNetBody()函數建立截斷的VGG基礎網絡。參數傳遞進去。model_libs.py文件中提供了四種基礎網絡,即VGG、ZF、ResNet101和ResNet152。能夠分別查看不一樣基礎網絡的調用方式。  
      VGGNetBody(net, from_layer='data', fully_conv=True, reduced=True, dilated=True,  
          dropout=False)  ##這些參數分別表示:from_layer表示本基礎網絡的數據源來自data層的輸出,fully_conv=Ture表示使用全卷積,reduced=Ture在該文件中能夠發現是負責選用全卷積層的某幾個參數的取值和最後選擇不一樣參數的全連接層,dilated=True表示是否須要fc6和fc7間的pool5層以及選擇其參數還有配合reduced共同選擇全卷積層的參數選擇,dropout表示是否須要dropout層flase表示不須要。  
        
      #如下爲添加特徵提取的層,即調用咱們本文件最上面定義的須要額外添加的幾個層,即conv6_1,conv6_2等等。  
      AddExtraLayers(net, use_batchnorm, lr_mult=lr_mult)  
        
      #調用CreateMultiBoxHead()函數建立先驗框的提取及匹配等層數,下面這些參數其實咱們在上面所有都有解釋,具體仍然能夠參照caffe.proto和model_libs.py以及該層對應的cpp實現文件去閱讀理解。這些層包括conv_mbox_conf、conv_mbox_loc、對應前二者的perm和flat層(這兩層的做用在我博文《SSD詳解》中有解釋)、還有conv_mbox_priorbox先驗框產生層等。  
      mbox_layers = CreateMultiBoxHead(net, data_layer='data', from_layers=mbox_source_layers,  
              use_batchnorm=use_batchnorm, min_sizes=min_sizes, max_sizes=max_sizes,  
              aspect_ratios=aspect_ratios, steps=steps, normalizations=normalizations,  
              num_classes=num_classes, share_location=share_location, flip=flip, clip=clip,  
              prior_variance=prior_variance, kernel_size=3, pad=1, lr_mult=lr_mult)  
        
      # 建立MultiBoxLossLayer。即建立損失層。這裏包括置信損失和位置損失的疊加。具體計算的實如今multibox_loss_layer.cpp中實現,其中的哥哥參數想multi_loss_param和loss_param等參數在前面均有定義。  
      name = "mbox_loss"  
      mbox_layers.append(net.label)  
      net[name] = L.MultiBoxLoss(*mbox_layers, multibox_loss_param=multibox_loss_param,  
              loss_param=loss_param, include=dict(phase=caffe_pb2.Phase.Value('TRAIN')),  
              propagate_down=[True, True, False, False]) #這裏重點講一下參數propagate_down,指定是否反向傳播到每一個底部。若是未指定,Caffe會自動推斷每一個輸入是否須要反向傳播來計算參數梯度。若是對某些輸入設置爲true,則強制向這些輸入反向傳播; 若是對某些輸入設置爲false,則會跳過對這些輸入的反向傳播。大小必須是0或等於底部的數量。具體解讀cpp文件中的參數propagate_down[0]~[3]。  
        
      with open(train_net_file, 'w') as f: #打開文件將上面編輯的這些層寫入到prototxt文件中。  
          print('name: "{}_train"'.format(model_name), file=f)  
          print(net.to_proto(), file=f)  
      shutil.copy(train_net_file, job_dir) #將寫入的訓練文件train.prototxt複製一份給目錄job_dir。  
        
       # 建立測試網絡。前一部分基本上與訓練網絡一致。  
      net = caffe.NetSpec()  
      net.data, net.label = CreateAnnotatedDataLayer(test_data, batch_size=test_batch_size,  
              train=False, output_label=True, label_map_file=label_map_file,  
              transform_param=test_transform_param)  
        
      VGGNetBody(net, from_layer='data', fully_conv=True, reduced=True, dilated=True,  
          dropout=False)  
        
      AddExtraLayers(net, use_batchnorm, lr_mult=lr_mult)  
        
      mbox_layers = CreateMultiBoxHead(net, data_layer='data', from_layers=mbox_source_layers,  
              use_batchnorm=use_batchnorm, min_sizes=min_sizes, max_sizes=max_sizes,  
              aspect_ratios=aspect_ratios, steps=steps, normalizations=normalizations,  
              num_classes=num_classes, share_location=share_location, flip=flip, clip=clip,  
              prior_variance=prior_variance, kernel_size=3, pad=1, lr_mult=lr_mult)  
        
      conf_name = "mbox_conf"  #置信的交叉驗證。  
      if multibox_loss_param["conf_loss_type"] == P.MultiBoxLoss.SOFTMAX:  
        reshape_name = "{}_reshape".format(conf_name)  
        net[reshape_name] = L.Reshape(net[conf_name], shape=dict(dim=[0, -1, num_classes]))  
        softmax_name = "{}_softmax".format(conf_name)  
        net[softmax_name] = L.Softmax(net[reshape_name], axis=2)  
        flatten_name = "{}_flatten".format(conf_name)  
        net[flatten_name] = L.Flatten(net[softmax_name], axis=1)  
        mbox_layers[1] = net[flatten_name]  
      elif multibox_loss_param["conf_loss_type"] == P.MultiBoxLoss.LOGISTIC:  
        sigmoid_name = "{}_sigmoid".format(conf_name)  
        net[sigmoid_name] = L.Sigmoid(net[conf_name])  
        mbox_layers[1] = net[sigmoid_name]  
        
      #下面這一部分是test網絡獨有的,爲檢測輸出和評估網絡。  
      net.detection_out = L.DetectionOutput(*mbox_layers,  
          detection_output_param=det_out_param,  
          include=dict(phase=caffe_pb2.Phase.Value('TEST')))  
      net.detection_eval = L.DetectionEvaluate(net.detection_out, net.label,  
          detection_evaluate_param=det_eval_param,  
          include=dict(phase=caffe_pb2.Phase.Value('TEST')))  
        
      with open(test_net_file, 'w') as f: #寫入test.txt。  
          print('name: "{}_test"'.format(model_name), file=f)  
          print(net.to_proto(), file=f)  
      shutil.copy(test_net_file, job_dir)  
        
      # 建立deploy網絡。  
      # 從測試網中刪除第一層和最後一層。  
      deploy_net = net  
      with open(deploy_net_file, 'w') as f:  
          net_param = deploy_net.to_proto()  
          # 從測試網中刪除第一個(AnnotatedData)和最後一個(DetectionEvaluate)層。  
          del net_param.layer[0] #刪除首層  
          del net_param.layer[-1] #刪除尾層。  
          net_param.name = '{}_deploy'.format(model_name) #建立網絡名。  
          net_param.input.extend(['data']) #輸入擴展爲data。  
          net_param.input_shape.extend([  
              caffe_pb2.BlobShape(dim=[1, 3, resize_height, resize_width])]) #deploy.prototxt文件中特有的輸入數據維度信息,這裏應該爲[1,3,300,300]。  
          print(net_param, file=f) #輸出到文件  
      shutil.copy(deploy_net_file, job_dir) #複製一份到job_dir中。  
        
      # 建立Slover.prototxt。  
      solver = caffe_pb2.SolverParameter(  #將上面定義的solver參數通通拿下來。  
              train_net=train_net_file,  
              test_net=[test_net_file],  
              snapshot_prefix=snapshot_prefix,  
              **solver_param)  
        
      with open(solver_file, 'w') as f: #將拿下來的參數通通寫入solver.prototxt中。  
          print(solver, file=f)  
      shutil.copy(solver_file, job_dir) #複製一份到job_dir中。  
        
      max_iter = 0  #最大迭代次數首先初始化爲0。  
      # 找到最近的快照。即若是中途中斷訓練,再次訓練首先尋找上次中斷時保存的模型繼續訓練。  
      for file in os.listdir(snapshot_dir): #依次在快照模型所保存的文件中查找相對應的模型。  
        if file.endswith(".solverstate"): #若是存在此模型,則繼續往下訓練。  
          basename = os.path.splitext(file)[0]  
          iter = int(basename.split("{}_iter_".format(model_name))[1])  
          if iter > max_iter: #若是已迭代的次數大於max_iter,則賦值給max_iter。  
            max_iter = iter  
        
      #如下部分爲訓練命令。  
      train_src_param = '--weights="{}" \\\n'.format(pretrain_model) #加載與訓練微調模型命令。  
      if resume_training:  
        if max_iter > 0:  
          train_src_param = '--snapshot="{}_iter_{}.solverstate" \\\n'.format(snapshot_prefix, max_iter) #權重的初始參數即從咱們定義的imagenet訓練VGG16模型中獲取。  
        
      if remove_old_models:  
        # 刪除任何小於max_iter的快照。上一段和本段程序主要的目的是隨着訓練的推動,max_iter隨之逐漸增大,知道訓練至120000次後把前面生成的快照模型都刪除了,就是保存下一次的模型後刪除上一次的模型。  
        for file in os.listdir(snapshot_dir):  #遍歷查找模型文件。  
          if file.endswith(".solverstate"): #找到後綴爲solverstate的模型文件。  
            basename = os.path.splitext(file)[0]  
            iter = int(basename.split("{}_iter_".format(model_name))[1]) #獲取已迭代的次數。  
            if max_iter > iter: #若是迭代知足條件,則下一條語句去刪除。  
              os.remove("{}/{}".format(snapshot_dir, file))  
          if file.endswith(".caffemodel"):  #找到後綴爲caffemodel的模型文件。  
            basename = os.path.splitext(file)[0]  
            iter = int(basename.split("{}_iter_".format(model_name))[1]) #獲取迭代次數iter。  
            if max_iter > iter: #判斷若是知足條件則刪除已存在的模型。  
              os.remove("{}/{}".format(snapshot_dir, file))  
        
      # 建立工做文件。  
      with open(job_file, 'w') as f: #將訓練文件寫入執行文件中生成.sh可執行文件後執行命令訓練。  
        f.write('cd {}\n'.format(caffe_root))  
        f.write('./build/tools/caffe train \\\n')  
        f.write('--solver="{}" \\\n'.format(solver_file))  
        f.write(train_src_param)  
        if solver_param['solver_mode'] == P.Solver.GPU:  
          f.write('--gpu {} 2>&1 | tee {}/{}.log\n'.format(gpus, job_dir, model_name))  
        else:  
          f.write('2>&1 | tee {}/{}.log\n'.format(job_dir, model_name))  
        
      # 複製本腳本只job_dir中。  
      py_file = os.path.abspath(__file__)  
      shutil.copy(py_file, job_dir)  
        
      # 運行。  
      os.chmod(job_file, stat.S_IRWXU)  
      if run_soon:  
        subprocess.call(job_file, shell=True) 
    • 具體須要修改的參數和路徑已經在腳本中註釋,在修改類別數量時請+1(背景類),好比只有一類則改成2。

    四 用本身的模型在圖片上進行目標識別

  • # -*- coding: utf-8 -*
    import numpy as np

    import timeit
    from PIL import Image
    from PIL import ImageDraw 
    import os
    import numpy as np
    import matplotlib.pyplot as plt

    plt.rcParams['figure.figsize'] = (10, 10)
    plt.rcParams['image.interpolation'] = 'nearest'
    plt.rcParams['image.cmap'] = 'gray'

    # Make sure that the work directory is caffe_root
    caffe_root = './' 
    # modify img_dir to your path of testing images of kitti
    #須要測試的集合的圖片
    img_dir = 'models/knife/test1/'
    import os
    os.chdir(caffe_root)
    import sys
    sys.path.insert(0, 'python')
    from google.protobuf import text_format
    from caffe.proto import caffe_pb2

    import caffe
    #from _ensemble import *

    caffe.set_device(0)
    caffe.set_mode_gpu()
    #deploy,模型,和labelmap的位置
    model_def = 'models/knife/model-v1/SSD_300x300/deploy.prototxt'
    model_weights = 'models/knife/model-v1/SSD_300x300/VGG_knife_SSD_300x300_iter_150000.caffemodel'
    voc_labelmap_file = caffe_root+'data/VOC_knife/ImageSets/labelmap_knife.prototxt'
    #最後標記完保存的路徑
    save_dir = 'models/knife/result1-150000/'
    txt_dir = 'models/knife/result1-150000/'
    #f = open (r'out_3d.txt','w')

    if not(os.path.exists(txt_dir)):
        os.makedirs(txt_dir)
    if not(os.path.exists(save_dir)):
        os.makedirs(save_dir)    
    file = open(voc_labelmap_file, 'r')
    labelmap = caffe_pb2.LabelMap()
    text_format.Merge(str(file.read()), labelmap) 

    net = caffe.Net(model_def,      # defines the structure of the model
                    model_weights,  # contains the trained weights
                    caffe.TEST)     # use test mode (e.g., don't perform dropout)

    # input preprocessing: 'data' is the name of the input blob == net.inputs[0]
    transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})
    transformer.set_transpose('data', (2, 0, 1))
    transformer.set_mean('data', np.array([104,117,123])) # mean pixel
    transformer.set_raw_scale('data', 255)  # the reference model operates on images in [0,255] range instead of [0,1]
    transformer.set_channel_swap('data', (2,1,0))  # the reference model has channels in BGR order instead of RGB

    # set net to batch size of 1

    image_width = 300
    image_height = 300

    net.blobs['data'].reshape(1,3,image_height,image_width)
        
    def get_labelname(labelmap, labels):
        num_labels = len(labelmap.item)
        labelnames = []
        if type(labels) is not list:
            labels = [labels]
        for label in labels:
            found = False
            for i in xrange(0, num_labels):
                if label == labelmap.item[i].label:
                    found = True
                    labelnames.append(labelmap.item[i].display_name)
                    break
            assert found == True
        return labelnames
     
    im_names = list(os.walk(img_dir))[0][2]

    for im_name in im_names:

        img_file = img_dir + im_name
        image = caffe.io.load_image(img_file)
        
        transformed_image = transformer.preprocess('data', image)
        net.blobs['data'].data[...] = transformed_image
        
        #t1 = timeit.Timer("net.forward()","from __main__ import net")
        #print t1.timeit(2)

        # Forward pass.
        detections = net.forward()['detection_out']
        
        # Parse the outputs.
        det_label = detections[0,0,:,1]
        det_conf = detections[0,0,:,2]
        det_xmin = detections[0,0,:,3]
        det_ymin = detections[0,0,:,4]
        det_xmax = detections[0,0,:,5]
        det_ymax = detections[0,0,:,6]

        # Get detections with confidence higher than 0.001
        top_indices = [i for i, conf in enumerate(det_conf) if conf >= 0.15]
        top_conf = det_conf[top_indices]
        top_label_indices = det_label[top_indices].tolist()
        top_labels = get_labelname(labelmap, top_label_indices)
        top_xmin = det_xmin[top_indices]
        top_ymin = det_ymin[top_indices]
        top_xmax = det_xmax[top_indices]
        top_ymax = det_ymax[top_indices]   

        #colors = plt.cm.hsv(np.linspace(0, 1, 21)).tolist()
     
        #img = Image.open(img_dir + "%06d.jpg"%(img_idx))
        img = Image.open(img_file)
        draw = ImageDraw.Draw(img)       
        for i in xrange(top_conf.shape[0]):
            xmin = top_xmin[i] * image.shape[1]
            ymin = top_ymin[i] * image.shape[0]
            xmax = top_xmax[i] * image.shape[1]
            ymax = top_ymax[i] * image.shape[0]
            
            h = float(ymax - ymin)
            w = float(xmax - xmin)
            #if (w==0) or (h==0):
            #   continue
            #if (h/w >=2)and((xmin<10)or(xmax > 1230)):
            #   continue
            
            score = top_conf[i]
            label_num = top_label_indices[i]
            if score > 0.3:
                draw.line(((xmin,ymin),(xmin,ymax),(xmax,ymax),(xmax,ymin),(xmin,ymin)),fill=(0,255,0))
                draw.text((xmin,ymin),'%s%.2f'%(top_labels[i], score),fill=(255,255,255))
            #elif score > 0.02:
            #    draw.line(((xmin,ymin),(xmin,ymax),(xmax,ymax),(xmax,ymin),(xmin,ymin)),fill=(255,0,255))
            #    draw.text((xmin,ymin),'%.2f'%(score),fill=(255,255,255))
            
        #img.save(save_dir+"%06d.jpg"%(img_idx))
        img.save(save_dir+im_name)  

  • 而後就會在定義的路徑下產生識別好的圖片:

相關文章
相關標籤/搜索