Python 深度学习架构实用指南:第一、二部分(5)

简介: Python 深度学习架构实用指南:第一、二部分(5)

Python 深度学习架构实用指南:第一、二部分(4)https://developer.aliyun.com/article/1426974

现在,是时候添加全连接层了。 要添加全连接层,我们首先需要将来自上一层的输出展平。 您可以在 TensorFlow 中使用Flatten()函数,也可以重塑上一层的输出:

with tf.name_scope('flatten') as scope:
      flatt = tf.layers.Flatten()(conv5)
      #shape = conv5.get_shape().as_list()
      #flatt = tf.reshape(conv5, [-1, shape[1]*shape[2]*shape[3]])

我们将添加三个全连接层,其单元分别为 1,024、512 和 256。这些层将使用前面定义的dropOutrely激活函数。 全连接层也称为密集层,因为它们创建具有全局连接的密集结构:

with tf.name_scope('dense_1') as scope:
      dense1 = tf.layers.dense(flatt, units = 1024, activation = 'relu',name='fc_1')
      dropOut1 = tf.nn.dropout(dense1, self.keepProb)
    with tf.name_scope('dense_2') as scope:
      dense2 = tf.layers.dense(dropOut1, units = 512, activation = 'relu',name='fc_2')
      dropOut2 = tf.nn.dropout(dense2, self.keepProb)
    with tf.name_scope('dense_3') as scope:
      dense3 = tf.layers.dense(dropOut2, units = 256, activation = 'relu',name='fc_3')
      dropOut3 = tf.nn.dropout(dense3, self.keepProb)

输出层也将是一个全连接层,不同之处在于我们将在此层中不使用任何激活函数:

with tf.name_scope('out') as scope:
        outLayer = tf.layers.dense(dropOut3, units = self.classNum, activation = None, name='out_layer')

当我们为深度前馈网络定义损失函数和优化器时,我们将在此处类似地对其进行定义:

with tf.name_scope('loss') as scope:
      self.loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits = outLayer, labels = self.y))
    with tf.name_scope('optimizer') as scope:
      optimizer = tf.train.AdamOptimizer(learning_rate = self.learningRate)
      self.train = optimizer.minimize(self.loss)
    with tf.name_scope('accuracy') as scope:
      correctPredictions = tf.equal(tf.argmax(outLayer, axis=1), tf.argmax(self.y, axis = 1))
      # calculating average accuracy
      self.avgAccuracy = tf.reduce_mean(tf.cast(correctPredictions, tf.float32))

现在,让我们创建model类的对象以启动模型图:

modelGraph = model(batchSize = BATCH_SIZE, classNum = CLASS_NUM, dropOut = DROPOUT,
          learningRate = LEARNING_RATE, epochs = EPOCHS, imageSize = IMAGE_SIZE,       savePath = 'model')

接下来,我们将创建一个 TensorFlow 会话并在批量文件周围循环。 对于从 1 到 5 的每个批量文件,我们将创建一个数据类的对象,并调用我们创建的函数来加载和预处理数据。 此外,generate_batches函数不断生成用于训练的批量。 您可以保存模型,例如,每隔 10 个时间段:

with tf.Session() as sess:
  sess.run(tf.global_variables_initializer())
  saver = tf.train.Saver()
  for epoch in range(modelGraph.epochs):
    for iBatch in range(1, 6):
      dataObj = data(DATA_DIR, 'data_batch_' + str(iBatch), BATCH_SIZE, SEED)
      dataObj.load_data_batch()
      dataObj.reshape_data()
      #dataObj.visualise_data([100, 4000, 2, 8000])
      dataObj.one_hot_encoder()
      dataObj.normalize_images()
      dataObj.shuffle_data()
      #print(dataObj.generate_batches()[0])
      for batchX, batchY in dataObj.generate_batches():
        #print(batchX[0])
        #print(batchY[0])
        _, lossT, accT = sess.run([modelGraph.train, modelGraph.loss, modelGraph.avgAccuracy],
                feed_dict = {modelGraph.x: batchX, modelGraph.y: batchY, modelGraph.keepProb: modelGraph.dropOut})
        print('Epoch: '+str(epoch)+' Minibatch_Loss: '+"{:.6f}".format(lossT)+' Train_acc: '+"{:.5f}".format(accT)+"\n")
      if epoch % 10 == 0:
        saver.save(sess, modelGraph.savePath)

下一部分将讨论使用 CNN 进行对象检测的任务。 我们将学习一些成功的对象检测架构,并使用 TensorFlow 实现对象检测。

使用 CNN 的对象检测

我们在日常生活中遇到的大多数自然图像都不是由覆盖整个图像的单个对象组成的。 通常,它是位于不同位置的不同对象的混合体。 在这种情况下,简单的对象识别将行不通。 因此,检测图像中存在的各种物体及其位置变得具有挑战性。 这是深度学习大放异彩的地方!

因此,对象检测可以分为两部分:

  • 对象定位:确定图像中对象的xy坐标
  • 对象识别:确定位置是否有对象,如果有,则确定是什么对象

因此,对象检测网络具有两个单独的子网来执行这两个任务。 第一个网络在图像中生成不同的兴趣区域,而第二个网络对它们进行分类。

R-CNN

这是用于对象检测的深度学习方法的早期阶段之一。 它利用选择性搜索算法生成区域提议。 区域提议是图像中任何包含对象的可能性很大的长宽比的有界框。 选择性搜索是一种基于图的算法,该算法首先使用像素强度划分区域,然后根据颜色,纹理和大小对它们进行分层分组以生成区域。 该算法的问题在于,即使对于低分辨率图像,它也会产生太多区域提议。 因此,R-CNN 将区域提议限制为 2,000。

将这些建议调整为相同的形状,然后输入到 CNN 网络中,该网络将从区域中提取特征并输出特征向量。 每个类别的对象都有一个 SVM 分类器,该分类器被馈入该特征向量以预测包含该对象的区域的概率。 同样,相同的特征向量被馈送到线性回归器中以预测对象边界框中的偏移。 尽管区域提议包含对象,但可能不会涵盖整个对象。 因此,预测偏移量有助于校正边界框的坐标。 下图显示了 R-CNN 架构中涉及的步骤:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rnKFQIGh-1681704767273)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/0ecedb2a-76fe-43c4-bd37-6aed5ca377ba.png)]

可以在这个页面上找到《用于准确的对象检测和语义分割的丰富的特征层次结构》本文的链接。

接下来,我们将研究通过将选择性搜索替换为单独的区域提议网络来改进 R-CNN 的架构。

更快的 R-CNN

由于为每个图像生成了 2,000 个区域提议,因此 R-CNN 速度相当慢。 选择性搜索算法也不总是产生好的候选区域提议。 更快的 R-CNN 引入了区域提议网络以生成区域提议,从而取代了选择性搜索算法。 它具有以下功能:

  • 最初从输入图像中提取特征映射的 CNN
  • 九个锚点(三个比例和三个比例),以覆盖特征映射中不同大小和比例的对象
  • 区域提议网络RPN),以生成感兴趣的区域并对其进行排名
  • 兴趣区域ROI)合并以将不同形状的提案重塑为固定大小

首先,将输入图像输入到 CNN 中以生成特征映射。 特征映射进入 RPN,RPN 使用3 x 3卷积核将特征映射调整为固定大小。 对于特征映射上的每个点,将预测九个锚框以及它们的无对象(是否存在对象)和边界框坐标(中心 x,中心 y,宽度和高度)。 RPN 生成的许多建议彼此重叠。 通过非最大抑制来消除重叠的边界框,该非最大抑制将计算边界框的交并比Iou),并消除具有大于设置阈值分数的框。 RPN 为我们提供了建议的区域,但大小不同。 为了通过 R-CNN 对它们进行分类,我们需要获得相同大小的建议。 ROI 池通过将建议区域划分为相等数量的部分,然后应用最大池化来执行工作。 这样,无论初始大小如何,输出将始终为固定大小。 然后将这些 ROI 合并的输出馈送到 R-CNN 模块中进行分类。 在下图中,您可以观察到架构的完整管道:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0MU3h50v-1681704767273)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/90d06ae2-4f5f-4f1f-90b1-4c35fdb5f78c.png)]

图片来自纸本,更快的 R-CNN:使用区域提议网络实现实时目标检测

可以在这个页面上找到 Faster-RCNN 原始论文的链接。

接下来,我们将研究另一类对象检测器,即基于回归的对象检测器,它大大简化了对象检测的任务。

你只看一次(YOLO)

到目前为止,我们已经讨论过的对象检测架构依赖于区域提议(通过选择性搜索或单独的区域提议网络)。 这些类型的架构的问题在于,由于内部存在多个网络的集成,它们的实现非常复杂。 这些架构涉及大量参数,这使它们在计算上过于昂贵。 而且,网络首先提出了许多感兴趣的区域,这使得不可能实时执行检测。

为了应对这些挑战,Joseph Redmon,Santosh Divvala,Ross Girshick 和 Ali Farhadi 在 2015-16 年开发了一种新的基于预测(基于回归)的架构,该架构能够进行实时检测。 该架构称为 YOLO ,它是您只看一次的缩写。 YOLO 是一种端到端可训练的架构,仅使用单个 CNN 来检测对象。

YOLO 将图像划分为S x S网格。 为每个网格预测两个边界框,以及对象属于特定类别的概率。 边界框大小不限于在网格内部。 每个边界框具有五个预测值-(xywh)。xy表示边界框相对于网格的中心,而wh表示网格的宽度和高度。 相对于图像尺寸的边界框。 因此,网络进行S x S x (B x 5 + C)预测,其中B是每个单元格(例如两个)和CC类的类概率。 您现在会注意到,该网络依赖于预测值,因此是基于回归的对象检测网络。 在下图中,您可以观察如何将图像划分为网格以预测边界框和类分数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hQDdSlby-1681704767274)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/477e049a-49c7-40b3-8a65-4615bd81cd4f.png)]

图片取自本文,您只看一次:统一的实时对象检测

论文的链接《只看一次:统一的实时对象检测》,可在这里找到。

YOLO 使用 24 个卷积层。 这些层遵循简单的结构,一个接一个地重复使用1 x 11 x 1卷积。 最后,存在两个全连接层以输出预测的张量。 下图中可以看到该架构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0ttcWokm-1681704767274)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/e88ddb72-9c5a-4de7-926d-0f5798a6e5c3.png)]

图片来自本文,《只看一次:统一的实时对象检测》

YOLO 中使用的损失函数可分为四个部分:

  • 边界框的位置xy的预测的平方和损失
  • 在边界框的宽度和高度中进行预测的平方根损失
  • 边界框置信度得分的损失
  • 分类损失

以下公式包含 YOLO 的组合损失函数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fbNtiKHZ-1681704767274)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/6154dd4a-bbd5-474c-8c2c-4931ad15fda3.png)]

图片出自原始论文,“您只看一次”:统一的实时对象检测

损失函数中的第一项采用所有B边界框预测变量的边界框位置差的平方和。 第二项使用宽度和高度做相同的事情。 您会注意到额外的平方根。 这组作者说,大边界框中的小偏差比小边界框中的小偏差要小。 对项进行平方根运算有助于我们降低对较大值的敏感度。 我们还预测了置信度分数C[i],以及边界框(预测边界框时模型的置信度)。 损失函数中的第三项与置信度得分有关。 损失函数中的最后一项是关于将对象分类为不同类别的。

尽管 YOLO 极大地简化了对象检测架构并能够实时进行预测,但是也存在某些缺点。 该模型不会提取不同比例的特征,因此对于不同大小和比例的对象不具有鲁棒性。 该模型还难以检测组合在一起的较小尺寸的对象。 接下来,我们将研究另一种基于回归的对象检测架构,即单发多框检测器SSD),该架构可弥补 YOLO 的缺点。

单发多框探测器

与 YOLO 一样,SSD 也是基于回归的对象检测器,但是 SSD 的创建者声称 SSD 比 YOLO 更快,更准确。 我们可以将 SSD 分为四个主要部分:

  • 基本网络 - VGG16
  • 多个比例的特征映射
  • 用于边界框预测的卷积
  • 用于预测的默认边界框

任何卷积网络的首要任务是减小输入的尺寸并增加特征映射的深度,以便提取特征。 然后,可以将特征映射中提取的特征用于不同任务,无论是分类还是检测。 SSD 也一样! SSD 使用著名的 VGG16 架构作为模型的初始层(基础网络)进行特征提取(请记住,这与 YOLO 不同,因为图像本身首先被划分为网格,然后将卷积应用于预测)。 VGG16 架构末尾的全连接层已被删除,因为使用 VGG16 的目的只是为了提供丰富的特征来学习而非分类。 在改良的 VGG16 网络的末端,SSD 引入了六层以上的卷积。 这些额外的六层的大小逐渐减小。 添加额外层的目的是使网络能够从不同大小和不同比例的对象中提取特征。 这就是为什么这些层中的特征映射的大小不断减小(多比例缩放的特征映射)的原因。 下图显示了 SSD 的总体架构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vUFfPshG-1681704767274)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/f30e4535-41dc-4b52-94e1-562e8fe946b9.png)]

图片来自纸张,SSD:单发多框检测器

《SSD:单发多框检测器》原始文件的链接可以在以下网址找到

第一个特征映射集是从 VGG 16 架构的第 23 层提取的,大小为38 x 38 x 512(此处 512 是过滤器的深度或数量)。 第二组特征映射的大小为19 x 19 x 1,024,适用于捕获稍大的对象。 进一步的特征映射集将尺寸减小到10 x 10 x 5125 x 5 x 2563 x 3 x 256,最后减小到1 x 1 x 256

为了进行预测,SSD 在提取的特征映射上使用3 x 3 x dd表示过滤器的深度)卷积核。 对于特征映射上的每个点,3 x 3核输出边界框偏移量和类分数。 SSD 为特征映射中的每个点分配了默认框。 3 x 3卷积的工作是从覆盖对象的默认边界框预测四个偏移值。 除偏移量外,它还可以预测类别的c类别分数。 如果我们在每个位置都有m x n尺寸特征映射,并且在每个位置都有k默认边界框,则从该层做出的预测总数将为(c + 4) x k x m x n。 每个位置的默认框数通常为 4 到 6。 这些默认边界框的比例和大小由网络中最低和最高特征映射的比例决定。 假设我们有m个特征映射; 然后,默认边界框的比例(s[k])由以下公式给出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ViMwWEMg-1681704767275)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/93c36209-1288-4964-b35e-b1a8b03d31fa.png)]

在此, s_min是最低特征映射的比例, s_max是最高特征映射的比例。 然后,默认框的高度和宽度由以下关系定义:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kmBcW432-1681704767275)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/d3f41649-c78b-4cd0-8e69-d67a555b0e87.png)]

以下屏幕快照说明了使用8 x 8函数图和8 x 8函数图进行 SSD 预测。 边界框偏移,Δ(cx, cy, w, h)p类的类分数(c[1], c[2], ..., c[p])的预测:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CWGwYLls-1681704767275)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/5b9fce5d-d17d-4e37-9c44-665f6047ddf6.png)]

图片来自纸张,SSD:单发多框检测器

SSD 中使用的损失函数是定位损失和分类损失的组合。 本地化损失是按以下方式定义的平滑 L1 损失:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NiUA0SQ6-1681704767275)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/ddd6a2ca-05a3-45d2-8f1b-a0cb254f967d.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wvvouAkg-1681704767275)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/873d282c-c59c-4417-936a-a5c6e707c5e9.png)]

在此,为预测边界框(l)和地面真实边界框(g)之间的所有N默认边界框计算平滑损失。 。 分类损失是针对类别的 softmax 计算类别分数之上的简单交叉熵损失。 分类损失由以下公式给出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TdW5As0c-1681704767276)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/9ce711e0-639d-427e-8c9a-b7dd247ec465.png)]

在这里,第一项是边界框是否包含对象,第二项是没有对象时。 c_hat代表该类别的 softmax 得分。

到目前为止,我们已经了解了 SSD 的工作原理。 现在,让我们使用 SSD 使用 TensorFlow 来检测对象!

TensorFlow 对象检测动物园

目标检测模型很难训练。 这是由于其复杂的架构以及进行大量预测的要求。 要训练诸如 Faster RCNN,YOLO 或 SSD 的对象检测模型,需要大量的并行处理能力,这并不是每个人都可以使用的。 即使您可以进行这种计算,也要花费数小时和数小时的时间,并要进行仔细的监视以训练端到端的对象检测模型。 尽管非常准确,但这可能会限制这些模型的易于使用。

为了克服这个普遍面临的问题,研究人员提出了预训练网络的想法。 使用可在公共大型数据集(例如 COCO 数据集,PASCAL VOC 数据集,Kitti 数据集等)上获得的最新资源来训练模型。 这些数据集的链接可以在这个页面,和这个页面中找到。

然后将模型的权重和图公开。 对对象检测深度学习模型感兴趣的任何人都可以下载这些权重和图以将其实现以用于对象检测。

TensorFlow 凭借其 TensorFlow 对象检测 API 和 TensorFlow 模型库向前迈出了一步,以开源各种预先训练的模型权重和 TensorFlow 冻结图来帮助深度学习开发人员。 您可以查看以下 TensorFlow 模型动物园的链接,并比较不同对象检测模型的运行时间和平均精度均值MAP

接下来,我们将研究如何使用 TensorFlow 模型库进行对象检测。 让我们基于前面讨论的 SSD 建立模型。 我们的第一步是为我们要实现的模型下载预训练的权重。 在这里,我们将考虑模型ssd_mobilenet_v2_coco。 MobileNet 背后的想法将在下一章中进行讨论。 现在,将其视为在 COCO 数据集上训练的 SSD 对象检测网络。 您可以通过单击模型名称以压缩形式下载包含所有相关文件的目录,如以下屏幕截图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1qVGGUgI-1681704767276)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/2f5bd5e7-6903-4b80-ac6e-02d6aab1a418.png)]

下载.zip文件后,可以将其解压缩到deep_learning文件夹中。 接下来,我们将看一个脚本,该脚本将从冻结的图中加载模型和权重,并检测输入图像中的对象。 首先,导入所需的依赖项:

请记住要激活用于深度学习的 Python 库和 TensorFlow 的安装环境。 我们在“第 1 章”,“深度学习”中创建了一个名为test_env的环境。 您可以使用它! 如果您缺少任何依赖项,则可以在终端中(在激活的环境下)简单地执行conda install 命令。

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import cv2
import os
import argparse

我们将使用argparse模块传递需要执行检测的图像的路径。 使用argparse,可以提供运行脚本时要检测到的图像的存储目录的路径:

parser.add_argument('--im_path', type=str, help='path to input image')
#parser.add_argument('--save_path', type=str, help='save path to output image')
args = parser.parse_args()
IM_PATH = args.im_path

接下来,我们将使用opencv定义一个简单的函数来读取图像:

def read_image(imPath):
  img = cv2.imread(imPath)
  return img

提取的文件夹必须包含protobuf格式(带有.pb扩展名)的模型的冻结图。 我们需要读取此文件以加载冻结的图:

FROZEN_GRAPH_FILE = 'frozen_inference_graph.pb'  #path to frozen graph

我们在 Python 中使用 TensorFlow,但是 TensorFlow 的实际库是用 C++ 编写的。 TensorFlow 使用名为protobuf的模块将图转换为不同的语言。 因此,在读取由protobuf(通常带有.pb扩展名)存储的图时,我们需要首先使用tf.GraphDef定义一个序列图,然后将其放入要创建的空图内。 以下代码执行相同的操作:

# making an empty graph
graph = tf.Graph()
with graph.as_default():
  # making a serial graph
  serialGraph = tf.GraphDef() 
  # reading from saved frozen graph
  with tf.gfile.GFile(FROZEN_GRAPH_FILE, 'rb') as f:
      serialRead = f.read()
      serialGraph.ParseFromString(serialRead)
      tf.import_graph_def(serialGraph, name = '')

接下来,我们使用加载的图初始化会话:

sess = tf.Session(graph = graph)

现在,我们将读取指定目录路径中的图像。 在这里,我们仅考虑.jpeg图片,但您可以根据需要将其更改为其他格式:

for dirs in os.listdir(IM_PATH):
  if not dirs.startswith('.'):
    for im in os.listdir(os.path.join(IM_PATH, dirs)):
      if im.endswith('.jpeg'):
        image = read_image(os.path.join(IM_PATH, dirs, im))
        if image is None:
          print('image read as None')
        print('image name: ', im)

TensorFlow 图由张量变量和占位符组成,它们用于在会话期间流动和馈送数据。 为了获取输出并将输入输入模型,我们需要取出负责输入和输出的张量。 我们可以通过图中的名称来获取张量。 我们使用以下代码为图像的输出检测到的边界框,类和输入占位符获取张量:

imageTensor = graph.get_tensor_by_name('image_tensor:0')
bboxs = graph.get_tensor_by_name('detection_boxes:0')
classes = graph.get_tensor_by_name('detection_classes:0')

现在,我们准备对图像执行对象检测。 在这里,我们需要使用np.expand_dims()在图像中添加一个额外的尺寸,因为 TensorFlow 会保留批量尺寸的第一个尺寸:

(outBoxes, classes) = sess.run([bboxs, classes],feed_dict = {imageTensor:np.expand_dims(image, axis=0)})

我们可以使用简单的np.squeeze()操作将结果提取为可视的,以通过使用以下代码来消除多余的尺寸:

cnt = 0
imageHeight, imageWidth = image.shape[:2]
boxes = np.squeeze(outBoxes)
classes = np.squeeze(classes)
boxes = np.stack((boxes[:,1] * imageWidth, boxes[:,0] * imageHeight,
                boxes[:,3] * imageWidth, boxes[:,2] * imageHeight),axis=1).astype(np.int)

一旦有了预测的边界框,我们将使用opencv在其周围绘制一个矩形框。 您也可以选择打印类值。 将打印数字类值; 您可以参考 COCO 数据集并将数字标签转换为实际标签。 我们会将其留给您作为练习:

for i, bb in enumerate(boxes):
    print(classes[i])
    cv2.rectangle(image, (bb[0], bb[1]), (bb[2], bb[3]), (100,100,255), thickness = 1)

此后,我们只需要绘制最终图像以查看图像上的边界框:

plt.figure(figsize = (10, 10))
plt.imshow(image)
plt.show()

就是这样了! 如果您陷入前面的片段的缩进或流程中,则以下是完整的代码供您参考:

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import cv2
import os
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--im_path', type=str, help='path to input image')
args = parser.parse_args()
IM_PATH = args.im_path
def read_image(imPath):
  img = cv2.imread(imPath)
  return img
FROZEN_GRAPH_FILE = 'frozen_inference_graph.pb' #path to frozen graph
# making an empty graph
graph = tf.Graph()
with graph.as_default():
  serialGraph = tf.GraphDef()
  with tf.gfile.GFile(FROZEN_GRAPH_FILE, 'rb') as f:
    serialRead = f.read()
    serialGraph.ParseFromString(serialRead)
    tf.import_graph_def(serialGraph, name = '')
sess = tf.Session(graph = graph)
for dirs in os.listdir(IM_PATH):
  if not dirs.startswith('.'):
    for im in os.listdir(os.path.join(IM_PATH, dirs)):
      if im.endswith('.jpeg'):
        image = read_image(os.path.join(IM_PATH, dirs, im))
        if image is None:
          print('image read as None')
        print('image name: ', im)
        # here we will bring in the tensors from the frozen graph we loaded,
        # which will take the input through feed_dict and output the bounding boxes
        imageTensor = graph.get_tensor_by_name('image_tensor:0')
        bboxs = graph.get_tensor_by_name('detection_boxes:0')
        classes = graph.get_tensor_by_name('detection_classes:0')
        (outBoxes, classes) = sess.run([bboxs, classes],feed_dict={imageTensor: np.expand_dims(image, axis=0)})
        # visualize
        cnt = 0
        imageHeight, imageWidth = image.shape[:2]
        boxes = np.squeeze(outBoxes)
        classes = np.squeeze(classes)
        boxes = np.stack((boxes[:,1] * imageWidth, boxes[:,0] * imageHeight,
                boxes[:,3] * imageWidth, boxes[:,2] * imageHeight),axis=1).astype(np.int)
        for i, bb in enumerate(boxes):
          print(classes[i])
          cv2.rectangle(image, (bb[0], bb[1]), (bb[2], bb[3]), (255,255,0), thickness = 1)
        plt.figure(figsize = (10, 10))
        plt.imshow(image)
        plt.show()

让我们拿起一张图片向您展示它的外观。 我们拍摄了以下两个人类站立的图像(图像来自 Wikipedia):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-98mPLSq4-1681704767276)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/3c18033c-5bf7-43b6-9d01-44969dedba53.png)]

这是检测到的结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LOJMxMDi-1681704767276)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/6633cb52-a38d-4d5d-9919-5ee13e9ec169.png)]

总结

在本章中,我们首先讨论了深度前馈网络的缺点以及 CNN 如何克服这些缺点而发展。 接下来,我们深入研究了 CNN 的架构,了解了 CNN 的不同层-输入层,卷积层,最大池化层和全连接层。 我们研究了一些著名的图像分类 CNN 的架构,然后在 CIFAR-10 数据集上构建了我们的第一个 CNN 图像分类器。 然后,我们继续使用 CNN 进行对象检测。 我们讨论了各种对象检测器,例如 RCNN,Faster-RCNN,YOLO 和 SSD。 最后,我们使用 TensorFlow 检测模型 Zoo 通过 SSD 实现了我们的第一个对象检测器。

在下一章中,我们将介绍 CNN 架构,这些架构需要较少的计算能力,并且轻便,可以在移动设备上运行。 它们被称为 MobileNets

五、移动神经网络和 CNN

深度学习网络所需的计算成本一直是扩展的关注点。 进行推理需要数百万个乘法运算。 这限制了已开发的卷积神经网络CNN)模型的实际使用。 移动神经网络为这一问题提供了突破。 它们是超小型且计算量较小的深度学习网络,并具有与原始同类产品相当的表现。 移动神经网络只是经过修改以具有更少参数的 CNN,这意味着它们消耗的内存更少。 这样,它们就可以在内存和处理能力有限的移动设备上工作。 因此,移动神经网络在使 CNN 用于实时应用中起着至关重要的作用。 在本章中,我们将介绍 Google 引入的两种基准移动 CNN 架构-MobileNet 和 MobileNetV2。 完成本章后,您将了解以下主题:

  • MobileNet 如何发展
  • MobileNet 的架构
  • 用 Keras 实现 MobileNet
  • MobileNetV2
  • MobileNetV2 的动机
  • MobileNetV2 的架构
  • 比较两个 MobileNet
  • SSD

MobileNets 的演进之路

CNN 为计算机视觉带来了光明的未来。 CNN 凭借连续几年在 ILSVRC 竞赛中的出色表现,为复杂的计算机视觉任务(例如检测和识别)奠定了基准。 但是这些 CNN 模型所需的计算能力一直很高。 这可能导致 CNN 的商业使用受到重大挫折。 现实世界中几乎所有与对象检测有关的任务都是通过便携式设备执行的,例如移动电话,监控摄像头或任何其他嵌入式设备。 这些设备的计算能力和内存有限。 为了使任何深度学习网络都在便携式设备上运行,网络权重和网络中发生的计算数量(即网络中的参数数量)必须非常小。 CNN 具有数百万个参数和权重,因此似乎不可能在任何移动设备上打包和运行 CNN!

2017 年初,Google 的一组研究人员取得了突破,并推出了一种称为 MobileNets 的新型 CNN,用于移动和嵌入式视觉。 MobileNet 具有深度可分离卷积的概念,在保持相同模型深度的同时,显着减少了卷积网络的参数数量。 MobileNets 取得了巨大的成功! 许多公司开始使用 MobileNets 在移动设备上进行实时检测。

Google 在 2018 年推出了第二版 MobileNets,称为 MobileNetV2。 较新的 MobileNetV2 具有反向残差块。 各种成功的对象检测架构(例如 SSD)也与 MobileNetV2 结合在一起,以创建用于对象检测的高效移动架构。 因此,让我们在下一部分中了解 MobileNets 的架构。

MobileNets 的架构

MobileNets 架构的核心是深度可分离卷积的概念。 CNN 的标准卷积操作由深度卷积和点卷积代替。 因此,我们首先来看下一部分中的深度可分离卷积。

深度可分离卷积

顾名思义,深度可分离卷积必须与特征映射的深度有关,而不是其宽度和高度。 请记住,当我们在 CNN 中的输入图像上使用过滤器时,该过滤器覆盖了图像的所有通道(例如彩色图像的三个 RGB 通道)。 无论输入中存在多少个通道,卷积核总是覆盖所有通道并在单个通道特征映射中生成输出。

在任何层中,如果我们想要n个数量的特征映射,则在上一层上运行n个数量的核,因为每个核输出一个通道。 下图显示了标准卷积的输出响应:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GMrZJr5A-1681704767276)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/26400924-f3b0-4f17-9cb1-4e868d209c2d.png)]

但是与标准卷积不同,深度卷积并未将输入中的所有通道都考虑在内以输出单个通道。 而是,它分别在每个通道上执行卷积。 因此,对n通道图像执行深度卷积将在输出中产生n通道。 深度方向可分卷积有两个部分-深度方向卷积(我们刚刚讨论过)和点方向卷积。 深度卷积之后是点状卷积,这只是一个具有1 x 1核的常规卷积运算。 需要逐点卷积来组合深度卷积的结果。

下图显示了深度可分离卷积的输出响应:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n4nJQW4D-1681704767277)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/b7027c07-c5d8-47e5-8f0f-1eff9e9720ee.png)]

那么,使用n卷积过滤器生成深度n的特征映射有什么问题? 为什么我们需要用深度方向可分离卷积代替标准卷积? 在下一节中,我们将探讨原因。

深度可分离卷积的需求

在 MobileNets 中引入深度可分离卷积的目的是减少所需的计算费用。 因此,让我们将深度卷积可分离卷积与标准卷积的计算成本进行比较。 由于乘法运算而导致主要的计算成本(诸如加法的运算在计算上很简单)。 乘法次数越多,所需的计算就越高。 让我们考虑一个M x M x N图像的情况。 这是 RGB 图像的典型尺寸。 假设我们正在使用K x K x N大小的标准卷积核,并且我们希望尺寸特征映射为G x G x D。 为此,我们必须使用D个过滤器:

  • 因此,在一个位置上一个卷积过滤器所需的乘法运算次数为K . K . N = K^2 N
  • 该过滤器会滑动G x G次,以生成一个过滤器的完整输出。 这使得乘法的次数G^2 K^2 N
  • 我们有D个这样的核。 因此,这使得我们需要卷积G^2 K^2 ND的总成本。

现在,让我们计算使用深度方向可分离卷积生成相同结果所需的乘法运算次数。 我们知道深度方向可分卷积有两个部分-深度方向卷积和点方向卷积。 标准卷积和深度卷积之间的主要区别在于,深度卷积在卷积核中使用 1 的深度。 让我们考虑一下前面提到的相同场景。 我们有一个MxMxN图片:

  • 此处的核大小为K x K x 1。我们需要N个核,以适应完整的图像,这将为我们提供一个G x G x N的输出尺寸。 因此,此处所需的乘法数为G^2 K^2 N
  • 现在,该进行逐点卷积了。 这涉及组合深度方向卷积的输出。 点式卷积的核是1 x 1 x N。 如果该核在深度卷积的整个输出中滑动,则一个核所需的乘法运算次数将为G^2 N
  • 如果我们要在最终输出特征映射中使用深度D,则使用D个点状核的最终输出为GxGxD。 因此,乘法数变为G^2 ND
  • 深度方向可分离卷积所需的乘法总数是深度方向卷积和点方向卷积所需的乘法总和,如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZnfnqVOq-1681704767277)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/4686e008-9c3f-4021-bd27-85cd318f15d0.png)]

我们可以通过以下方式比较标准卷积和深度可分离卷积所需的乘法次数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sdN2haad-1681704767277)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/9b23f1db-5fa5-43a3-9c3b-d96b685696c0.png)]

通常,如果我们将D = 256K = 3,则比率为 0.115。 这意味着深度方向可分离卷积的参数是标准卷积的九倍。

希望您现在对 MobileNet 如何通过深度方向可分离卷积减少参数数量有所了解。 现在,让我们在下一个小节中查看 MobileNet 的结构。

MobileNet 的结构

MobileNet 的结构由 30 层组成。 它以3 x 3的标准卷积作为第一层开始。 此后,继续进行深度卷积和点卷积。 深度可拆分卷积块是深度可拆分卷积和点式卷积的连续组合,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BHlsQ7GV-1681704767277)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/9ca9c54e-df3e-4dbf-9f52-4f2d7a72786c.png)]

图片来自《MobileNets:用于移动视觉应用的高效卷积神经网络》。 BN 代表批量归一化。

在该结构中,前面的块重复 13 次。 为了减少特征映射的宽度和高度,MobileNet 在深度卷积中使用了第二步。 是的,它不使用 maxpooling! 为了增加特征映射的深度,逐点卷积将通道数量加倍。 通道的加倍发生在相应的逐点层中,其中在深度卷积中使用跨步 2。

可以在这个页面上找到 MobileNets 研究论文的链接。

MobileNet 经过 ImageNet 数据的训练,图像的输入尺寸为224 x 224 x 3。根据 ImageNet 图像的输入尺寸,从卷积层出来的最终输出尺寸为7 x 7 x 1,024。 卷积结束后,将应用全局平均池GAP)层,以使尺寸为1 x 1 x 1,024。 假设尺寸为H x W x D的特征映射,GAP 层会计算HW值的平均值,并使用单个平均值替换H x W值,因此输出尺寸始终为1 x 1 x D

由于 MobileNet 主要用于分类,因此结束层是全连接层。 MobileNets 中使用的激活函数是 ReLU6。 我们已经了解了 ReLU,但是 ReLU6 是什么? ReLU6 与 ReLU 函数相同,但上限限制为六个。 作者声称 ReLU6 可帮助模型更早地学习稀疏特征。 以下等式定义了 ReLU6 激活函数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4Pq7Nc5S-1681704767277)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/9b45c6c3-fae7-4c2a-9d9c-b395303ff1e3.png)]

让我们在下表中查看 MobileNet 的完整架构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QsHETPns-1681704767278)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/6a59c5a9-4edb-4e15-a71f-6b2ef601ea7e.png)]

图片来自《MobileNets:针对移动视觉应用的高效卷积神经网络》

既然我们已经了解了 MobileNet 的架构以及如何通过深度方向上可分离的卷积减少参数的数量,那么让我们看一下 MobileNet 的实现。

MobileNet 与 Keras

MobileNet 经过 ImageNet 数据训练。 通过使用 Keras 应用类,我们可以使用模型的预训练权重来实现 MobileNet。 在 Keras 应用中,您可以找到许多预先训练的模型供使用。 您可以通过这里浏览 Keras 应用的文档。

所以,让我们开始吧! 首先,显然,我们将导入所需的依赖项:

import keras
from keras.preprocessing import image
from keras.applications import imagenet_utils
from keras.models import Model
from keras.applications.mobilenet import preprocess_input
import numpy as np
import argparse
import matplotlib.pyplot as plt

Keras preprocessing提供了一个类,例如ImageDataGenerator类,该类有助于从数据集中绘制成批图像。 我们的下一个工作是获取模型权重和图。 在我们的脚本中添加以下步骤后,下载将仅在您的系统上进行一次:

model = keras.applications.mobilenet.MobileNet(weights = 'imagenet')

下载可能需要一些时间,具体取决于您的互联网连接。 Keras 将继续更新状态,完成后将如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LRjlTg6h-1681704767278)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/aae353e4-6a4d-4882-8ad1-dc65e337676a.png)]

我们将使用argparse模块来帮助将图像路径传递到我们希望 MobileNet 分类的图像的脚本:

parser = argparse.ArgumentParser()
parser.add_argument('--im_path', type = str, help = 'path to the image')
args = parser.parse_args()
# adding the path to image
IM_PATH = args.im_path

我们将使用 Keras 提供的load_img函数来加载此图像,并使用img_to_array将其转换为数组:

img = image.load_img(IM_PATH, target_size = (224, 224))
img = image.img_to_array(img)

ImageNet 中的图像的宽度和高度为224。 因此,默认情况下将目标大小设置为(224, 224)。 正如我们前面所看到的,第一维始终保持批量大小。 我们将扩展图像的尺寸,以将批量大小作为第一个尺寸(因为我们使用的是单个图像,因此可以假定其为批量大小 1):

img = np.expand_dims(img, axis = 0)

最后,我们将通过mobilenetpreprocess_input()函数传递img,该函数执行基本的预处理操作,例如重新塑形和标准化图像的像素值:

img = preprocess_input(img)

现在,是时候让 MobileNet 对我们提供的图像做出预测了:

prediction = model.predict(img)

当模型根据 ImageNet 数据集预测类别时,我们将使用decode_predictions函数以人类可读的形式带回前五项预测:

output = imagenet_utils.decode_predictions(prediction)
print(output)

让我们使用以下鹈鹕鸟的图像并检查 MobileNet 是否能够对其进行分类:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EmEwqaoU-1681704767278)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/d2a57e22-fd53-4083-9327-c861985f5444.png)]

图片来自维基百科

您需要按照以下方式从正在运行的环境下的终端运行脚本以及图像的路径:

$python mobilenet_keras.py --im_path=pelican.jpg

以下是我们脚本的输出。 您可以看到 MobileNet 将该图像预测为鹈鹕,概率为 0.99! 在预测的前五类中,还有其他一些看起来像鹈鹕的鸟,但是由于 softmax 激活,它们的概率被抑制了:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4UUSYMwv-1681704767278)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/941bcc42-9998-4ab2-835a-7fef5862d7e3.png)]

您可以使用与 ImageNet 数据类有关的不同图像来探索 MobileNet 的表现。 在 MobileNet 成功之后,Google 研究团队于 2018 年 4 月发布了 MobileNet 的更新版本。我们将在下一部分中了解 MobileNet 的第二版本。

MobileNetV2

第二个版本的 MobileNet,称为 MobileNetV2,甚至比 MobileNet 还要快。 第二个版本也具有较少的参数。 自发布以来,MobileNetV2 已广泛用于最新的对象检测和分段架构中,以使资源有限的设备上的对象检测或分段成为可能。 让我们看看创建 MobileNetV2 的动机。

MobileNetV2 的动机

Google 的研究人员希望 MobileNet 更轻巧。 如何使 MobileNet 具有更少的参数? 所有基于 CNN 的模型都增加了特征映射(深度通道)的数量,同时减小了宽度和高度。 减小网络大小的一种简单方法是减小特征映射的深度。 通道数越少,参数越少。 但这会削弱 CNN! 卷积过滤器将无法从浅层特征映射中提取特征。 那我们现在怎么办?

Google 研究人员找到了解决该问题的方法,并介绍了现有 MobileNet 架构的两个主要变化:扩展线性瓶颈层倒置残差块。我们将在下一部分中介绍 MobileNetV2 的详细结构。

MobileNetV2 的结构

MobileNetV2 的核心架构仍然依赖于深度方向上可分离的卷积层。 还记得 MobileNet 的基石吗? 它具有3 x 3的深度卷积层,然后是3 x 3的逐点卷积和批量归一化,中间是 ReLU6。 MobileNetV2 遵循相同的块,不同之处在于顶部有一个额外的扩展层和一个线性瓶颈层来代替3 x 3点向卷积。 首先让我们看一下线性瓶颈层的作用。

线性瓶颈层

在 MobileNet 中,1 x 1点向卷积负责增加通过网络的特征映射的深度。 MobileNetV2 中的线性瓶颈层执行相反的工作。 它实际上减少了特征映射的深度。 为了保留层中的非线性,ReLU 激活函数会降低负值。 这导致信道中的信息丢失。 为了解决这个问题,特征映射中使用了许多通道,因此很有可能一个通道中的信息丢失会保留在任何其他通道中。

但是,MobileNetV2 的作者证明,如果将输入通道投影到低维空间而不是高维空间,则 ReLU 激活能够保留来自输入通道的所有信息。 这是一个重大突破! 作者还提供原始文件中的补充材料来证明这一点。

可以在这个页面上找到 MobileNetV2 研究论文的链接。

因此,作者在卷积之后引入了所谓的线性瓶颈层,这降低了尺寸。 例如,如果中间层具有 384 个通道,则它将减少为 128 个通道。 但是减小尺寸并不是我们所需要的! 为了执行深度卷积,我们需要更高的维数。 因此,在深度卷积之前使用扩展层以增加通道的数量。 让我们看一下扩展层的功能。

扩展层

扩展层1 x 1卷积层,始终具有比输入维更大的输出通道维。 扩展量由称为扩展因子的超参数控制。 整个 MobileNetV2 的扩展因子都设置为 6。例如,如果输入具有 64 个通道,它将被扩展为64 * 6 = 384个通道。 在其上进行深度卷积,然后瓶颈层将其带回到 128 个通道。

MobileNetV2 的架构首先扩展通道,执行卷积,然后减小它。 这使架构的端到端维数较低,从而减少了所需参数的数量。

下图显示了 MobileNetV2 的总体构建块:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cJc9AlIo-1681704767279)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/27394f16-3c33-45fe-9c6f-14d757432e20.png)]

现在,架构还剩下一件事:剩余层。 尽管它类似于 ResNet 模型中的跳过连接,但我们将查看有关它的一些详细信息。

反向残差块

由于通过将许多层堆叠在一起,深度学习模型变得太深,训练网络变得非常困难。 这不仅是由于需要巨大的计算能力,而且还因为梯度逐渐消失在层中。 我们知道深度学习模型中的所有学习都取决于通过反向传播的梯度流。 在大型网络中,梯度随着每一步而变小,并在穿过所有层之前消失。 这种现象限制了我们使网络变得太深。 ResNet 架构中引入的剩余连接或跳过连接可帮助我们克服此问题。 来自上一层的连接将添加到一层,以便梯度获得易于流动的路径。 这些跳过连接使 ResNet 模型比通常的更深。

受跳过连接的启发,MobileNetV2 的作者声称有用的信息仅位于瓶颈层,因此,为了使梯度易于通过多个瓶颈块流动,他们增加了从一个瓶颈层到另一个瓶颈层的剩余连接。 由于 ResNet 中原始残差连接和 MobileNetV2 中残差连接之间的设计差异,作者选择将此称为反向残差。

下图可以看出差异:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QQwLMwYa-1681704767279)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/2778f97c-d627-4359-9458-b39cfdeaf5cc.png)]

图片来自研究论文《MobileNetV2:残差和线性瓶颈》

既然我们已经涵盖了 MobileNetV2 的所有元素,我们将研究 MobileNetV2 的整体结构。

整体架构

MobileNetV2 首先对图像执行标准卷积,以创建 32 个过滤器特征映射。 此后,有 19 个残存的瓶颈层块(我们在扩展层子部分中看到的块)。 所有卷积核的大小均为3 x 3。整个网络中一直使用 6 的恒定扩展因子。 下表列出了 MobileNetV2 的总体架构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Or2aQTsK-1681704767279)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/d335fb34-5b5e-43ba-8a2e-e83db7eb88a9.png)]

图片来自研究论文《MobileNetV2:残差和线性瓶颈》

在上表中,n列表示重复特定层的次数。 s列代表用于该层的跨步。 列ct分别表示层中使用的通道数和扩展因子。

与 MobileNet 相似,我们也可以使用 Keras 来实现 MobileNetV2。

实现 MobileNetV2

我们将遵循与 MobileNet 相似的过程。 您可以在 Keras 应用中找到 MobileNetV2。 我们将使用与 MobileNet 相同的代码,除了这次将使用 MobileNetV2。 供参考,代码如下:

import keras
from keras.preprocessing import image
from keras.applications import imagenet_utils
from keras.applications.mobilenet import preprocess_input
from keras.models import Model
import numpy as np
import argparse
import matplotlib.pyplot as plt
model = keras.applications.mobilenet_v2.MobileNetV2(weights = 'imagenet')
parser = argparse.ArgumentParser()
parser.add_argument('--im_path', type = str, help = 'path to the image')
args = parser.parse_args()
# adding the path to image
IM_PATH = args.im_path
img = image.load_img(IM_PATH, target_size = (224, 224))
img = image.img_to_array(img)
img = np.expand_dims(img, axis = 0)
img = preprocess_input(img)
prediction = model.predict(img)
output = imagenet_utils.decode_predictions(prediction)
print(output)

该脚本将首先下载 MobileNetV2 的权重,这可能需要一些时间,具体取决于您的互联网连接。 它看起来像这样:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rTiSCIbR-1681704767279)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/c0f39c88-43b3-45ed-9438-fce2223cf5ee.png)]

让我们使用以下火烈鸟图像来检查输出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dWLxYVuZ-1681704767279)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/03938748-1aca-4107-a9a3-bab2a30a48bd.png)]

这是输出的样子。 我们可以看到该网络大约有 86% 的人确定该图像是火烈鸟。 您可以观察到其他类别的概率由于 softmax 而受到抑制:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8iO8nJYw-1681704767280)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/17770212-865d-48cc-adbc-4a34a751818a.png)]

一年内推出了两个版本的 MobileNet。 第二个版本包含重大更改,我们已经讨论过。 现在,让我们比较两个网络的一些标准参数。

比较两个 MobileNet

MobileNetV2 对 MobileNet 的架构进行了重大更改。 这些更改值得吗? 在表现方面,MobileNetV2 比 MobileNet 好多少? 我们可以根据一次推理所需的乘法运算数量来比较模型,这通常称为 MAC(乘法累加数)。 MAC 值越高,网络越重。 我们还可以根据模型中的参数数量来比较模型。 下表显示了 MobileNet 和 MobileNetV2 的 MAC 和参数数:

网络 参数数 MAC
MobileNet V1 420 万 575M
MobileNet V2 340 万 300M

我们还可以根据不同通道数和分辨率所需的内存来比较模型。 下表提供了比较数据。 测量的内存为千字节Kb):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tmnnl2oH-1681704767280)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/5042d3d3-2d3b-478d-8952-ec150a8b3c2a.png)]

TensorFlow 还提供了在像素 1 移动电话上运行的两个 MobileNet 之间的准确率与延迟比较。 延迟基本上表示运行模型需要多少时间。 下图显示了比较:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WR6tNauZ-1681704767280)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/e78595f8-4f7c-4cd9-b4a4-49d9aeced026.png)]

您可以在这个页面上找到有关比较的更多详细信息。

MobileNetV2 不仅仅是分类。 该架构的作者提出了将对象检测和分段架构相结合的想法。 在下一部分中,我们将介绍用于对象检测的 MobileNetV2 和 SSD 的非常成功的组合。

SSD MobileNetV2

MobileNetV2 的制造商还使移动设备的实时对象检测成为可能。 他们介绍了 SSD 对象检测器和 MobileNetV2(称为 SSDLite)的组合。 请记住,在“第 4 章”,“CNN 架构”中,我们将ssd_mobilenetv2用于对象检测。 与 SSDLite 相同。 选择 SSD 的原因很简单。 SSD 的构建独立于基础网络,因此卷积被深度可分离卷积替代。 SSDLite 的第一层连接到 MobileNetV2 的第 15 层的扩展。 用深度可分离卷积替换标准卷积可以显着减少网络进行对象检测所需的参数数量。

下表显示了原始 SSD 网络和 SSDLite 所需的参数数量和乘法运算的比较:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CV0l7eqM-1681704767280)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/8ee9bbcf-e883-4886-ad12-da9bea86256f.png)]

图片来自研究论文《MobileNetV2:残差和线性瓶颈》

总结

在本章的开头,我们讨论了使神经网络在实时应用中运行所需的移动神经网络。 我们讨论了 Google 推出的两种基准 MobileNet 架构-MobileNet 和 MobileNetV2。 我们研究了深度可分离卷积之类的修改如何工作,并取代了标准卷积,从而使网络能够以更少的参数获得相同的结果。 通过 MobileNetV2,我们研究了通过扩展层和瓶颈层进一步缩小网络的可能性。 我们还研究了 Keras 中这两个网络的实现,并根据参数数量,MAC 和所需的内存比较了这两个网络。 最后,我们讨论了 MobileNets 与对象检测网络(例如 SSD)的成功组合,以在移动设备上实现对象检测。

在下一章中,我们将介绍另一种成功的深度学习架构,称为循环神经网络RNN)。 这些网络旨在捕获序列中的时间信息,例如句子或任何其他文本。

相关文章
|
5月前
|
机器学习/深度学习 人工智能 算法
猫狗宠物识别系统Python+TensorFlow+人工智能+深度学习+卷积网络算法
宠物识别系统使用Python和TensorFlow搭建卷积神经网络,基于37种常见猫狗数据集训练高精度模型,并保存为h5格式。通过Django框架搭建Web平台,用户上传宠物图片即可识别其名称,提供便捷的宠物识别服务。
595 55
|
4月前
|
机器学习/深度学习 人工智能 算法
基于Python深度学习的眼疾识别系统实现~人工智能+卷积网络算法
眼疾识别系统,本系统使用Python作为主要开发语言,基于TensorFlow搭建卷积神经网络算法,并收集了4种常见的眼疾图像数据集(白内障、糖尿病性视网膜病变、青光眼和正常眼睛) 再使用通过搭建的算法模型对数据集进行训练得到一个识别精度较高的模型,然后保存为为本地h5格式文件。最后使用Django框架搭建了一个Web网页平台可视化操作界面,实现用户上传一张眼疾图片识别其名称。
341 5
基于Python深度学习的眼疾识别系统实现~人工智能+卷积网络算法
|
2月前
|
机器学习/深度学习 人工智能 算法
基于Python深度学习的【害虫识别】系统~卷积神经网络+TensorFlow+图像识别+人工智能
害虫识别系统,本系统使用Python作为主要开发语言,基于TensorFlow搭建卷积神经网络算法,并收集了12种常见的害虫种类数据集【"蚂蚁(ants)", "蜜蜂(bees)", "甲虫(beetle)", "毛虫(catterpillar)", "蚯蚓(earthworms)", "蜚蠊(earwig)", "蚱蜢(grasshopper)", "飞蛾(moth)", "鼻涕虫(slug)", "蜗牛(snail)", "黄蜂(wasp)", "象鼻虫(weevil)"】 再使用通过搭建的算法模型对数据集进行训练得到一个识别精度较高的模型,然后保存为为本地h5格式文件。最后使用Djan
179 1
基于Python深度学习的【害虫识别】系统~卷积神经网络+TensorFlow+图像识别+人工智能
|
2月前
|
设计模式 机器学习/深度学习 前端开发
Python 高级编程与实战:深入理解设计模式与软件架构
本文深入探讨了Python中的设计模式与软件架构,涵盖单例、工厂、观察者模式及MVC、微服务架构,并通过实战项目如插件系统和Web应用帮助读者掌握这些技术。文章提供了代码示例,便于理解和实践。最后推荐了进一步学习的资源,助力提升Python编程技能。
|
3月前
|
机器学习/深度学习 人工智能 算法
基于Python深度学习的【蘑菇识别】系统~卷积神经网络+TensorFlow+图像识别+人工智能
蘑菇识别系统,本系统使用Python作为主要开发语言,基于TensorFlow搭建卷积神经网络算法,并收集了9种常见的蘑菇种类数据集【"香菇(Agaricus)", "毒鹅膏菌(Amanita)", "牛肝菌(Boletus)", "网状菌(Cortinarius)", "毒镰孢(Entoloma)", "湿孢菌(Hygrocybe)", "乳菇(Lactarius)", "红菇(Russula)", "松茸(Suillus)"】 再使用通过搭建的算法模型对数据集进行训练得到一个识别精度较高的模型,然后保存为为本地h5格式文件。最后使用Django框架搭建了一个Web网页平台可视化操作界面,
217 11
基于Python深度学习的【蘑菇识别】系统~卷积神经网络+TensorFlow+图像识别+人工智能
|
2月前
|
机器学习/深度学习 设计模式 API
Python 高级编程与实战:构建微服务架构
本文深入探讨了 Python 中的微服务架构,介绍了 Flask、FastAPI 和 Nameko 三个常用框架,并通过实战项目帮助读者掌握这些技术。每个框架都提供了构建微服务的示例代码,包括简单的 API 接口实现。通过学习本文,读者将能够使用 Python 构建高效、独立的微服务。
|
5月前
|
机器学习/深度学习 数据可视化 TensorFlow
使用Python实现深度学习模型的分布式训练
使用Python实现深度学习模型的分布式训练
271 73
|
4月前
|
机器学习/深度学习 算法 前端开发
基于Python深度学习果蔬识别系统实现
本项目基于Python和TensorFlow,使用ResNet卷积神经网络模型,对12种常见果蔬(如土豆、苹果等)的图像数据集进行训练,构建了一个高精度的果蔬识别系统。系统通过Django框架搭建Web端可视化界面,用户可上传图片并自动识别果蔬种类。该项目旨在提高农业生产效率,广泛应用于食品安全、智能农业等领域。CNN凭借其强大的特征提取能力,在图像分类任务中表现出色,为实现高效的自动化果蔬识别提供了技术支持。
177 0
基于Python深度学习果蔬识别系统实现
|
5月前
|
机器学习/深度学习 数据采集 供应链
使用Python实现智能食品消费需求分析的深度学习模型
使用Python实现智能食品消费需求分析的深度学习模型
157 21
|
5月前
|
机器学习/深度学习 数据采集 数据挖掘
使用Python实现智能食品消费模式预测的深度学习模型
使用Python实现智能食品消费模式预测的深度学习模型
137 2