【YOLO V3】目標檢測 Darknet 訓練自定義模型
- 前言
- 整體思路
- 環境檢查與依賴配置
- 克隆 YOLOv3 Darknet 并編譯
- Clone Darknet 項目文件
- 修改 Makefile 文件
- 修改模型保存頻率
- 項目編譯
- 準備數據集
- 配置訓練文件
- 數據集:`datasets` (自制)
- 權重文件 `yolov3-tiny.weights` (下載)
- 預訓練卷積層權重文件:`yolov3-tiny.conv.15` (下載)
- 標簽索引文件 `classes.names` (自制)
- 數據集配置文件 `mydata.data` (自制)
- 模型配置文件:`yolov3-tiny.cfg` (修改)
- 第一處修改
- 第二處修改
- 第三處修改
- 預先準備文件夾
- 模型訓練
- 導出訓練結果
前言
對于初學者而言,學習 YOLO 并實現目標檢測時,并不需要深入理解底層原理。本篇文章的重點是如何快速上手 YOLOv3,使用 Darknet 源碼訓練自定義模型,并實現目標檢測。
PS:本文適用于已有 YOLOv5 基礎的讀者,你可以參考【YOLO V5】目標檢測 WSL2 AutoDL VScode SSH學習 YOLOv5。由于 YOLOv5 的 PyTorch 訓練方式與 YOLOv3 的 Darknet 訓練方法存在相似之處,兩者是互通的,這將有助于你更快理解和上手 YOLOv3。
我的環境:
WSL2 Ubuntu 20.04 YOLOv3 Darknet
關聯文章:
- 【YOLO V5】目標檢測 WSL2 AutoDL VScode SSH
- 【YOLO】X-Anylabeling 數據集標注
- 【YOLO】X-AnyLabeling 半自動標注
參考資料:
- Darknet框架2:YOLOV3-使用Darknet訓練檢測自定義模型+COCO數據集!
- YOLOV3 - 使用 Darknet 訓練檢測模型
整體思路
環境檢查與依賴配置—克隆 YOLOv3 Darknet 并編譯—準備數據集—配置訓練文件—模型訓練—導出訓練結果
- 環境檢查與依賴配置:確保 OpenCV 版本為 2.x 或 3.x,避免使用 OpenCV 4.x,因為 Darknet C 框架當前不兼容 OpenCV 4.x。
- 克隆 YOLOv3 Darknet 并編譯:從官方源碼倉庫克隆 Darknet,修改
Makefile
以適配環境,修改模型保存頻率。 - 準備數據集:確保數據集符合 YOLOv3 Darknet 訓練要求,即采用 YOLO 格式(.txt 標簽文件與對應的圖片)。
- 配置訓練文件:準備以下關鍵配置文件:
.weights
(預訓練權重)classes.names
(類別標簽索引)mydata.data
(數據集配置).cfg
(模型結構參數).conv.x
(預訓練特征提取層權重)
【對應于 YOLOv5 的.pt
、classes.txt
(YOLOv5 實際上不使用)、mydata.yaml
、yolov5s.yaml
】 - 模型訓練:基于 Darknet 框架啟動訓練過程,優化模型權重。
- 導出訓練結果:生成最終的
.weights
權重文件,供后續部署與推理使用。
環境檢查與依賴配置
在此步驟中,需確保 OpenCV 版本為 2.x 或 3.x,因 Darknet 訓練不兼容 OpenCV 4.x。若當前 OpenCV 版本不符合要求,請參考相關資料進行降級或在虛擬環境中安裝較低版本。
克隆 YOLOv3 Darknet 并編譯
Clone Darknet 項目文件
git clone https://github.com/pjreddie/darknet
cd darknet
修改 Makefile 文件
修改 Makefile 時,重點關注前幾行的配置,通常只需調整前五行及 ARCH 參數,其余部分會由 Makefile 自動處理。若編譯時報錯,再根據具體錯誤信息進行調整。
# 編譯選項開關(根據自身需求修改)
GPU=1 # 是否啟用 GPU 支持 (1:啟用, 0:禁用)
CUDNN=1 # 是否啟用 CUDNN 加速庫 (1:啟用, 0:禁用)
OPENCV=1 # 是否啟用 OpenCV 支持 (1:啟用, 0:禁用)
OPENMP=0 # 是否啟用 OpenMP 并行計算 (1:啟用, 0:禁用)
DEBUG=0 # 是否啟用調試模式 (1:啟用, 0:禁用)# NVIDIA GPU 架構設置
# compute_XX 表示虛擬架構版本,sm_XX 表示實際架構版本
# 支持多個不同的 GPU 架構版本編譯
# 支持的 GPU 架構配置包括:
# Kepler, Maxwell, Turing, Ampere 和 Ada Lovelace
ARCH= -gencode arch=compute_30,code=sm_30 \-gencode arch=compute_35,code=sm_35 \-gencode arch=compute_50,code=[sm_50,compute_50] \-gencode arch=compute_52,code=[sm_52,compute_52] \-gencode arch=compute_75,code=[sm_75,compute_75] \-gencode arch=compute_86,code=[sm_86,compute_86] \-gencode arch=compute_89,code=[sm_89,compute_89]
# compute_30/35: Kepler
# compute_50/52: Maxwell
# compute_75: Turing (RTX 2080 Ti)
# compute_86: Ampere (RTX 3090/3060)
# compute_89: Ada Lovelace (RTX 4090)# 如果明確知道自己的 GPU 架構,可以取消下面的注釋并指定
# ARCH= -gencode arch=compute_52,code=compute_52VPATH=./src/:./examples
SLIB=libdarknet.so
ALIB=libdarknet.a
EXEC=darknet
OBJDIR=./obj/CC=gcc
CPP=g++
NVCC=nvcc
AR=ar
ARFLAGS=rcs
OPTS=-Ofast
LDFLAGS= -lm -pthread
COMMON= -Iinclude/ -Isrc/
CFLAGS=-Wall -Wno-unused-result -Wno-unknown-pragmas -Wfatal-errors -fPIC# OpenCV 相關配置
# 當 OPENCV=1 時啟用以下配置
ifeq ($(OPENCV), 1)
COMMON+= -DOPENCV # 添加 OpenCV 宏定義
CFLAGS+= -DOPENCV # 編譯標志添加 OpenCV 支持
LDFLAGS+= `pkg-config --libs opencv` -lstdc++ # 鏈接 OpenCV 庫和 C++ 標準庫
COMMON+= `pkg-config --cflags opencv` # 添加 OpenCV 頭文件路徑
# 下面兩行是 OpenCV4 的配置,如果使用 OpenCV4 則取消注釋
# LDFLAGS+= `pkg-config --libs opencv4` -lstdc++
# COMMON+= `pkg-config --cflags opencv4`
endif# CUDA 相關配置
# 當 GPU=1 時啟用以下配置
ifeq ($(GPU), 1)
COMMON+= -DGPU -I/usr/local/cuda/include/ # 添加 CUDA 宏定義和頭文件路徑
CFLAGS+= -DGPU # 編譯標志添加 GPU 支持
LDFLAGS+= -L/usr/local/cuda/lib64 -lcuda -lcudart -lcublas -lcurand # 鏈接 CUDA 相關庫
endif# CUDNN 相關配置
# 當 CUDNN=1 時啟用以下配置
ifeq ($(CUDNN), 1)
COMMON+= -DCUDNN # 添加 CUDNN 宏定義
CFLAGS+= -DCUDNN # 編譯標志添加 CUDNN 支持
LDFLAGS+= -lcudnn # 鏈接 CUDNN 庫
endif
修改模型保存頻率
進入 darknet/examples/detector.c 文件,找到第 138 行的以下代碼:
if(i%10000==0 || (i < 1000 && i%100 == 0)){
#ifdef GPU
將其修改為:
if(i%1000==0){
#ifdef GPU
項目編譯
make -j$(nproc)
make -j$(nproc) 通過并行使用所有 CPU 核心加速編譯,提高編譯效率。
準備數據集
準備數據集時,可參考:
- 【YOLO】X-Anylabeling 數據集標注
- 【YOLO】X-AnyLabeling 半自動標注。
【YOLO】X-Anylabeling 數據集標注 中寫的的標簽導出為 YOLO 格式,無需額外轉換。該文章提供的數據集拆分腳本會自動生成 train.txt、test.txt 和 val.txt 文件,完全符合 Darknet YOLO 訓練的要求。
所需的數據集結構應如下所示:
在 train.txt 文件中,每行應包含一個訓練圖像的路徑,如下所示:
如果訓練時的路徑與導出時不一致,可以使用 VSCode 的搜索與替換功能,將文件內所有路徑前綴更改為實際路徑。
配置訓練文件
本文以 YOLOv3-tiny 模型為例,首先需要準備以下訓練文件:
- 數據集:
datasets
數據集文件夾 - 權重文件:
yolov3-tiny.weights
- 預訓練卷積層權重文件:
yolov3-tiny.conv.15
- 標簽索引文件:
classes.names
- 數據集配置文件:
mydata.data
- 模型配置文件:
yolov3-tiny.cfg
-
建議將這些文件統一放入一個文件夾中進行管理,例如命名為
abaaba
文件夾,這樣便于上傳到服務器并組織好所有預先準備的訓練和配置文件。 -
我習慣于將工程文件夾、預先準備的訓練文件和數據集壓縮后,直接通過 VSCode 的 SSH 功能上傳到服務器。這樣比使用 wget 更為高效,避免了傳輸速率慢的問題。
文件下載地址:
- yolov3-tiny.weights
- yolov3-tiny.conv.15
數據集:datasets
(自制)
根據前文提到的兩篇文章的操作步驟,準備好自制的數據集,并將其存放到自定義的文件夾中即可,例如 abaaba 文件夾。
權重文件 yolov3-tiny.weights
(下載)
該 yolov3-tiny.weights 權重文件可以直接下載,并存放到自定義的文件夾中,例如 abaaba
文件夾。
預訓練卷積層權重文件:yolov3-tiny.conv.15
(下載)
該yolov3-tiny.conv.15文件可以直接下載,并存放到自定義的文件夾中,例如 abaaba
文件夾。
標簽索引文件 classes.names
(自制)
標簽索引文件的名稱可以自定義,但文件后綴必須為 .names
。
該文件中的每一行對應一個類別名稱,并且類別順序必須與標注時的順序一致,因為 YOLO 標簽使用從 0 到 n 的數字索引來對應不同類別,所以類別順序必須嚴格保持一致。
例如: classes.names
watermelon
banana
apple
milk
red
green
pepper
cake
cola
potato
tomato
該標簽索引文件的含義是*:在 YOLO 的 .txt 標簽文件中,索引 0 對應 watermelon
,索引 1 對應 banana
,以此類推,順序必須與類別一致。
數據集配置文件 mydata.data
(自制)
以下僅為示例,請根據你的項目實際情況調整路徑配置,但格式需保持一致:
classes = 11 # 目標檢測類別數量
train = /root/darknet_AutoDL/abaaba/datasets/train.txt # 訓練集圖片路徑列表文件
valid = /root/darknet_AutoDL/abaaba/datasets/val.txt # 驗證集圖片路徑列表文件
names = /root/darknet_AutoDL/abaaba/classes.names # 類別名稱文件,每行一個類別名稱
backup = root/darknet_AutoDL/backup # 訓練過程中權重文件的保存目錄
模型配置文件:yolov3-tiny.cfg
(修改)
該文件位于 yolo/darknet/cfg/yolov3-tiny.cfg
中,我們需要根據實際情況修改一些參數。
第一處修改
在 yolov3-tiny.cfg 文件的開頭幾行,根據訓練或測試階段的不同,注釋掉不需要的配置。在訓練時,注釋掉測試相關的配置;在測試時,注釋掉訓練相關的配置。
以訓練時為例:
# 測試/推理階段配置 (訓練時注釋掉這幾行)
# batch=1 # 推理時每批處理1張圖片
# subdivisions=1 # 不需要進行批次細分# 訓練階段配置 (測試/推理時注釋掉這幾行)
batch=96 # 每次迭代訓練96張圖片
subdivisions=2 # 將batch分成2份,降低GPU顯存占用# 實際每次處理: batch/subdivisions = 2張圖片# 較小的subdivisions訓練更快但需要更多顯存# 如果顯存不足,可以增加subdivisions的值
第二處修改
在 darknet 配置文件的第 20 行左右,找到 max_batches
參數,并根據實際需求修改。
該參數表示模型將在多少次迭代后停止訓練,是迭代的總次數。
對于 max_batches 參數的設置,一般建議將其設置為 類別數 * 2000。
max_batches = 22000 # 11*2000 = 22000
適當增加該參數可以提高模型的性能,但過大的值可能會導致過擬合。根據數據集的大小和類別數調整這一參數,以達到最佳訓練效果。
需要注意的是:
darknet 的訓練方式與 PyTorch 不同。darknet 是基于每次迭代來更新模型,而 PyTorch 是基于每輪訓練來更新。具體來說,darknet 中一次迭代對應一次完整的 batch 訓練,例如如果 batch 大小為 64,那么一次迭代就是使用 64 張圖片進行訓練并更新權重。
第三處修改
- 找到文件中的多個
[yolo]
標簽 - 將每個
[yolo]
標簽下的classes
修改為實際檢測的類別數量 - 修改每個
[yolo]
標簽的上面緊挨的[convolutional]
層,將其中的filters
參數設置為filters = 3 * (classes + 5)
,例如,如果類別為 4,則filters = 3 * (4 + 5) = 27
。
每個 [yolo]
標簽和緊挨著的 [convolutional]
層中的這兩個參數都需要根據實際情況進行修改
例如:
# 卷積層配置
[convolutional]
size=1
stride=1
pad=1
filters=48 # filters = 3 * ( classes + 5 ),如,filters= 3*(11+5) = 48
activation=linear# yolo配置
[yolo]
mask = 3,4,5
anchors = 10,14, 23,27, 37,58, 81,82, 135,169, 344,319
classes=11 #自定義數據集的類別數
num=6
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=1
預先準備文件夾
預先準備的文件夾 abaaba 如下圖所示,用于上傳到服務器進行訓練。
模型訓練
將上文準備好的 abaaba 文件夾放置于項目根目錄下。
- 如果自制的數據集與 COCO 數據集差異較大,可以使用 conv.15 文件從頭開始訓練自己的數據集:
./darknet detector train /root/darknet_AutoDL/abaaba/mydata.data /root/darknet_AutoDL/abaaba/yolov3-tiny.cfg /root/darknet_AutoDL/abaaba/yolov3-tiny.conv.15
或者使用續行符:
./darknet detector train \/root/darknet_AutoDL/abaaba/mydata.data \/root/darknet_AutoDL/abaaba/yolov3-tiny.cfg \/root/darknet_AutoDL/abaaba/yolov3-tiny.conv.15
- 如果自制的數據集與 COCO 數據集差異較小,可以基于 yolov3-tiny.weights 進行微調:
./darknet detector train /root/darknet_AutoDL/abaaba/mydata.data /root/darknet_AutoDL/abaaba/yolov3-tiny.cfg /root/darknet_AutoDL/abaaba/yolov3-tiny.weights
或者使用續行符:
./darknet detector train \/root/darknet_AutoDL/abaaba/mydata.data \/root/darknet_AutoDL/abaaba/yolov3-tiny.cfg \/root/darknet_AutoDL/abaaba/yolov3-tiny.weights
請注意,參數的順序必須嚴格按照上述順序,這是固定的順序。
拓展知識:
-
yolov3-tiny.weights 是在 COCO 數據集上完整訓練的模型權重文件,包含了所有層的參數(卷積層 + 檢測層),已學習 COCO 數據集的 80 個類別的特征,適合遷移學習/微調。如果你的任務與 COCO 類別相似,使用該權重可以加快收斂。
-
yolov3-tiny.conv.15 只包含模型前 15 層的卷積層權重,沒有涉及與特定類別相關的檢測層權重,適合從頭開始訓練自己的數據集,避免 COCO 數據集類別特征的干擾。
導出訓練結果
模型權重文件的保存位置由 mydata.data 文件中的 backup 參數指定,通常默認保存在項目根目錄下的 backup 文件夾中。
具體示例:
backup/
├── yolov3-tiny_last.weights # 最后一次迭代的權重
├── yolov3-tiny_1000.weights # 第1000次迭代的權重
├── yolov3-tiny_2000.weights # 第2000次迭代的權重
└── yolov3-tiny_best.weights # 最佳性能的權重