点击下方卡片,关注“CVer”公众号
AI/CV重磅干货,第一时间送达
作者:yangxue | 已授权转载(源:知乎)
https://zhuanlan.zhihu.com/p/372357305
一、前言
先给文章和代码:
主页:https://yangxue0827.github.io/
代码:https://arxiv.org/abs/2101.11952
代码链接(已开源):
https://github.com/yangxue0827/RotationDetection
有段时间我一直在想如何让旋转检测也能用上通用检测那一套IoU系列的损失函数。由于我不确定旋转的IoU计算是不是真的不可导不能直接作为损失函数,我还特意在知乎上提了一个问题,希望有知乎大佬能给个比较有说服力的解释和证明。
两个任意旋转框的IoU计算过程可导吗?
https://www.zhihu.com/question/402342622
之所以想用IoU loss,是因为我觉得IoU loss在旋转检测可能比通用检测的作用更大,它不仅可以解决损失(loss)和评估(metric)的不一致问题,还可以解决由于角度周期性等引起的回归边界问题。虽然我在SCRDet中给了一种比较巧妙的损失形式,但是这里面还是有几个问题的。一是损失函数的梯度方向依然是由smooth l1损失主导的,二是在边界处计算ground truth和预测框的时候需要额外判断这个预测框是不是超出了当前框的定义,如果是则需要把预测框转成和ground truth相同的表示形式(这个判断和转换的功能其实OpenCV自带的函数已经有了,所以我们只要愉快地调包就行)。但目前的结论是旋转IoU应该是不可导的,但是这不意味着工程师无法实现,具体可以看下面一片文章和相关的实现:
论文:https://arxiv.org/abs/1908.03851
代码:github.com/lilanxiao/Rotated_IoU
但这毕竟是工程方面的手段,学术上还是要想办法找到近似的方法。想归想,但是实在想不出能让人眼前一亮的方法,这期间就去做其他的idea了,比如CSL。当CSL中了ECCV的时候,我在ECCV官方的相关论文推荐以及提醒我有新引用的邮件上发现了两篇做近似IoU loss的文章。
第一篇是中ECCV20的PIoU Loss
ECCV 2020 | PIoU Loss:面向旋转目标检测的损失函数(已开源)
PIoU Loss的想法比较简单,就是统计两个任意旋转框之间的共有像素个数来近似IoU:
文章有一个很大的问题就是baseline太低了,文章最终报的性能才60出头,但是在这个领域做DOTA数据集的baseline不给65以上都不好意思了,所以有理由质疑这个方法是否有效了。
第二篇是做3D检测的,也中的ECCV20,给了一种近似的可导的IoU方法:
http://www.ecva.net/papers/eccv_2020/papers_ECCV/papers/123650460.pdf
近似的方式如下:
大致做法就是:每次让一个框不动,另一个框旋转到和不动框垂直,然后计算IoU,最终取最小值。由于仿射变换过程是可导的,两个垂直框计算IoU也是有办法可导的,所以整个过程就是可导的。我当时觉得这个做法还是比较有趣的,但是还是不够惊艳。
看完这两篇让我产生了重拾做近似IoU loss的想法。
二、灵感的诞生
回到我们的这篇论文,这篇文章中“将任意旋转矩形近似成一个二维的高斯分布,通过计算分布之间的Wasserstein距离解决RIoU不可导的问题”的相似是非常具有脑洞的,具体转换过程就是下面这张图:
但是,最初的想法并不是我想出来的,而是我的一个同学在看完SCRDet之后和我交流时提到的。当时虽然被惊艳到,但是框到分布之间怎么转换、分布之间Wasserstein距离怎么计算、的分布之间的计算是否可导仍未可知。然后通过查找资料以及咨询实验室数学比较好的同学,得到以下几个初步的结果。
任意旋转矩形框和二维高斯分布的转换关系:
两个高斯分布之间的Wasserstein距离公式:
很明显,两个高斯分布之间的Wasserstein距离是可导的,具体的推导过程可以参考下面这篇博客:
https://djalil.chafai.net/blog/2010/04/30/wasserstein-distance-between-two-gaussians/
万事具备,就要准备初步的实验了,毕竟实验是否成功才是最重要的。
三、初步的实践
由于我以前没有用过tensorflow中关于矩阵计算的操作,所以不清楚有没有具体的算子。于是乎,我让同学帮我用matlab仿真了一下Wasserstein距离公式,让他发我化简之后的公式。当我拿到结果的时候我是傻了眼的,化简结果整整好几页的,但总算是可导吧。
https://github.com/yangxue0827/RotationDetection/blob/main/utils/gaussian_wasserstein_distance.py
可能是公式太长了,每次tensorflow启动都要半天,真的要命。代码其实是比较简单的,但再简单的代码我也很少能一次写对,经过几次单步调试解决几个低级错误之后终于跑起来了。没开心到一分钟,loss直接nan了。然后的然后就是各种调参数还是持续nan,加上要准备CVPR了,所以暂时搁置了这个想法,去做了CSL的优化方法DCL。初步实验虽然以失败告终,但好在把大体的框架打通了,我还会卷土重来的。
四、再次实验
在准备CVPR论文期间,我知道了tensorflow是有矩阵操作的算子的,但是pytorch好像并不怎么友好,缺少矩阵开方的算子,需要使用第三方实现的。
有了官方的矩阵算子,Wasserstein距离公式的代码也就是几行代码的事了,启动速度也很快,方便调试。CVPR投稿结束之后,我就重新开始了实验,很快我就发现了一个bug,就是我把协方差的开方当协方差在用了,难怪一直nan。另外,我发现两个任意框位置从重合逐渐分离时,Wasserstein距离值会越来越大。举个例子,在两个框重叠面积较大的时候,他们的Wasserstein距离值都在100以内,之后就爆发式增长,甚至一下好几千,如下图蓝线所示:
这就使得我们不能直接使用Wasserstein距离作为损失函数,需要做变换控制值域,让它在大误差(重叠率较小)情况下不要这么敏感。我最早的变化是这样子的:
这是仿照IoU loss的形式(1-IoU),引进了两个超参数,我一开始默认使用 , ,代码总算是不nan了。
以65.73%性能的旋转RetinaNet为baseline,GWD在DOTA的结果只有64%出头,虽然没有体现有效性,至少跑出结果了。需要吐槽的是,这个回归损失的训练收敛非常慢,下面就是和Smooth L1 loss的损失训练收敛对比图:
这个图还算好一些,在DOTA数据集上,Wasserstein距离损失(GWD)的收敛曲线就是上图前半截(近似水平),悠哉悠哉地下降,一直到训练结束,整个过程loss也就下降了零点几。而且训练前半部分都没有学习到角度信息,预测出来的框都是水平的。
接下来的任务就是调优性能了,我在比较IoU loss和GWD曲线的时候,意外得发现当两个框接近重合的时候,GWD的值出现了负数(是因为我忘记在log里+1了)。这表示之前训练时高度重合的结果并没有作为模型优化的最终目的,反而让模型远离预测这种结果。这下性能不高的原因找到了,经过截断和+1等处理之后,模型很快学习到了角度信息,但是整个loss曲线还是接近水平,收敛依然缓慢。最后的结果是好的,性能提高到了67.87%,高了2.14%,这已经挺不错了(后来经过调整参数后又提高到了68.93%)。原以为后面就到此为止了,文章以脑洞为主要买点,有一些“高大上”的数学公式可以点缀,然后各种消融实验做一下,SOTA刷一下,就可以写论文投稿了。没想到的是,惊喜却在后面。
五、方法的分析
我们最初的目的就是像构造一个近似的旋转IoU损失来解决损失和评估不一致的问题(SLP),但在一次意外经历之后发现问题并没有我想的这么简单。
首先我常用的旋转矩形的定义方式有两种,下图左边是OpenCV采用的方式(但是最新版本的好像吧角度范围改了),记为 ,右边是长边定义发,记为 :
这两种定义法是可以相互转换的,转换关系如下:
这两种定义法各有各的缺点,比如 在边界处有角度周期性(PoA)和边的交换性(EoE)两种问题,而 有角度周期性(PoA)和类正方形检测问题(SLP)。这些问题我都总结在下图中了:
我们的目的是想让模型可以按照way1走通。这里有些小伙伴可能会有疑惑,way1走不通不是有way2吗,反正模型能有回归到正确位置的方式就行了。我们来看下面这张图,在非边界位置的时候模型是按下图底部方式回归的,而在边界位置就不会这么回归了,这说明存在边界位置和非边界位置的回归方式的不一致。由于非边界位置出现的概率非常大,因此模型在边界处时“经验”告诉它应该通过way1训练,但是计算出来的Loss又告诉它要用way2,这使得模型在边界处训练的非常“左右摇摆不定”。加之way2也比较复杂,因此边界处的预测往往比较不准,尤其是使用 定义法。
在之前的工作中,我们提出了各种针对不同问题的方法,但是在一次意外测试的时候我们发现了GWD几个重要的性质。我们模拟了不同边界框定义下PoA、EoE、SLP的情况,发现这些情况下GWD依然可以计算出准确的IoU。
这意味着GWD可能对这些问题是免疫的。因此就有了以下三个性质的提炼:
这三个性质分别对应解决 的PoA和EoE、 的PoA、 的SLP问题。其实我们发现根据性质1, 和 在GWD下是等价的。这下好了,我们就不用考虑选用什么边界框的定义方式、以及乱七八糟问题的困扰,GWD可以说是全免疫了。回过头来再分析,这些好处都归功于旋转框转换成高斯分布这一步骤,正式因为高斯分布的 才有了这些性质。至于后续使用Wasserstein距离来作为回归损失都是水到渠成的操作了。
六、实验结果
我们首先做了GWD里两个超参数的实验,最终使用 和 的组合。
当然我们后来发现直接使用 的形式会更好。
不同的是此时 ,原因是使用 依然会产生非常大的损失值。
随后我们在不同数数据集和检测器上进行了验证。
我们发现GWD更NB的地方在于高精度的检测,它在高精度指标上碾压Smooth L1 Loss。
我们还对比了IoU-Smooth L1 Loss, Modulated loss, CSL, 和DCL在DOTA-v1.0上的比较,GWD都基本是完胜的。
最后在华为的8*V100机器上把SOTA刷了一下。
七、后记
DOTA-v1.0目前Task1已经被刷到80+了,个人认为已经差不多到头了。想要接着做的话可以着眼于提高高精度指标( 等),或者专注于小目标检测(DOTA-v1.5和DOTA-v2.0)。最近把十几种方法在DOTA三个版本的Task1上跑了一个baseline结果,方便相关的小伙伴比较使用。代码、权重和结果就放在我开源的RotationDetection上。
论文PDF和代码下载
后台回复:GWD,即可下载上述论文和代码
后台回复:CVPR2021,即可下载CVPR 2021论文和代码开源的论文合集
后台回复:Transformer综述,即可下载最新的两篇Transformer综述PDF
重磅!旋转目标检测交流群成立
扫码添加CVer助手,可申请加入CVer-旋转目标检测 微信交流群,方向已涵盖:目标检测、图像分割、目标跟踪、人脸检测&识别、OCR、姿态估计、超分辨率、SLAM、医疗影像、Re-ID、GAN、NAS、深度估计、自动驾驶、强化学习、车道线检测、模型剪枝&压缩、去噪、去雾、去雨、风格迁移、遥感图像、行为识别、视频理解、图像融合、图像检索、论文投稿&交流、PyTorch和TensorFlow等群。
一定要备注:研究方向+地点+学校/公司+昵称(如旋转目标检测+上海+上交+卡卡),根据格式备注,可更快被通过且邀请进群
▲长按加小助手微信,进交流群
▲点击上方卡片,关注CVer公众号
整理不易,请给CVer点赞和在看