目标检测系列(4)— 在目标检测中不能不聊的 YOLOv3

前言

YOLO 是 You Only Look Once 是一个基于神经网络设计用于目标检测的模型,可以检测图像中存在多个目标。给出目标类别同时还用一个边界框将目标位置和大小表示出来。

关于 YOLOv3 介绍原文《YOLOv3: An Incremental Improvement》是作者 Joseph Redmon 的封山之作。YOLOv3 是 2018 年推出的,随后 2 年好,作者 Redmon 就退出了计算机视觉领域的研究。

josephredmon.jpeg

随后推出的 YOLOv4 和 YOLOv5 就不再是 Redmon 的作品了,最近可能已经出到 YOLOv7,不过今天我们还是看一看 YOLOv3 这篇技术文章,之所以说这一个一篇技术文章,而不是论文,主要因为这篇文章写比较随性。内容有些简单和随意,所以缺少一些相对比较重要信息,例如损失函数是如何定义的,对于这样问题作者在 paper 中并没有给出明确的回答,这需要看源码来找到答案,

YOLOv3 亮点

YOLOv3 是将当时主流技术进行整合,例如引入 FPN 也参考 Fast R-CNN 中的锚框的概念。性能依旧是 YOLOv3 的看点。论文中这张图为了突显 YOLOv3 的速度,将 YOLOv3 已经画出了坐标之外。

011.png

  • YOLOv3 将主干网络从 Darknet19 更新到了 Darknet53
007.png

我们来看一下在 paper 给出关于 Darknet53 对比当时主干网络的一些指标

012.png

  • Darknet-53 要比其上一代 Darknet-19 在 Top-1 准确度提升了 5%,虽然速度有所下降,但是还是要优于准确度相似的 ResNet-101 和 ResNet-152

  • Darknet-53 速度快原因还是参数量要比 ResNet-101 和 ResNet-152 少的多

  • 在 YOLOv3 采用类似 FPN 结构来做多尺度目标检测

FPN 结构

005.png

在 YOLOv3 中也引入 FPN 结构来输出多尺度的特征图,让网络具有更好泛化能力,可以检测出不同尺寸的目标。在主干网络后几层分别输出 13 × 13 13 \times 13 26 × 26 26 \times 26 以及 52 × 52 52 \times 52 不同尺度(分辨率)特征图,而且通过将小尺度的特征图进行上采样然后和大尺度的特征图进行融合来得到更丰富的特征,利于检测出小目标。

输入和输出

对于网络输入,在 paper 中输入是 416 × 416 416 \times 416 的图像,也可以是 608 × 608 608 \times 608 或者 832 × 832 832 \times 832 大小图像,只要是能够被 32 整除即可。

当然对于输入图像并没有限制为 416 × 416 416 \times 416 或者是 32 的倍数的图像尺寸,可以是任意尺寸,随后通过缩放可以将其转换为想要尺寸,在转换过程可以保持宽高比,也可以不保持宽高比,要保持宽高比,可以通过在缩放后的图像的两侧或者上下添加灰色边框来实现。

网络架构图

在 YOLOv3 原论文中,作者并没有给出网络架构完整的图,这里是根据代码和自己理解给大家绘制一张 YOLOv3 完整的架构图。

008.png

不难看出在 YOLOv3 中,有 3 个不同尺度输出,分别为 13 × 13 × 75 13 \times 13 \times 75 26 × 26 × 75 26 \times 26 \times 75 52 × 52 × 75 52 \times 52 \times 75 ,这里 13、26 和 52 分别代表不同尺度特征图,而 75 表示每一个网格会输出 3 个锚框,这个 3 个锚框是通过聚类来得到不同宽高,每一个锚框会给出 25 向量,其中包括边界框中心点和宽高、以及置信度和 20 类别的概率值,假设这里是 VOC 数据集,在 VOC 数据集提供了 20 类别做预测。

这里对这张图简单解释一下,因为现在分享 YOLOv3 代码实现就是基于上面这个结构图来做的,在主干网络根据特征图大小(或者分辨率)分别从提取 3 个特征图作为主干网络的输出,分别深层第一个 4 stage 的输出,其输出为 13 × 13 × 1024 13 \times 13 \times 1024 输出后经过一系列卷积,先是降低通道(通道数减半)数 1 × 1 1 \times 1 的卷积层,然后在经过 3 × 3 3 \times 3 卷积层,接下里在经过 1 × 1 1 \times 1 通道数减半的卷积层,并且由此分出通过上采样增加特征图宽高以便和上一个 stage 输出的 26 × 26 26 \times 26 做拼接,这里没有采用 FPN 逐点相加融合而是采用了拼接,接下来进入卷积层。

009.png

这是我绘制的一张当下相对主流的 YOLOv3 网络结构图,与之前不同之处就是在特征图在用于做检测前进行更多卷积层,然后在做目标检测是用到了 1 × 1 1 \times 1 的卷积来在输出特征图做检测。

在 YOLOv3 没有使用 MaxPooling 进行下采样,而是采用步长为 2 的 1 × 1 1 \times 1 卷积来做下采样。从而避免对于底层(浅层)特征的丢失。

推理

模型会输出在不同尺度上预测边界框,分别是在原图 8、16 和 32 倍下采样的特征图进行预测,所以模型会输出 13 × 13 13\times 13 26 × 26 26 \times 26 52 × 52 52 \times 52 的网格,每个网格单元会输出一个向量,维度为 75 75 也就是每个网格会输出 3 个在不同尺度上预测框,所以每个预测框就会输出 25 25 长向量,其中包括边界框中心点位置和宽高、目标置信度、以及 20 类别概率(如果在 VOC 数据集上做的训练,那么类别是 20)

屏幕快照 2022-07-28 下午4.15.05.png

关于预测边界框的简单说明,在 YOLOv3 中采用的是和 YOLOv2 一样的边界框机制,

006.png

在 SSD 和 faster RCNN 关于边界框中心的偏移量都是相对于锚框或者默认框的中心点来说的,而在 YOLOv3 中,中心点的偏移则是相对于其所在网格的左上角点作为基准来计算相对位置。

预测边界框的中心点

网络预测边界框的中心值为 t x , t y t_x,t_y 为了将其约束到 0 到 1 之间,对其进行 sigmoid 操作,然后这样就得到 0 到 1 之间,也就是相对于网格宽度 1 和 1 的比率,然后在将其所在网格的索引就得到中心点的坐标。

预测边界框宽度和高度

这里 b w b_w b h b_h 为边界框的宽度和高度,这里 p w p_w p h p_h 是锚框的宽度和高度,然后乘以 e t w e^{t_w}

损失函数

如果看过之前 YOLOv1 分享我们知道 YOLO 是把损失函数拆分为了,定位损失函数、目标(置信度)损失函数和类别损失函数几个部分,分别计算后再求和作为 YOLO 的损失函数。在 YOLOv3 继续沿用这样方式来计算损失,不过在内部进行简化和修改。

定位损失(location loss)

在定位损失依旧沿用之前的 YOLOv2 关于定位损失,通过平方差来衡量定位误差,这里需要注意的是,这里计算定位回归参数,所以需要对目标(target)边界框进行变换后才可以进行对比。

i = 0 S 2 j = 0 A 1 i j o b j [ ( σ ( t x i ) g x i ^ ) 2 + ( σ ( t y i ) g y i ^ ) 2 + ( t w i g w i ^ ) 2 + ( t h i g h i ^ ) 2 ] \sum_{i=0}^{S^2} \sum_{j=0}^A \mathbb{1}^{obj}_{ij} \left[ ( \sigma(t_{x_i}) -\hat{g_{x_i}})^2 + ( \sigma(t_{y_i}) -\hat{g_{y_i}})^2 + (t_{w_i} -\hat{g_{w_i}})^2 + (t_{h_i} -\hat{g_{h_i}})^2\right]

目标损失(object loss)

目标损失函数,也有叫置信度损失函数,每一个预测边界框会给出一个关于这个边界框是否框住一个目标(前景)的概率得分。这个置信度体现在两个方面,第一个是模型对于预测边界框包含目标有多大信心,还反应在对预测边界框有多拟合真实框有多大信心。所以通常在考虑目标损失函数,还会乘以一个边界框和目标边界框的 IoU 值。

不过好像在 YOLOv3 中对目标损失函数进行简化,可能是为了提升训练速度,将有目标直接设置为 1 ,而对于没有目标的位置将目标直接设置为 0。

i = 0 S 2 j = 0 A 1 i j o b j ( 1 C ^ i ) 2 + i = 0 S 2 j = 0 A 1 i j n o o b j ( 0 C ^ i ) 2 \sum_{i=0}^{S^2} \sum_{j=0}^A \mathbb{1}^{obj}_{ij}(1 - \hat{C}_i)^2 + \sum_{i=0}^{S^2} \sum_{j=0}^A \mathbb{1}^{noobj}_{ij} (0 - \hat{C}_i)^2

对于所有网格其中并没有目标, 1 i j n o o b j \mathbb{1}^{noobj}_{ij} 值为 1,因为置信度是通过 logistic 回归函数来进行预测,那么好对于 Logistic 回归损失函数通常都会采用二值交叉熵(Binary Cross Entropy)

I o U i ( log ( C ^ i ) ) + ( 1 I o U i ) ( 1 log ( C ^ i ) ) IoU_i(\log(\hat{C}_i) ) + (1 - IoU_i)(1 - \log(\hat{C}_i))

注意这里 I o U i IoU_i 是预测边界框和目标边界框的 IoU 值,而在上面 YOLOv3 中直接就用 1 和 0

C i ^ = S i g m o i d ( C i ) \hat{C_i} = Sigmoid(C_i)

对于包含目标的锚框希望预测出一个比较合适的边界框,可以有目标的目标损失函数是计算预测边界框和目标边界框的 IoU,

分类损失(class loss)

在 paper 中关于类别损失函数,作者采用 Binary Cross Entropy 不再是 Softmax,这样一来突破类别限制,输出就不再是一个类别的概率分布,这样做好处就是对于同一个预测边界框可以预测多个类别。有些标签可能存在包含关系,例如 Person 标签包括 Woman 标签,这时候 Binary CrossEntropy 就派上。

i = 0 S 2 j = 0 A 1 i j o b j ( p i ( C ) P i j ^ ( C ) ) 2 \sum_{i=0}^{S^2} \sum_{j=0}^A \mathbb{1}^{obj}_{ij} \left( p_i(C) - \hat{P_{ij}}(C) \right)^2

不过在实际代码中,也有不少实现直接用的是交叉熵

i = 0 S 2 j = 0 A 1 i j o b j log exp ( z j ) k exp ( z k ) \sum_{i=0}^{S^2} \sum_{j=0}^A \mathbb{1}^{obj}_{ij} -\log \frac{\exp(z_j)}{\sum_{k} \exp(z_k)}

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿

猜你喜欢

转载自juejin.im/post/7125356491951276045