首页 计算机视觉(One-Stage 目标检测)
文章
取消

计算机视觉(One-Stage 目标检测)

本文介绍了计算机视觉中单阶段目标检测问题的解决方法,即 Yolo 系列。


1. 前言

Yolo,SSD 这类 one-stage 算法,仅仅使用一个卷积神经网络 CNN 直接预测不同目标的类别与位置。一阶段方法的速度快,但是准确性要低一些。

2. YOLO V1

YOLO意思是You Only Look Once,创造性的将候选区和对象识别这两个阶段合二为一,看一眼图片(不用看两眼哦)就能知道有哪些对象以及它们的位置。

实际上,YOLO并没有真正去掉候选区,而是采用了预定义的候选区(准确点说应该是预测区,因为并不是Faster RCNN所采用的Anchor)。也就是将图片划分为 $7\times 7=49$ 个网格(grid),每个网格允许预测出 2 个边框(bounding box,包含某个对象的矩形框),总共 $49\times 2=98$ 个 bounding box。可以理解为 98 个候选区,它们很粗略的覆盖了图片的整个区域。

2.1. 网络结构

网络结构借鉴了 GoogLeNet 。24个卷积层,2个全链接层。(用1×1 reduction layers 紧跟 3×3 convolutional layers 取代Goolenet的 inception modules )

2.2. 输入

输入就是原始图像,唯一的要求是缩放到 $448\times 448$ 的大小。主要是因为YOLO的网络中,卷积层最后接了两个全连接层,全连接层是要求固定大小的向量作为输入,所以倒推回去也就要求原始图像有固定的尺寸。

2.3. 输出

输出是一个 $7\times 7\times 30$ 的张量。$7\times 7$ 对应原始图像的网格,30维向量 = 20个对象的概率 + 2个bounding box * 4个坐标 + 2个bounding box的置信度。

前 20 维,one hot 编码。因为YOLO支持识别20种不同的对象(人、鸟、猫、汽车、椅子等),所以这里有20个值表示该网格位置存在任一种对象的概率。

中 2 维,2 个 bounding box 的置信度。 = 该 bounding box 内有对象的概率 * 该 bounding box 与该对象实际 bounding box 的 IOU。

后 8 维,2 个 bounding box 的位置。每个 bounding box 需要 4 个数值来表示其位置,(Center_x, Center_y, width, height),2 个 bounding box 共需要 8 个数值来表示其位置。

$7\times 7$ 网格,每个网格 2 个 bounding box,对 $448\times 448$ 输入图像来说覆盖粒度有点粗。我们也可以设置更多的网格以及更多的bounding box。设网格数量为 $S\times S$,每个网格产生 B 个边框(4 位置 + 1 置信度),网络支持识别 C 个不同的对象。这时,输出的向量长度为: $C + B\times (4+1)$ 整个输出的tensor就是: $S\times S\times (C + B\times (4+1))$。

2.4. 构造训练样本

  • 20 个对象分类的概率

对于输入图像中的每个对象,先找到其中心点。中心点落在某个网格内,该网格对应 30 维向量中的 1 维置 1,其它维度置 0(也即一个网格只能预测 1 个对象,网络一共能从一张图片中检测49个对象)。这就是所谓的”中心点所在的网格对预测该对象负责”。

  • 2 个 bounding box 的位置

训练样本的 bounding box 位置应该填写对象实际的 bounding box,但一个对象对应了 2 个 bounding box,该填哪一个呢?上面讨论过,需要根据网络输出的 bounding box 与对象实际 bounding box 的 IOU 来选择,所以要在训练过程中动态决定到底填哪一个 bounding box。参考下面第 3 点。

  • 2 个 bounding box 的置信度

上面讨论过置信度公式

\[Confidence = Pr(Object) * IOU^{truth}_{pre}\]

2 个 bounding box 的 $IOU$,哪个比较大就由哪个 bounding box 负责预测该对象是否存在,相应的 $P(Object)=1$,$Confidence = IOU$,该网格其它 bounding box 的 $Confidence = 0$。

注意,在训练过程中等网络输出以后,比较两个 bounding box 与自行车实际位置的 IOU,自行车的位置(实际 bounding box)放置在 IOU 比较大的那个 bounding box(图中假设是 bounding box1),且该 bounding box 的置信度设为 1。

2.5. 损失函数

损失函数就是网络实际输出与标签之间的误差,YOLO V1 简单粗暴采用 sum-squared error loss。

\[\begin{aligned} loss =&\lambda_{coord}\sum_{i=0}^{S^2}\sum_{j=0}^{B}[(x_i-\hat{x}_i)^2+(y_i-\hat{y}_i)^2]\\ &+\lambda_{coord}\sum_{i=0}^{S^2}\sum_{j=0}^{B}\mathbb{I}_{ij}^{obj}[(\sqrt w_i-\sqrt{\hat{w}_i})^2+(\sqrt h_i-\sqrt{\hat{h}_i})^2]\\ &+\sum_{i=0}^{S^2}\sum_{j=0}^{B}\mathbb{I}_{ij}^{obj}(C_i-\hat C_i)^2\\ &+\lambda_{noobj}\sum_{i=0}^{S^2}\sum_{j=0}^{B}\mathbb{I}_{ij}^{noobj}(C_i-\hat C_i)^2\\ &+\sum_{i=0}^{S^2}\mathbb{I}_{i}^{obj}\sum_{c\in classes}(p_i(c) - \hat p_i(c))^2 \end{aligned}\]

其中,三个布尔系数:

  • $\mathbb{I}_{ij}^{obj}=1$ 表示第 $i$ 个网格的第 $j$ 个 bounding box 中存在对象;
  • $\mathbb{I}_{ij}^{noobj}=1$ 表示第 $i$ 个网格的第 $j$ 个 bounding box 中不存在对象;
  • $\mathbb{I}_{i}^{obj}=1$ 表示第 $i$ 个网格中存在对象。

加入这些项的意义在于,只对符合布尔条件的 网格 / bounding box 才参与误差计算。

第一行,中心点的误差。

第二行,边框宽度高度的误差。注意取了平方根,这是为了平衡小框和大框的不同权重,因为绝对偏差在小框上更加敏感。取平方根可以降低这种敏感度的差异,使得较大的对象和较小的对象在尺寸误差上有相似的权重

系数 $\lambda_{coord}=5$ 用来调高位置误差(相对于分类误差和置信度误差)的权重。

第三行,存在对象的 bounding box 的置信度误差。

第四行,不存在对象的 bounding box 的置信度误差。因为不存在对象的 bounding box 应该老老实实的说 “我这里没有对象”,也就是输出尽量低的置信度。如果它不恰当的输出较高的置信度,会与真正有对象预测的那个 bounding box 产生混淆。即正确的对象概率最好是 1,所有其它对象的概率最好是 0。

系数 $\lambda_{noobj}=0.5$ 用来调低位置误差(相对于分类误差和置信度误差)的权重。

第五行,对于有对象的网格($\mathbb{I}_{i}^{obj}=1$),分类的误差。这里将分类的向量与标签的 one-hot 向量进行作差然后求平方,即把分类问题当作回归问题

2.6. 训练

最后一层采用线性激活函数,其它层都是 Leaky ReLU。训练过程采用了 drop out 和数据增强来防止过拟合。

2.7. 测试预测

测试时,每个网格预测的 class 的概率(比如前 20 维向量中最大的那个值),和 bounding box 预测的 confidence 值相乘,就得到每个 bounding box 的 class-specific confidence score:

\[Pr(C_i) = Pr(C_i\vert Object) *Pr(Object) * IOU_{pred}^{truth}\]

然后设置阈值,滤掉得分低的 boxes,对保留的 boxes 进行 NMS 处理,就得到最终的检测结果。

具体而言:每个网格有 20 个对象的概率 * 2个 bounding box 的置信度,共40个得分(候选对象)。49个网格共1960个得分。Andrew Ng建议每种对象分别进行NMS,那么每种对象有 1960/20=98 个得分。

NMS步骤如下: 1)设置一个Score的阈值,低于该阈值的候选对象排除掉(将该Score设为0) 2)遍历每一个对象类别  2.1)遍历该对象的98个得分   2.1.1)找到Score最大的那个对象及其bounding box,添加到输出列表   2.1.2)对每个Score不为0的候选对象,计算其与上面2.1.1输出对象的bounding box的IOU   2.1.3)根据预先设置的IOU阈值,所有高于该阈值(重叠度较高)的候选对象排除掉(将Score设为0)   2.1.4)如果所有bounding box要么在输出列表中,要么Score=0,则该对象类别的NMS完成,返回步骤2处理下一种对象 3)输出列表即为预测的对象

2.8. 结论

YOLO以速度见长,处理速度可以达到45fps,其快速版本(网络较小)甚至可以达到155fps。这得益于其识别和定位合二为一的网络设计,而且这种统一的设计也使得训练和预测可以端到端的进行,非常简便。

不足之处是小对象检测效果不太好(尤其是一些聚集在一起的小对象),对边框的预测准确度不是很高,总体预测精度略低于Fast RCNN。主要是因为网格设置比较稀疏,而且每个网格只预测两个边框,另外Pooling层会丢失一些细节信息,对定位存在影响。

3. YOLO V2

相比 YOLO V1,改进如下表所示。

3.1. 网络结构

主干网络换成 Darknet-19,19 个卷积层和 5 个最大池化层,比 VGG-16 小一些,精度不弱于它,但浮点运算减少到约 1/5,运算速度更快。

  • 移除全连接层

很长一段时间以来,全连接网络一直是 CNN 分类网络的标配结构。一般在全连接后会有激活函数来做分类,假设这个激活函数是一个多分类 softmax,那么全连接网络的作用就是将最后一层卷积得到的 feature map stretch 成向量,对这个向量做乘法,最终降低其维度,然后输入到 softmax 层中得到对应的每个类别的得分。

全连接层如此的重要,以至于全连接层过多的参数重要到会造成过拟合,所以也会有一些方法专门用来解决过拟合,比如dropout。

YOLO V2 移除了全连接层,而是采用 全局平均池化层来进行分类。global average pooling 与 average pooling 的差别就在 “global” 这一个字眼上。global 与 local 在字面上都是用来形容 pooling 窗口区域的。 local 是取 feature map 的一个子区域求平均值,然后滑动这个子区域; global 显然就是对整个 feature map 求平均值了。

  • 摆脱了固定尺寸的 FC 对输入图像尺寸的约束,可以适应各种尺寸的输入图像。
  • 剔除了 FC 的黑箱特性,直接赋予每个 channel 实际意义。

PLoB. 关于global average pooling理解和介绍

  • 移除一个池化层

去掉了一个池化层,使网络卷积层输出具有更高的分辨率。

3.2. 批归一化

该主干网络中的每个卷积层中还引入了 Batch Normalization 稳定训练,加快收敛,防止过拟合。同时放弃了 dropout。批归一化有助于解决反向传播过程中的梯度消失和梯度爆炸问题,降低对一些超参数(比如学习率、网络参数的大小范围、激活函数的选择)的敏感性,并且每个 batch 分别进行归一化的时候,起到了一定的正则化效果(YOLO V2 不再使用 dropout),从而能够获得更好的收敛速度和收敛效果。

通常,一次训练会输入一批样本(batch)进入神经网络。批规一化在神经网络的每一层,在网络(线性变换)输出后和激活函数(非线性变换)之前增加一个批归一化层(BN),BN层进行如下变换:

  • 对该批样本的各特征量(对于中间层来说,就是每一个神经元)分别进行归一化处理,分别使每个特征的数据分布变换为均值 0,方差 1。从而使得每一批训练样本在每一层都有类似的分布。这一变换不需要引入额外的参数。
  • 对上一步的输出再做一次线性变换,假设上一步的输出为Z,则 $Z_1=\gamma Z + \beta$。这里γ、β是可以训练的参数。增加这一变换是因为上一步骤中强制改变了特征数据的分布,可能影响了原有数据的信息表达能力。增加的线性变换使其有机会恢复其原本的信息。

批归一化使 mAP 有 2.4% 的提升。

天雨粟. Batch Normalization原理与实战. 知乎

3.3. 高分辨率分类器

图像分类的训练样本很多,而标注了边框的用于训练对象检测的样本相比而言就比较少了,因为标注边框的人工成本比较高。所以对象检测模型通常都先用图像分类样本训练卷积层,提取图像特征。但这引出的另一个问题是,图像分类样本的分辨率不是很高。所以YOLO v1使用ImageNet的图像分类样本采用 224 x 224 作为输入,来训练CNN卷积层。然后在训练对象检测时,检测用的图像样本采用更高分辨率的 448 x 448 的图像作为输入。但这样切换对模型性能有一定影响。

所以YOLO2在采用 224 x 224 图像进行分类模型预训练后,再采用 448 x 448 的高分辨率样本对分类模型进行微调(10个epoch),使网络特征逐渐适应 448 x 448 的分辨率。然后再使用 448 x 448 的检测样本进行训练,缓解了分辨率突然切换造成的影响。

mAP 提升了 3.7%。

3.4. 先验框(anchor)

借鉴 Faster R-CNN 的做法,YOLO V2 也尝试采用先验框(anchor)。在每个网格预先设定一组不同大小和宽高比的边框,来覆盖整个图像的不同位置和多种尺度,这些先验框作为预定义的候选区在神经网络中将检测其中是否存在对象,以及微调边框的位置。

之前 YOLO V1 并没有采用先验框,并且每个网格只预测两个 bounding box,整个图像 98 个。YOLO V2 如果每个网格采用 9 个先验框,总共有 $13\times 13\times 9=1521$ 个先验框。

召回率大幅提升到88%,同时mAP轻微下降了0.2%。不过 YOLO V2 接着进一步对先验框进行了改良。之前先验框都是手工设定的,YOLO2尝试统计出更符合样本中对象尺寸的先验框,这样就可以减少网络微调先验框到实际位置的难度。YOLO V2 对训练集中标注的边框进行聚类分析,以寻找尽可能匹配样本的边框尺寸。

聚类算法最重要的是选择如何计算两个边框之间的“距离”,对于常用的欧式距离,大边框会产生更大的误差,但我们关心的是边框的IOU。所以,YOLO V2 在聚类时采用以下公式来计算两个边框之间的 “距离”。

\[d(box,centroid) = 1- IOU(box, centroid)\]

$centroid$ 是聚类时被选作中心的边框,$box$ 就是其它边框,$d$ 就是两者间的 “距离”。IOU 越大,“距离” 越近。聚类分析结果如下图所示:

上图左边是选择不同的聚类 k 值情况下,得到的 k 个 centroid 边框,计算样本中标注的边框与各 centroid 的 Avg IOU。显然,边框数 k 越多,Avg IOU 越大。YOLO V2 选择 k=5 作为边框数量与 IOU 的折中。对比手工选择的先验框,使用 5 个聚类框即可达到 61 Avg IOU,相当于 9 个手工设置的先验框 60.9 Avg IOU。

上图右边显示了 5 种聚类得到的先验框,VOC 和 COCO 数据集略有差异,不过都有较多的瘦高形边框。

3.5. 约束预测框位置

借鉴于 Faster RCNN 的先验框方法,其位置预测公式为:

\[x = (t_x \cdot w_a) + x_a\\ y = (t_y \cdot h_a) + y_a\]

其中,$x_a, y_a$ 时先验框中心,$w_a, h_a$ 是先验框宽高,$x,y$ 是预测框中心,$t_x, t_y$ 是待学习参数。

由于 $t_x, t_y$ 没有任何约束,因此预测框的中心可能出现在任何位置。训练早期阶段不容易稳定。因此 YOLO V2 调整了预测公式,将预测框中心约束在特定的网格内。

\[x = \sigma (t_x) + c_x\\ y = \sigma (t_y) + c_y\\ w = w_a e^{t_w}\\ h = h_a e^{t_h}\\ Pr(obj)\cdot IOU(b, obj) = \sigma (t_o)\]

式中,$t_x, t_y, t_w, t_h, t_o$ 是待学习参数。

$c_x, x_y$ 是当前网格左上角到图像左上角的距离,要先将网格大小归一化,即令一个网格的宽=1,高=1。$\sigma$ 即 sigmoid 函数,可以将 $[-\infty,+\infty]$ 的 $t_x, t_y$ 映射到 $[0,1]$,从而约束在了当前网格内(蓝色区域)。

对于预测框的宽高,采用指数函数,将 $[-\infty,+\infty]$ 的 $t_w, t_h$ 映射到 $[0,+\infty]$,此时 $t=0$ 对应 $e^t=1$ ,则预测框宽高就等于原始宽高,相当于让参数围绕 0 附近波动,更好学习。

引入先验框和约束预测框位置,使得 mAP 有 4.8% 的提升。

对于置信度,YOLO V1 直接预测置信度的值,而 YOLO V2 预测 $t_o$,要经过 sigmoid 变换作为置信度的值。

3.6. pass through

为了更好的检测出一些比较小的对象,最后输出的特征图需要保留一些更细节的信息。

YOLO V2 引入一种称为 passthrough 层的方法在特征图中保留一些细节信息。具体来说,就是在最后一个 pooling 之前,特征图的大小是 26 x 26 x 512,将其 1 拆 4,直接传递(passthrough)到 pooling 后(并且又经过一组卷积)的特征图,两者叠加到一起作为输出的特征图。

由于之后的 YOLO V3 等不再运用,此处就不介绍了。

3.7. 训练

YOLO V2 的训练主要包括两个阶段。第一阶段就是先在 ImageNet 分类数据集上预训练 Darknet-19,此时模型输入为 224 x 224 ,共训练 160 个 epochs。然后第二阶段将网络的输入调整为 448 x 448 ,继续在 ImageNet 数据集上 finetune 分类模型,训练 10 个 epochs,此时分类模型的 top-1 准确度为 76.5%,而 top-5 准确度为 93.3%。

3.8. 测试预测

修改 Darknet-19 分类模型为检测模型,移除最后一个卷积层、global avgpooling 层以及 softmax 层,并且新增了三个 3 x 3 x 1024 卷积层,同时增加了一个passthrough层,最后使用 1 x 1 卷积层输出预测结果,输出的 channels 数为:num_anchors x (5+num_classes),和训练采用的数据集有关系。由于 anchors 数为 5,对于 VOC 数据集(20 种分类对象)输出的 channels 数就是 125,最终的预测矩阵 T 的 shape 为 (batch_size, 13, 13, 125),可以先将其 reshape 为 (batch_size, 13, 13, 5, 25) ,其中 T[:, :, :, :, 0:4] 为边界框的位置和大小 $[t_x, t_y, t_w, t_h]$ ,T[:, :, :, :, 4] 为边界框的置信度,而 T[:, :, :, :, 5:] 为类别预测值。

4. YOLO V3

4.1. 网络结构

其中主干网络(Backbone)为 Backbone。从 YOLO V3 开始引入 Neck 。在目标检测领域,为了更好的提取融合特征,通常在 Backbone 和输出层之间会插入一些层,这个部分称为 Neck。相当于目标检测网络的颈部,也是非常关键的。

DarkNet-53 的基本单元为 Convolutional,包括 {conv2d + BN + Leaky ReLU}

上图的 Darknet-53 网络采用 256 x 256 x 3 作为输入,首先经过一个 3 x 3 x 32 Convolutional,然后经过一个 3 x 3 x 64s2 Convolutional 下采样一倍。后面还可以看到, DarkNet 摒弃了所有最大池化层,转而用 stride=2 的 Convolutional 来做下采样。比如第二层的 Convolutional:

\[\begin{aligned} output &= (input + 2*padding - kernel)/stride+1\\ &= (256+2*0-3)/2 + 1\\ &= ceil(136.5) + 1\\ &= 128\\ \end{aligned}\]

紧接是若干重复模块。最左侧那一列的 1、2、8 等数字表示多少个重复的残差组件。每个残差组件有两个 Convolutional 和一个快捷链路。以第一个残差模块为例:

1
2
3
4
            [1 x 1] x 32 kernel          [3 x 3] x 64 kernel
128 x 128 x 64 ---------->  128 x 128 x 32 ---------->  128 x 128 x 64
       |                                                       ^
       |_______________________________________________________| (+)

后接一个 3 x 3 x 256s2 的下采样 Convolutional。进入第二个残差模块,这里由 8 个残差模块堆叠而成。后面以此类推。

4.2. 多尺度特征

YOLO V2 曾采用 passthrough 结构来检测细粒度特征,在 YOLO V3 更进一步采用了 3 个不同尺度的特征图来进行对象检测,构成特征金字塔(Feature Pyramid Networks, FPN)。

分别从主干网络的中间层引出两个特征图,分别为:

  • b: 52 x 52 x 256
  • c: 26 x 26 x 512

下面详细讨论特征金字塔:

  • Predict one

主干网络输出的最抽象的特征,具有大尺度的感受野(特征图上很小的区域就对应原图很大的一块区域),用来检测大目标。

首先,主干网络最末端输出 13 x 13 x 1024 ,经过 Convolutional Set,包含五个 Convolutional 组(图右侧),其中每个 Convolutional 的卷积核具体为:

1
[1 x 1] x 512 --> [3 x 3] x 1024 --> [1 x 1] x 512 --> [3 x 3] x 1024 --> [1 x 1] x 512

得到 13 x 13 x 512 特征,记作 A

再经过一个单独的 {[3 x 3] x 1024 + BN + Leaky ReLU} Convolutional 和一个 1 x 1 x 255 纯卷积 conv2d,得到 13 x 13 x 255 的特征图 Y1,可识别 13 x 13 = 169 个大物体。

  • Predict Two

具有中等尺度的感受野,用来检测中目标。

将前面的 A 拿过来,经过一个单独的 {[1 x 1] x 256 + BN + Leaky ReLU} Convolutional 和一个上采样,得到 26 x 26 x 256 的特征图。将上采样的结果与 b 拼接后得到 26 x 26 x 768

1
2
3
4
            26 x 26 x 512 ------------------------------------------ 
                                                                    | (concat)
A:13 x 13 x 512 --> 13 x 13 x 256 --------> 26 x 26 x 512 --> 26 x 26 x 768
                                  (upsample)

再经过 Convolutional Set,其中每个 Convolutional 的卷积核具体为:

1
[1 x 1] x 256 --> [3 x 3] x 512 --> [1 x 1] x 256 --> [3 x 3] x 512 --> [1 x 1] x 256

得到 26 x 26 x 256,记作 B

再经过一个单独的 {[3 x 3] x 512 + BN + Leaky ReLU} Convolutional 和一个 1 x 1 x 255 纯卷积 conv2d,得到 26 x 26 x 255 的特征图 Y2,可识别 26 x 26 = 676 个中物体。

  • Predict Three

具有小尺度的感受野,用来检测中目标。将前面的 B 拿过来,经过一个单独的 {[1 x 1] x 128 + BN + Leaky ReLU} Convolutional 和一个上采样,得到 52 x 52 x 128 的特征图。

1
2
3
4
            52 x 52 x 256 ------------------------------------------ 
                                                                    | (concat)
B:26 x 26 x 256 --> 26 x 26 x 128 --------> 52 x 52 x 128 --> 52 x 52 x 384
                                  (upsample)

将上采样的结果与 c 拼接后得到 26 x 26 x 384。再经过 Convolutional Set,其中每个 Convolutional 的卷积核具体为:

1
[1 x 1] x 128 --> [3 x 3] x 256 --> [1 x 1] x 128 --> [3 x 3] x 256 --> [1 x 1] x 128

再经过一个单独的 {[3 x 3] x 256 + BN + Leaky ReLU} Convolutional 和一个 1 x 1 x 255 纯卷积 conv2d,得到 52 x 52 x 255 的特征图 Y3,可识别 52 x 52 = 2704 个小物体。

两个注意点:

  • 上采样(up sampling)方法包括:双线性插值(简单,不需要学习参数)和反卷积,不知道 YOLO V3 采用哪种。
  • 输出特征维度中 255 = [ 80 class + 4 (x,y,w,h) + 1 confidence ] x 3 bbox

4.3. 损失函数

5. 参考文献

[1] X猪. YOLO v1深入理解. 简书

本文由作者按照 CC BY 4.0 进行授权

计算机视觉(Two-Stage 目标检测)

计算机视觉(YOLO V5)