前言 此篇文章将从Darknet框架的编译开始,到YOLO-v3数据集的制作,到数据集的生成,到训练,以及识别
关于NVIDIA显卡驱动以及CUDA、Cudnn需要先行安装 ,
此篇文章中,Anaconda、OpenCV并不是必须的 ,演示中出现Anaconda的虚拟环境只是我没有退出虚拟环境
GPU也不是必须的,可以使用CPU
安装环境为Ubuntu 20.04 
训练的电脑借自好友hjs的DELL-G5笔记本(RTX2060,显存6GB)
结果展示 不能显示物品名,有待继续研究
安装过程 获取Darknet框架 仓库地址
1 https://github.com/pjreddie/darknet 
 
下载Darknet框架–通过Git
1 git clone  https://github.com/pjreddie/darknet darknet 
 
然后进入darknet文件夹
注意:本文从这里开始,终端就一直处于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 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)  $(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 : cleanclean: 	rm -rf $(OBJS)  $(SLIB)  $(ALIB)  $(EXEC)  $(EXECOBJ)  $(OBJDIR) /* 
 
接下来就可以开始编译了
输入以下命令
 
开启OPENCV选项导致的问题 因开启OPENCV选项,而我的OPENCV版本为4.0(太高)而导致的问题 
(听说用下面这个仓库可以避免很多问题,但我没有试过)
1 https://github.com/AlexeyAB/darknet 
 
CUDNN_CONVOLUTION_FWD_SPECIFY_WORKSPACE_LIMIT处报错 遇到下图错误时,请修改编译目录下的src/convolutional_layer.c,
这里给出修改后文件,点击即可下载 
IplImage ipl处报错 如果遇到以下错误
请修改src/image_opencv.cpp,找到mat_to_image函数,修改为下图所示
CV_CAP_PROP_FRAME_WIDTH处报错 如果遇到以下错误,需要修改image_opencv.cpp
将带CV_的去掉就行,
例如CV_CAP_PROP_FRAME_WIDTH,改成CAP_PROP_FRAME_WIDTH,
源码文件中的CV_全部都要去掉,如下图
数据集处理 初步测试 建议编译完后先测试一下,确定编译出来的darknet程序无问题
先获取预训练模型
1 wget https://pjreddie.com/media/files/yolov3.weights 
 
输入以下命令调用预训练的模型识别测试
1 ./darknet detect cfg/yolov3.cfg yolov3.weights data/dog.jpg 
 
如果识别成功,将生成priedictions.jpg
数据集制作 收集图片 需要收集图片,这里收集了两个人物图片各80张
先给他们起个名字2333
开始标注 然后开始标注,这里使用labelimg,
labelimg的安装方法这里就不赘述了
这是它的左侧工具栏,可以直接点击,也可以用快捷键
注意下图标红的地方,选择YOLO格式
加载图片后添加label,例如这里添加了人物名称为Teach,
添加之前一定要 删去多余的名字,否则存在无法对应图片的标注
标注完成后,输出文件夹里会多出很多txt,文件名除后缀外与图片同名
下图就是label txt 
里面存着图片的index号,以及标注中心点x,y进行x/width、y/height计算后的数值(归一化处理)
顺序为index, x,y,w,h
index是从0开始的,这里打开的是第二个人物的标注,所以index为1
数据集生成对应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  randomimport  osdata_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' ) 
 
完成上述操作后的目录应该如下图所示
test.txt、train.txt、val.txt里差不多应该是下图所示的样子
修改训练配置 修改cfg配置 修改cfg/yolov3.cfg
原文件是这个样子的
修改之后是下图这个样子,
至于为什么要修改,因为训练时和检测时设置的batch size、subdivisions参数是不同的
batch size的意思大概是每轮训练下来喂入的数据量(仅为个人观点),
batch size设置太大了会导致显存不足无法继续训练,但设大一点训练速度会加快。
这里的修改只是将前面的注释掉,这里batch size设置为32的原因,文章的后半部分会再讲
然后后面的filters参数还需要修改,只要搜索yolo 就行,大概要改三次 
filters的计算:3*(5+数据集中类的个数),这里是2个类,结果就是21
classes这里我们的类型只要两种,就改为2
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    # 学习率变动步长  
 
训练过程 修改数据配置 coco.data 修改data/coco.data,按实际情况修改 
下面是我的配置
如果配置不对的话,会报下面这样的错误
例如这里由于buckup的路径不对导致的报错,图片上是改过的路径
coco.names 修改data/coco.names
这里直接按序填名字(也就是类别名),顺序不要搞错
coco.yaml 修改data/coco.yaml
这里Albert是填错了的😭,不要犯我的错误,会导致训练完无法识别名字为robert的图片
开始训练 初步准备 还需要获取一个文件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
仔细观察,可以发现一些异常,如下图所示,
右边的双引号本来应该在最后面,现在却跑到了下一行去了
最后发现是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 
 
出现报错out of memery CUDA Error: out of memery
需要修改cfg配置,即cfg/yolov3.cfg
batch设置为合适的值,这里为32,主要看图片大小与数量,本来batch为64
接下来即可重试命令,这时候就没问题了
1 ./darknet detector train data/coco.data cfg/yolov3.cfg darknet53.conv.74 -gups 0 
 
在另一个终端输入
 
可以查看GPU情况
训练完成 训练到1215轮感觉数值差不多了,avg在0.133, 0.1586
结果发现保存的权重就到900轮,应该是和前面的cfg配置有关
图片识别测试过程 先修改cfg文件(cfg/yolov3.cfg),注释掉训练时用的参数,再取消注释测试时的参数
输入命令测试以下,先用数据集里的图片
1 ./darknet detector test data/coco.data cfg/yolov3.cfg backup/yolov3_900.weights 1.jpg 
 
测试结果如下,另外一个人物由于填错名字了导致无法识别
写在最后 最终测试实拍图片,结果如下 
看起来应该是数据集里的图片太少了
之前测试过pythorch版yolov4,训练过程比darknet的yolov3慢很多,
可能是因为c语言编译出来的程序跑的更快?
提供权重文件与数据集(数据集在删除原来的data目录后直接在darknet目录下解压即可)下载 :
1 2 链接:https://pan.baidu.com/s/15HEr88r13PCFwDDOI5-h_Q?pwd=p3yd  提取码:p3yd 
 
谢谢观看
EOF