一、寫(xiě)在前面
?博主也是最近開(kāi)始玩yolov5的,甚至也是最近開(kāi)始使用python的,很多東西都沒(méi)有接觸過(guò),因此訓(xùn)練自己的數(shù)據(jù)集花了不少時(shí)間,所以想寫(xiě)篇博客記錄一下,希望同樣是零基礎(chǔ)的小伙伴們可以更加輕松的上手。同時(shí)大家如果發(fā)現(xiàn)了錯(cuò)誤和理解偏差,歡迎指正。
參考資料:
- Yolov5訓(xùn)練自己的數(shù)據(jù)集(詳細(xì)完整版)
- 訓(xùn)練集、驗(yàn)證集、測(cè)試集的劃分
- yolov5 訓(xùn)練結(jié)果解析
- 關(guān)于yolov5的一些說(shuō)明(txt文件、訓(xùn)練結(jié)果分析等)
?
本教程所安裝版本:
- pycahrm:2021.3.3
- Anconda:2022.05
- python:3.9
- yolov5:v6.2
- pytorch:CUDA 11.6
踩坑經(jīng)歷:
- 路徑中就不要有
短橫杠-
以及空格
等等特殊字符,中文更不能要有?
。否則在之后訓(xùn)練時(shí)會(huì)出現(xiàn)各種路徑找不到的問(wèn)題?? - 使用pip等下載指令時(shí)最好不要掛VPN,否則可能會(huì)下載失敗
?在上一篇博客里 博客鏈接,我們完成了yolov5的安裝和相關(guān)環(huán)境的配置,在這篇博客里,我們繼續(xù)yolov5的學(xué)習(xí),嘗試訓(xùn)練自己的數(shù)據(jù)集
二、使用labelimg標(biāo)記圖片
1.準(zhǔn)備工作
- 在yolov5目錄下新建一個(gè)名為
VOCData
的文件夾 - 在VOCData文件夾下創(chuàng)建
Annotations
和images
文件夾(【??易錯(cuò)】:images的文件名不建議修改,否則之后訓(xùn)練時(shí)容易出現(xiàn)No labels found
的錯(cuò)誤,原因見(jiàn)下)
[說(shuō)明]:
-
Annotations
文件夾用于存放使用labelimg標(biāo)記后的圖片(XML格式) -
images
文件夾用于存放用于標(biāo)記的圖片
(【??易錯(cuò)】:images
文件夾下直接放圖片,內(nèi)部不要嵌套有文件夾,否則之后訓(xùn)練可能會(huì)出現(xiàn)No label found
的錯(cuò)誤,具體原因見(jiàn)下文中xml_to_yolo.py
文件的第67行)
[為什么]:
?在 yolov5 的 utils 文件夾打開(kāi) dataloaders.py
文件后,搜索define,便可以找到這樣的一段代碼:
?該段代碼的作用是由images文件夾的地址直接推出labels文件夾的位置,所以我們存儲(chǔ)圖片的文件必須叫做images
,同時(shí)labels文件必須和images文件必須在同一目錄下(先不管labels具體是什么,有個(gè)基本的概念即可,接下來(lái)會(huì)細(xì)說(shuō))
2.標(biāo)記圖片
-
在cmd窗口下輸入
labelimg
或者運(yùn)行labelimg.py
文件進(jìn)入labelimg的可執(zhí)行程序(注:如果是在虛擬環(huán)境下安裝的labelimg,記得先激活虛擬環(huán)境) -
分別設(shè)置需要標(biāo)注圖片的文件夾和存放標(biāo)記結(jié)果的文件夾的地址
-
推薦設(shè)置自動(dòng)保存
-
標(biāo)記圖片快捷鍵:
w:標(biāo)記
?a:上一張圖片
?d:下一張圖片
標(biāo)注的時(shí)候盡可能貼近物體輪廓
?不知道有沒(méi)有和我一樣開(kāi)始只能標(biāo)記方形框的,按住ctrl+shift+R
就可以恢復(fù)創(chuàng)建矩形框
?在Annotations文件夾下可以看到我們標(biāo)記好的XML文件
三、 劃分?jǐn)?shù)據(jù)集以及配置文件修改
1. 劃分訓(xùn)練集、驗(yàn)證集、測(cè)試集
?在VOCData目錄下創(chuàng)建程序 split_train_val.py
并運(yùn)行以下代碼。代碼可以不做任何修改
# coding:utf-8
import os
import random
import argparse
parser = argparse.ArgumentParser()
#xml文件的地址,根據(jù)自己的數(shù)據(jù)進(jìn)行修改 xml一般存放在Annotations下
parser.add_argument('--xml_path', default='Annotations', type=str, help='input xml label path')
#數(shù)據(jù)集的劃分,地址選擇自己數(shù)據(jù)下的ImageSets/Main
parser.add_argument('--txt_path', default='ImageSets/Main', type=str, help='output txt label path')
opt = parser.parse_args()
trainval_percent = 1.0 # 訓(xùn)練集和驗(yàn)證集所占比例。 這里沒(méi)有劃分測(cè)試集
train_percent = 0.9 # 訓(xùn)練集所占比例,可自己進(jìn)行調(diào)整
xmlfilepath = opt.xml_path
txtsavepath = opt.txt_path
total_xml = os.listdir(xmlfilepath)
if not os.path.exists(txtsavepath):
os.makedirs(txtsavepath)
num = len(total_xml)
list_index = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list_index, tv)
train = random.sample(trainval, tr)
file_trainval = open(txtsavepath + '/trainval.txt', 'w')
file_test = open(txtsavepath + '/test.txt', 'w')
file_train = open(txtsavepath + '/train.txt', 'w')
file_val = open(txtsavepath + '/val.txt', 'w')
for i in list_index:
name = total_xml[i][:-4] + '\n'
if i in trainval:
file_trainval.write(name)
if i in train:
file_train.write(name)
else:
file_val.write(name)
else:
file_test.write(name)
file_trainval.close()
file_train.close()
file_val.close()
file_test.close()
運(yùn)行結(jié)束后會(huì)在生成一個(gè)名為 ImageSets 的文件夾:
?測(cè)試集里的內(nèi)容為空,因?yàn)樵趧澐謹(jǐn)?shù)據(jù)的時(shí)候,將90%
的數(shù)據(jù)劃分到訓(xùn)練集,將10%
的數(shù)據(jù)劃分到訓(xùn)練集。如果要分配,則調(diào)整上面14,15行代碼中trainval和train的所占的比例
[說(shuō)明]:
- 訓(xùn)練集是用來(lái)訓(xùn)練模型的,通過(guò)嘗試不同的方法和思路使用訓(xùn)練集來(lái)訓(xùn)練不同的模型
- 驗(yàn)證集使用交叉驗(yàn)證來(lái)挑選最優(yōu)的模型,通過(guò)不斷的迭代來(lái)改善模型在驗(yàn)證集上的性能
- 測(cè)試集用來(lái)評(píng)估模型的性能
2.XML格式轉(zhuǎn)yolo_txt格式
在VOCData目錄下創(chuàng)建程序 xml_to_yolo.py
并運(yùn)行以下代碼,注意:
- 將classes改為自己標(biāo)注時(shí)設(shè)置的類(lèi)名(我這里叫"DM")
- 將各個(gè)絕對(duì)路徑修改為自己的
-
\
是 python中的轉(zhuǎn)義字符,所以表示地址時(shí)要使用\\
取消轉(zhuǎn)義,或者/
# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
import os
from os import getcwd
sets = ['train', 'val', 'test']
classes = ["DM"] # 改成自己的類(lèi)別
abs_path = os.getcwd()
print(abs_path)
def convert(size, box):
dw = 1. / (size[0])
dh = 1. / (size[1])
x = (box[0] + box[1]) / 2.0 - 1
y = (box[2] + box[3]) / 2.0 - 1
w = box[1] - box[0]
h = box[3] - box[2]
x = x * dw
w = w * dw
y = y * dh
h = h * dh
return x, y, w, h
def convert_annotation(image_id):
in_file = open('D:/yolov5/VOCData/Annotations/%s.xml' % (image_id), encoding='UTF-8')
out_file = open('D:/yolov5/VOCData/labels/%s.txt' % (image_id), 'w')
tree = ET.parse(in_file)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
for obj in root.iter('object'):
difficult = obj.find('difficult').text
# difficult = obj.find('Difficult').text
cls = obj.find('name').text
if cls not in classes or int(difficult) == 1:
continue
cls_id = classes.index(cls)
xmlbox = obj.find('bndbox')
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
float(xmlbox.find('ymax').text))
b1, b2, b3, b4 = b
# 標(biāo)注越界修正
if b2 > w:
b2 = w
if b4 > h:
b4 = h
b = (b1, b2, b3, b4)
bb = convert((w, h), b)
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
wd = getcwd()
for image_set in sets:
if not os.path.exists('D:/yolov5/VOCData/labels/'):
os.makedirs('D:/yolov5/VOCData/labels/')
image_ids = open('D:/yolov5/VOCData/ImageSets/Main/%s.txt' % (image_set)).read().strip().split()
if not os.path.exists('D:/yolov5/VOCData/dataSet_path/'):
os.makedirs('D:/yolov5/VOCData/dataSet_path/')
# 這行路徑不需更改,這是相對(duì)路徑
list_file = open('dataSet_path/%s.txt' % image_set, 'w')
# 圖片格式為jpg則設(shè)置為 .jpg, 如果為png則設(shè)置為 .png。否則會(huì)出現(xiàn)路徑找不到的問(wèn)題
for image_id in image_ids:
list_file.write('D:/yolov5/VOCData/images/%s.jpg\n' % image_id)
convert_annotation(image_id)
list_file.close()
【??易錯(cuò) 】:第59行代碼中的split()
函數(shù)是以空格作為分隔符的,因此如果你的xml文件名中帶有空格,就會(huì)將文件名錯(cuò)誤劃分?;貞?yīng)開(kāi)頭說(shuō)的話(huà),為了避免各種路徑找不到的問(wèn)題,文件路徑中不要有空格,不要有特殊符號(hào),不要有中文??!
?運(yùn)行后會(huì)生成如下圖所示的 dataSet_path
和 labels
文件夾。dataSet_path下會(huì)有三個(gè)數(shù)據(jù)集的txt文件,labels下存放各個(gè)圖像的標(biāo)注文件
3.配置文件
?在 yolov5 的 data
文件夾下創(chuàng)建一個(gè)名為 myvoc.yaml
,模板如下,根據(jù)自己實(shí)際情況填寫(xiě):
【??易錯(cuò)】:注意冒號(hào)后面是有空格的
train: D:/yolov5/VOCData/dataSet_path/train.txt
val: D:/yolov5/VOCData/dataSet_path/val.txt
# number of classes
nc: 1
# class names
names: ["DM"]
4.聚類(lèi)獲得先驗(yàn)框
- 獲取anchors
?較高版本的yolov5都可以在utils
文件夾下找到autoanchor.py
文件,它的作用是自動(dòng)獲取anchors,因此我們不需要額外的操作。 - 在
models
文件夾下找到yolov5s.yaml
(如果使用這個(gè)權(quán)重模型訓(xùn)練的話(huà)),將其中的nc
改為實(shí)際上標(biāo)注類(lèi)的數(shù)量,和 myvoc.yaml 一樣(記得保存)。
四、使用CPU訓(xùn)練
?在cmd窗口下激活相應(yīng)虛擬環(huán)境后 cd 到 yolov5 文件夾后,輸入下列指令即可開(kāi)始訓(xùn)練
python train.py --weights yolov5s.pt --cfg models/yolov5s.yaml --data data/myvoc.yaml --epoch 200 --batch-size 8 --img 640 --device cpu
[參數(shù)說(shuō)明]:
-
--weights
:權(quán)重文件所在的相對(duì)路徑 -
--cfg
:存儲(chǔ)模型結(jié)構(gòu)配置文件的相對(duì)路徑 -
--data
:存儲(chǔ)訓(xùn)練、測(cè)試數(shù)據(jù)的文件的相對(duì)路徑 -
--epoch
:訓(xùn)練過(guò)程中整個(gè)數(shù)據(jù)集將被迭代(訓(xùn)練)了多少次 -
--batch-size
:訓(xùn)練完多少?gòu)垐D片才進(jìn)行權(quán)重更新 -
--img
:自適應(yīng)縮放輸入圖片的尺寸為指定大小。在YOLOv5中,輸入圖像的大小需要是正方形,并且是 32 的倍數(shù) -
--device
:選擇用CPU或者GPU訓(xùn)練
【??易錯(cuò)】:在指定路徑的時(shí)候需要注意,在python中,\
是轉(zhuǎn)移字符,如果我們想要表示路徑,則需要使用/
或者\\
取消轉(zhuǎn)義
(開(kāi)始訓(xùn)練)
五、使用GPU訓(xùn)練
1.開(kāi)始訓(xùn)練
?CPU適合處理少量復(fù)雜運(yùn)算,GPU適合處理大量簡(jiǎn)單運(yùn)算。相較于 CPU,GPU 在具備大量重復(fù)數(shù)據(jù)集運(yùn)算和頻繁內(nèi)存訪(fǎng)問(wèn)等特點(diǎn)的應(yīng)用場(chǎng)景中具有無(wú)可比擬的優(yōu)勢(shì),在運(yùn)行分析、深度學(xué)習(xí)和機(jī)器學(xué)習(xí)算法尤其有用。
?GPU 能夠讓某些計(jì)算比傳統(tǒng) CPU 上運(yùn)行相同的計(jì)算速度快 10 倍至 100 倍。所以更加推薦使用GPU進(jìn)行訓(xùn)練。
?使用GPU訓(xùn)練,只需將代碼中的--device cpu
改為--device 0/1……
即可,顯卡編號(hào)可以使用nvidia-smi
指令來(lái)查看。如下圖所示,我的電腦中只安裝了一塊GPU,在訓(xùn)練中只能使用 --device 0
.
python train.py --weights yolov5s.pt --cfg models/yolov5s.yaml --data data/myvoc.yaml --epoch 200 --batch-size 8 --img 640 --device 0
而在下面例子中,則有兩塊GPU
??易錯(cuò)①:如果訓(xùn)練時(shí)出現(xiàn) CUDA out of memory
的錯(cuò)誤,將 batch_size
改到4基本能解決問(wèn)題,再不行就改成1
??易錯(cuò):yolov5
是基于 pytorch
實(shí)現(xiàn)的,而使用 pip 默認(rèn)安裝的 pytorch 是以CPU作為計(jì)算平臺(tái),因此CUDA是不可用的,需要重新下載基于 CUDA 計(jì)算的pytorch
2.重新下載pytorch
pytorch文件比較大,建議下載的時(shí)候首先給 pip 換源
- Pytorch官方下載鏈接 :https://pytorch.org/get-started/locally/
-
首先在相應(yīng)虛擬環(huán)境下刪除原先版本的pytorch。注意!僅僅使用
pip uninstall torch
指令是不夠的,因?yàn)橹匦孪螺d的 torch 可能與其他軟件之間存在版本不兼容問(wèn)題。正確的做法是:找到自己Anconda中對(duì)應(yīng)虛擬環(huán)境的位置,將下面這些文件全部刪除。 -
使用
nvidia-smi
查看最高能下載的 pytorch CUDA版本,我這里是11.6 -
強(qiáng)烈推薦使用
pip
安裝而不要使用conda
安裝,conda
安裝太慢了,換源還是很慢,而且還很容易失敗 pip install 與 conda install 的使用區(qū)別 -
切換到相應(yīng)虛擬環(huán)境中,運(yùn)行 “Run this Command:” 提示的 pip 代碼安裝
-
檢測(cè)cuda是否可用:首先包含頭文件
import torch
,在輸入指令torch.cuda.is_available()
,返回true說(shuō)明可以使用。接下來(lái)我們就可以使用GPU進(jìn)行訓(xùn)練
六、訓(xùn)練結(jié)果可視化
訓(xùn)練結(jié)果將保存在 \runs\train
文件夾下,部分文件意義如下:
- weights:訓(xùn)練生成權(quán)重。包含
best.pt
(最好的權(quán)重,detect時(shí)用到它),和last.pt
(最近生成的權(quán)重模型) - confusion:混淆矩陣。混淆矩陣讓我們了解分類(lèi)模型所犯的錯(cuò)誤,更重要的是可以了解哪些錯(cuò)誤類(lèi)型正在發(fā)生。
- F1_curve:置信度和F1分?jǐn)?shù)的關(guān)系圖
- P_curve:準(zhǔn)確率和置信度的關(guān)系圖
- R_curve:召回率和置信度之間的關(guān)系
- PR_curve:PR曲線(xiàn)中的P代表的是precision(精準(zhǔn)率),R代表的是recall(召回率),其代表的是精準(zhǔn)率與召回率的關(guān)系
- labels:左上圖表示個(gè)類(lèi)別的數(shù)據(jù)量;右上圖表示標(biāo)簽;左下圖表示 center 的 xy 坐標(biāo);右下圖表示各個(gè)標(biāo)簽的長(zhǎng)和寬
?TensorBoard 是 TensorFlow 提供的一個(gè)可視化工具,用于幫助用戶(hù)通過(guò)交互式的數(shù)據(jù)可視化方式監(jiān)控、調(diào)試、優(yōu)化深度學(xué)習(xí)模型。使用指令 tensorboard --logdir=xxx
啟動(dòng)TensorBoard 服務(wù),并遞歸式的讀取指定路徑下的所有事件數(shù)據(jù)。
?訓(xùn)練后的事件數(shù)據(jù)存儲(chǔ)在 runs/train
路徑中,我們想查看exp13的訓(xùn)練結(jié)果,可以執(zhí)行下面的指令: tensorboard --logdir=runs/train/exp13
?訪(fǎng)問(wèn)網(wǎng)頁(yè) http://localhost:6006/
即可看到各種訓(xùn)練結(jié)果(注:localhost指的是你所在的計(jì)算機(jī)本身)
使用剛剛訓(xùn)練好的 best.pt
模型來(lái)檢測(cè):
python detect.py --weights runs/train/exp/weights/best.pt --source ../source/test.png
[說(shuō)明]:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-455066.html
-
--weights
:表示我們選擇的權(quán)重模型 -
--source
:表示待檢測(cè)的圖片的路徑 (…/表示上級(jí)路徑)
成功實(shí)現(xiàn)了惡劣環(huán)境下的DM碼的定位文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-455066.html
到了這里,關(guān)于【零基礎(chǔ)玩轉(zhuǎn)yolov5】yolov5訓(xùn)練自己的數(shù)據(jù)集(CPU訓(xùn)練+GPU訓(xùn)練)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!