前言

此篇文章将从Darknet框架的编译开始,到YOLO-v3数据集的制作,到数据集的生成,到训练,以及识别

关于NVIDIA显卡驱动以及CUDA、Cudnn需要先行安装

此篇文章中,Anaconda、OpenCV并不是必须的,演示中出现Anaconda的虚拟环境只是我没有退出虚拟环境

GPU也不是必须的,可以使用CPU

安装环境为Ubuntu 20.04

训练的电脑借自好友hjs的DELL-G5笔记本(RTX2060,显存6GB)

结果展示

不能显示物品名,有待继续研究

predictions

安装过程

获取Darknet框架

仓库地址

1
https://github.com/pjreddie/darknet

下载Darknet框架–通过Git

1
git clone https://github.com/pjreddie/darknet darknet

然后进入darknet文件夹

注意:本文从这里开始,终端就一直处于darknet文件夹里

1
cd darknet

安装必要软件

安装gcc和g++工具

1
sudo apt install build-essential

修改makefile

为了支持使用GPU训练,需要对makefile进行部分修改

这里因为电脑装了OPENCV4.0,故opencv选项也开启了(导致后面编译时出现很多错误)

我设置的选项如下,GPU=1表示开启GPU,CUDNN=1表示使用CUDNN,以此类推

CUDNN、CUDNN_HALF、OPENCV其实都不是必须的

1
2
3
4
5
6
7
8
9
GPU=1
CUDNN=1
CUDNN_HALF=1
OPENCV=1
AVX=0
OPENMP=0
LIBSO=0
ZED_CAMERA=0
ZED_CAMERA_v2_8=0

下面这里贴上我的makefile以便需要的人使用

做了部分修改(相对于makefile,做了修改以保证识别已安装的opencv4.0,以及根据GPU设置ARCH),

适合pjreddie仓库的darknet,适于RTX2060和安装了OPENCV4.x的机器,可以直接拿去用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
GPU=1
CUDNN=1
CUDNN_HALF=1
OPENCV=1
AVX=0
OPENMP=0
LIBSO=0
ZED_CAMERA=0
ZED_CAMERA_v2_8=0

# set GPU=1 and CUDNN=1 to speedup on GPU
# set CUDNN_HALF=1 to further speedup 3 x times (Mixed-precision on Tensor Cores) GPU: Volta, Xavier, Turing and higher
# set AVX=1 and OPENMP=1 to speedup on CPU (if error occurs then set AVX=0)
# set ZED_CAMERA=1 to enable ZED SDK 3.0 and above
# set ZED_CAMERA_v2_8=1 to enable ZED SDK 2.X

USE_CPP=0
DEBUG=0

ARCH= -gencode arch=compute_75,code=[sm_75,compute_75]


VPATH=./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

ifeq ($(OPENMP), 1)
CFLAGS+= -fopenmp
endif

ifeq ($(DEBUG), 1)
OPTS=-O0 -g
endif

CFLAGS+=$(OPTS)

ifeq ($(OPENCV), 1)
COMMON+= -DOPENCV
CFLAGS+= -DOPENCV
LDFLAGS+= `pkg-config --libs opencv4 2> /dev/null || pkg-config --libs opencv`
COMMON+= `pkg-config --cflags opencv4 2> /dev/null || pkg-config --cflags opencv`
endif

ifeq ($(GPU), 1)
COMMON+= -DGPU -I/usr/local/cuda/include/
CFLAGS+= -DGPU
LDFLAGS+= -L/usr/local/cuda/lib64 -lcuda -lcudart -lcublas -lcurand
endif

ifeq ($(CUDNN), 1)
COMMON+= -DCUDNN

CFLAGS+= -DCUDNN
LDFLAGS+= -lcudnn
endif

OBJ=gemm.o utils.o cuda.o deconvolutional_layer.o convolutional_layer.o list.o image.o activations.o im2col.o col2im.o blas.o crop_layer.o dropout_layer.o maxpool_layer.o softmax_layer.o data.o matrix.o network.o connected_layer.o cost_layer.o parser.o option_list.o detection_layer.o route_layer.o upsample_layer.o box.o normalization_layer.o avgpool_layer.o layer.o local_layer.o shortcut_layer.o logistic_layer.o activation_layer.o rnn_layer.o gru_layer.o crnn_layer.o demo.o batchnorm_layer.o region_layer.o reorg_layer.o tree.o lstm_layer.o l2norm_layer.o yolo_layer.o iseg_layer.o image_opencv.o
EXECOBJA=captcha.o lsd.o super.o art.o tag.o cifar.o go.o rnn.o segmenter.o regressor.o classifier.o coco.o yolo.o detector.o nightmare.o instance-segmenter.o darknet.o
ifeq ($(GPU), 1)
LDFLAGS+= -lstdc++
OBJ+=convolutional_kernels.o deconvolutional_kernels.o activation_kernels.o im2col_kernels.o col2im_kernels.o blas_kernels.o crop_layer_kernels.o dropout_layer_kernels.o maxpool_layer_kernels.o avgpool_layer_kernels.o
endif

EXECOBJ = $(addprefix $(OBJDIR), $(EXECOBJA))
OBJS = $(addprefix $(OBJDIR), $(OBJ))
DEPS = $(wildcard src/*.h) Makefile include/darknet.h

all: obj backup results $(SLIB) $(ALIB) $(EXEC)
#all: obj results $(SLIB) $(ALIB) $(EXEC)


$(EXEC): $(EXECOBJ) $(ALIB)
$(CC) $(COMMON) $(CFLAGS) $^ -o $@ $(LDFLAGS) $(ALIB)

$(ALIB): $(OBJS)
$(AR) $(ARFLAGS) $@ $^

$(SLIB): $(OBJS)
$(CC) $(CFLAGS) -shared $^ -o $@ $(LDFLAGS)

$(OBJDIR)%.o: %.cpp $(DEPS)
$(CPP) $(COMMON) $(CFLAGS) -c $< -o $@

$(OBJDIR)%.o: %.c $(DEPS)
$(CC) $(COMMON) $(CFLAGS) -c $< -o $@

$(OBJDIR)%.o: %.cu $(DEPS)
$(NVCC) $(ARCH) $(COMMON) --compiler-options "$(CFLAGS)" -c $< -o $@

obj:
mkdir -p obj
backup:
mkdir -p backup
results:
mkdir -p results

.PHONY: clean

clean:
rm -rf $(OBJS) $(SLIB) $(ALIB) $(EXEC) $(EXECOBJ) $(OBJDIR)/*

接下来就可以开始编译了

输入以下命令

1
make

开启OPENCV选项导致的问题

因开启OPENCV选项,而我的OPENCV版本为4.0(太高)而导致的问题

(听说用下面这个仓库可以避免很多问题,但我没有试过)

1
https://github.com/AlexeyAB/darknet

CUDNN_CONVOLUTION_FWD_SPECIFY_WORKSPACE_LIMIT处报错

遇到下图错误时,请修改编译目录下的src/convolutional_layer.c,

这里给出修改后文件,点击即可下载

Screenshot-from-2022-02-11-21-43-16

IplImage ipl处报错

如果遇到以下错误

Screenshot-from-2022-02-11-21-43-48

请修改src/image_opencv.cpp,找到mat_to_image函数,修改为下图所示

360截图16820125193268

CV_CAP_PROP_FRAME_WIDTH处报错

如果遇到以下错误,需要修改image_opencv.cpp

Screenshot-from-2022-02-11-21-44-10

将带CV_的去掉就行,

例如CV_CAP_PROP_FRAME_WIDTH,改成CAP_PROP_FRAME_WIDTH,

源码文件中的CV_全部都要去掉,如下图

360截图18430707668574

数据集处理

初步测试

建议编译完后先测试一下,确定编译出来的darknet程序无问题

先获取预训练模型

1
wget https://pjreddie.com/media/files/yolov3.weights

输入以下命令调用预训练的模型识别测试

1
./darknet detect cfg/yolov3.cfg yolov3.weights data/dog.jpg

Screenshot-from-2022-02-11-21-44-24

如果识别成功,将生成priedictions.jpg

Screenshot-from-2022-02-11-21-45-47

数据集制作

收集图片

需要收集图片,这里收集了两个人物图片各80张

360截图17090919358858

先给他们起个名字2333

b546202c4f99a4edbfc803582bdf646

开始标注

然后开始标注,这里使用labelimg,

labelimg的安装方法这里就不赘述了

这是它的左侧工具栏,可以直接点击,也可以用快捷键

注意下图标红的地方,选择YOLO格式

e2434732ded62e441d30f8f7447b7d5

加载图片后添加label,例如这里添加了人物名称为Teach,

添加之前一定要删去多余的名字,否则存在无法对应图片的标注

ec4dc21c32420b79592aa7588288d95

标注完成后,输出文件夹里会多出很多txt,文件名除后缀外与图片同名

下图就是label txt

360截图16530709659365

里面存着图片的index号,以及标注中心点x,y进行x/width、y/height计算后的数值(归一化处理)

顺序为index, x,y,w,h

index是从0开始的,这里打开的是第二个人物的标注,所以index为1

360截图16751031171538

数据集生成对应txt

这里提供一份Python3程序,以供根据图片生成路径txt

因为训练还需要test.txt、train.txt、val.txt这3个路径txt

关于此程序:

1)程序里面的data_path变量请设置为你的darknet目录下的data目录

2)将图片放在data目录下的images目录里,若没有images文件夹,就新建一个

3)在data目录下新建labels文件夹,然后将前面标注的label txt全部放在data目录下的labels里

4)如果你的图片后缀名不是.jpg,请修改程序中的extend_name变量

然后就可以开始运行此程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
'''
@Rferer = https://blog.lxscloud.top
'''
import random
import os

data_path = '/home/albert/darknet/data' #此处前面单引号里面的路径请按情况修改
images_path = 'images'
txt_path = data_path

#下面两个变量设置测试集占比
test_percent = 0.1
val_percent = 0.1

og_files = os.listdir(txt_path)
for i in og_files:
os.remove(txt_path+'/'+i)

images_name = [i[0:-4] for i in os.listdir(images_path)]
extend_name = '.jpg'

test_txt = open(txt_path+'/'+'test.txt', 'a+')
val_txt = open(txt_path+'/'+'val.txt', 'a+')
train_txt = open(txt_path+'/'+'train.txt', 'a+')

total_num = len(images_name)
test_set = random.sample(images_name, round(test_percent*total_num))
for i in test_set:
images_name.remove(i)
test_txt.write(f"{data_path}/{images_path}/{i}{extend_name}" + '\n')
val_set = random.sample(images_name, round(val_percent*total_num))
for i in val_set:
images_name.remove(i)
val_txt.write(f"{data_path}/{images_path}/{i}{extend_name}" + '\n')
train_set = images_name
for i in train_set:
train_txt.write(f"{data_path}/{images_path}/{i}{extend_name}" + '\n')

test_txt.close()
val_txt.close()
train_txt.close()

print('Done')

完成上述操作后的目录应该如下图所示

360截图18430701527178

test.txt、train.txt、val.txt里差不多应该是下图所示的样子

360截图17001018568753

修改训练配置

修改cfg配置

修改cfg/yolov3.cfg

原文件是这个样子的

360截图18720119778981

修改之后是下图这个样子,

至于为什么要修改,因为训练时和检测时设置的batch size、subdivisions参数是不同的

batch size的意思大概是每轮训练下来喂入的数据量(仅为个人观点),

batch size设置太大了会导致显存不足无法继续训练,但设大一点训练速度会加快。

这里的修改只是将前面的注释掉,这里batch size设置为32的原因,文章的后半部分会再讲

360截图173902228886130

然后后面的filters参数还需要修改,只要搜索yolo就行,大概要改三次

filters的计算:3*(5+数据集中类的个数),这里是2个类,结果就是21

classes这里我们的类型只要两种,就改为2

360截图173902218782134

R

指定次数

可以指定训练批次和训练轮数,也可以不改

1
2
3
4
5
6
7
8
9
10
11
momentum=0.9         # 动量 
decay=0.0005 # 权重衰减
angle=0
saturation = 1.5 # 饱和度
exposure = 1.5 # 曝光度
hue=.1 # 色调
learning_rate=0.001 # 学习率
burn_in=1000 # 学习率控制的参数
max_batches = 50200 # 迭代次数
policy=steps # 学习率策略
steps=40000,45000 # 学习率变动步长

360截图17891226439054

训练过程

修改数据配置

coco.data

修改data/coco.data,按实际情况修改

下面是我的配置

360截图17001020549870

如果配置不对的话,会报下面这样的错误

例如这里由于buckup的路径不对导致的报错,图片上是改过的路径

Screenshot-from-2022-02-11-22-47-47

coco.names

修改data/coco.names

这里直接按序填名字(也就是类别名),顺序不要搞错

360截图170701138811668

coco.yaml

修改data/coco.yaml

这里Albert是填错了的😭,不要犯我的错误,会导致训练完无法识别名字为robert的图片

360截图187804205210288

开始训练

初步准备

还需要获取一个文件darknet53.conv.74(预训练权重)

1
wget https://pjreddie.com/media/files/darknet53.conv.74

获取完成就可以开始了

1
./darknet detector train data/coco.data cfg/yolov3.cfg darknet53.conv.74 -gups 0

出现报错annot load image

annot load image

Screenshot-from-2022-02-11-22-20-39

仔细观察,可以发现一些异常,如下图所示,

右边的双引号本来应该在最后面,现在却跑到了下一行去了

111111

最后发现是windows下换行符与linux不同的原因(windows下是\r\n,linux下是\n),

因为我的test.txt、train.txt、val.txt这3个路径txt是在windows下生成的。

所以现在解决方法有两种

1)直接在ubuntu下生成路径txt

2)去掉符号\r,参考文章

这里用的方法2,输入以下命令处理文件

1
sed -i 's/\r//g' data/test.txt
1
sed -i 's/\r//g' data/train.txt
1
sed -i 's/\r//g' data/val.txt

Screenshot-from-2022-02-11-22-21-21

出现报错out of memery

CUDA Error: out of memery

Screenshot-from-2022-02-11-21-59-22

需要修改cfg配置,即cfg/yolov3.cfg

batch设置为合适的值,这里为32,主要看图片大小与数量,本来batch为64

Screenshot-from-2022-02-11-22-04-19

接下来即可重试命令,这时候就没问题了

1
./darknet detector train data/coco.data cfg/yolov3.cfg darknet53.conv.74 -gups 0

Screenshot-from-2022-02-11-22-38-05

在另一个终端输入

1
nvidia-smi

可以查看GPU情况

训练完成

训练到1215轮感觉数值差不多了,avg在0.133, 0.1586

Screenshot from 2022-02-12 13-50-16

结果发现保存的权重就到900轮,应该是和前面的cfg配置有关

360截图182805028190109

图片识别测试过程

先修改cfg文件(cfg/yolov3.cfg),注释掉训练时用的参数,再取消注释测试时的参数

360截图17390220113144137

输入命令测试以下,先用数据集里的图片

1
./darknet detector test data/coco.data cfg/yolov3.cfg backup/yolov3_900.weights 1.jpg

Screenshot from 2022-02-12 13-56-34

测试结果如下,另外一个人物由于填错名字了导致无法识别

Screenshot from 2022-02-12 15-20-48

写在最后

最终测试实拍图片,结果如下

看起来应该是数据集里的图片太少了

360截图16890312616287

之前测试过pythorch版yolov4,训练过程比darknet的yolov3慢很多,

可能是因为c语言编译出来的程序跑的更快?

提供权重文件与数据集(数据集在删除原来的data目录后直接在darknet目录下解压即可)下载

1
2
链接:https://pan.baidu.com/s/15HEr88r13PCFwDDOI5-h_Q?pwd=p3yd 
提取码:p3yd

谢谢观看

EOF