思想和特点

之前的目标检测算法均需要多个步骤实现目标的分类和定位。如RCNN系列,首先需要进行region proposal,RCNN到Faster RCNN模块逐步将其他任务整合到网络,最终将region proposal也用网络来实现,但是仍然是分步骤实现的。分步骤实现的缺点是实现复杂,运行速度慢。

YOLO的核心思想就是利用整张图作为网络的输入,直接在输出层回归bounding box的位置和bounding box所属的类别。

而YOLO采用回归的方法将定位,分类等步骤统一到了一个网络,比之前RCNN系列的网络简洁。YOLO最突出的特点速度飞快,能够达到实时的效果。
YOLO-v1 论文阅读笔记

网络结构分析

首先放一张论文YOLO-v1原图YOLO-v1 论文阅读笔记

下面的序号1. 2. 3. 1.\ 2.\ 3.\ \cdots表示总层数,C1, C2,C1,\ C2, \cdots表示第几个卷积层,64@7×7×3[email protected]\times7\times3表示有64个这样的卷积核,没有写出参数默认stride=1stride=1,卷积层pad=filterSize12pad = \frac {filterSize - 1}{2}

  1. INPUT: image=448×448×3image=448\times448\times3

  2. C1: input=448×448×3,filters=64@7×7×3,stride=2,pad=3Leaky ReLU,output=224×224×64input=448\times448\times3, [email protected]\times7\times3, stride=2, pad=3 \rightarrow Leaky\ ReLU, output=224\times224\times64

  3. MAX_POOl1: input=224×224×64,window=2×2,stride=2output=112×112×64input=224 \times 224 \times 64, window=2 \times 2, stride=2,output=112 \times 112 \times 64

  4. C2: input=112×112×64,filters=92@3×3×64Leaky ReLUoutput=112×112×192input=112 \times 112 \times 64, [email protected] \times 3 \times 64 \rightarrow Leaky\ ReLU,output=112 \times 112 \times 192

  5. MAX_POOL2: input=112×112×192,window=2×2,stride=2output=56×56×192input=112 \times 112 \times 192, window=2 \times 2, stride=2,output=56 \times 56 \times 192

  6. C3: input=56×56×192,filters=128@1×1×192Leaky ReLU,output=56×56×128input=56 \times 56 \times 192, [email protected] \times 1 \times 192 \rightarrow Leaky\ ReLU, output=56 \times 56 \times 128

  7. C4: input=56×56×128,filters=256@3×3×128Leaky ReLU,output=56×56×256input=56 \times 56 \times 128, [email protected]\times3\times128 \rightarrow Leaky\ ReLU, output=56\times56\times256

  8. C5: input=56×56×256,filters=256@1×1×256Leaky ReLU,output=56×56×256input=56\times56\times256, [email protected]\times1\times256\rightarrow Leaky\ ReLU,output=56\times 56\times256

  9. C6: input=56×56×512,filters=512@3×3×256Leaky ReLU,output=56×56×512input=56\times 56\times512, [email protected]\times3\times256\rightarrow Leaky\ ReLU, output=56\times 56\times512

  10. MAX_POOL3: input=56×56×512,window=2×2,stride=2,output=28×28×512input= 56 \times 56 \times 512, window=2 \times 2, stride=2, output=28 \times 28 \times 512

  11. C7:input=28×28×512,filters=256@1×1×512Leaky ReLU,output=28×28×256input=28\times28\times512,[email protected]\times1\times512 \rightarrow Leaky\ ReLU, output=28\times28\times256

  12. C8:input=28×28×256,filters=512@3×3×256Leaky ReLU,output=28×28×512input=28\times28\times256,[email protected]\times3\times256 \rightarrow Leaky\ ReLU, output=28\times28\times512

  13. C9:input=28×28×512,filters=256@1×1×512Leaky ReLU,output=28×28×256input=28\times28\times512,[email protected]\times1\times512 \rightarrow Leaky\ ReLU, output=28\times28\times256

  14. C10:input=28×28×256,filters=512@3×3×256Leaky ReLU,output=28×28×512input=28\times28\times256,[email protected]\times3\times256 \rightarrow Leaky\ ReLU, output=28\times28\times512

  15. C11: input=28×28×512,filters=256@1×1×512Leaky ReLU,output=28×28×256input=28 \times 28 \times 512, [email protected] 1 \times 1 \times 512 \rightarrow Leaky\ ReLU, output=28 \times 28 \times 256

  16. C12: input=28×28×256,filters=512@3×3×256Leaky ReLU,output=28×28×512input=28 \times 28 \times 256, [email protected] 3 \times 3 \times 256 \rightarrow Leaky\ ReLU, output=28 \times 28 \times 512

  17. C13: input=28×28×512,filters=256@1×1×512Leaky ReLU,output=28×28×256input=28 \times 28 \times 512, [email protected] 1 \times 1 \times 512 \rightarrow Leaky\ ReLU, output=28 \times 28 \times 256

  18. C14: input=28×28×256,filters=512@3×3×256Leaky ReLU,output=28×28×512input=28 \times 28 \times 256, [email protected] 3 \times 3 \times 256 \rightarrow Leaky\ ReLU, output=28 \times 28 \times 512

  19. C15: input=28×28×512,filters=256@1×1×512Leaky ReLU,output=28×28×256input=28 \times 28 \times 512, [email protected] 1 \times 1 \times 512 \rightarrow Leaky\ ReLU, output=28 \times 28 \times 256

  20. C16: input=28×28×256,filters=1024@3×3×256Leaky ReLU,output=28×28×1024input=28 \times 28 \times 256, [email protected] 3 \times 3 \times 256 \rightarrow Leaky\ ReLU, output=28 \times 28 \times 1024

  21. MAX_POOL4: input=28×28×1024,window=2×2,stride=2,output=14×14×1024input= 28 \times 28 \times 1024, window=2 \times 2, stride=2, output=14 \times 14 \times 1024

  22. C17: input=14×14×1024,filters=512@1×1×1024Leaky ReLU,output=14×14×512input=14 \times 14 \times 1024, [email protected] 1 \times 1 \times 1024 \rightarrow Leaky\ ReLU, output=14 \times 14 \times 512

  23. C18: input=14×14×512,filters=1024@3×3×512Leaky ReLU,output=14×14×1024input=14 \times 14 \times 512, [email protected] 3 \times 3 \times 512 \rightarrow Leaky\ ReLU, output=14 \times 14 \times 1024

  24. C19: input=14×14×1024,filters=512@1×1×1024Leaky ReLU,output=14×14×512input=14 \times 14 \times 1024, [email protected] 1 \times 1 \times 1024 \rightarrow Leaky\ ReLU, output=14 \times 14 \times 512

  25. C20: input=14×14×512,filters=1024@3×3×512Leaky ReLU,output=14×14×1024input=14 \times 14 \times 512, [email protected] 3 \times 3 \times 512 \rightarrow Leaky\ ReLU, output=14 \times 14 \times 1024

  26. C21: input=14×14×1024,filters=1024@3×3×1024Leaky ReLU,output=14×14×1024input=14 \times 14 \times 1024, [email protected] 3 \times 3 \times 1024 \rightarrow Leaky\ ReLU, output=14 \times 14 \times 1024

  27. C22: input=14×14×1024,filters=1024@3×3×1024,stride=2Leaky ReLU,output=7×7×1024input=14 \times 14 \times 1024, [email protected] 3 \times 3 \times 1024, stride=2\rightarrow Leaky\ ReLU, output=7 \times 7 \times 1024

  28. C23: input=7×7×1024,filters=1024@3×3×1024Leaky ReLU,output=7×7×1024input=7 \times 7 \times 1024, [email protected] 3 \times 3 \times 1024 \rightarrow Leaky\ ReLU, output=7 \times 7 \times 1024

  29. C24: input=7×7×1024,filters=1024@3×3×1024Leaky ReLU,output=7×7×1024input=7 \times 7 \times 1024, [email protected] 3 \times 3 \times 1024 \rightarrow Leaky\ ReLU, output=7 \times 7 \times 1024

  30. FC1: input=7×7×1024=50176,weight=512×50176Leaky ReLU,output=512input = 7 \times 7 \times 1024 = 50176, weight = 512\times 50176 \rightarrow Leaky\ ReLU, output=512

  31. FC2: input=512,weight=4096×512,drop_prob=0.5Leaky ReLU,output=4096input = 512, weight = 4096\times 512, drop\_prob = 0.5 \rightarrow Leaky\ ReLU, output=4096

  32. OUTPUT: input=?drop_dims,output=7×7×30input=?drop\_dims, output = 7\times 7 \times 30

可以发现,YOLO运用了24个卷积层,4个最大池化层,2个全连接层。3×33\times 3卷积层用于提取特征,1×11\times 1卷积层是为了跨通道信息整合,池化层用于缩小特征图width,heigt(width,heigt)两个维度,全连接层用于预测物体的位置和类别概率。

另外,YOLO-v1中采用的**函数是Leaky ReLU,公式如下:ϕ(x)={x, if x>00.1x, otherwise\phi(x)=\begin{cases} x, & \text{ if } x>0\\ 0.1x, & \text{ otherwise} \end{cases}画一下,长这样

import matplotlib.pyplot as plt
import numpy as np

def leaky_relu(x):
    return np.where(x > 0, x, 0.1*x)

x = np.linspace(-10, 10)
plt.plot(x, leaky_relu(x))

YOLO-v1 论文阅读笔记

详细分析

网络输出

将输入图像分成S×SS\times S的格子,一个物体的中心点落在哪个格子,这个格子就负责检测该物体。每个格子有B个Bounding Box(后面称Bbox),每个Bbox有5个值(x,y,width,height,confidence)(x,y,width,height,confidence),下面逐一解释这五个值的含义:

  1. (x,y)[0,1](x, y) \in [0, 1]表示Bbox中心点相对该格子左上角的坐标(相对于格子高宽归一化之后的坐标);

  2. (height,width)[0,1](height, width) \in [0, 1]表示相对于整张图片高宽归一化之后的高宽;

  3. confidenceconfidence是置信度,描述了该Bbox含有物体的确信程度和该Bbox包含物体的精准程度,公式为confidence=Pr(Object)IOUpredtruthconfidence = Pr(Object)*IOU_{pred}^{truth}当有物体存在时Pr(Object)=1Pr(Object)=1,此时confidence=IOUpredtruth=predtruthpredtruthconfidence = IOU_{pred}^{truth}= \frac {pred\cap truth}{pred\cup truth},即预测框和真实框的重合比例;当没有物体时Pr(Object)=1Pr(Object)=1,即confidence=0confidence = 0

每一个框还要预测C个类别出现在该格子中的条件概率Pr(ClassiObject)Pr(Class_i|Object),并且一个格子只负责预测一个物体而不是B个。

现在让我们来统计一下一张图片经过网络最终的输出有哪些。首先有S×SS\times S个格子,每个格子有B个Bounding Box,每个Bounding Box包含上述(x,y,width,height,confidence)(x,y,width,height,confidence)这样5个参数,在加上每个格子包含C个含有物体的条件概率,所以总共输出为S×S×(B×5+C)S\times S \times (B\times 5 + C)。论文中S=7,B=2,C=20S=7,B=2,C=20,所以输出是一个7×7×(2×5+20)=7×7×307\times 7 \times (2\times 5 + 20) = 7\times 7 \times 30的张量。

训练

训练过程

  1. 预训练。使用 ImageNet 1000 类数据训练YOLO网络的前20个卷积层+1个average池化层+1个全连接层。训练图像分辨率resize到224x224。
  2. 用步骤1.得到的前20个卷积层网络参数来初始化YOLO模型前20个卷积层的网络参数,然后用 VOC 20 类标注数据进行YOLO模型训练。检测通常需要有细密纹理的视觉信息,所以为提高图像精度,在训练检测模型时,将输入图像分辨率从224 × 224 resize到448x448。

损失函数

训练主要围绕损失函数展开讨论,下面是损失函数

loss=λcoordi=0S2j=0B1ijobj[xix^i2+yiy^i2]+λcoordi=0S2j=0B1ijobj[wiw^i2+hih^i2]+i=0S2j=0B1ijobj(CiC^i)2+λnoobji=0S2j=0B1ijnoobj(CiC^i)2+i=0S21iobjcclasses(pi(c)p^i(c))2\begin{aligned} loss &= \lambda_{coord}\sum_{i=0}^{S^2}\sum_{j=0}^B \mathbb{1}_{ij}^{obj}\left [(x_i - \hat{x}_i)^2 + y_i - \hat{y}_i)^2\right ]\\ &+ \lambda_{coord}\sum_{i=0}^{S^2}\sum_{j=0}^B \mathbb{1}_{ij}^{obj}\left [(\sqrt{w_i} - \sqrt{\hat{w}_i})^2 + \sqrt{h_i} - \sqrt{\hat{h}_i})^2\right ] \\ &+ \sum_{i=0}^{S^2}\sum_{j=0}^B \mathbb{1}_{ij}^{obj}(C_i -\hat{C}_i)^2\\ &+ \lambda_{noobj}\sum_{i=0}^{S^2}\sum_{j=0}^B \mathbb{1}_{ij}^{noobj}(C_i -\hat{C}_i)^2\\ &+ \sum_{i=0}^{S^2}\mathbb{1}_{i}^{obj} \sum_{c \in classes}(p_i(c)-\hat{p}_i(c))^2 \end{aligned}

为了便于分析,下面将损失函数各部分解释标注在图上YOLO-v1 论文阅读笔记

  • 损失函数采用的是误差平方和的形式,这样便于优化,主要由三部分组成:坐标,置信度,类别。所以YOLO能够用一个网络同时进行定位和识别。

  • ① YOLO-v1使用误差平方和作为损失函数,但是直接把定位误差平方和与分类误差平方等权值相加显然不合理,同时还考虑到在很多的格子里根本没有物体,这将导致没有物体的这些格子的置信度分数为0,这将会压制包含对象的单元格的梯度,这将导致在训练前期容易发散。为了修正这两个问题我们在定位误差项前面乘以λcoord=5\lambda_{coord}=5,在没有物体的置信度误差项前面乘以系数λnoobj=0.5\lambda_{noobj}=0.5

  • ② 为什么要在width,heightwidth,height上加根号?原文是想反映大的Bbox中的小偏差比小Bbox中小。画个图可以解释:

x = np.linspace(0,5)
plt.plot(x,x)
plt.plot(x,np.sqrt(x))

YOLO-v1 论文阅读笔记
从加根号可以看出,在width,heightwidth,height较大时,根号具有缓解增长的作用。

  • 1ijobj\mathbb{1}_{ij}^{obj}ii个格子的第jj个Bbox有负责的Object, 所以坐标惩罚项实际惩罚的是有物体的那些Bbox,如果该Bbox没有负责某一个物体,则该部分损失值为0。

  • 1ijnoobj\mathbb{1}_{ij}^{noobj}表示第ii个格子的第jj个Bbox没有负责的Object, 在置信度惩罚项中我们对有没有物体的Bbox都要惩罚,但是没有物体的项乘以了系数λnoobj\lambda_{noobj}来降低它的权重。

  • 1iobj\mathbb{1}_{i}^{obj}表示第ii个格子中有Object,这个时候需要分别计算属于各个类的概率。

其他细节

  • 在训练时采用的是小批迭代动量梯度下降。动量mc=0.9,衰减率是0.0005。学习速率在需要各个阶段动态调整,如果在初期迭代学习率过高将导致梯度不稳定而发散。

  • 为了防止过拟合,仍然采用了dropout正则化技术(第一个全连接层后面,drop_prob=0.5)和数据增强(尺度缩放,图像变换,调节曝光度和饱和度等)。

预测

经过网络,输出是7×7×307\times 7\times 30,其中 30=(x,y,width,height,confidence)×2+2030 = (x, y, width, height, confidence)\times2 + 20个类别的条件概率

For cic_i in C:

  1. cic_i的98个bbox按置信度分数降序排列。
  2. 选择cic_i预测框中置信度最大(排序后为第一个)的那个bbox然后挨个计算其与剩余bbox的IOU,如果IOU值大于一定阈值(如0.5),则重合度过高,就将该类别置信度值置为0。
  3. 重复2.直到处理完cic_i的所有检测框。

相关文章: