目录
1 训练yolov4-tiny
1.1 文件准备
1.1.1 Annotations
1.1.2 JPEGImages
1.1.3 labels
1.1.4 trained_models
1.1.5 classes.name
1.1.6 create_labels_txt.py
1.1.7 custom_training.data
1.1.8 get_labels.py
1.1.9 get_train_val.py
1.1.10 train.txt
1.1.11 val.txt
1.1.12 yolov4-tiny.cfg
1.1.13 yolov4-tiny.conv.29
1.1.14 yolov4-tiny-test.cfg
1.2 训练过程
1.3 训练结果
2 制作预训练模型
1 训练yolov4-tiny
首先在darknet创建一个文件夹custom_training
文件夹中包含这些文件
我们从左往右来讲,在文章的最后有识别效果,如果想先看效果的可以拉下去,看完再拉上来
1.1 文件准备
1.1.1 Annotations
获取方式:标注软件标注图像后获得
这里面放的都是标注好的xml文件,使用不同的标注软件标注结果可能略有不同,但xml的文件格式都是一样的,我们打开一个看一下
下面这个xml是一个图中只有一个框子的
下面这个是一个图中有多个框子的
1.1.2 JPEGImages
获取方式:把标注的图像放进去
这里面放的都是被标注的图片,注意要与标注文件对应(比如0.jpg与0.xml)
1.1.3 labels
获取方式:由create_labels_txt.py转换xml文件获得
这个文件夹中都是放的xml转换过来的项目专用的txt文件(我们后面有一个文件专门转换它),由于是转换过来的,所以自然也是与图像一一对应的
我们打开一个看一下
下面这个是一个框的情况,发现里面有5个数,这5个数的含义依次是
- 标签号,从0开始
- 标注框的中心点x坐标 除 图像宽度 的比值
- 标注框的中心点y坐标 除 图像高度 的比值
- 标注框的宽度 除 图像宽度 的比值
- 标注框的高度 除 图像高度 的比值
我们下面再看一个多个框的情况
有几个框就有几行,最后不要有换行符
1.1.4 trained_models
创建一个空的文件夹就行了,这个文件夹是专门放训练好的模型文件的,没训练之前文件夹中是空的
在训练之后每1000个batch会自动保存一次,我训练了4000个batch,现在里面是这样的
后面要用的话用 yolov4-tiny_final.weights就可以了
1.1.5 classes.name
获取方式:用记事本写一个文件,后缀为.name就可以了
这里面写的是所有标注的标签,有几个就写几个,标签用换行符隔开,最后一行末尾没有换行符
我再举个例子
这里的顺序要与后续create_labels_txt.py的顺序一致
如果你不知道有多少标签,后面可以通过get_labels.py来查询
1.1.6 create_labels_txt.py
获取方式:一个py文件
文件中的内容是这样的
import xml.etree.ElementTree as ET
import os
import re
with open('classes.names','r') as f:
classes = f.read().splitlines()
p = re.compile(r'(.*)\.')
for i in os.listdir('Annotations'):
i = p.findall(i)[0]
xml_file = '/home/nvidia/darknet/custom_training/Annotations/{}.xml'.format(i)
tree = ET.parse(xml_file)
root = tree.getroot()
for size in root.iter('size'):
width = int(size.find('width').text)
height = int(size.find('height').text)
all_content = ''
for obj in root.iter('object'):
cls = obj.find('name').text
cls_id = classes.index(cls)
xmlbox = obj.find('bndbox')
xmin = int(xmlbox.find('xmin').text)
ymin = int(xmlbox.find('ymin').text)
xmax = int(xmlbox.find('xmax').text)
ymax = int(xmlbox.find('ymax').text)
x = (xmin+xmax)/2/width
y = (ymin+ymax)/2/height
w = (xmax-xmin)/width
h = (ymax-ymin)/height
one_content = '{} {:.4f} {:.4f} {:.4f} {:.4f}'.format(cls_id,x,y,w,h)
all_content = all_content + one_content + '\n'
all_content = all_content.strip('\n')
file = open('./labels/{}.txt'.format(i),'w')
print(all_content,file=file,flush=True)
有几个参数需要随着数据集变动
- classes 这个是训练的标签,需要与classes.name的内容一致,注意顺序也要一致
- xml_file 放的是Annotations中xml的文件,我这里放的绝对路径,也可以放相对路径
- xmin,ymin,ymin,ymax 我不确定所有的标注文件是否都是这四个点(有的可能是直接给宽度),如果你的xml文件中写的是别的,那么你就对应改一下
- cls_id 这个是labels中产生txt的第一个内容
- x,y,w,h 这四个就是labels中产生的txt的后四个内容
如果按照我写的结构放置文件的话,直接运行就可以了
1.1.7 custom_training.data
获取方式:用记事本写,后缀改为data,最后一行末尾没有换行符
这里的信息的含义如下,注意所有路径都是绝对路径
字段 | 含义 |
---|---|
classes | 类别总数 |
train | train.txt的绝对路径,train.txt我们后面会提到 |
valid | val.txt的绝对路径,val.txt我们后面会提到 |
names | classes.name的绝对路径 |
backup | trained_models的绝对路径,注意后面要再加一个斜杠 |
1.1.8 get_labels.py
原理是用python的set()变量,set()变量中的元素不重复,访问xml中的name,将所有的内容添加的set中
1.1.9 get_train_val.py
这个是用来辅助创建train.txt与val.txt的
import os
a = 0
train_txt = open('train.txt','w')
val_txt = open('val.txt','w')
for i in os.listdir('JPEGImages'):
a = a + 1
if a < 15712: #train
print('/home/nvidia/darknet/custom_training/JPEGImages/' + i,file=train_txt,flush=True)
else: #test
print('/home/nvidia/darknet/custom_training/JPEGImages/' + i,file=val_txt,flush=True)
6065是我当前有7581张图片,我想选择其中的80%(6065张)作为训练图片,其余的作为测试图片
1.1.10 train.txt
获取方式:笔记本搞
这里放的是训练图像的绝对路径
1.1.11 val.txt
获取方式:笔记本搞
这里放的是所有测试图像的绝对路径,方法与train.txt相同,让其余图像作为测试图像
1.1.12 yolov4-tiny.cfg
获取方式:在darkent的cfg中有,复制过来
yolov4-tiny.cfg与其余的yolo模型修改起来差不多,我比较常用yolov4-tiny.cfg与yolov4-custom.cfg,这两个cfg文件在这个文件夹中都有,tiny速度快但精度差,custom精度高但速度慢,下面说几个经常改的参数
- batch与subdivisions
训练模型时可能会出现CUDA爆掉的情况,这个时候我们需要修改batch与subdivisions,batch是会影响训练结果的,太小是不行的,subdivisions的意思是把batch分成多少份,我下面这种图就是batch为64,然后把batch再分成64份一点一点儿给,这样就不会出现CUDA爆掉的问题了
- width与height
这个并不是图像的宽与高,这个实际上是模型的输入大小,宽和高必须为32的倍数
你的模型宽高越大,训练时间越长,模型越精准,预测的更慢
你的模型宽高越小,训练时间越短,模型越不精准,预测的更快
- learning_rate
learning_reate是学习速率,我们在训练的过程中可能会出现loss为NaN的情况,这个时候就不用再训练了,这个情况叫梯度爆炸或梯度消失,这个时候可以尝试改一下学习率
- max_batches与steps
在帮助文档中建议 max_batches的值设置为(种类数)*2000
step有两个值,第一个值是max_batches的80%,第二个值是max_batches的90%
- classes与filters
classes是训练的类别数量,比如你要识别5种不同的东西classes就给5,识别7种类别不同的东西就给7
filters计算公式为(种类数+5)*3,我下面图片种类数为2,所以这里的filters是21
在yolov4-custom.cfg中一共有三处,在yolv4-tiny.cfg有两处,都要改
1.1.13 yolov4-tiny.conv.29
获取方式:从我上面的网盘链接中搞,或者用这个github链接 https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v4_pre/yolov4-tiny.conv.29
这个链接是在项目 GitHub - AlexeyAB/darknet: YOLOv4 / Scaled-YOLOv4 / YOLO - Neural Networks for Object Detection (Windows and Linux version of Darknet ) 中的这里
1.1.14 yolov4-tiny-test.cfg
获取方式:把yolov4-tiny.cfg复制过来,然后改一些东西
只改batch,把下面的batch注释掉
这个其实你改不改都行,在测试的时候直接使用yolv4-tiny.cfg进行使用就行
1.2 训练过程
在darkent路径下打开终端,之后输入
./darknet detector train custom_training/custom_training.data custom_training/yolov4-tiny.cfg custom_training/yolov4-tiny.conv.29
输入之后终端的情况是这样的
而且会出现一个图,这里会记录我们每一个batch的loss情况
训练接触后会显示模型已保存,我大致训练了两个小时左右
我的loss情况是这样的
最终loss是0.1498左右
1.3 训练结果
在darknet下打开终端后输入
./darknet detector test custom_training/custom_training.data custom_training/yolov4-tiny-test.cfg custom_training/trained_models/yolov4-tiny_final.weights custom_training/JPEGImages/000001.jpg
最后的图片路径你可以自定义,输入之后终端是这样的
之后会出现一个小图像
把它放大后的效果是这样的
2 制作预训练模型
我们先说层这个概念,在训练的开始你可以看到你选用的模型一共有多少层。
你可以将训练好的模型作为预训练模型,比如
./darknet partial ./custom_training/yolov3-tiny.cfg ./custom_training/yolov3-tiny_final.weights ./yolov3-tiny.conv.15 15
- ./darknet partial[训练模型用到的cfg位置] [训练好的模型文件位置] [要生成的预训练模型文件位置] [保留网络层数权重]
之后就在执行代码的位置就会得到 yolov3-tiny.conv.15 这个预训练模型文件