机器学习之 darknet YOLO 训练 VOC 数据集

最近被安排到中汽研实习(就算是实习吧),做了一些基于深度学习的图像识别工作,其实说起来自己对深度学习也没什么太深入的了解,都是现学现卖,跑人家的例子。不过还是在这边记录一下,以后回首可以稍稍感慨一下年轻时的无知。

关于机器学习,基础知识是看周志华的西瓜书(清华大学出版社的机器学习)来学习的,不过大致是囫囵吞枣,没有静下心来安安稳稳地钻研(时间也不允许)。

之后看了网易云课堂中吴恩达的机器学习教程,受益匪浅,推荐刚入门的同学去看看,讲的很好。

在吴恩达的视频中,大致了解了卷积神经网络和深度学习大致的套路,其实说到底就是各种卷积层(convolution layer),池化层(pooling layer),全连接层(fool connected layer)不断组合。

感觉机器学习想要深入了解,可能需要看很多相关的论文,之后的学习路程就记录在之后的博客中好了。

先说说甲方的需求:需要在行车过程中动态识别出前方交通标志,如果是限速标识,需要识别中里面的数字。

最近只做了第一部分,也就是交通标志的识别。对卷积神经网络有过了解的话能感觉出来这就是一个分类问题,好在公司已经标好了数据,并且是按照 VOC 格式标记的,接下来就是使用现成的网络训练就可以了。

我用的是 YOLO 在darknet 网站上有 v1 和 v2 两个版本。

https://pjreddie.com/darknet/yolo/

以上是 YOLO darknet 版本的官网,上面的说明非常详细,也非常人性化,即使没有 GPU 也可以使用,可以简单的按照上面的教程进行安装。

注意,darknet 默认是不开启 GPU 的,当然这对没有 GPU 的人(比如我…)是很友好的,但这并不影响 CPU 训练网络慢的要死的事实。如果想要开启对 GPU 的支持,在 Makefile 最上面开启对 GPUCUDNN 的支持就可以了

GPU=0 // 开启GPU,使用 nvidia-smi 来查看本机 GPU 使用情况
CUDNN=0 // 开启CUDA,请确保事先安装好了CUDA,可以运行 nvcc -V 来查看
OPENCV=0 // 开启后可以直接查看预测后的图片
OPENMP=0
DEBUG=0

如果顺利,此时你已经可以使用 darknet 预先训练好的 YOLO 模型检测出了样例中的物体。接下来我们要尝试使用自己的数据训练。

关于 VOC 数据集的训练在官网的教程上面也有提到。

./darknet detector train cfg/voc.data cfg/yolo-voc.cfg darknet19_448.conv.23

一切准备工作就绪后,上面的工作会开始训练,不过你大概会失败,因为有一些坑官网并没有提到。

首先在 voc.data 里面

classes= 20
train  = <your work directory>/darknet/train.txt
valid  = <your work directory>/darknet/2007_test.txt
names = data/voc.names
backup = backup

这里的 backup 是在训练工程中权重文件储存的位置,需要提前创建好这个文件夹

还需要编辑一下 cfg/yolo-voc.cfg 这个文件,将前几行的 test 次改成 train

[net]
# Testing //注释到测试的部分,将训练部分取消注释
#batch=1
#subdivisions=1
# Training
batch=64
subdivisions=8
height=416
width=416
channels=3
momentum=0.9
decay=0.0005
angle=0
saturation = 1.5
exposure = 1.5
hue=.1

然后应该就不会有问题了,这里建议大家用 GPU 服务器去训练,不要头铁用 CPU 去尝试,不然你会等到天荒地老。

训练结束后的权重文件在 backup 文件夹中会有保存,按照官网的例子运行就可以了


然后说说自己的数据集怎么训练,公司给的数据集是按照 VOC 的格式标注的,虽然有一点不一样,不过总体上已经解决很多问题了,在转换 VOC 数据集的时候,官方提供了一个脚本 voc_label.py 我们修改这个文件即可。

强烈建议大家复制一份配置文件出来,不要直接在原文件修改

首先我们来看看在训练 VOC 的数据集中,darknet 都用到了什么。

在 cfg/voc.data 中

classes= 20
train  = <your work directory>/darknet/train.txt
valid  = <your work directory>/darknet/2007_test.txt
names = data/voc.names
backup = backup

classes 参数需要改成你的数据集中具体的分类数目,下面用来训练和测试的图片列表都是有上述的脚本自动生成的,voc.names 里面保存了所有分类。打开 train.txt 可以看到里面保存的是用来训练的图片的绝对路径,但是,真正的标注信息却没有体现,也就是 VOCdevkit/VOC2007/labels 下面的标签文件。本来百思不得其解,后来发现是在源码中写死的,在src/data.c 中有。

find_replace(path, "images", "labels", labelpath);
find_replace(labelpath, "JPEGImages", "labels", labelpath);

也就是说他会根据 train.txt 中图片的路径,替换 JPEGImageslabels 作为标签路径,因此在生成标签的时候一定要让标签目录与图片目录同级。

如果你根据你的数据正确修改了 voc_label.py 这个脚本文件,那么你的准备工作应该已经接近尾声了。(训练数据和测试数据按照 10 : 1 的比率划分)

最后修改一些网络中的参数就可以了。

cfg/voc.data 里面的 classes 改成你的分类数量,voc.names 里面存好具体分类的名称,然后修改 cfg/yolo-voc.2.0.cfg


[convolutional]
...
filters=5*(N+5)
...
classes=N
...

修改上面两个参数即可,其他参数可以按需修改。

之后按照同样的操作训练即可

简单说一下训练的时候的输出

Region Avg IOU: 0.432582, Class: 0.452631, Obj: 0.143766, No Obj: 0.005994, Avg Recall: 0.391304,  count: 23
623: 14.499426, 14.887394 avg, 0.001000 rate, 1.074940 seconds, 39872 images

在训练的时候会有这些信息在控制台打出,简单说下这些东西的含义

Region Avg IOU 交并比,即检测出的框框和标注的框框相交程度,期望趋近于 1

Avg Recall 平均召回率,即检测出物体的个数除以标注的所有物体个数,期望趋近于 1

count 标注的物体个数

最后一行的格式:

<迭代次数> : < train loss >, < avg train loss >, <学习率> …

反正我主要关心召回率和损失

据说一个模型训练要很长时间,如果你不小心退出了,没关系,backup/yolo2.backup 这个文件是实时备份的,所以只需要从这个备份点重新开始就可以了

./darknet detector train cfg/catarc.data cfg/yolo2.0-catarc.cfg backup/yolo2.backup -gpus 0,1

参考博客