目录链接:吴恩达Deep Learning学习笔记目录
参考资料:
YOLO系列之yolo v1、YOLO系列之yolo v2、yolo系列之yolo v3【深度解析】
论文:
YOLO1:You Only Look Once:Unified, Real-Time Object Detection
YOLO2:YOLO9000: Better, Faster, Stronger
YOLO3:YOLOv3: An Incremental Improvement
1.YOLO1
2.YOLO2
3.YOLO3
4.tensorflow实现YOLO3
1.YOLO1
1.1 Unified Detection
YOLO算法在一个神经网络中对各个目标进行统一的检测。从整张图片提取特征,同时预测每个类的各个bounding boxes。
①YOLO将输入图片分割为SXS大小个格子,如果一个目标的中心点在这个格子内,那么这个格子将检测到这个目标(这并不意味着目标只有格子大,如上图,狗的中心在格子(5,2)内,但狗的bounding box远比这个格子大)。
如果没有目标的中心落在格子里,那么这个格子的confidence score=0,否则应该为预测box和真实box(标签box)之间的IOU值(交并比)。
③每个bounding box由5个预测值:x,y,w,h,confidence score,分别代表对象坐标,大小和置信分数。
⑤在测试时,获得的概率为类别的条件概率乘以box的confidence score:
1.2 Network
YOLO算法神经网络,一共有24层卷积和来层全连接,输出为7x7x30。
1.3 Training
采用如上图所示的网络的前20卷积层连接一个平均池化层对ImageNet-1000数据进行预训练,预训练后的模型在交叉验证集上(ImageNet2012)top-5准确率达88%。然后将预训好的模型用于检测,在预训练好的模型后连接4个卷积层和两个个全连接层。由于检测通常需要比较细微的纹理,所以将输入图片的大小由224x224x3放大到448x448x448.。
最后一层用于预测类别概率和bouding box的坐标。box的大小标准化到0-1之间(以整张图片大小为基准),box坐标也标准化到0-1之间(以格子大小为基准)。
激活函数:最后一层采用线性激活,其他层采用leaky ReLu,系数为0.1。
loss函数采用方差和:
训练时,为避免过拟合,还采用了dropout和数据增强。
2.YOLO2
YOLO2是对YOLO1的改进,在速度和精度上进行了权衡。由于含标记的检测数据获取的代价较为昂贵,一般规模较小。最常见的检测数据集包含数千到数十万张图像和数十到数百个类别,而分类数据集包含数以百万计的图像和成千上万的类别。YOLO2使用层次图将不同的数据集组合在一起(world tree)。通过有目标标记的数据集(COCO)来学习精确定位,通过图片分类(ImageNet)来提升词表(图像类别)和泛化性能。
2.1 Better
YOLO1相对于Fast-R-CNN,定位误差较大;相对于区域提议法的召回率较低。YOLO2聚焦于在维持分类准确性、快速的基础上改善召回率和定位性能。
(1)Batch Normalization
在所有卷积层上添加Batch Noremalization使得YOLO在mAP上提升2%,同时Batch Normalization也具有一定的正则化作用,所以YOLO2删除了dropout
。
(2)High Resolution Classifier
许多检测方法都是基于ImageNet数据集来预训练的,AlexNet输入图片分辨率小于256x256,YOLO1将输入的分辨率提升到448,这意味着,在学习检测时,模型还要同时去适应这种转化。而在YOLO2中,在分类网络上先采用448x448的图片训练10 epoches,让网络的filters调整到能够适应高分辨率输入,然后再调整网络用于检测。这使得YOLO的mAP提升了4%。
(3)Convolutional With Anchor Boxes
YOLO1采用全连接层直接预测bounding box的坐标。而YOLO从Faster R-CNN中获取了灵感(训练前人工确定bounding box的形状,即Anchor Boxes,然后通过RPN预测Anchor Boxes的偏差和置信评分)。
YOLO2删除了全连接层,采用Anchor Boxes来预测bounding boxes。同时还删除了一层池化层(应该是指在预训练时的最后那一层池化?)保证输出图片的分辨率更高。但在检测网络中,图片输入的分辨率不再是448x448而是416(为了卷积后的输出是是奇数乘奇数的,确保在输出的中心是一个格子,一般目标趋于占据中间位置,在中心有个单独的格子有利于预测),在YOLO2的网络中416经过一系列卷积后,输出大小刚好是13x13,也就是要在13x13个格子中做bouding box预测。
当移动anchor box时,通过预测每个anchor box的类别和目标来代替空间位置和类别的预测机制(空间位置和类别相互作用,这里相对于分开来解决)。采用anchor box后虽然mAP有所降低,但在召回率和每张图片能预测的box数量上提升较大。(实际上最后没没没采用anchor box)
(4)Dimension Clusters
在YOLO中采用anchor box时遇到了两个问题:①anchor box 的大小是手选的(网络只能学习到相近的box,如果先期就选择一个好的anchor box(文中为priors),那么更有利益学习和预测);
YOLO2采用K-means聚类方法来对训练集的bounding boxes来进行自动挑选priors,标准的k-means方法采用的欧几里得距离,这会使得较大的box误差较大,作者想要的是这个priors具有较好的IOU分数,所以在k-means中采用的距离为:
在YOLO中采用anchor box时遇到的第二个问题:②模型不稳定,尤其在迭代早期。这种不稳定来源于box坐标(x,y)的预测,在RPN网络中,预测 tx 和 ty来计算(x,y):
(6)Fine-Grained Features
YOLO2在13x13的特征映射上进行预测检测。这对于小目标的检测效果提升较大。Faster R-CNN和SSD等通过在不同的特征映射上运行RPN来获取不同的分辨率,YOLO2仅是添加一层passthrough层从前面的层提取26x26分辨率的特征。类似于ResNet中的恒等映射。
(7)Multi-Scale Training
由于YOLO2都是卷积层和池化层,对于输入的大小都可以动态的调整,为了使模型更健壮,YOLO2在不同大小的图片上进行训练。每10 epochs改变一次输入图片大小。
(8)Darknet19
darknet19含19层卷积、5层最大池化,相比于VGG-16,运行更快,且能达到同等级准确率。在检测的时候,与YOLO1有所不同,每个格子预测5个box,每个box含5个预测值和20个类别,所以每个格子的长度是125,即filter有125个。YOLO2可以检测多个目标,而在YOLO1一个格子只给出一类的概率。
2.2Stronger
YOLO2通过带标记的数据集来学习bounding box的定位与大小,通过带类别标签的数据集来扩展可检测的类别数量。当网络遇到检测标签数据时,采用YOLO2的loss函数来后向传播,遇到类别标签数据则采用结构中特定的分类部分用于损失传递。
在检测标签类数据中,其标签只是简单的“dog”或“boat”,但在分类数据中,其标签的语义更深,如在ImageNet中,“Norfilk terrier”、“Yorkshire terrier”等都归属于狗类,这里需要一个方法来将这些标签进行合并。
大多数分类方法采用softmax层来对所有类别计算概率分布。这种方法是基于各个类别相互独立的假设上的。但在融合的数据集中,不能采用这种方法来计算概率,因为两个数据集的类别不独立,如“Norfolk terrier”和“dog”不独立。作者采用多类别标签来融合数据集,这种方法不用假设类别独立。
(1)Hierarchical classification
ImageNet的类别标签来自于WordNet(一个语言数据集)。在WordNet中“Norfilk terrier”、“Yorkshire terrier”、都属于“terrier”的下位词,属于“hunting dog”,属于“dog”,属于“canine”等等。而在大多数分类任务中,类别的假设是平坦的(所有类别处于一个层级),而对于融合的数据集,层级化的结构才是我们所需要的。
WordNet是一个有向图结构,而不是树结构,因为语言是复杂的。例如“dog”即属于“canine”也属于“domestic animal”,这在WordNet中是同义词集。作者简化了这种图结构,来用于YOLO2数据集的融合,称为WordTree。
为了构建WordTree,需要检索ImageNet中的类别标签,并在WordNet中沿着路径一直回溯到根节点(physical object)。一些同义词集只有一条路径,所以首先将这些路径添加到WordTree中,然后将剩下的概念词添加到树结构中,并尽量减少路径的数量。例如,如果一个概念词到达根节点需要添加3条边,但另一路径仅仅需要添加一条边,这里会选择较短的路径。
为了将WordTree用于分类,需要预测给定节点同义词集下的每个下位词的条件概率,例如“terrier”节点:
为了验证这个方法,作者用ImageNet-1000来构建WordTree,通过Darknet19来训练。构建WordTree时添加了所有需要的中间节点,标签空间从1000上升到1369。在训练时,会在WordTree上传递标签值,如果一张图片的标签是“Norkfolk terrier”,那么它将获得“dog”、“mammal”等标签。为了计算条件概率,模型将预测出一个向量(长度为1369),将每个节点(同义词集)当作一个数据集,通过softmax计算其下位词基于同义词集的概率分布,如下图所示:
通过WordTree可以将多个数据集融合起来,仅需要将数据集的类别映射到WordTree的同义词集下:
3.YOLO3
3.1 Bounding Box
YOLO3采用同YOLO2的方法来预测bounding box的坐标和大小,但做出了一点改善:在对每个bounding box进行逻辑回归获得预测分数后,如果一个prior与真正box的重叠部分大于其他prior,那么这个prior的置信分数为1,如果一个prior覆盖了真box,但不是最好的,那么通过一个阈值来将其过滤掉(这里取0.5)。如果一个prior没有被分配到任何一个目标,那么它将不会产生关于坐标和类别的loss,仅有是否是目标的loss
3.2 class prediction
在类别预测中不再使用softmax,而是逻辑回归分类器,同时,采用binary cross-entropy作为类别的loss。这样做的原因是,当将模型用于更复杂的数据集如Open Images,这些数据集有相当多的重叠标签(如women和person),如果采用softmax会强制将每个box分配一个类,而实际上并不是。
3.3 prediction across scales
YOLO3在三种尺寸类型上预测box,最后一层用于预测一个3-d tensor,用于编码bounding box、objectness、和类别,在每个尺寸上预测3个box,所以这个tensor为N x N x(3*(4+1+80)),即4个box的高宽及坐标、1个object、80个类别。
随后,从将倒数两层的输出分辨率 x 2,再和倒数4层的输出进行结合,后输入给另一个尺寸的预测器,这样做的好处是,可以通过上采样提取一些有意义的特征和低层的细微纹理特征。再重复操作一次,获得第三种尺寸的预测。通过聚类的方法总共获取9个prior,每种尺寸上3个。
4.tensorflow2.0 实现YOLO
下文中代码和图片都参照YunYang1994大神的分享YOLOv3 算法的一点理解
4.1 YOLO模型结构
YOLO模型结构如下图所示:
4.2 代码说明
文件目录
├─data
│ ├─anchors # prior存放文件夹
│ ├─classes # 类别名存放文件夹
│ └─dataset # 训练数据存放文件夹
├─model_dir # 模型存放文件夹,存放权重数据
│ └─log # 训练日志存放文件夹
├─__init__.py
├─backbone.py # 主干网络结构
├─common.py # 构建网络共用层结构
├─config.ini # 配置文件
├─dataset.py # 构建datasei迭代器
├─generate_data_txt.py # 生成图片路径及lablel对应txt文件
├─getConfig.py # 配置信息获取
├─image_detect.py # 测试图片
├─train.py # 训练
├─utils.py # 工具函数
├─yolo3.py # yolo结构
└─yolov3.weights # yolov3官方权重文件
4.2.1 配置文件及读取
config.ini
:配置文件
为方便解析,将配置文件中含4个section,分别为strings、ints、floats、lists。
getConfig.py
:读取配置信息文件
读取配置信息后返回一个dict,key-value对为配置文件中各键值对。注意:configparser读取出来的value都是str,想要获取整数、列表想要进行转换。
4.2.3 utils文件
内置函数为:read_class_names()
读取类别名称,get_anchors()
获取anchors,image_preprocess()
图片预处理,draw_bbox()
绘制带box的图片,bboxes_iou()
计算bounding boxes之间的IoU,nms()
非极大值限制函数,load_weights()
官方权重文件加载,load_partial_weights()
部分权重加载,postprocess_boxes()
预测box后处理。
read_class_names()
:读取类别名文件,生成一个字典,key=索引,value=类别名
get_anchors()
:读取prior anchors文件,文件中为一行以逗号分割的数字,记录box的高宽。返回一个np.array,reshape为(3,3,2),指三种类型prior anchors,每种类型下又有三种。
image_preprocess()
:用于将图片预处理为模型输入大小。在处理过程中,是将图片按照原图片高宽与目标大小高宽之间的最小比例缩放,在无像素值的位置填充灰度值128。如下图所示
draw_bbox()
:绘制图片,对object进行bounding box标注,并在box左上角写出置信度和object所属类别。首先根据所有类别名构建不同颜色的映射(采用hsv转换),用于不同类别绘制不同颜色box。然后通过cv2库进行图片、box、文字标注绘制。如果是预测的box,应该进行处理还原到原图对应的坐标。
bboxes_iou()
:计算bounding boxes之间的IoU值,输入值为两个二维数组,每一行为一个box,4列分别为左上角和右下角坐标x_min,y_min,x_max,y_max
。返回值为n行1列数组,对应每一行对应box之间iou。
nms()
:非极大值限制函数,输入值为一个二维数组,每一行为一个box,各列依次为x_min,y_min,x_max,y_max,prob,class
,代码中若采用softnms,采用的高斯加权,nms和softnms之间区别参见:NMS和soft-nms算法
load_weights()
:官方权重文件为weights格式,不能直接加载到tf中,如果要将官方权重迁移到tf写的yolo中,需要写一个函数来进行加载。首先对Yolov3结构进行分析如下:
load_partial_weights()
:这个函数的目的是读取时忽略输出层参数,仅将其他层参数迁移到tf写的model中,以仅训练三个输出层。
postprocess_boxes()
:对于预测的bbox,还原到原图坐标系中去。输入预测信息(二维数组,行为预测的box,列依次为坐标,置信度,one-hot编码类别)、原图大小、模型输入大小。将预测box坐标由x_center,y_center,w,h
转化为x_min,y_min,x_max,y_max
。缩放回原图比例,修改box坐标超出原图左上角和右下角的坐标,剔除x_min > x_max或y_min > y_max的box。求各个box面积,筛选面积大于0小于inf的。筛选置信度大于设定阈值的。返回还原后的box,包含坐标、置信度、类别。
4.2.4 dataset文件
用于构造一个迭代器,对模型输入数据。同时对图片进行数据增强、prior anchor标签绑定。包含__init__()
,load_annotation()
,random_horizotal_flip()
,random_crop()
,random_translate()
,parse_annotation()
,bbox_iou()
,preprocess_true_boxes()
,__iter__()
,__next__()
,__len__()
。
__init__()
:设置初始化参数
load_annotation()
:读取image_path - ground_truth. txt文件
random_horizotal_flip()
:随机水平翻转
random_crop()
:随机裁剪
random_translate()
:随机平移
parse_annotation()
:解析annotation,输入一行annotation(图片路径+真实box),读取图片并进行数据增强,BGR2RGB。通过utils.image_preprocess()
处理为模型输入大小。
bbox_iou()
:输入两个二维数组,列为[x_center,y_center,h,w],先转化为[ x_min,y_min,x_max,y_max]再计算IoU。
preprocess_true_boxes()
:输入一张图片ground truth,为一个二维数组,列为[ x_min,y_min,x_max,y_max,class_id]。返回label_bbox(ground truth转化后三类绑定了prior的输出)和bboxes(ground truth转化后三类输出)。①构建一个label列表,含3类0值数组,每个数组对应模型的三个输出之一:
__iter__()
:实现了 next() 方法并通过 StopIteration 异常标识迭代的完成
__next__()
:用于返回下一个迭代对象。①构造图片存储数组,label_bbox,bboxes存储数组。②每次返回一个batch的数据,首先获取每条annotation,并解析,通过preprocess_true_boxes()
获取label_bbox,bboxes,追加到前述构造的数组中,每次返回一个batch的image,label_bbox,bboxes。
__len__()
:返回batch数量。
4.2.5 common文件
主要用于定义基础卷积块、残差块、上采样块。含BatchNormalization()
类方法重构、conv()
、res_block()
、upsample()
。
BatchNormalization()
类方法重构:在keras.BatchNormalization
源码中,有一个trainining
参数,默认None
;如果training=True/None
,训练时,batch normalization依赖于当前batch的moving average和variance,但是在推理模式下,却依赖于整个训练集的average和variance;如果training=False
,训练时,batch normalization依赖于每个batch的moving average和variance,但是在推理模式下,依赖于整个训练集的average和variance。显然我们希望实现后一种情况,但不能通过层的trainable
参数来控制,这是两个概念,trainable
控制了在归一化时是否更新beta和gamma,而training
则控制了使用什么类型的均值和方差来进行归一化。所以需要将BatchNormalization中的call函数中training默认改为False。
conv()
:卷积块,整个网络用到的卷积块,从缩放上来看分为两种一种无缩放,一种缩小两倍。还设置了是否归一化、激活、添加偏置(如果没有归一化时添加偏置)
res_block()
:残差块,两个卷积块进行跳跃连接
upsample()
:采用两种方法,一种是直接resize,一种则是反卷积。
4.2.6 backbone文件
定义darknet53网络
4.2.7 yolo3文件
含yolo3()
、decode()
、bbox_iou()
、bbox_giou()
、loss_layer()
yolo3()
:定义yolo3神经网络,返回值为三种类型的预测输出,每个格子为长3*(num_class+5)的向量。
decode()
:首先将yolo3的输出转化为[batch_size,output_size,output_size,3,5+num_class],将预测得到的xy偏移量转化到输出大小的坐标上去,将,依据prior和预测得到的wh偏移量进行调整得到预测box的wh,同时对置信度和类别进行激活。
bbox_iou()
:输入两个二维数组,列为[x,y,w,h],计算IoU
bbox_giou()
:输入两个二维数组,列为[x,y,w,h],计算giou值,参见GIOU:对IOU的优化。
loss_layer()
:计算loss。①获取yolo3网络输出和decode后的pred输出;②获取label中的xywh,类别概率分布,获取respond_bbox=label[...,4:5]这个表示格子中存在obj的话标记为1其余为0,与giou相乘即计算有obj的损失;③bbox_loss_scale作为一个放大系数,对于小目标而言,目标越小,这个值越大,与giou相乘后,giou框回归损失越大。④计算置信度损失:计算预测bbox和*bboxes的iou,并求最大值,如果最大值小于阈值则认为是背景。计算前景和背景的置信度损失。⑤计算分类损失。
4.2.8 train文件
用于训练数据,训练时加载了官方权重,并锁定了一些层权重不可更新。
4.2.9 image_detect文件
预测并绘图。