一.前言
近期,在研究人工智能机器视觉领域,拜读了深度学习相关资料,在练手期间比较了各前沿的网络架构,个人认为基于darknet53网络结构的yolov3以及retinanet的faster rcnn最合适深度学习工程落地的技术选型。以下是整理的对yolov3的认知解析,同时有个基于人员吸烟检测识别的小工程练手,以望沟通学习交流。后期会继续更新对faster rcnn的认知解析。
二.yolov3理解
you only look once.采用的是多尺度预测,类似FPN;更好的基础分类网络(类ResNet)和分类器。yolov3使用逻辑回归预测每个边界框(bounding box)的对象分数。如果先前的边界框比之前的任何其他边界框重叠ground truth对象,则该值应该为1。如果以前的边界框不是最好的,但是确实将ground truth对象重叠了一定的阈值以上,我们会忽略这个预测。yolov3只为给每个groun truth对象分配一个边界框,如果之前的边界框未分配给grounding box对象,则不会对坐标或类别预测造成损失。yolo3在训练过程中,使用二元交叉熵损失来进行类别预测。yolo3创新地使用了金字塔网络,是端到端,输入图像,一次性输出每个栅格预测的一种或多种物体。每个格子可以预测B个bounding box,但是最终只选择IOU最高的bounding box作为物体检测输出,即每个格子最多只预测出一个问题。当物体占画面比例较小,如图像中包含牲畜或鸟群时,每个格子包含多个物体,但只能检测出其中一个。
1. 分类器:
YOLOv3不使用Softmax对每一个框进行分类,而使用多个logistic分类器,因为Softmax不适用于多标签分类,用独立的多个logistic分类器准确率也不会下降。分类损失采用binary cross-entropy loss.
2. 多尺度预测
每种尺度预测3个box,anchor的设计方式仍然适用聚类,得到9个聚类中心,将其按照大小均分给3种尺度。尺度1:在基础网络之后添加一些卷积层再输出box信息;尺度2:在尺度1中的倒数第二层卷积层上采样(×2)再与最后一个16×16大小的特征图相加,再次通过多个卷积后输出box信息。相比尺度1变大两倍;尺度3:与尺度2类似,使用了32×32大小的特征图。
3.基础网络
基础网络采用Darknet-53(53个卷积层),仿RestNet,与ResNet-10或ResNet-152正确率接近,采用2562563作为输入。基础网络如下:
4.YOLO3算法的基本思想
- 首先通过特征提取网络对输入图像提取特征,得到一定大小的特征图,比如13×13(相当于416416图片大小 ),然后将输入图像分成13 ×13个grid cells,接着如果GT中某个目标的中心坐标落在哪个grid cell中,那么就由该grid cell来预测该目标。每个grid cell都会预测3固定数量的边界框(YOLO v1中是2个,YOLO v2中是5个,YOLO v3中是3个,这几个边界框的初始大小是不同的)
- 预测得到的输出特征图有两个维度是提取到的特征的维度,比如13 × 13,还有一个维度(深度)是 B ×(5+C),注:YOLO v1中是(B×5+C),其中B表示每个grid cell预测的边界框的数量(比如YOLO v1中是2个,YOLO v2中是5个,YOLO v3中是3个); C表示边界框的类别数(没有背景类,所以对于VOC数据集是20),5表示4个坐标信息和一个目标性得分(objectness score)
5.类别预测
- 大多数分类器假设输出标签是互斥的。 如果输出是互斥的目标类别,则确实如此。 因此,YOLO应用softmax函数将得分转换为总和为1的概率。 而YOLOv3使用多标签分类。 例如,输出标签可以是“行人”和“儿童”,它们不是非排他性的。 (现在输出的总和可以大于1)
- YOLOv3用多个独立的逻辑(logistic)分类器替换softmax函数,以计算输入属于特定标签的可能性。在计算分类损失时,YOLOv3对每个标签使用二元交叉熵损失。 这也避免使用softmax函数而降低了计算复杂度。
三.yolov3对比情况
- 算法很快,因为我们把目标检测问题看做一个回归问题,在测试时,我们在整个图片上运行我们的神经网络来进行目标检测
- 在检测过程中,因为是在整个图片上运行网络,和滑动窗口方法和区域提议方法不同,这些方法都是以图片的局部作为输入,即已经划分好的可能存在目标的区域;而yolo则是近以整张图片作为输入,此yolo在检测物体时能很好的利用上下文信息,从而不容易在背景上预测出错误的物体信息。
- yolo可以学到物体的泛化特征,当yolo在自然图像上做训练,在艺术品上做测试时,YOLO表现的性能比DPM、R-CNN等物体检测系统要好,因为yolo可以学习到高度泛化的特征,从而迁移到其他领域。
- 在输入 320 × 320 的图片后,YOLOv3 能在 22 毫秒内完成处理,并取得 28.2 mAP 的检测精准度,它的精准度和 SSD 相当,但是速度要快 3 倍。当我们用老的 .5 IOU mAP 检测指标时,YOLOv3 的精准度也是相当好的。在 Titan X 环境下,YOLOv3 在 51 毫秒内实现了 57.9 AP50 的精准度,和 RetinaNet 在 198 毫秒内的 57.5 AP50 相当,但是 YOLOv3 速度要快 3.8 倍。
- 区域建议方法将分类器限制在特定区域。YOLO在预测边界框时访问整个图像,YOLO在背景区域显示的假阳性更少。
- YOLO每个网格单元检测一个对象。它加强了预测的空间多样性。
- Darknet53新网络比Darknet-19功能强大,也比ResNet-101或ResNet-152更有效,以下是一些ImageNet结果
- 就COCO的mAP指标而言,yolo3与ssd及faster rcnn比较如下
- yolo3与Faster R-CNN、ResNet、SSD等训练算法比较,yolo3的速度与精确度远远大于他们,具体如下:
四.练手工程
1. 基本环境、工具准备:
- win10(x64)
- 显卡gtx1060
- python3.6
- cuda_9.0.176_win10
- cudnn-9.0-windows10-x64-v7
- tensorflow-gpu1.7.0
- pycharm
- labelImg
- voc2007数据集
2. 数据集说明:
- PASCAL VOC challenge: voc挑战在2005年至2012年间展开,该数据集中有20个分类,该数据集包含11530张用于训练和验证的图像,以下是数据集中20个分类:人、鸟、猫、牛、狗、马、羊、飞机、自行车、船、巴士、汽车、摩托车、火车、瓶、椅子、餐桌、盆栽植物、沙发、电视/监视器,平均每个图像有2.4个目标。下载链接:http://host.robots.ox.ac.uk/pascal/VOC/voc2012/
- ImageNet数据集:ImageNet拥有分类、定位和检测任务评估的数据。与分类数据相似,定位任务有1000个类别。正确率是根据Top5检测结果计算出来的,对200个检测问题有470000个图像,平均每个图像有1.1个目标。下载链接:http://image-net.org/download-images
- COCO:MS coco的全称是Microsoft Common Objects in Context,起源于微软与2014年出资标注的Microsoft COCO数据集,与ImageNet竞赛一样,被视为是计算机视觉领域最受关注和权威的比赛之一。在ImageNet竞赛停办后,COCO竞赛就成为是当前目标识别、检测等领域的一个最权威、最重要的标杆,也是目前该领域在国际上唯一能汇集Google、微软、Facebook以及国内外众多顶尖院校和优秀创新企业共同参与的大赛。COO数据集包含20万个图像,80个类别中有超过50万个目标标注,他是广泛公开的目标检测数据库,平均每个图像的目标数为7.2下载链接:http://cocodataset.org/ 类别包含:person,bicycle,car,motorbike,aeroplane,bus,train,truck,boat,traffic light,fire hydrant,stop sign,parking meter,bench,bird,cat,dog,horse,sheep,cow,elephant,bear,zebra,giraffe,backpack,umbrella,handbag,tie,suitcase,frisbee,skis,snowboard,sports ball,kite,baseball bat,baseball glove,skateboard,surfboard,tennis racket,bottle,wine glass,cup,fork,knife,spoon,bowl,banana,apple,sandwich,orange,broccoli,carrot,hot dog,pizza,donut,cake,chair,sofa,pottedplant,bed,diningtable,toilet,tvmonitor,laptop,mouse,remote,keyboard,cell phone,microwave,oven,toaster,sink,refrigerator,book,clock,vase,scissors,teddy bear,hair drier,toothbrush
- 本文练手工具是基于VOC2007格式,通过爬虫工具建立的自己的数据集
3. 训练前准备:
- 建立VOC2007数据集文件夹(ImageSets下还需建立Main文件集),如下:
import os
from config import cfg
def mkdir(path):
# 去除首位空格
path = path.strip()
# 去除尾部 \ 符号
path = path.rstrip("\\")
# 判断路径是否存在
# 存在 True
# 不存在 False
isExists = os.path.exists(path)
# 判断结果
if not isExists:
# 如果不存在则创建目录
# 创建目录操作函数
os.makedirs(path)
print(path + ' 创建成功')
return True
else:
# 如果目录存在则不创建,并提示目录已存在
print(path + ' 目录已存在')
return False
if __name__=='__main__':
rootPath = cfg.ROOT.PATH
mkdir(os.path.join(rootPath, 'Annotations'))
mkdir(os.path.join(rootPath,'ImageSets'))
mkdir(os.path.join(rootPath,'JPEGImages'))
mkdir(os.path.join(rootPath,'ImageSets/Main'))
- 把所有的图片都复制到JPEGImages里面
- 利用LabelImg生成每张图片的配置文件
- 划分训练集、验证集、测试集:过去,人们运用机器学习传统的方法,一般将训练集和测试集划分为7:3,若有验证集,则划分为6:2:2这样划分确实很科学(万级别以下),但到了大数据时代,数据量徒增为百万级别,此时我们不需要那么多的验证集和训练集。这时只需要拉出来1W条当验证集,1W条来当测试集,就能很好的工作了,,甚至可以达到99.5:0.3:0.2。(训练集是用来训练模型的,通过尝试不同的方法和思路使用训练集来训练不同的模型,再通过验证集使用交叉验证来挑选最优的模型,通过不断的迭代来改善模型在验证集上的性能,最后再通过测试集来评估模型的性能。如果数据集划分的好,可以提高模型的应用速度。如果划分的不好则会大大影响模型的应用的部署,甚至可能会使得我们之后所做的工作功亏一篑。),这里因为数据集不多,我们采用9:1开展训练工作
- 在Imagesets根据标注结果xml生成yolo3所需的train.txt,val.txt,test.txt,trainval.txt 保存至ImageSets/Main
import os
import random
from config import cfg
# # trainval集占整个数据集的百分比,剩下的就是test集所占的百分比
trainval_percent = cfg.ROOT.TRAIN_VAL_PERCENT
# train集占trainval集的百分比, 剩下的就是val集所占的百分比
train_percent = cfg.ROOT.TRAIN_PERCENT
rootPath = cfg.ROOT.PATH
xmlfilepath = os.path.join(rootPath, 'Annotations')
txtsavepath = 'ImageSets'
total_xml = os.listdir(os.path.join(rootPath, xmlfilepath))
# 总数据集个数
num = len(total_xml)
list = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list, tv)
train = random.sample(trainval, tr)
#用来训练和验证的图片文件的文件名列表
ftrainval = open(os.path.join(rootPath, 'ImageSets/Main/trainval.txt'), 'w')
#用来测试的图片文件的文件名列表
ftest = open(os.path.join(rootPath, 'ImageSets/Main/test.txt'), 'w')
#是用来训练的图片文件的文件名列表
ftrain = open(os.path.join(rootPath, 'ImageSets/Main/train.txt'), 'w')
#是用来验证的图片文件的文件名列表
fval = open(os.path.join(rootPath, 'ImageSets/Main/val.txt'), 'w')
for i in list:
name = total_xml[i][:-4] + '\n'
if i in trainval:
# 写到训练和验证集
ftrainval.write(name)
if i in train:
# 在训练集里的写到测试集里
ftest.write(name)
else:
# 不在训练集里,写到验证集
fval.write(name)
else:
# 写到训练集
ftrain.write(name)
ftrainval.close()
ftrain.close()
fval.close()
ftest.close()
- 修改yolov3源码的voc_annotation.py文件,执行生产新的三个txt文件:test.txt,train.txt,val.txt
- 使用k-means聚类算法生成对应自己样本的anchor box尺寸
- 修改参数文件yolo3.cfg,找到[yolo]节点,修改以下三个地方,共计三个节点: fiters: 3*(5+len(classes))、classes:len(classes)=1(因为只需要识别烟)、random:默认1,显卡内存小改为0
- 新增model_data下的文件,放入你的类别,coco、voc这两个文件都需要放入,修改类别名称为smoking
4. 修改代码,开始训练。
以下代码是基于原有权重的.weights文件继续训练,需要执行python convert.py -w yolov3.cfg model/yolov3.weights model/yolo_weights.h5 转成keras可用.h5权重文件,然后在源码的train.py训练。若需全新训练的代码,可在我的github下载train.py。(batch_size设为8,epoch设为5000,连续运行18个小时左右,loss将为0.01以下即可。)
import numpy as np
import keras.backend as K
from keras.layers import Input, Lambda
from keras.models import Model
from keras.optimizers import Adam
from keras.callbacks import TensorBoard, ModelCheckpoint, ReduceLROnPlateau, EarlyStopping
import os
import sys
sys.path.append('..')
from yolov3.model import preprocess_true_boxes, yolo_body, tiny_yolo_body, yolo_loss
from yolov3.utils import get_random_data
from config import cfg
def _main():
annotation_path = os.path.join(cfg.ROOT.PATH, 'train.txt')
log_dir = os.path.join(cfg.ROOT.PATH, 'logs/000/')
classes_path = os.path.join(cfg.ROOT.PATH, 'Model/voc_classes.txt')
anchors_path = os.path.join(cfg.ROOT.PATH, 'Model/yolo_anchors.txt')
class_names = get_classes(classes_path)
num_classes = len(class_names)