【目标检测】SSD目标检测

场景文字识别

目标检测任务的目标是给定一张图像或是视频帧,让计算机找出其中所有目标的位置,并给出每个目标的具体类别。对于人类来说,目标检测是一个非常简单的任务。然而,计算机能够“看到”的仅有一些值为0 ~ 255的矩阵,很难解图像或是视频帧中出现了人或是物体这样的高层语义概念,也就更加难以定位目标出现在图像中哪个区域。与此同时,由于目标会出现在图像或是视频帧中的任何位置,目标的形态千变万化,图像或是视频帧的背景千差万别,诸多因素都使得目标检测对计算机来说是一个具有挑战性的问题。

目标检测

SSD目标检测

|1. 概述

SSD全称:Single Shot MultiBox Detector,是目标检测领域较新且效果较好的检测算法之一[1],有着检测速度快且检测精度高的特点。PaddlePaddle已集成SSD算法,本示例旨在介绍如何使用PaddlePaddle中的SSD模型进行目标检测。下文首先简要介绍SSD原理,然后介绍示例包含文件及如何使用,接着介绍如何在PASCAL VOC数据集上训练、评估及检测,最后简要介绍如何在自有数据集上使用SSD。

|2. SSD原理

SSD使用一个卷积神经网络实现“端到端”的检测:输入为原始图像,输出为检测结果,无需借助外部工具或流程进行特征提取、候选框生成等。论文中SSD使用VGG16[2]作为基础网络进行图像特征提取。但SSD对原始VGG16网络做了一些改变:

  1. 将最后的fc6、fc7全连接层变为卷积层,卷积层参数通过对原始fc6、fc7参数采样得到。
  2. 将pool5层的参数由2x2-s2(kernel大小为2x2,stride size为2)更改为3x3-s1-p1(kernel大小为3x3,stride size为1,padding size为1)。
  3. 在conv4_3、conv7、conv8_2、conv9_2、conv10_2及pool11层后面接了priorbox层,priorbox层的主要目的是根据输入的特征图(feature map)生成一系列的矩形候选框。更详细的介绍可参考[1]。

下图为模型(输入图像尺寸:300x300)的总体结构:

图1. SSD 网络结构

图中每个矩形盒子代表一个卷积层,最后两个矩形框分别表示汇总各卷积层输出结果和后处理阶段。在预测阶段,网络会输出一组候选矩形框,每个矩形包含:位置和类别得分。图中倒数第二个矩形框即表示网络的检测结果的汇总处理。由于候选矩形框数量较多且很多矩形框重叠严重,这时需要经过后处理来筛选出质量较高的少数矩形框,主要方法有非极大值抑制(Non-maximum Suppression)。

从SSD的网络结构可以看出,候选矩形框在多个特征图(feature map)上生成,不同的feature map具有的感受野不同,这样可以在不同尺度扫描图像,相对于其他检测方法可以生成更丰富的候选框,从而提高检测精度;另一方面SSD对VGG16的扩展部分以较小的代价实现对候选框的位置和类别得分的计算,整个过程只需要一个卷积神经网络完成,所以速度较快。

|3.示例总览

本示例共包含如下文件:

表1. 示例文件

训练阶段需要对数据做预处理,包括裁剪、采样等,这部分操作在image_util.py和data_provider.py中完成。

需要注意:config/vgg_config.py是参数配置文件,含有训练参数、神经网络参数等。配置文件中的参数针对PASCAL VOC数据集而配置,当训练自有数据时,需要进行针对性的修改。

data/prepare_voc_data.py脚本用来生成文件列表,包括切分训练集和测试集,使用时需要事先下载并解压数据,默认采用VOC2007和VOC2012。

|4. PASCAL VOC数据集

[1]数据准备

请首先下载数据集:VOC2007[3]和VOC2012[4]。VOC2007包含训练集和测试集,VOC2012只包含训练集,将下载好的数据解压,目录结构为data/VOCdevkit/VOC2007和data/VOCdevkit/VOC2012。

进入data目录,运行python prepare_voc_data.py即可生成trainval.txt和test.txt。核心函数为:

def prepare_filelist(devkit_dir, years, output_dir):

trainval_list = []

test_list = []

for year in years:

trainval, test = walk_dir(devkit_dir, year)

trainval_list.extend(trainval)

test_list.extend(test)

random.shuffle(trainval_list)

with open(osp.join(output_dir, 'trainval.txt'), 'w') as ftrainval:

for item in trainval_list:

ftrainval.write(item[0] + ' ' + item[1] + '\n')

with open(osp.join(output_dir, 'test.txt'), 'w') as ftest:

for item in test_list:

ftest.write(item[0] + ' ' + item[1] + '\n')

该函数首先对每一年(year)数据进行处理,然后将训练图像的文件路径列表进行随机乱序,最后保存训练文件列表和测试文件列表。默认prepare_voc_data.py和VOCdevkit在相同目录下,且生成的文件列表也在该目录次数。

需注意trainval.txt既包含VOC2007的训练数据,也包含VOC2012的训练数据,test.txt只包含VOC2007的测试数据。

下面是trainval.txt前几行输入示例:

```text

VOCdevkit/VOC2007/JPEGImages/000005.jpg VOCdevkit/VOC2007/Annotations/000005.xml

VOCdevkit/VOC2007/JPEGImages/000007.jpg VOCdevkit/VOC2007/Annotations/000007.xml

VOCdevkit/VOC2007/JPEGImages/000009.jpg VOCdevkit/VOC2007/Annotations/000009.xml

```

文件共两个字段,第一个字段为图像文件的相对路径,第二个字段为对应标注文件的相对路径。

[2]预训练模型准备

下载预训练的VGG-16模型,我们提供了一个转换好的模型,下载模型http://paddlepaddle.bj.bcebos.com/model_zoo/detection/ssd_model/vgg_model.tar.gz,并将其放置路径为vgg/vgg_model.tar.gz。

[3]模型训练

直接执行python train.py即可进行训练。需要注意本示例仅支持CUDA GPU环境,无法在CPU上训练,主要因为使用CPU训练速度很慢,实践中一般使用GPU来处理图像任务,这里实现采用硬编码方式使用cuDNN,不提供CPU版本。train.py的一些关键执行逻辑:

paddle.init(use_gpu=True, trainer_count=4)

data_args = data_provider.Settings(

data_dir='./data',

label_file='label_list',

resize_h=cfg.IMG_HEIGHT,

resize_w=cfg.IMG_WIDTH,

mean_value=[104,117,124])

train(train_file_list='./data/trainval.txt',

dev_file_list='./data/test.txt',

data_args=data_args,

init_model_path='./vgg/vgg_model.tar.gz')

主要包括:

  1. 调用paddle.init指定使用4卡GPU训练。
  2. 调用data_provider.Settings配置数据预处理所需参数,其中cfg.IMG_HEIGHT和cfg.IMG_WIDTH在配置文件config/vgg_config.py中设置,这里均为300,300x300是一个典型配置,兼顾效率和检测精度,也可以通过修改配置文件扩展到512x512。
  3. 调用train执行训练,其中train_file_list指定训练数据列表,dev_file_list指定评估数据列表,init_model_path指定预训练模型位置。
  4. 训练过程中会打印一些日志信息,每训练1个batch会输出当前的轮数、当前batch的cost及mAP(mean Average Precision,平均精度均值),每训练一个pass,会保存一次模型,默认保存在checkpoints目录下(注:需事先创建)。

下面给出SDD300x300在VOC数据集(train包括07+12,test为07)上的mAP曲线,迭代140轮mAP可达到71.52%。

图2. SSD300x300 mAP收敛曲线

[4]模型评估

执行python eval.py即可对模型进行评估,eval.py的关键执行逻辑如下:

paddle.init(use_gpu=True, trainer_count=4) # use 4 gpus

data_args = data_provider.Settings(

data_dir='./data',

label_file='label_list',

resize_h=cfg.IMG_HEIGHT,

resize_w=cfg.IMG_WIDTH,

mean_value=[104, 117, 124])

eval(

eval_file_list='./data/test.txt',

batch_size=4,

data_args=data_args,

model_path='models/pass-00000.tar.gz')

调用paddle.init指定使用4卡GPU评估;data_provider.Settings参见训练阶段的配置;调用eval执行评估,其中eval_file_list指定评估数据列表,batch_size指定评估时batch size的大小,model_path指定模型:位置。评估结束会输出loss信息和mAP信息。

[5]图像检测

执行python infer.py即可使用训练好的模型对图片实施检测,infer.py关键逻辑如下:

infer(

eval_file_list='./data/infer.txt',

save_path='infer.res',

data_args=data_args,

batch_size=4,

model_path='models/pass-00000.tar.gz',

threshold=0.3)

其中eval_file_list指定图像路径列表;save_path指定预测结果保存路径;data_args如上;batch_size为每多少样本预测一次;model_path指模型的位置;threshold为置信度阈值,只有得分大于或等于该值的才会输出。下面给出infer.res的一些输出样例:

VOCdevkit/VOC2007/JPEGImages/006936.jpg 12 0.997844 131.255611777 162.271582842 396.475315094 334.0

VOCdevkit/VOC2007/JPEGImages/006936.jpg 14 0.998557 229.160234332 49.5991278887 314.098775387 312.913876176

VOCdevkit/VOC2007/JPEGImages/006936.jpg 14 0.372522 187.543615699 133.727034628 345.647156239 327.448492289

...

共包含4个字段,以tab分割,第一个字段是检测图像路径,第二字段为检测矩形框内类别,第三个字段是置信度,第四个字段是4个坐标值(以空格分割)。

示例还提供了一个可视化脚本,直接运行python visual.py即可,须指定输出检测结果路径及输出目录,默认可视化后图像保存在./visual_res,下面是用训练好的模型infer部分图像并可视化的效果:

图3. SSD300x300 检测可视化示例

|5. 自有数据集

在自有数据上训练PaddlePaddle SSD需要完成两个关键准备,首先需要适配网络可以接受的输入格式,这里提供一个推荐的结构,以train.txt为例

image00000_file_path image00000_annotation_file_path

image00001_file_path image00001_annotation_file_path

image00002_file_path image00002_annotation_file_path

...

文件共两列,以空白符分割,第一列为图像文件的路径,第二列为对应标注数据的文件路径。对图像文件的读取比较直接,略微复杂的是对标注数据的解析,本示例中标注数据使用xml文件存储,所以需要在data_provider.py中对xml解析,核心逻辑如下:

bbox_labels = []

root = xml.etree.ElementTree.parse(label_path).getroot()

for object in root.findall('object'):

bbox_sample = []

# start from 1

bbox_sample.append(float(settings.label_list.index(

object.find('name').text)))

bbox = object.find('bndbox')

difficult = float(object.find('difficult').text)

bbox_sample.append(float(bbox.find('xmin').text)/img_width)

bbox_sample.append(float(bbox.find('ymin').text)/img_height)

bbox_sample.append(float(bbox.find('xmax').text)/img_width)

bbox_sample.append(float(bbox.find('ymax').text)/img_height)

bbox_sample.append(difficult)

bbox_labels.append(bbox_sample)

这里一条标注数据包括:label、xmin、ymin、xmax、ymax和is_difficult,is_difficult表示该object是否为难例,实际中如果不需要,只需把该字段置零即可。自有数据也需要提供对应的解析逻辑,假设标注数据(比如image00000_annotation_file_path)存储格式如下:

label1 xmin1 ymin1 xmax1 ymax1

label2 xmin2 ymin2 xmax2 ymax2

...

每行对应一个物体,共5个字段,第一个为label(注背景为0,需从1编号),剩余4个为坐标,对应的解析逻辑可更改为如下:

bbox_labels = []

with open(label_path) as flabel:

for line in flabel:

bbox_sample = []

bbox = [float(i) for i in line.strip().split()]

label = bbox[0]

bbox_sample.append(label)

bbox_sample.append(bbox[1]/float(img_width))

bbox_sample.append(bbox[2]/float(img_height)) bbox_sample.append(bbox[3]/float(img_width)) bbox_sample.append(bbox[4]/float(img_height))

bbox_sample.append(0.0)

bbox_labels.append(bbox_sample)

同时需要注意根据图像大小及检测物体的大小等更改网络结构相关的超参数,请仿照config/vgg_config.py创建自己的配置文件,参数设置经验请参考论文[1]。

【参考文献】

  • Wei Liu, Dragomir Anguelov, Dumitru Erhan, Christian Szegedy, Scott Reed, Cheng-Yang Fu, Alexander C. Berg. SSD: Single shot multibox detector. European conference on computer vision. Springer, Cham, 2016.
  • Simonyan, Karen, and Andrew Zisserman. Very deep convolutional networks for large-scale image recognition. arXiv preprint arXiv:1409.1556 (2014).
  • The PASCAL Visual Object Classes Challenge 2007
  • Visual Object Classes Challenge 2012 (VOC2012)

原文发布于微信公众号 - PaddlePaddle(PaddleOpenSource)

原文发表时间:2018-03-21

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Petrichor的专栏

深度学习: 经典 数据集 汇总

官网:www.cs.toronto.edu/~kriz/cifar 介绍:CIFAR-10数据集说明、TensorFlow CNN 测试CIFAR-10数据...

4403
来自专栏AILearning

卷积神经网络

注意:本教程面向TensorFlow 的高级用户,并承担机器学习方面的专业知识和经验。 概观 CIFAR-10分类是机器学习中常见的基准问题。问题是将R...

22710
来自专栏量子位

想尝试搭建图像识别系统?这里有一份TensorFlow速成教程

李林 编译整理 量子位 出品 | 公众号 QbitAI 从我们见到的各种图像识别软件来看,机器似乎能认出人脸、猫、狗、花草、各种汽车等等日常生活中出现的物体,但...

5387
来自专栏机器学习算法原理与实践

支持向量机原理(四)SMO算法原理

  在SVM的前三篇里,我们优化的目标函数最终都是一个关于$\alpha$向量的函数。而怎么极小化这个函数,求出对应的$\alpha$向量,进而求出分离超平面我...

1042
来自专栏Python中文社区

支持向量机原理推导(二)

專 欄 ❈ exploit,Python中文社区专栏作者。希望与作者交流或者对文章有任何疑问的可以与作者联系: Email: 15735640998@163....

1985
来自专栏生信小驿站

无监督学习 聚类分析②划分聚类分析

同样是聚类分析,上一次介绍的是层次聚类分法,这种方法输出的聚类树状图是其最大的优点,但是层次分析法的缺点就在于适合的样本数比较小,大概在150个左右。所以,当我...

941
来自专栏AI研习社

YOLO 升级到 v3 版,速度相比 RetinaNet 快 3.8 倍

雷锋网 AI 研习社按,YOLO 是一种非常流行的目标检测算法,速度快且结构简单。日前,YOLO 作者推出 YOLOv3 版,在 Titan X 上训练时,在 ...

1393
来自专栏素质云笔记

caffe︱ImageData层、DummyData层作为原始数据导入的应用

Part1:caffe的ImageData层 ImageData是一个图像输入层,该层的好处是,直接输入原始图像信息就可以导入分析。 在案例中利用Image...

74110
来自专栏听雨堂

用scikit-learn和pandas学习线性回归

      对于想深入了解线性回归的童鞋,这里给出一个完整的例子,详细学完这个例子,对用scikit-learn来运行线性回归,评估模型不会有什么问题了。 1....

2995
来自专栏深度学习思考者

DL开源框架Caffe | 模型微调 (finetune)的场景、问题、技巧以及解决方案

前言 什么是模型的微调?   使用别人训练好的网络模型进行训练,前提是必须和别人用同一个网络,因为参数是根据网络而来的。当然最后一层是可以修改的,因为我们...

5976

扫码关注云+社区

领取腾讯云代金券