用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原作連結