YOLO系列正负样本分配策略

1、YOLOv3

使用MaxIoUAssigner策略来给gt分配样本,基本上保证每个gt都有唯一的anchor对应,匹配的原则是该anchor与gt的IOU最大且大于FG_THRESH,这种分配制度会导致正样本比较少,cls和bbox分支训练起来可能比较慢。在剩余的anchor中,如果有anchor跟所有gt的IOU都小于BG_THRESH,则将此类anchor设为负样本,如果有anchor跟所有gt的IOU大于BG_THRESH且小于FG_THRESH,则忽视掉此类anchor。

下面以Towards-Realtime-MOT/utils/utils.py中的代码为例:

def build_targets_thres(target, anchor_wh, nA, nC, nGh, nGw):
    ID_THRESH = 0.5
    FG_THRESH = 0.5
    BG_THRESH = 0.4
    nB = len(target)  # number of images in batch
    assert(len(anchor_wh)==nA)

    tbox = torch.zeros(nB, nA, nGh, nGw, 4).cuda()  # batch size, anchors, grid size
    tconf = torch.LongTensor(nB, nA, nGh, nGw).fill_(0).cuda()
    tid = torch.LongTensor(nB, nA, nGh, nGw, 1).fill_(-1).cuda() 
    for b in range(nB):
        t = target[b]
        t_id = t[:, 1].clone().long().cuda()
        t = t[:,[0,2,3,4,5]]
        nTb = len(t)  # number of targets
        if nTb == 0:
            continue

        gxy, gwh = t[: , 1:3].clone() , t[:, 3:5].clone()
        gxy[:, 0] = gxy[:, 0] * nGw
        gxy[:, 1] = gxy[:, 1] * nGh
        gwh[:, 0] = gwh[:, 0] * nGw
        gwh[:, 1] = gwh[:, 1] * nGh
        gxy[:, 0] = torch.clamp(gxy[:, 0], min=0, max=nGw -1)
        gxy[:, 1] = torch.clamp(gxy[:, 1], min=0, max=nGh -1)

        gt_boxes = torch.cat([gxy, gwh], dim=1)
        
        anchor_mesh = generate_anchor(nGh, nGw, anchor_wh)
        anchor_list = anchor_mesh.permute(0,2,3,1).contiguous().view(-1, 4)
        iou_pdist = bbox_iou(anchor_list, gt_boxes)
        iou_max, max_gt_index = torch.max(iou_pdist, dim=1)  ## 取出每个pre与gt的IOU最大值

        iou_map = iou_max.view(nA, nGh, nGw)       
        gt_index_map = max_gt_index.view(nA, nGh, nGw)
        
        id_index = iou_map > ID_THRESH
        fg_index = iou_map > FG_THRESH  ## 若IOU大于FG_THRESH,则为foreground
        bg_index = iou_map < BG_THRESH  ## 若IOU小于BG_THRESH,则为background
        ign_index = (iou_map < FG_THRESH) * (iou_map > BG_THRESH)  ## 若IOU大于BG_THRESH并小于FG_THRESH,则ignore
        tconf[b][fg_index] = 1
        tconf[b][bg_index] = 0
        tconf[b][ign_index] = -1

        gt_index = gt_index_map[fg_index]
        gt_box_list = gt_boxes[gt_index]
        gt_id_list = t_id[gt_index_map[id_index]]
        if torch.sum(fg_index) > 0:
            tid[b][id_index] =  gt_id_list.unsqueeze(1)
            fg_anchor_list = anchor_list.view(nA, nGh, nGw, 4)[fg_index] 
            delta_target = encode_delta(gt_box_list, fg_anchor_list)
            tbox[b][fg_index] = delta_target
    return tconf, tbox, tid

2、YOLOv4

yolov4为了增加正样本,采用multi anchor策略,只要大于IoU阈值的anchor,都视为正样本

3、YOLOv5

确定gt是否匹配当前特征图的anchors

因为yolov5是多尺度预测,所以首先需要确定gt应该跟哪个尺度的特征图上的anchor进行匹配。规则为:gt的宽高分别与当前尺度下的anchor的宽高进行比较,如果它们的比例在[1/4,4]之间,则当前gt可以与当前尺度下的anchor进行匹配。

下面以yolov5/utils/loss.py代码为例:

# wh ratio
r = t[..., 4:6] / anchors[:, None]

# compare
j = torch.max(r, 1 / r).max(2)[0] < self.hyp['anchor_t']

# filter
t = t[j]

将与gt中心点邻近的两个点也作为正样本点(即总共有3个正样本点) 

将gt所在的中心点视作一个ceil,并将该ceil划分成4个象限,如果gt的中心点位于该ceil中的第四象限,则将该ceil右边的单元格以及下边的单元格也视为正样本点(如下图的黄色单元格)

anchors, shape = self.anchors[i], p[i].shape
gain[2:6] = torch.tensor(shape)[[3, 2, 3, 2]]  ## 取当前尺度特征图的w、h,如果原图尺寸为[640,640],下采样8倍后,特征图的尺寸就变成[80,80],则gain[2:6]=[80,80,80,80]

t = targets * gain  ## 将[0,1]之间的坐标映射到[0,80]上
g = 0.5  ## 偏移量,用于判断gt的x、y坐标在单元格的哪个象限

if nt:
    ## 求gt的宽高与anchor的宽高的比值
    r = t[..., 4:6] / anchors[:, None]
    ## 判断比值是否在[1/4,4]这个范围内
    j = torch.max(r, 1 / r).max(2)[0] < self.hyp['anchor_t']
    ## 挑选符合条件的gt
    t = t[j]  

    gxy = t[:, 2:4]  ## gt的x、y坐标
    gxi = gain[[2, 3]] - gxy  ## gt的x、y坐标到w、h的距离

    ## 由于gxy+gxi=[80,80],因此j和l互斥,k和m互斥
    j, k = ((gxy % 1 < g) & (gxy > 1)).T
    l, m = ((gxi % 1 < g) & (gxi > 1)).T
    j = torch.stack((torch.ones_like(j), j, k, l, m))

    ## 将t复制成5份(gt中心点所在单元格加上该单元格的左、上、右、下的单元格)
    t = t.repeat((5, 1, 1))[j]
    offsets = (torch.zeros_like(gxy)[None] + off[:, None])[j]

4、YOLOX

yolox的正负样本分配策略的代码可以参考:YOLOX中的SimOTA_Cassiel_cx的博客-CSDN博客

这里简单记录下SimOTA(simple optimal transport assignment)的步骤:

  1. 确定候选正样本计算anchor_box的中心点,若anchor_box的中心点落在gt内或者在以gt的中心点为圆心,以center_radius为半径的圆内,就将该anchor视为候选正样本。如下图所示,红色框为gt,绿色框为蓝点预测的anchor_box,假设该anchor_box的中心点为绿点,由于绿点在红框内,因此该anchor_box便作为候选正样本
  2. 计算候选正样本跟gt之间的cls loss和iou loss并以一定比例加权求和,作为cost,计算公式如下(如果anchor_box的中心点不在gt内,该anchor_box与gt的cost就会很大,对应代码中的100000.0 * (~is_in_boxes_and_center)
    cost = (
               pair_wise_cls_loss
               + 3.0 * pair_wise_ious_loss
               + 100000.0 * (~is_in_boxes_and_center)
           )
  3. 对候选正样本与gt之间的iou进行大小排序,挑选前10个(可自己调整)最大iou,对这些iou求和并取整,该数值作为当前gt的dynamic_k
  4. 对于该gt,取前dynamic_k个最小的cost的候选正样本,作为正样本
  5. 如果存在一个正样本匹配多个gt的情况,则选cost较小的gt来匹配(如,某anchor_box与gt_1和gt_2的cost分别为0.8和0.2,则取消该anchor_box与gt_1的匹配,只匹配gt_2

5、YOLOv7

yolov7的正负样本分配策略为yolov5和yolox的结合体。首先使用yolov5的策略去挑选候选正样本,再使用yolox中的SimOTA策略去从候选正样本中挑选正样本。

【参考文章】

目标检测正负样本区分策略和平衡策略总结(一) - 知乎

yolov7正负样本分配详解 - 知乎 

深入浅出Yolo系列之Yolox核心基础完整讲解 - 知乎

猜你喜欢

转载自blog.csdn.net/qq_38964360/article/details/131460053