---
# System prepended metadata

title: YOLO3 darknet训练自己的数据 (呼之)

---

# YOLO3 darknet训练自己的数据 (呼之)

本文内容列表

1 准备训练数据和验证数据
2 生成train.txt 和 val.txt
3 准备KD.names文件
4 准备KD.data文件
5 准备yolov3-KD.cfg_train文件
6 下载预训练权重
7 开始训练
8 测试
9 计算mAP
10 常见问题
如果照着本文的流程出现问题，可以查看第10常见问题，也许会有解决办法

本文将在darknet环境下用yolo3来训练自己的数据，如若未安装darknet，请参考下面的链接

GPU版本
JustDoIT：windows下darknet之yolo(gpu版本）安装
​
zhuanlan.zhihu.com
图标
CPU版本
JustDoIT：windows下darknet之YOLO3安装
​
zhuanlan.zhihu.com
图标
如果电脑有独显，建议使用GPU版本的，因为实在是快太多了

官方教程

https://github.com/AlexeyAB/darknet
​
github.com
本文的数据和代码均可以在我github上找到

JustTryItNow/YOLO_images_script
​
github.com
图标
1 准备训练数据和验证数据

收集图片
这里的图片我是从网上自己下载的，下载的是库里和杜兰特的图片。由于时间原因，只下载了50张图片，当然，这些数据对于训练一个很好的模型是远远不够的。当然，我也会把这些数据集放到百度云盘上，方便大家下载从而节省时间。把图片分到两个文件夹中，一个是train_images（一般放build\darknet\x64\data文件夹里），一个是val_images（一般放build\darknet\x64\data文件夹里）。



做标签
推荐labelImg




下载后解压缩，双击labelImg.exe出现下面的界面


点击Open Dir打开要做标签的文件夹，这里用train_images举例，val_images的操作一样


点击Create RectBox开始做标签（图片里面出现几个目标就标注几个框）

如下图，图片里有库里(KuLi)和杜兰特(DuLanTe)，分别标注两个目标，然后点击Save，然后Next Image就可以标注下一张了


做完标签的两个文件夹分别是这样的



xml转txt
标签文件是xml格式的，yolo要求是txt（每张图片对应一个txt文件，txt的文件名要和图片的文件名一个）

xml文件格式如下


而txt的格式要求每个标注框的格式如下

<object-class> <x_center> <y_center> <width> <height>
<object-class> 类名：从0到（类别数-1），在这里一共有KuLI和DuLanTe两类，这里记KuLI为0，DuLanTe为

<x_center> 中心点横坐标：这里的坐标并不是绝对坐标（真实坐标），而是相对于图片宽度的相对坐标，转换公式为<x_center> = <absolute_x> / <image_width>，这里的absolute_x是指标注框中心的横坐标

<y_center> 中心点纵坐标：这里的坐标并不是绝对坐标（真实坐标），而是相对于图片高度的相对坐标，转换公式为<x_center> = <absolute_y> / <image_height>，这里的absolute_x是指标注框中心的纵坐标

<width> 标注框宽度：这里的宽度并不是绝对宽度（真实宽度），而是相对于图片宽度的相对宽度，转换公式为<width> = <absolute_width> / <image_width>

<height> 标注框宽度：这里的宽度并不是绝对宽度（真实宽度），而是相对于图片宽度的相对高度，转换公式为<height> = <absolute_height> / <image_height>

对于上面的那个xml文件，其对应的txt文件内容应为

0 0.229 0.3605015673981191 0.063 0.10344827586206896
1 0.685 0.22100313479623823 0.061 0.10815047021943573
xml转txt代码(python3)如下：

# 此代码和data文件夹同目录
import glob
import xml.etree.ElementTree as ET


#  类名
class_names = ['KuLi', 'DuLanTe']
#  xml文件路径，train_images只需改为val_images就可以处理val_images的了
path = 'data/train_images/' 

#  转换一个xml文件为txt
def single_xml_to_txt(xml_file):
    tree = ET.parse(xml_file)
    root = tree.getroot()
    # 保存的txt文件路径
    txt_file = xml_file.split('.')[0]+'.txt'
    with open(txt_file, 'w') as txt_file:
        for member in root.findall('object'):
	    # filename = root.find('filename').text
	    picture_width = int(root.find('size')[0].text)
	    picture_height = int(root.find('size')[1].text)
	    class_name = member[0].text
	    #  类名对应的index
	    class_num = class_names.index(class_name)
	    box_x_min = int(member[4][0].text)  # 左上角横坐标
	    box_y_min = int(member[4][1].text)  # 左上角纵坐标
	    box_x_max = int(member[4][2].text)  # 右下角横坐标
	    box_y_max = int(member[4][3].text)  # 右下角纵坐标
	    # 转成相对位置和宽高
	    x_center = (box_x_min + box_x_max) / (2 * picture_width)
	    y_center = (box_y_min + box_y_max) / (2 * picture_height)
	    width = (box_x_max - box_x_min) / picture_width
	    height = (box_y_max - box_y_min) / picture_height
	    print(class_num, x_center, y_center, width, height)
	    txt_file.write(str(class_num) + ' ' + str(x_center) + ' ' + str(y_center) + ' ' + str(width) + ' ' + str(height) + '\n')

#  转换文件夹下的所有xml文件为txt
def dir_xml_to_txt(path):
    for xml_file in glob.glob(path + '*.xml'):
        single_xml_to_txt(xml_file)

dir_xml_to_txt(path)
转换完的结果如下图：



转换完之后可以把xml删除掉，当然xml文件留着说不定以后还有用，建议剪切到其他地方。

## **生成train.txt 和 val.txt （一般放build\darknet\x64\data文件夹里）**

train.txt文件包含train_images中所有图片的路径，每个图片一行

val.txt文件包含val_images中所有图片的路径，每个图片一行

train.txt格式如下：

data/train_images/1.jpg
data/train_images/2.jpg
生成train.txt val.txt的代码如下：

#此代码和data文件夹同目录
import glob

path = 'data/'

def generate_train_and_val(image_path, txt_file):
    with open(txt_file, 'w') as tf:
        for jpg_file in glob.glob(image_path + '*.jpg'):
            tf.write(jpg_file + '\n')

generate_train_and_val(path + 'train_images/', path + 'train.txt')
generate_train_and_val(path + 'val_images/', path + 'val.txt')
3 准备KD.names文件（一般放build\darknet\x64\data文件夹里）

这个文件每一行是一个类名（注意顺序），如下：

KuLi
DuLanTe
4 准备KD.data文件（一般放build\darknet\x64\data文件夹里）

classes= 2
train = data/train.txt
valid = data/val.txt
names = data/KD.names
backup = backup/
classes 是类别数

5 准备yolov3-KD.cfg_train文件（一般放build\darknet\x64\cfg文件夹里）

在build\darknet\x64\cfg下创建一个yolov3-KD.cfg_train文件，里面的内容可以复制build\darknet\x64\cfg\yolov3.cfg里面的内容，然后做几处修改即可：

batch=16
subdivisions=16
每次计算的图片数目 = batch/subdivisions，然后把结果合起来也就是一个batch更新模型一次


如果batch设置过大，会报显存不够错误，我电脑配置太低，batch设置为16，subdivisions为16，可以根据自己的情况进行设置，如下图


classes=2
classes是类别数，这里只有两类，所以设置为2，文件里一共有3处需要修改classes，一般是文件的第610、689、776行

filters=21
filters的计算公式为(classes + 5)x3，我们这里有两类，所以是(2+5)*3=21，文件里一共有3处需要修改，这里需要注意，文件里有多处filter，但我们只需要修改[yolo]层之前的[convolutional]层下面的filters，位置一般是文件的第603、696、783行，如下图




6 下载预训练权重（放到build\darknet\x64文件夹里）

https://pjreddie.com/media/files/darknet53.conv.74
​
pjreddie.com
7 开始训练

使用以下的命令进行训练（命令窗口切换到build\darknet\x64）

darknet.exe detector train data/KD.data cfg/yolov3-KD.cfg_train darknet53.conv.74
每迭代100次就会在backup文件夹上生成一个模型权重

当avg loss在好几个迭代中都没有下降，此时可以停止训练了

8 测试

使用以下的命令进行训练（命令窗口切换到build\darknet\x64）

darknet.exe detector test data/KD.data cfg/yolov3-KD.cfg_test backup/KD_yolov3_2000.weights data/test1.jpg -thresh 0.5
cfg/yolov3-KD.cfg_test 为测试的配置文件，只需要复制cfg/yolov3-KD.cfg_train的内容然后把batch和subdivisions设置为1即可

backup/KD_yolov3_2000.weights 为模型权重

data/test1.jpg 为测试图片的路径

-thresh 0.5 设置阈值，例如0.5，如果没有标注框就设置低点，如果很低都还没有标注框，那可能是某个环节出问题了，再仔细检查一下每个步骤

9 计算mAP

如果想计算mAP，可以参考下面链接

rafaelpadilla/Object-Detection-Metrics
​
github.com
图标
10 常见问题

Couldn't open file: data/train_labels/1.txt
可以新建一个data/train_labels文件夹，把data/train_images中的所有txt文件放到该文件夹中

显示darknet.exe不是内部或外部命令，也不是可运行的程序（感谢@汪洋 提供的解决办法）
把darknet.exe复制到x64目录下，就可以了

couldn't open file: data/train.txt
可能是在window环境下处理文件出现编码格式的问题

解决方法： 应该使用Notepad++
1）视图》显示符号》显示所有符号；
2）编辑》换行符》转换为UNIX格式；
确保最后一行有且仅有一个LF;
如果大家有什么问题请在下方留言！

如果觉得文章不错的可以关注我，一起交流学习！
