前言
本人從一個(gè)小白,一路走來(lái),已能夠熟練使用YOLOv5算法來(lái)幫助自己解決一些問(wèn)題,早就想分析一下自己的學(xué)習(xí)心得,一直沒(méi)有時(shí)間,最近工作暫時(shí)告一段落,今天抽空寫(xiě)點(diǎn)東西,一是為自己積累一些學(xué)習(xí)筆記,二是可以為一些剛接觸YOLOv5算法的小白們提供一些參考,希望大家看之前能夠動(dòng)動(dòng)你的小手,給我點(diǎn)個(gè)關(guān)注,給文章點(diǎn)個(gè)贊,如果此文確實(shí)給你提供了幫助,希望你能在留言區(qū)打兩個(gè)字個(gè)“此文有用!”,以此來(lái)讓這篇文章獲得更多的流量,讓更多小白能夠看到。
YOLOv5
那么多深度學(xué)習(xí)算法,為什么要用YOLOv5?我覺(jué)得很簡(jiǎn)單,因?yàn)閅OLOv5快、YOLOv5火、YOLOv5流行啊,為什么不用YOLOv7、YOLOv8,因?yàn)椴怀墒臁⒉环€(wěn)定,噱頭大于改進(jìn)。所以你要做深度學(xué)習(xí),用YOLOv5算法是最穩(wěn)妥的,就像做人一樣,不會(huì)很優(yōu)秀,也不會(huì)犯錯(cuò)。中庸之道才是王道。
數(shù)據(jù)集是什么?就是一堆包含你最終要檢測(cè)的目標(biāo)的照片,例如貓、狗、豬。
訓(xùn)練是什么?就是不停的告訴電腦這是貓,這是狗,這是豬的一個(gè)不斷重復(fù)的過(guò)程。
檢測(cè)是什么?就是你教了電腦300遍后,你再拿張有著貓、狗、豬的照片問(wèn)電腦,哪是貓、哪是狗、哪是豬。
我這樣講,大家應(yīng)該對(duì)這是個(gè)怎么回事有個(gè)初步的了解了吧,那我們繼續(xù)往下走。
1.目標(biāo)檢測(cè)整體流程
獲取算法源碼 --------》 配置運(yùn)行環(huán)境 --------》 建立數(shù)據(jù)集 --------》 標(biāo)注數(shù)據(jù)集 --------》 數(shù)據(jù)集分組 --------》 文件、代碼修改 --------》 開(kāi)始訓(xùn)練、得到模型 --------》 用得到的模型、檢測(cè)目標(biāo) --------》 OVER
2.獲取算法源碼
YOLOv5代碼是開(kāi)源的,可以免費(fèi)下載不同的版本,我使用的是YOLOv5 6.1版本,如果你想?yún)⒖嘉业牟襟E做,就下載這個(gè)版本的,別的版本我也不熟悉,操作步驟有可能不同。
yolov5-6.1版本代碼下載地址
點(diǎn)開(kāi)網(wǎng)址后,點(diǎn)擊左上角的master,下拉框里選擇v6.1版本,如下圖:
然后點(diǎn)擊右邊的克隆,下載源代碼里選擇zip,這樣就把源代碼下載下來(lái)了。如下圖:
3.配置運(yùn)行環(huán)境
有了代碼,怎樣讓代碼運(yùn)行通,這需要配置環(huán)境,新手小白在這一步搞個(gè)三五天很正常,各種錯(cuò)誤,各種bug,我也是這么過(guò)來(lái)的,但是你配置好環(huán)境,運(yùn)行代碼沒(méi)有bug后,你會(huì)感覺(jué)非常有成就感。記住,不要放棄,就一點(diǎn)一點(diǎn)的解決bug,不要中途放棄。
這一塊有其他博主寫(xiě)的很好很詳細(xì),大家可以點(diǎn)進(jìn)去跟著做。
點(diǎn)擊配置YOLOv5運(yùn)行環(huán)境
我的環(huán)境貼出來(lái)給大家看看:
我用的是筆記本:聯(lián)想拯救者Y9000P2022
操作系統(tǒng):windows11
顯卡:RTX 3060
編譯軟件:Pycharm
python版本:3.9.1
pytorch版本:1.9.1
cuda版本:11.1
怎樣判斷環(huán)境是否配置好了呢?那就是運(yùn)行檢測(cè)程序——detcet.py文件,然后會(huì)在runs文件夾里的detcet文件夾下生成一個(gè)exp文件夾,里面是一張框出幾個(gè)人或者車(chē)的圖片,具體什么我也忘了。反正就是能夠運(yùn)行detect.py文件。
4.建立數(shù)據(jù)集
在data目錄下新建Annotations, images, ImageSets, labels 四個(gè)文件夾。
目錄結(jié)構(gòu)如下圖所示:
其中images存放的是原始的圖片數(shù)據(jù)集,Annotations存放的是標(biāo)記后生成的xml文件,labels存放的是保存標(biāo)記內(nèi)容的txt文件,ImageSets存放的是訓(xùn)練數(shù)據(jù)集和測(cè)試數(shù)據(jù)集的分類(lèi)情況。
巧婦難為無(wú)米之炊。做好準(zhǔn)備工作之后,下一步要干什么?
那就是數(shù)據(jù),數(shù)據(jù)從哪里來(lái)?一個(gè)就是下載網(wǎng)上公開(kāi)的數(shù)據(jù)集,一個(gè)就是自己收集數(shù)據(jù)集。
基本上現(xiàn)在研究YOLOv5算法的主要有兩個(gè)方向:一是在源代碼上做文章,以此能夠達(dá)到提高精度或者提高速度的目標(biāo),然后發(fā)論文。二是在數(shù)據(jù)集上做文章,用自己的數(shù)據(jù)集去解決某一實(shí)際問(wèn)題,然后發(fā)論文。往往第一種方向會(huì)用公開(kāi)的數(shù)據(jù)集,第二個(gè)方向會(huì)用自己的數(shù)據(jù)集。由于第一個(gè)方向需要對(duì)編程和數(shù)學(xué)有深厚的學(xué)識(shí)造詣,所以自然而然我就奔向了第二個(gè)方向。我使用的是自己的數(shù)據(jù)集。
5.標(biāo)注數(shù)據(jù)集
Yolov5 使用精靈標(biāo)注助手制作數(shù)據(jù)集詳細(xì)教程
6.數(shù)據(jù)集分組
在yolov5-6.1根目錄下新建一個(gè)文件makeTxt.py,(根目錄大家都知道啥意思吧?我還真擔(dān)心有人不知道)
代碼如下:
import os
import random
trainval_percent = 0.9
train_percent = 0.9
xmlfilepath = 'data/Annotations'
txtsavepath = 'data/ImageSets'
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('data/ImageSets/trainval.txt', 'w')
ftest = open('data/ImageSets/test.txt', 'w')
ftrain = open('data/ImageSets/train.txt', 'w')
fval = open('data/ImageSets/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()
接著再新建另一個(gè)文件voc_label.py,切記,代碼中classes=[……] 中填入的一定要是自己在數(shù)據(jù)集中所標(biāo)注的類(lèi)別名稱(chēng),標(biāo)記了幾個(gè)類(lèi)別就填寫(xiě)幾個(gè)類(lèi)別名,填寫(xiě)錯(cuò)誤的話會(huì)造成讀取不出xml文件里的標(biāo)注信息。代碼如下:
# -*- coding: utf-8 -*-
# xml解析包
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
sets = ['train', 'test', 'val']
classes = ['cat', 'dog', 'pig']
# 進(jìn)行歸一化操作
def convert(size, box): # size:(原圖w,原圖h) , box:(xmin,xmax,ymin,ymax)
dw = 1./size[0] # 1/w
dh = 1./size[1] # 1/h
x = (box[0] + box[1])/2.0 # 物體在圖中的中心點(diǎn)x坐標(biāo)
y = (box[2] + box[3])/2.0 # 物體在圖中的中心點(diǎn)y坐標(biāo)
w = box[1] - box[0] # 物體實(shí)際像素寬度
h = box[3] - box[2] # 物體實(shí)際像素高度
x = x*dw # 物體中心點(diǎn)x的坐標(biāo)比(相當(dāng)于 x/原圖w)
w = w*dw # 物體寬度的寬度比(相當(dāng)于 w/原圖w)
y = y*dh # 物體中心點(diǎn)y的坐標(biāo)比(相當(dāng)于 y/原圖h)
h = h*dh # 物體寬度的寬度比(相當(dāng)于 h/原圖h)
return (x, y, w, h) # 返回 相對(duì)于原圖的物體中心點(diǎn)的x坐標(biāo)比,y坐標(biāo)比,寬度比,高度比,取值范圍[0-1]
# year ='2012', 對(duì)應(yīng)圖片的id(文件名)
def convert_annotation(image_id):
'''
將對(duì)應(yīng)文件名的xml文件轉(zhuǎn)化為label文件,xml文件包含了對(duì)應(yīng)的bunding框以及圖片長(zhǎng)款大小等信息,
通過(guò)對(duì)其解析,然后進(jìn)行歸一化最終讀到label文件中去,也就是說(shuō)
一張圖片文件對(duì)應(yīng)一個(gè)xml文件,然后通過(guò)解析和歸一化,能夠?qū)?duì)應(yīng)的信息保存到唯一一個(gè)label文件中去
labal文件中的格式:calss x y w h 同時(shí),一張圖片對(duì)應(yīng)的類(lèi)別有多個(gè),所以對(duì)應(yīng)的bunding的信息也有多個(gè)
'''
# 對(duì)應(yīng)的通過(guò)year 找到相應(yīng)的文件夾,并且打開(kāi)相應(yīng)image_id的xml文件,其對(duì)應(yīng)bund文件
in_file = open('data/Annotations/%s.xml' % (image_id), encoding='utf-8')
# 準(zhǔn)備在對(duì)應(yīng)的image_id 中寫(xiě)入對(duì)應(yīng)的label,分別為
# <object-class> <x> <y> <width> <height>
out_file = open('data/labels/%s.txt' % (image_id), 'w', encoding='utf-8')
# 解析xml文件
tree = ET.parse(in_file)
# 獲得對(duì)應(yīng)的鍵值對(duì)
root = tree.getroot()
# 獲得圖片的尺寸大小
size = root.find('size')
# 如果xml內(nèi)的標(biāo)記為空,增加判斷條件
if size != None:
# 獲得寬
w = int(size.find('width').text)
# 獲得高
h = int(size.find('height').text)
# 遍歷目標(biāo)obj
for obj in root.iter('object'):
# 獲得difficult ??
difficult = obj.find('difficult').text
# 獲得類(lèi)別 =string 類(lèi)型
cls = obj.find('name').text
# 如果類(lèi)別不是對(duì)應(yīng)在我們預(yù)定好的class文件中,或difficult==1則跳過(guò)
if cls not in classes or int(difficult) == 1:
continue
# 通過(guò)類(lèi)別名稱(chēng)找到id
cls_id = classes.index(cls)
# 找到bndbox 對(duì)象
xmlbox = obj.find('bndbox')
# 獲取對(duì)應(yīng)的bndbox的數(shù)組 = ['xmin','xmax','ymin','ymax']
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
float(xmlbox.find('ymax').text))
print(image_id, cls, b)
# 帶入進(jìn)行歸一化操作
# w = 寬, h = 高, b= bndbox的數(shù)組 = ['xmin','xmax','ymin','ymax']
bb = convert((w, h), b)
# bb 對(duì)應(yīng)的是歸一化后的(x,y,w,h)
# 生成 calss x y w h 在label文件中
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
# 返回當(dāng)前工作目錄
wd = getcwd()
print(wd)
for image_set in sets:
'''
對(duì)所有的文件數(shù)據(jù)集進(jìn)行遍歷
做了兩個(gè)工作:
?。保畬⑺袌D片文件都遍歷一遍,并且將其所有的全路徑都寫(xiě)在對(duì)應(yīng)的txt文件中去,方便定位
?。玻瑫r(shí)對(duì)所有的圖片文件進(jìn)行解析和轉(zhuǎn)化,將其對(duì)應(yīng)的bundingbox 以及類(lèi)別的信息全部解析寫(xiě)到label 文件中去
最后再通過(guò)直接讀取文件,就能找到對(duì)應(yīng)的label 信息
'''
# 先找labels文件夾如果不存在則創(chuàng)建
if not os.path.exists('data/labels/'):
os.makedirs('data/labels/')
# 讀取在ImageSets/Main 中的train、test..等文件的內(nèi)容
# 包含對(duì)應(yīng)的文件名稱(chēng)
image_ids = open('data/ImageSets/%s.txt' % (image_set)).read().strip().split()
# 打開(kāi)對(duì)應(yīng)的2012_train.txt 文件對(duì)其進(jìn)行寫(xiě)入準(zhǔn)備
list_file = open('data/%s.txt' % (image_set), 'w')
# 將對(duì)應(yīng)的文件_id以及全路徑寫(xiě)進(jìn)去并換行
for image_id in image_ids:
list_file.write('data/images/%s.jpg\n' % (image_id))
# 調(diào)用 year = 年份 image_id = 對(duì)應(yīng)的文件名_id
convert_annotation(image_id)
# 關(guān)閉文件
list_file.close()
# os.system(‘comand’) 會(huì)執(zhí)行括號(hào)中的命令,如果命令成功執(zhí)行,這條語(yǔ)句返回0,否則返回1
# os.system("cat 2007_train.txt 2007_val.txt 2012_train.txt 2012_val.txt > train.txt")
# os.system("cat 2007_train.txt 2007_val.txt 2007_test.txt 2012_train.txt 2012_val.txt > train.all.txt")
先運(yùn)行makeTxt.py,makeTxt.py主要是將數(shù)據(jù)集分類(lèi)成訓(xùn)練數(shù)據(jù)集和測(cè)試數(shù)據(jù)集,默認(rèn)train,val,test按照8:1:1的比例進(jìn)行隨機(jī)分類(lèi),運(yùn)行后ImagesSets文件夾中會(huì)出現(xiàn)四個(gè)文件,主要是生成的訓(xùn)練數(shù)據(jù)集和測(cè)試數(shù)據(jù)集的圖片名稱(chēng),如下圖:
這幾個(gè)txt你要點(diǎn)開(kāi)看看,如果時(shí)空白的,說(shuō)明你出錯(cuò)了,如果里面是下圖這樣的內(nèi)容,說(shuō)明沒(méi)問(wèn)題。
再運(yùn)行voc_label.py文件,主要是將圖片數(shù)據(jù)集標(biāo)注后的xml文件中的標(biāo)注信息讀取出來(lái)并寫(xiě)入txt文件,運(yùn)行后在labels文件夾中出現(xiàn)所有圖片數(shù)據(jù)集的標(biāo)注信息,如下圖:
到這一步,已經(jīng)把米準(zhǔn)備好了,下一步就是修改幾個(gè)參數(shù)就可以訓(xùn)練了。是不是感覺(jué)很麻煩,其實(shí)不是,你以后這些步驟都不用再做了,主要做的就是train和detect,也就是訓(xùn)練和檢測(cè)。
7.文件、代碼修改
7.1 文件修改
首先在data目錄下,找到存在的coco.yaml,然后復(fù)制一份重命名為animal.yaml,這個(gè)animal是我的名字,你的名字自己取就行,打開(kāi)animal.yaml,其中path,train,val,test分別為數(shù)據(jù)集的路徑, nc為數(shù)據(jù)集的類(lèi)別數(shù),我這里只分了3類(lèi),names為類(lèi)別的名稱(chēng)。這幾個(gè)參數(shù)均按照自己的實(shí)際需求來(lái)修改。總之,這一步只用修改nc后面的數(shù)字,別的不要?jiǎng)印?br> animal.yaml的代碼如下:
# Train command: python train.py --data data/cat.yaml
# Dataset should be placed next to yolov5 folder:
# parent
# ├── yolov5
# └── data
# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
path: data # dataset root dir
train: train.txt # train images (relative to 'path')
val: val.txt # val images (relative to 'path')
test: test.txt # test images (optional)
# number of classes
nc: 3
# class names
names: ['cat', 'dog', 'pig']
7.2 代碼修改
yolov5提供了好幾個(gè)訓(xùn)練模型,讓你作為“基模型”進(jìn)行訓(xùn)練,我用的是yolov5s模型,所以我找到models目錄下提供了yolov5s.yaml文件進(jìn)行修改,只用修改類(lèi)的個(gè)數(shù),我的類(lèi)是3,所以nc:3.你根據(jù)你的實(shí)際情況決定n是多少,其余的不用修改。代碼如下:
# Parameters
nc: 3 # number of classes
depth_multiple: 1.0 # model depth multiple
width_multiple: 1.0 # layer channel multiple
最后要對(duì)train.py中的一些參數(shù)進(jìn)行修改,train.py顧名思義就是用來(lái)訓(xùn)練的,
我們平時(shí)訓(xùn)練的話,主要用到的只有這幾個(gè)參數(shù)而已:–weights,–cfg,–data,–epochs,–batch-size,–img-size,–project。
parser = argparse.ArgumentParser()
# 加載預(yù)訓(xùn)練的模型權(quán)重文件,如果文件夾下沒(méi)有該文件,則在訓(xùn)練前會(huì)自動(dòng)下載
parser.add_argument('--weights', type=str, default=ROOT / 'yolov5s.pt', help='initial weights path')
# 模型配置文件,網(wǎng)絡(luò)結(jié)構(gòu),使用修改好的yolov5l.yaml文件
parser.add_argument('--cfg', type=str, default='models/yolov5s.yaml', help='model.yaml path')
# 數(shù)據(jù)集配置文件,數(shù)據(jù)集路徑,類(lèi)名等,使用配置好的animal.yaml文件
parser.add_argument('--data', type=str, default=ROOT / 'data/animal.yaml', help='dataset.yaml path')
# 超參數(shù)文件
parser.add_argument('--hyp', type=str, default=ROOT / 'data/hyps/hyp.scratch.yaml', help='hyperparameters path')
# 訓(xùn)練總輪次,1個(gè)epoch等于使用訓(xùn)練集中的全部樣本訓(xùn)練一次,值越大模型越精確,訓(xùn)練時(shí)間也越長(zhǎng),默認(rèn)為300
parser.add_argument('--epochs', type=int, default=300)
# 批次大小,一次訓(xùn)練所選取的樣本數(shù),顯卡不太行的話,就調(diào)小點(diǎn),反正3060是帶不動(dòng)batch-size=16的,傳-1的話就是autobatch
parser.add_argument('--batch-size', type=int, default=16, help='total batch size for all GPUs')
# 輸入圖片分辨率大小,默認(rèn)為640
parser.add_argument('--imgsz', '--img', '--img-size', type=int, default=640, help='train, val image size (pixels)')
# 是否采用矩形訓(xùn)練,默認(rèn)False,開(kāi)啟后可顯著的減少推理時(shí)間
parser.add_argument('--rect', action='store_true', help='rectangular training')
# 繼續(xù)訓(xùn)練,默認(rèn)從打斷后的最后一次訓(xùn)練繼續(xù),需開(kāi)啟default=True
parser.add_argument('--resume', nargs='?', const=True, default=False, help='resume most recent training')
# 僅保存最終一次epoch所產(chǎn)生的模型
parser.add_argument('--nosave', action='store_true', help='only save final checkpoint')
# 僅在最終一次epoch后進(jìn)行測(cè)試
parser.add_argument('--noval', action='store_true', help='only validate final epoch')
# 禁用自動(dòng)錨點(diǎn)檢查
parser.add_argument('--noautoanchor', action='store_true', help='disable autoanchor check')
# 超參數(shù)演變
parser.add_argument('--evolve', type=int, nargs='?', const=300, help='evolve hyperparameters for x generations')
# 谷歌云盤(pán)bucket,一般不會(huì)用到
parser.add_argument('--bucket', type=str, default='', help='gsutil bucket')
# 是否提前緩存圖片到內(nèi)存,以加快訓(xùn)練速度,默認(rèn)False
parser.add_argument('--cache', type=str, nargs='?', const='ram', help='--cache images in "ram" (default) or "disk"')
# 選用加權(quán)圖像進(jìn)行訓(xùn)練
parser.add_argument('--image-weights', action='store_true', help='use weighted image selection for training')
# 訓(xùn)練的設(shè)備,cpu;0(表示一個(gè)gpu設(shè)備cuda:0);0,1,2,3(多個(gè)gpu設(shè)備)。值為空時(shí),訓(xùn)練時(shí)默認(rèn)使用計(jì)算機(jī)自帶的顯卡或CPU
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
# 是否進(jìn)行多尺度訓(xùn)練,默認(rèn)False
parser.add_argument('--multi-scale', action='store_true', help='vary img-size +/- 50%%')
# 數(shù)據(jù)集是否只有一個(gè)類(lèi)別,默認(rèn)False
parser.add_argument('--single-cls', action='store_true', help='train multi-class data as single-class')
# 是否使用adam優(yōu)化器,默認(rèn)False
parser.add_argument('--adam', action='store_true', help='use torch.optim.Adam() optimizer')
# 是否使用跨卡同步BN,在DDP模式使用
parser.add_argument('--sync-bn', action='store_true', help='use SyncBatchNorm, only available in DDP mode')
# dataloader的最大worker數(shù)量,大于0時(shí)使用子進(jìn)程讀取數(shù)據(jù),訓(xùn)練程序有可能會(huì)卡住
parser.add_argument('--workers', type=int, default=8, help='maximum number of dataloader workers')
# 訓(xùn)練結(jié)果所存放的路徑,默認(rèn)為runs/train
parser.add_argument('--project', default=ROOT / 'runs/train', help='save to project/name')
# 訓(xùn)練結(jié)果所在文件夾的名稱(chēng),默認(rèn)為exp
parser.add_argument('--name', default='exp', help='save to project/name')
# 如訓(xùn)練結(jié)果存放路徑重名,不覆蓋已存在的文件夾
parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
# 使用四合一dataloader
parser.add_argument('--quad', action='store_true', help='quad dataloader')
# 線性學(xué)習(xí)率
parser.add_argument('--linear-lr', action='store_true', help='linear LR')
# 標(biāo)簽平滑處理,默認(rèn)0.0
parser.add_argument('--label-smoothing', type=float, default=0.0, help='Label smoothing epsilon')
# 已訓(xùn)練多少次epoch后結(jié)果仍沒(méi)有提升就終止訓(xùn)練,默認(rèn)100
parser.add_argument('--patience', type=int, default=100, help='EarlyStopping patience (epochs without improvement)')
# 凍結(jié)模型層數(shù),默認(rèn)0不凍結(jié),凍結(jié)主干網(wǎng)就傳10,凍結(jié)所有就傳24
parser.add_argument('--freeze', type=int, default=0, help='Number of layers to freeze. backbone=10, all=24')
# 設(shè)置多少次epoch保存一次模型
parser.add_argument('--save-period', type=int, default=-1, help='Save checkpoint every x epochs (disabled if < 1)')
# 分布式訓(xùn)練參數(shù),請(qǐng)勿修改
parser.add_argument('--local_rank', type=int, default=-1, help='DDP parameter, do not modify')
# Weights & Biases arguments(一般上用不著)
parser.add_argument('--entity', default=None, help='W&B: Entity')
parser.add_argument('--upload_dataset', action='store_true', help='W&B: Upload dataset as artifact table')
parser.add_argument('--bbox_interval', type=int, default=-1, help='W&B: Set bounding-box image logging interval')
parser.add_argument('--artifact_alias', type=str, default='latest', help='W&B: Version of dataset artifact to use')
opt = parser.parse_known_args()[0] if known else parser.parse_args()
6.開(kāi)始訓(xùn)練、得到模型
完成以上步驟后,把電腦所以運(yùn)行的程序都關(guān)掉,就留一個(gè)P有Charm,運(yùn)行train.py文件開(kāi)始訓(xùn)練,這是就是檢驗(yàn)?zāi)汶娔X性能的時(shí)候了,主要是看你的顯卡性能,時(shí)間長(zhǎng)短由你的顯卡性能決定,也由你的數(shù)據(jù)集的數(shù)量決定。
當(dāng)程序出現(xiàn)如下界面時(shí),說(shuō)明程序開(kāi)始訓(xùn)練,沒(méi)有錯(cuò)誤。
為了截圖,我的訓(xùn)練輪次epochs設(shè)置的時(shí)2次,默認(rèn)是300次。
當(dāng)訓(xùn)練結(jié)束后,會(huì)在runs文件夾下train文件里生成一個(gè)exp文件夾,里面有一堆文件,其中的weights里的best.pt就是訓(xùn)練得到的檢測(cè)模型,用于下一步的檢測(cè)之中。如下圖所示:
6.用得到的模型、檢測(cè)目標(biāo)
有了訓(xùn)練得到的模型后,下一步我們就要干嘛了?檢測(cè)試驗(yàn),看訓(xùn)練得到的檢測(cè)模型好不好用?那就要在detect.py文件里做文章了,這里面也有一些參數(shù)需要設(shè)置一下,主要用到的有這幾個(gè)參數(shù):–weights,–source,–img-size,–conf-thres,–project。詳細(xì)如下圖所示:
parser = argparse.ArgumentParser()
# 選用訓(xùn)練的權(quán)重,不指定的話會(huì)使用yolov5l.pt預(yù)訓(xùn)練權(quán)重
parser.add_argument('--weights', nargs='+', type=str, default=ROOT / 'runs/train/exp/weights/best.pt', help='model path(s)')
# 檢測(cè)數(shù)據(jù),可以是圖片/視頻路徑,也可以是'0'(電腦自帶攝像頭),也可以是rtsp等視頻流
parser.add_argument('--source', type=str, default=ROOT / 'inference/videos/動(dòng)物識(shí)別.mp4', help='file/dir/URL/glob, 0 for webcam')
# 指定推理圖片分辨率,默認(rèn)640
parser.add_argument('--imgsz', '--img', '--img-size', nargs='+', type=int, default=[640], help='inference size h,w')
# 置信度閾值,檢測(cè)到的對(duì)象屬于特定類(lèi)(狗,貓,香蕉,汽車(chē)等)的概率,默認(rèn)為0.25
parser.add_argument('--conf-thres', type=float, default=0.25, help='confidence threshold')
# 指定NMS(非極大值抑制)的IOU閾值,默認(rèn)為0.45
parser.add_argument('--iou-thres', type=float, default=0.45, help='NMS IoU threshold')
# 每張圖最多檢測(cè)多少目標(biāo),默認(rèn)為1000個(gè)
parser.add_argument('--max-det', type=int, default=1000, help='maximum detections per image')
# 檢測(cè)的設(shè)備,cpu;0(表示一個(gè)gpu設(shè)備cuda:0);0,1,2,3(多個(gè)gpu設(shè)備)。值為空時(shí),訓(xùn)練時(shí)默認(rèn)使用計(jì)算機(jī)自帶的顯卡或CPU
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
# 是否展示檢測(cè)之后的圖片/視頻,默認(rèn)False
parser.add_argument('--view-img', action='store_true', help='show results')
# 是否將檢測(cè)的框坐標(biāo)以txt文件形式保存(yolo格式),默認(rèn)False
parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')
# 在輸出標(biāo)簽結(jié)果txt中同樣寫(xiě)入每個(gè)目標(biāo)的置信度,默認(rèn)False
parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')
# 從圖片\視頻上把檢測(cè)到的目標(biāo)摳出來(lái)保存,默認(rèn)False
parser.add_argument('--save-crop', action='store_true', help='save cropped prediction boxes')
# 不保存圖片/視頻,默認(rèn)False
parser.add_argument('--nosave', action='store_true', help='do not save images/videos')
# 設(shè)置只檢測(cè)特定的類(lèi),如--classes 0 2 4 6 8,默認(rèn)False
parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --classes 0, or --classes 0 2 3')
# 使用agnostic NMS(前背景),默認(rèn)False
parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')
# 推理的時(shí)候進(jìn)行多尺度,翻轉(zhuǎn)等操作(TTA)推理,屬于增強(qiáng)識(shí)別,速度會(huì)慢不少,默認(rèn)False
parser.add_argument('--augment', action='store_true', help='augmented inference')
# 特征可視化,默認(rèn)False
parser.add_argument('--visualize', action='store_true', help='visualize features')
# 更新所有模型,默認(rèn)False
parser.add_argument('--update', action='store_true', help='update all models')
# 檢測(cè)結(jié)果所存放的路徑,默認(rèn)為runs/detect
parser.add_argument('--project', default=ROOT / 'runs/detect', help='save results to project/name')
# 檢測(cè)結(jié)果所在文件夾的名稱(chēng),默認(rèn)為exp
parser.add_argument('--name', default='exp', help='save results to project/name')
# 若現(xiàn)有的project/name存在,則不進(jìn)行遞增
parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
# 畫(huà)圖時(shí)線條寬度
parser.add_argument('--line-thickness', default=3, type=int, help='bounding box thickness (pixels)')
# 隱藏標(biāo)簽
parser.add_argument('--hide-labels', default=False, action='store_true', help='hide labels')
# 隱藏置信度
parser.add_argument('--hide-conf', default=False, action='store_true', help='hide confidences')
# 半精度檢測(cè)(FP16)
parser.add_argument('--half', action='store_true', help='use FP16 half-precision inference')
# 在onnx推理中使用OpenCV DNN
parser.add_argument('--dnn', action='store_true', help='use OpenCV DNN for ONNX inference')
opt = parser.parse_args()
修改好參數(shù)后,直接執(zhí)行detect.py文件,如果不更改檢測(cè)結(jié)果所產(chǎn)生的路徑的話,檢測(cè)完成后會(huì)在runs/detect/exp文件夾得到檢測(cè)后的圖片。如下圖所示:
至此,整個(gè)從0到最后能檢測(cè)你想要的目標(biāo)就結(jié)束了,到這里是不感覺(jué)一點(diǎn)都不難了。萬(wàn)事開(kāi)頭難真的是句真理,等你掌握了這一整套流程后,你會(huì)發(fā)現(xiàn)你有很多好想法,利用YOLOv5算法來(lái)解決生活中很多的現(xiàn)實(shí)問(wèn)題。
大家過(guò)程中有什么問(wèn)題,可以在文章下面留言,我看到會(huì)及時(shí)回復(fù)的,別的朋友看到也會(huì)回復(fù)的!
再次提醒,別忘了點(diǎn)贊、關(guān)注、留言!
我告訴大家一個(gè)很殘酷的現(xiàn)實(shí),那就是你在學(xué)習(xí)過(guò)程中,出現(xiàn)bug了,你想讓哪位博主給你解決,或者你想加別人聯(lián)系方式,然后想有問(wèn)題就問(wèn),在無(wú)償情況下是不可能的,因?yàn)榇蠹叶己苊Γ晕腋嬖V大家一個(gè)方法,那就是遇到問(wèn)題,就在CSDN上查,沒(méi)有什么解決不了的,反正我一路走來(lái),遇到的所以問(wèn)題都是在CSDN上自己查的解決方法,相信我,你也可以的。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-428693.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-428693.html
到了這里,關(guān)于手把手教你用YOLOv5算法訓(xùn)練數(shù)據(jù)和檢測(cè)目標(biāo)(不會(huì)你捶我)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!