一步一步带你训练自己的SSD检测算法
目錄
- 一、前言
- 二、實現細節
- 1、前提條件
- 2、數據標注
- 2.1 Labelme
- 2.1.1 工具特點簡介
- 2.1.2 工具安裝
- 2.1.3 工具使用簡介
- 2.2 LabelImg
- 2.2.1 工具安裝
- 2.2.2 工具使用簡介
- 3、標簽預處理
- 3.1 PASCAL VOC數據集格式詳解
- 3.2 構造新的PASCAL VOC數據集
- 3.3 COCO數據集格式詳解
- 3.4 構造新的COCO數據集
- 4、搭建SSD運行環境
- 5、修改代碼訓練網絡
- 5.1 代碼架構詳解
- 5.2 修改網絡配置參數
- 5.3 修改VOC類別參數
- 5.4 下載模型
- 5.5 訓練模型
- 5.6 Loss和Accuracy可視化
- 5.7 模型測試和效果分析
- 6、SSD性能改進
- 7、Pytorch模型加速
- 三、總結
- 參考資料
- 注意事項
一、前言
??隨著深度學習的快速發展,越來越多的場景都開始應用這么技術,但是當前的深度學習還是具有一些門檻,厲害的大佬們都在設計各種各樣的網絡,發各種各樣的頂會論文,他們關注的是算法的設計。然而在現實場景中,對于很多人而言,他們更多的是關注如何去使用這個算法,比如我如何快速的使用現有的工具訓練一個滿足自己需求的模型,當前網上淺顯易懂的帶小白構建自己的模型的教程太少了,我決定來彌補一下這個空缺,為自己為大家做一點點小貢獻。本文專注于使用SSD網絡從頭訓練自己的數據集,我們詳細講解里面的細節,具體的內容如下所示感興趣的小伙伴可以掃描下方的二維碼加群,我們可以一起交流更多的問題。
二、實現細節
1、前提條件
??在執行下面的操作之前,請保證你當前已經具有以下的這些環境。
- Ubuntu 16.04系統
- NVIDIA 顯卡,建議不低于GTX 1080
- CUDA驅動,CUDA8.0\CUDA9.0\CUDA10.0均可,建議設置為可以隨時切換的模式
- Anaconda3
2、數據標注
??對于一個完整的深度學習流程而言,數據標注是必不可少的一環,數據標注質量會極大的影響算法的性能。當前如果你使用別人已經標注好的數據或者公有的數據集,請直接跳過該步驟。
??下面就來簡單的介紹幾種數據標注工具,當前已經有很多開源的數據標注工具,這些工具基本上可以滿足我們98%的需求,工具沒有好壞之分,合適自己的工具就是好工具,所以大家需要根據自己的情況去選擇合適的標注工具。
2.1 Labelme
Github鏈接
2.1.1 工具特點簡介
- 同時支持Ubuntu / macOS / Windows系統
- 可以使用多邊形、矩形、圓、線和點進行圖像注釋
- 支持VOC和COCO數據格式
- 支持分類、檢測和分割等多個計算機視覺任務
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 工具使用簡介
??上圖展示了Labelme工具的標注界面,工具的使用留給大家自己去熟悉,在這里我只會強調幾個需要注意的地方,具體的內容如下所示:
- 建議把需要標注的數據集存放在一個文件夾下面,并以相應的名字來命名
- 該工具默認的標注方式是多邊形,如果進行目標檢測任務,通過Edit->Create Rectangle來切換
- 在標注的過程中,盡量做到精細化的標注,盡量先將目標放大,再執行精細標注,標注的質量會對極大的影響檢測算法的精度
- 該工具默認會生成.json格式的標簽并和標注圖片存放在同一個目錄,.json格式的標簽文件適合用來構建COCO數據集格式,而.xml格式的標簽文件適合用來構建VOC數據集格式,
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 工具使用簡介
??上圖展示了LabelImg工具的標注界面,工具的使用留給大家自己去熟悉,在這里我只會強調幾個需要注意的地方,具體的內容如下所示:
- 該工具默認生成的標簽格式為.xml,便于構造成VOC數據集的格式
- 該工具可以將以前使用的標簽保存起來,并將其設置為默認標簽,這個單類目標檢測任務中能夠極大的節約你的時間
??總而言之,通過使用上面兩個工具對我們自己的數據集進行標注之后,我們最終會獲得一些.json或者.xml格式的標簽文件。當然你也可以使用其它的工具進行標注,具體的細節請看該鏈接。
3、標簽預處理
??通過標注工具我們已經獲得了我們需要的標簽文件,那么我們為什么還需要標簽預處理操作呢?其實原因是這樣的,在計算機視覺界,你可能都會或多或少的聽說過PASCAL VOC和COCO數據集吧,由于這兩個數據集得到了大量的應用,學者們已經針對這兩個數據集寫了很好有用的代碼,比如如何快速的加載圖片和標簽文件等,那么,對于一個新手來說,能夠復用大佬們的代碼是一個很明智的選擇,這樣你不僅不需要去寫冗長的加載和處理代碼,而且還可以極大的加快你的開發進程,這就是對生成的標簽進行預處理的目的。簡而言之,所謂的標簽預處理即是將自己的圖片和標簽文件調整為PASCAL VOC和COCO數據集的格式,這樣我們只需要更換數據集,就可以很塊的完成數據的加載和解析工作。
3.1 PASCAL VOC數據集格式詳解
datasets |__ VOC2007|_ JPEGImages|_ Annotations|_ .xml|_ ImageSets|_ Main|_ train.txt/val.txt/trainval.txt/test.txt|_ SegmentationClass |__ VOC2012|_ JPEGImages|_ .jpg|_ Annotations|_ ImageSets|_ SegmentationClass |__ ...1、Annotations文件夾中存放使用標注工具獲得的.xml標簽文件; 2、ImageSets/Main文件夾中存放著訓練集\驗證集\訓練驗證集\測試集圖片的名稱,后續代碼訓練的圖片從這些文件中讀取; 3、JPEGImages文件夾中存放的是原始的訓練和驗證圖片,驗證圖片是隨機的從訓練集中獲得的; 4、SegmentationClass和SegmentationObject用來存放目標分割的信息,由于當前的任務僅僅是一個目標檢測的任務,因而不需要在這兩個文件夾下面存放任何文件;3.2 構造新的PASCAL VOC數據集
# 切換到自己的想要存放數據集的目錄,xxx代表自己的具體目錄 cd xxx #########################1、創建數據集包含的相關文件夾######################### mkdir datasets cd datasets mkdir Annotations mkdir ImageSets mkdir JPEGImages mkdir SegmentationClass mkdir SegmentationObject cd ImageSets mkdir Main #########################2、移動圖片和標簽到相應的文件夾####################### cd xxx # xxx表示你的圖片路徑 mv * JPEGImages # 將圖片(.jpg/.png/.bmp等)移動到JPEGImages文件夾中 cd xxx # xxx表示.xml文件所在的路徑 mv * Annotations # 將標簽文件(.xml)移動到Annotations文件夾中 #########################3、生成Main文件夾下面的.txt文件####################### # coding=utf-8 import os import random# 用來劃分訓練集合驗證集的比例 trainval_percent = 0.95 train_percent = 0.85 # 設置輸入的.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)# 打開相應的文件,準備存儲內容 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')# 遍歷所有文件進行寫文件 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)# 寫完之后關閉文件 ftrainval.close() ftrain.close() fval.close() ftest .close()??如果你是使用Labelme生成的.json格式的標簽文件,你想要使用VOC格式訓練網絡,那么你需要使用下面這個小工具將.json格式轉換為.xml格式
#########################4、對.json格式的標簽文件進行處理####################### # 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.標簽路徑 labelme_path = "./xxx/" # 原始xxx標注數據路徑,需要更換成自己的數據集名稱 saved_path = "./datasets/VOC2007/" # 保存路徑#2.創建要求文件夾 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.讀取標注信息并寫入 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.復制圖片到 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數據集格式詳解
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表示自己的數據集所在的文件夾的名稱; 2、annotations文件夾用來存放標簽文件,instances_train2014.json和instances_val2014.json分別表示訓練集和驗證集的標簽; 3、train2014和val2014文件夾分別用來存儲訓練集和驗證集的圖片,命名格式為im-N-name.jpg的格式;3.4 構造新的COCO數據集
??本文更傾向于生成VOC格式的數據集,如果你需要生成COCO格式的數據,請參考博文1和博文2。
4、搭建SSD運行環境
# 克隆一份SSD代碼到本地 git clone https://github.com/lufficc/SSD.git cd SSD # 創建conda虛擬環境 conda create -n ssd python=3.6 # 激活虛擬環境 conda activate ssd # 安裝相應的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等操作,用來加速網絡的訓練過程 python build.py build_ext develop5、修改代碼訓練網絡
5.1 代碼架構詳解
configs-該文件夾下面存放的是不同的網絡配置文件;mobilenet_v2_ssd320_voc0712.yaml-表示mobilenet backbone的配置文件;vgg_ssd512_voc0712.yaml-表示vgg backbone的配置文件; datasets-該文件夾存放數據文件,具體的結果看上文; demo-該文件夾存放一些測試圖片; ext-該文件夾存放CPU和GPU版本的NMS代碼; figures-該文件夾存放網絡訓練過程中的一些可視化文件; outputs-該文件夾存放訓練過程中的log文件和模型文件(.pth); demo.py-該文件用來測試新的圖片并輸出檢測結果; train.py-該文件用來訓練網絡; test.py-該文件用來測試網絡; ssd-該文件夾中存放ssd算法的實現代碼;config-該文件夾存放著默認的配置文件;data-該文件夾中存放著基本的數據預處理源文件,包括voc和coco數據集;engine-該文件夾中存放著基本的訓練和推理引擎的實現源碼;layers-該文件夾中存放著separable_conv的實現源碼;modeling-該文件夾中存放著SSD網絡的Backbone和head網絡的源碼;solver-該文件中存放著SSD網絡中使用到的優化器的源碼;structures-該文件夾中存放著一些Box的help函數的源碼;utils-該文件夾中存放著一些訓練和推理SSD算法的小工具的源碼:5.2 修改網絡配置參數
mv datasets SSD # 將構造好的VOC數據集移動到SSD文件夾中 cd configs # 切換到配置文件夾下面 vim vgg_ssd512_voc0712.yaml # 使用vim打開配置文件注:
1、vgg_ssd512_voc0712.yaml表示使用VGG作為基準網絡,SSD網絡的輸入大小為512x512 ,使用VOC2007格式的數據集來訓練新的網絡,大量的實驗結果表明該配置參數下能夠獲得最好的檢測效果。
2、vgg_ssd300_voc0712.yaml表示使用VGG作為基準網絡,SSD網絡的輸入大小為300x300 ,使用VOC2007格式的數據集來訓練新的網絡,由于輸入圖片的分辨率由512減小到300,整個網絡的訓練速度得到了提升,但是效果不如vgg_ssd512_voc0712.yaml架構。
3、mobilenet_v2_ssd320_voc0712.yaml表示使用Mobilenet_v2作為基準網絡,SSD網絡的輸入大小為300x300 ,使用VOC2007格式的數據集來訓練新的網絡,由于使用Mobilenet_v2作為基準網絡,因此該架構下面的訓練速度最快,但是最終獲得精度也是最差的,這個架構適合于一些簡單的檢測任務,Mobilenet_v2不能能夠獲得較好的檢測效果,而且可以獲得接近實時的推理速度。
注:
1、NUM_CLASSES參數表示檢測的類型,由于當前的缺陷數據集的類別數目為17,另外加上一個__background__類,因此需要將該數值修改為17+1=18;
2、BATCH_SIZE參數表示每次訓練的圖片的個數,由于訓練的圖片需要加載到內存中,該數值和你當前使用的顯卡的內容有著密切的關系,我根據自己的顯卡(Tesla T4)將BATCH_SIZE調整為12;
3、LR參數表示網絡訓練時的學習率,由于我設置的迭代次數比較多,害怕網絡跳過最優值,因而我將LR調整為1e-4;
4、TRAIN參數表示網絡訓練時使用的數據集,由于我們新建了一個類似于VOC2007的數據集,因而我刪除了voc_2012_trainval;
5、其它的這些配置參數,你可以根據自己的需要進行修改,但是我建議你不用修改。
5.3 修改VOC類別參數
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預訓練模型 wget https://github.com/lufficc/SSD/releases/download/1.2/vgg_ssd300_voc0712.pth # 下載vgg_ssd512_voc0712.pth預訓練模型 wget https://github.com/lufficc/SSD/releases/download/1.2/vgg_ssd512_voc0712.pth # 下載mobilenet_v2_ssd320_voc0712.pth預訓練模型 wget https://github.com/lufficc/SSD/releases/download/1.2/mobilenet_v2_ssd320_voc0712.pth # 創建.torch文件夾用來存放模型 mkdir ~/.torch/ # 將預訓練好的模型移動到這個文件夾中 mv vgg_ssd300_voc0712.pth ~/.torch mv vgg_ssd512_voc0712.pth ~/.torch mv mobilenet_v2_ssd320_voc0712.pth ~/.torch5.5 訓練模型
# 導入環境變量VOC_ROOT export VOC_ROOT="./datasets" # 使用vim打開train.py文件 vim train.py ###############################修改前網絡訓練參數############################### parser = argparse.ArgumentParser(description='Single Shot MultiBox Detector Training With PyTorch') # 設置使用的配置文件 parser.add_argument( "--config-file", default="", metavar="FILE", help="path to config file",type=str) # 設置是否進行local_rank parser.add_argument("--local_rank", type=int, default=0) # 設置保存log的步長 parser.add_argument('--log_step', default=10, type=int, help='Print logs every log_step') # 設置保存checkpoint文件的步長 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') # 設置是否使用tensorboard進行loss和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") # 設置是否打開使用命令行來改變參數 parser.add_argument("opts", help="Modify config options using the command-line", default=None, nargs=argparse.REMAINDER) args = parser.parse_args() ###############################修改后網絡訓練參數############################### 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、為了便于后續的代碼運行,這里直接將–config-file的默認參數設置為vgg_ssd512_voc0712.yaml,即使用VGG作為基準網路,輸入大小為512x512,使用自己創建的VOC2007格式的數據集訓練新的模型。
2、對于其它的一些超參數,不建議你去改動。
注:
1、下面展示是網絡訓練過程中輸出的配置參數。
2、下面展示的是網絡訓練過程中的Loss、Accuracy和運行時間等輸出。
5.6 Loss和Accuracy可視化
# 切換到相應的文件夾 cd ./SSD/outputs/vgg_ssd512_voc0712/ # 使用tensorboard打開網絡訓練過程中保存的log文件 tensorboard --logdir=tf_logs # 在谷歌瀏覽器中輸入該網址查看logs http://ubuntu:6006注:
1、Tensorboard展示的logs文件如下圖所示,cls_loss表示分類分支的loss曲線,橫軸表示的是網絡的迭代次數,縱軸表示cls_loss值,該數值越小表示網絡預測的結果和標簽的結果越接近;reg_loss表示回歸分支的loss曲線,橫軸表示的是網絡的迭代次數,縱軸表示reg_loss值,該數值越小表示網絡預測的結果和標簽的結果越接近;total_loss是reg_loss和cls_loss之和,如果這3條曲線的整體趨勢是隨著迭代次數的增加呈現下降趨勢就表示我們的網絡正在不斷的收斂,不斷的尋找局部最優解。
2、LR表示網絡的學習率,該參數一般設置為1e-3或者1e-4,隨著迭代次數的增加應該不斷的減小該數值的值以防跳躍最優解,這也就是所謂的衰減率。當在訓練的過程中出現NAN或者NULL時可以嘗試著調節該參數,或許可以解決該問題。
5.7 模型測試和效果分析
###############################使用test.txt下的圖片做測試############################ # 切換到改文件夾 cd SSD/outputs/vgg_ssd512_voc0712/ # 將訓練好的模型復制到model文件夾中 cp model_final.pth ../../model/ # 如果你有一個GPU,執行該指令訓練模型 python test.py --config-file configs/vgg_ssd300_voc0712.yaml # 如果你有4個GPU,執行該指令訓練模型 export NGPUS=4 python -m torch.distributed.launch --nproc_per_node=$NGPUS test.py --config-file configs/vgg_ssd300_voc0712.yaml ###############################使用特定文件夾下的圖片做測試########################### # 切換到改文件夾 cd SSD/outputs/vgg_ssd512_voc0712/ # 將訓練好的模型復制到model文件夾中 cp model_final.pth ../../model/ # 創建測試文件夾 mkdir test_imgs # 將自己的測試圖片拷貝到該文件夾下面,這里*.jpg只是用來進行演示 cp *jpg test_imgs # 使用SSD對test_imgs文件夾下面的圖片進行預測 python demo.py --config-file configs/vgg_ssd300_voc0712.yaml --images_dir test_imgs注:
1、下圖展示了運行python test.py之后的結果,這個輸出中主要包含測試圖片的數量和訓練好的模型在測試集上面的mAP指標值,該指標可以用來評估檢測模型的好壞,具體的細節請參考該博客。
2、下圖展示了運行python demo.py之后的結果,這個輸出中不僅包含著測試圖片的個數1768,同時包含著裝載每一張圖片所需要的時間,以及對每一張圖片進行預測所花費的時間,除此之外,你還可以看到當前的幀率值。
6、SSD性能改進
??如果直接使用現成的SSD訓練的結果可能并不能達到你的預期精度,那么你可以從以下的幾個地方快速的對該模型進行改進,以下的幾個思路只是我自己的思路,希望大佬們不要吐槽。
- 思路1-該SSD網絡在訓練過程中已經包含了數據增強操作,具體的代碼在./SSD/ ssd/data/ transforms/init.py下面,但是對于多類別檢測任務而言,經常會存在類別不均衡問題,為了解決該問題,最笨的方法就是使用數據增強工具將不同類別的數目調整為相同數量,推薦使用Augmentor工具,具體的使用代碼如下所示:
- 思路2-如果你熟悉目標檢測算法,可能知道Focal_loss可以很好的抑制樣本不均衡問題,因而一個改進的思路就是利用Focal_loss替換掉原始的smooth_l1損失,具體的實現可以參考Focal_loss實現。
- 思路3-對于目標檢測問題而言,Loss函數的好壞直接或者間接會影響到算法的性能,針對這個問題當前已經有很好改進的Loss函數,比較具有代表性的就是IoU_Loss和IoU的變種GIoU和DIoU等,因而你可以嘗試著使用新的Loss來訓練SSD網絡,具體的實現可以參考DIoU實現。
- 思路4-NMS是目標檢測中必不可少的一步, 當前已經有很好NMS的變種算法,你或許僅僅需要修改幾行代碼就可以提升整個模型的性能,因而你可以嘗試著使用Soft-NMS等其它的變種去替換掉原始的NMS操作,具體的實現可以參考Soft-NMS實現。
7、Pytorch模型加速
??當你訓練完自己的SSD檢測模型之后,如果你使用MobileNet_v2的Backbone之后,仍然感覺到整個網絡的運行速度不能夠滿足你的需求,那么你可能就需要做模型加速啦,模型加速的主要方式包括模型蒸餾、模型裁剪和模型量化,當前大量使用的技術是模型量化,如果你需要對Pytorch訓練好的模型做模型量化,建議你參考官網量化1、官網量化2。除了官方的量化工具外,你還可以使用這個Pytorch模型優化工具。具體的量化工作就交給大家自己去實現了,只有自己動手去做了才能真正的學懂知識,哈哈。
三、總結
??本文的主要目的是教你如何一步步使用現有SSD代碼在自己的數據集上面訓練出一個可用的檢測網絡。主要的步驟包括數據標注、標簽預處理、配置運行環境、修改模型參數、修改代碼參數、訓練模型、測試模型、優化模型和部署模型等。本文教給你的不僅僅訓練一個SSD網絡,當你理解了其中的道理,訓練一個Yolov3或者其它的檢測算法也就變成一件比較簡單的事情了。
參考資料
1、SSD代碼
2、Pytorch模型優化工具
3、官網量化1
4、官網量化2
5、Focal_loss實現
6、DIoU實現
7、Soft-NMS實現
8、Augmentor工具
注意事項
[1] 該博客是本人原創博客,如果您對該博客感興趣,想要轉載該博客,請與我聯系(qq郵箱:1575262785@qq.com),我會在第一時間回復大家,謝謝大家的關注.
[2] 由于個人能力有限,該博客可能存在很多的問題,希望大家能夠提出改進意見。
[3] 如果您在閱讀本博客時遇到不理解的地方,希望您可以聯系我,我會及時的回復您,和您交流想法和意見,謝謝。
[4] 本文測試的圖片可以通過網盤鏈接進行下載。提取碼:hku5。
[5] 本人業余時間承接各種本科畢設設計和各種小項目,包括圖像處理(數據挖掘、機器學習、深度學習等)、matlab仿真、python算法及仿真等,有需要的請加QQ:1575262785詳聊,備注“項目”!!!
總結
以上是生活随笔為你收集整理的一步一步带你训练自己的SSD检测算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: bWAPP 安装_bud在哪里下载
- 下一篇: 计算机视觉各领域前沿算法积累