1. 环境搭建

  1. 将github库download下来。
git clone https://github.com/ultralytics/yolov3.git
  1. 建议在linux环境下使用anaconda进行搭建
conda create -n yolov3 python=3.7
  1. 安装需要的软件
pip install -r requirements.txt

环境要求:

  • python >= 3.7
  • pytorch >= 1.1
  • numpy
  • tqdm
  • opencv-python

其中只需要注意pytorch的安装:

https://pytorch.org/中根据操作系统,python版本,cuda版本等选择命令即可。

关于深度学习环境搭建请参看:https://www.cnblogs.com/pprp/p/9463974.html

anaconda常用用法:https://www.cnblogs.com/pprp/p/9463124.html

2. 数据集构建

1. xml文件生成需要Labelimg软件

在Windows下使用LabelImg软件进行标注,能在网上下载,或者通过github搜索得到。

  • 使用快捷键:
Ctrl + u  加载目录中的所有图像,鼠标点击Open dir同功能
Ctrl + r 更改默认注释目标目录(xml文件保存的地址)
Ctrl + s 保存
Ctrl + d 复制当前标签和矩形框
space 将当前图像标记为已验证
w 创建一个矩形框
d 下一张图片
a 上一张图片
del 删除选定的矩形框
Ctrl++ 放大
Ctrl-- 缩小
↑→↓← 键盘箭头移动选定的矩形框

2. VOC2007 数据集格式

-data
- VOCdevkit2007
- VOC2007
- Annotations (标签XML文件,用对应的图片处理工具人工生成的)
- ImageSets (生成的方法是用sh或者MATLAB语言生成)
- Main
- test.txt
- train.txt
- trainval.txt
- val.txt
- JPEGImages(原始文件)
- labels (xml文件对应的txt文件)

通过以上软件主要构造好JPEGImages和Annotations文件夹中内容,Main文件夹中的txt文件可以通过python脚本生成:

import os
import random trainval_percent = 0.8
train_percent = 0.8
xmlfilepath = 'Annotations'
txtsavepath = 'ImageSets\Main'
total_xml = os.listdir(xmlfilepath) num=len(total_xml)
list=range(num)
tv=int(num*trainval_percent)
tr=int(tv*train_percent)
trainval= random.sample(list,tv)
train=random.sample(trainval,tr) ftrainval = open('ImageSets/Main/trainval.txt', 'w')
ftest = open('ImageSets/Main/test.txt', 'w')
ftrain = open('ImageSets/Main/train.txt', 'w')
fval = open('ImageSets/Main/val.txt', 'w') for i in list:
name=total_xml[i][:-4]+'\n'
if i in trainval:
ftrainval.write(name)
if i in train:
ftrain.write(name)
else:
fval.write(name)
else:
ftest.write(name) ftrainval.close()
ftrain.close()
fval.close()
ftest.close()

生成labels文件,voc_label.py文件具体内容如下:

# -*- coding: utf-8 -*-
"""
Created on Tue Oct 2 11:42:13 2018
将本文件放到VOC2007目录下,然后就可以直接运行
需要修改的地方:
1. sets中替换为自己的数据集
2. classes中替换为自己的类别
3. 将本文件放到VOC2007目录下
4. 直接开始运行
""" import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
sets=[('2007', 'train'), ('2007', 'val'), ('2007', 'test')] #替换为自己的数据集
classes = ["head", "eye", "nose"] #修改为自己的类别
#classes = ["eye", "nose"] def convert(size, box):
dw = 1./(size[0])
dh = 1./(size[1])
x = (box[0] + box[1])/2.0 - 1
y = (box[2] + box[3])/2.0 - 1
w = box[1] - box[0]
h = box[3] - box[2]
x = x*dw
w = w*dw
y = y*dh
h = h*dh
return (x,y,w,h)
def convert_annotation(year, image_id):
in_file = open('VOC%s/Annotations/%s.xml'%(year, image_id)) #将数据集放于当前目录下
out_file = open('VOC%s/labels/%s.txt'%(year, image_id), 'w')
tree=ET.parse(in_file)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
for obj in root.iter('object'):
difficult = obj.find('difficult').text
cls = obj.find('name').text
if cls not in classes or int(difficult)==1:
continue
cls_id = classes.index(cls)
xmlbox = obj.find('bndbox')
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))
bb = convert((w,h), b)
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
wd = getcwd()
for year, image_set in sets:
if not os.path.exists('VOC%s/labels/'%(year)):
os.makedirs('VOC%s/labels/'%(year))
image_ids = open('VOC%s/ImageSets/Main/%s.txt'%(year, image_set)).read().strip().split()
list_file = open('%s_%s.txt'%(year, image_set), 'w')
for image_id in image_ids:
list_file.write('VOC%s/JPEGImages/%s.jpg\n'%(year, image_id))
convert_annotation(year, image_id)
list_file.close()
#os.system("cat 2007_train.txt 2007_val.txt > train.txt") #修改为自己的数据集用作训练

到底为止,VOC格式数据集构造完毕,但是还需要继续构造符合darknet格式的数据集(coco)。

需要说明的是:如果打算使用coco评价标准,需要构造coco中json格式,如果要求不高,只需要VOC格式即可,使用作者写的mAP计算程序即可。

voc的xml转coco的json文件脚本:xml2json.py


# -*- coding: utf-8 -*-
"""
Created on Tue Aug 28 15:01:03 2018
需要改动xml_path and json_path
"""
#!/usr/bin/python
# -*- coding:utf-8 -*-
# @Description: xml转换到coco数据集json格式 import os, sys, json,xmltodict from xml.etree.ElementTree import ElementTree, Element
from collections import OrderedDict XML_PATH = "/home/learner/datasets/VOCdevkit2007/VOC2007/Annotations/test"
JSON_PATH = "./test.json"
json_obj = {}
images = []
annotations = []
categories = []
categories_list = []
annotation_id = 1 def read_xml(in_path):
'''读取并解析xml文件'''
tree = ElementTree()
tree.parse(in_path)
return tree def if_match(node, kv_map):
'''判断某个节点是否包含所有传入参数属性
node: 节点
kv_map: 属性及属性值组成的map'''
for key in kv_map:
if node.get(key) != kv_map.get(key):
return False
return True def get_node_by_keyvalue(nodelist, kv_map):
'''根据属性及属性值定位符合的节点,返回节点
nodelist: 节点列表
kv_map: 匹配属性及属性值map'''
result_nodes = []
for node in nodelist:
if if_match(node, kv_map):
result_nodes.append(node)
return result_nodes def find_nodes(tree, path):
'''查找某个路径匹配的所有节点
tree: xml树
path: 节点路径'''
return tree.findall(path) print ("-----------------Start------------------")
xml_names = []
for xml in os.listdir(XML_PATH):
#os.path.splitext(xml)
#xml=xml.replace('Cow_','')
xml_names.append(xml) '''xml_path_list=os.listdir(XML_PATH)
os.path.split
xml_path_list.sort(key=len)'''
xml_names.sort(key=lambda x:int(x[:-4]))
new_xml_names = []
for i in xml_names:
j = 'Cow_' + i
new_xml_names.append(j) #print xml_names
#print new_xml_names
for xml in new_xml_names:
tree = read_xml(XML_PATH + "/" + xml)
object_nodes = get_node_by_keyvalue(find_nodes(tree, "object"), {})
if len(object_nodes) == 0:
print (xml, "no object")
continue
else:
image = OrderedDict()
file_name = os.path.splitext(xml)[0]; # 文件名
para1 = file_name + ".jpg"
height_nodes = get_node_by_keyvalue(find_nodes(tree, "size/height"), {})
para2 = int(height_nodes[0].text)
width_nodes = get_node_by_keyvalue(find_nodes(tree, "size/width"), {})
para3 = int(width_nodes[0].text) fname=file_name[4:]
para4 = int(fname) for f,i in [("file_name",para1),("height",para2),("width",para3),("id",para4)]:
image.setdefault(f,i) #print(image)
images.append(image) #构建images name_nodes = get_node_by_keyvalue(find_nodes(tree, "object/name"), {})
xmin_nodes = get_node_by_keyvalue(find_nodes(tree, "object/bndbox/xmin"), {})
ymin_nodes = get_node_by_keyvalue(find_nodes(tree, "object/bndbox/ymin"), {})
xmax_nodes = get_node_by_keyvalue(find_nodes(tree, "object/bndbox/xmax"), {})
ymax_nodes = get_node_by_keyvalue(find_nodes(tree, "object/bndbox/ymax"), {})
for index, node in enumerate(object_nodes):
annotation = {}
segmentation = []
bbox = []
seg_coordinate = [] #坐标
seg_coordinate.append(int(xmin_nodes[index].text))
seg_coordinate.append(int(ymin_nodes[index].text))
seg_coordinate.append(int(xmin_nodes[index].text))
seg_coordinate.append(int(ymax_nodes[index].text))
seg_coordinate.append(int(xmax_nodes[index].text))
seg_coordinate.append(int(ymax_nodes[index].text))
seg_coordinate.append(int(xmax_nodes[index].text))
seg_coordinate.append(int(ymin_nodes[index].text))
segmentation.append(seg_coordinate)
width = int(xmax_nodes[index].text) - int(xmin_nodes[index].text)
height = int(ymax_nodes[index].text) - int(ymin_nodes[index].text)
area = width * height
bbox.append(int(xmin_nodes[index].text))
bbox.append(int(ymin_nodes[index].text))
bbox.append(width)
bbox.append(height) annotation["segmentation"] = segmentation
annotation["area"] = area
annotation["iscrowd"] = 0
fname=file_name[4:]
annotation["image_id"] = int(fname)
annotation["bbox"] = bbox
cate=name_nodes[index].text
if cate=='head':
category_id=1
elif cate=='eye':
category_id=2
elif cate=='nose':
category_id=3
annotation["category_id"] = category_id
annotation["id"] = annotation_id
annotation_id += 1
annotation["ignore"] = 0
annotations.append(annotation) if category_id in categories_list:
pass
else:
categories_list.append(category_id)
categorie = {}
categorie["supercategory"] = "none"
categorie["id"] = category_id
categorie["name"] = name_nodes[index].text
categories.append(categorie) json_obj["images"] = images
json_obj["type"] = "instances"
json_obj["annotations"] = annotations
json_obj["categories"] = categories f = open(JSON_PATH, "w")
#json.dump(json_obj, f)
json_str = json.dumps(json_obj)
f.write(json_str)
print ("------------------End-------------------")

(运行bash yolov3/data/get_coco_dataset.sh,仿照格式将数据放到其中)

但是这个库还需要其他模型:

3. 创建*.names file,

其中保存的是你的所有的类别,每行一个类别,如data/coco.names:

head
eye
nose

4. 更新data/coco.data,其中保存的是很多配置信息

classes = 3 # 改成你的数据集的类别个数
train = ./data/2007_train.txt # 通过voc_label.py文件生成的txt文件
valid = ./data/2007_test.txt # 通过voc_label.py文件生成的txt文件
names = data/coco.names # 记录类别
backup = backup/ # 记录checkpoint存放位置
eval = coco # 选择map计算方式

5. 更新cfg文件,修改类别相关信息

打开cfg文件夹下的yolov3.cfg文件,大体而言,cfg文件记录的是整个网络的结构,是核心部分,具体内容讲解请见:https://pprp.github.io/2018/09/20/tricks.html

只需要更改每个[yolo]层前边卷积层的filter个数即可:

每一个[region/yolo]层前的最后一个卷积层中的 filters=预测框的个数(mask对应的个数,比如mask=0,1,2, 代表使用了anchors中的前三对,这里预测框个数就应该是3*(classes+5) ,5的意义是4个坐标+1个置信度代表这个格子含有目标的概率,也就是论文中的tx,ty,tw,th,po

举个例子:我有三个类,n = 3, 那么filter = 3x(n+5) = 24

[convolutional]
size=1
stride=1
pad=1
filters=255 # 改为 24
activation=linear [yolo]
mask = 6,7,8
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
classes=80 # 改为 3
num=9
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=1

6. 数据集格式说明

- yolov3
- data
- 2007_train.txt
- 2007_test.txt
- coco.names
- coco.data
- annotations(json files)
- images(将2007_train.txt中的图片放到train2014文件夹中,test同理)
- train2014
- 0001.jpg
- 0002.jpg
- val2014
- 0003.jpg
- 0004.jpg
- labels(voc_labels.py生成的内容需要重新组织一下)
- train2014
- 0001.txt
- 0002.txt
- val2014
- 0003.txt
- 0004.txt
- samples(存放待测试图片)

2007_train.txt内容示例:

/home/dpj/yolov3-master/data/images/val2014/Cow_1192.jpg
/home/dpj/yolov3-master/data/images/val2014/Cow_1196.jpg
.....

注意images和labels文件架构一致性,因为txt是通过简单的替换得到的:

images -> labels
.jpg -> .txt

3. 训练模型

预训练模型:

开始训练:

python train.py --data data/coco.data --cfg cfg/yolov3.cfg

如果日志正常输出那证明可以运行了

如果中断了,可以恢复训练

python train.py --data data/coco.data --cfg cfg/yolov3.cfg --resume

4. 测试模型

将待测试图片放到data/samples中,然后运行

python detect.py --weights weights/best.pt

5. 评估模型

python test.py --weights weights/latest.pt

如果使用cocoAPI使用以下命令:

git clone https://github.com/cocodataset/cocoapi && cd cocoapi/PythonAPI && make && cd ../.. && cp -r cocoapi/PythonAPI/pycocotools yolov3
cd yolov3 python3 test.py --save-json --img-size 416
Namespace(batch_size=32, cfg='cfg/yolov3-spp.cfg', conf_thres=0.001, data_cfg='data/coco.data', img_size=416, iou_thres=0.5, nms_thres=0.5, save_json=True, weights='weights/yolov3-spp.weights')
Using CUDA device0 _CudaDeviceProperties(name='Tesla V100-SXM2-16GB', total_memory=16130MB)
Class Images Targets P R mAP F1
Calculating mAP: 100%|█████████████████████████████████████████| 157/157 [05:59<00:00, 1.71s/it]
all 5e+03 3.58e+04 0.109 0.773 0.57 0.186
Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.335
Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.565
Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.349
Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.151
Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.360
Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.493
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.280
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.432
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.458
Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.255
Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.494
Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.620 python3 test.py --save-json --img-size 608 --batch-size 16
Namespace(batch_size=16, cfg='cfg/yolov3-spp.cfg', conf_thres=0.001, data_cfg='data/coco.data', img_size=608, iou_thres=0.5, nms_thres=0.5, save_json=True, weights='weights/yolov3-spp.weights')
Using CUDA device0 _CudaDeviceProperties(name='Tesla V100-SXM2-16GB', total_memory=16130MB)
Class Images Targets P R mAP F1
Computing mAP: 100%|█████████████████████████████████████████| 313/313 [06:11<00:00, 1.01it/s]
all 5e+03 3.58e+04 0.12 0.81 0.611 0.203
Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.366
Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.607
Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.386
Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.207
Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.391
Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.485
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.296
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.464
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.494
Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.331
Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.517
Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.618

6. 可视化

可以使用python -c from utils import utils;utils.plot_results()

创建drawLog.py

def plot_results():
# Plot YOLO training results file 'results.txt'
import glob
import numpy as np
import matplotlib.pyplot as plt
#import os; os.system('rm -rf results.txt && wget https://storage.googleapis.com/ultralytics/results_v1_0.txt') plt.figure(figsize=(16, 8))
s = ['X', 'Y', 'Width', 'Height', 'Objectness', 'Classification', 'Total Loss', 'Precision', 'Recall', 'mAP']
files = sorted(glob.glob('results.txt'))
for f in files:
results = np.loadtxt(f, usecols=[2, 3, 4, 5, 6, 7, 8, 17, 18, 16]).T # column 16 is mAP
n = results.shape[1]
for i in range(10):
plt.subplot(2, 5, i + 1)
plt.plot(range(1, n), results[i, 1:], marker='.', label=f)
plt.title(s[i])
if i == 0:
plt.legend()
plt.savefig('./plot.png')
if __name__ == "__main__":
plot_results()

7. 高级进阶-网络结构更改

详细cfg文件讲解:https://pprp.github.io/2018/09/20/YOLO cfg文件解析/

参考资料以及网络更改经验:https://pprp.github.io/2019/06/20/YOLO经验总结/

欢迎在评论区进行讨论,也便于我继续完善该教程。

ps: 最近写了一个一键生成脚本,可以直接将VOC2007数据格式转换为U版yolov3要求的格式,地址在这里:https://github.com/pprp/voc2007_for_yolo_torch

YOLOv4出来了,点击这篇文章查看笔者总结的YOLOv4梳理。

pytorch版yolov3训练自己数据集的更多相关文章

  1. win10 下的YOLOv3 训练 wider_face 数据集检测人脸

    1.数据集下载 (1)wider_face 数据集网址为 http://shuoyang1213.me/WIDERFACE/index.html 下载以上几项文件(这里推荐 google Drive ...

  2. Win10 + YOLOv3训练VOC数据集-----How to train Pascal VOC Data

    How to train (Pascal VOC Data): Download pre-trained weights for the convolutional layers (154 MB):  ...

  3. 基于pytorch实现Resnet对本地数据集的训练

    本文是使用pycharm下的pytorch框架编写一个训练本地数据集的Resnet深度学习模型,其一共有两百行代码左右,分成mian.py.network.py.dataset.py以及train.p ...

  4. 目标检测-基于Pytorch实现Yolov3(1)- 搭建模型

    原文地址:https://www.cnblogs.com/jacklu/p/9853599.html 本人前段时间在T厂做了目标检测的项目,对一些目标检测框架也有了一定理解.其中Yolov3速度非常快 ...

  5. 记pytorch版faster rcnn配置运行中的一些坑

    记pytorch版faster rcnn配置运行中的一些坑 项目地址 https://github.com/jwyang/faster-rcnn.pytorch 一般安装配置参考README.md文件 ...

  6. 利用YOLOV3训练自己的数据

    写在前面:YOLOV3只有修改了源码才需要重新make,而且make之前要先make clean. 一.准备数据 在/darknet/VOCdevkit1下建立文件夹VOC2007. voc2007文 ...

  7. Pytorch多GPU训练

    Pytorch多GPU训练 临近放假, 服务器上的GPU好多空闲, 博主顺便研究了一下如何用多卡同时训练 原理 多卡训练的基本过程 首先把模型加载到一个主设备 把模型只读复制到多个设备 把大的batc ...

  8. Pytorch版本yolov3源码阅读

    目录 Pytorch版本yolov3源码阅读 1. 阅读test.py 1.1 参数解读 1.2 data文件解析 1.3 cfg文件解析 1.4 根据cfg文件创建模块 1.5 YOLOLayer ...

  9. 目标检测之车辆行人(tensorflow版yolov3)

    背景: 在自动驾驶中,基于摄像头的视觉感知,如同人的眼睛一样重要.而目前主流方案基本都采用深度学习方案(tensorflow等),而非传统图像处理(opencv等). 接下来我们就以YOLOV3为基本 ...

随机推荐

  1. 什么是 https ?这应该是全网把 https 讲的最好的一篇文章了

    https://blog.csdn.net/m0_37907797/article/details/102759257

  2. HDU1237 简单计算器 栈

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1237 题目大意:读入一个只包含 +, -, *, / 的非负整数计算表达式,计算该表达式的值. 题目分 ...

  3. Java程序内存分析

    1. Runtime.getRuntime().freeMemory() 和 jvisualvm.exe http://blog.csdn.net/u011004037/article/details ...

  4. Deepin Linux下为Wine创建文件关联

    在Deepin Linux下,默认地,使用apt安装的Wine并没有创建文件关联,这使得在文件管理器中双击exe等Windows可执行文件时,不能直接运行.为此,必须手动在桌面环境中创建文件关联. 文 ...

  5. 通用mybatis单表操作接口

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "- ...

  6. JVM插码之六:jacoco插码及问题“$jacocodata 属性 Method not found: is$jacocoData”

    在使用jacoco统计自动化代码覆盖率 jacoco统计自动化代码覆盖率 1. 简介1.1. 什么是JacocoJacoco是一个开源的代码覆盖率工具,可以嵌入到Ant .Maven中,并提供了Ecl ...

  7. Android中使用LitePal操控SQLite数据库

    <第一行代码>读书手札 (一)什么是LitePal数据库 LitePal数据库是安卓的一个开源库,我们在以后的开发中,将会遇到许许多多的开源库,感谢开源社 区:因为开源社区的存在,一些我们 ...

  8. Python23之内置函数filter()和map()

    首先我们了解一个概念:迭代 迭代是访问集合元素的⼀种⽅式.迭代器是⼀个可以记住遍历的位置的对象.迭代器对象从集合的第⼀个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退. 我们已经知道 ...

  9. 从零开始学Flask框架-005

    表单 Flask-WTF 项目结构 pip install flask-wtf 为了实现CSRF 保护,Flask-WTF 需要程序设置一个密钥.Flask-WTF 使用这个密钥生成加密令牌,再用令牌 ...

  10. Java正则表达式获取中括号之间的内容

    参考: 求一个正则表达式提取中括号里的内容 [问题点数:80分]CSDN论坛 > Java > Web 开发 正则表达式 - 菜鸟教程 不包含中括号 正则表达式如下: \\[(.*?)] ...