赞
踩
本文为YOLOv5旋转目标检测踩坑纪录篇之一——项目篇:
略略略:YOLOv5_DOTAv1.5(遥感旋转目标检测,全踩坑记录)339 赞同 · 240 评论文章编辑
文章开头直接放上我自己的项目代码:
https://github.com/hukaixuan19970627/YOLOv5_DOTA_OBBgithub.com/hukaixuan19970627/YOLOv5_DOTA_OBB
star⭐还请多多益善。
前言:
其实这篇文章算比较过时的了,因为已经有人实现了YOLOv5旋转目标的检测:
junjieliang:【旋转目标检测】修改YOLOv5旋转目标检测103 赞同 · 212 评论文章编辑
我连YOLOv5源码还没看完,就有人实现了,速度真快,不过这个项目在应用到DOTA数据集上的时候会有诸多的坑,像数据维度这种问题可能会稍微多一点,不过这都是小问题,花几天看看源码结构,就知道应该修改哪里。
另外junjieliang作者发布的项目漏检情况比较严重,甚至两个相距比较远的大目标也会被同时漏检,但是YOLOv5进行DOTA水平目标检测却不会出现漏检情况(至少间距大的目标不会漏),那说明肯定是改建的地方哪里出了错,发现问题之后我开始检查问题。
从后向前排查,首先直接从NMS函数入手(因为旋转IOU计算函数一定是新添加的,可能会出问题),在删除NMS之后,只进行置信度阈值筛选,直接可视化网络的预测结果,发现被漏检的目标已经被大量的框标记了,说明至少网络模型的召回率是没问题的,新添加的NMS函数有问题。于是我自己写了个poly_NMS函数进行替换,果然漏检的问题解决了。修改完NMS后的检测结果如下:
junjieliang作者改建的项目训练150epoch的检测结果
。。。召回率已经可以保证了,但是边框回归的位置也有比较多的问题,而且光靠调节NMS置信度阈值或是IOU阈值都无法解决。那就只剩两个办法了:
因为改建的作者把可视化代码都删了嘛,所以我还是得自己写,再加上第二个办法也得同步进行,那我不如自己重新在YOLOv5源码的基础上去改代码算了,就算之后出问题也好解决。(本人比较磨蹭,改了一个月)
本人从头开始修改的YOLOv5项目训练150epoch的检测结果
效果极其可观,但是改建的过程坑太多,简直苦不堪言,因此我的这一系列文章还是有意义的。(就当诉苦了)
废话到此为止,正文开始。
(使用自己数据集之前麻烦先下载demofiles把demo都跑一遍,demo不报错,说明环境没问题;如果自己数据集评估的时候你觉得结果莫名其妙,那么麻烦先检查自制数据集是否和demofiles结构一致、数据格式一致;
另外遇到问题麻烦自己先检查一下,先分析一下可能哪里存在问题,比如自制数据集与DOTA的区别在哪儿,尤其是改动了代码的各位大哥大姐麻烦直接把改了哪里说出来,节省双方讨论时间)
$ pip install -r requirements.txt
2. 安装swig
- $ cd \.....\yolov5_DOTA_OBB\utils
- $ sudo apt-get install swig
3. 为了方便Python调用c++写的一个库(poly_iou),请做以下操作:(推荐Linux系统)
- $ swig -c++ -python polyiou.i
- $ python setup.py build_ext --inplace
DOTA数据集必须经过切割后才能输入网络进行训练,至于原因和切割代码请参考我的另一篇文章,里面有详细的代码原理分析和使用方法,以及DOTA数据集的介绍:
略略略:DOTA遥感数据集以及相关工具DOTA_devkit的整理(踩坑记录)153 赞同 · 103 评论文章编辑
https://github.com/hukaixuan19970627/DOTA_devkit_YOLOgithub.com/hukaixuan19970627/DOTA_devkit_YOLO
对原始数据集进行切割后,获得新的图像数据集和YOLO格式注释文件,DOTA采用的是任意四边形四点坐标进行标注,我选取的旋转矩形框的表示方法为长边表示法,其形式为:
- $ classid x_c y_c longside shortside Θ Θ∈[0, 180)
-
-
- * longside: 旋转矩形框的最长边
-
- * shortside: 与最长边对应的另一边
-
- * Θ: x轴顺时针旋转遇到最长边所经过的角度
转换后的YOLO长边表示法图示
注意:切割数据集时,切割的size大小要满足:height = width; 另不要更改切割后的图像名称,其中包含了图像切割时的位置信息,这对最后的merge检测结果、评估十分重要。
当然如果你的旋转目标数据集并不需要切割,可以只参考项目代码中”DOTA标签格式转YOLO长边表示法标签格式“的部分:
略略略:DOTA数据格式转YOLO数据格式工具(cv2.minAreaRect踩坑记录):85 赞同 · 78 评论文章编辑
Yolov5预训练模型(提取码都是6666):yolov5x.pt. yolov5l.pt. yolov5m.pt. yolov5s.pt.
你也可以接着我训练后的权重文件继续训练:YOLOv5_DOTAv1.5_OBB.pt.
--------------------------------------------------------------------------------------------
2021.03.29更新
权重文件改了下,由FL32转为FL16,仅40M
--------------------------------------------------------------------------------------------
训练方法与yolov5完全一致,包括单卡训练,跨卡同步训练等等,Θ部分的结构在代码中我已经写死了,无需更改。
权重文件路径不填,填入模型配置文件路径,填入数据集信息配置文件
更改模型配置文件中的nc为你自己所使用的数据集的类别数量 (建议结构配置文件选择yolov5m或更小的yolov5s,性能完全够用)
更改数据集信息配置文件 (验证集有没有都可以,原始代码中的评估部分已删除,因为另外写了个评估程序)
-----------------------------------------------------------------------------------------
2021.05.18
发现好多人总说按照默认配置跑有问题,loss有问题,检测有问题,然后问了一大堆也没问出什么,最后直接让他把觉得有问题的图和训练生成的图片全发过来,发现他改为了单目标识别(......你管这这叫什么都没改?(ˉ▽ˉ;).........)
这里也强调一下吧,因为至少有十个人来报bug都是因为改为了单类别识别:yolov5本身的损失函数设计就不适合识别模型,也就是nc设为1的话,loss会异常,比如分类loss一直为0,其他损失函数有时不降反升等等。原因是因为yolo系列的head部分由分类分支(不包含背景类,通道数为num_classes)+回归分支+置信度分支组成,其它检测器的head部分由分类分支(包含背景类,因此通道数为num_classes+1)+回归分支组成,所以yolo依靠置信度分支来判断是否为前景,nc>1的话则分类分支就多余了。
解决办法:
2. 如果不想从头开始训练,节省训练时间,推荐你采用预训练模型(实验证明预训练方案在最终精度相近的前提下训练时间更短):
删除或注释本段冻结代码(本段代码会冻结backbone层所有参数)
填入权重文件路径,模型配置文件路径不用填,填入数据集信息配置文件
更改数据集信息配置文件 (验证集有没有都可以,原始代码中的评估部分已删除,因为另外写了个评估程序)
当预训练模型中的类别信息与数据集信息配置文件 DOTA_ROTATED.ymal 不一致时,模型会将 DOTA_ROTATED.ymal 中的类别信息自动重载进模型中。
3. 更改epoch、batch_size、img_size、device参数(根据实验设备显卡条件自行选择)
如果是单卡训练:
$ python train.py --batch-size 4 --device 0
如果是多卡训练,运行时默认进入 DataParallel 模式(不推荐,该方法运行速度太慢):
$ python train.py --batch-size 16 --device 0,1,2,3
建议多卡训练采用 DistributedDataParallel 模式(单张GPU batchsize<8效果最好):
python -m torch.distributed.launch --nproc_per_node 4 train.py --sync-bn
文件开始训练后,代码会直接可视化batch数据进入网络训练之前的数据增强结果:
train_batch0(batch_size=4)
train_batch1(batch_size=4)
train_batch2(batch_size=4)
可视化的作用:确保进入网络前的img和labels数据正常。
友情提示:开始正式训练之前,先训练一个小的数据集看训练过程是否会出现问题。
demo files主要是为了方便直接运行两个demo(detect.py和evaluation.py)快速上手,你也可以替换成自己的数据。
添加权重文件路径、待检测图片集文件夹路径、检测结果输出路径
待检测图片集文件夹结构: (由于遥感图像比较大,所以训练时要切割,预测前也要进行切割,再将预测结果合并)
|_images:
----------|_P0003__1__0___0.png
----------|_P0003__1__123___0.png
----------|_P0004__1__0___0.png
----------|_ ...
----------|_P0019__1__5549___2703.png
2. 运行"detect.py"。获取可视化检测结果和检测结果文本文件。
$ python detect.py
merge前的遥感图像检测结果1
merge前的遥感图像检测结果2
输出的detection文件夹结构如下:
|_detection:
----------|_result_txt:
----------------------|_result_before_merge:
-----------------------------------------------|_P0003.txt
-----------------------------------------------|_.....txt
----------|_P0003__1__0___0.png
----------|_P0003__1__123___0.png
----------|_ ...
----------|_可视化的检测结果?.png
输出的检测结果文本文件中的数据格式: 【目标所属图片 置信度 poly坐标 类别】
不想查看可视化检测结果,可以手动注释掉#191行代码
评估所需文件结构如下:
|_detection: (detect.py输出的文件夹)
----------|_result_txt:
----------------------|_result_before_merge:
-----------------------------------------------|_P0003.txt
-----------------------------------------------|_.....txt
|_row_DOTA_labels: (新增文件夹:未分割的待检测图片的真实GT,格式为DOTA格式)
---------------------|_P0003.txt
---------------------|_.....txt
|_row_images: (新增文件夹:未分割的待检测图片)
---------------|_P0003.png
---------------|_.....txt
2. 更改成你自己的文件夹路径:
classnames_inVal为待检测图片集中的类别
3. 运行程序。获取评估结果并可视化merge后的检测结果
$ python evaluation.py
程序会自动生成评估所需的各种文件,并最后调用可视化函数展现检测结果。(若不需可视化,可以注释掉Draw_DOTA_Image函数)
- 评估程序会输出模型在demo_files上的评估结果
- ...
- npos num: 118
- ap: 0.879517382469394
- map: 0.7317064576976655
- classaps: [67.64370381 87.46635739 49.62078363 87.95173825]
merge后的检测结果可视化展现
常见问题:
Q1 我的数据集不需要切割,如何使用evaluation.py评估检测器性能?
A1 第一种办法(瞒天过海):让代码以为你的图片已经经过了切割,这样就能正常使用evaluation了,更改待检测的图片名,比如“P0003.png”改为“P0003__1__0___0”,之后正常调用detect.py与evaluation.py即可。(自己写代码改名字或者使用前面的DOTA_devkit_YOLO工具将原始图像切成同样的size都行)
第二种办法:改代码,把merge操作去掉就行,代码中有注释,改起来很快的,等我改的话可能要几个月之后了。
Q2 性能如何?超参如何组合性能比较好呢?
A2 超参太多我自己也没有排列组合试过,不过根据我踩坑的经验,默认的算是中等性能了,乱改可能会严重掉点,具体性能与实验对比可以参考这篇文章最底部的表格,偶尔有时间了会更新在这个上面:
略略略:YOLOv5_DOTAv1.5(遥感/无人机旋转目标检测,全踩坑记录)339 赞同 · 240 评论文章编辑
Q3 多类别数据集训练效果不错,但是单类别训练效果很差而且部分loss数据异常
A3 原始yolov5就不支持单目标识别,所以基于这个项目更改的旋转检测自然也不支持;yolov5的每个anchor输出的通道为[ x, y, w, h, conf_score, [num_classes]], 如果是单类别识别,只需要输出通道[x, y, w, h, conf_score]即可,因此原始项目是不适用于单类别目标识别任务的。解决方法:修改检测层输出通道和对应损失函数部分/直接把nc改为2,后者不用改代码,就相当于是一个缺失了一个类别的二分类目标识别任务。(感觉两种方法性能上应该没有区别,不过一切以实验结果为准)
Q4 没有在线验证评估功能?results结果中部分数据为空。
A4 由于原始的test程序不适合评估旋转目标,因此删除了训练时的验证部分,所以有些人会疑惑为什么生成的result.txt文件中后面的数据大部分没有或是0,因为result.txt每行的内容为:
- [
- epoch/epochs-1 , mem , loss_obx , loss_obj , loss_cls ,loss_angle ,
- total_loss , targets_num , imgs.shape, precision , recall , mAP@.5 ,
- mAP@.5-.95, val_loss_box, val_loss_obj, val_loss_cls, val_loss_angle
- ]
因为没有在线评估,所以后面8个数据内容都为空或0是正常现象(之所以保留是因为我以后可能要把在线评估加进去,但是目前没什么时间改),旋转目标的评估程序可以参考evaluation.py。
2022.03.12
项目已大更新,本篇博客的教程只适合老版的项目,最新的教程都会放在readmd.md里,安装环境请参考install.md,使用教程请参考getstart.md。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。