日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 人工智能 > 目标检测 >内容正文

目标检测

【目标检测实验系列】使用yolov3 spp训练西工大遥感数据集NWPU VHR-10(包括如何将NWPU VHR-10转为VOC格式和yolov3 spp实验调试的详细步骤,且附上训练完的权重文件)

發(fā)布時(shí)間:2024/3/13 目标检测 114 豆豆

目錄

    • 1. 文章主要內(nèi)容
    • 2. 西工大數(shù)據(jù)集轉(zhuǎn)換為VOC格式數(shù)據(jù)集
      • 2.1 VOC數(shù)據(jù)集結(jié)構(gòu)
      • 2.2 西工大數(shù)據(jù)集
      • 2.3 轉(zhuǎn)換格式
        • 2.3.1 構(gòu)建與VOC類(lèi)似的數(shù)據(jù)集文件結(jié)構(gòu)(文件夾名可以自定義)
        • 2.3.2 數(shù)據(jù)預(yù)處理
    • 3. yolov3 spp 訓(xùn)練NWPU-VHR-10 dataset
      • 3.1 源碼下載及其相關(guān)準(zhǔn)備文件
      • 3.2 VOC格式數(shù)據(jù)集轉(zhuǎn)換為yolo格式
    • 4. yolov3 spp 項(xiàng)目參數(shù)解析、如何獲取best.pt以及定位到對(duì)應(yīng)的epoch
      • 4.1 yolov3 spp 項(xiàng)目部分參數(shù)解析
      • 4.2 如何獲取best.pt以及定位到對(duì)應(yīng)的epoch
    • 5. 本篇小結(jié)

1. 文章主要內(nèi)容

? ? ? ?此篇博客為[目標(biāo)檢測(cè)實(shí)驗(yàn)系列]的首篇博客,該系列主要記錄在目標(biāo)檢測(cè)實(shí)驗(yàn)過(guò)程中的詳細(xì)步驟和所思所想,歡迎大家互相交流。

? ? ? ?本篇博客參考的主要內(nèi)容來(lái)源于
? ? ? ?大彤小憶–使用Python將NWPU VHR-10數(shù)據(jù)集的格式轉(zhuǎn)換成VOC2007數(shù)據(jù)集的格式
? ? ? ?霹靂吧啦Wz–YOLOv3 SPP源碼解析-1代碼使用簡(jiǎn)介

? ? ? ?本篇博客主要涉及兩個(gè)主體內(nèi)容。第一個(gè):將西工大遙感數(shù)據(jù)集NWPU VHR-10的格式轉(zhuǎn)為VOC通用的數(shù)據(jù)集。第二個(gè):再次將轉(zhuǎn)換后的VOC格式的數(shù)據(jù)集轉(zhuǎn)變成yolov3 spp所需要格式的數(shù)據(jù)集,之后調(diào)試項(xiàng)目?jī)?nèi)容,達(dá)到成功跑通項(xiàng)目的要求。(通讀本篇博客大概需要10分鐘左右的時(shí)間)。

2. 西工大數(shù)據(jù)集轉(zhuǎn)換為VOC格式數(shù)據(jù)集

2.1 VOC數(shù)據(jù)集結(jié)構(gòu)

? ? ? ?VOC通用數(shù)據(jù)集格式如下圖所示:

? ? ? ?特別注意以下幾點(diǎn)內(nèi)容

? ? ? ?1.這里的箭頭表示的文件夾由外到內(nèi),比如VOCdevkit文件夾包含了VOC2012文件,而VOC2012文件夾包含Annotations、ImageSets和JPEGImages

? ? ? ?2.這里的VOC2012文件夾名字也可以換成VOC2007,代表不同的版本。事實(shí)上,只要保證整體的文件夾結(jié)構(gòu)沒(méi)有問(wèn)題,所有的文件夾名字都可以自定義,只需要在對(duì)應(yīng)的模型代碼中指定好自定義的文件夾名字。

? ? ? ?3.和Annotations、ImageSets和JPEGImages同級(jí)目錄下,還有兩個(gè)文件夾SegmentationClass、SegementionObject。因?yàn)檫@里用不到,所以不過(guò)多贅述。各個(gè)文件內(nèi)容代表什么,請(qǐng)各位同學(xué)自己研究。

2.2 西工大數(shù)據(jù)集

? ? ? ?特別注意:西工大數(shù)據(jù)集-(提取密碼:1234)
? ? ? ?西工大NWPU VHR-10數(shù)據(jù)集包含10種類(lèi)別,其主要結(jié)構(gòu)如下圖所示:
? ? ? ?特別注意以下幾點(diǎn)內(nèi)容

? ? ? ?1.groud truth文件夾內(nèi)容是坐標(biāo)信息以及所對(duì)應(yīng)的類(lèi)別;positive image set文件夾內(nèi)容是正樣本的圖片; negative image set文件夾內(nèi)容是負(fù)樣本的圖片

2.3 轉(zhuǎn)換格式

2.3.1 構(gòu)建與VOC類(lèi)似的數(shù)據(jù)集文件結(jié)構(gòu)(文件夾名可以自定義)

? ? ? ?本篇博客構(gòu)建的文件結(jié)構(gòu)如下圖所示:(用于存放轉(zhuǎn)換后的數(shù)據(jù)集)

2.3.2 數(shù)據(jù)預(yù)處理

? ? ? ?因?yàn)镹WPU VHR-10數(shù)據(jù)集的positive image set圖片內(nèi)容是從001.jpg-650.jpg,而negative image set圖片內(nèi)容是從001.jpg到150.jpg。首先,需要將正樣本的數(shù)據(jù)和負(fù)樣本的數(shù)據(jù)合并,并且重寫(xiě)進(jìn)行順序編號(hào)處理,改成000001.jpg-000800.jpg。其中positive image set文件夾內(nèi)的圖片被重命名為000001.jpg-000650.jpg,negative image set文件夾內(nèi)的圖片被重命名為000651.jpg-000800.jpg。
? ? ? ?最后將這800張圖片存放在D:/deeplearning-by-liaohuan/object-detection/datasets/NWPU-VHR-10 dataset/nwpu-10/voc-nwpu-10/JPEGImages(注意:這里的文件夾路徑是自定義的,需按照每個(gè)同學(xué)文件夾的實(shí)際絕對(duì)路徑填寫(xiě)),具體的轉(zhuǎn)換代碼如下所示:

import os import shutildef imag_rename(old_path, new_path,start_number = 0):filelist = os.listdir(old_path) # 該文件夾下所有的文件(包括文件夾)if os.path.exists(new_path) == False:os.mkdir(new_path)for file in filelist: # 遍歷所有文件Olddir = os.path.join(old_path, file) # 原來(lái)的文件路徑if os.path.isdir(Olddir): # 如果是文件夾則跳過(guò)continuefilename = os.path.splitext(file)[0] # 文件名filetype = os.path.splitext(file)[1] # 文件擴(kuò)展名if filetype == '.jpg':Newdir = os.path.join(new_path, str(int(filename) + start_number).zfill(6) + filetype) # 用字符串函數(shù)zfill 以0補(bǔ)全所需位數(shù)shutil.copyfile(Olddir, Newdir)if __name__ == "__main__":# 解決positive image set文件夾中的重命名問(wèn)題,start_number = 0old_path = "D:/deeplearning-by-liaohuan/object-detection/datasets/NWPU-VHR-10 dataset/positive image set/"new_path = "D:/deeplearning-by-liaohuan/object-detection/datasets/NWPU-VHR-10 dataset/nwpu-10/voc-nwpu-10/JPEGImages"imag_rename(old_path, new_path)# 解決negative image set文件夾中的重命名問(wèn)題,start_number = 650old_path = "D:/deeplearning-by-liaohuan/object-detection/datasets/NWPU-VHR-10 dataset/negative image set/"new_path = "D:/deeplearning-by-liaohuan/object-detection/datasets/NWPU-VHR-10 dataset/nwpu-10/voc-nwpu-10/JPEGImages"imag_rename(old_path,new_path,start_number = 650)print("done!")

? ? ? ?特別注意以下幾點(diǎn)內(nèi)容

? ? ? ?1.代碼需要修改的部分在main函數(shù)里面,分別是old_path、new_path一共兩對(duì)。其中old_path分別對(duì)應(yīng)于西工大數(shù)據(jù)集的positive image set、negative image set文件夾路徑,而new_path對(duì)應(yīng)于轉(zhuǎn)換合并后創(chuàng)建的新文件夾路徑,上面有提到。

? ? ? ?接下來(lái)處理Annoatations文件夾的內(nèi)容:將NWPU VHR-10數(shù)據(jù)集的ground truth文件夾內(nèi)的標(biāo)注信息txt文件轉(zhuǎn)換為與VOC2007數(shù)據(jù)集的Annotations文件夾內(nèi)的標(biāo)注信息xml文件格式相同的xml文件,并重命名為000001.xml-000650.xml;由于negative image set文件夾內(nèi)的圖片沒(méi)有對(duì)應(yīng)的標(biāo)注信息文件,所以生成包含圖片的size信息、不包含object的bounding box信息的xml文件,并命名為000651.xml-000800.xml,并且存放在路徑為D:/deeplearning-by-liaohuan/object-detection/datasets/NWPU-VHR-10 dataset/nwpu-10/voc-nwpu-10/Annoatations下(和上面一樣,這里也是自定義的路徑),具體的代碼如下所示:

from lxml.etree import Element,SubElement,tostring from xml.dom.minidom import parseString import xml.dom.minidom import os import sys from PIL import Image# 處理NWPU VHR-10數(shù)據(jù)集中的txt標(biāo)注信息轉(zhuǎn)換成 xml文件 # 此處的path應(yīng)該傳入的是NWPU VHR-10數(shù)據(jù)集文件夾下面的ground truth文件夾的目錄 # 即 path = "E:/Remote Sensing/Data Set/NWPU VHR-10 dataset/ground truth" def deal(path):files=os.listdir(path) # files獲取所有標(biāo)注txt文件的文件名# 此處可以自行設(shè)置輸出路徑 按照VOC數(shù)據(jù)集的格式,xml文件應(yīng)該輸出在數(shù)據(jù)集文件下面的Annotations文件夾下面outpath = "D:/deeplearning-by-liaohuan/object-detection/datasets/NWPU-VHR-10 dataset/nwpu-10/voc-nwpu-10/Annoatations/"# 如果輸出文件夾不存在,就創(chuàng)建它if os.path.exists(outpath) == False:os.mkdir(outpath)# 遍歷所有的txt標(biāo)注文件,一共650個(gè)txt文件for file in files:filename=os.path.splitext(file)[0] # 獲取ground truth文件夾中標(biāo)注txt文件的文件名,比如如果文件名為001.txt,那么filename = '001'sufix=os.path.splitext(file)[1]# 獲取標(biāo)注txt文件的后綴名 判斷是否為txtif sufix=='.txt': # 標(biāo)注txt文件中每一行代表一個(gè)目標(biāo),(x1,y1),(x2,y2),class_number來(lái)表示xmins=[] ymins=[]xmaxs=[]ymaxs=[]names=[]# num,xmins,ymins,xmaxs,ymaxs,names=readtxt(path + '/' + file) # 調(diào)用readtxt文件獲取信息,轉(zhuǎn)到readtxt函數(shù)path_txt = path + '/' + file # 獲取txt標(biāo)注文件的路徑信息# 打開(kāi)txt標(biāo)注文件with open(path_txt, 'r') as f:contents = f.read() # 將txt文件的信息按行讀取到contents列表中objects=contents.split('\n') # 以換行劃分每一個(gè)目標(biāo)的標(biāo)注信息,因?yàn)槊恳粋€(gè)目標(biāo)的標(biāo)注信息在txt文件中為一行 for i in range(objects.count('')): objects.remove('') # 將objects中的空格移除num=len(objects) # 獲取一個(gè)標(biāo)注文件的目標(biāo)個(gè)數(shù),objects中一個(gè)元素代表的信息就是一個(gè)檢測(cè)目標(biāo) # 遍歷 objects列表,獲取每一個(gè)檢測(cè)目標(biāo)的五維信息for objecto in objects: xmin=objecto.split(',')[0] # xmin = '(563'xmin=xmin.split('(')[1] # xmin = '563' 可能存在空格xmin=xmin.strip() # strip函數(shù)去掉字符串開(kāi)頭結(jié)尾的空格符ymin=objecto.split(',')[1] # ymin = '478)'ymin=ymin.split(')')[0] # ymin = '478' 可能存在空格ymin=ymin.strip() # strip函數(shù)去掉字符串開(kāi)頭結(jié)尾的空格符xmax=objecto.split(',')[2] # xmax同理xmax=xmax.split('(')[1]xmax=xmax.strip()ymax=objecto.split(',')[3] # ymax同理ymax=ymax.split(')')[0]ymax=ymax.strip()name=objecto.split(',')[4] # 與上 同理name=name.strip()if name=="1 " or name=="1": # 將數(shù)字信息轉(zhuǎn)換成label字符串信息name='airplane'elif name=="2 "or name=="2":name='ship'elif name== "3 "or name=="3":name='storage tank'elif name=="4 "or name=="4":name='baseball diamond'elif name=="5 "or name=="5":name='tennis court'elif name=="6 "or name=="6":name='basketball court'elif name=="7 "or name=="7":name='ground track field'elif name=="8 "or name=="8":name='harbor'elif name=="9 "or name=="9":name='bridge'elif name=="10 "or name=="10":name='vehicle' else:print(path) # print(xmin,ymin,xmax,ymax,name)xmins.append(xmin)ymins.append(ymin)xmaxs.append(xmax)ymaxs.append(ymax)names.append(name)filename_fill = str(int(filename)).zfill(6) # 將xml的文件名填充為6位數(shù),比如1.xml就改為000001.xmlfilename_jpg = filename_fill + ".jpg" # 由于xml中存儲(chǔ)的文件名為000001.jpg,所以還得對(duì)所有的NWPU數(shù)據(jù)集中的圖片進(jìn)行重命名print(filename_fill)dealpath = outpath + filename_fill +".xml"# 注意,經(jīng)過(guò)重命名轉(zhuǎn)換之后,圖片都存放在E:/Remote Sensing/Data Set/VOCdevkit2007/VOC2007/JPEGImages/中imagepath = "D:/deeplearning-by-liaohuan/object-detection/datasets/NWPU-VHR-10 dataset/nwpu-10/voc-nwpu-10/JPEGImages/" + filename_fill + ".jpg"with open(dealpath, 'w') as f:img=Image.open(imagepath) # 根據(jù)圖片的地址打開(kāi)圖片并獲取圖片的寬 和 高width=img.size[0]height=img.size[1]# 將圖片的寬和高以及其他和VOC數(shù)據(jù)集向?qū)?yīng)的信息writexml(dealpath,filename_jpg,num,xmins,ymins,xmaxs,ymaxs,names, height, width)# 同時(shí)也得給negative image set文件夾下面的所有負(fù)樣本圖片生成xml標(biāo)注negative_path = "D:/deeplearning-by-liaohuan/object-detection/datasets/NWPU-VHR-10 dataset/negative image set/"negative_images = os.listdir(negative_path)for file in negative_images:filename = file.split('.')[0] # 獲取文件名,不包括后綴名filename_fill = str(int(filename) + 650).zfill(6) # 將xml的文件名填充為6位數(shù)。同時(shí)加上650,比如1.xml就改為00001.xmlfilename_jpg = filename_fill + '.jpg' # 比如第一個(gè)負(fù)樣本001.jpg的filename_jpg 為000651.jpg## 重命名為6位數(shù)print(filename_fill)## 生成不含目標(biāo)的xml文件dealpath = outpath + filename_fill +".xml"# 注意,經(jīng)過(guò)重命名轉(zhuǎn)換之后,圖片都存放在E:/Remote Sensing/Data Set/VOCdevkit2007/VOC2007/JPEGImages/中imagepath = "D:/deeplearning-by-liaohuan/object-detection/datasets/NWPU-VHR-10 dataset/nwpu-10/voc-nwpu-10/JPEGImages/" + filename_fill + ".jpg"with open(dealpath, 'w') as f:img = Image.open(imagepath)width = img.size[0]height = img.size[1]# 將寬高和空的目標(biāo)標(biāo)注信息寫(xiě)入xml標(biāo)注writexml(dealpath,filename_jpg,num = 0,xmins = [],ymins = [],xmaxs = [],ymaxs = [],names = [],width=width,height=height)# NWPU數(shù)據(jù)集中標(biāo)注的五維信息 (x1,y1) denotes the top-left coordinate of the bounding box, # (x2,y2) denotes the right-bottom coordinate of the bounding box # 所以 xmin = x1 ymin = y1, xmax = x2, ymax = y2 同時(shí)要注意這里的相對(duì)坐標(biāo)是以圖片左上角為坐標(biāo)原點(diǎn)計(jì)算的 # VOC數(shù)據(jù)集對(duì)于包圍框標(biāo)注的格式是bounding-box(包含左下角和右上角xy坐標(biāo)# 將從txt讀取的標(biāo)注信息寫(xiě)入到xml文件中 def writexml(path,filename,num,xmins,ymins,xmaxs,ymaxs,names,height, width):# Nwpu-vhr-10 < 1000*600node_root=Element('annotation')node_folder=SubElement(node_root,'folder')node_folder.text="VOC2007"node_filename=SubElement(node_root,'filename')node_filename.text="%s" % filenamenode_size=SubElement(node_root,"size")node_width = SubElement(node_size, 'width')node_width.text = '%s' % widthnode_height = SubElement(node_size, 'height')node_height.text = '%s' % heightnode_depth = SubElement(node_size, 'depth')node_depth.text = '3'for i in range(num):node_object = SubElement(node_root, 'object')node_name = SubElement(node_object, 'name')node_name.text = '%s' % names[i]node_name = SubElement(node_object, 'pose')node_name.text = '%s' % "unspecified"node_name = SubElement(node_object, 'truncated')node_name.text = '%s' % "0"node_difficult = SubElement(node_object, 'difficult')node_difficult.text = '0'node_bndbox = SubElement(node_object, 'bndbox')node_xmin = SubElement(node_bndbox, 'xmin')node_xmin.text = '%s'% xmins[i]node_ymin = SubElement(node_bndbox, 'ymin')node_ymin.text = '%s' % ymins[i]node_xmax = SubElement(node_bndbox, 'xmax')node_xmax.text = '%s' % xmaxs[i]node_ymax = SubElement(node_bndbox, 'ymax')node_ymax.text = '%s' % ymaxs[i]xml = tostring(node_root, pretty_print=True) dom = parseString(xml)with open(path, 'wb') as f:f.write(xml)returnif __name__ == "__main__":# path指定的是標(biāo)注txt文件所在的路徑path = "D:/deeplearning-by-liaohuan/object-detection/datasets/NWPU-VHR-10 dataset/ground truth"deal(path)print("done!")

? ? ? ?特別注意以下幾點(diǎn)內(nèi)容
? ? ? ?1.代碼里只需要修改outpath 、negative_path、imagepath、path這四個(gè)變量的路徑值(也就是代碼里面所有和路徑相關(guān)的變量,都需要改成自己的實(shí)際路徑) ,其他不需要修改。修改后Annotations文件夾的部分內(nèi)容如下:



? ? ? ?最后我們來(lái)處理ImageSets文件夾,我們對(duì)NWPU VHR-10數(shù)據(jù)集進(jìn)行劃分,劃分為train、val、trainval、test四個(gè)文件(其中train代表訓(xùn)練集數(shù)據(jù)信息,val代表驗(yàn)證集數(shù)據(jù)信息,trainval代表訓(xùn)練和驗(yàn)證集合并的數(shù)據(jù)信息,test為測(cè)試集數(shù)據(jù)的信息)。需要注意到trainval、test組成了整個(gè)數(shù)據(jù)集的信息,仔細(xì)分清楚各個(gè)之間的關(guān)系。我們給出劃分?jǐn)?shù)據(jù)集信息的代碼,劃分后的數(shù)據(jù)集信息存放在D:/deeplearning-by-liaohuan/object-detection/datasets/NWPU-VHR-10 dataset/nwpu-10/voc-nwpu-10/ImageSets/Main文件夾里面(注意:這里的路徑同樣是自定義路徑,不再做過(guò)多解釋),具體的代碼如下:

import os import randomtrainval_percent = 0.8 # 表示訓(xùn)練集和驗(yàn)證集(交叉驗(yàn)證集)所占總圖片的比例 train_percent = 0.75 # 訓(xùn)練集所占交叉驗(yàn)證集的比例 xmlfilepath = 'D:/deeplearning-by-liaohuan/object-detection/datasets/NWPU-VHR-10 dataset/nwpu-10/voc-nwpu-10/Annotations' txtsavepath = 'D:/deeplearning-by-liaohuan/object-detection/datasets/NWPU-VHR-10 dataset/nwpu-10/voc-nwpu-10/ImageSets/Main' total_xml = os.listdir(xmlfilepath)num = 650 # 有目標(biāo)的圖片數(shù) list = range(num) tv = int(num * trainval_percent) # xml文件中的交叉驗(yàn)證集數(shù) tr = int(tv * train_percent) # xml文件中的訓(xùn)練集數(shù),注意,我們?cè)谇懊娑x的是訓(xùn)練集占交叉驗(yàn)證集的比例 trainval = random.sample(list, tv) train = random.sample(trainval, tr)ftrainval = open('D:/deeplearning-by-liaohuan/object-detection/datasets/NWPU-VHR-10 dataset/nwpu-10/voc-nwpu-10/ImageSets/Main/trainval.txt', 'w') ftest = open('D:/deeplearning-by-liaohuan/object-detection/datasets/NWPU-VHR-10 dataset/nwpu-10/voc-nwpu-10/ImageSets/Main/test.txt', 'w') ftrain = open('D:/deeplearning-by-liaohuan/object-detection/datasets/NWPU-VHR-10 dataset/nwpu-10/voc-nwpu-10/ImageSets/Main/train.txt', 'w') fval = open('D:/deeplearning-by-liaohuan/object-detection/datasets/NWPU-VHR-10 dataset/nwpu-10/voc-nwpu-10/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)for i in range(150):num = str(651 + i).zfill(6) + '\n'ftest.write(num)ftrainval.close() ftrain.close() fval.close() ftest.close()print("done!")

? ? ? ?特別注意以下幾點(diǎn)內(nèi)容
? ? ? ?1.代碼里修改的變量為xmlfilepath 、txtsavepath 、ftrainval、ftest、ftrain和fval.
? ? ? ?2.由于NWPU-VHR-10 dataset中只有positive image set文件夾中的650張圖片包含目標(biāo)的標(biāo)注信息,所以訓(xùn)練集train及驗(yàn)證集val只能在從這650張圖片中劃分,negative image set文件夾中的150張圖片不包含目標(biāo)的標(biāo)注信息,劃分在測(cè)試集test中.
? ? ? ?3.代碼中的trainval_percent 代表trainval.txt所占總數(shù)據(jù)集的比例,train_percent 代表在train.txt所占trainval.txt數(shù)據(jù)集(這里的總數(shù)據(jù)集是650張圖片,不包括不標(biāo)注的150張圖片)的比例,千萬(wàn)別搞混了,這里的比例自己也可以進(jìn)行修改!這樣我們就成功的將西工大數(shù)據(jù)集轉(zhuǎn)為VOC格式的數(shù)據(jù)集了。最后,ImageSets的Main文件夾的內(nèi)容為:

3. yolov3 spp 訓(xùn)練NWPU-VHR-10 dataset

3.1 源碼下載及其相關(guān)準(zhǔn)備文件

? ? ? ?前提說(shuō)明:以下操作都在本地環(huán)境OK的情況下進(jìn)行的,如果沒(méi)有配置好環(huán)境,請(qǐng)根據(jù)下面源碼鏈接里環(huán)境配置一欄一一配置完成
? ? ? ?首先下載yolov3 spp 源碼(yolov3 spp源碼鏈接),之后準(zhǔn)備新建一個(gè)pascal_voc_classes.json放在源碼的data文件夾下(注意:如果源碼里面本來(lái)存在pascal_voc_classes.json,那么修改其內(nèi)容如下即可),因?yàn)槲覀冞@里使用的NWPU-VHR-10 dataset數(shù)據(jù)集,所以種類(lèi)一共有10類(lèi),其json文件的代碼如下所示:

{"airplane": 1,"ship": 2,"storage tank": 3,"baseball diamond": 4,"tennis court": 5,"basketball court": 6,"ground track field": 7,"harbor": 8,"bridge": 9,"vehicle": 10 }

? ? ? ?其json文件在源代碼的目錄擺放位置如下圖所示(data文件夾里面其他文件暫時(shí)先別管,后面會(huì)生成):


3.2 VOC格式數(shù)據(jù)集轉(zhuǎn)換為yolo格式

? ? ? ?使用源碼根目錄當(dāng)中trans_voc2yolo.py腳本進(jìn)行轉(zhuǎn)換,并在源碼下的data文件夾下生成my_data_label.names標(biāo)簽文件.執(zhí)行腳本前,需要根據(jù)自己的路徑修改trans_voc2yolo.py中的一些參數(shù):(特別注意:這里的voc_root、voc_version正好對(duì)應(yīng)于我們?cè)谏弦徊糠痔幚淼玫降腣OC格式的西工大數(shù)據(jù)集的最外面兩層的文件夾名字,其他的路徑按照以下代碼照搬即可

voc_root = "nwpu-10" voc_version = "voc-nwpu-10"# 轉(zhuǎn)換的訓(xùn)練集以及驗(yàn)證集對(duì)應(yīng)txt文件 train_txt = "train.txt" val_txt = "val.txt"# 轉(zhuǎn)換后的文件保存目錄 save_file_root = "./my_yolo_dataset"# label標(biāo)簽對(duì)應(yīng)json文件 label_json_path = './data/pascal_voc_classes.json'

? ? ? ?轉(zhuǎn)換之后生成my_data_label.names標(biāo)簽文件、以及train、val文件夾,其中文件夾內(nèi)的labels為圖片的標(biāo)簽,而image為圖片的路徑信息,生成的內(nèi)容如下圖所示:



? ? ? ?然后,我們先來(lái)處理一下源碼cfg文件夾下的yolov3-spp.cfg文件,這里我們需要修改兩個(gè)參數(shù),分別是如下代碼的filters、classes,特別注意:我們的種類(lèi)是10,所以classes為10,filters的計(jì)算公式為(classes + 5)x3,所以filters為45.

[convolutional] size=1 stride=1 pad=1 filters=45 activation=linear[yolo] mask = 6,7,8 anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326 classes=10 num=9 jitter=.3 ignore_thresh = .7 truth_thresh = 1 random=1

? ? ? ?之后使用源碼根目錄下的calculate_dataset.py腳本生成my_train_data.txt文件、my_val_data.txt文件以及my_data.data文件,并生成新的my_yolov3.cfg文件,執(zhí)行腳本前,需要修改以下參數(shù):

train_annotation_dir = "./my_yolo_dataset/train/labels" val_annotation_dir = "./my_yolo_dataset/val/labels" classes_label = "./data/my_data_label.names" cfg_path = "./cfg/yolov3-spp.cfg"

? ? ? ?特別注意:這里的train_annotation_dir、val_annotation_dir、classes_label、cfg_path 在之前的操作中都已經(jīng)生成,只需要根據(jù)實(shí)際路徑進(jìn)行相關(guān)修改(如果一直跟著我的操作,則不需要修改路徑)

? ? ? ?運(yùn)行calculate_dataset.py腳本之后,會(huì)在data文件夾下生成my_train_data.txt文件、my_val_data.txt文件以及my_data.data文件,并在cfg文件夾下生成新的my_yolov3.cfg文件,如下圖所示:

? ? ? ?特別注意:至此,我們已經(jīng)將NWPU-VHR-10 dataset的VOC格式數(shù)據(jù)集轉(zhuǎn)換成了yolo格式的數(shù)據(jù)集.

4. yolov3 spp 項(xiàng)目參數(shù)解析、如何獲取best.pt以及定位到對(duì)應(yīng)的epoch

? ? ? ?首先,我們?cè)谏厦鎦olov3 spp源碼鏈接預(yù)訓(xùn)練權(quán)重下載地址一欄,下載相應(yīng)的權(quán)重,博主使用的是yolov3-spp-ultralytics-512.pt權(quán)重,并存放在根目錄下的weight文件夾下,如下圖所示:

4.1 yolov3 spp 項(xiàng)目部分參數(shù)解析

? ? ? ?為了成功啟動(dòng)項(xiàng)目,首先我們?cè)陧?xiàng)目中定位到train.py文件,找到如下代碼所在位置,將nw設(shè)置為0,因?yàn)椴┲魇窃趙indows下跑項(xiàng)目,所以需要設(shè)置為0:

nw = 0 # min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8]) # number of workers windows默認(rèn)設(shè)置為0

? ? ? 繼續(xù)在train.py文件的關(guān)注以下代碼:

if __name__ == '__main__':parser = argparse.ArgumentParser()parser.add_argument('--epochs', type=int, default=200)parser.add_argument('--batch-size', type=int, default=8)parser.add_argument('--cfg', type=str, default='cfg/my_yolov3.cfg', help="*.cfg path")parser.add_argument('--data', type=str, default='data/my_data.data', help='*.data path')parser.add_argument('--hyp', type=str, default='cfg/hyp.yaml', help='hyperparameters path')parser.add_argument('--multi-scale', type=bool, default=True,help='adjust (67%% - 150%%) img_size every 10 batches')parser.add_argument('--img-size', type=int, default=512, help='test size')parser.add_argument('--rect', action='store_true', help='rectangular training')parser.add_argument('--savebest', type=bool, default=True, help='only save best checkpoint')parser.add_argument('--notest', action='store_true', help='only test final epoch')parser.add_argument('--cache-images', action='store_true', help='cache images for faster training')parser.add_argument('--weights', type=str, default='weights/yolov3-spp-ultralytics-512.pt',help='initial weights path')parser.add_argument('--name', default='', help='renames results.txt to results_name.txt if supplied')parser.add_argument('--device', default='cuda:0', help='device id (i.e. 0 or 0,1 or cpu)')parser.add_argument('--single-cls', action='store_true', help='train as single-class dataset')parser.add_argument('--freeze-layers', type=bool, default=False, help='Freeze non-output layers')parser.add_argument('--seed', type=int, default=42,help='Random seed.')# 是否使用混合精度訓(xùn)練(需要GPU支持混合精度)parser.add_argument("--amp", default=False, help="Use torch.cuda.amp for mixed precision training")opt = parser.parse_args()# 檢查文件是否存在opt.cfg = check_file(opt.cfg)opt.data = check_file(opt.data)opt.hyp = check_file(opt.hyp)print(opt)with open(opt.hyp) as f:hyp = yaml.load(f, Loader=yaml.FullLoader)print('Start Tensorboard with "tensorboard --logdir=runs", view at http://localhost:6006/')tb_writer = SummaryWriter(comment=opt.name)seed_torch(opt.seed)train(hyp)

? ? ? ?特別注意以下幾點(diǎn):
? ? ? ?1.epoch為項(xiàng)目訓(xùn)練的次數(shù),batch_size為批量數(shù),cfg指定到我們生成的my_yolov3.cfg(自己數(shù)據(jù)集生成的配置文件)路徑,其他的都是類(lèi)似.

? ? ? ?2.代碼中的–seed是我后來(lái)添加進(jìn)去的,這是為了能夠復(fù)現(xiàn)實(shí)驗(yàn)效果而編寫(xiě)的(注意,即使固定了隨機(jī)種子,兩次同樣的實(shí)驗(yàn)也不保證實(shí)驗(yàn)數(shù)據(jù)完全一樣,還有很大因素會(huì)影響最終的實(shí)驗(yàn)結(jié)果,只能說(shuō)盡可能減少兩次相同實(shí)驗(yàn)的數(shù)據(jù)誤差),建議加上,其在seed_torch(opt.seed)中調(diào)用,seed_torch的函數(shù)也申明在train.py中(seed的具體取值可以自定義),代碼如下所示:

def seed_torch(seed=42):seed = int(seed)random.seed(seed)os.environ['PYTHONHASHSEED'] = str(seed)np.random.seed(seed)torch.manual_seed(seed)torch.cuda.manual_seed(seed)torch.cuda.manual_seed_all(seed)torch.backends.cudnn.deterministic = Truetorch.backends.cudnn.benchmark = Falsetorch.backends.cudnn.enabled = False

? ? ? ?3.特別注意到–savebest參數(shù),我這里設(shè)置的是true,是因?yàn)槲业男枨笾灰詈蒙傻臋?quán)重文件best.pt;如果想要每一個(gè)epoch的權(quán)重文件,設(shè)置為False即可

4.2 如何獲取best.pt以及定位到對(duì)應(yīng)的epoch

? ? ? ?訓(xùn)練完成之后,會(huì)生成一個(gè)best.pt存放在weight文件夾下,可yolov3 spp 項(xiàng)目本身不會(huì)告訴這個(gè)best.pt對(duì)應(yīng)于哪一個(gè)epoch,因?yàn)槲业男枨笫菍ふ业絙est.pt所對(duì)應(yīng)epoch的map。我們?cè)趖rain.py文件夾中找到了best.pt即為map(0.5-0.9-all)最大一個(gè)所對(duì)應(yīng)的epoch的權(quán)重,如下所示:

? ? ? ?仔細(xì)的發(fā)現(xiàn),在訓(xùn)練完畢之后,會(huì)生成存放每一個(gè)epoch控制臺(tái)輸出的信息在根目錄下的resultxxxx.txt文件中(xxxx中間代表的是數(shù)字,每個(gè)人或許都不相同),我們利用這個(gè)文件導(dǎo)入到excel表格中,找到第二列即為map(0.5-0.9-all)的值,這樣再通過(guò)excel表格公式就可以計(jì)算出這一列最大的值所對(duì)應(yīng)的epoch,從而獲取到對(duì)應(yīng)的map等相關(guān)數(shù)據(jù)。

5. 本篇小結(jié)

? ? ? ?本篇博客主要分析了從NWPU-VHR-10 dataset格式轉(zhuǎn)換為VOC,再次轉(zhuǎn)換為yolo格式,以及在模型配置、模型參數(shù)解析方法的內(nèi)容,通過(guò)仔細(xì)一步步的實(shí)驗(yàn),完成了相關(guān)實(shí)驗(yàn)要求。

? ? ? ?之所以比較啰嗦、詳細(xì)的寫(xiě)實(shí)驗(yàn)文章,最重要的是記錄實(shí)驗(yàn)的所思所想。而且對(duì)于本人基礎(chǔ)比較差,網(wǎng)上的一些博客跳躍比較大,不太適合我,所以本人就詳細(xì)、一步步的記錄和分析實(shí)驗(yàn)的心路歷程,希望能夠讓大家和自己能順心的閱讀。如在復(fù)現(xiàn)本文博客中有任何問(wèn)題,請(qǐng)?jiān)谠u(píng)論區(qū)進(jìn)行交流,這樣大家就會(huì)有更多的收獲,博主也會(huì)及時(shí)的進(jìn)行交流。最后文末附上本人訓(xùn)練好的權(quán)重文件,供大家以需要時(shí)使用

? ? ? ?本人訓(xùn)練好的權(quán)重文件best.pt(提取密碼:1234)

總結(jié)

以上是生活随笔為你收集整理的【目标检测实验系列】使用yolov3 spp训练西工大遥感数据集NWPU VHR-10(包括如何将NWPU VHR-10转为VOC格式和yolov3 spp实验调试的详细步骤,且附上训练完的权重文件)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。