黄金时代 —— 深度学习 (目标检测)

发布时间:2024-01-25 10:00

文章目录

  • `0 指标 & 技巧`
  • IoU
    • 交并比
    • 代码
  • mAP / mmAP
    • AP计算
    • mAP / mmAP
    • AP计算代码
    • COCO上的12个度量指标
  • NMS及其变种
    • NMS(贪心算法)
      • 代码
    • Soft NMS 2017
      • 代码
    • ConvNMS 2015
    • Pure NMS Network 2017
    • Softer NMS 2018
      • 代码
    • Fitness NMS 2017
  • `1 深度网络发展历程`
  • `2 Two-Stage Network`
  • R-CNN 2014
    • *对深度网络和检测任务的说明
    • R-CNN方案简化复杂度的思路
    • 流程
    • 说明
    • Selective Search
    • R-CNN分类器为什么不采用Softmax
    • BBox回归方式
    • BBox回归为什么不直接对坐标进行回归
    • 为什么当 Region Proposals 和 Ground Truth 较接近时, 即 IoU 较大时, 可以认为是边框回归函数是线性变换?
    • R-CNN缺点
  • Fast R-CNN 2015
    • 有哪些改进
    • ROI Pooling
      • 前向传播
      • 反向传播
    • 为什么 RoI Pooling 比 SPP 效果好
    • Multi-task Loss
    • Smooth L1 相比于 L2 损失, 在回归时有什么优势
    • SVD 奇异值分解简介
  • Faster R-CNN 2016
    • RPN网络
    • 正负样本标点策略
    • 损失函数
    • 共享参数训练
    • 如果两个物体重合度很高, Faster R-CNN 会怎么样?
  • Mask R-CNN 2017
    • 简介
    • ROI Align
      • 思路
      • ROI Align 的BP过程
    • Mask分支的实现
    • 总结
  • FCN 2014
    • 全卷积结构
    • 跳级结构
    • 反卷积
  • R-FCN 2016
    • 动机
    • Position-sensitive Score Map
    • R-FCN缺点
  • CoupleNet 2017
    • 动机
    • 简介
    • 结构
    • 如何结合 局部特征 和 全局特征
    • CoupleNet 为什么可以解决遮挡类问题
  • Cascade R-CNN 2017
    • 简介
  • `3 One-Stage Network`
    • 说明
  • YOLOv1 2015
    • 简介
    • 优化目标
    • 损失函数
    • 优点
    • 缺点
  • YOLOv2 & YOLO9000 2016
    • 改进
    • 训练
      • 损失函数
    • Stronger:YOLO 9000
  • SSD 2016
    • 简介
    • 多尺度
    • 为什么设置不同的Anchor
    • 如何选择anchor的scale和Aspect ratio?
    • 数据增强
    • 为什么SSD不直接使用浅层的特征图谱, 而非要额外增加卷积层, 这样不是增加模型的复杂度了吗?
    • 缺点
    • 代码
  • DSSD 2017
  • YOLOv3 2018
    • 改进
    • 数据增强
    • 网络结构
    • 网络输出
    • 边框回归
    • 网络训练
      • train_bottleneck.py
      • yolo_loss.py
      • 训练流程
  • RefineDet 2018
    • Transfer Connection Block
    • RefineDet 使用two-stage 的边框回归过程, 为什么还说它是 one-stage 模型?
  • YOLOv4 2020
  • `4 Scale Problem`
  • OverFeat 2014
  • SPPNet 2015
    • 空间金字塔池化
    • SPPNet缺点
  • FPN 2017
    • 介绍
    • FPN的使用
  • PANet 2018
    • Adaptive Feature Pooling
    • Fully-connected Fusion
  • M2Det 2018
    • 动机
    • 结构
  • RFBNet 2018
    • RFB
  • TridentNet 2019
    • 创新点
    • Trident Block
    • 网络结构
  • `5 形变卷积`
  • Deformable ConvNets V1 2017
    • Deformable Convolution
    • Deformable RoI Pooling
    • Deformable Ps RoI Pooling
  • Deformable ConvNets V2 2018
    • 动机
    • 改进
    • 细节 [链接](https://blog.csdn.net/u014380165/article/details/88072737)
  • `6 样本困难和不平衡`
  • OHEM 2016
  • RetinaNet 2017
    • Focal Loss
      • Focal Loss中的参数作用
    • R-CNN 系列为什么不用 Focal Loss 进行优化
    • 损失函数
    • 网络结构
  • GHM 2019
    • 梯度密度公式
    • GHM-C 损失函数
    • GHM-R 损失函数
  • Generalized Focal Loss 2020
    • 问题
    • 提出的方法
      • 联合
      • 替换
    • GFL
    • 总结
    • 作者的闲谈
  • `7 Anchor-free Network`
  • DenseBox 2015
    • 主要贡献
    • 网络结构
    • 结合关键点的多任务模型
  • CornerNet 2018
    • 简介
    • 损失函数(3部分)
      • Corner Points 损失
      • Offsets 损失 (角点偏置损失)
      • Grouping Centers 损失
    • 网络结构
    • Coner相对于Bbox的优势
  • CornerNet-Lite 2019
    • 动机
    • 改进
      • CornerNet-Saccade
      • CornerNet-Squeeze
    • CornerNet-Squeeze-Saccade
  • FSAF 2019
    • 简介
    • Groud-truch and Loss
      • Classification Output:
      • Box Regression Output:
      • Inference
    • Online Feature Selection
  • FoveaBox 2019
    • FoveaBox 的 Scale Assignment
    • FoveaBox 与其他模型的比较
  • FCOS 2019
    • 损失函数
    • 目标重叠问题
    • Center-ness
    • FCOS 与其他模型的比较
  • ExtremeNet 2019
    • 分组策略
    • Ghost box suppression(分组存在的问题)
    • Edge aggregation
    • Extreme points 与 Corner points 的区别
  • CenterNet(Triplets) 2019
    • 简介
    • 损失函数
    • Enriching Center and Corner Information
      • Center Pooling
      • Cascade Corner Pooling
  • CenterNet(Objects as Points) 2019
    • 简介
    • 损失函数 (3部分)
      • 中心点正负分类损失:
      • 中心点偏置(offset)补偿损失:
      • 中心点对应物体尺寸的损失:
    • CenterNet(Points) 与其他 One-Stage 方法的区别
    • CenterNet(Points) 的缺点
  • ATSS 2019
  • `8 NAS 网络结构搜索优化`
  • NASNet 2016
  • EfficientNet 2019
  • NAS-FPN 2019
  • Detnas 2019
  • `Mess`
  • DetNet 2018
  • STDNet 2018
  • RFBNet 2018
  • Relation-Network 2018
  • PFPNet 2018
  • MetaAnchor 2018
  • Pelee 2018
  • SNIPER 2018
  • Non-local Neural Networks 2018
  • Bag of Freebies 2019

0 指标 & 技巧

IoU

交并比

x 1 = max ⁡ ( b o x 1 x 1 , b o x 2 x 1 ) , y 1 = max ⁡ ( b o x 1 y 1 , b o x 2 y 1 ) x 2 = min ⁡ ( b o x 1 x 2 , b o x 2 x 2 ) , y 2 = min ⁡ ( b o x 1 y 2 , b o x 2 y 2 ) i n t e r s e c t i o n = ( x 2 − x 1 + 1 ) × ( y 2 − y 1 + 1 ) I o U = i n t e r s e c t i o n a r e a 1 + a r e a 2 − i n t e r s e c t i o n x 1=\max \left(b o x 1_{x 1}, b o x 2_{x 1}\right), y 1=\max \left(b o x 1_{y 1}, b o x 2_{y 1}\right) \\ x 2=\min \left(b o x 1_{x 2}, b o x 2_{x 2}\right), y 2=\min \left(b o x 1_{y 2}, b o x 2_{y 2}\right)\\ intersection=(x 2-x 1+1) \times(y 2-y 1+1)\\ I o U=\frac{intersection}{area_1+area_2-intersection} x1=max(box1x1,box2x1),y1=max(box1y1,box2y1)x2=min(box1x2,box2x2),y2=min(box1y2,box2y2)intersection=(x2x1+1)×(y2y1+1)IoU=area1+area2intersectionintersection

代码

  • numpy
import numpy as np
def get_inter_matrix(boxes_pd, boxes_gt):
    A = boxes_pd.shape[0]
    B = boxes_gt.shape[0]
    lt_xy = np.maximum( # left top
            np.repeat(np.expand_dims(boxes_pd[:, :2], axis=1), B, axis=1),
            np.repeat(np.expand_dims(boxes_gt[:, :2], axis=0), A, axis=0)
            )
    rb_xy = np.minimum( # right bottom
            np.repeat(np.expand_dims(boxes_pd[:, 2:], axis=1), B, axis=1),
            np.repeat(np.expand_dims(boxes_gt[:, 2:], axis=0), A, axis=0)
            )
    inter = np.clip(rb_xy - lt_xy, a_min=0, a_max=None) # (A, B, 2): (h, w)
    return inter[:, :, 0] * inter[:, :, 1] # return (A, B), inter_matrix
def get_iou_matrix(boxes_pd, boxes_gt):
    """
    Args:
        box_pd: (tensor) Prior boxes from priorbox layers, Shape: [num_priors,4]
        box_gt: (tensor) Ground truth bounding boxes, Shape: [num_objects,4]
    Return:
        jaccard overlap: (tensor) Shape: [box_a.size(0), box_b.size(0)]
    """
    A = boxes_pd.shape[0]
    B = boxes_gt.shape[0]
    area_pd = (boxes_pd[:, 3]-boxes_pd[:, 1]) * (boxes_pd[:, 2]-boxes_pd[:, 0]) # h * w
    area_gt = (boxes_gt[:, 3]-boxes_gt[:, 1]) * (boxes_gt[:, 2]-boxes_gt[:, 0]) # h * w
    inter_matrix = get_inter_matrix(boxes_pd, boxes_gt)
    return inter_matrix / (area_gt + area_pd - inter_matrix) # (A, B), iou_matrix
  • Pytorch
import torch
def intersect(box_a, box_b):
    """ We resize both tensors to [A,B,2] without new malloc:
    [A,2] -> [A,1,2] -> [A,B,2]
    [B,2] -> [1,B,2] -> [A,B,2]
    Then we compute the area of intersect between box_a and box_b.
    Args:
      box_a: (tensor) bounding boxes, Shape: [A,4].
      box_b: (tensor) bounding boxes, Shape: [B,4].
    Return:
      (tensor) intersection area, Shape: [A,B].
    """
    A = box_a.size(0)
    B = box_b.size(0)
    max_xy = torch.min(box_a[:, 2:].unsqueeze(1).expand(A, B, 2),
                       box_b[:, 2:].unsqueeze(0).expand(A, B, 2))
    # print(box_a[:, :2])
    # print(box_a[:, :2].unsqueeze(1))
    # print(box_a[:, :2].unsqueeze(1).expand(A, B, 2))
    min_xy = torch.max(box_a[:, :2].unsqueeze(1).expand(A, B, 2),
                       box_b[:, :2].unsqueeze(0).expand(A, B, 2))
    inter = torch.clamp((max_xy - min_xy), min=0)
    return inter[:, :, 0] * inter[:, :, 1]


def jaccard(box_a, box_b):
    """Compute the jaccard overlap of two sets of boxes.  The jaccard overlap
    is simply the intersection over union of two boxes.  Here we operate on
    ground truth boxes and default boxes.
    E.g.:
        A ∩ B / A ∪ B = A ∩ B / (area(A) + area(B) - A ∩ B)
    Args:
        box_a: (tensor) Ground truth bounding boxes, Shape: [num_objects,4]
        box_b: (tensor) Prior boxes from priorbox layers, Shape: [num_priors,4]
    Return:
        jaccard overlap: (tensor) Shape: [box_a.size(0), box_b.size(0)]
    """
    inter = intersect(box_a, box_b)
    area_a = ((box_a[:, 2]-box_a[:, 0]) *
              (box_a[:, 3]-box_a[:, 1])).unsqueeze(1).expand_as(inter)  # [A,B]
    area_b = ((box_b[:, 2]-box_b[:, 0]) *
              (box_b[:, 3]-box_b[:, 1])).unsqueeze(0).expand_as(inter)  # [A,B]
    union = area_a + area_b - inter
    return inter / union  # [A,B]

box_a = torch.Tensor([[1,2,3,4],[2,3,4,5]])
box_b = torch.Tensor([[1,2,3,4],[2,3,4,5],[4,5,6,7]])
iou = jaccard(box_a, box_b)
print(iou)

mAP / mmAP

  • 准确率 Acc
    A c c = T P + T N F P + F N + T P + T N A c c=\frac{T P+T N}{F P+F N+T P+T N} Acc=FP+FN+TP+TNTP+TN
  • 精确度 Pre
    P r e = T P F P + T P P r e=\frac{T P}{F P+T P} Pre=FP+TPTP
  • 召回率 Recall
    R e c a l l = T P T P + F N Recall=\frac{T P}{T P+F N} Recall=TP+FNTP
  • 目标检测中没有正确的负样本这一说,所以一般 A c c = P r e Acc = Pre Acc=Pre
  • PR曲线的AUC指标 ∑ k = 1 N max ⁡ k ′ ≥ k P ( k ′ ) Δ r ( k ) \sum_{k=1}^{N} \max _{k^{\prime} \geq k} P\left(k^{\prime}\right) \Delta r(k) k=1NmaxkkP(k)Δr(k)计算复杂,所以使用AP(Average Precision)近似

AP计算

  • 注意,AP是一个和IoU阈值和类别相关函数。设IoU阈值为0.5,表示可以选择所有和A类物体的GT-BBox的IoU大于0.5的候选框BBox用于计算该A类物体的PR曲线!
  • 例子,设定IoU为0.5。对于某一类物体的检测,网络给了10个候选框,按照每个框的置信度从高到低排序:
    黄金时代 —— 深度学习 (目标检测)_第1张图片
    P = # T r u e s − u p p e r − t h r e s # A l l − u p p e r − t h r e s R = # T r u e s − u p p e r − t h r e s # A l l − T r u e s \begin{aligned} P &=\frac{\#Trues-upper-thres}{\#All-upper-thres} \\ R &=\frac{\#Trues-upper-thres}{\# All-Trues} \end{aligned} PR=#Allupperthres#Truesupperthres=#AllTrues#Truesupperthres
  • PR曲线
    黄金时代 —— 深度学习 (目标检测)_第2张图片
  • 计算方法:
  • ① voc2010之前,选取 Recall >= 0, 0.1, …, 1 的11处Percision最值的均值
    黄金时代 —— 深度学习 (目标检测)_第3张图片
  • 2010之后,绘制出平滑后的PR曲线后,用积分的方式计算平滑曲线下方的面积作为最终的AP值。
    黄金时代 —— 深度学习 (目标检测)_第4张图片

mAP / mmAP

  • mAP:mean Average Precision 指的是分别求每个类别的 AP, 然后取其平均值. AP对于类别的均值,是IoU阈值的函数
  • mmAP:给定一组IOU阈值,在每个IOU阈值下面,求所有类别的AP,并将其平均起来,作为这个IOU阈值下的检测性能,称为mAP(比如mAP@0.5就表示IOU阈值为0.5时的mAP);最后,将所有IOU阈值下的mAP进行平均,就得到了最终的性能评价指标。AP对于类别和IoU阈值的均值

AP计算代码

  • 1 计算Precisions和Recalls
# input:
# - score: 样本为正样本的概率
# - label: 样本的真实标签

import numpy as np

def get_pre_rec(scores, labels):
    sort_index = np.argsort(scores)[::-1]
    scores = scores[sort_index]
    labels = labels[sort_index]

    y_preds = scores >= 0.5

    tps  = np.cumsum((y_preds == True) & (labels == True))
    fps  = np.cumsum((y_preds == True) & (labels == False))
    tns  = np.cumsum((y_preds == False) & (labels == False))
    fns  = np.cumsum((y_preds == False) & (labels == True))

    print("tps:", tps)
    print("fps:", fps)
    print("tns:", tns)
    print("fns:", fns)
    pres = tps / (tps + fps)
    recs = tps / (tps[-1]+fns[-1])
    return pres, recs

scores = np.array([0.9, 0.89, 0.8, 0.78, 0.7, 0.7, 0.7, 0.67, 0.62, 0.58, 0.4, 0.3])
labels = np.array([1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1])
pres, recs = get_pre_rec(scores, labels)
print("pres:", pres)
print("recs:", recs)
  • 计算方法1 - 11个点
import numpy as np
def average_precision(precision, recall):
    if pre is None or rec is None:
        return np.nan
    psum = 0
    for t in np.arange(0., 1.1, 0.1):
        if np.sum(recall >= t) == 0:
            break # 如果后面已经不存在大于阈值的 recall 了, 则可跳出
        else: # 找到大于阈值的所有对应精度中, 最大的那个
            psum += np.max(np.nan_to_num(precision)[recall >= t])
    return psum / 11.0 # 除以采样点个数(11个)
  • 计算方法2 - 平滑计算
import numpy as np
def average_precision_rec(pre, rec):
    pre = np.nan_to_num(pre)
    # pre = np.concatenate([[0], pre, [0]]) # 添加哨兵元素
    pre = np.concatenate([[1], pre, [0]]) # 添加哨兵元素
    rec = np.concatenate([[0], rec, [1]]) # 添加哨兵元素

    n = len(pre)
    for i in range(n-1, 0, -1): # 将pre置为后面最大的
        pre[i-1] = max(pre[i-1], pre[i]) # 对于每一个阈值, 我们选取大于该 recal 阈值的最大精度作为积分精度

    i = np.where(rec[1:] != rec[:-1])[0] # i = 0 ~ n-2, 找到 rec 变化的点
    print((rec[i+1] - rec[i]), pre[i+1]) # 输出相乘元素, 方便检查
    return np.sum((rec[i+1] - rec[i]) * pre[i+1])

COCO上的12个度量指标

  • AR是对每张图片在给定一定数量的检测个数下最大的召回率,取所有类别和IoU下的均值。
    黄金时代 —— 深度学习 (目标检测)_第5张图片
  • COCO 介绍

NMS及其变种

NMS(贪心算法)

  • 1 将所有框的得分排序,选中最高分及其对应的框;
  • 2 遍历其余的框,如果和当前最高分框的重叠面积 (IOU) 大于一定阈值,我们就将框删除;
  • 3 从未处理的框中继续选一个得分最高的,重复上述过程。
  • 问题:如果两个物体重叠较大,就会漏检一个

代码

import cv2
import numpy as np

def nms(bounding_boxes, confidence_score, threshold):
    if len(bounding_boxes) == 0:
        return [], []
    bboxes = np.array(bounding_boxes)
    score = np.array(confidence_score)

    # 计算 n 个候选框的面积大小
    x1 = bboxes[:, 0]
    y1 = bboxes[:, 1]
    x2 = bboxes[:, 2]
    y2 = bboxes[:, 3]
    areas =(x2 - x1 + 1) * (y2 - y1 + 1)

    # 对置信度进行排序, 获取排序后的下标序号, argsort 默认从小到大排序
    order = np.argsort(score)

    picked_boxes = [] # 返回值
    picked_score = [] # 返回值
    while order.size > 0:
        # 将当前置信度最大的框加入返回值列表中
        index = order[-1]
        picked_boxes.append(bounding_boxes[index])
        picked_score.append(confidence_score[index])

        # 获取当前置信度最大的候选框与其他任意候选框的相交面积
        x11 = np.maximum(x1[index], x1[order[:-1]])
        y11 = np.maximum(y1[index], y1[order[:-1]])
        x22 = np.minimum(x2[index], x2[order[:-1]])
        y22 = np.minimum(y2[index], y2[order[:-1]])
        w = np.maximum(0.0, x22 - x11 + 1) # 这一步千万不要忘了, 因为在求 iou 时, 如果不相交, 则会出现负值
        h = np.maximum(0.0, y22 - y11 + 1)
        intersection = w * h

        # 利用相交的面积和两个框自身的面积计算框的交并比, 将交并比大于阈值的框删除
        ratio = intersection / (areas[index] + areas[order[:-1]] - intersection)
        left = np.where(ratio < threshold) # 当 left 进行计算时, 如 left+1, 才使用 [0], 详见求 AP 的代码
        order = order[left]

    return picked_boxes, picked_score
#include 
#include 
#include 

struct Bbox {
    int x1;
    int y1;
    int x2;
    int y2;
    float score;
    Bbox(int x1_, int y1_, int x2_, int y2_, float s):
	x1(x1_), y1(y1_), x2(x2_), y2(y2_), score(s) {};
};

float iou(Bbox box1, Bbox box2) {
    float area1 = (box1.x2 - box1.x1 + 1) * (box1.y2 - box1.y1 + 1);
    float area2 = (box2.x2 - box2.x1 + 1) * (box2.y2 - box2.y1 + 1);
    int x11 = std::max(box1.x1, box2.x1);
    int y11 = std::max(box1.y1, box2.y1);
    int x22 = std::min(box1.x2, box2.x2);
    int y22 = std::min(box1.y2, box2.y2);
    float intersection = (x22 - x11 + 1) * (y22 - y11 + 1);
    return intersection / (area1 + area2 - intersection);
}

std::vector<Bbox> nms(std::vector<Bbox> &vecBbox, float threshold) {
    auto cmpScore = [](Bbox box1, Bbox box2) {
	return box1.score < box2.score; // 升序排列, 令score最大的box在vector末端
    };
    std::sort(vecBbox.begin(), vecBbox.end(), cmpScore);

    std::vector<Bbox> pickedBbox;
    while (vecBbox.size() > 0) {
        pickedBbox.emplace_back(vecBbox.back());
        vecBbox.pop_back();
        for (size_t i = 0; i < vecBbox.size(); i++) {
            if (iou(pickedBbox.back(), vecBbox[i]) >= threshold) {
                vecBbox.erase(vecBbox.begin() + i);
            }
        }
    }
    return pickedBbox;
}

Soft NMS 2017

  • 将重合率高的得分减少,而不是直接为0(即删掉)
  • 有线性加权和高斯加权
    s i = { s i ,  iou  ( M , b i ) < N t s i ( 1 − iou ⁡ ( M , b i ) ) ,  iou  ( M , b i ) s_{i}=\left\{\begin{array}{ll} s_{i}, & \text { iou }\left(\mathcal{M}, b_{i}\right)si={si,si(1iou(M,bi)), iou (M,bi)<Nt iou (M,bi)
    s i = s i e − i o u ( M , b i ) 2 σ , ∀ b i ∉ D s_{i}=s_{i} e^{-\frac{i o u\left(\mathcal{M}, b_{i}\right)^{2}}{\sigma}}, \forall b_{i} \notin \mathcal{D} si=sieσiou(M,bi)2,bi/D
  • 流程:
    黄金时代 —— 深度学习 (目标检测)_第6张图片
  • Soft NMS:将重叠bbox加权抑制,减少得分。但还是会设置一个阈值,当得分小于这个阈值(如0.001)的时候,把这个框删掉。
  • 优缺点:
优点:
1、Soft-NMS可以很方便地引入到object detection算法中,不需要重新训练原有的模型、代码容易实现,不增加计算量(计算量相比整个object detection算法可忽略)。并且很容易集成到目前所有使用NMS的目标检测算法。
2、soft-NMS在训练中采用传统的NMS方法,仅在推断代码中实现soft-NMS。作者应该做过对比试验,在训练过程中采用soft-NMS没有显著提高。
3、NMS是Soft-NMS特殊形式,当得分重置函数采用二值化函数时,Soft-NMS和NMS是相同的。soft-NMS算法是一种更加通用的非最大抑制算法。
缺点:
soft-NMS也是一种贪心算法,并不能保证找到全局最优的检测框分数重置。除了以上这两种分数重置函数,我们也可以考虑开发其他包含更多参数的分数重置函数,比如Gompertz函数等。但是它们在完成分数重置的过程中增加了额外的参数。

代码

import numpy as np

def soft_nms(bboxes, scores, iou_thresh=0.3, sigma2=0.5, score_thresh=0.001, method=2):
    # 在 bboxes 之后添加对于的下标[0, 1, 2...], 最终 bboxes 的 shape 为 [n, 5], 前四个为坐标, 后一个为下标
    N = bboxes.shape[0] # 总的 box 的数量
    indexes = np.array([np.arange(N)])  # 下标: 0, 1, 2, ..., n-1
    bboxes = np.concatenate((bboxes, indexes.T), axis=1) # concatenate 之后, bboxes 的操作不会对外部变量产生影响

    # 计算每个 box 的面积
    x1 = bboxes[:, 0]
    y1 = bboxes[:, 1]
    x2 = bboxes[:, 2]
    y2 = bboxes[:, 3]
    areas = (x2 - x1 + 1) * (y2 - y1 + 1)

    # for i in range(N):
    for i in range(N-1): # 实际上, i = N-1 时, 循环内不会发生任何改变, 因此, 可以令 i 最大循环到 N-2
        # 找出 i 后面的最大 score 及其下标
        pos = i + 1

        maxscore = np.max(scores[pos:], axis=0)
        maxpos = np.argmax(scores[pos:], axis=0)
        # 因为现在 i 为 0 ~ n-2, 因此无序进行下面的判断, 可以直接利用上面的式子代替
        """
        if i != N-1:
            maxscore = np.max(scores[pos:], axis=0)
            maxpos = np.argmax(scores[pos:], axis=0)
        else:
            maxscore = scores[-1]
            maxpos = 0
        """

        # 如果当前 i 的得分小于后面的最大 score, 则与之交换, 确保 i 上的 score 最大
        if scores[i] < maxscore:
            bboxes[[i, maxpos + i + 1]] = bboxes[[maxpos + i + 1, i]]
            scores[[i, maxpos + i + 1]] = scores[[maxpos + i + 1, i]]
            areas[[i, maxpos + i + 1]] = areas[[maxpos + i + 1, i]]

        # IoU calculate, 注意, 这里使用 bboxes 而不使用 x1, y1, ... 的原因是因为上面只交换了 bboxes, 而没有交换 x1, y1, ...
        xx1 = np.maximum(bboxes[i, 0], bboxes[pos:, 0])
        yy1 = np.maximum(bboxes[i, 1], bboxes[pos:, 1])
        xx2 = np.minimum(bboxes[i, 2], bboxes[pos:, 2])
        yy2 = np.minimum(bboxes[i, 3], bboxes[pos:, 3])
        w = np.maximum(0.0, xx2 - xx1 + 1)
        h = np.maximum(0.0, yy2 - yy1 + 1)
        intersection = w * h
        iou = intersection / (areas[i] + areas[pos:] - intersection)

        # Three methods: 1.linear 2.gaussian 3.original NMS
        if method == 1:  # linear
            weight = np.ones(iou.shape)
            weight[iou > iou_thresh] = weight[iou > iou_thresh] - iou[iou > iou_thresh]
        elif method == 2:  # gaussian
            weight = np.exp(-(iou * iou) / sigma2)
        else:  # original NMS
            weight = np.ones(iou.shape)
            weight[iou > iou_thresh] = 0

        scores[pos:] = weight * scores[pos:]

    # select the boxes and keep the corresponding indexes
    inds = bboxes[:, 4][scores > score_thresh]
    keep = inds.astype(int)
    return keep

# boxes and scores
boxes = np.array([[200, 200, 400, 400], [220, 220, 420, 420],
                  [240, 200, 440, 400], [200, 240, 400, 440],
                  [1, 1, 2, 2]], dtype=np.float32)
boxscores = np.array([0.9, 0.8, 0.7, 0.6, 0.5], dtype=np.float32)
index = soft_nms(boxes, boxscores, method=2)
print(index) # 按照 scores 的排序指明了对应的 box 的下标
print(boxes[index])
print(boxscores) # 注意, scores 不需要用 index 获取, scores 已经是更新过的排序 scores

ConvNMS 2015

  • A Convnet for Non-maximum Suppression
  • 其主要考虑IoU阈值设定得高一些,则可能抑制得不够充分,而将IoU阈值设定得低一些,又可能多个ture positive被merge到一起。其设计一个卷积网络组合具有不同overlap阈值的greedyNMS结果,通过学习的方法来获得最佳的输出。
  • 介绍

Pure NMS Network 2017

  • 和ConvNMS是相同作者
  • Learning non-maximum suppression
  • 检测器对于每个目标仅产生一个检测结果有两个关键点是必要的,一是一个loss惩罚double detections以告诉检测器我们对于每个目标仅需一个检测结果,二是相邻检测结果的joint processing以使得检测器具有必要的信息来分辨一个目标是否被多次检测。论文提出Gnet,其为第一个“pure”NMS网络。

Softer NMS 2018

  • Rethinking Bounding Box Regression for Accurate Object Detection
  • 论文的motivation来自于NMS时用到的score仅仅是分类置信度得分,不能反映Bounding box的定位精准度,既分类置信度和定位置信非正相关的。NMS只能解决分类置信度和定位置信度都很高的,但是对其它三种类型:“分类置信度低-定位置信度低”,“分类置信度高-定位置信度低”,“分类置信度低-定位置信度高“都无法解决。
  • 首先假设Bounding box的是高斯分布,ground truth bounding box是狄拉克delta分布(即标准方差为0的高斯分布极限)。KL 散度用来衡量两个概率分布的非对称性度量,KL散度越接近0代表两个概率分布越相似。
  • 提出的KL loss,即为最小化Bounding box regression loss,既Bounding box的高斯分布和ground truth的狄拉克delta分布的KL散度。直观上解释,KL Loss使得Bounding box预测呈高斯分布,且与ground truth相近。而将包围框预测的标准差看作置信度。
    黄金时代 —— 深度学习 (目标检测)_第7张图片
  • 提出的Softer-NMS,基于soft-NMS,对预测标注方差范围内的候选框加权平均,使得高定位置信度的bounding box具有较高的分类置信度。
  • 流程:
    黄金时代 —— 深度学习 (目标检测)_第8张图片
  • 预测的四个顶点坐标,分别对IoU>Nt的预测加权平均计算,得到新的4个坐标点,第i个矩形框的x1:
    x 1 i : = ∑ j x 1 j / σ x 1 , j 2 ∑ j 1 / σ x 1 , j 2  subject to  IoU ⁡ ( x 1 j , x 2 ) = N t \begin{array}{l} x 1_{i}:=\frac{\sum_{j} x 1_{j} / \sigma_{x 1, j}^{2}}{\sum_{j} 1 / \sigma_{x 1, j}^{2}} \\ \text { subject to } \operatorname{IoU}\left(x_{1 j}, x_{2}\right)=N_{t} \end{array} x1i:=j1/σx1,j2jx1j/σx1,j2 subject to IoU(x1j,x2)=Nt

代码

import cv2
import numpy as np

def nms(bounding_boxes, confidence_score, threshold):
    if len(bounding_boxes) == 0:
        return [], []

    bboxes = np.array(bounding_boxes)

    x1 = bboxes[:, 0]
    y1 = bboxes[:, 1]
    x2 = bboxes[:, 2]
    y2 = bboxes[:, 3]

    score = np.array(confidence_score)

    picked_boxes = []
    picked_score = []

    areas =(x2 - x1 + 1) * (y2 - y1 + 1)

    order = np.argsort(score)

    while order.size > 0:
        index = order[-1]

        picked_boxes.append(bounding_boxes[index])
        picked_score.append(confidence_score[index])

        x11 = np.maximum(x1[index], x1[order[:-1]])
        y11 = np.maximum(y1[index], y1[order[:-1]])
        x22 = np.minimum(x2[index], x2[order[:-1]])
        y22 = np.minimum(y2[index], y2[order[:-1]])

        w = np.maximum(0.0, x22 - x11 + 1)
        h = np.maximum(0.0, y22 - y11 + 1)
        intersection = w * h

        ratio = intersection / (areas[index] + areas[order[:-1]] - intersection)

        left = np.where(ratio < threshold)
        order = order[left]

    return picked_boxes, picked_score

def main():
    img_path = "/home/zerozone/Pictures/testimg/face.jpg"
    bounding_boxes = [(187, 82, 337, 317), (150, 67, 305, 282),
            (246, 121, 368, 304)]
    confidence_score = [0.9, 0.75, 0.8]
    image = cv2.imread(img_path)
    orig = image.copy()

    font = cv2.FONT_HERSHEY_SIMPLEX
    font_scale = 0.7
    thickness = 2
    threshold = 0.5

    # Draw bboxes and score
    for (x1, y1, x2, y2), confidence in zip(bounding_boxes, confidence_score):
        (w, h), baseline = cv2.getTextSize(
                str(confidence), font, font_scale, thickness)
        cv2.rectangle(orig, (x1, y1 - (2 * baseline + 5)),
                (x1 + w, y1), (0, 255, 255), -1)
        cv2.rectangle(orig, (x1, y1), (x2, y2), (0, 255, 255), 2)
        cv2.putText(orig, str(confidence), (x1, y1),
                font, font_scale, (0, 0, 0, thickness))

    # Draw bboxes and score after NMS
    picked_boxes, picked_score = nms(bounding_boxes, confidence_score, threshold)

    for (x1, y1, x2, y2), confidence in zip(picked_boxes, picked_score):
        (w, h), baseline = cv2.getTextSize(
                str(confidence), font, font_scale, thickness)
        cv2.rectangle(image, (x1, y1 - (2 * baseline + 5)),
                (x1 + w, y1), (0, 255, 255), -1)
        cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 255), 2)
        cv2.putText(image, str(confidence), (x1, y1),
                font, font_scale, (0, 0, 0, thickness))
    cv2.imshow("origin", orig)
    cv2.imshow("NMS", image)
    cv2.waitKey(0)

if __name__ == "__main__":
    main()
#include 
#include 
#include 

struct Bbox {
    int x1;
    int y1;
    int x2;
    int y2;
    float score;
    Bbox(int x1_, int y1_, int x2_, int y2_, float s):
	x1(x1_), y1(y1_), x2(x2_), y2(y2_), score(s) {};
};

float iou(Bbox box1, Bbox box2) {
    float area1 = (box1.x2 - box1.x1 + 1) * (box1.y2 - box1.y1 + 1);
    float area2 = (box2.x2 - box2.x1 + 1) * (box2.y2 - box2.y1 + 1);

    int x11 = std::max(box1.x1, box2.x1);
    int y11 = std::max(box1.y1, box2.y1);
    int x22 = std::min(box1.x2, box2.x2);
    int y22 = std::min(box1.y2, box2.y2);
    float intersection = (x22 - x11 + 1) * (y22 - y11 + 1);
    return intersection / (area1 + area2 - intersection);
}

std::vector<Bbox> nms(std::vector<Bbox> &vecBbox, float threshold) {
    auto cmpScore = [](Bbox box1, Bbox box2) {
	return box1.score < box2.score; // 升序排列, 令score最大的box在vector末端
    };
    std::sort(vecBbox.begin(), vecBbox.end(), cmpScore);

    std::vector<Bbox> pickedBbox;
    while (vecBbox.size() > 0) {
        pickedBbox.emplace_back(vecBbox.back());
        vecBbox.pop_back();
        for (size_t i = 0; i < vecBbox.size(); i++) {
            if (iou(pickedBbox.back(), vecBbox[i]) >= threshold) {
                vecBbox.erase(vecBbox.begin() + i);
            }
        }
    }
    return pickedBbox;
}

int main() {
    std::vector<Bbox> vecBbox;
    vecBbox.emplace_back(Bbox(187, 82, 337, 317, 0.9));
    vecBbox.emplace_back(Bbox(150, 67, 305, 282, 0.75));
    vecBbox.emplace_back(Bbox(246, 121, 368, 304, 0.8));

    auto pickedBbox = nms(vecBbox, 0.5);

    for (auto box : pickedBbox) {
	std::cout << box.x1 << ", " <<
		box.y1 << ", " <<
		box.x2 << ", " <<
		box.y2 << ", " <<
		box.score << std::endl;
    }
    return 0;
}

Fitness NMS 2017

  • Improving Object Localization with Fitness NMS and Bounded IoU Loss
    • 这种方法考虑了大于估计的重叠的IoU和预测的bounding boxes的分类得分。

1 深度网络发展历程

黄金时代 —— 深度学习 (目标检测)_第9张图片

2 Two-Stage Network

R-CNN 2014

*对深度网络和检测任务的说明

  • 纯粹的NN网络适合图像识别或分类
  • 目标检测任务可以拆分为:图像分类和定位,后者如何通过深度网络解决呢?
  • 定位的解决思路:① 将边框定位问题看作四个参数的回归问题,即Classification + Regression;② 传统的滑动窗搜索思路
  • 上述两种思路,在多类目标检测时,复杂度爆炸

R-CNN方案简化复杂度的思路

  • 先使用传统人工特征(纹理,边缘和颜色等)进行Region Proposal (Selective Search)
  • 再对候选区域进行暴力枚举,计算候选区域的深度特征,用SVM进行分类,并对每个框进行回归(线性回归模型),将特征图上的候选框调整到原图上!

流程

黄金时代 —— 深度学习 (目标检测)_第10张图片

  • 1 S e l e c t i v e S e a r c h Selective Search SelectiveSearch提取出候选区域框;
  • 2 根据候选区域框与真实框的交并比决定正负样本标签(此时不关心框内物体的类别);
  • 3 送入到 A l e x N e t AlexNet AlexNet 中提取 C N N CNN CNN 特征
  • 4 将提取到的特征送入到 S V M SVM SVM 分类器中进行分类, 每一个类别都单独训练了一个 SVM 分类器;
  • 5 对每一个框进行边框回归, 学习特征图谱候选区域框到真实框的转换, 调整框的位置.

说明

  • 特征提取网络采用AlexNet
  • 输入图片227x227
  • 正负样本划分: 与 gt-box 的 IoU 大于 0.5 的认为是正样本, 反之认为是负样本. 训练时, mini-batch 中正样本采样数为32(over all classes), 负样本的采样数为 96. 负样本数量多是因为在真实情况下, 背景的区域数量远大于物体数量.
  • 分类器: 为每个类别训练了一个 SVM.

Selective Search

  • 首先利用分割算法(Graph-Based Image Segmentation, 2004, IJCV, 贪心)得到一些初始化的区域,
  • 然后计算每个相邻区域的相似性, 相似性的计算依赖于颜色相似性纹理相似性, 同时给较小的区域赋予更多的权重, 也就是优先合并小区域(否则大区域有可能会不断吞并周围区域, 使得多尺度之应用了在局部区域, 而不是在每个位置都具有多尺度),
  • 接着找出相似性最大的区域, 将它们合并, 并计算新合并的区域与其他相邻区域的相似性, 重复这个过程, 直到所有的区域被合并完为止.

R-CNN分类器为什么不采用Softmax

  • 作者尝试过但是 mAP 从 54.2% 降到了 50.9%
  • 下降的原因是多因素造成的, 比如对正负样本的定义, 再比如在训练 Softmax 时使用的负样本是随机采样的, 而训练 SVM 时的负样本更像是 “hard negatives” 的子集, 导致训练精度更高等等.
  • 后续的 Fast RCNN 使用 Softmax 也达到了和 SVM 差不多的准确率, 训练过程更加简单.

BBox回归方式

  • 在 R-CNN 的边框回归中, 我们不是直接学习真实框的坐标, 而是学习从 Proposals 到 真实框的一个偏移变换函数,
  • 具体来说, 对于中心点, 需要学习的是 proposal 和 真实框相对位移, 这个位移会用 proposal 的宽和高进行归一化, 对于宽和高, 需要学习的是真实框相对于 proposal 的 log 缩放度.
    t x = ( G x − P x ) / P w t y = ( G y − P y ) / P h t w = log ⁡ ( G w / P w ) t h = log ⁡ ( G h / P h ) t_{x}=\left(G_{x}-P_{x}\right) / P_{w} \\ t_{y}=\left(G_{y}-P_{y}\right) / P_{h} \\ t_{w}=\log \left(G_{w} / P_{w}\right) \\ t_{h}=\log \left(G_{h} / P_{h}\right) tx=(GxPx)/Pwty=(GyPy)/Phtw=log(Gw/Pw)th=log(Gh/Ph)

BBox回归为什么不直接对坐标进行回归

  • 尺度不变性(宽高):不归一化对于固定的偏移量,就会导致小物体偏移了很多,大物体偏移了一点
  • 平移不变性(中心点)

为什么当 Region Proposals 和 Ground Truth 较接近时, 即 IoU 较大时, 可以认为是边框回归函数是线性变换?

  • 当输入的 Proposal 与 Ground Truth 相差较小时(RCNN 设置的是 IoU>0.6), 可以认为这种变换是一种线性变换, 那么我们就可以用线性回归来建模对窗口进行微调, 否则会导致训练的回归模型不 work (当 Proposal跟 GT 离得较远,就是复杂的非线性问题了,此时用线性回归建模显然不合理).
    lim ⁡ x → 0 log ⁡ ( 1 + x ) = x t w = log ⁡ ( G w / P w ) = log ⁡ ( G w + P w − P w P w ) = log ⁡ ( 1 + G w − P w P w ) \lim _{x \rightarrow 0} \log (1+x)=x \\ t_{w}=\log \left(G_{w} / P_{w}\right)=\log \left(\frac{G_{w}+P_{w}-P_{w}}{P_{w}}\right)=\log \left(1+\frac{G_{w}-P_{w}}{P_{w}}\right) x0limlog(1+x)=xtw=log(Gw/Pw)=log(PwGw+PwPw)=log(1+PwGwPw)
  • G w − P w = 0 G_{w}-P_{w}=0 GwPw=0 的时候, 回归函数 t w t_{w} tw 可以看做是线性函数.

R-CNN缺点

  • 训练过程是分阶段的(Training is a multi-stage pipeline)
  • 耗费资源(Training is expensive in space and time)
  • 目标检测速度太慢(Object detection is slow)
  • R-CNN对于将近2000个候选框都要进行CNN提取+SVM分类!完全可以值进行一次卷积特征提取,每个region proposal的尺度不同,而全连接层输入必须是固定的长度,所以直接这样输入全连接层肯定是不行的。SPP Net解决这个问题!

Fast R-CNN 2015

黄金时代 —— 深度学习 (目标检测)_第11张图片
黄金时代 —— 深度学习 (目标检测)_第12张图片

有哪些改进

  • 直接采用在特征图谱上的候选框组成 mini-batch, 共享卷积计算结果, 大大加速训练和推演速度.
  • 提出了 RoI Pooling, 从而可以在特征图谱上截取任意尺寸的候选框
  • 联合训练Smooth L1 损失的边框回归和Softmax交叉熵分类损失,训练过程更加统一.
  • 对于计算量较大的全连接层, 使用奇异值分解加速计算

ROI Pooling

前向传播

  • 任意给定尺寸为 h × w h×w h×w 的feature map的 RoI 窗口, 将其划分成 W × H W×H W×H 的网格大小
  • 网格中的尺寸大约为 h / H × w / W h/H×w/W h/H×w/W(就近取整,PS:这里会导致精度损失), 然后我们在网格中执行max pooling操作
  • RoI pooling 在卷积图谱上的各个通道之间是独立计算的
  • 对于任意size的输入, 都可以获得固定长度的输出
  • RoI layer ≈ ≈ single spatial pyramid pooling layer
  • 相比于金字塔池化, RoI池化只确定了唯一大小的pooling 窗口, 以便于可以使用反向传播更新池化层之前的网络层参数, 进而提高准确率(而SPP不能更新前面的Conv层参数,只能fine tuning后面fc层的参数)

反向传播

  • RoI Pooling 的反向传播过程和 Max Pooling 类似, 不同的是, 对于每个 mini-batch 的 RoI r r r 和每个 pooling 单元 j j j 及其输出 y r j y_{rj} yrj ,偏导数 ∂ L / ∂ y r j ∂L/∂y_{rj} L/yrj 是所有 RoI 反向传播回来的累加和.
  • 具体如下图所示, 当不同的 RoI 区域出现重叠时, 恰好这两个区域都选取了 x 2 , 3 x_{2,3} x2,3 作为激活点, 那么对这个点的反向传播值 ∂ L / ∂ x 2 , 3 ∂L/∂x_{2,3} L/x2,3 就应该等于 ∂ L / ∂ y 0 , 2 + ∂ L / ∂ y 1 , 0 ∂L/∂y_{0,2}+∂L/∂y_{1,0} L/y0,2+L/y1,0
    黄金时代 —— 深度学习 (目标检测)_第13张图片
    ∂ L ∂ x i = ∑ r ∑ j [ i = i ∗ ( r , j ) ] ∂ L ∂ y r j \frac{\partial L}{\partial x_{i}}=\sum_{r} \sum_{j}\left[i=i^{*}(r, j)\right] \frac{\partial L}{\partial y_{r j}} xiL=rj[i=i(r,j)]yrjL

为什么 RoI Pooling 比 SPP 效果好

  • SPP的Pooling方式是组合不同划分粒度下feature map的max pooling. 它也具有和RoI Pooling 类似的效果, 可以接受任意尺度的特征图谱, 并将其提取成固定长度的特征向量
  • 但是 SPPNet 池化方式使得无法用恰当的方式来进行反向传播注意这里不是说不能反传,只不过计算代价十分昂贵!在当时情况下近乎于不能反传!), 即SPPNet 没有对浅层的网络进行 fine-tuning, 而是直接在最后两个全连接层上进行fine-tune, 虽然最后也取得了不错的成果
  • 但是Roos认为, 虽然离输入层较近的前几层卷积层是比较generic和task independent的, 但是靠近输出层的卷积层还是很有必要进行fine-tune的, 他也通过实验证实了这种必要性
  • 于是他简化了SPP的Pooling策略, 用一种更简单粗暴的Pooling方式来获得固定长度的输出向量, 同时也设计了相应的RoI Pooling的反向传播规则, 并对前面的基层卷积层进行了fine-tune, 最终取得了不错的效果.

Multi-task Loss

L ( p , u , t u , v ) = L c l s ( p , u ) + λ [ u ≥ 1 ] L l o c ( t u , v ) L c l s ( p , u ) = − log ⁡ p u L l o c ( t u , v ) = ∑ i ∈ x , y , w , h smooth ⁡ L 1 ( t i u − v i ) smooth ⁡ L 1 ( x ) = { 0.5 x 2 ∣ x ∣ < 1 ∣ x ∣ − 0.5  otherwise  L\left(p, u, t_{u}, v\right)=L_{c l s}(p, u)+\lambda[u \geq 1] L_{l o c}\left(t^{u}, v\right) \\ L_{c l s}(p, u)=-\log p_{u} \\ L_{l o c}\left(t^{u}, v\right)=\sum_{i \in x, y, w, h} \operatorname{smooth}_{L_{1}}\left(t_{i}^{u}-v_{i}\right) \\ \operatorname{smooth}_{L_{1}}(x)=\left\{\begin{array}{ll}0.5 x^{2} & |x|<1 \\ |x|-0.5 & \text { otherwise }\end{array}\right. L(p,u,tu,v)=Lcls(p,u)+λ[u1]Lloc(tu,v)Lcls(p,u)=logpuLloc(tu,v)=ix,y,w,hsmoothL1(tiuvi)smoothL1(x)={0.5x2x0.5x<1 otherwise 

Smooth L1 相比于 L2 损失, 在回归时有什么优势

  • smooth L1 损失对离异点的敏感度更低,更鲁棒;smooth L1 在值相差很大时, 其梯度为 ±1,L1 在 x 绝对值较大时, 是线性的
  • L2 损失在当预测值与目标值相差很大时, 梯度很容易爆炸, 因为梯度里面包含了 ( t u i − v i ) (t_{ui}−v_i) (tuivi) 这一项

SVD 奇异值分解简介

  • 简化了计算量较大的全连接层的参数
    W ≈ U Σ t V T W \approx U \Sigma_{t} V^{T} WUΣtVT
  • 矩阵 W W W 的参数量从 u v uv uv 降低到了 u t + t ut+t ut+t
  • t t t为矩阵的奇异值数量,奇异值有一个性质,很多情况下,前 10% 甚至 1% 的奇异值的和就站了全部奇异值之和的 99% 以上的比例. 也就是说, 我们可以用最大的 k k k 个奇异值来近似描述矩阵. 由于 k k k 远远小于 m i n ( u , v ) min(u,v) min(u,v)
  • 将单个的全连接网络层的权重矩阵 W 用两层全连接层所替代, 第一个全连接层使用的权重矩阵为 Σ t V T Σ_tV^T ΣtVT (没有偏置项), 第二个权重矩阵为 U U U (带有原始矩阵 W W W 的偏置项).

Faster R-CNN 2016

  • Faster R-CNN 主要由两部分组成. 其一是用于生成候选区域框的深度全卷积网络(RPN), 其二是 Fast R-CNN 检测模型. 二者在训练的时候会进行参数共享.
    黄金时代 —— 深度学习 (目标检测)_第14张图片

RPN网络

  • RPN 主要是通过在 BackBone 网络输出的特征图谱上设置一组固定大小的 anchor boxes 实现.
  • 对于图谱上的每一个像素点, 都会枚举 k k k 个具有预设尺寸的 anchor boxes. 对于一个 W × H W×H W×H 大小的特征图谱, 总共会产生 W H k WHk WHk个 anchor boxes. 对于每一个 anchor box, 我们需要预测 (4+2) 个值,代表 location 偏移量是否包含物体的二分类预测(正负样本,类别位置)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    黄金时代 —— 深度学习 (目标检测)_第15张图片
  • RPN分为两条线,分别进行正负样本分类和边框回归!
  • 上面一条线:① 使用 3 × 3 3×3 3×3的filter对feature map进行卷积,目的是使提取出来的feature更鲁棒;② 使用 3 × 3 3×3 3×3的filter对feature map进行卷积,目的是使提取出来的feature更鲁棒;③ 使用 1 × 1 1×1 1×1的filter进行卷积,一共为18个;④ reshape操作 ( W , H , D = 18 ) (W,H,D=18) (W,H,D=18)reshape成 ( 2 , 9 × W × H ) (2,9×W×H) (2,9×W×H);⑤ 然后我们进行softmax,得出对这 9 × W × H 9×W×H 9×W×H每一个的两个score,一个是有物体,一个是没有物体。PS:9个Anchor!

正负样本标点策略

黄金时代 —— 深度学习 (目标检测)_第16张图片
PS:见上图:Faster R-CNN 网络实际上进行了两次 location regression.(同理也进行了两次 NMS)

  • 根据 RPN 网络的输出结果, 选取 128 个 positive anchor boxes 和 128 个 negative anchor boxes 参与 Fast R-CNN 网络的训练
  • 我们通常利用下面的方式确定 anchor 属于正样本还是负样本
  • ① 正样本:
    • 和某个GT box 具有最大(不一定大于0.7) 的 IoU (防止有些 GT boxes 没有匹配的 anchor)
    • 和某个GT box 的 IoU 大于0.7
  • ② 负样本:
    • 和所有的 GT boxes 的 IoU 都小于 0.3
  • 注意1: 对于剩下的既不是正样本也不是负样本的 anchor boxes, 不参与训练过程;注意2: 一个 GT box 可能会与多个anchor boxes相匹配;注意3: 一个 anchor box 只能与 一个 GT box 相匹配

损失函数

L ( { p i } , { t i } ) = 1 N c l s ∑ i L c l s ( p i , p i ∗ ) + λ 1 N r e g ∑ i p i ∗ L r e g ( t i , t i ∗ ) L\left(\left\{p_{i}\right\},\left\{t_{i}\right\}\right)=\frac{1}{N_{c l s}} \sum_{i} L_{c l s}\left(p_{i}, p_{i}^{*}\right)+\lambda \frac{1}{N_{r e g}} \sum_{i} p_{i}^{*} L_{r e g}\left(t_{i}, t_{i}^{*}\right) L({pi},{ti})=Ncls1iLcls(pi,pi)+λNreg1ipiLreg(ti,ti)

  • 上式中, i i i 代表 mini-batch 中 anchor 的下标, p i p_i pi 代表预测 anchor i i i 是一个物体的可能性大小. 如果 anchor 是正样本, 则真实标签 p i ∗ p^∗_i pi 为1, 反之, 如果是负样本, 则为0. t i t_i ti 是一个 vector, 用来表示参数化以后的边框坐标, t i ∗ t^∗_i ti 是正样本 anchor 对应的真实框的参数化坐标.
  • 分别是对数函数和smooth L1

共享参数训练

黄金时代 —— 深度学习 (目标检测)_第17张图片

  • 上图是 4-Step Alternating training(四步式交叉训练, 实验默认训练方法):

    • 1 用 ImageNet 初始化 RPN, 训练 region proposals 任务直至收敛(得到一个不错的边界框生成器)
    • 2 使用第一步生成的 proposals 来训练一个单独的 Fast R-CNN 网络. 这个检测网络同样也是用 ImageNet 预训练的模型进行初始化. 到这里为止, 这两个网络还没有共享卷积层.
    • 3 使用检测网络的参数对 RPN 网络进行初始化, 但是此时 我们固定住共享的卷积层(backbone), 仅仅 fine-tuning 属于 RPN 独有的那些网络层.
    • 4 利用新的 RPN 产生的 proposals, 训练 Fast R-CNN, 同样固定住共享的卷积层, 仅仅 fine-tuning 属于 Fast R-CNN 独有的网络层.
  • Approximate joint training(近似联合训练, 包含在官方代码中)

    • 将 RPN 和 Fast R-CNN 在训练期间合并到一个网络中, 在每个 SGD 迭代过程, 前向计算会先由 RPN 生成 proposals (修正后的)
    • 然后这些 proposals 会作为输入送到 Fast R-CNN 中, 在反向传播的时候, 对于共享层的参数更新, 会同时考虑来自 RPN 损失和 Fast R-CNN 损失传递过来的信号.
    • 但是这种策略忽略了相对于 proposals boxes 坐标的导数, 因此这只是一种粗略的联合训练方式. 在实验中, 近似联合训练可以大幅减少训练时间(25~50%), 精度会有所损失, 但是性能依然客观.
  • Non-approximate joint training(非近似联合训练)

    • 正如上面讨论的, RPN 预测的 bounding boxes 的坐标同样与输入数据之间存在联系. 在 Fast R-CNN 的 RoI pooling Layer 中会接受卷积特征, 同时也会将预测的 bounding boxes 作为输入, 因此理论上来说, 一个有效的优化器(backpropagation solver)应该包含相对于 box coordinate 的梯度. 因此, 我们需要 RoI pooling layer 相对于 box coordinates 是可导的.

如果两个物体重合度很高, Faster R-CNN 会怎么样?

  • 在 RPN 阶段, 优势 proposals 是类别不可知的(class-agnostic)
    因此重叠度非常高的两个框会被 NMS 过滤掉. 因此对于重合度非常高的两个物体, 很有可能会发生漏检
  • 注, 但是在 Detection 阶段, 每个 anchor boxes 都与一个 GT 对应, 并且会预测特定的类别, 在进行 NMS 的时候, 是对每个类别分别进行 NMS, 所以, 也有一定的概率能够检测成功, 不过可能置信度较低, 同时框的 IoU 水平也较低(因为较高的 IoU 已经在 RPN 阶段被 NMS 掉了)
  • Faster R-CNN = class-agnostic RPN + class-specific detection

Mask R-CNN 2017

黄金时代 —— 深度学习 (目标检测)_第18张图片

简介

  • 在 Faster R-CNN 模型中添加了一个与分类和回归分支平行的掩膜预测分支. 掩膜分支(mask branch) 是一个小型的 FCN 网络, 它会作用在每一个 RoI 上, 以像素到像素的方式来预测一个分割掩膜.
  • Mask R-CNN的掩膜预测分支对于每一个ROI的输出都是 K m 2 Km^2 Km2,即每个类别都输出 m 2 m^2 m2的掩膜
  • 预测掩膜时非常关键的一点就是要对分类任务和分割任务解耦:① 损失函数上:Mask R-CNN 使用了基于单像素的 sigmoid 二值交叉熵来替换基于单像素的 Softmax 多项式交叉熵.(softmax适合多分类)② ROI pooling 改进为 ROI Align:前者存在两次量化误差(图像坐标到特征图谱坐标, 特征图谱划分固定网格),会导致ROI和原图上坐标存在不完全对齐,这个对精度要求高的分割任务有较大的影响!所以采用ROI Align
  • 除此之外,Mask R-CNN 也有一些其他的优化, 比如说更多的 anchor, 更大的 batch size, 更强的 backbone 网络(ResNeXt+FPN)等等.
  • Mask R-CNN是一个很多state-of-the-art算法的合成体,并非常巧妙的设计了这些模块的合成接口:
    • 使用残差网络作为卷积结构
    • 使用FPN作为骨干架构
    • 使用Faster R-CNN的物体检测流程:RPN+Fast R-CNN
    • 增加FCN用于语义分割

ROI Align

  • ROI Pooling存在的两次量化导致位置映射粗糙!> Misalignment问题
  • 量化1 将原图上的ROI坐标就近取整到特征图上(取整损失)
  • 量化2 将特征图上的ROI区域分割乘 k × k k×k k×k个cell(取整损失)
  • 小目标受Misalignment影响更大!

思路

  • RoI Align的解决思路:
  • 1 取消量化操作(即, 我们使用 x/16, 而不是 [x/16]),
  • 2 使用双线性内插的方法获得坐标为浮点数的像素点上的图像数值, 从而将整个特征聚集过程转换为一个连续的操作
  • 具体流程:
  • 1 计算 RoI 在特征图谱上的浮点坐标 x/16
  • 2 在每一个 bin 当中, 均匀的选取若干个( 实验效果显示 4 个较好)采样点
  • 3 对于每一个采样点, 利用离它最近的四个 feature map 点计算当前采样点的值(双线性插值)
  • 4 对每个 bin 中的所有采样点, 分别执行 max / average pooling操作.
  • 5 最终输出 k × k k×k k×k 的固定尺寸特征图谱 (如 2x2).

ROI Align 的BP过程

  • 常规的ROI Pooling
    ∂ L ∂ x i = ∑ r ∑ j [ i = i ∗ ( r , j ) ] ∂ L ∂ y r , j \frac{\partial L}{\partial x_{i}}=\sum_{r} \sum_{j}\left[i=i^{*}(r, j)\right] \frac{\partial L}{\partial y_{r, j}} xiL=rj[i=i(r,j)]yr,jL
  • ROI Align
    ∂ L ∂ x i = ∑ r ∑ j [ d ( i , i ∗ ( r , j ) ) < 1 ] ( 1 − Δ h ) ( 1 − Δ w ) ∂ L ∂ y r j \frac{\partial L}{\partial x_{i}}=\sum_{r} \sum_{j}\left[d\left(i, i^{*}(r, j)\right)<1\right](1-\Delta h)(1-\Delta w) \frac{\partial L}{\partial y_{r j}} xiL=rj[d(i,i(r,j))<1](1Δh)(1Δw)yrjL
  • 在池化前的特征图中, 每一个与 x i ∗ ( r , j ) x_{i^{*}(r, j)} xi(r,j)横纵坐标均小于 1 的点都会接受与次对应的点 y r j y_{rj} yrj 回传的梯度. 故 RoI Align 的反向传播公式如上!

Mask分支的实现

  • 用多层感知机(若干全连接层)或者全卷积网络(若干卷积层)实现
  • 后者因为具有空间编码的优势,效果更好!

总结

  • 基于 Faster R-CNN 的基本结构, 替换 backbone 为 ResNet-50/101-C4 和 ResNet-50/101-FPN, ResNeXt-101-FPN.
  • 除了边框回归和目标分类两个分支外, 新添加了一个全卷积的 Mask Prediction 网络, 该分支在每一个 RoI 上的输出维度为 K × m 2 K×m^2 K×m2, 代表 K 个类别的二值掩膜.
  • 在计算 mask 的损失时, 使用的是 sigmoid 的二分类损失, 而不是多分类的 softmax 损失, 这减少了类别之前的竞争, 使得可以生成更好的分割结果.
  • RoIAlign 消除了 RoI Pooling 中的两次量化操作, 使得最终提取到的特征可以 RoI 尽可能的对齐. 从而大幅提升在实例分割任务上的性能表现.

FCN 2014

  • 分割模型,为R-FCN铺垫
  • FCN 将网络中的全连接层全都换成了卷积层(卷积核大小为 1 × 1 1×1 1×1, 通道数为 FC 神经元个数), 这么做有两个好处: ① 可以接受任意尺寸的图片输入 ② 对于较大的图片输入, 最终会产生多个卷积块, 共用一套权重, 减少重复计算, 从而可以加快模型的计算速度.
  • 在进行分割任务时, FCN 会利用 反卷积跳跃连接 进行像素预测. 也就是说它会将深层图谱的预测结果和 upsample 后的图谱预测结果融合(采用 max fusion), 这样预测出来的结果会更加精细。
  • 在 FCN 中, 分别在步长为 16 和 步长为 8 的特征图谱进行了分割预测, 结果也显示越精细的特征图谱, 分割的结果也越好. 只不过随着步长的缩短, 获得的提升也慢慢变小了.

全卷积结构

  • 基于全卷积网络结构,把FC全去掉!
    黄金时代 —— 深度学习 (目标检测)_第19张图片

跳级结构

黄金时代 —— 深度学习 (目标检测)_第20张图片
黄金时代 —— 深度学习 (目标检测)_第21张图片
黄金时代 —— 深度学习 (目标检测)_第22张图片

  • 反卷积层相对于双线性插值来说, 具有可学习的参数, 可以通过网络的输出 loss 动态的调整自身的上采样过程

反卷积

普通卷积 反卷积
黄金时代 —— 深度学习 (目标检测)_第23张图片 黄金时代 —— 深度学习 (目标检测)_第24张图片
  • 尺寸计算的公式的运算全是逆的!

R-FCN 2016

  • R-FCN 的主要贡献在于解决了“分类网络的位置不敏感性(translation-invariance in image classification)”与“检测网络的位置敏感性(translation-variance in object detection)”之间的矛盾,在提升精度的同时利用“位置敏感得分图(position-sensitive score maps)”提升了检测速度。
    黄金时代 —— 深度学习 (目标检测)_第25张图片

动机

  • 对于 two-stage 检测网络来说, 在 RoI Pooling 之前的 backbone 网络层的计算时被所有 RoI 所共享的, 而 RoI Pooling 之后的计算时单独对每个 RoI 进行分类和回归, 因此虽然各个 RoIs 的计算存在大量的重复, 依然无法共享这部分计算.
  • 对于前面的 backbone 网络来说, 它本身不会考虑坐标信息, 因此具有 “位置不敏感性(translation-invariance)”, 而 RoI Pooling 之后的网络, 它会考虑目标的位置信息, 因此具有 “位置敏感性(translation-variance)”
  • 为了让目标检测网络具有 “位置敏感性”, Faster R-CNN 采取了一种不太自然的做法, 那就是将 RoI Pooling 插在了 backbone 网络的卷积层之间
  • 对于 ResNet 来说, 它将 RoI Pooling 插在了 C4 卷积段的后面, 这样带来的问题就是 RoI Pooling Layer 之后的 C5 卷积段是无法在 RoIs 上共享卷积计算

Position-sensitive Score Map

  • 编码每个物体的 RoI 信息, 它将 RoI 划分成 k × k k×k k×k 子区域, 每个子区域代表了 RoI 的某一特定部位.
  • R-FCN 首先会共享 backbone 的所有卷积层, 然后在共享的卷积层的最后接上一层卷积,这个卷积层的输出图谱就是 position-sensitive score map, 它的 height, width 保持不变, 其 channels = k 2 ( C + 1 ) k^2(C+1) k2(C+1), C+1 代表物体类别, 也就是说每个类别都有 k 2 k^2 k2 个 score maps, 每个 score map 都对应了物体某一特定部位的响应值!
    黄金时代 —— 深度学习 (目标检测)_第26张图片
  • 利用 Position-sensitive RoI Pooling 对这个尺度为 W × H × k 2 ( C + 1 ) W×H×k^2(C+1) W×H×k2(C+1) 的特征图谱进行池化, 池化时对于每个 RoI 的第 i 个子区域, 我们就在第 i i i 个 score map 上的对应区域采用池化来得到 RoI 第 i 个子区域的值, 对于每一个类别都是如此, Ps-RoI Pooling 的输出就是一个 k 2 × ( C + 1 ) k^2×(C+1) k2×(C+1) 大小的特征图谱(注意 RoI Pooling 的输出尺寸是用划分的网格数决定的).
  • 我们对得到的 k 2 × ( C + 1 ) k^2×(C+1) k2×(C+1) 大小的特征图谱通过取 平均值 的方式来投票, 得到 (C+1) 维的向量, 然后利用 softmax 得到每个类别的预测概率.
  • 对于回归操作, 其方法类似, 通过在回归分支上添加一个卷积层实现, 卷积层的输出尺寸是 W × H × 4 k 2 W×H×4k^2 W×H×4k2, 然后经过 Ps-RoI Pooling, 对每个 RoI 都输出 k 2 × 4 k^2×4 k2×4 尺寸的特征图谱, 再经过取平均投票的方式, 得到一个 4−d 的向量, 该向量就代表了这个 RoI 所对应的偏移量 t = ( t x , t y , t w , t h ) t=(tx,ty,tw,th) t=(tx,ty,tw,th)
  • 利用position sensitive score map将目标位置信息融合进RoI + 让更多的层共享计算

R-FCN缺点

  • 对于遮挡情况效果很差, 因为当物体被遮挡时, 其关键部位往往被被识别成遮挡物体, 使得无法正确识别被遮挡物体, 详情见 CoupleNet 解释.

CoupleNet 2017

动机

  • R-FCN 利用 PSRoI Pooling, 利用物体的局部特征, 将位置敏感行引入到了检测网络中, 但是同时, PsRoI Pooling 的设计在一定程度上忽略了物体的整体结构信息以及它的 Global Feature.

简介

  • PsRoI Pooling 的设计在一定程度上忽略了物体的整体结构信息以及它的 Global Feature. 因此 CoupleNet 提出利用 RoI Pooling 捕捉物体的 Global Feature, 并将其与 PsRoI Pooling 提取到的 local feature 相结合, 共同决定最终的预测结果.

结构

黄金时代 —— 深度学习 (目标检测)_第27张图片

  • Local FCN: 由 PSRoI Pooling 和相应的预测网络组成, 用于提取 local feature, 就像 R-FCN 中的那样
  • Global FCN: 由 RoI Pooling 和相应的预测网络组成, 其中, RoI Pooling 会分别在原始的 RoI 和 扩大两倍 后的 RoI 上提取特征, 然后将结果在通道维度上连接. 这样做不仅可以提取到该物体的全局特征, 同时也可以提取到物体的 context 信息, 这对于解决遮挡类问题有很大帮助.

如何结合 局部特征 和 全局特征

  • Normalization:
    • L2 归一化: 会损伤性能
    • 1x1 卷积自动学习合适的输出: 输入输出 Tensor 形状不变, 每个值都根据学习到的结果进行变化. 效果好, 提升 0.6 个百分点.
  • Coupling Structure(结合 local 和 global 分支):
    • element-wise sum: 在任何情况下有最优, 即使在不使用 Normalization - - - 的情况下, 也是最优选择
    • element-wise product: 很差
    • element-wise maximum: 不如 sum
      黄金时代 —— 深度学习 (目标检测)_第28张图片

CoupleNet 为什么可以解决遮挡类问题

  • 以下图为例, 沙发坐了了两个人, 当我们对检测沙发时, PSRoI Pooling 会给出非常低的置信度, 因他目标框内的各个部位都不是沙发的特定部位, 而是其他类别的物体, 因此, R-FCN 对于遮挡问题表现很差. 对于 RoI Pooling 来说, 由于它在一定程度上考虑了 RoI 的整体特征, 因此, 可以给出一定的置信度, 但是也只有 0.45.
  • 而 CoupleNet 一方面使用 PSRoI Pooling 解决了分类网络位置不敏感和检测网络位置敏感性之间的矛盾, 同时利用 global 信息和 context 信息弥补了 PSRoI Pooling 只关注 local feature 的缺点, 因此在结合两个分支的输出结果以后, 可以很好的检测出被局部遮挡的物体.(人坐在沙发, 椅子, 桌子前等等)
    黄金时代 —— 深度学习 (目标检测)_第29张图片

Cascade R-CNN 2017

简介

  • 本文针对检测问题中正负样本区分的 IoU 阈值选择问题提出了一种新的目标检测框架, Cascade R-CNN
  • 在 two-stage 的目标检测模型当中, 需要设置 IoU 阈值来区分正样本和负样本, 通常, 阈值选的越高, 正样本的框就与真实框越接近, 但是这样就会使得正样本的数量大大降低, 训练时容易产生过拟合问题, 而如果阈值选的较低, 就会产生大量的 FP 样本. 根据经验和实验证明可知, 当输入的 proposals 和真实框的 IoU 的值, 与训练器训练时采用的 IoU 的阈值比较接近的时候, 训练器的性能会比较好, 为此, 作者提出了一种级联式的阈值训练方法, 先在较低的阈值上训练检测器, 得到具有更高 IoU 的候选框输出, 然后在此基础上进行训练, 不断提升 IoU 的阈值, 这样一来, 最终生成的候选框质量会变得更高 (与真实框的 IoU 更大). 作者提出这种框架的启发来自于图1©, 整体来说, 输入的 proposals 的 IoU 在经过检测器边框回归以后, 其输出的边框与真实框会有更大的 IoU, 因此可以将这个具有更大 IoU 的框作为下一个检测器的输入, 同时调高训练时的 IoU(H1 变成 H2), 进而得到质量更高的框
    黄金时代 —— 深度学习 (目标检测)_第30张图片
    黄金时代 —— 深度学习 (目标检测)_第31张图片
  • 每一个 stage 的 detector 都可以有足够多满足阈值的样本进行训练, 不会发生过拟合问题
  • 更深层的 detector 可以在精度更高的 proposals 上进行预测, 生成的结果效果更好
  • 每个 stage 的阈值是逐级升高的(对比 Iterative BBox 使用的是相同的阈值), 使之可以逐渐提高 proposals 的 IoU

3 One-Stage Network

说明

  • 2stage目标检测继承自传统的Region Proposal后穷举筛选的思路;
  • 1stage目标检测继承自传统的滑动窗穷举搜索的思路,主要思路是均匀地在图片的不同位置进行密集抽样,抽样时可以采用不同尺度和长宽比,然后利用CNN提取特征后直接进行分类与回归,整个过程只需要一步,所以其优势是速度快,但是均匀的密集采样的一个重要缺点是训练比较困难,这主要是因为正样本(物体)与负样本(背景)极其不均衡(参见Focal Loss),导致模型准确度稍低。不同算法的性能如图1所示,可以看到两类方法在准确度和速度上的差异。
    黄金时代 —— 深度学习 (目标检测)_第32张图片
    黄金时代 —— 深度学习 (目标检测)_第33张图片

YOLOv1 2015

简介

黄金时代 —— 深度学习 (目标检测)_第34张图片

  • 经过若干卷积层和池化层,变成 7 × 7 × 1024 7×7×1024 7×7×1024张量;再经过两次全连接,输出 7 × 7 × 30 7×7×30 7×7×30. 后面接0.5的dropout
  • 7:实际就是将448x448的图片划分为7x7个网格,每个网格负责检测中心落在该网格中的物体。每一个网格预测B个bounding boxes,以及这些bounding boxes的confidence scores。
  • c o n f i d e n c e = P r ( O b j e c t ) ∗ I O U p r e d t r u t h confidence=Pr(Object) * I O U_{pred}^{truth} confidence=Pr(Object)IOUpredtruth,栅格中不存在一个 object,则confidence score应该为0;否则的话,confidence score则为 predicted bounding box与 ground truth box之间的 IOU(intersection over union)。这个 confidence scores反映了模型对于这个栅格的预测:该栅格是否含有物体,以及这个box的坐标预测的有多准。
  • 30:2x(4+1)+20,每个网格支持两个box,1个非背景置信度,20个类别!
  • ① YOLO对每个bounding box有5个predictions:x, y, w, h,
    and confidence。
  • ② 坐标x,y代表了预测的bounding box的中心与栅格边界的相对值。
  • ③ 坐标w,h代表了预测的bounding box的width、height相对于整幅图像width,height的比例
  • ④ confidence就是预测的bounding box和ground truth box的IOU值。
  • ⑤ 每一个栅格还要预测C个 conditional class probability(条件类别概率):Pr(Classi|Object)。即在一个栅格包含一个Object的前提下,它属于某个类的概率。
  • ⑥ conditional class probability信息是针对每个网格的。confidence信息是针对每个bounding box的。
    Pr ⁡ (  Class  i ∣  Object  ) ∗ Pr ⁡ (  Object  ) ∗ IOU ⁡ pred  truth  = Pr ⁡ (  Class  i ) ∗ IOU ⁡ pred  truth  \operatorname{Pr}\left(\text { Class }_{i} | \text { Object }\right) * \operatorname{Pr}(\text { Object }) * \operatorname{IOU}_{\text {pred }}^{\text {truth }}=\\ \operatorname{Pr}\left(\text { Class }_{i}\right) * \operatorname{IOU}_{\text {pred }}^{\text {truth }} Pr( Class i Object )Pr( Object )IOUpred truth =Pr( Class i)IOUpred truth 
    黄金时代 —— 深度学习 (目标检测)_第35张图片

优化目标

  • 问题建模为回归问题
  • 直接通过整张图片的所有像素得到bounding box的坐标、box中包含物体的置信度和class probabilities。通过YOLO,每张图像只需要看一眼就能得出图像中都有哪些物体和这些物体的位置
  • 1、将图像resize到448x448作为神经网络的输入
    2、运行神经网络,得到一些bounding box坐标、box中包含物体的置信度和class probabilities
    3、进行非极大值抑制,筛选Boxes

损失函数

  • 都是平方损失函数,回归

λ c o o r d ∑ i = 0 S 2 ∑ j = 0 B I i j o b j [ ( x i − x ^ x ) 2 + ( y i − y ^ i ) 2 ] + λ c o o r d ∑ i = 0 S 2 ∑ j = 0 B I i j o b j [ ( w i − w ^ i ) 2 + ( h i − h ^ i ) 2 ] + ∑ i = 0 S 2 ∑ j = 0 B I i j o b j ( C i − C ^ i ) 2 + λ n o o b j ∑ i = 0 S 2 ∑ j = 0 B I i j n o o b j ( C i − C ^ i ) 2 + ∑ i = 0 S 2 I i o b j ∑ c ∈  classes  ( p i ( c ) − p ^ i ( c ) ) 2 \lambda_{c o o r d} \sum_{i=0}^{S^{2}} \sum_{j=0}^{B} I_{i j}^{o b j}\left[\left(x_{i}-\hat{x}_{x}\right)^{2}+\left(y_{i}-\hat{y}_{i}\right)^{2}\right]+ \\\lambda_{c o o r d} \sum_{i=0}^{S^{2}} \sum_{j=0}^{B} I_{i j}^{o b j}\left[\left(\sqrt{w}_{i}-\sqrt{\hat{w}_{i}}\right)^{2}+\left(\sqrt{h}_{i}-\sqrt{\hat{h}_{i}}\right)^{2}\right] \\ +\sum_{i=0}^{S^{2}} \sum_{j=0}^{B} I_{i j}^{o b j}\left(C_{i}-\hat{C}_{i}\right)^{2}+\lambda_{n o o b j} \sum_{i=0}^{S^{2}} \sum_{j=0}^{B} I_{i j}^{n o o b j}\left(C_{i}-\hat{C}_{i}\right)^{2} \\ +\sum_{i=0}^{S^{2}} I_{i}^{o b j} \sum_{c \in \text { classes }}\left(p_{i}(c)-\hat{p}_{i}(c)\right)^{2} λcoordi=0S2j=0BIijobj[(xix^x)2+(yiy^i)2]+λcoordi=0S2j=0BIijobj[(w iw^i )2+(h ih^i )2]+i=0S2j=0BIijobj(CiC^i)2+λnoobji=0S2j=0BIijnoobj(CiC^i)2+i=0S2Iiobjc classes (pi(c)p^i(c))2

  • 对于有物体中心落入的 cell, 需要计算分类 loss 和两个 confidence loss,
  • 对于没有物体中心落入的 cell, 只需要计算 confidence loss.
  • 对于两个 bbox loss, 只计算 IOU 较大的 bounding box loss

优点

  • 检测速度快,45fps;FAST YOLO 155fps(YOLOv2在嵌入式设备上)
  • YOLO可以很好的避免背景错误,产生false positives。不像其他物体检测系统使用了滑窗或region proposal,分类器只能得到图像的局部信息。YOLO在训练和测试时都能够看到一整张图像的信息,因此YOLO在检测物体时能很好的利用上下文信息,从而不容易在背景上预测出错误的物体信息。
  • YOLO可以学到物体的泛化特征。

缺点

  • YOLO的物体检测精度低于其他state-of-the-art的物体检测系统。
  • YOLO容易产生物体的定位错误。定位粗糙
  • YOLO对小物体的检测效果不好(尤其是密集的小物体,因为一个栅格只能预测2个物体)。缺少尺度

YOLOv2 & YOLO9000 2016

  • Better / Faster / Stronger
  • 引入了Anchor,不再是Anchor-free检测框架了!

改进

  • 1 BN层:mAP上升2.4!放弃了Dropout,添加了BN层;BN层有助于解决反向传播中的梯度消失和梯度爆炸,降低对一些超参数的敏感(学习率+网格参数大小+激活函数选择)
  • 2 使用高分辨图像fine tuning网络中的分类模型,mAP上升3.7!图像分类的训练样本很多,但是标注了边框的检测定位样本相对小,因为标注边框的人工成本高!所以目标检测网络通常都先用图像分类样本训练卷积层,提取图像特征(图像分类样本的分辨率不是很高)① YOLO v1使用ImageNet的图像分类样本 224x224 训练CNN卷积层。然后在训练对象检测时,采用更高分辨率的 448x448 的图像作为输入。但切换模型对性能有一定影响。② YOLO2采用 224x224 图像进行分类模型预训练后,再采用 448x448 的高分辨率样本对分类模型进行微调(10epoch),使网络特征逐渐适应 448×448 的分辨率。然后再使用 448×448 的检测样本进行训练,缓解了分辨率突然切换造成的影响。
  • 3 采用Anchor Boxes,召回率提升,81%>88%,mAP轻微下降到0.2!在每个grid预先设定一组不同大小和宽高比的边框,来覆盖整个图像的不同位置和多种尺度,这些先验框作为预定义的候选区在神经网络中将检测其中是否存在对象,以及微调边框的位置。同时YOLO2移除了全连接层。另外去掉了一个池化层,使网络卷积层输出具有更高的分辨率。YOLO 利用 anchor 会生成 13 × 13 × 5 = 845 13×13×5=845 13×13×5=845 个候选区域框, 相比于YOLOv1的98( 7 × 7 × 2 7×7×2 7×7×2)个!导致召回率增加但是准确率却稍稍降低了!可能是训练的锅!
  • 4 聚类提取先验框的尺度。采用1-IoU作为相似性度量,采用kmeans作为聚类算法,聚类出k个尺度!
  • 5 Direct location prediction(直接位置预测),约束预测边框的位置。借鉴了Faster R-CNN的先验框方法,如下。预测框的中心 = 先验框的中心 + 学习偏差( t 是 学 习 参 数 t是学习参数 t)。是没有约束的,导致训练早期,预测边框可能会出现在任何位置!不稳定!
    x = ( t x ∗ w a ) + x a y = ( t y ∗ h a ) + y a x=\left(t_{x} * w_{a}\right)+x_{a} \\ y=\left(t_{y} * h_{a}\right)+y_{a} x=(txwa)+xay=(tyha)+ya
    YOLOv2调整了公式,将预测框的中心约束在特定的grid网格内!
    b x = σ ( t x ) + c x b y = σ ( t y ) + c y b w = p w e t w b h = p h e t h P r ( object ) ∗ I O U ( b , object ) = σ ( t o ) b_{x}=\sigma\left(t_{x}\right)+c_{x} \\ b_{y}=\sigma\left(t_{y}\right)+c_{y} \\ b_{w}=p_{w} e^{t_{w}} \\ b_{h}=p_{h} e^{t_{h}} \\ Pr(\text {object}) * I O U(b, \text {object})=\sigma\left(t_{o}\right) bx=σ(tx)+cxby=σ(ty)+cybw=pwetwbh=phethPr(object)IOU(b,object)=σ(to)
    黄金时代 —— 深度学习 (目标检测)_第36张图片
  • 6 Fine-Grained Features,Passthrough层检测细粒度特征。参考了ResNet的恒等连接,将卷积层wxh进行4等分,进行通道拼接和经过卷积核池化的另一分支进行通道拼接!具体是将 26 × 26 × 512 26×26×512 26×26×512 reshape 成 13 × 13 × 2048 13×13×2048 13×13×2048, 然后与原来的 13 × 13 × 1024 13×13×1024 13×13×1024 的特征图谱连接在一起形成 13 × 13 × 3072 13×13×3072 13×13×3072 的特征图谱, 然后在此特征图谱上进行预测! mAP 提升 1%!
    黄金时代 —— 深度学习 (目标检测)_第37张图片
    黄金时代 —— 深度学习 (目标检测)_第38张图片
  • 7 Multi-Scale Training. 提升mAP的1.4!在训练过程中, 每隔一定(10) 的 iterations 之后就随机改变输入图片的大小, 图片大小为一系列 32 倍数的值(因为总的 stride 为 32 32 32): 320 , 352 , … , 608 {320, 352, …, 608} 320,352,,608
  • 8 Darknet-19,DarkNet-19比VGG-16小一些,精度不弱于VGG-16,但浮点运算量减少到约1/5,以保证更快的运算速度。
    黄金时代 —— 深度学习 (目标检测)_第39张图片

训练

  • 对样本的处理方式: 和 YOLOv1 相同, 对于训练图片中的 ground truth, 如果其中心落在了某个 cell 内, 那么该 cell 内的 5 个 anchor 就负责预测该物体, 并且最终只有一个边界框会与之匹配, 其他的会被 NMS 掉. 所以 YOLOv2 同样假定每个 cell 至多含有一个 ground truth, 而在实际上基本不会出现多于一个的情况. 与 gt 匹配的 anchor 会计算坐标误差, 置信度误差, 和分类误差, 而其他的边框则只计算置信度误差.

损失函数

黄金时代 —— 深度学习 (目标检测)_第40张图片
黄金时代 —— 深度学习 (目标检测)_第41张图片
loss ⁡ t = ∑ i = 0 W ∑ j = 0 H ∑ k = 0 A 1 Max  IOU  <  Thresh  λ noobj  ∗ ( − b i j k o ) 2 + 1 t < 12800 λ prior  ∗ ∑ r ϵ ( x , y , w , h ) (  prior  k r − b i j k r ) 2 + 1 k truth  ( λ coord  ∗ ∑ r ϵ ( x , y , w , h ) (  truth  r − b i j k r ) 2 + λ obj  ∗ ( I O U truth  k − b i j k o ) 2 + λ class  ∗ ( ∑ c = 1 c (  truth  c − b i j k c ) 2 ) ) \begin{array}{rl}\operatorname{loss}_{t}=\sum_{i=0}^{W} \sum_{j=0}^{H} \sum_{k=0}^{A} & 1_{\text {Max } \text { IOU }<\text { Thresh }} \lambda_{\text {noobj }} *\left(-b_{i j k}^{o}\right)^{2} \\ & +1_{t<12800} \lambda_{\text {prior }} * \sum_{r_{\epsilon}(x, y, w, h)}\left(\text { prior }_{k}^{r}-b_{i j k}^{r}\right)^{2} \\ & +1_{k}^{\text {truth }}( \lambda_{\text {coord }} * \sum_{r_{\epsilon}(x, y, w, h)}\left(\text { truth }^{r}-b_{i j k}^{r}\right)^{2} \\ & +\lambda_{\text {obj }} *\left(I O U_{\text {truth }}^{k}-b_{i j k}^{o}\right)^{2} \\ & \left.+\lambda_{\text {class }} *\left(\sum_{c=1}^{c}\left(\text { truth }^{c}-b_{i j k}^{c}\right)^{2}\right)\right)\end{array} losst=i=0Wj=0Hk=0A1Max  IOU < Thresh λnoobj (bijko)2+1t<12800λprior rϵ(x,y,w,h)( prior krbijkr)2+1ktruth (λcoord rϵ(x,y,w,h)( truth rbijkr)2+λobj (IOUtruth kbijko)2+λclass (c=1c( truth cbijkc)2))

  • 背景的置信度误差 + 预测框和Anchor的坐标误差(前12800次迭代时计算) + 预测框和GT的匹配损失(后三项:坐标误差+置信度误差+分类误差)

Stronger:YOLO 9000

  • Hierarchical classification(多层分类)
  • YOLO2于是根据WordNet[5],将ImageNet和COCO中的名词对象一起构建了一个WordTree,以physical object为根节点,各名词依据相互间的关系构建树枝、树叶,节点间的连接表达了对象概念之间的蕴含关系(上位/下位关系)
  • VOC数据集可以检测20种对象,但实际上对象的种类非常多,只是缺少相应的用于对象检测的训练样本。YOLO2尝试利用ImageNet非常大量的分类样本,联合COCO的对象检测数据集一起训练,使得YOLO2即使没有学过很多对象的检测样本,也能检测出这些对象。
  • 基本的思路是,如果是检测样本,训练时其Loss包括分类误差和定位误差,如果是分类样本,则Loss只包括分类误差。
  • ImageNet的数据标签来源于WordNet,具有一定层次结构。作者根据WordNet建立了ImageNet标签的树(WordTree)。作者采用标签树训练了Darknet-19.
    黄金时代 —— 深度学习 (目标检测)_第42张图片
    黄金时代 —— 深度学习 (目标检测)_第43张图片
  • 构建好的WordTree有9418个节点(对象类型),包括ImageNet的Top 9000个对象,COCO对象,以及ImageNet对象检测挑战数据集中的对象,以及为了添加这些对象,从WordNet路径中提取出的中间对象
  • 构建WordTree:①检查每一个将用于训练和测试的ImageNet和COCO对象,在WordNet中找到对应的节点,如果该节点到WordTree根节点(physical object)的路径只有一条(大部分对象都只有一条路径),就将该路径添加到WrodTree。②经过上面操作后,剩下的是存在多条路径的对象。对每个对象,检查其额外路径长度(将其添加到已有的WordTree中所需的路径长度),选择最短的路径添加到WordTree。这样就构造好了整个WordTree
  • 既然各节点预测的是条件概率,那么一个节点的绝对概率就是它到根节点路径上所有条件概率的乘积!
  • 但是一般采用贪婪的算法:从根节点开始向下遍历,对每一个节点,在它的所有子节点中,选择概率最大的那个(一个节点下面的所有子节点是互斥的),一直向下遍历直到某个节点的子节点概率低于设定的阈值(意味着很难确定它的下一层对象到底是哪个),或达到叶子节点,那么该节点就是该WordTree对应的对象!

SSD 2016

  • Single Shot MultiBox Detector

简介

  • SSD 是一种 one-stage 检测模型, 它最主要的特点就是使用了多尺度的特征图谱进行 one-stage 的目标检测预测
  • 具体来说, SSD 在 VGGNet 之后又添加了五个卷积段, 每个卷积段都是用 1 × 1 1×1 1×1 3 × 3 3×3 3×3 大小的卷积核组成的, 然后在加上 VGGNet 的 conv4_3 卷积层, 总共可以得到六种不同尺度的特征图谱.
  • 对于每一个特征图谱上的每一个 location, 都会有 k k k 个 default boxes 作为初始的候选框, 不同尺度的特征图谱对应的 k k k 的大小也不相同 ( 4 , 6 , 6 , 6 , 4 , 4 ) (4, 6, 6, 6, 4, 4) (4,6,6,6,4,4). 对于一个尺度为 m × n m×n m×n 的特征图谱来说, 它具有的 default box 的个数就是 m × n × k m×n×k m×n×k, 又因为 one-stage 模型会在回归的同时进行分类, 因此, 最终的输出结果是一个形状为 m × n × k × ( c + 4 ) m×n×k×(c+4) m×n×k×(c+4)的 tesor, k k k就代表了 k k k 个 default box, ( c + 4 ) (c+4) (c+4) 代表了每个 box 的分类得分和坐标偏移量.

黄金时代 —— 深度学习 (目标检测)_第44张图片
黄金时代 —— 深度学习 (目标检测)_第45张图片

  • SSD效果为什么好?多尺度设置了多种宽高比的anchor数据增强 PS:Default Box、Prior box和 Anchor表示同一个意思!
  • PS:Anchor技术的鼻祖是DeepMultiBox(Scalable Object Detection using Deep Neural Networks),这篇论文里首次提出使用prior(先验框),并且提出使用prior做匹配。后面的众多工作,比如RCNN系列,YOLO系列都使用了anchor这个术语,而在SSD中anchor又叫default box,本质上都是表示的同一个东西。

多尺度

黄金时代 —— 深度学习 (目标检测)_第46张图片

  • SSD使用6个不同特征图检测不同尺度的目标。低层预测小目标,高层预测大目标。

为什么设置不同的Anchor

  • SSD300中anchor的数量: ( 38384 + 19196 + 10106 + 556 + 334 + 114 ) = 8732 (38384 + 19196 + 10106 + 556 + 334 + 114) = 8732 (38384+19196+10106+556+334+114)=8732
  • 通过anchor设置每一层实际响应的区域,使得某一层对特定大小的目标响应
  • 为什么在同一个特征图上可以设置多个anchor检测到不同尺度的目标?虽然分类和回归使用的是同一个特征图,但是不同通道的3x3卷积核会学习到那块区域的不同的特征,所以不同通道对应的anchor可以检测到不同尺度的目标。
  • anchor本身不参与网络的实际训练,anchor影响的是classification和regression分支如何进行encode box(训练阶段)和decode box(测试阶段)。测试的时候,anchor就像滑动窗口一样,在图像中滑动,对每个anchor做分类和回归得到最终的结果。
  • 为什么anchor可以设置每一层实际响应的区域呢?
    因为每一层实际响应的区域其实是有效感受野区域,而有效感受野理论表明有效感受野在训练过程中会发生变化,所以每一层实际响应的区域其实是会发生变化的,正是由于有效感受野大小会发生变化,所以可以通过anchor设置每一层实际响应的区域。
  • anchor的匹配。既然anchor是实际响应的区域,训练的时候就需要知道每个anchor的分类和回归的label,如何确定呢?SSD通过anchor与groundtruth匹配来确定label。
  • 在训练阶段,SSD会先寻找与每个anchor的IOU最大的那个ground truth(大于IOU阈值0.5),这个过程叫做匹配。如果一个anchor找到了匹配的ground truth,则该anchor就是正样本,该anchor的类别就是该ground truth的类别,如果没有找到,该anchor就是负样本。
  • 由于现实中的目标会有各种宽高比(比如行人),设置多个宽高比可以检测到不同宽高比的目标。

如何选择anchor的scale和Aspect ratio?

  • 如feature maps数量为 m,每个特征图中的Anchor的Scale,计算公式如下:
    s k = s min ⁡ + s max ⁡ − s min ⁡ m − 1 ( k − 1 ) , k ∈ [ 1 , m ] s_{k}=s_{\min }+\frac{s_{\max }-s_{\min }}{m-1}(k-1), k \in[1, m] sk=smin+m1smaxsmin(k1),k[1,m]
  • 设定 S m i n S_{min} Smin S m a x S_{max} Smax为0.2和0.9,最低到最高层的尺度为0.2~0.9!
  • m = 6 ( 4 , 6 , 6 , 6 , 4 , 4 ) m=6(4,6,6,6,4,4) m=6(4,6,6,6,4,4), 因此就有 s = ( 0.2 , 0.34 , 0.48 , 0.62 , 0.76 , 0.9 ) s=(0.2,0.34,0.48,0.62,0.76,0.9) s=(0.2,0.34,0.48,0.62,0.76,0.9)
  • 设置的宽高比为 1 , 2 , 3 , 1 / 2 , 1 / 3 {1,2,3,1/2,1/3} 1,2,3,1/2,1/3
  • 则每一个default boxes 的width 和height就可以得到( w k a h k a = a r w_{k}^{a} h_{k}^{a}=a_{r} wkahka=ar)
    w k a = s k a r h k a = s k a r w_{k}^{a}=s_{k} \sqrt{a_{r}} \\ h_{k}^{a}=\frac{s_{k}}{\sqrt{a_{r}}} wka=skar hka=ar sk
  • 当aspect ratio为1时,作者还增加一种scale的anchor,尺度为 s k ′ = s k s k + 1 s_{k}^{\prime}=\sqrt{s_{k} s_{k+1}} sk=sksk+1
  • 所以每个feature map上每个点像素对应6个Anchor!
  • 表格中每个宽高比的anchor的实际宽和高需要乘以输入图像的大小,如SSD300,则需要使用上面的数值乘以300得到anchor实际大小。

数据增强

  • 水平翻转, 随机裁剪+颜色扭曲(random crop & color distortion), 随机采集区域块(randomly sample a patch, 目标是为了获取小目标训练样本)

为什么SSD不直接使用浅层的特征图谱, 而非要额外增加卷积层, 这样不是增加模型的复杂度了吗?

  • 理想情况下, SSD 的特征金字塔是从多个卷积层输出的特征图谱得到的, 因此它的计算成本几乎为零. 但是为了避免使用到那些表征能力不强的低阶特征图谱(浅层), SSD 只使用了深层的特征图谱(conv4_3), 同时在 backbone 网络的后面又添加了几层卷积层来提取高表征能力的特征图谱.
  • 但是这样就使得 SSD 错过了那些低阶特征的信息, 这些低阶特征中往往包含了高阶特征不具有的信息, 如小物体的特征信息, 这也是为什么 SSD 对小物体不敏感的原因之一.

缺点

  • SSD主要缺点:SSD对小目标的检测效果一般,作者认为小目标在高层没有足够的信息。1. 增大输入尺寸;2. 使用更低的特征图做检测(比如S3FD中使用更低的conv3_3检测);3. FPN(已经是检测网络的标配了)
  • 关于anchor的设置的优化;YOLOV2中使用聚类的方式初始化anchor

代码

SSD- Pytorch

DSSD 2017

  • 利用反卷积模块向特征图谱中添加更多的上下文信息
    主要是对SSD的一点改进, SSD使用了不同阶段的卷积特征图谱进行目标检测, 而DSSD受到人体姿态识别任务的启发, 将这些不同阶段的卷积特征图谱通过反卷积模块连接起来, 然后再进行目标检测的预测任务.
  • 预测模块采用Residual模块
    这个不算是亮点, 不过也是改动之一, 基本来说就说原始的SSD是直接在特征图谱预测结果并计算损失的, 而DSSD在预测之前会先经过一个Residual模块做进一步的特征提取, 然后在进行预测.

YOLOv3 2018

  • YOLOv3 加入了更多被验证过的有效技术, 使得 YOLO 模型的 mAP 可以与 SSD 系列相媲美, 同时速度依然很快(约为 SSD 的三倍).

改进

  • Bounding Box Prediction: YOLOv3 使用了和 YOLOv2 相同的 bbox 回归策略, 都是预测相对于 cell 左上角的偏移量进行回归. YOLO 中每个 gt box 只会与负责它的 cell 中的一个 anchor box 匹配(IoU 最大), 其他的 anchor box 只会计算 objectness 置信度损失, 而不会计算坐标和分类损失.
  • 类别预测: 由于有的物体可能不止属于一个标签, 如 “人” 和 “女人”. 因此, 为了减少类别之间的对抗性, YOLOv3 没有使用 softmax 计算分类损失, 而是采用了 二值交叉熵来预测类别(非对称概率分布差异).
  • 特征金字塔: 使用类似于 FPN 的方法(upsample and top-down)提取到不同尺度的特征图谱(文中3个), 在每个尺度的特征图谱上的都会预测3 boxes, 因此, 每个尺度的特征图谱的输出为: N × N × [ 3 × ( 4 + 1 + 80 ) ] N×N×[3×(4+1+80)] N×N×[3×(4+1+80)]. 在确定 anchor 大小时, 利用 kmean 聚类选出 9 个聚类中心, 然后划分成 3 个簇分别赋给 3 个尺度的特征图谱.
  • Darknet-53: 使用了残差模块的思想, 提出了层数为 53 的 Darknet-53 网络 (1, 2, 8, 8, 4). 在 ImageNet 上, Darknet-53 is better than ResNet-101 and 1.5x faster. Darknet-53 has similar performance to ResNet-152 and is 2x faster.

黄金时代 —— 深度学习 (目标检测)_第47张图片

  • Focal Loss 对 YOLOv3 不起作用的原因可能是 YOLO 的选框策略和损失函数(objectness+class)使得 YOLO 在背景样本不均衡问题上影响较小.

数据增强

翻转变换 flip
随机修剪 random crop
色彩抖动 color jittering
平移变换 shift
尺度变换 scale
对比度变换 contrast
噪声扰动 noise
旋转变换/反射变换 Rotation/reflection
  • 这里需要注意的是,虽然输入尺寸是416x416,但原图是按照纵横比例缩放至416x416的, 取 m i n ( w / i m g w , h / i m g h ) min(w/img_w, h/img_h) min(w/imgw,h/imgh)这个比例来缩放,保证长的边缩放为需要的输入尺寸416,而短边按比例缩放不会扭曲, i m g w , i m g h img_w,img_h imgw,imgh是原图尺寸768,576, 缩放后的尺寸为 n e w w , n e w h = 416 , 312 new_w, new_h=416,312 neww,newh=416,312,需要的输入尺寸是w,h=416*416(128值填充):
    在这里插入图片描述
  • yolov3需要的训练数据的label是根据原图尺寸归一化了的,这样做是因为怕大的边框的影响比小的边框影响大,因此做了归一化的操作,这样大的和小的边框都会被同等看待了,而且训练也容易收敛。

网络结构

参考

  • YOLOv3
    在这里插入图片描述
  • resn:n代表数字,表示这个res_block里含有多少个res_unit
  • 整个yolo_v3_body包含252层:
    在这里插入图片描述
  • 整个v3结构里面,是没有池化层和全连接层的。前向传播过程中,张量的尺寸变换是通过改变卷积核的步长来实现的,比如stride=(2, 2),这就等于将图像边长缩小了一半(即面积缩小到原来的1/4)
  • 在yolo_v2中,要经历5次缩小,会将特征图缩小到原输入尺寸的1/32。输入为416x416,则输出为13x13(416/32=13)
  • yolo_v3也和v2一样,backbone都会将输出特征图缩小到输入的1/32
  • 通常都要求输入图片是32的倍数
  • 步长2的卷积 + 零填充
    在这里插入图片描述
  • tiny-yolo3
    在这里插入图片描述
  • YOLOv3和tiny-YOLOv3的战绩
    在这里插入图片描述
    在这里插入图片描述

网络输出

在这里插入图片描述

  • 借鉴了FPN的思想
  • yolo v3设定的是每个网格单元预测3个box,所以每个box需要有(x, y, w, h, confidence)五个基本参数,然后还要有80个类别的概率,即3*(5 + 80) = 255(YOLOv1:7x7x30,2*(5+20) = 50)
  • 作者并没有像SSD那样直接采用backbone中间层的处理结果作为feature map的输出,而是和后面网络层的上采样结果进行一个拼接之后的处理结果作为feature map

边框回归

  • YOLOv3使用逻辑回归预测每个边界框的客观得分
  • logistic回归用于对anchor包围的部分进行一个目标性评分(objectness score),即这块位置是目标的可能性有多大。这一步是在predict之前进行的,可以去掉不必要anchor
  • 忽视IoU小的Anchor:If the bounding box prior is not the best but does overlap a ground truth object by more than some threshold we ignore the prediction, following[17]. We use the threshold of 0.5. Unlike [17] our system only assigns one bounding box prior for each ground truth object.
  • 不同于faster R-CNN的是,yolo_v3只会对1个prior进行操作,也就是那个最佳prior。而logistic回归就是用来从9个anchor priors中找到objectness score(目标存在可能性得分)最高的那一个。logistic回归就是用曲线对prior相对于 objectness score映射关系的线性建模
  • 第一点, 9个anchor会被三个输出张量平分的。根据大中小三种size各自取自己的anchor。
  • 第二点,每个输出y在每个自己的网格都会输出3个预测框,这3个框是9除以3得到的,这是作者设置的,我们可以从输出张量的维度来看,13x13x255。255是怎么来的呢,3*(5+80)。80表示80个种类,5表示位置信息和置信度,3表示要输出3个prediction。在代码上来看,3*(5+80)中的3是直接由num_anchors//3得到的
  • 第三点,作者使用了logistic回归来对每个anchor包围的内容进行了一个目标性评分(objectness score)。根据目标性评分来选择anchor prior进行predict,而不是所有anchor prior都会有输出!

网络训练

train_bottleneck.py

  • model, bottleneck_model, last_layer_model
  • model:将网络的输出(3个y)和真值,以及输出数据连起来!一边得到最后的loss层
  • last_layer_model:
    在这里插入图片描述
  • model和last_layer_model是接了loss层的模型!
  • bottleneck_model没有包括yolo_body中的输出前面的卷积层!

bottleneck_model = Model([model_body.input, *y_true], [out1, out2, out3])

  • last_layer_model是相对于yolo_body的bottleneck_model的差集!
model_loss_last =Lambda(yolo_loss, 
output_shape=(1,), name='yolo_loss', arguments={
'anchors': anchors, 
'num_classes': num_classes, 
'ignore_thresh': 0.5})([*model_last.output, *y_true])
last_layer_model = Model([in0,in1,in2, *y_true], model_loss_last)
  • model是包括了损失函数的yolo_body
model_loss = Lambda(yolo_loss, output_shape=(1,), name='yolo_loss',
        arguments={'anchors': anchors, 'num_classes': num_classes, 'ignore_thresh': 0.5})(
        [*model_body.output, *y_true])
model = Model([model_body.input, *y_true], model_loss)
  • 而train.py里面,create_model()只有model,没有将model拆成bottleneck_mdel和last_layer_model!

yolo_loss.py

训练流程

在这里插入图片描述

RefineDet 2018

  • 提出将Two-Stage模型中的思想集成到 One-Stage 框架中, 以此来综合 One-Stage 和 Two-Stage 各自的优势.
  • 将 SSD 的 forward 流程分成了两大部分, 分别是 ARM(Anchor Refinement Module) 和 ODM(Object Detection Module)
  • ARM 的作用有两点: (1), 移除 negative anchors 解决正负样本不均衡问题 (2), 粗略的调整 anchors 的 locations 和 sizes, 为后续的回归器提供更好地初始 anchors
  • ODM 的作用是: 将 ARM refine 后的 anchor 作为输入, 进一步的提升 bbox reg 和 multi-class pred 的精度.
  • 设计思想分别来自于 Two-Stage 网络的 RPN 网络和 Detection 网络.

黄金时代 —— 深度学习 (目标检测)_第48张图片

  • RefineDet 的网络框架设计和 SSD 类似, 首先在常规的 SSD 金字塔特征图谱上(即: conv4_3, conv5_3, conv_fc7, conv6_2) 产生预定义的固定数量的 default boxes, 然后每一个特征图都会共享两个子网络分支, 分别为 anchor 的回归网络和正负样本的二分类预测网络.
  • 这一步产生的负样本置信度高于 0.99 的 anchor 不会传入 ODM 阶段, 起到了预先去除 easy negative examples 的作用.
  • 再利用一个 Transfer Connection Block 结构将 down-top 的特征图谱和 top-down 的特征图谱结合, 并利用 ARM 产生的 anchor 作为输入, 传入 ODM 中进行第二次的 bbox regression 和物体类别的多分类预测.

Transfer Connection Block

黄金时代 —— 深度学习 (目标检测)_第49张图片

  • ARM 的特征图谱经过两个卷积层, top-down 的特征图谱由反卷积层得到, 二者进行 Elementwisz sum 后, 输出的图谱再经过一层卷积, 最终产生在 ODM 中进行预测的特征图谱

RefineDet 使用two-stage 的边框回归过程, 为什么还说它是 one-stage 模型?

  • RefineDet 是 One-Stage 和 Two-Stage 的结合
    虽然有了 anchor refine 的步骤, 但是不想 Faster R-CNN 那样, proposals 的生成和最终 bbox 的预测有很明显的分割, 因此, 勉强算 One-Stage.

YOLOv4 2020

TODO !!!
黄金时代 —— 深度学习 (目标检测)_第50张图片
黄金时代 —— 深度学习 (目标检测)_第51张图片

4 Scale Problem

OverFeat 2014

  • Multi-Scale Classification::
    • 在分类任务上, 虽然训练时采用和AlexNet相同的multi crop方法, 但是在预测阶段没有使用AlexNet的crop投票策略, 而是提出了Multi-Scale Classification方法, 一句话概括就是 对整个图片以不同的尺寸, 并且对每一个location进行模型预测
  • 利用了全卷积的思想代替全连接:降低了滑动窗口的计算代价, 同时支持任意尺寸的图片输入
  • 可以用同一个模型完成分类, 定位, 检测任务:
    • 同一个模型, 只需要用回归层替换分类层, 即可完成目标定位任务, 同时利用了贪心策略来融合最终的定位结果

SPPNet 2015

空间金字塔池化

  • spatial pyramid pooling layer
  • 可以接受任意尺寸的输入图片,并生成固定长度的表征向量
  • 可以进行多尺度的联合训练, 提升模型精度
  • 这种池化方法是比较general的, 可以提升不同模型架构的性能(分类任务)

黄金时代 —— 深度学习 (目标检测)_第52张图片

  • 网络划分粒度 1x1 2x2 4x4 三种;共21 spatial bins
  • 将SPP用于目标检测, 并且提出了先求卷积特征图谱, 然后在特征图谱上取区域的的策略: 大大提升了模型训练和预测的速度(在预测阶段, 比RCNN快24~102倍, 同时取得了更好的精度).
  • 相比于RCNN, SPPNet使用了EdgeBoxes( 0.2s/img )的方法来进行候选区域推荐, 而不是Selective Search( 1∼2s/img )

SPPNet缺点

  • 训练过程是分阶段的(Training is a multi-stage pipeline)
  • 无法 Fine-Tuning 金字塔池化层之前的卷积层
  • 严格说,SPP-Net也可以反向传播,但是会复杂很多,所以用connot update the convolutional layers其实是不算太准确的说法。Ross大神在Fast R-CNN中给出的解释是:SPP训练样本来自不同图像导致反向传播效率低下。知乎回答
  • SPP-Net中fine-tuning的样本是来自所有图像的所有RoI打散后均匀采样的,即RoI-centric sampling,这就导致SGD的每个batch的样本来自不同的图像,需要同时计算和存储这些图像的Feature Map,过程变得expensive.
  • Fast R-CNN采用分层采样思想,先采样出N张图像(image-centric sampling),在这N张图像中再采样出R个RoI,具体到实际中,N=2,R=128,同一图像的RoI共享计算和内存,也就是只用计算和存储2张图像,消耗就大大减少了。

FPN 2017

  • feature pyramid networks

介绍

  • 将最后一层特征图谱进行不断尽快上采样, 并与每一个金字塔阶级的特征图谱进行加法合并操作, 得到新的表征能力更强的不同金字塔层次的特征图谱, 然后将RoI按照尺寸分别映射到这些特征图谱上, 再在每个特征图谱上进行类别和位置预测.
  • 四种获取图像多维度特征组合的方法:
    黄金时代 —— 深度学习 (目标检测)_第53张图片
  • (a) 使用图像金字塔建造特征金字塔,计算不同尺度的特征图过程相互独立 (b) 使用单尺度特征进程检测 © 使用卷积层对多尺度特征层处理 (d) FPN网络结构
    黄金时代 —— 深度学习 (目标检测)_第54张图片
  • element-wise add后使用一个 3x3 的卷积对每一个融合后的图谱进行操作以生成最终的金字塔图谱, 这是为了消除上采样的混叠效应(aliasing effect of unsampling??),
  • 浅层特征负责感知和检测小物体, 但是欠缺足够深度的高级语义信息, 因此将具备深层语义信息的特征层通过反卷积的方式扩大 feature map 的 size, 然后结合浅层和深层的特征图谱来进行预测.
    黄金时代 —— 深度学习 (目标检测)_第55张图片

FPN的使用

  • FPN for RPN:
    对于特征图谱 P 2 , P 3 , P 4 , P 5 , P 6 {P2,P3,P4,P5,P6} P2,P3,P4,P5,P6, 为其分别分配不同大小的 anchors 3 2 2 , 6 4 2 , 12 8 2 , 25 6 2 , 51 2 2 32^2,64^2,128^2,256^2,512^2 322,642,1282,2562,5122, 然后利用一个 共享的 rpn heads 进行训练和预测。原文是共享的
    黄金时代 —— 深度学习 (目标检测)_第56张图片
  • FPN for Fast R-CNN:
    ① 将FPN用于FastRCNN时, 我们需要在不同的金字塔层次上赋予不同尺度的 RoI大小 (因为 RoI pooling 是根据特征图谱和原图的尺寸关系决定的), ② 较小的 RoI, 会被分配到较浅的网络层, 因为太深的图谱可能已经不包含小物体信息了. 注意, detect heads 在所有层级上的权重都是共享的

PANet 2018

  • Path Aggregation Network for Instance Segmentation
  • coco2017 实例分割的冠军
    黄金时代 —— 深度学习 (目标检测)_第57张图片
    黄金时代 —— 深度学习 (目标检测)_第58张图片
  • P2直接copy到N2,然后N2通过步长为2的3*3卷积后分辨率缩小2倍,和P3尺寸一致,然后element-wise 相加。注: 所有channel和FPN中一致P2-P5, N2-N5都是256。

Adaptive Feature Pooling

  • 在FPN那篇论文的解读中我们也讲过FPN从P2-P6(P6仅用作生成proposal,不用作RoIPooling时提取特征)多尺度地生成proposal,然后做RoIPooling时会根据proposal的大小将它分配到不同的level去crop特征,小的proposal去low-level的层,大的proposal去high-level的层。
  • 尽管P2-P5(N2-N5)已经融合了low-level和high-level的特征,然后它们的主要特征还是以它本有的level为主 这时如果小的proposal能从high-level层获取到更多的上下文语义信息是有利于它分类的,而大的proposal能从low-leve层获取到更好的细节是有利于它定位的。
  • 因此本文打算每个proposal从所有level的特征上做RoIPooling,然后在后面融合,融合的阶段和方式都可实验,比如分类时是两个fc,这个融合阶段可以是fuse, fc1, fc2或者fc1, fuse, fc2,融合策略可是sum也可以是max,最后证明fc1, fuse, fc2和max最好。当然这个改进是增加了一些运算负担。
  • 因为我们后续融合时不是要做max融合嘛,这样我们可以看看到底是哪个level被选中了,这样计算一些比例。从下图可以到蓝色代表小的proposal,以此类推黄色是最大的proposal,我们以小的proposal为例,发现,只有30%确实是在最低的level被选中了,但是70%的feature值选中了更高的level。而最大的proposal,有超过50%的feature值选中了低的level(这也说明了给最高层加低层特征的必要性,即Bottom-up Path Augmentation的必要性)。
    黄金时代 —— 深度学习 (目标检测)_第59张图片

Fully-connected Fusion

  • Mask RCNN中Mask分支就是个简版的fcn,fcn是全卷积网络,它根据一个局部的视野域来预测,且参数是全图共享,而全连接fc是全图视野域对位置更敏感,看得更大,这一点sunjian老师的large kernel也间接证明了大视野域的作用。因此本文打算多加一条用全连接层预测的支路来做mask预测,然后和fcn融合,具体做法如下图所示,至于conv4_fc接在fcn支路哪一个卷积后后面消融实验有做对比,conv3后面结果更好一点。

M2Det 2018

动机

  • M2Det 从特征金字塔的构建角度出发, 认为现有的 sota 的特征金字塔的构建方式存在两个缺点:
  • ① 直接简单利用了 backbone 网络固有的金字塔式的特征图谱来构建, 但这些 backbone 实际上是针对分类任务设计的, 因此不足以表示针对目标检测任务的特征;
  • ② 构建的金字塔中每一个尺度的特征仅仅来自于 backbone 中单一层级(level)的特征. 这样一来, 小尺度的特征图谱往往缺少浅层低级的语义信息, 而大尺度的特征图谱又缺少深层的高级语义信息
  • 因此, 作者就提出了融合多个层级特征的 MLFPN (multi-level FPN). MLFPN 主要由三个模块组成:1 特征融合模块(Feature Fusion Module, FFM), 2 简化的 U-shape 模块(Thinned U-shape Module, TUM), 以及3 尺度特征聚合模块(Scale-wise Feature Aggregation Module, SFAM)

结构

  • ① FFMv1 融合了 backbone 网络中浅层和深层的特征来生成 base feature, 具体来说就是 VGGNet 的 conv4_3 和 conv5_3. ② 若干个 TUMs 和 FFMv2 交替连接. 具体的说, 每一个 TUM 都会生成多个不同尺度的 feature maps.
  • ③ FFMv2 融合了 base feature 和前一个 TUM 输出个最大的 feature map. 融合后的 feature maps 会被送到下一个 TUM 中.
  • ④ SFAM 会通过按照尺度划分的特征连接操作(scale-wise feature concatenation operation) 和 通道注意力机制(channel-wise attention mechanism)来聚集 multi-level multi-scale features, 形成最终的特征金字塔结构. 最后用两个卷积层在特征金字塔上分别进行回归和分类预测. 可以看出, 整体的流程和 SSD 类似, 不同之处就在于特征金字塔的构建方式.

黄金时代 —— 深度学习 (目标检测)_第60张图片
黄金时代 —— 深度学习 (目标检测)_第61张图片
黄金时代 —— 深度学习 (目标检测)_第62张图片

RFBNet 2018

  • RFBNet 从感受野的角度出发, 提出了利用空洞卷积(Dilated Conv)来构建 RFB(Receptive Field Block), 其核心思想是利用具有不同空洞间距的 Dilated Conv 来集成不同范围的感受野, 以便可以在同一个特征图谱上获得更大尺度范围内的信息
  • 其实,RFBNet就是将Inception和ASPP进行了融合,达到更好的效果。如下图所示,使用空洞卷积来控制离心率,得到不同的感受野大小。

RFB

  • RFB-Net在inception的基础上加入了dilated卷积层,增大了感受野,增强网络特征提取能力。
  • 两个组件:具有不同卷积核的多分支卷积层以及随后的膨胀卷积层。微调RFB参数主要就是调节RFB模块中卷积核kernel size以及膨胀因子rate(内核元素间的距离)。
    黄金时代 —— 深度学习 (目标检测)_第63张图片
    黄金时代 —— 深度学习 (目标检测)_第64张图片
  • RFB 与其他具有多尺度感受野的工作之间的区别

黄金时代 —— 深度学习 (目标检测)_第65张图片

  • RFB Net Detection Architecture

黄金时代 —— 深度学习 (目标检测)_第66张图片

TridentNet 2019

  • RFBNet 虽然考虑了 Dilated Conv, 但是它的 RFB 结构较为复杂, 带来的计算量也很高. 为此, TridentNet 从不同的角度来使用 Dilated Conv
  • TridentNet 从不同的角度来使用 Dilated Conv, 对于目标检测问题来说, 不同的特征图谱感受野对于不同的物体尺寸, 其检测效果是不同的, 简单来说, 更大的 receptive field 对于大物体的性能会更好, 更小的 receptive filed 对于小物体的性能会更好. 因此, TridentNet 提出了 Trident Block 结构
  • 不同尺度物体的检测性能和dilation rate正相关!
  • Scale Variations比较
    黄金时代 —— 深度学习 (目标检测)_第67张图片

创新点

  • 构造了多路并行的 Trident 分支, 每个 Trident Block 分支具有不同的感受野大小, 用于检测不同尺度的物体
  • 在每一个 Trident Block 当中, 它们的参数是共享的, 区别仅在于去感受野的大小不同;
  • 对于每一个 branch, 训练和测试都只负责一定尺度范围内的样本(借鉴 SNIP), 这样避免了极端 scale 对检测性能的影响.
  • PS:实际上, 在测试阶段,我们可以只保留一个branch来近似完整TridentNet的结果,后面我们做了充分的对比实验来寻找了这样single branch approximation的最佳setting,一般而言,这样的近似只会降低0.5到1点map,但是和baseline比起来不会引入任何额外的计算和参数。

Trident Block

  • 构造了多路并行的 Trident 分支, 每个 Trident Block 分支具有不同的感受野大小, 用于检测不同尺度的物体
  • 在每一个 Trident Block 当中, 它们的参数是共享的, 区别仅在于去感受野的大小不同;
  • 对于每一个 branch, 训练和测试都只负责一定尺度范围内的样本(借鉴 SNIP), 这样避免了极端 scale 对检测性能的影响.

网络结构

黄金时代 —— 深度学习 (目标检测)_第68张图片
黄金时代 —— 深度学习 (目标检测)_第69张图片

5 形变卷积

Deformable ConvNets V1 2017

  • DCN v1
  • Deformable convolution:对于可形变卷积来说, 通过在每个卷积核的采样点上加一个偏移量来达到更好的采样效果
  • Deformable ROI:对于可形变 RoI pooling 来说, 通过对传统的 RoI bins 添加一个偏移量还使得 RoI pooling 中的窗口具有能够适应几何形变的效果
  • Deformable ConvNet 一个比较好的性质就是它不会对原有检测模型的整体结构进行更改, 也不会增加过多的计算量, 因此可以相对容易的添加到现有的检测模型当中, 同时还可以和其他多种提升精度的 trick 叠加使用.

Deformable Convolution

黄金时代 —— 深度学习 (目标检测)_第70张图片

  • 在原始特征图谱平面的每一个location上都生成一组与卷积核采样点个数相关的偏移量,这个偏移量就是在算卷积的适合,按照当前位置进行偏移后再得到加权和
  • 对于一个形状为 W × H × C W×H×C W×H×C 的特征图谱, 我们将其作为输入, 添加一个新的卷积层, 这个卷积层的输出形状为 W × H × 2 N W×H×2N W×H×2N N = W k × H k N=W^k×H^k N=Wk×Hk 代表卷积核平面上的采样点个数, N = ∣ R ∣ , R = { ( − 1 , − 1 ) , ( − 1 , 0 ) , … , ( 0 , 1 ) , ( 1 , 1 ) } N = \vert R \vert, R = \{ (-1, -1), (-1, 0), …, (0, 1), (1, 1)\} N=R,R={(1,1),(1,0),,(0,1),(1,1)}
  • 在计算某一个的输出值时, 会根据 offset filed 的偏移量来决定新的采样点 position
  • 利用双线性插值重新计算每个 position 的值(因为加了偏移量后的新 position 是浮点的, 所以利用双线性插值根据四角的值来算这个 position 的值)
  • 得到所有 position 的值后, 就利用卷积计算得到该点的结果.
  • 对所有的卷积计算都执行类似的过程, 直到结算结束.

Deformable RoI Pooling

黄金时代 —— 深度学习 (目标检测)_第71张图片

  • 先利用普通的 RoI Pooling 得到池化后 k × k k×k k×k 大小的特征图谱
  • 利用一个 FC 层生成归一化的 offsets
  • 将 offsets 应用到对应的 bins 中得到新的像素值.

Deformable Ps RoI Pooling

黄金时代 —— 深度学习 (目标检测)_第72张图片

Deformable ConvNets V2 2018

  • DCN v2

动机

  • DCNv1 虽然可以适应不同几何形变的物体形状, 但是它存在的问题就是学习到的 offsets 不可控, 进而导致引入了过多的 context, 而这些 context 对于网络来说属于干扰信息, 是有害的

改进

  • 增加了更多的 Deformable Convolution
    DCNv1 中只有 ResNet C5 中有 Deformable Conv(共 3 个), 而在 DCNv2 中把 C3~C5 的 3x3 conv 都换成了 Deformable Conv(共 12 个)
  • 让 Deformable Conv 不仅能够学习 offsets, 还能够学习每个采样点的权重, 就是文中的 modulation 机制
    在 DCNv1 中, Deformable Conv 只学习 offset;而在 DCNv2 中, 还加入了对每个采样点的权重 Δ m k Δm_k Δmk,即Modulation机制!对采样点值的选取起到了更灵活的规范作用, 对于一些不想要的采样点,学习到权值为0就行!
    y ( p ) = ∑ k = 1 K w k ⋅ x ( p + p k + Δ p k ) y ( p ) = ∑ k = 1 K w k ⋅ x ( p + p k + Δ p k ) ⋅ Δ m k y(p)=\sum_{k=1}^{K} w_{k} \cdot x\left(p+p_{k}+\Delta p_{k}\right) \\ y(p)=\sum_{k=1}^{K} w_{k} \cdot x\left(p+p_{k}+\Delta p_{k}\right) \cdot \Delta m_{k} y(p)=k=1Kwkx(p+pk+Δpk)y(p)=k=1Kwkx(p+pk+Δpk)Δmk
  • 利用知识蒸馏(feature mimicking) 让 DCNv2 来模拟 R-CNN 的 feature. 有论文指出将 R-CNN 和 Faster R-CNN 的 Classification score 结合起来可以提升 performance, 说明 R-CNN 学到的 focus 在物体上的 feature 可以解决 redundant context 的问题. 但是增加的额外计算会使得 inference 速度慢很多, 因此 DCNv2 的解决方法就是让 R-CNN 当做 teacher network, 让 DCNv2 的 RoI Pooling 之后的 feature 去模拟 R-CNN 的feature.
    黄金时代 —— 深度学习 (目标检测)_第73张图片

细节 链接

6 样本困难和不平衡

OHEM 2016

  • Online Hard Example Mining
  • 作者根据每个 RoI 的 loss 的大小来决定哪些是难样例, 哪些是简单样例, 通过这种方法, 可以更高效的训练网络, 并且可以使得网络收敛到更好的解. 同时, OHEM 还具有以下两个优点:
    • 消除Fast R-CNN系列模型中的一些不必要这参数.这些参数大多都是为了解决难样例问题服务的, 在使用 OHEM 以后, 不仅无需在对这些超参数进行调优, 同时还能获得更好的性能表现.
    • OHEM算法可以与其他多种提升模型精度的trick相结合, 对于大多数模型(R-CNN系列), 在使用了OHEM以后, 都能够获得精度上的提高, 可以看做是一种普适性的提升精度的方法.
  • 文章提出两个可以对难分类样本的方法:
    • ① 明显的方法:直接修改损失层,然后直接进行hard example selection;损失层计算所有的RoIs,然后按损失从大到小排序,当然这里有个NMS(非最大值抑制) 操作,选择hard RoIs并non-hard RoIs的损失置0,会导致效率低下,为每个ROI分配内存并反传,即使某些ROI权值为0
    • ② 更好的方法就采用下图的方法,给出两个RoI模块,一个是只读模块,Forward传播获取所有ROI的损失,进行Hard ROI Sample,再传到可读写的下面红色RoI模块中,正反传更新网络!这个方式和第一种方式在内存空间是差不多的,但第二种方式的速度快 了两倍
    • 提出两个ROI模块,减少无效运算
      黄金时代 —— 深度学习 (目标检测)_第74张图片

RetinaNet 2017

Focal Loss

  • one stage 方法由于没有对候选框的预先过滤策略, 因此后产生大量的 easy negative examples(易分类的背景区域), 这些 easy negative examples 由于数量众多, 最终会对loss有很大的贡献, 从而导致优化的时候过度关注这些 easy examples, 这样会收敛到一个不够好的结果.

黄金时代 —— 深度学习 (目标检测)_第75张图片

  • Focal Loss 正是为了解决这个问题而提出的, 它的核心思想非常直接, 就是在优化的过程中抑制这些 easy (negative) examples 对最终 Loss 的贡献程度. 所谓 easy example 指的就是那些预测概率与真实概率十分相近的样本. 这些样本已经被网络很容易的正确分类了, 所以应该适当减少他们的loss以降低他们对参数更新的影响程度. 以下面的公式为例, 当真实标签为1时, 如果预测概率(假设二分类) p p p 接近于1, 则此样本是easy样本, 因此, 前面的 ( 1 − p ) γ (1−p)^γ (1p)γ , 就会非常小, 起到了抑制易分类样本的作用.
    黄金时代 —— 深度学习 (目标检测)_第76张图片
    F L ( p ) = { − ( 1 − p ) γ log ⁡ ( p )  当  y = 1 − p γ log ⁡ ( 1 − p )  当  y = 0 F L(p)=\left\{\begin{array}{ll}-(1-p)^{\gamma} \log (p) & \text { 当 } y=1 \\ -p^{\gamma} \log (1-p) & \text { 当 } y=0\end{array}\right. FL(p)={(1p)γlog(p)pγlog(1p)  y=1  y=0
  • γ γ γ 的值越大, 则 easy example 对于loss的贡献程度越小, 当 γ = 0 γ=0 γ=0 时, 会退化成普通的交叉熵函数.
  • 实际中在使用 γ γ γ 参数的情况下, 还会用了另一个参数 α α α , 该参数的主要作用是调节正负样本的权重, α α α 的取值通常会根据正负样本的实际比例决定, 如下所示:

F L ( p ) = { − α ( 1 − p ) γ log ⁡ ( p )  当  y = 1 − ( 1 − α ) p γ log ⁡ ( 1 − p )  当  y = 0 F L(p)=\left\{\begin{array}{ll}-\alpha(1-p)^{\gamma} \log (p) & \text { 当 } y=1 \\ -(1-\alpha) p^{\gamma} \log (1-p) & \text { 当 } y=0\end{array}\right. FL(p)={α(1p)γlog(p)(1α)pγlog(1p)  y=1  y=0

  • 下图可以看出, 对于易分类样本来说, 绝大部分都是负样本, 因为 FocalLoss 对于正样本的抑制效果并不明显, 而对负样本的抑制效果会随着 γ γ γ 参数的升高而迅速增大

Focal Loss中的参数作用

  • γ γ γ 决定了对 easy example 的抑制力度, 当 γ γ γ 的值越大时, 则 easy example 对于 loss 的贡献程度就越小, 而 γ γ γ的值为 0 时, 相对于不施加抑制, Focal Loss 推退化成普通的交叉熵函数
  • α α α 的作用是解决正负样本不平衡问题, α α α的取值通常会根据正负样本的实例比例决定. 对于样本数量较多的类别, 其对应的权重应该较小. 但是当 α α α 参数和 γ γ γ 参数共同使用时, 则需要综合考虑样本的数量和 γ γ γ 的取值
  • 二者关系: ① 当 γ γ γ 取值较小时, FocalLoss 对易分类样本的抑制力度不强, 此时由于存在大量的易分类负样本, 因此 α α α 应当对正样本施加更多的权重( γ γ γ=0 时, α α α 最优值为 0.75). ② 当 γ γ γ取值较大时 Focal Loss 对易分类负样本抑制力度变强, 使得原本在数量上不在优势的前景区域或许在对 loss 的贡献度上反超了背景区域, 因此, 需要对前景区域赋予更低的权重 γ γ γ=2 时, α α α 最优值为 0.25 (最优组合).
    黄金时代 —— 深度学习 (目标检测)_第77张图片

R-CNN 系列为什么不用 Focal Loss 进行优化

  • 因为 Focal Loss 主要解决的问题是正负样本框的极度不均衡问题.
  • 而 Two-Stage 网络, 通常会利用 RPN 和启发式的采样算法来过滤掉大量的负样本, 因而不存在正负样本不均衡的问题:
    • RPN 会将物体候选框的数量降低很多, 移除了大量的易分类负样本(背景框) γ γ γ
    • 采用了 biased-minibatch 的采样策略, 比如, 保证正样本和负样本的比例为 1:3 进行训练 α α α

损失函数

在这里插入图片描述

  • p i : p_{i}: pi: Anchor[i]的预测分类概率
    Anchor[i]是正样本时, p i ∗ = 1 p_{i}^{*}=1 \quad pi=1; Anchor[i]是负样本时, p i ∗ = 0 \quad p_{i}^{*}=0 pi=0
    黄金时代 —— 深度学习 (目标检测)_第78张图片
    黄金时代 —— 深度学习 (目标检测)_第79张图片

网络结构

  • RetinaNet 由一个backbone网络和两个子网络组成。backbone网络是现成的,主要负责计算卷积特征图谱。① 第一个子网络负责物体分类任务,② 第二个子网络负责bounding box回归任务,它们都是在backbone网络输出的卷积图谱上进行计算的

黄金时代 —— 深度学习 (目标检测)_第80张图片

  • Backbone: RetinaNet 选用 FPN 作为 backbone 网络, 对P3到P7使用了不同大小的anchors.
  • 分类子网: 由一个简单的 FCN 组合, 分类子网的参数在所有的金字塔层级都是共享的,对于规定的金字塔层级, 其输出的特征图谱的通道数为 C C C, 则分类子网由 4 个 3 x 3 3x3 3x3 的卷积层构成, 每个卷积层的输入输出通道数都是 C C C, 尺寸大小维持不变, 并且均使用 R e L U ReLU ReLU 作为激活函数, 最后接上一层输出通道数为 K A KA KA 3 x 3 3x3 3x3 卷积, 并利用 Sigmoid 激活函数在每个 location 上输出 K A KA KA 的二值预测概率. 通常情况下 C = 256 , A = 9 C=256,A=9 C=256,A=9
  • 回归子网: 整体结构与分类子网相同, 唯一区别在于最后一层卷积层的输出通道数变成了 4 A 4A 4A, 因为它要在每一个 location 上为每一个 anchor 预测 4 4 4 个坐标偏移量. 回归子网的参数在金字塔层级上也是共享的. 注意, 从回归子网的通道数就可以看出, RetinaNet 的 bbox 预测是类别不可知(class-agnostic)的
    黄金时代 —— 深度学习 (目标检测)_第81张图片

GHM 2019

  • Gradient Harmonized Single-stage Detector
  • 易分样本(即,置信度高的样本)对模型的提升效果非常小,模型应该主要关注与那些难分样本(这个假设是有问题的,是GHM的主要改进对象)
  • 但是当难分样本点是离群点的时候,就会起反作用!!!
  • 并且 α 和 γ \alpha和\gamma αγ需要手工联合确定
  • 采用gradient harmonizing mechanism改进难易样本的不均匀,提出Focal loss容易受到离群点的影响(难分但是离群),过度的关注于难分离群点会导致模型的泛化能力下降!
    黄金时代 —— 深度学习 (目标检测)_第82张图片
  • Focal Loss是从置信度p的角度入手衰减loss,而GHM是一定范围置信度p的样本数量的角度衰减loss
  • 梯度模长 g g g: g = torch.abs(pred.sigmoid().detach() - target)
    g = ∣ p − p ∗ ∣ = { 1 − p ,  if  p ∗ = 1 p ,  if  p ∗ = 0 g=\left|p-p^{*}\right|=\left\{\begin{aligned} 1-p, & \text { if } & p^{*}=1 \\ p, & \text { if } & p^{*}=0 \end{aligned}\right. g=pp={1p,p, if  if p=1p=0
  • p是模型预测的概率, p ∗ p^* p是 ground-truth的标签, p ∗ p^* p的取值为0或1.
  • 为啥叫梯度模长:
    L C E = { − log ⁡ ( p ) ,  if  p ∗ = 1 − log ⁡ ( 1 − p ) ,  if  p ∗ = 0 p = sigmoid ⁡ ( x ) ∂ p ∂ x = p ( 1 − p ) ∂ L C E ∂ x = ∂ L C E ∂ p ∂ p ∂ x = { p − 1 ,  if  p ∗ = 1 p ,  if  p ∗ = 0 = p − p ∗ g = ∣ ∂ L C E ∂ x ∣ L_{C E}=\left\{\begin{aligned} -\log (p), & \text { if } \quad p^{*}=1 \\ -\log (1-p), & \text { if } \quad p^{*}=0 \end{aligned}\right. \\ p=\operatorname{sigmoid}(x) \\ \frac{\partial p}{\partial x}=p(1-p) \\ \frac{\partial L_{C E}}{\partial x}=\frac{\partial L_{C E}}{\partial p} \frac{\partial p}{\partial x} =\left\{\begin{array}{rll} p-1, & \text { if } \quad p^{*}=1 \\ p, & \text { if } \quad p^{*}=0 \end{array}=p-p^{*}\right.\\ g=\left|\frac{\partial L_{C E}}{\partial x}\right| LCE={log(p),log(1p), if p=1 if p=0p=sigmoid(x)xp=p(1p)xLCE=pLCExp={p1,p, if p=1 if p=0=ppg=xLCE
  • 如下图,难分类样本的梯度模长 大且样本数量也不少,所以像FL那样过度关注于这些样本降低了模型的精度!
    黄金时代 —— 深度学习 (目标检测)_第83张图片
  • GHM思想:易分样本和特别难分的样本都不应该过多的关注

梯度密度公式

  • 按照上述思路,需要同时衰减易分类和极难分类的样本,采用谁的数量多就衰减谁的思路进行!
  • 通过定义 样本在单位梯度范围内的数量,表示数量多少的概念!
  • 梯度密度:单位梯度模长g部分的样本个数 (差分函数)
    G D ( g ) = 1 l ε ( g ) ∑ k = 1 N δ ε ( g k , g ) δ ϵ ( x , y ) = { 1  if  y − ϵ 2 < = x < y + ϵ 2 0  otherwise  l ϵ ( g ) = min ⁡ ( g + ϵ 2 , 1 ) − max ⁡ ( g − ϵ 2 , 0 ) \begin{array}{l} G D(g)=\frac{1}{l_{\varepsilon}(g)} \sum_{k=1}^{N} \delta_{\varepsilon}\left(g_{k}, g\right) \\ \delta_{\epsilon}(x, y)=\left\{\begin{array}{ll} 1 & \text { if } y-\frac{\epsilon}{2}<=xGD(g)=lε(g)1k=1Nδε(gk,g)δϵ(x,y)={10 if y2ϵ<=x<y+2ϵ otherwise lϵ(g)=min(g+2ϵ,1)max(g2ϵ,0)
  • 其中: δ ε ( g k , g ) \delta_{\varepsilon}\left(g_{k}, g\right) δε(gk,g) 表明了样本1 ∼ N \sim \mathrm{N} N 中, 梯度模长分布在 ( g − ε 2 , g + ε 2 ) \left(g-\frac{\varepsilon}{2}, g+\frac{\varepsilon}{2}\right) (g2ε,g+2ε) 范围内的样本个数, l ϵ ( g ) l_{\epsilon}(g) lϵ(g) 代表了 ( g − ε 2 , g + ε 2 ) \left(g-\frac{\varepsilon}{2}, g+\frac{\varepsilon}{2}\right) (g2ε,g+2ε) 区间的长度。

GHM-C 损失函数

  • 将其的倒数作为交叉熵的加权!用于分类:
  • 定义权重:
    β i = N G D ( g i ) \beta_{i}=\frac{N}{G D\left(g_{i}\right)} βi=GD(gi)N
  • 得到改进的分类损失函数:
    L G H M − C = 1 N ∑ i = 1 N β i L C E ( p i , p i ∗ ) = ∑ i = 1 N L C E ( p i , p i ∗ ) G D ( g i ) L_{G H M-C}=\frac{1}{N} \sum_{i=1}^{N} \beta_{i} L_{C E}\left(p_{i}, p_{i}^{*}\right)=\sum_{i=1}^{N} \frac{L_{C E}\left(p_{i}, p_{i}^{*}\right)}{G D\left(g_{i}\right)} LGHMC=N1i=1NβiLCE(pi,pi)=i=1NGD(gi)LCE(pi,pi)
  • 代码:算10个区域
class GHMC(nn.Module):
    def __init__(self, bins=10, ......):
        self.bins = bins
        edges = torch.arange(bins + 1).float() / bins
......
>>> edges = tensor([0.0000, 0.1000, 0.2000, 0.3000, 0.4000, 
                  0.5000, 0.6000, 0.7000, 0.8000,0.9000, 1.0000])
# 计算梯度模长
g = torch.abs(pred.sigmoid().detach() - target)
# n 用来统计有效的区间数。
# 假如某个区间没有落入任何梯度模长,密度为0,需要额外考虑,不然取个倒数就无穷了。
n = 0  # n valid bins
# 通过循环计算落入10个bins的梯度模长数量
for i in range(self.bins):
    inds = (g >= edges[i]) & (g < edges[i + 1]) & valid
    num_in_bin = inds.sum().item()
    if num_in_bin > 0:
        # 重点,所谓的梯度密度就是1/num_in_bin
        weights[inds] = num_labels / num_in_bin 
        n += 1
if n > 0:
    weights = weights / n
# 把上面计算的weights填到binary_cross_entropy_with_logits里就行了
loss = torch.nn.functional.binary_cross_entropy_with_logits(
    pred, target, weights, reduction='sum') / num_labels
  • 抑制的效果
    黄金时代 —— 深度学习 (目标检测)_第84张图片

GHM-R 损失函数

  • 认为Smooth L1回归函数在输出和真值差值大于阈值时梯度都变成了1,就无法根据梯度来估计一些example输出贡献度,作者就提出了一个函数:
    A S L 1 ( d ) = d 2 + μ 2 − μ ; ∂ A S L 1 ∂ d = d d 2 + μ 2 A S L_{1}(d)=\sqrt{d^{2}+\mu^{2}}-\mu; \frac{\partial A S L_{1}}{\partial d}=\frac{d}{\sqrt{d^{2}+\mu^{2}}} ASL1(d)=d2+μ2 μdASL1=d2+μ2 d
  • 超参数 μ = 0.02 \mu = 0.02 μ=0.02,定义Gradient Norm为 ∣ d d 2 + μ 2 ∣ |\frac{d}{\sqrt{d^{2}+\mu^{2}}}| d2+μ2 d,其和Gradient Norm样本分布如下:
    黄金时代 —— 深度学习 (目标检测)_第85张图片
  • 和分类采用类似的权重定义方法:
    L G H M − R = 1 N ∑ i = 1 N β i A S L 1 ( d i ) = ∑ i = 1 N A S L 1 ( d i ) G D ( g r i ) \begin{aligned} L_{G H M-R} &=\frac{1}{N} \sum_{i=1}^{N} \beta_{i} A S L_{1}\left(d_{i}\right) \\ &=\sum_{i=1}^{N} \frac{A S L_{1}\left(d_{i}\right)}{G D\left(g r_{i}\right)} \end{aligned} LGHMR=N1i=1NβiASL1(di)=i=1NGD(gri)ASL1(di)
  • 这里GHM-R中并没有对easy example做抑制,作者认为,在目标框的回归阶段,easy examples同样能够对提升框回归的准确性带来帮助。

Generalized Focal Loss 2020

  • Generalized Focal Loss: Learning Qualified and Distributed Bounding Boxes for Dense Object Detection
  • 知乎 - 作者的介绍
    黄金时代 —— 深度学习 (目标检测)_第86张图片
  • 作者针对的是 One-stage,Head包含三个内容:① 分类 ② 检测 ③ 检测框的评分( FCOS+ATSS用的是centerness,其他某些工作使用IoU,范围都是0~1之间 )

问题

  • classification score 和 IoU/centerness score不一致:
    • ① 两个指标各自训练,测试的时候是乘在一起作为NMS score排序的依据,这肯定是存在gap的,不是end-to-end
    • ② 两个指标作用不同对象:
      • focal loss针对大量负样本+少量正样本,边框得分针对的是正样本;
      • 那在NMS的时候,对于负样本的边框得分可能会分到一个未定义的值,使得NMS排序变为一个未定义行为;
      • 出现一个坏情况就是:一个分类score相对低的真正的负样本,由于预测了一个不可信的极高的质量score,而导致它可能排到一个真正的正样本(分类score不够高且质量score相对低)的前面
  • bbox回归采用的表示不够灵活!不能处理复杂情况:目前的边界回归都得建立一个狄拉克分布,这个不够符合复杂情况,比如被模糊的边界!
    在这里插入图片描述

提出的方法

  • 第一个分类得分和边框得分的Gap问题:联合
  • 第二个边界分布的不鲁棒问题:替换,用softmax分布替换狄拉克方程

联合

  • 将分类得分和边框联合,① 之前Focal Loss是为one-stage的检测器的分类分支服务的,支持0或者1这样的离散类别label。② 然而对于我们的分类-质量联合表示,label却变成了0~1之间的连续值
  • 为此将Focal Loss修改为:Quality Focal Loss (QFL)
    F L ( p ) = − ( 1 − p t ) γ log ⁡ ( p t ) , p t = { p ,  when  y = 1 1 − p ,  when  y = 0 Q F L ( σ ) = − ∣ y − σ ∣ β ( ( 1 − y ) log ⁡ ( 1 − σ ) + y log ⁡ ( σ ) ) \mathbf{F L}(p)=-\left(1-p_{t}\right)^{\gamma} \log \left(p_{t}\right), p_{t}=\left\{\begin{aligned} p, & \text { when } y=1 \\ 1-p, & \text { when } y=0 \end{aligned}\right. \\ \mathbf{Q F L}(\sigma)=-|y-\sigma|^{\beta}((1-y) \log (1-\sigma)+y \log (\sigma)) FL(p)=(1pt)γlog(pt),pt={p,1p, when y=1 when y=0QFL(σ)=yσβ((1y)log(1σ)+ylog(σ))
  • 其中,y为0~1的质量标签 σ \sigma σ为预测。 soft label是预测的框和对应gt的框计算iou得到的,是一种动态的label
    • 注意QFL的全局最小解即是 σ = y \sigma = y σ=y
    • 这样交叉熵部分变为完整的交叉熵,同时调节因子 ∣ y − σ ∣ β |y-\sigma|^{\beta} yσβ变为距离绝对值的幂次函数。
    • 和Focal Loss类似,我们实验中发现一般取 β = 2 \beta = 2 β=2为最优。

替换

  • 如果分布过于任意,网络学习的效率可能会不高,原因是一个积分目标可能对应了无穷多种分布模式
    黄金时代 —— 深度学习 (目标检测)_第87张图片
  • 考虑到真实的分布通常不会距离标注的位置太远,所以我们又额外加了个loss,希望网络能够快速地聚焦到标注位置附近的数值,使得他们概率尽可能大。
  • 涉及到如何从狄拉克分布的积分形式推导到一般分布的积分形式来表示框,详情:一般把边框回归模型的常用方法是把边框标签y(1,非边界区域0)看作Dirac分布 δ ( x − y ) \delta(x-y) δ(xy),这个分布满足: ∫ − ∞ + ∞ δ ( x − y ) d x = 1 \int_{-\infty}^{+\infty} \delta(x-y) \mathrm{d} x=1 +δ(xy)dx=1(通过FC层计算),一般这个标签y的积分形式为: y = ∫ − ∞ + ∞ δ ( x − y ) x d x y=\int_{-\infty}^{+\infty} \delta(x-y) x \mathrm{d} x y=+δ(xy)xdx
  • 作者直接抛去可能的狄拉克和高斯分布(所有先验),直接学习underlying General distribution P ( x ) P(x) P(x),定义一个边界可能的范围 y 0 ≤ y ≤ y n , n ∈ N + y_0 \leq y \leq y_{n}, n \in \mathbb{N}^{+} y0yyn,nN+,估计值 y ^ = ∫ − ∞ + ∞ P ( x ) x d x = ∫ y 0 y n P ( x ) x d x \hat{y}=\int_{-\infty}^{+\infty} P(x) x \mathrm{d} x=\int_{y_{0}}^{y_{n}} P(x) x \mathrm{d} x y^=+P(x)xdx=y0ynP(x)xdx
  • 将上述连续形式离散化: y ^ = ∑ i = 0 n P ( y i ) y i \hat{y}=\sum_{i=0}^{n} P\left(y_{i}\right) y_{i} y^=i=0nP(yi)yi,将P定义为softmax分布,然后只关注于边界最近的相邻两个概率( y i 和 y i + 1 y_i和y_{i+1} yiyi+1 y i ≤ y ≤ y i + 1 y_{i} \leq y \leq y_{i+1} yiyyi+1),为了迫使网络快速聚焦于标签y附近的值,采用交叉熵形式作为损失函数,由于Bbox的学习只针对正样本,没有类不平衡问题的风险!
    • Distribution Focal Loss (DFL):
      D F L ( S i , S i + 1 ) = − ( ( y i + 1 − y ) log ⁡ ( S i ) + ( y − y i ) log ⁡ ( S i + 1 ) ) \mathbf{D F L}\left(\mathcal{S}_{i}, \mathcal{S}_{i+1}\right)=-\left(\left(y_{i+1}-y\right) \log \left(\mathcal{S}_{i}\right)+\left(y-y_{i}\right) \log \left(\mathcal{S}_{i+1}\right)\right) DFL(Si,Si+1)=((yi+1y)log(Si)+(yyi)log(Si+1))
  • 其形式上与QFL的右半部分很类似,含义是以类似交叉熵的形式去优化与标签y最接近的一左一右两个位置的概率,从而让网络快速地聚焦到目标位置的邻近区域的分布中去
  • The global minimum solution of DFL: S i = y i + 1 − y y i + 1 − y i , S i + 1 = y − y i y i + 1 − y i \mathcal{S}_{i}=\frac{y_{i+1}-y}{y_{i+1}-y_{i}}, \mathcal{S}_{i+1}=\frac{y-y_{i}}{y_{i+1}-y_{i}} Si=yi+1yiyi+1y,Si+1=yi+1yiyyi
  • y ^ = ∑ j = 0 n P ( y j ) y j = S i y i + S i + 1 y i + 1 = y i + 1 − y y i + 1 − y i y i + y − y i y i + 1 − y i y i + 1 = y \hat{y}=\sum_{j=0}^{n} P\left(y_{j}\right) y_{j}=\mathcal{S}_{i} y_{i}+\mathcal{S}_{i+1} y_{i+1}=\frac{y_{i+1}-y}{y_{i+1}-y_{i}} y_{i}+\frac{y-y_{i}}{y_{i+1}-y_{i}} y_{i+1}=y y^=j=0nP(yj)yj=Siyi+Si+1yi+1=yi+1yiyi+1yyi+yi+1yiyyiyi+1=y

GFL

  • QFL和DFL其实可以统一地表示为GFL,我们将其称之为Generalized Focal Loss
    G F L ( p y t , p y r ) = − ∣ y − ( y l p y l + y r p y r ) ∣ β ( ( y r − y ) log ⁡ ( p y l ) + ( y − y l ) log ⁡ ( p y r ) ) \mathbf{G F L}\left(p_{y_{t}}, p_{y_{r}}\right)=-\left|y-\left(y_{l} p_{y_{l}}+y_{r} p_{y_{r}}\right)\right|^{\beta}\left(\left(y_{r}-y\right) \log \left(p_{y_{l}}\right)+\left(y-y_{l}\right) \log \left(p_{y_{r}}\right)\right) GFL(pyt,pyr)=y(ylpyl+yrpyr)

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号