YOLO-V3
设计思想
主要特点:
-
单阶段检测,同时完成候选区域生成 + 物体位置和类别的预测
-
每个真实框只对应一个正的候选区域
实现方案
生成锚框和候选区域
生成锚框
step1:将原图划分成多个小方块区域
原图大小:640 × 480
小方块尺寸:32 × 32
划分成了20 × 15 个小方块区域
step2:以小方块区域为中心,生成一系列锚框
生成锚框的规则:
-
中心点:小方块区域的中心
-
大小:3种 [w,h]
-
[116,90] , [156,198] , [373,326] (数字的选择-聚类中心)
-
在每个小方块区域都生成三种形状的锚框,将会覆盖整张图片
一张图片上锚框的总数为20 ×15 × 3 = 900
产生候选区域
生成预测框
所有锚框覆盖住了整张图片,但是锚框的位置是固定的,一般不会跟物体真实框刚好重合。
要想得到跟物体真实框足够接近的预测结果,需要在锚框的基础上做一些小的调整。
预测框
可以看作是 在锚框的基础上做微调, 可以调整 中心坐标和宽度、高度。
step1:选择第10行第4列的小方块区域, Cx ,Cy = [4,10]
step2:以小方块为中心,生成锚框。所示锚框大小为[250,250]
step3:计算预测框的中心坐标和尺寸
标注候选区域
标注锚框 ----objectness
-
标注objectness标签
-
找出真实框中心点所在的小方块区域,从这个区域的3个锚框中挑选出跟他形状最匹配的锚框
-
计算iou,找出跟真实框最匹配的锚框,将其objectness标注为1,其余标注为0。
-
标注锚框----location(位置)
标注锚框----classification
-
当objectness = 1 时,需要确定包含的物体所属的类别 classification
-
YOLO-V3模型对每个类别独立的计算概率,标注的时候将类别标签表示成one-hot 向量
标注总结
-
每个锚框需要的标注数据个数:
-
location(4) + objectness(1) + classification(C) = 5+C
-
m × n 个方块区域,每个区域K个锚框,则标注数据的维度是[k(5+C)] × m × n
-
标注锚框----程序实现
特征提取
生成候选区域并对其进行标注 objectness location classification
-
使用卷积神经网络提取特征
-
网络选择:Darknet53
-
一共52层卷积 + 1层全连接
-
-
检测模型将 C0 以后的层去掉,从输入到 C0 部分的网络被称为检测模型的 骨干模型(backbone)
-
YOLO-V3 算法中使用了C0、C1、C2 三个层级的特征图来产生候选区域的输出。
-
创建Darknet53骨干网络模块
对C0进一步提取特征运用到的模块 YoloDetectionBlock
关联特征图与候选区域
建立损失函数
采用sum-squared error(损失总平方和)的方式把localization error(bounding box的坐标误差)和classificaton error整合在一起。
找出objectness = -1的锚框
建立损失函数
损失函数的设计目标就是让坐标(x,y,w,h)8维,confidence 2维,classification 20维这个三个方面达到很好的平衡。
采用误差的平方和整合了预测框定位误差与有无目标的IOU误差以及分类误差
多尺度检测
端到端训练
模型预测
消除冗余预测框
非极大值抑制(NMS)
如果有多个预测框位置比较接近,只选出得分最高的那个预测框,剩下的预测框被丢弃掉。
核心思想是:选择得分最高的作为输出,与该输出重叠的去掉,不断重复这一过程直到所有备选处理完。
预测框位置是否接近?
-
看iou是否超过阈值
-
超过阈值说明他们重合度很大,很有可能预测的是同一个物体。
-
例如:
YOLO -V3流程总结
附:
L2正则化
L2正则化就是loss function后边所加正则项为L2范数的平方,加上L2正则相比于L1正则来说,得到的解比较平滑(不是稀疏),但是同样能够保证解中接近于0(但不是等于0,所以相对平滑)的维度比较多,降低模型的复杂度。
upsample和subsample
-
subsample:缩小图像(或称为下采样(subsample)或降采样(downsample))的主要目的有两个:
-
使得图像符合显示区域的大小
-
生成对应图像的缩略图。
-
-
upsample:放大图像(或称为上采样(upsampling)或图像插值(interpolating))的主要目的是
-
放大原图像,从而可以显示在更高分辨率的显示设备上。
-
anchors
使用道德anchor的尺寸,如[10,13,16,30,33,23,30,61,156,198,373]
anchor_mask
每个层级上使用的anchor的掩码,[[6,7,8],[3,4,5],[0,1,2]]
conf_thresh
置信度阈值,得分低与该阈值的预测框位置数值不用计算直接设置为0.0
category
为现有的类添加新方法。
好处:可以把类的实现分开在几个不同的文件里面。这样做有几个显而易见的好处
a)可以减少单个文件的体积
b)可以把不同的功能组织到不同的category里
c)可以由多个开发者共同完成一个类
d)可以按需加载想要的category
range
range(start,stop[,step])
-
start: 计数从 start 开始。默认是从 0 开始。例如range(5)等价于range(0, 5);
-
stop: 计数到 stop 结束,但不包括 stop。例如:range(0, 5) 是[0, 1, 2, 3, 4]没有5
-
step:步长,默认为1。例如:range(0, 5) 等价于 range(0, 5, 1)
enumerate()说明
enumerate在字典上是枚举、列举的意思
对于一个可迭代的(iterable)/可遍历的对象(如列表、字符串),enumerate将其组成一个索引序列,利用它可以同时获得索引和值
#如果对一个列表,既要遍历索引又要遍历元素时,首先可以这样写 list1 = ["这", "是", "一个", "测试"] for i in range (len(list1)): print i ,list1[i] #上述方法有些累赘,利用enumerate()会更加直接和优美 list1 = ["这", "是", "一个", "测试"] for index, item in enumerate(list1): print index, item >>> 0 这 1 是 2 一个 3 测试 #enumerate还可以接收第二个参数,用于指定索引起始值 list1 = ["这", "是", "一个", "测试"] for index, item in enumerate(list1, 1): print index, item >>> 1 这 2 是 3 一个 4 测试
listdir()
os.listdir() 方法用于返回指定的文件夹包含的文件或文件夹的名字的列表,
基本语法:os.listdir(path),其中参数需要列出的目录路径 返回值:返回指定路径下的文件和文件夹列表。
np.random.seed()随机数种子
随机数种子,相当于我给接下来需要生成的随机数一个初值,按照我给的这个初值,按固定顺序生成随机数。
通俗理解: 随机数种子是给了一批数(随机数种子应该是按一定顺序生成随机数的,并不是一次给了一批数,但如果这些数有固定顺序,我们是不是可以简单认为随机种子给了我们很多数据),当需要生成随机数时,就从这一批数中依次取值。
例子:
np.random.seed(0) # 先定义一个随机数种子,()中的参数值后下个小节说 print(np.random.rand(5)) # "随机"生成5个数 print(np.random.rand(5)) # 再"随机"生成5个数 np.random.seed(0) for i in range(7): print(np.random.random()) # "随机"生成7个数 # 结果 [0.5488135 0.71518937 0.60276338 0.54488318 0.4236548 ] [0.64589411 0.43758721 0.891773 0.96366276 0.38344152] 0.5488135039273248 0.7151893663724195 0.6027633760716439 0.5448831829968969 0.4236547993389047 0.6458941130666561 0.4375872112626925
观察结果:下边的 ‘ 随机 ’ 生成的7个数和上边 ‘ 随机 ’ 生成的十个数中的前7个相同。
为了方便理解,我们把随机种子看成一个超级超级长的 list ,我们接下来的取随机数操作,( np.random.rand(5) )就是从这个 list 中依次取5个随机数。
np.random.seed(0) for i in range(7): print(np.random.random()) np.random.seed(0) print(np.random.rand(2, 3)) 0.5488135039273248 0.7151893663724195 0.6027633760716439 0.5448831829968969 0.4236547993389047 0.6458941130666561 0.4375872112626925 [[0.5488135 0.71518937 0.60276338] [0.54488318 0.4236548 0.64589411]]
2x3矩阵的6个数 和 随机生成的前6个数相同!
所以如果我想生成两个一样的随机矩阵怎么办?在生成矩阵前,定义随机数种子就好了。
总结:
(1)随机数种子相当于给我们一个初值,之后按照固定顺序生成随机数(也就是我们说的超级长的 list ) (2)随机数种子对后面的结果一直有影响,在一个随机数种子后生成的随机数都受这个随机数种子的影响,即生成的随机数都是由这个随机数种子给的初值,按照固定顺序生成的(生成的随机数受离它最近的随机数种子影响,即它之前的随机数种子)。