查看原文
其他

【源头活水】YOLOX解读与感想



“问渠那得清如许,为有源头活水来”,通过前沿领域知识的学习,从其他研究领域得到启发,对研究问题的本质有更清晰的认识和理解,是自我提高的不竭源泉。为此,我们特别精选论文阅读笔记,开辟“源头活水”专栏,帮助你广泛而深入的阅读科研文献,敬请关注。

来源:知乎—Hanson
地址:https://zhuanlan.zhihu.com/p/395554928


01

前言

最近一段时间yolox在各公众号平台被刷屏,文章刚发出来的时候粗略的看了一样感觉并没有像公众号里面说的那么惊艳,后面认真读了一下论文和代码感觉还是没像公众号说的那样吊打一切yolo。不过yolox的代码我还是很喜欢的,层次结构很清楚,比yolo5的代码读起来要舒服不少。yolox主要提出解耦Head、anchor-free和SimOTA,下面会具体分析。    


02

数据对比
图1

图2

图3

从图1可以看出yolox各系列确实比yolo5精确要高出一点(也就是各公众号平台吊打一切yolo的数据来源),但是仔细看图2可以看出yolox的参数量和浮点计算量都比yolo5各系列高出一些,所以这样对比貌似不太公平的样子。再来看看图3,这个yolo5 github上的试验结果,可以看出最新的p6系列精度有了较大的提升,(开始我以以yolo5m6与yolox-m对比,这个地方不严谨,经评论区里面提醒这是在640输入下的flops,1280下flops应该是差不多4倍。期待yolox的P6结构出来)


03

Decoupled Head

解耦Head这个比较常见,比如RetinaNet和SSD等都有两个分支(分类和回归),主要认为分类的特征与回归的特征不太一样,分类更细腻一些回归特征则更多是一些轮廓边界特征。如果放在一起反向传播的时候可能会导致网络收敛速度慢,精度降低。我以前在用yolo做人脸检测的时候尝试过把检测跟分类分两个分支,收敛速度确实加快了,在fddb的精度也提高了一点点,但是我怀疑这个精度提升是我split的后面又加了几个卷积有关,毕竟计算量增加了。

图4:解耦头


04

Anchor-free

yolox把原来的yolo的anchor-base框架改成了anchor-free框架,这种改进其实很早就有论文提出了,FASF在anchor-base的基础上添加anchor-free的分支,anchor-base与anchor-free联合训练与inference,如图5所示。yolox相当于放弃了anchor-base的部分仅用anchor-free部分,如果加上anchor-base的部分联合训练我相信精度还会再提升一些,哪怕inference的时候把anchor-base部分去掉,相当于anchor-base部分当成一个辅助loss部分,这样精度因该也会提升一些,至少误检会少一些。我在人脸检测的时候通常会加一个人脸关键点回归的分支来辅助检测,这样检测结果通常会稍好一点。

图5:FASF


05

SimOTA

我觉得这篇文章还是这一部分有含金量一些,当然这一部分是在他自家OTA基础上做优化,省掉Sinkhorn-Knopp迭代求解过程,减小训练时间。说起SimOTA 必须要了解OTA,我又去把这篇文章认真读了一遍。OTA解决的是anchor assignment,通常我们在分配正负样本的时候是根据anchor与gt的iou来进行分配,作为认为目标在不同大小、形状和遮挡条件下正负样本的分界面应该也不一样,同时正负样本的分配也需要考虑上下文信息,所以把anchor assignment当成一个线性规划问题中的Optimal Transport问题来处理,核心思想就是建立一个代价矩阵,假设我有M个gt与N个anchor,那么代价矩阵的大小就是    ,矩阵中的每个元素就是该gt与anchor的代价(一般是loss值),loss越大则说明选取这对gt和anchor的代价越大,op的目的是去选取gt与anchor的匹配对,使得总体代价最小。这里有篇外文博客介绍OP问题挺详细的,如果有疑惑可以参考:

https://michielstock.github.io/posts/2017/2017-11-5-OptimalTransport/

OTA这篇文章建议大家还是认真读一下写的还是很详细的,但是我数学底子不好还是有一些疑惑点。SimOTA 是OTA的简化版本,省去Sinkhorn-Knopp迭代求解最优匹配对的过程,直接用 dynamic top-k stratrgy的方式选取匹配对。接下来对照代码分析一下dynamic top-k stratrgy过程。
1. 为每个grid分配正样本,参考函数get_in_boxes_info()。判断grid在不在gt-box内部,如果在则为正样本fg_mask,同时判断grid在不在gt-box中心点2.5个像素点区域内(论文是3个像素点),如果在同时满足则用is_in_boxes_and_center 表示,这一步是一个筛选的过程选更靠近gt的grid为正样本。
fg_mask, is_in_boxes_and_center = self.get_in_boxes_info( gt_bboxes_per_image, expanded_strides, x_shifts, y_shifts, total_num_anchors, num_gt, )
2. 计算代价矩阵
cost = ( pair_wise_cls_loss + 3.0 * pair_wise_ious_loss + 100000.0 * (~is_in_boxes_and_center) )
pair_wise_cls_loss和pair_wise_ious_loss是通过fg_mask选取的gird与gt的分类和iou loss,
可以看到cost 里面有100000.0 * (~is_in_boxes_and_center)的计算,可知如果grid在不在is_in_boxes_and_center 里面的代价是非常大的。

3. dynamic_k_matching
dynamic_k_matching的选取过程代码写的很详细,主要选取topk_ious的索引(最多n_candidatek个,默认为10),根据这些索引在代价矩阵中找到代价最小的top_dynamic_k个grid作为正样本,top_dynamic_k的计算过程可以参考代码。
def dynamic_k_matching(self, cost, pair_wise_ious, gt_classes, num_gt, fg_mask): # Dynamic K # --------------------------------------------------------------- matching_matrix = torch.zeros_like(cost)
ious_in_boxes_matrix = pair_wise_ious n_candidate_k = 10 topk_ious, _ = torch.topk(ious_in_boxes_matrix, n_candidate_k, dim=1) dynamic_ks = torch.clamp(topk_ious.sum(1).int(), min=1) for gt_idx in range(num_gt): _, pos_idx = torch.topk( cost[gt_idx], k=dynamic_ks[gt_idx].item(), largest=False ) matching_matrix[gt_idx][pos_idx] = 1.0
del topk_ious, dynamic_ks, pos_idx
anchor_matching_gt = matching_matrix.sum(0) a = anchor_matching_gt.sum() aa = cost[:, anchor_matching_gt == 1] if (anchor_matching_gt > 1).sum() > 0: cost_min, cost_argmin = torch.min(cost[:, anchor_matching_gt > 1], dim=0) matching_matrix[:, anchor_matching_gt > 1] *= 0.0 matching_matrix[cost_argmin, anchor_matching_gt > 1] = 1.0 fg_mask_inboxes = matching_matrix.sum(0) > 0.0 num_fg = fg_mask_inboxes.sum().item()
fg_mask[fg_mask.clone()] = fg_mask_inboxes
matched_gt_inds = matching_matrix[:, fg_mask_inboxes].argmax(0) gt_matched_classes = gt_classes[matched_gt_inds]
pred_ious_this_matching = (matching_matrix * pair_wise_ious).sum(0)[ fg_mask_inboxes ] return num_fg, gt_matched_classes, pred_ious_this_matching, matched_gt_inds

06

总结
YOLOX十一篇实践性很强的论文,整个代码看起来让人很舒服还有配套的部署,代码工程意强,但是创新点比较少。Decoupled Head+Anchor-free部分完全可以利用FASF框架,就像我上面提到的加上anchor-base的部分联合训练,inference阶段把anchor-base部分去掉,这样精度应该也会提升一些,至少误检会少一些。dynamic_k_matching的分配策略效果挺好的能提2.3个点,但是如果做进一步对比实验,使用FASF的的分配策略作对比如图6所示,或则其它的一些anchor-free的Sample Assignment策略做对比,会更加有说服力一点。最后旷世在目标检测领域的成果还是蛮多的,只是现在有的公众号标题取得真是太夸张了。

图6:FASF Sample Assignment

关注目标检测的小伙伴可以关注我的github,我会持续更新目标检测相关文章。
https://github.com/Hanson0910/DL-Algorithm-Summary

本文目的在于学术交流,并不代表本公众号赞同其观点或对其内容真实性负责,版权归原作者所有,如有侵权请告知删除。


“源头活水”历史文章


更多源头活水专栏文章,

请点击文章底部“阅读原文”查看



分享、在看,给个三连击呗!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存