时间: 2020-09-03 00:08:26 人气: 2286 评论: 0
这是《使用腾讯云GPU学习深度学习》系列文章的第六篇,本文以如何识别马路上的行人、车辆为主题,介绍了基于 Tensorflow 的 SSD 模型如何应用在物体识别定位项目中。本系列文章主要介绍如何使用腾讯云GPU服务器进行深度学习运算,前面主要介绍原理部分,后期则以实践为主。
往期内容:
我们在第三讲中,提到过如何搭建一个简单的深度神经网络,识别一张图片中的单一物体。进而在上一节中,以车牌为例,谈到如何识别多个物体。
但是实际上,上一讲提到的技术,在实际应用过程中,会遇到问题,就是我们需要识别的物体,未必会给我们机会去“摆拍”。比如对于车牌识别,我们想要的数据是这样的:
但是我们从摄像头拿到的车牌数据,可能是这样的:
这下我们上一讲提到的 “93% 识别准确率”,可能就一点用都没有了,因为我们 不仅要识别目标,更需要对目标进行定位——没有人会帮我们用截图工具从第二张图中“抠图”,得到第一张图中的样式,再给我们去训练深度学习模型。而我们接下来要讲的,就是如何同时实现物体的识别与定位。这里我们以 SSD 模型为例,谈一谈如何在上图中,识别车辆和行人。
首先是物体 识别问题,这里回顾下第三讲最后的 CIFAR-10 分类。
CIFAR-10 给出了物体的截图信息:
下一步可以根据这些截图,训练一个分类器:
model.fit_generator(datagen.flow(X_train, Y_train, batch_size=batch_size), nb_epoch=nb_epoch, validation_data=(X_test, Y_test), callbacks=[tb])
我们训练好分类器 model 之后,可以载入新的图片,用训练好的模型预测分类结果:
# 使用训练好的模型,预测输入图片属于 CIFAR-10 哪个类 import cv2 NEW_IMG_MATRIX = cv2.imread("test.png") NEW_IMG_MATRIX = cv2.resize(NEW_IMG_MATRIX, (32, 32)) predictions = model.predict(NEW_IMG_MATRIX)
有了分类器,接下来要做的,就是 用分类器扫描整张图像,定位特征位置。这里关键的方法,就是用什么算法扫描,比如可以将图片分成若干网格,用分类器一个格子、一个格子扫描,这种方法有几个问题:
针对第一点,可以采用相互重叠的网格。比如一个网格大小是 32x32 像素,那么就网格向下移动时,只动 8 个像素,走四步才完全移出以前的网格。针对第二点,可以采用大小网格相互结合的策略,32x32 网格扫完,64x64 网格再扫描一次,16x16 网格也再扫一次。
但是这样会带来其他问题——我们为了保证准确率,对同一张图片扫描次数过多,严重影响了计算速度,造成这种策略 无法做到实时标注。也就是说,如果应用在无人驾驶汽车时,如果前面突然出现一个行人,此时模型一遍一遍扫描整个图片、终于完成标注之后,可能已经是三四秒以后了,此时汽车已经开出去几十米,会造成很大的危险。
于是,为了快速、实时标注图像特征,对于整个识别定位算法,就有了诸多改进方法。
一个最基本的思路是,合理使用卷积神经网络的内部结构,避免重复计算。如前几篇文章所述,用卷积神经网络扫描某一图片时,实际上卷积得到的结果已经存储了不同大小的网格信息,这一过程实际上已经完成了我们上一部分提出的改进措施,如下图所示,我们发现前几层卷积核的结果更关注细节,后面的卷积层结果更加关注整体:
对于问题1,如果一个物体位于两个格子的中间,虽然两边都不一定足够显著,但是两边的基本特征如果可以合理组合的话,我们就不需要再扫描一次。而后几层则越来越关注整体,对问题2,目标可能会过大过小,但是特征同样也会留下。也就是说,用卷积神经网络扫描图像过程中,由于深度神经网络本身就有好几层卷积、实际上已经反复多次扫描图像,以上两个问题可以通过合理使用卷积神经网络的中间结果得到解决。
我们先回顾下 SSD 之前的算法,在 SSD 算法之前,MultiBox,FastR-CNN 法都采用了两步的策略,即第一步通过深度神经网络,对潜在的目标物体进行定位,即先产生Box;至于Box 里面的物体如何分类,这里再进行第二步计算。此外第一代的 YOLO 算法可以做到一步完成计算加定位,但是结构中采用了全连接层,而我们在 第三讲 的 1.3.3. 部分提到过全连接层有很多问题,并且正在逐步被深度神经网络架构“抛弃”。
图片来源山人七深度学习知乎专栏
SSD 就是一种一步完成计算、不采用全连接层的计算架构。一步到位的计算框架可以使计算速度更快,并且由于完全抛弃全连接层, 第三讲 的 1.3.3. 部分提到全连接层带来的过拟合问题也会得到改善。:
图片来源山人七深度学习知乎专栏
图片来源山人七深度学习知乎专栏
大体思路就是,用VGG 深度神经网络的前五层,在额外多加六层结构。然后提取其中几层进过卷积后的结果,进行网格搜索,找目标特征。
代码:
default_params = SSDParams( img_shape=(300, 300), num_classes=21, no_annotation_label=21, feat_layers=['block4', 'block7', 'block8', 'block9', 'block10', 'block11'], feat_shapes=[(38, 38), (19, 19), (10, 10), (5, 5), (3, 3), (1, 1)], anchor_size_bounds=[0.15, 0.90], anchor_sizes=[(21., 45.), (45., 99.), (99., 153.), (153., 207.), (207., 261.), (261., 315.)], anchor_ratios=[[2, .5], [2, .5, 3, 1./3], [2, .5, 3, 1./3], [2, .5, 3, 1./3], [2, .5], [2, .5]], anchor_steps=[8, 16, 32, 64, 100, 300], anchor_offset=0.5, normalizations=[20, -1, -1, -1, -1, -1], prior_scaling=[0.1, 0.1, 0.2, 0.2] ) def ssd_net(inputs, num_classes=default_params.num_classes, feat_layers=default_params.feat_layers, anchor_sizes=default_params.anchor_sizes, anchor_ratios=default_params.anchor_ratios, normalizations=default_params.normalizations, is_training=True, dropout_keep_prob=0.5, prediction_fn=slim.softmax, reuse=None, scope='ssd_300_vgg'): """SSD net definition. """ end_points = {} with tf.variable_scope(scope, 'ssd_300_vgg', [inputs], reuse=reuse): ###################################### # 前五个 Blocks,首先照搬 VGG16 架构 # # 注意这里使用 end_points 标注中间结果 # ###################################### # Block 1. net = slim.repeat(inputs, 2, slim.conv2d, 64, [3, 3], scope='conv1') end_points['block1'] = net net = slim.max_pool2d(net, [2, 2], scope='pool1') # Block 2. net = slim.repeat(net, 2, slim.conv2d, 128, [3, 3], scope='conv2') end_points['block2'] = net net = slim.max_pool2d(net, [2, 2], scope='pool2') # Block 3. net = slim.repeat(net, 3, slim.conv2d, 256, [3, 3], scope='conv3') end_points['block3'] = net net = slim.max_pool2d(net, [2, 2], scope='pool3') # Block 4. net = slim.repeat(net, 3, slim.conv2d, 512, [3, 3], scope='conv4') end_points['block4'] = net net = slim.max_pool2d(net, [2, 2], scope='pool4') # Block 5. net = slim.repeat(net, 3, slim.conv2d, 512, [3, 3], scope='conv5') end_points['block5'] = net net = slim.max_pool2d(net, [3, 3], stride=1, scope='pool5') #################################### # 后六个 Blocks,使用额外卷积层 # #################################### # Block 6: let's dilate the hell out of it! net = slim.conv2d(net, 1024, [3, 3], rate=6, scope='conv6') end_points['block6'] = net net = tf.layers.dropout(net, rate=dropout_keep_prob, training=is_training) # Block 7: 1x1 conv. Because the fuck. net = slim.conv2d(net, 1024, [1, 1], scope='conv7') end_points['block7'] = net net = tf.layers.dropout(net, rate=dropout_keep_prob, training=is_training) # Block 8/9/10/11: 1x1 and 3x3 convolutions stride 2 (except lasts). end_point = 'block8' with tf.variable_scope(end_point): net = slim.conv2d 技术沙龙 教程文章 热点综合