YOLOX: Exceeding YOLO Series in 2021
文章目录
参考
- 1、【论文笔记】FPN —— 特征金字塔
- 2、EMA - 指数移动平均
- 3、多标签分类与BCEloss
- 4、PyTorch余弦学习率衰减
- 5、YOLOv4的Tricks解读一 — 多图融合的数据增强(MixUp/CutMix/Mosaic)
- 6、Anchor-based与Anchor-free检测方法的优劣势
- 7、最优传输之浅谈
- 8、原论文
- 9、代码
- 10、指数移动平均函数 EMA 的介绍
- 11、BCE Loss
- 12、YOLOX深度解析(二)-simOTA详解
综述
- YOLOX是基于YOLOv3,结合了一些经过验证有效的技术而形成的高性能的检测器,旷世发的,是一篇偏向工程的文章
- 原论文:https://arxiv.org/abs/2107.08430
- 代码:https://github.com/Megvii-BaseDetection/YOLOX
结构
Baseline
-
backbone采用DarkNet53,为什么不用CSP-DarkNet53这种结构而采用DarkNet53呢?按作者的原话是:考虑到YOLOv4/YOLOv5对于anchor-based pipeline过度优化,而YOLO X采用的是anchor-free的方法,所以以YOLO v3为基础。
-
后面再加一个SPP结构
-
Loss部分
-
用BCE Loss训练cls&obj分支
-
关于BCE Loss 可以参考11、BCE Loss
-
BCE Loss又叫Binary Cross Entropy Loss,用于二分类,而对于YOLOX里面的BCE Loss,我想应该是BCEWithLogitsLoss
-
于是我特地查了下源代码
-
-
-
-
用IoU Loss训练回归分支
-
-
权重更新用的是EMA
-
关于EMA可以看下这篇(虽然是股市扫盲~):10、指数移动平均函数 EMA 的介绍
-
S[0] = Y[0] S[i] = Y[i] * alpha + S[i-1] * (1 - alpha) 其中 alpha 为平滑因子,数值越小曲线越平滑
-
Decoupled head
-
在YOLO v3中,一个检测头需要同时输出cls、reg、obj,这样的耦合性比较强。然鹅,分类与回归问题之间存在冲突,用一个检测头直接输出分类与回归效果不如将两个问题解耦分开了处理来的好
-
如上图的下半部分所示,在得到FPN输出的Feature Map之后,将其一分为二,用两组参数分别解决Cls和Reg+IoU问题,按作者的话来说,可以提高收敛速度 (原来耦合的时候,可能会发生cls低但reg高的情况或者反过来,这样loss就会僵住下不去)
-
Strong data augmentation
- Mosaic:YOLOv4的创新点,是CutMix的拓展,混合了四张具有不同语义信息的图片,可以让检测器检测出超乎常规语境的目标,增强鲁棒性,并减少对大的mini-batch的依赖。
- MixUp:MixUp就是将两张图片采用比例混合,label也按照比例混合。
- 论文:mixup: BEYOND EMPIRICAL RISK MINIMIZATION
arxiv:https://arxiv.org/pdf/1710.09412.pdf
- 论文:mixup: BEYOND EMPIRICAL RISK MINIMIZATION
Anchor-free
- Anchor-free的工作近两年挺多人做的,这是因为Anchor-base的方法有不少问题:
- 1、对于YOLOv3(v4、v5)来说,在训练之前需要聚类(k-means)来获得几组最佳大小的anchor,而聚类用的就是train的数据集,这就导致model在用于train数据集上表现更好,而泛化能力就下降了(指其他dataset下anchor大小(聚类)不一样的情况)
- 2、anchor-base类方法增加了检测头的复杂性,以及每个图像的预测数量
- YOLO实现Anchor-free(YOLOv1就是Anchor-free的但那时候没有FPN和Multi positives,因此YOLOv1对重叠、小目标的效果不好):
- 将每个位置的预测从3个减少到1个(指一个点预测一个anchor),并使它们直接预测4个值,即网格左上角的两个偏移量,以及预测框的高度和宽度。
- 将每个物体的中心位置指定为阳性样本,并预先定义一个刻度范围。以指定每个物体的FPN级别
Multi positives
- 防止同一个中心点导致的漏检问题,因此使用3x3替代原来的1个点,将这九个点都认为是正样本,即从上述策略每个gt有1个正样本增长到9个正样本
SimOTA
-
优点:
- 1、simOTA能够做到自动的分析每个gt要拥有多少个正样本。
- 2、能自动决定每个gt要从哪个特征图来检测。
- 3、相比较OTA,simOTA运算速度更快。
- 4、相比较OTA,避免额外超参数。
-
gt的gi 和预测的pj之间的cost cij为:
-
c i j = L i j c l s + λ L i j r e g c_{ij} = L_{ij}^{cls}+\lambda L_{ij}^{reg} cij=Lijcls+λLijreg
-
其源代码如下:
-
# cost源码位于:YOLOX/yolox/models/yolo_head.py 499行 cost = ( pair_wise_cls_loss # 这里的lambda固定为3.0,可见这里的cost总体来说重视Reg(或者是IoU) + 3.0 * pair_wise_ious_loss # 这里表示当这个框不在这个gt周围,is_in_boxes_and_center表示不在的时候去0,~去反就是1,然后加很大的权重就保证这个框一定不会被选到 + 100000.0 * (~is_in_boxes_and_center) )
-
-
然后,对于gt的gi,选择固定中心区域内成本最小的前k个预测作为其正样本
-
# dynamic_k_matching选出成本最小的k个样本作为其正样本 ( num_fg, gt_matched_classes, pred_ious_this_matching, matched_gt_inds, ) = self.dynamic_k_matching(cost, pair_wise_ious, gt_classes, num_gt, fg_mask)
-
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 = min(10, ious_in_boxes_matrix.size(1)) 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): # 用torch.topk选出topk个cost最小的框,然后将matching_matrix相应的值设置为1 _, 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 # .sum(0)表示求列表和,0表示从0开始 # 由于matching_matrix是个二维矩阵,因此anchor_matching_gt是一维,表示对每个gt,match的预测框的个数 anchor_matching_gt = matching_matrix.sum(0) # 如果有匹配到的(tensor可以直接数组比值,参考下图),将其分配给cost最小的那个gt,然后置0,只留匹配的到的 if (anchor_matching_gt > 1).sum() > 0: _, 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 # 这里是过滤了一遍的每个gt匹配到的anchor数,这就是上面说的:自动的分析每个gt要拥有多少个正样本 fg_mask_inboxes = matching_matrix.sum(0) > 0.0 # num_fg是匹配到的总数 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
-
我试了一下,list不能比但是tensor可以这么操作
-
-
效果
-
-
其他具体的再仔细看看代码再说吧