1. 程式人生 > >用SSD框架訓練自己的資料集

用SSD框架訓練自己的資料集

一份非常簡單易懂的SSD tensorflow訓練教程,初學者也可以看得懂。當初自學的時候走了很多彎路,所以把自己的經驗和踩過的坑分享給大家,如果文中有什麼寫的不對的地方歡迎大家在評論區和我討論。

https://blog.csdn.net/Echo_Harrington/article/details/81131441原作連結

零、前期準備

安裝Tensorflow-GPU版本。

 

一、SSD框架準備

從https://github.com/balancap/SSD-Tensorflow 下載、解壓。

解壓checkpoints資料夾中的zip檔案。

 

二、準備自己的資料集

在SSD-Tensorflow-master中建立資料夾: VOC2007,VOCtest

在VOC2007和VOCtest中分別建立:test

在兩個test資料夾中分別中建立:Annotations,ImageSets,JPEGImages

其中VOC2007中的Annotations和JPEGImages分別裝載訓練的標籤檔案(.xml)和圖片檔案(.jpg或.jpeg),VOCtes中則裝載相對應的測試檔案。

可以通過xml_to_txt.py和txt_to_xml.py將label檔案轉換為txt或xml,python檔案下載地址為:

連結: https://pan.baidu.com/s/1H0vOGef6oqrkvty0NHVGgg 密碼: tnxm

 

三、修改程式碼

(1)SSD-Tensorflow-mastet/datasets/pascalvoc_common.py檔案中,24-46行,第一類none不要動,其他的類修改為自己資料集的類。

(2)SSD-Tensorflow-master/datasets/pascalvoc_to_tfrecords.py檔案中,82行,格式改為相對應的‘.jpg’或‘.jpeg’,83行‘r’改為‘rb’。

67行,修改SAMPLES_PER_FILES為幾張圖片轉為一個tfrecord檔案

(3)SSD-Tensorflow-master/nets/ssd_vgg_300.py檔案中,96-97行的num_classes和no_annotation_label改為“類別數+1”

(4)SSD-Tensorflow-master/eval_ssd_network.py 檔案中,66行,修改num_classes為“類別數+1”

(5) SSD-Tensorflow-master/datasets/pascalvoc_2007.py檔案中,第31行和55行的none類不要動,其他類修改為自己資料集的類,其中括號內的第一個數為圖片數,第二個數為目標數(也就是bonding box數,關於如何計算目標數,我寫了一個python檔案,在附錄中),52行和76行的total是所有類的總和。

79和80行的數改為自己資料集的訓練和測試集總數,86行的NUM_CLASSES改為自己的類別數(不用加一)

(6)SSD-Tensorflow-master/train_ssd_network.py檔案中,修改135行的num_classes為“類別數+1

修改154行的‘None’為最大訓練步數(如50000),設為None時訓練會一直進行,需要手動停止。同時還可以修改此檔案中的batch size,leaning rate等等……

 

四、生成tfrecord檔案

(1)訓練的tfrecord。在SSD-Tensorflow-master中建立tf_convert_data.sh檔案,寫入下面的指令。在終端中用bash tf_convert_data.sh 執行。

DATASET_DIR=./VOC2007/test/
OUTPUT_DIR=./tfrecords
python tf_convert_data.py \
    --dataset_name=pascalvoc \
    --dataset_dir=${DATASET_DIR} \
    --output_name=voc_2007_train \
    --output_dir=${OUTPUT_DIR}

(2)測試的tfrecord。在SSD-Tensorflow-master中建立資料夾:tfrecords_test。修改上述程式碼為:

DATASET_DIR=./VOCtest/test/           #路徑改為測試檔案路徑
OUTPUT_DIR=./tfrecords_test		#修改路徑
python tf_convert_data.py \
    --dataset_name=pascalvoc \
    --dataset_dir=${DATASET_DIR} \
    --output_name=voc_2007_test \	  #注意這裡修改為test
    --output_dir=${OUTPUT_DIR}

 

五、訓練

在SSD-Tensorflow-master中建立資料夾log。在SSD-Tensorflow-master中建立train.sh檔案,寫入下面的指令。其中的數值都可以根據自己的資料集來修改。

DATASET_DIR=./tfrecords
TRAIN_DIR=./log/
CHECKPOINT_PATH=./checkpoints/ssd_300_vgg.ckpt/ssd_300_vgg.ckpt
python train_ssd_network.py \
    --train_dir=${TRAIN_DIR} \
    --dataset_dir=${DATASET_DIR} \
    --dataset_name=pascalvoc_2007 \
    --dataset_split_name=train \
    --model_name=ssd_300_vgg \
    --checkpoint_path=${CHECKPOINT_PATH} \
    --save_summaries_secs=60 \
    --save_interval_secs=600 \
    --weight_decay=0.0005 \
    --optimizer=adam \
    --learning_rate=0.001 \
    --batch_size=6 \
    --ignore_missing_vars = True \
    --num_classes = 21 \ #修改為自己的類別數+1
    --checkpoint_exclude_scopes =ssd_300_vgg/conv6,ssd_300_vgg/conv7,ssd_300_vgg/block8,ssd_300_vgg/block9,ssd_300_vgg/block10,ssd_300_vgg/block11,ssd_300_vgg/block4_box,ssd_300_vgg/block7_box,ssd_300_vgg/block8_box,ssd_300_vgg/block9_box,ssd_300_vgg/block10_box,ssd_300_vgg/block11_box \

最重要的是加上最後一行checkpoint_exclude_scopes。有人在訓練中出現如下的報錯:

InvalidArgumentError: Assign requires shapes of both tensors to match. lhs shape= [8] rhs shape= [84] 
[[Node: save_1/Assign_4 = Assign[T=DT_FLOAT, _class=["loc:@ssd_300_vgg/block10_box/conv_cls/biases"], use_locking=true, validate_shape=true, _device="/job:localhost/replica:0/task:0/device:CPU:0"](ssd_300_vgg/block10_box/conv_cls/biases, save_1/RestoreV2_4)]]

其中lhs的shape是自己的類別數2*4bounding box的值,rhs shape是checkpoint中的類21*4 bounding box的值。原因就在於這個已經存在的checkpoints是用21個類別來訓練的,所以我們要做的就是加上一行checkpoint_exclude_scopes,其目的是捨棄了一些層,我們可以只用這個原始結構中的weights來訓練。另一個很關鍵的因素是要使用Tensorflow-gpu版本而不是cpu版本來訓練。

 

六、測試

在SSD-Tensorflow-master中建立資料夾ssd_eval_log。在SSD-Tensorflow-master中建立test.sh檔案,寫入指令:

DATASET_DIR= ./test_tfrecords  		    #修改路徑為test的tfrecord資料夾
EVAL_DIR=./ssd_eval_log/     			#修改路徑
CHECKPOINT_PATH=./log/model.ckpt-0      #改成自己訓練的模型
python3 ./eval_ssd_network.py \
        --eval_dir=${EVAL_DIR} \
        --dataset_dir=${DATASET_DIR} \
        --dataset_name=pascalvoc_2007 \
        --dataset_split_name=test \			#這裡改為test
        --model_name=ssd_300_vgg \
        --checkpoint_path=${CHECKPOINT_PATH} \
        --batch_size=1 \
	--ignore_missing_vars = True \
        --num_classes = 21 \ 
        --checkpoint_exclude_scopes =ssd_300_vgg/conv6,ssd_300_vgg/conv7,ssd_300_vgg/block8,ssd_300_vgg/block9,ssd_300_vgg/block10,ssd_300_vgg/block11,ssd_300_vgg/block4_box,ssd_300_vgg/block7_box,ssd_300_vgg/block8_box,ssd_300_vgg/block9_box,ssd_300_vgg/block10_box,ssd_300_vgg/block11_box \

這時可能會遇到如下報錯:

TypeError: Can not convert a tuple into a Tensor or Operation

這時,需要在SSD-Tensorflow-master/eval_ssd_network.py檔案中加入程式碼:

def flatten(x):
    result = []
    for el in x:
         if isinstance(el, tuple):
               result.extend(flatten(el))
         else:
               result.append(el)
return result

並把318行和337行改為:

eval_op=flatten(list(names_to_updates.values())),

測試完後終端會顯示如下資訊,其中mAP(mean average precision)就是測試的準確率。

 

七、顯示訓練測試結果

在terminal中執行指令jupyter notebook notebooks/ssd_notebook.ipynb,終端會出現如下資訊,把網址複製進瀏覽器開啟,出現ssd_notebook介面,點選右上角的trusted按鈕。

把ckpt_filename 改為自己訓練模型的路徑:

把path改為自己測試的image的路徑,image_names是包含整個資料夾的圖片的list,-4代表檢視的是資料夾中倒數第四張圖片,修改這個數值可以選擇想要檢視的第幾張圖片:

最後顯示的結果如下:

 

八、附錄

(1)計算一種類別有多少個bounding box,程式碼如下:

import re
import os
class1 = 'cat'		#改為自己的類
class2 = 'dog'
annotation_folder = './VOC2007/test/Annotations'		#改為自己標籤資料夾的路徑
list = os.listdir(annotation_folder)
total_number1 = 0
total_number2 = 0

for i in range(0, len(list)):
	path = os.path.join(annotation_folder,list[i])
	annotation_file = open(annotation_folder + '/' + os.path.basename(path)).read()
	current_number1 = len(re.findall(class1, annotation_file))
	current_number2 = len(re.findall(class2, annotation_file))
	total_number1 += current_number1
	total_number2 += current_number2

print total_number1
print total_number2

(2)用SSD-Tensorflow訓練黑白圖片(單通道圖片)

我這次訓的dataset是熱成像圖片,也就是黑白圖片。然而ssd只能訓練彩色圖片(三通道圖片),所以在最後Jupiter notebook上顯示訓練結果時會報錯:

Cannot feed value of shape (240, 3) for Tensor u'Placeholder:0', which has shape ‘(?, ?, 3)’

解決的方法是,先用MATLAB把單通道影象轉為三通道的圖片,然後再把標籤檔案(.xml)中的<depth>改為3,.m檔案程式碼如下:

path = 'C:/Users/Administrator/Desktop/train300/';  #改為自己的單通道影象路徑
save_path = 'C:/Users/Administrator/Desktop/train/';  #改為儲存的路徑
img=dir([path,'*.jpg']);

for i=1:length(img)
    I=imread([path,img(i).name]);
    [rows,cols]=size(I);
    r=zeros(rows,cols);
    g=zeros(rows,cols);
    b=zeros(rows,cols);
    r=double(I);
    g=double(I);
    b=double(I);
    rgb=cat(3,r,g,b);
    imwrite(uint8(rgb),[save_path,img(i).name]);
end

(3)GPU記憶體問題

訓練的時候曾經出現瞭如下的error:

2018-07-19 16:43:02.811877: E tensorflow/stream_executor/cuda/cuda_dnn.cc:385] could not create cudnn handle: CUDNN_STATUS_INTERNAL_ERROR
2018-07-19 16:43:02.811920: F tensorflow/core/kernels/conv_ops.cc:717] Check failed: stream->parent()->GetConvolveAlgorithms( conv_parameters.ShouldIncludeWinogradNonfusedAlgo<T>(), &algorithms)

原本以為是cuda的問題,後來發現是因為和另一個同學同時在訓練,所以GPU記憶體滿了(終端輸入watch -n 0.1 nvidia-smi 可檢視GPU使用資訊)。在train_ssd_network.py檔案的開頭加上如下程式碼,成功解決問題。

import os
os.environ["CUDA_VISIBLE_DEVICES"] = '1' #後面的數字改為需要進行訓練的顯示卡編號

 

九、參考

https://blog.csdn.net/w5688414/article/details/78529884

https://blog.csdn.net/liuyan20062010/article/details/78905517

https://www.codetd.com/article/2139154

一份非常簡單易懂的SSD tensorflow訓練教程,初學者也可以看得懂。當初自學的時候走了很多彎路,所以把自己的經驗和踩過的坑分享給大家,如果文中有什麼寫的不對的地方歡迎大家在評論區和我討論。

https://blog.csdn.net/Echo_Harrington/article/details/81131441原作連結

零、前期準備