上一篇介绍了keras版本的yolov3项目使用经验,这一篇是pytorch版本的。
本文主要的参考资料和github
项目如下:
Could not find the Qt platform plugin windows
错误解决方法:
https://blog.csdn.net/DonetRen/article/details/106437538
pytorch
版yolov3
训练自己数据集:
https://www.cnblogs.com/pprp/p/10863496.html
https://blog.csdn.net/qq_38587510/article/details/106019905
https://blog.csdn.net/qq_39056987/article/details/104327638
github
项目:
labelImg:https://github.com/tzutalin/labelImg
yolov3:https://github.com/ultralytics/yolov3(master是yolov5
,archive是yolov3
)
labelImg
安装PyQt5
和lxml
就可以运行了,运行可能会需要配置环境变量
具体参考文章:https://blog.csdn.net/DonetRen/article/details/106437538
数据标注的方式和数据集分割的方式(划分为训练集、验证集和测试集)与keras-yolov3
项目是一样的,但是数据格式转换的操作有所不同,pytorch-yolov3
项目中没有keras
项目中的voc_annotation.py
用来转换数据格式,需要使用以下代码来完成操作。
# -*- coding: utf-8 -*-
"""
Created on Tue Oct 2 11:42:13 2018
此脚本VOC格式数据集构造完毕
需要说明的是:如果打算使用coco评价标准,需要继续构造符合 darknet格式的数据集(coco),构造coco中json格式
如果要求不高,只需要VOC格式即可,使用作者写的mAP计算程序即可
需要修改的地方:
1. sets中替换为自己的数据集
2. classes中替换为自己的类别
3. 将本文件放到VOC2007同级目录
4. 直接开始运行
"""
from xml.etree import ElementTree
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 = ["target"] # 因为仅有一个类别,所以给自己的类别命名为target
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(f'VOC{_year}/Annotations/{_image_id}.xml') # 将数据集放于当前目录下
out_file = open(f'VOC{_year}/labels/{_image_id}.txt', 'w')
# 读取xml文件
tree = ElementTree.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)
xml_box = obj.find('bndbox')
b = (float(xml_box.find('xmin').text),
float(xml_box.find('xmax').text),
float(xml_box.find('ymin').text),
float(xml_box.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(f'VOC{year}/labels/'):
os.makedirs(f'VOC{year}/labels/')
image_ids = open(f'VOC{year}/ImageSets/Main/{image_set}.txt').read().strip().split()
list_file = open(f'{year}_{image_set}.txt', 'w')
for image_id in image_ids:
# 这里的文件路径前面做了修改
list_file.write(f'data/VOCdevkit/VOC{year}/JPEGImages/{image_id}.jpg\n')
convert_annotation(year, image_id)
list_file.close()
# os.system("cat 2007_train.txt 2007_val.txt > train.txt") #修改为自己的数据集用作训练
list_file.write(f'data/VOCdevkit/VOC{year}/JPEGImages/{image_id}.jpg\n')
这里对写入的文件路径做了调整,前面加了data/VOCdevkit
,这样就可以直接把VOCdevkit
整个项目复制到pytorch-yolov3
项目的data目录下。
使用以上脚本生成的
MiniConda
实际上就是可以创建虚拟环境和包管理,但是不得不说使用conda
安装包很麻烦,使用国内镜像源下载很多包依然很慢很慢,而且想换源还要不断的改配置,相比较pip是真的好用,使用哪个镜像直接一个-i就可以,而且安装包快的飞起,这次算是对miniconda
的一次尝试吧,后面如果再做项目,没有特殊的环境需求,我可能还是倾向于使用统一环境,使用pip来安装包。
补充一个使用pycharm
配置conda
虚拟环境的方式。
Pytorch YoloV3
apex
混合精度加速,无显卡不能加速。若想要使用,需要单独从github拉相关的项目。
在data
目录下新建*.data文件,文件内容和说明如下:
classes = 1 # 改成你的数据集的类别个数
train = data/VOCdevkit/VOCdevkit/2007_train.txt # 通过voc_label.py文件生成的txt文件
valid = data/VOCdevkit/VOCdevkit/2007_test.txt # 通过voc_label.py文件生成的txt文件
names = data/yidun.names # 记录类别
backup = backup/ # 记录checkpoint存放位置
eval = coco # 选择map计算方式
backup=backup/
、eval=coco
这两项根据自身实际情况添加即可:
backup=backup/
指的是会在backup文件夹下存放训练得到的checkpoint;
eval=coco
指的是在测试和评判模型时采用coco数据集中的评判标准,倘若加了这一项,还需要对之前生成的.xml
文件做转换,将其由VOC
格式转换为coco格式的json
文件。
特别注意:文件真正的内容不要包含后面的注释,否则运行时会报错
修改yolov3.cfg
文件:
[convolutional]
size=1
stride=1
pad=1
filters=18 # 修改为3*(classes+5)
activation=linear
[yolo]
mask = 0,1,2
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
classes=1 # 修改为自己的类别数
num=9
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=1
只需要更改每个[yolo]
层前边卷积层的filter个数和[yolo]
层的classes数量即可,也就是上面代码注释部分标注的内容,一共三组6处地方需要修改;同样地,实际内容要去掉注释,否则会报错。
在Windows下运行该项目,可能会有多个地方出现打开文件时编码不正确的情况,因为Windows下默认编码为gbk
,所以要么将文件编码改为gbk
,要么在打开文件的地方设置encoding='utf-8'
。
我们在训练数据的时候,一般使用预训练权重,即yolov3.weights
,但是因为其是darket
版的权重文件,需要通过一定的方式转换成pytorch
版的;另外,除了最初的yolov3
模型,还有在此基础上改写的tiny模型和spp
模型。
这几个预训练权重文件(Darket
版)在weights
目录下的download_yolov3_weights.sh
文件中给出了说明
文件权重模型转换:
$ git clone https://github.com/ultralytics/yolov3 && cd yolov3
# convert darknet cfg/weights to pytorch model
$ python3 -c "from models import *; convert('cfg/yolov3-spp.cfg', 'weights/yolov3-spp.weights')"
Success: converted 'weights/yolov3-spp.weights' to 'weights/yolov3-spp.pt'
# convert cfg/pytorch model to darknet weights
$ python3 -c "from models import *; convert('cfg/yolov3-spp.cfg', 'weights/yolov3-spp.pt')"
Success: converted 'weights/yolov3-spp.pt' to 'weights/yolov3-spp.weights'
实际上就是使用models.py
脚本中的convert方法来转化,并且支持双向转换。不过需要搭配cfg
文件夹下的.cfg
文件来完成转换,会生成对应名称的.pt
文件,反之亦然。
train.py
这个yolov3.pt
文件在train.py
的main函数的weights
配置中使用,如果我们是使用命令行运行,那就在命令行中指定--weights
参数就好了,如果是在类似pycharm
的软件中直接执行,可以直接修改这个值,类似需要修改的还有--cfg, --data
等。
命令行运行示例:
python train.py --data data/coco.data --cfg cfg/yolov3.cfg --weights yolov3.pt
另外,按照我这里训练数据存放的形式,还需要在修改datasets.py
文件中的这个地方:
replace('images', 'labels')
– > replace('JPEGImages', 'labels')
(共两处)
因为这里在读取标签时,是直接将文件路径中的images替换为labels,后缀由jpg
改为txt
,而我并没有把图片放到指定的images文件下,所以这里需要改一下。
当一些必要的调整做完之后,如果不着急进度,我的建议是可以从train.py
开始逐步调试,搞清楚整个项目运行的流程,具体细节一开始不用搞的很清楚,只要大概知道哪些方法都是做什么用的就行了,而且,因为一些特殊原因,可能会遇到一些报错,基本一直调试下去,就可以把所有的错误都自己独立的解决掉。
比如上面遇到的编码问题、weights
参数报错(pt文件不存在)、修改配置文件不能加注释、改VOC
数据生成路径、修改datasets.py
等问题,均是在调试过程中发现并解决的。
上一次研究yolov3
一直不清楚yolov3.cfg
这个文件的作用,只是单纯的按照要求修改文件,这次研究了下,发现这个就是yolov3
模型的核心配置文件,就是从这个文件加载的神经网络模型,经过对比里面的内容发现与网络结构图一一对应:
[convolutional]
batch_normalize=1
filters=32
size=3
stride=1
pad=1
activation=leaky
# Downsample
[convolutional]
batch_normalize=1
filters=64
size=3
stride=2
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=32
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=64
size=3
stride=1
pad=1
activation=leaky
比如这里截取的部分,对应着Darknet53
最开始的地方。
相应的还有yolov3-spp.cfg
和yolov3-tiny.cfg
,对应着稍有不同的模型,使用的时候,要搭配yolov3-spp.weights
和yolov3-tiny.weights
使用,不过要先转换为.pt
文件才能通过pytorch
使用。
训练过程的输出(上面是输出的代码):
s = ('%10s' * 2 + '%10.3g' * 6) % ('%g/%g' % (epoch, epochs - 1), mem, *mloss, len(targets), img_size)
Epoch gpu_mem GIoU obj cls total targets img_size
11/299 0G 3.54 1.44 0 4.97 6 480: 100%|██████████| 16/16 [04:12<00:00, 15.77s/it]
Epoch:当前轮次
gpu_mem
:推测大概是GPU
占的内存吧,如果没有GPU
,那就是0
GIoU
:一种类似IoU
(交并比)的算法,使用其作为目标损失函数;
obj
:objectness(置信度);
cls
:classification
*/16表示共16个批次,当前训练到第几批,因为这里总共244张图片,batch_size=16,所以总共16个batch,最后一个batch的size只有4。
验证过程的输出:
Class Images Targets P R [email protected] F1: 100%|██████████| 3/3 [00:24<00:00, 8.18s/it]
all 48 48 0.911 0.917 0.892 0.914
Class
Images:表示有多少张图片
Targets:表示有多少个要检测的目标
P
:Precision准确率
R
:Recall召回率
mAP
:Mean Average Precision 的缩写,即均值平均精度。
作为 object dection
中衡量检测精度的指标。
计算公式为: 所有类别的平均精度求和除以所有类别。
F1
训练的过程中,会在weights文件夹下生成best.pt
和last.pt
两个权重文件,last.pt
随着训练轮次不断更新,best.pt
则保留截止当前训练过程中最好的权重。
测试使用detect.py
脚本,与train.py
类似,运行时通过很多命令行参数来配置,如果是直接在pycharm
中运行,那么直接修改相应的参数值就好了,比如这几个参数
parser.add_argument('--cfg', type=str, default='cfg/yolov3.cfg', help='*.cfg path')
parser.add_argument('--names', type=str, default='data/yidun.names', help='*.names path')
parser.add_argument('--weights', type=str, default='weights/best.pt', help='weights path')
parser.add_argument('--source', type=str, default='data/samples', help='source') # input file/folder, 0 for webcam
cfg
、names
这两个和train.py
保持一致,weights
这里改成我们训练过程中保存下来的最好的模型权重;
source
这里需要注意,这里默认的是读取data/samples
目录下的图片来测试,后面的注释还写了如果设置为0,他会测试视频;因为我上面说了我是直接把数据集都放在了data/VOCdevkit
下,samples里没有图片,如果要测试的话,我还需要把测试集的图片复制过去,所以我想是不是可以把这里的source值改成我的测试目录,所以就改成了data/VOCdevkit/2007_test.txt
(这个文件里保存了所有测试图片的路径,但是不是实际的测试图片)我也不确定这个source这样设置是否可行,所以我就去调试了,调试发现
webcam = source == '0' or source.startswith('rtsp') or source.startswith('http') or source.endswith('.txt')
if webcam:
view_img = True
torch.backends.cudnn.benchmark = True # set True to speed up constant image size inference
dataset = LoadStreams(source, img_size=imgsz)
else:
save_img = True
dataset = LoadImages(source, img_size=imgsz)
因为我这里source设置的值满足后缀是.txt
,所以webcam=True
,然后接下来走了视频测试的流程;
然后我就把webcam
主动设置为False,让程序去走图片检测的流程,最终发现source的值要求该路径下必须是实打实的图片,所以只好把测试图片全部放到samples
路径下,经过下面简单的代码移动就可以了:
with open(r'data/VOCdevkit/2007_test.txt', 'r', encoding='utf-8') as fr:
images = fr.readlines()
for i, image in enumerate(images):
with open(image.strip(), 'rb') as f:
image_content = f.read()
fw = open(f'data/samples/test{i+1}.jpg', 'wb')
fw.write(image_content)
fw.close()
预测完成之后,会在output文件夹下,生成检测结果——带有检测框的图片,直接打开查看框是否准确即可。
鉴于上次在keras版本的项目上踩了较多的坑,增长了很多经验,这次实践轻车熟路,而且使用更少的数据集,得到了更高的准确率,100张训练图片就得到了90%以上的准确率。
另外这篇博客补充了pytorch-YOLOv3项目更改anchor box的尺寸和数量需要做哪些调整,另外还根据个人经验,提出了一些关于文字点选验证码的一些解决思路,不涉及具体的实现方案。
还有这篇博客,介绍一些传统方式比如模板匹配来进行滑块缺口识别的技术。
我的gtihub博客地址:https://forchenxi.github.io/
另外,如果对投资理财感兴趣的同学,可以关注我的微信公众号:运气与实力。