YOLOv5_DOTA无人机/遥感旋转目标检测项目代码(数据集制作、模型训练、性能评估、常见问题)

本文详细记录了作者在将YOLOv5应用于遥感旋转目标检测时遇到的问题,如NMS函数的调试、数据维度调整、漏检和边框回归问题。通过逐步排查和代码修改,作者分享了从零开始改造YOLOv5以适应DOTA数据集的方法和注意事项。
摘要由CSDN通过智能技术生成

本文为YOLOv5旋转目标检测踩坑纪录篇之一——项目篇:

略略略:YOLOv5_DOTAv1.5(遥感旋转目标检测,全踩坑记录)339 赞同 · 240 评论文章​编辑

文章开头直接放上我自己的项目代码:

https://github.com/hukaixuan19970627/YOLOv5_DOTA_OBB​github.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阈值都无法解决。那就只剩两个办法了:

  • 先查看图片进行增强后,进入网络训练前的可视化结果,看是否有问题;
  • 查看代码,尤其是bbox、objectness损失函数这一块。

因为改建的作者把可视化代码都删了嘛,所以我还是得自己写,再加上第二个办法也得同步进行,那我不如自己重新在YOLOv5源码的基础上去改代码算了,就算之后出问题也好解决。(本人比较磨蹭,改了一个月)

本人从头开始修改的YOLOv5项目训练150epoch的检测结果

效果极其可观,但是改建的过程坑太多,简直苦不堪言,因此我的这一系列文章还是有意义的。(就当诉苦了)

废话到此为止,正文开始。


一、 相关运行环境准备

(如果demo运行都报错或有BUG,球球各位哥哥姐姐先检查一下环境版本吧!!!!)

(使用自己数据集之前麻烦先下载demofiles把demo都跑一遍,demo不报错,说明环境没问题;如果自己数据集评估的时候你觉得结果莫名其妙,那么麻烦先检查自制数据集是否和demofiles结构一致、数据格式一致;

另外遇到问题麻烦自己先检查一下,先分析一下可能哪里存在问题,比如自制数据集与DOTA的区别在哪儿,尤其是改动了代码的各位大哥大姐麻烦直接把改了哪里说出来,节省双方讨论时间)

  1. 建议Python 3.8环境下进行以下安装
$   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_YOLO​github.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 评论文章​编辑


三、 开始训练(train.py)

Yolov5预训练模型(提取码都是6666):yolov5x.ptyolov5l.ptyolov5m.ptyolov5s.pt.

你也可以接着我训练后的权重文件继续训练:YOLOv5_DOTAv1.5_OBB.pt.

--------------------------------------------------------------------------------------------

2021.03.29更新

权重文件改了下,由FL32转为FL16,仅40M

--------------------------------------------------------------------------------------------

训练方法与yolov5完全一致,包括单卡训练,跨卡同步训练等等,Θ部分的结构在代码中我已经写死了,无需更改。

  1. 如果不进行预训练,从头开始训练模型的话:

权重文件路径不填,填入模型配置文件路径,填入数据集信息配置文件

更改模型配置文件中的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的话则分类分支就多余了。

解决办法:

  1. 修改代码使其支持单类别的识别,把分类分支去掉,依靠置信度分支来判断是否为目标即可;
  2. 想让yolov5实现单目标识别,nc改为2就可以,这样子损失函数全部计算正常,自制数据集无需改动也无需标注另一个类别,当然也不用担心会识别出第二个类别,因为数据集两个类别完全失衡(另一个类别的gt数量为0),网络为了收益高会偏向于预测第一个类别,此法最简单有效。

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数据正常。

友情提示:开始正式训练之前,先训练一个小的数据集看训练过程是否会出现问题。


四、 测试结果(detect.py)

  1. 下载demo files.(提取码6666)

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行代码


五、 评估模型性能(evaluation.py)

  1. 准备好demofiles。(同第四步detect.py)

评估所需文件结构如下:

|_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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FL1768317420

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值