一步一步带你训练自己的SSD检测算法
目錄
- 一、前言
- 二、實(shí)現(xiàn)細(xì)節(jié)
- 1、前提條件
- 2、數(shù)據(jù)標(biāo)注
- 2.1 Labelme
- 2.1.1 工具特點(diǎn)簡(jiǎn)介
- 2.1.2 工具安裝
- 2.1.3 工具使用簡(jiǎn)介
- 2.2 LabelImg
- 2.2.1 工具安裝
- 2.2.2 工具使用簡(jiǎn)介
- 3、標(biāo)簽預(yù)處理
- 3.1 PASCAL VOC數(shù)據(jù)集格式詳解
- 3.2 構(gòu)造新的PASCAL VOC數(shù)據(jù)集
- 3.3 COCO數(shù)據(jù)集格式詳解
- 3.4 構(gòu)造新的COCO數(shù)據(jù)集
- 4、搭建SSD運(yùn)行環(huán)境
- 5、修改代碼訓(xùn)練網(wǎng)絡(luò)
- 5.1 代碼架構(gòu)詳解
- 5.2 修改網(wǎng)絡(luò)配置參數(shù)
- 5.3 修改VOC類別參數(shù)
- 5.4 下載模型
- 5.5 訓(xùn)練模型
- 5.6 Loss和Accuracy可視化
- 5.7 模型測(cè)試和效果分析
- 6、SSD性能改進(jìn)
- 7、Pytorch模型加速
- 三、總結(jié)
- 參考資料
- 注意事項(xiàng)
一、前言
??隨著深度學(xué)習(xí)的快速發(fā)展,越來越多的場(chǎng)景都開始應(yīng)用這么技術(shù),但是當(dāng)前的深度學(xué)習(xí)還是具有一些門檻,厲害的大佬們都在設(shè)計(jì)各種各樣的網(wǎng)絡(luò),發(fā)各種各樣的頂會(huì)論文,他們關(guān)注的是算法的設(shè)計(jì)。然而在現(xiàn)實(shí)場(chǎng)景中,對(duì)于很多人而言,他們更多的是關(guān)注如何去使用這個(gè)算法,比如我如何快速的使用現(xiàn)有的工具訓(xùn)練一個(gè)滿足自己需求的模型,當(dāng)前網(wǎng)上淺顯易懂的帶小白構(gòu)建自己的模型的教程太少了,我決定來彌補(bǔ)一下這個(gè)空缺,為自己為大家做一點(diǎn)點(diǎn)小貢獻(xiàn)。本文專注于使用SSD網(wǎng)絡(luò)從頭訓(xùn)練自己的數(shù)據(jù)集,我們?cè)敿?xì)講解里面的細(xì)節(jié),具體的內(nèi)容如下所示感興趣的小伙伴可以掃描下方的二維碼加群,我們可以一起交流更多的問題。
二、實(shí)現(xiàn)細(xì)節(jié)
1、前提條件
??在執(zhí)行下面的操作之前,請(qǐng)保證你當(dāng)前已經(jīng)具有以下的這些環(huán)境。
- Ubuntu 16.04系統(tǒng)
- NVIDIA 顯卡,建議不低于GTX 1080
- CUDA驅(qū)動(dòng),CUDA8.0\CUDA9.0\CUDA10.0均可,建議設(shè)置為可以隨時(shí)切換的模式
- Anaconda3
2、數(shù)據(jù)標(biāo)注
??對(duì)于一個(gè)完整的深度學(xué)習(xí)流程而言,數(shù)據(jù)標(biāo)注是必不可少的一環(huán),數(shù)據(jù)標(biāo)注質(zhì)量會(huì)極大的影響算法的性能。當(dāng)前如果你使用別人已經(jīng)標(biāo)注好的數(shù)據(jù)或者公有的數(shù)據(jù)集,請(qǐng)直接跳過該步驟。
??下面就來簡(jiǎn)單的介紹幾種數(shù)據(jù)標(biāo)注工具,當(dāng)前已經(jīng)有很多開源的數(shù)據(jù)標(biāo)注工具,這些工具基本上可以滿足我們98%的需求,工具沒有好壞之分,合適自己的工具就是好工具,所以大家需要根據(jù)自己的情況去選擇合適的標(biāo)注工具。
2.1 Labelme
Github鏈接
2.1.1 工具特點(diǎn)簡(jiǎn)介
- 同時(shí)支持Ubuntu / macOS / Windows系統(tǒng)
- 可以使用多邊形、矩形、圓、線和點(diǎn)進(jìn)行圖像注釋
- 支持VOC和COCO數(shù)據(jù)格式
- 支持分類、檢測(cè)和分割等多個(gè)計(jì)算機(jī)視覺任務(wù)
2.1.2 工具安裝
方案1:
conda create --name=labelme python=3.6 source activate labelme pip install labelme方案2:
sudo apt-get install python3-pyqt5 # PyQt5 sudo pip3 install labelme2.1.3 工具使用簡(jiǎn)介
??上圖展示了Labelme工具的標(biāo)注界面,工具的使用留給大家自己去熟悉,在這里我只會(huì)強(qiáng)調(diào)幾個(gè)需要注意的地方,具體的內(nèi)容如下所示:
- 建議把需要標(biāo)注的數(shù)據(jù)集存放在一個(gè)文件夾下面,并以相應(yīng)的名字來命名
- 該工具默認(rèn)的標(biāo)注方式是多邊形,如果進(jìn)行目標(biāo)檢測(cè)任務(wù),通過Edit->Create Rectangle來切換
- 在標(biāo)注的過程中,盡量做到精細(xì)化的標(biāo)注,盡量先將目標(biāo)放大,再執(zhí)行精細(xì)標(biāo)注,標(biāo)注的質(zhì)量會(huì)對(duì)極大的影響檢測(cè)算法的精度
- 該工具默認(rèn)會(huì)生成.json格式的標(biāo)簽并和標(biāo)注圖片存放在同一個(gè)目錄,.json格式的標(biāo)簽文件適合用來構(gòu)建COCO數(shù)據(jù)集格式,而.xml格式的標(biāo)簽文件適合用來構(gòu)建VOC數(shù)據(jù)集格式,
2.2 LabelImg
Github鏈接
2.2.1 工具安裝
方案1:
pip3 install labelImg labelImg labelImg [IMAGE_PATH] [PRE-DEFINED CLASS FILE]方案2:
git clone https://github.com/tzutalin/labelImg.git sudo apt-get install pyqt5-dev-tools sudo pip3 install -r requirements/requirements-linux-python3.txt make qt5py3 python3 labelImg.py python3 labelImg.py [IMAGE_PATH] [PRE-DEFINED CLASS FILE]2.2.2 工具使用簡(jiǎn)介
??上圖展示了LabelImg工具的標(biāo)注界面,工具的使用留給大家自己去熟悉,在這里我只會(huì)強(qiáng)調(diào)幾個(gè)需要注意的地方,具體的內(nèi)容如下所示:
- 該工具默認(rèn)生成的標(biāo)簽格式為.xml,便于構(gòu)造成VOC數(shù)據(jù)集的格式
- 該工具可以將以前使用的標(biāo)簽保存起來,并將其設(shè)置為默認(rèn)標(biāo)簽,這個(gè)單類目標(biāo)檢測(cè)任務(wù)中能夠極大的節(jié)約你的時(shí)間
??總而言之,通過使用上面兩個(gè)工具對(duì)我們自己的數(shù)據(jù)集進(jìn)行標(biāo)注之后,我們最終會(huì)獲得一些.json或者.xml格式的標(biāo)簽文件。當(dāng)然你也可以使用其它的工具進(jìn)行標(biāo)注,具體的細(xì)節(jié)請(qǐng)看該鏈接。
3、標(biāo)簽預(yù)處理
??通過標(biāo)注工具我們已經(jīng)獲得了我們需要的標(biāo)簽文件,那么我們?yōu)槭裁催€需要標(biāo)簽預(yù)處理操作呢?其實(shí)原因是這樣的,在計(jì)算機(jī)視覺界,你可能都會(huì)或多或少的聽說過PASCAL VOC和COCO數(shù)據(jù)集吧,由于這兩個(gè)數(shù)據(jù)集得到了大量的應(yīng)用,學(xué)者們已經(jīng)針對(duì)這兩個(gè)數(shù)據(jù)集寫了很好有用的代碼,比如如何快速的加載圖片和標(biāo)簽文件等,那么,對(duì)于一個(gè)新手來說,能夠復(fù)用大佬們的代碼是一個(gè)很明智的選擇,這樣你不僅不需要去寫冗長(zhǎng)的加載和處理代碼,而且還可以極大的加快你的開發(fā)進(jìn)程,這就是對(duì)生成的標(biāo)簽進(jìn)行預(yù)處理的目的。簡(jiǎn)而言之,所謂的標(biāo)簽預(yù)處理即是將自己的圖片和標(biāo)簽文件調(diào)整為PASCAL VOC和COCO數(shù)據(jù)集的格式,這樣我們只需要更換數(shù)據(jù)集,就可以很塊的完成數(shù)據(jù)的加載和解析工作。
3.1 PASCAL VOC數(shù)據(jù)集格式詳解
datasets |__ VOC2007|_ JPEGImages|_ Annotations|_ .xml|_ ImageSets|_ Main|_ train.txt/val.txt/trainval.txt/test.txt|_ SegmentationClass |__ VOC2012|_ JPEGImages|_ .jpg|_ Annotations|_ ImageSets|_ SegmentationClass |__ ...1、Annotations文件夾中存放使用標(biāo)注工具獲得的.xml標(biāo)簽文件; 2、ImageSets/Main文件夾中存放著訓(xùn)練集\驗(yàn)證集\訓(xùn)練驗(yàn)證集\測(cè)試集圖片的名稱,后續(xù)代碼訓(xùn)練的圖片從這些文件中讀取; 3、JPEGImages文件夾中存放的是原始的訓(xùn)練和驗(yàn)證圖片,驗(yàn)證圖片是隨機(jī)的從訓(xùn)練集中獲得的; 4、SegmentationClass和SegmentationObject用來存放目標(biāo)分割的信息,由于當(dāng)前的任務(wù)僅僅是一個(gè)目標(biāo)檢測(cè)的任務(wù),因而不需要在這兩個(gè)文件夾下面存放任何文件;3.2 構(gòu)造新的PASCAL VOC數(shù)據(jù)集
# 切換到自己的想要存放數(shù)據(jù)集的目錄,xxx代表自己的具體目錄 cd xxx #########################1、創(chuàng)建數(shù)據(jù)集包含的相關(guān)文件夾######################### mkdir datasets cd datasets mkdir Annotations mkdir ImageSets mkdir JPEGImages mkdir SegmentationClass mkdir SegmentationObject cd ImageSets mkdir Main #########################2、移動(dòng)圖片和標(biāo)簽到相應(yīng)的文件夾####################### cd xxx # xxx表示你的圖片路徑 mv * JPEGImages # 將圖片(.jpg/.png/.bmp等)移動(dòng)到JPEGImages文件夾中 cd xxx # xxx表示.xml文件所在的路徑 mv * Annotations # 將標(biāo)簽文件(.xml)移動(dòng)到Annotations文件夾中 #########################3、生成Main文件夾下面的.txt文件####################### # coding=utf-8 import os import random# 用來劃分訓(xùn)練集合驗(yàn)證集的比例 trainval_percent = 0.95 train_percent = 0.85 # 設(shè)置輸入的.xml和輸出的.txt文件的路徑 xmlfilepath = './datasets/VOC2007/Annotations' txtsavepath = '.datasets/VOC2007/ImageSets/Main' total_xml = os.listdir(xmlfilepath)num=len(total_xml) list=range(num) tv=int(num*trainval_percent) tr=int(tv*train_percent) trainval= random.sample(list,tv) train=random.sample(trainval,tr)# 打開相應(yīng)的文件,準(zhǔn)備存儲(chǔ)內(nèi)容 ftrainval = open('./datasets/VOC2007/ImageSets/Main/trainval.txt', 'w') ftest = open('./datasets/VOC2007/ImageSets/Main/test.txt', 'w') ftrain = open('./datasets/VOC2007/ImageSets/Main/train.txt', 'w') fval = open('./datasets/VOC2007/ImageSets/Main/val.txt', 'w')# 遍歷所有文件進(jìn)行寫文件 for i in list:name=total_xml[i][:-4]+'\n'if i in trainval:ftrainval.write(name)if i in train:ftrain.write(name)else:fval.write(name)else:ftest.write(name)# 寫完之后關(guān)閉文件 ftrainval.close() ftrain.close() fval.close() ftest .close()??如果你是使用Labelme生成的.json格式的標(biāo)簽文件,你想要使用VOC格式訓(xùn)練網(wǎng)絡(luò),那么你需要使用下面這個(gè)小工具將.json格式轉(zhuǎn)換為.xml格式
#########################4、對(duì).json格式的標(biāo)簽文件進(jìn)行處理####################### # coding=utf-8 import os import numpy as np import codecs import json from glob import glob import cv2 import shutil from sklearn.model_selection import train_test_split #1.標(biāo)簽路徑 labelme_path = "./xxx/" # 原始xxx標(biāo)注數(shù)據(jù)路徑,需要更換成自己的數(shù)據(jù)集名稱 saved_path = "./datasets/VOC2007/" # 保存路徑#2.創(chuàng)建要求文件夾 if not os.path.exists(saved_path + "Annotations"):os.makedirs(saved_path + "Annotations") if not os.path.exists(saved_path + "JPEGImages/"):os.makedirs(saved_path + "JPEGImages/") if not os.path.exists(saved_path + "ImageSets/Main/"):os.makedirs(saved_path + "ImageSets/Main/")#3.獲取待處理文件 files = glob(labelme_path + "*.json") files = [i.split("/")[-1].split(".json")[0] for i in files]#4.讀取標(biāo)注信息并寫入 xml for json_file_ in files:json_filename = labelme_path + json_file_ + ".json"json_file = json.load(open(json_filename,"r",encoding="utf-8"))height, width, channels = cv2.imread(labelme_path + json_file_ +".jpg").shapewith codecs.open(saved_path + "Annotations/"+json_file_ + ".xml","w","utf-8") as xml:xml.write('<annotation>\n')xml.write('\t<folder>' + 'UAV_data' + '</folder>\n')xml.write('\t<filename>' + json_file_ + ".jpg" + '</filename>\n')xml.write('\t<source>\n')xml.write('\t\t<database>The Defect Detection</database>\n')xml.write('\t\t<annotation>Defect Detection</annotation>\n')xml.write('\t\t<image>flickr</image>\n')xml.write('\t\t<flickrid>NULL</flickrid>\n')xml.write('\t</source>\n')xml.write('\t<owner>\n')xml.write('\t\t<flickrid>NULL</flickrid>\n')xml.write('\t\t<name>WZZ</name>\n')xml.write('\t</owner>\n')xml.write('\t<size>\n')xml.write('\t\t<width>'+ str(width) + '</width>\n')xml.write('\t\t<height>'+ str(height) + '</height>\n')xml.write('\t\t<depth>' + str(channels) + '</depth>\n')xml.write('\t</size>\n')xml.write('\t\t<segmented>0</segmented>\n')for multi in json_file["shapes"]:points = np.array(multi["points"])xmin = min(points[:,0])xmax = max(points[:,0])ymin = min(points[:,1])ymax = max(points[:,1])label = multi["label"]if xmax <= xmin:passelif ymax <= ymin:passelse:xml.write('\t<object>\n')xml.write('\t\t<name>'+json_file["shapes"][0]["label"]+'</name>\n')xml.write('\t\t<pose>Unspecified</pose>\n')xml.write('\t\t<truncated>1</truncated>\n')xml.write('\t\t<difficult>0</difficult>\n')xml.write('\t\t<bndbox>\n')xml.write('\t\t\t<xmin>' + str(xmin) + '</xmin>\n')xml.write('\t\t\t<ymin>' + str(ymin) + '</ymin>\n')xml.write('\t\t\t<xmax>' + str(xmax) + '</xmax>\n')xml.write('\t\t\t<ymax>' + str(ymax) + '</ymax>\n')xml.write('\t\t</bndbox>\n')xml.write('\t</object>\n')print(json_filename,xmin,ymin,xmax,ymax,label)xml.write('</annotation>')#5.復(fù)制圖片到 VOC2007/JPEGImages/下 image_files = glob(labelme_path + "*.jpg") print("copy image files to VOC007/JPEGImages/") for image in image_files:shutil.copy(image,saved_path +"JPEGImages/")#6.split files for txt txtsavepath = saved_path + "ImageSets/Main/" ftrainval = open(txtsavepath+'/trainval.txt', 'w') ftest = open(txtsavepath+'/test.txt', 'w') ftrain = open(txtsavepath+'/train.txt', 'w') fval = open(txtsavepath+'/val.txt', 'w') total_files = glob("./VOC2007/Annotations/*.xml") total_files = [i.split("/")[-1].split(".xml")[0] for i in total_files] test_filepath = "./test" for file in total_files:ftrainval.write(file + "\n") #test for file in os.listdir(test_filepath):ftest.write(file.split(".jpg")[0] + "\n") #split train_files,val_files = train_test_split(total_files,test_size=0.15,random_state=42) #train for file in train_files:ftrain.write(file + "\n") #val for file in val_files:fval.write(file + "\n")ftrainval.close() ftrain.close() fval.close() ftest.close()3.3 COCO數(shù)據(jù)集格式詳解
COCO_ROOT |__ annotations|_ instances_valminusminival2014.json|_ instances_minival2014.json|_ instances_train2014.json|_ instances_val2014.json|_ ... |__ train2014|_ <im-1-name>.jpg|_ ...|_ <im-N-name>.jpg |__ val2014|_ <im-1-name>.jpg|_ ...|_ <im-N-name>.jpg |__ ...1、COCO_ROOT表示自己的數(shù)據(jù)集所在的文件夾的名稱; 2、annotations文件夾用來存放標(biāo)簽文件,instances_train2014.json和instances_val2014.json分別表示訓(xùn)練集和驗(yàn)證集的標(biāo)簽; 3、train2014和val2014文件夾分別用來存儲(chǔ)訓(xùn)練集和驗(yàn)證集的圖片,命名格式為im-N-name.jpg的格式;3.4 構(gòu)造新的COCO數(shù)據(jù)集
??本文更傾向于生成VOC格式的數(shù)據(jù)集,如果你需要生成COCO格式的數(shù)據(jù),請(qǐng)參考博文1和博文2。
4、搭建SSD運(yùn)行環(huán)境
# 克隆一份SSD代碼到本地 git clone https://github.com/lufficc/SSD.git cd SSD # 創(chuàng)建conda虛擬環(huán)境 conda create -n ssd python=3.6 # 激活虛擬環(huán)境 conda activate ssd # 安裝相應(yīng)的python依賴包 pip install torch torchvision yacs tqdm opencv-python vizer pip install tensorboardX# 克隆一份cocoapi到本地 git clone https://github.com/cocodataset/cocoapi.git # 切換到PythonAPI路徑并安裝 cd cocoapi/PythonAPI python setup.py build_ext install # 切換到ext文件夾 cd ext # 編譯NMS等操作,用來加速網(wǎng)絡(luò)的訓(xùn)練過程 python build.py build_ext develop5、修改代碼訓(xùn)練網(wǎng)絡(luò)
5.1 代碼架構(gòu)詳解
configs-該文件夾下面存放的是不同的網(wǎng)絡(luò)配置文件;mobilenet_v2_ssd320_voc0712.yaml-表示mobilenet backbone的配置文件;vgg_ssd512_voc0712.yaml-表示vgg backbone的配置文件; datasets-該文件夾存放數(shù)據(jù)文件,具體的結(jié)果看上文; demo-該文件夾存放一些測(cè)試圖片; ext-該文件夾存放CPU和GPU版本的NMS代碼; figures-該文件夾存放網(wǎng)絡(luò)訓(xùn)練過程中的一些可視化文件; outputs-該文件夾存放訓(xùn)練過程中的log文件和模型文件(.pth); demo.py-該文件用來測(cè)試新的圖片并輸出檢測(cè)結(jié)果; train.py-該文件用來訓(xùn)練網(wǎng)絡(luò); test.py-該文件用來測(cè)試網(wǎng)絡(luò); ssd-該文件夾中存放ssd算法的實(shí)現(xiàn)代碼;config-該文件夾存放著默認(rèn)的配置文件;data-該文件夾中存放著基本的數(shù)據(jù)預(yù)處理源文件,包括voc和coco數(shù)據(jù)集;engine-該文件夾中存放著基本的訓(xùn)練和推理引擎的實(shí)現(xiàn)源碼;layers-該文件夾中存放著separable_conv的實(shí)現(xiàn)源碼;modeling-該文件夾中存放著SSD網(wǎng)絡(luò)的Backbone和head網(wǎng)絡(luò)的源碼;solver-該文件中存放著SSD網(wǎng)絡(luò)中使用到的優(yōu)化器的源碼;structures-該文件夾中存放著一些Box的help函數(shù)的源碼;utils-該文件夾中存放著一些訓(xùn)練和推理SSD算法的小工具的源碼:5.2 修改網(wǎng)絡(luò)配置參數(shù)
mv datasets SSD # 將構(gòu)造好的VOC數(shù)據(jù)集移動(dòng)到SSD文件夾中 cd configs # 切換到配置文件夾下面 vim vgg_ssd512_voc0712.yaml # 使用vim打開配置文件注:
1、vgg_ssd512_voc0712.yaml表示使用VGG作為基準(zhǔn)網(wǎng)絡(luò),SSD網(wǎng)絡(luò)的輸入大小為512x512 ,使用VOC2007格式的數(shù)據(jù)集來訓(xùn)練新的網(wǎng)絡(luò),大量的實(shí)驗(yàn)結(jié)果表明該配置參數(shù)下能夠獲得最好的檢測(cè)效果。
2、vgg_ssd300_voc0712.yaml表示使用VGG作為基準(zhǔn)網(wǎng)絡(luò),SSD網(wǎng)絡(luò)的輸入大小為300x300 ,使用VOC2007格式的數(shù)據(jù)集來訓(xùn)練新的網(wǎng)絡(luò),由于輸入圖片的分辨率由512減小到300,整個(gè)網(wǎng)絡(luò)的訓(xùn)練速度得到了提升,但是效果不如vgg_ssd512_voc0712.yaml架構(gòu)。
3、mobilenet_v2_ssd320_voc0712.yaml表示使用Mobilenet_v2作為基準(zhǔn)網(wǎng)絡(luò),SSD網(wǎng)絡(luò)的輸入大小為300x300 ,使用VOC2007格式的數(shù)據(jù)集來訓(xùn)練新的網(wǎng)絡(luò),由于使用Mobilenet_v2作為基準(zhǔn)網(wǎng)絡(luò),因此該架構(gòu)下面的訓(xùn)練速度最快,但是最終獲得精度也是最差的,這個(gè)架構(gòu)適合于一些簡(jiǎn)單的檢測(cè)任務(wù),Mobilenet_v2不能能夠獲得較好的檢測(cè)效果,而且可以獲得接近實(shí)時(shí)的推理速度。
注:
1、NUM_CLASSES參數(shù)表示檢測(cè)的類型,由于當(dāng)前的缺陷數(shù)據(jù)集的類別數(shù)目為17,另外加上一個(gè)__background__類,因此需要將該數(shù)值修改為17+1=18;
2、BATCH_SIZE參數(shù)表示每次訓(xùn)練的圖片的個(gè)數(shù),由于訓(xùn)練的圖片需要加載到內(nèi)存中,該數(shù)值和你當(dāng)前使用的顯卡的內(nèi)容有著密切的關(guān)系,我根據(jù)自己的顯卡(Tesla T4)將BATCH_SIZE調(diào)整為12;
3、LR參數(shù)表示網(wǎng)絡(luò)訓(xùn)練時(shí)的學(xué)習(xí)率,由于我設(shè)置的迭代次數(shù)比較多,害怕網(wǎng)絡(luò)跳過最優(yōu)值,因而我將LR調(diào)整為1e-4;
4、TRAIN參數(shù)表示網(wǎng)絡(luò)訓(xùn)練時(shí)使用的數(shù)據(jù)集,由于我們新建了一個(gè)類似于VOC2007的數(shù)據(jù)集,因而我刪除了voc_2012_trainval;
5、其它的這些配置參數(shù),你可以根據(jù)自己的需要進(jìn)行修改,但是我建議你不用修改。
5.3 修改VOC類別參數(shù)
cd .ssd/data/datasets # 1、切換到datasets文件夾 vim voc.py # 2、打開voc文件 ###############################修改前的類別############################### class_names = ('__background__','aeroplane', 'bicycle', 'bird', 'boat','bottle', 'bus', 'car', 'cat', 'chair','cow', 'diningtable', 'dog', 'horse','motorbike', 'person', 'pottedplant','sheep', 'sofa', 'train', 'tvmonitor') ###############################修改后的類別###############################class_names = ('_background_', 'Buttercup', 'Colts Foot', 'Daffodil', 'Daisy', 'Dandelion', 'Fritillary', 'Iris', 'Pansy', 'Sunflower', 'Windflower', 'Snowdrop', 'LilyValley','Bluebell', 'Crocus', 'Tigerlily', 'Tulip', 'Cowslip')5.4 下載模型
# 下載vgg_ssd300_voc0712.pth預(yù)訓(xùn)練模型 wget https://github.com/lufficc/SSD/releases/download/1.2/vgg_ssd300_voc0712.pth # 下載vgg_ssd512_voc0712.pth預(yù)訓(xùn)練模型 wget https://github.com/lufficc/SSD/releases/download/1.2/vgg_ssd512_voc0712.pth # 下載mobilenet_v2_ssd320_voc0712.pth預(yù)訓(xùn)練模型 wget https://github.com/lufficc/SSD/releases/download/1.2/mobilenet_v2_ssd320_voc0712.pth # 創(chuàng)建.torch文件夾用來存放模型 mkdir ~/.torch/ # 將預(yù)訓(xùn)練好的模型移動(dòng)到這個(gè)文件夾中 mv vgg_ssd300_voc0712.pth ~/.torch mv vgg_ssd512_voc0712.pth ~/.torch mv mobilenet_v2_ssd320_voc0712.pth ~/.torch5.5 訓(xùn)練模型
# 導(dǎo)入環(huán)境變量VOC_ROOT export VOC_ROOT="./datasets" # 使用vim打開train.py文件 vim train.py ###############################修改前網(wǎng)絡(luò)訓(xùn)練參數(shù)############################### parser = argparse.ArgumentParser(description='Single Shot MultiBox Detector Training With PyTorch') # 設(shè)置使用的配置文件 parser.add_argument( "--config-file", default="", metavar="FILE", help="path to config file",type=str) # 設(shè)置是否進(jìn)行l(wèi)ocal_rank parser.add_argument("--local_rank", type=int, default=0) # 設(shè)置保存log的步長(zhǎng) parser.add_argument('--log_step', default=10, type=int, help='Print logs every log_step') # 設(shè)置保存checkpoint文件的步長(zhǎng) parser.add_argument('--save_step', default=2500, type=int, help='Save checkpoint every save_step') # 設(shè)置執(zhí)行網(wǎng)絡(luò)評(píng)估操作的步長(zhǎng) parser.add_argument('--eval_step', default=2500, type=int, help='Evaluate dataset every eval_step, disabled when eval_step < 0') # 設(shè)置是否使用tensorboard進(jìn)行l(wèi)oss和accuracy的可視化 parser.add_argument('--use_tensorboard', default=True, type=str2bool) parser.add_argument("--skip-test", dest="skip_test", help="Do not test the final model", action="store_true") # 設(shè)置是否打開使用命令行來改變參數(shù) parser.add_argument("opts", help="Modify config options using the command-line", default=None, nargs=argparse.REMAINDER) args = parser.parse_args() ###############################修改后網(wǎng)絡(luò)訓(xùn)練參數(shù)############################### parser = argparse.ArgumentParser(description='Single Shot MultiBox Detector Training With PyTorch') parser.add_argument("--config-file", default="configs/vgg_ssd512_voc0712.yaml", metavar="FILE", help="path to config file", type=str) parser.add_argument("--local_rank", type=int, default=0) parser.add_argument('--log_step', default=20, type=int, help='Print logs every log_step') parser.add_argument('--save_step', default=2500, type=int, help='Save checkpoint every save_step') parser.add_argument('--eval_step', default=2500, type=int, help='Evaluate dataset every eval_step, disabled when eval_step < 0') parser.add_argument('--use_tensorboard', default=True, type=str2bool) parser.add_argument("--skip-test", dest="skip_test", help="Do not test the final model", action="store_true") parser.add_argument("opts", help="Modify config options using the command-line", default=None, nargs=argparse.REMAINDER) args = parser.parse_args()注:
1、為了便于后續(xù)的代碼運(yùn)行,這里直接將–config-file的默認(rèn)參數(shù)設(shè)置為vgg_ssd512_voc0712.yaml,即使用VGG作為基準(zhǔn)網(wǎng)路,輸入大小為512x512,使用自己創(chuàng)建的VOC2007格式的數(shù)據(jù)集訓(xùn)練新的模型。
2、對(duì)于其它的一些超參數(shù),不建議你去改動(dòng)。
注:
1、下面展示是網(wǎng)絡(luò)訓(xùn)練過程中輸出的配置參數(shù)。
2、下面展示的是網(wǎng)絡(luò)訓(xùn)練過程中的Loss、Accuracy和運(yùn)行時(shí)間等輸出。
5.6 Loss和Accuracy可視化
# 切換到相應(yīng)的文件夾 cd ./SSD/outputs/vgg_ssd512_voc0712/ # 使用tensorboard打開網(wǎng)絡(luò)訓(xùn)練過程中保存的log文件 tensorboard --logdir=tf_logs # 在谷歌瀏覽器中輸入該網(wǎng)址查看logs http://ubuntu:6006注:
1、Tensorboard展示的logs文件如下圖所示,cls_loss表示分類分支的loss曲線,橫軸表示的是網(wǎng)絡(luò)的迭代次數(shù),縱軸表示cls_loss值,該數(shù)值越小表示網(wǎng)絡(luò)預(yù)測(cè)的結(jié)果和標(biāo)簽的結(jié)果越接近;reg_loss表示回歸分支的loss曲線,橫軸表示的是網(wǎng)絡(luò)的迭代次數(shù),縱軸表示reg_loss值,該數(shù)值越小表示網(wǎng)絡(luò)預(yù)測(cè)的結(jié)果和標(biāo)簽的結(jié)果越接近;total_loss是reg_loss和cls_loss之和,如果這3條曲線的整體趨勢(shì)是隨著迭代次數(shù)的增加呈現(xiàn)下降趨勢(shì)就表示我們的網(wǎng)絡(luò)正在不斷的收斂,不斷的尋找局部最優(yōu)解。
2、LR表示網(wǎng)絡(luò)的學(xué)習(xí)率,該參數(shù)一般設(shè)置為1e-3或者1e-4,隨著迭代次數(shù)的增加應(yīng)該不斷的減小該數(shù)值的值以防跳躍最優(yōu)解,這也就是所謂的衰減率。當(dāng)在訓(xùn)練的過程中出現(xiàn)NAN或者NULL時(shí)可以嘗試著調(diào)節(jié)該參數(shù),或許可以解決該問題。
5.7 模型測(cè)試和效果分析
###############################使用test.txt下的圖片做測(cè)試############################ # 切換到改文件夾 cd SSD/outputs/vgg_ssd512_voc0712/ # 將訓(xùn)練好的模型復(fù)制到model文件夾中 cp model_final.pth ../../model/ # 如果你有一個(gè)GPU,執(zhí)行該指令訓(xùn)練模型 python test.py --config-file configs/vgg_ssd300_voc0712.yaml # 如果你有4個(gè)GPU,執(zhí)行該指令訓(xùn)練模型 export NGPUS=4 python -m torch.distributed.launch --nproc_per_node=$NGPUS test.py --config-file configs/vgg_ssd300_voc0712.yaml ###############################使用特定文件夾下的圖片做測(cè)試########################### # 切換到改文件夾 cd SSD/outputs/vgg_ssd512_voc0712/ # 將訓(xùn)練好的模型復(fù)制到model文件夾中 cp model_final.pth ../../model/ # 創(chuàng)建測(cè)試文件夾 mkdir test_imgs # 將自己的測(cè)試圖片拷貝到該文件夾下面,這里*.jpg只是用來進(jìn)行演示 cp *jpg test_imgs # 使用SSD對(duì)test_imgs文件夾下面的圖片進(jìn)行預(yù)測(cè) python demo.py --config-file configs/vgg_ssd300_voc0712.yaml --images_dir test_imgs注:
1、下圖展示了運(yùn)行python test.py之后的結(jié)果,這個(gè)輸出中主要包含測(cè)試圖片的數(shù)量和訓(xùn)練好的模型在測(cè)試集上面的mAP指標(biāo)值,該指標(biāo)可以用來評(píng)估檢測(cè)模型的好壞,具體的細(xì)節(jié)請(qǐng)參考該博客。
2、下圖展示了運(yùn)行python demo.py之后的結(jié)果,這個(gè)輸出中不僅包含著測(cè)試圖片的個(gè)數(shù)1768,同時(shí)包含著裝載每一張圖片所需要的時(shí)間,以及對(duì)每一張圖片進(jìn)行預(yù)測(cè)所花費(fèi)的時(shí)間,除此之外,你還可以看到當(dāng)前的幀率值。
6、SSD性能改進(jìn)
??如果直接使用現(xiàn)成的SSD訓(xùn)練的結(jié)果可能并不能達(dá)到你的預(yù)期精度,那么你可以從以下的幾個(gè)地方快速的對(duì)該模型進(jìn)行改進(jìn),以下的幾個(gè)思路只是我自己的思路,希望大佬們不要吐槽。
- 思路1-該SSD網(wǎng)絡(luò)在訓(xùn)練過程中已經(jīng)包含了數(shù)據(jù)增強(qiáng)操作,具體的代碼在./SSD/ ssd/data/ transforms/init.py下面,但是對(duì)于多類別檢測(cè)任務(wù)而言,經(jīng)常會(huì)存在類別不均衡問題,為了解決該問題,最笨的方法就是使用數(shù)據(jù)增強(qiáng)工具將不同類別的數(shù)目調(diào)整為相同數(shù)量,推薦使用Augmentor工具,具體的使用代碼如下所示:
- 思路2-如果你熟悉目標(biāo)檢測(cè)算法,可能知道Focal_loss可以很好的抑制樣本不均衡問題,因而一個(gè)改進(jìn)的思路就是利用Focal_loss替換掉原始的smooth_l1損失,具體的實(shí)現(xiàn)可以參考Focal_loss實(shí)現(xiàn)。
- 思路3-對(duì)于目標(biāo)檢測(cè)問題而言,Loss函數(shù)的好壞直接或者間接會(huì)影響到算法的性能,針對(duì)這個(gè)問題當(dāng)前已經(jīng)有很好改進(jìn)的Loss函數(shù),比較具有代表性的就是IoU_Loss和IoU的變種GIoU和DIoU等,因而你可以嘗試著使用新的Loss來訓(xùn)練SSD網(wǎng)絡(luò),具體的實(shí)現(xiàn)可以參考DIoU實(shí)現(xiàn)。
- 思路4-NMS是目標(biāo)檢測(cè)中必不可少的一步, 當(dāng)前已經(jīng)有很好NMS的變種算法,你或許僅僅需要修改幾行代碼就可以提升整個(gè)模型的性能,因而你可以嘗試著使用Soft-NMS等其它的變種去替換掉原始的NMS操作,具體的實(shí)現(xiàn)可以參考Soft-NMS實(shí)現(xiàn)。
7、Pytorch模型加速
??當(dāng)你訓(xùn)練完自己的SSD檢測(cè)模型之后,如果你使用MobileNet_v2的Backbone之后,仍然感覺到整個(gè)網(wǎng)絡(luò)的運(yùn)行速度不能夠滿足你的需求,那么你可能就需要做模型加速啦,模型加速的主要方式包括模型蒸餾、模型裁剪和模型量化,當(dāng)前大量使用的技術(shù)是模型量化,如果你需要對(duì)Pytorch訓(xùn)練好的模型做模型量化,建議你參考官網(wǎng)量化1、官網(wǎng)量化2。除了官方的量化工具外,你還可以使用這個(gè)Pytorch模型優(yōu)化工具。具體的量化工作就交給大家自己去實(shí)現(xiàn)了,只有自己動(dòng)手去做了才能真正的學(xué)懂知識(shí),哈哈。
三、總結(jié)
??本文的主要目的是教你如何一步步使用現(xiàn)有SSD代碼在自己的數(shù)據(jù)集上面訓(xùn)練出一個(gè)可用的檢測(cè)網(wǎng)絡(luò)。主要的步驟包括數(shù)據(jù)標(biāo)注、標(biāo)簽預(yù)處理、配置運(yùn)行環(huán)境、修改模型參數(shù)、修改代碼參數(shù)、訓(xùn)練模型、測(cè)試模型、優(yōu)化模型和部署模型等。本文教給你的不僅僅訓(xùn)練一個(gè)SSD網(wǎng)絡(luò),當(dāng)你理解了其中的道理,訓(xùn)練一個(gè)Yolov3或者其它的檢測(cè)算法也就變成一件比較簡(jiǎn)單的事情了。
參考資料
1、SSD代碼
2、Pytorch模型優(yōu)化工具
3、官網(wǎng)量化1
4、官網(wǎng)量化2
5、Focal_loss實(shí)現(xiàn)
6、DIoU實(shí)現(xiàn)
7、Soft-NMS實(shí)現(xiàn)
8、Augmentor工具
注意事項(xiàng)
[1] 該博客是本人原創(chuàng)博客,如果您對(duì)該博客感興趣,想要轉(zhuǎn)載該博客,請(qǐng)與我聯(lián)系(qq郵箱:1575262785@qq.com),我會(huì)在第一時(shí)間回復(fù)大家,謝謝大家的關(guān)注.
[2] 由于個(gè)人能力有限,該博客可能存在很多的問題,希望大家能夠提出改進(jìn)意見。
[3] 如果您在閱讀本博客時(shí)遇到不理解的地方,希望您可以聯(lián)系我,我會(huì)及時(shí)的回復(fù)您,和您交流想法和意見,謝謝。
[4] 本文測(cè)試的圖片可以通過網(wǎng)盤鏈接進(jìn)行下載。提取碼:hku5。
[5] 本人業(yè)余時(shí)間承接各種本科畢設(shè)設(shè)計(jì)和各種小項(xiàng)目,包括圖像處理(數(shù)據(jù)挖掘、機(jī)器學(xué)習(xí)、深度學(xué)習(xí)等)、matlab仿真、python算法及仿真等,有需要的請(qǐng)加QQ:1575262785詳聊,備注“項(xiàng)目”!!!
總結(jié)
以上是生活随笔為你收集整理的一步一步带你训练自己的SSD检测算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: bWAPP 安装_bud在哪里下载
- 下一篇: 计算机视觉各领域前沿算法积累