深度学习与计算机视觉教程(二)

随笔4个月前发布 药都小哥
60 0 0

原文:Computer Vision Using Deep Learning

协议:CC BY-NC-SA 4.0

四、VGGNet 和 AlexNet 网络

一旦我们接受了自己的极限,我们就会超越它们。—阿尔伯特·爱因斯坦

在某一点之后,即使是极其复杂的解决方案也不再改进。然后我们应该改进解决方案的设计。我们回到绘图板,集思广益,提高能力。有了更多可用的选项,我们可以迭代和测试多个解决方案。然后根据手头的业务问题,选择并实施最佳解决方案。我们在深度学习架构中遵循同样的原则。我们致力于网络架构,并对其进行改进,使其更加健壮、准确和高效。神经网络体系结构的选择是基于对各种体系结构进行的测试。

在上一章中,我们从 LeNet 深度学习架构开始。我们浏览了网络架构,并使用它开发了用例。在本章中,我们将讨论 VGG 和 AlexNet 神经网络架构,并开发一个复杂的多类分类功能。我们还将比较这两种架构的性能。与此同时,我们将讨论如何在训练深度学习模型时使用检查点。在生成混淆矩阵时,我们面临一个常见的错误;我们会明白错误的原因以及如何纠正它。

我们将在本章中讨论以下主题:

  1. AlexNet 架构

  2. VGG16 体系结构

  3. VGG16 和 VGG19 的区别

  4. 使用 AlexNet 的 CIFAR-10 案例研究

  5. 使用 VGG16 的 CIFAR-10 案例研究

欢迎来到第四章,祝一切顺利!

4.1 技术要求

本章的代码和数据集上传到本书的 GitHub 链接 https://github.com/Apress/computer-vision-using-deep-learning/tree/main/Chapter4 。我们将使用朱庇特笔记本。对于这一章,CPU 足以执行代码,但如果需要,您可以使用谷歌合作实验室。如果你不能自己设置 Google Colab,你可以参考这本书的参考资料。

让我们在下一部分继续深入学习架构。

4.2 AlexNet 和 VGG 神经网络

AlexNet 于 2012 年推出,立即成为每个人的最爱,用于图像和对象分类。它还赢得了 ImageNet 大规模视觉识别挑战赛(ILSVRC)。随后,VGG 在 2014 年应运而生,其准确性被证明优于 AlexNet。这并不意味着 AlexNet 不是一个有效的网络,它只是意味着 VGG 有更好的准确性。

现在让我们详细了解这两种架构。我们从 AlexNet 作为第一个架构开始。

4.3 什么是 AlexNet 神经网络?

AlexNet 是由 Alex Krizhevsky、Ilya Sutskever 和 Geoffrey E. Hinton 提出的。论文原文可在 https://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf 访问。

AlexNet 架构看起来如图 4-1 所示。这是来自上述论文的原始图像。

深度学习与计算机视觉教程(二)

图 4-1

完整的 AlexNet 架构(图片取自 https://papers.nips.cc/paper/4824-imagenet- classification-with-deep-convolutional-neural-networks.pdf )

我们现在可以更详细地探索各个层。表 4-1 给出了网络所有层的描述。

表 4-1

网络的每一层和相应的输入参数、信道大小、步幅和激活函数

| – ![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fgitee.com%2FOpenDocCN%2Fvkdoc-cv-zh%2Fraw%2Fmaster%2Fdocs%2Fcv-dl%2Fimg%2F496201_1_En_4_Figa_HTML.jpg&pos_id=img-mUDcTVUV-1724460761343) |

如我们所见,第一层是大小为 227x227x3 的输入图像。它通过 96 个特征映射的第一卷积层,步幅为 4,滤波器大小为 11×11,激活函数为 ReLU。输出图像的尺寸为 55x55x96。

接下来是最大池层,过滤器大小为 3×3,跨距为 2,输出图像尺寸为 27×27×96。您可以以这种方式继续分析每一层。必须完全理解每一层及其各自的功能。

AlexNet 拥有 6000 万个参数和 65 万个神经元。我们可以观察到,总共有八层。前五层用于执行卷积运算。最后三层是完全连接的一层。在卷积层中,少数具有以下层作为最大池层。在 AlexNet 中使用 ReLU 非线性,与 tanh 和 sigmoid 激活相比,它显示了改进的训练性能和更快的网络训练。发明人使用数据增加和丢弃层来对抗网络中的过拟合。

现在让我们理解 VGG 架构,然后我们将使用 AlexNet 和 VGG 开发用例。

4.4 什么是 VGG 神经网络?

VGGNet 是由牛津大学的卡伦·西蒙扬和安德鲁·齐泽曼提出的 CNN 架构。VGG 是视觉几何组。您可以在 https://arxiv.org/pdf/1409.1556.pdf 访问原文。

它于 2014 年推出,在 ImageNet 大规模视觉识别挑战赛(ILSVRC)中表现出色。它是最受欢迎的深度学习架构之一,因为它简单(我们将在下面的章节中研究)。通常,人们批评网络的规模,因为它需要更多的计算能力和更多的时间。但是该网络是一个非常健壮的解决方案,并且被称为计算机视觉解决方案的标准解决方案之一。

VGG 神经网络模型有两种形式: VGG16VGG19 。让我们详细研究一下 VGG16,然后我们将考察 VGG19 与 VGG16 有何不同。

4.5 VGG16 体系结构

VGG 是一个容易理解的网络,原因如下:

  1. 它在整个网络中仅使用 3×3 卷积和 2×2 池。

  2. 卷积层使用非常小的内核大小(3×3)。

  3. 有 1×1 卷积来线性变换输入。

  4. 跨距为 1 个像素,有助于保持空间分辨率。

  5. ReLU 用于所有隐藏层。

  6. 有三个完全连接的层,前两层有 4096 个通道,最后一层有 1000 个通道。最后,我们有一个 softmax 层。

在图 4-2 中,我们可以看到各自的配置以及每一层的描述。

深度学习与计算机视觉教程(二) 深度学习与计算机视觉教程(二)

图 4-2

VGG 简单网络

请注意,它在整个网络中仅使用 3×3 conv 层和 2×2 池层。左边的图取自前面引用的原始论文。随着层的增加,配置的深度从左(A)到右(E)增加。conv 层参数表示为“conv(感受野大小)–(通道数量)”为了简洁起见,没有显示 ReLU 激活功能。

Note

不使用 LRN(局部反应标准化),因为尽管训练时间增加,但准确性没有明显提高。

VGG16 是一个相当受欢迎的网络。由于其简单性,它可以作为衡量许多复杂图像分类问题的性能的基准。VGG19 比 VGG16 稍微复杂一些。接下来将研究两者之间的差异。

4.6 vgg 16 和 VGG19 的区别

表 4-2 列出了 VGG16 和 VGG19 之间的主要区别。一般 VGG16 用的最多,也比较普及。这是因为在一般的商业世界中,我们不会对一个问题进行八到十类以上的分类。此外,没有获得真正有代表性和平衡的数据。因此,在实践中,VGG16 是最常用的。

表 4-2

VGG16 和 VGG19 的主要区别

| – ![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fgitee.com%2FOpenDocCN%2Fvkdoc-cv-zh%2Fraw%2Fmaster%2Fdocs%2Fcv-dl%2Fimg%2F496201_1_En_4_Figb_HTML.jpg&pos_id=img-8KETWdGs-1724460761344) |

我们现在将使用 AlexNet 和 VGG16 在 CIFAR-10 数据集上开发用例。CIFAR-10 是一个开源数据集。

4.7 使用 AlexNet 和 VGG 开发解决方案

我们将使用 CIFAR 数据集通过 AlexNet 和 VGGNet 创建解决方案。可以在 www.cs.toronto.edu/~kriz/cifar.html.访问 CIFAR 数据集

根据数据集来源:

CIFAR-10 数据集由 10 类 60000 张 32×32 彩色图像组成,每类 6000 张。有 50000 个训练图像和 10000 个测试图像。数据集分为五个训练批次和一个测试批次,每个批次有 10000 幅图像。测试批次包含从每个类别中随机选择的 1000 个图像。训练批次以随机顺序包含剩余的图像,但是一些训练批次可能包含来自一个类别的比来自另一个类别的更多的图像。在它们之间,训练批次正好包含来自每个类的 5000 个图像。这些类是完全互斥的。汽车和卡车之间没有重叠。“汽车”包括轿车、SUV 等。而“卡车”只包括大卡车。两者都不包括皮卡车。

CIFAR-10 数据集如图 4-3 所示。

深度学习与计算机视觉教程(二)

图 4-3

CIFAR-10 数据集中的类以及每个类的一些示例。CIFAR-10 数据集是用于测试神经网络功效的流行数据集之一

CIFAR-100 数据集与 CIFAR-10 非常相似,只是它有 100 个类,每个类包含 600 幅图像。每个类有 500 个训练图像和 100 个测试图像。CIFAR-100 中的 100 个类被分成 20 个超类。每个图像都有一个“精细”标签(它所属的类)和一个“粗糙”标签(它所属的超类)。

图 4-4 提供了 CIFAR-100 中的等级列表。每个超类都有子类。例如,苹果、蘑菇、橙子、梨等等是子类,它们的超类是水果和蔬菜。

深度学习与计算机视觉教程(二)

图 4-4

CIFAR-100 数据集中所有超类和类的列表

是时候使用 AlexNet 和 VGG16 开发用例了。

4.8 使用 AlexNet 在 CIFAR-10 上工作

让我们使用 AlexNet 在 CIFAR-10 上开发一个分类解决方案。

  1. 在这里导入所有必要的库,并加载 CIFAR-10 数据集。

    import keras
    from keras.datasets import cifar10
    from keras import backend as K
    from keras.layers import Input, Conv2D, GlobalAveragePooling2D, Dense, BatchNormalization, Activation, MaxPooling2D
    from keras.models import Model
    from keras.layers import concatenate,Dropout,Flatten
    
    

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  2. 这里,我们导入 ModelCheckpoint,它将用于创建检查点,以保存基于验证准确性的最佳模型。配置完设置后,我们将详细研究检查点。

    from keras import optimizers,regularizers
    from keras.preprocessing.image import ImageDataGenerator
    from keras.initializers import he_normal
    from keras.callbacks import LearningRateScheduler, TensorBoard, ModelCheckpoint
    
    

    • 1
    • 2
    • 3
    • 4
    • 5
  3. 接下来,我们用 cifar.load_data()步骤加载 CIFAR-10 数据。(x_train,y_train),(x_test,y_test) = cifar10.load_data()

  4. Now, preprocess the images by getting the mean and standard deviation and then standardizing them.

    mean = np.mean(x_train,axis=(0,1,2,3))
    std = np.std(x_train, axis=(0, 1, 2, 3))
    x_train = (x_train-mean)/(std+1e-7)
    x_test = (x_test-mean)/(std+1e-7)
    
    

    • 1
    • 2
    • 3
    • 4
    • 5

    注意实验在有和没有步骤 4 的情况下执行代码,以检查已处理和未处理图像之间的性能差异。

  5. 现在,让我们创建我们的训练和测试目标变量数据。这一步类似于我们在前一章中开发的解决方案。

    y_train = keras.utils.to_categorical(y_train, num_classes)
    y_test = keras.utils.to_categorical(y_test, num_classes)
    
    

    • 1
    • 2
    • 3

    Let us have a look at the dataset.

    fig = plt.figure(figsize=(18, 8))
    columns = 5
    rows = 5
    for i in range(1, columns*rows + 1):
       fig.add_subplot(rows, columns, i)
       plt.imshow(X_train[i], interpolation="lanczos")
    
    

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Figure 4-5 shows some of the images from our dataset. Look at the different classes of images we have. Also, examine the resolution and aspect ratio of the images.

    深度学习与计算机视觉教程(二)

    图 4-5

    此处显示了数据集中的一些示例图像

  6. 现在让我们创建 AlexNet 架构。这里,我们将尝试一种不同的方法。我们将定义一个函数来创建网络。在创建 AlexNet 时,我们从网络中定义了参数的卷积层开始。第一层的内核大小为 11×11,96 个通道,跨距为 4×4。网络紧随其后。

    def alexnet(img_input,classes=10):
        xnet = Conv2D(96,(11,11),strides=(4,4),padding='same',activation='relu',kernel_initializer='uniform')(img_input)
        xnet = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same',data_format=DATA_ FORMAT)(xnet)
        xnet = Conv2D(256,(5,5),strides=(1,1),padding='same', activation="relu",kernel_initializer='uniform')(xnet)
        xnet = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same',data_format=DATA_ FORMAT)(xnet)
        xnet = Conv2D(384,(3,3),strides=(1,1),padding='same', activation="relu",kernel_initializer='uniform')(xnet)
        xnet = Conv2D(384,(3,3),strides=(1,1),padding='same', activation="relu",kernel_initializer='uniform')(xnet)
        xnet = Conv2D(256,(3,3),strides=(1,1),padding='same', activation="relu",kernel_initializer='uniform')(xnet)
        xnet = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same',data_format=DATA_ FORMAT)(xnet)
        xnet = Flatten()(xnet)
        xnet = Dense(4096,activation='relu')(xnet)
        xnet = Dropout(0.25)(xnet)
        xnet = Dense(4096,activation='relu')(xnet)
        xnet = Dropout(0.25)(xnet)
        out_model = Dense(classes, activation="softmax")(xnet)
        return out_model
    
    

    深度学习与计算机视觉教程(二)

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    前面函数的输出将是网络。

  7. 现在让我们输入 32x32x3 形状的图像。然后,使用 AlexNet 函数得到想要的模型。

    img_input=Input(shape=(32,32,3))
    output = alexnet(img_input)
    model=Model(img_input,output)
    
    

    • 1
    • 2
    • 3
    • 4
  8. 然后,我们生成模型的摘要。

    model.summary()
    
    

    • 1
    • 2

    这是总结。我们有所有的层、各自的输出形状和参数数量(图 4-6 )。

深度学习与计算机视觉教程(二)

图 4-6

AlexNet 模型综述。它有 2100 万个参数需要训练

模型摘要表明我们有 2100 万个参数来训练这个 AlexNet 模型。

  1. 我们现在需要编译模型,优化器是用来编译基于 Keras 的模型的参数之一。我们在这里使用随机梯度下降(SGD ),学习率为 0.01,动量为 0.8。随意用不同的学习率和动量值迭代。SGD 包括对动量、学习率衰减和内斯特罗夫动量的支持。你必须意识到什么是学习率,我们在前面的章节中已经讨论过了。动量是在相关方向上加速 SGD 并抑制振荡的参数。最后一个参数内斯特罗夫表示是否应用内斯特罗夫动量。
sgd = optimizers.SGD(lr=.01, momentum=0.8, nesterov=True) model.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy'])

  • 1
  • 2

Note

我们在这里使用了 SGD。对 Adam、RMSProp、Adagrad 和 Adadelta 进行实验,比较训练时间和各自的表现。

  1. 接下来,我们设置检查点。我们这样做是为了只有当验证准确性提高时,最后保存的模型才会被替换。

    想知道什么是检查点吗?

    While we develop our model, the validation accuracy decreases often in the next epoch, and hence we would like to use the accuracy in the earlier epoch. For example, if in the fifth epoch we get 74% accuracy which decreases to 73% in the sixth epoch, we would like to use the fifth epoch and not the sixth. Using checkpoint, we can save the model only when the accuracy has increased and not with each subsequent epoch. If we do not have a checkpoint, we will get only the final model in the final epoch. And it might not be the best performing model. Hence, it is advised to use checkpoints.

    filepath="weights.best.hdf5"
    checkpoint = ModelCheckpoint(filepath, monitor="val_acc", verbose=1, save_best_only=True, mode="max")
    callbacks_list = [checkpoint]
    epochs = 50
    
    

    • 1
    • 2
    • 3
    • 4
    • 5
  2. 现在,让我们使用 ImageDataGenerator 进行数据扩充。它通过实时数据扩充生成批量张量图像数据。数据将(分批)循环。

    datagen = ImageDataGenerator(horizontal_flip=True, width_shift_range=0.115, height_shift_range=0.115, fill_mode="constant",cval=0.)
    datagen.fit(x_train)
    
    

    • 1
    • 2
    • 3
  3. 开始训练网络。注意,我们已经将回调设置为 callbacks_list,这是我们为模型检查点描述的。

    model.fit_generator(datagen.flow(x_train, y_train,batch_size=batch_size), steps_per_epoch=iterations,
    epochs=epochs,
    callbacks=callbacks_list,
    validation_data=(x_test, y_test))
    
    

    • 1
    • 2
    • 3
    • 4
    • 5

训练网络后,您将获得以下输出。为了简洁起见,我们只显示了最后的时代。

深度学习与计算机视觉教程(二)

注意,在 50 个时期之后,我们获得了 74.29%的验证准确度。还要注意,如果精度没有提高,模型不会改变最佳精度 74.80%。

  1. 现在让我们来衡量一下我们的表现。首先,我们衡量我们的准确性;随之而来的将是损失。代码和图形类似于前一章中生成的代码和图形。

    import matplotlib.pyplot as plt
    f, ax = plt.subplots()
    ax.plot([None] + model.history.history['acc'], 'o-') ax.plot([None] + model.history.history['val_acc'], 'x-') ax.legend(['Train acc', 'Validation acc'], loc = 0)
    ax.set_title('Training/Validation acc per Epoch')
    ax.set_xlabel('Epoch')
    ax.set_ylabel('acc')
    
    

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

下面是输出(图 4-7 )。

深度学习与计算机视觉教程(二)

图 4-7

我们正在绘制训练数据集与验证数据集的准确性对比图

  1. 现在,让我们来衡量损失。
import matplotlib.pyplot as plt
f, ax = plt.subplots()
ax.plot([None] + model.history.history['loss'], 'o-') ax.plot([None] + model.history.history['val_loss'], 'x-')
ax.legend(['Train loss', 'Validation loss'], loc = 0) ax.set_title('Training/Validation loss per Epoch') ax.set_xlabel('Epoch')
ax.set_ylabel('acc')

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

下面是输出(图 4-8 )。

深度学习与计算机视觉教程(二)

图 4-8

此处显示了培训和验证损失

  1. 我们现在将使用该模型进行预测,并使用 predict 函数为我们的预测生成混淆矩阵。

  2. 当您尝试使用以下函数生成混淆矩阵时,会收到以下错误:

    from sklearn.metrics import confusion_matrix import numpy as np
    confusion_matrix(y_test, np.argmax(predictions,axis=1))
    
    

    • 1
    • 2
    • 3
predictions = model.predict(x_test)

  • 1
  • 2

深度学习与计算机视觉教程(二)

我们得到这个错误是因为混淆矩阵要求预测值和图像标签都是一位数,而不是一次性编码的向量。我们的 test_values 必须在这里进行转换,它应该会为我们解决错误。

Note

打印预测值和 y_test[1]值以检查差异。

  1. 让我们将标签转换为一位数 1,然后我们将生成混淆矩阵。

    rounded_labels=np.argmax(y_test, axis=1) rounded_labels[1]
    cm = confusion_matrix(rounded_labels, np.argmax(predictions,axis=1))
    cm
    
    

    • 1
    • 2
    • 3
    • 4

您将得到下面的混淆矩阵作为最后一条语句的输出。作为一个十类问题,它将生成所有相应十类的值。

深度学习与计算机视觉教程(二)

  1. 现在定义混淆矩阵函数并生成混淆矩阵:

    def plot_confusion_matrix(cm):
       cm = [row/sum(row) for row in cm]
       fig = plt.figure(figsize=(10, 10))
       ax = fig.add_subplot(111)
       cax = ax.matshow(cm, cmap=plt.cm.Oranges) fig.colorbar(cax)
       plt.title('Confusion Matrix') plt.xlabel('Predicted Class IDs')    plt.ylabel('True Class IDs')
       plt.show()
    
       plot_confusion_matrix(cm)
    
    

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

前面代码的输出如图 4-9 所示。

深度学习与计算机视觉教程(二)

图 4-9

此处显示了问题的混淆矩阵。该网络对一些课程做出了很好的预测。建议通过调整超参数来测试网络

从混淆矩阵可以清楚地看出,网络对某些类别做出了正确的预测。建议您调整超参数,并检查哪些类会因网络变化而受到影响。

在这个用例中,我们使用 AlexNet 在 CIFAR-10 数据集上工作,我们获得了 74.80%的验证准确率。接下来让我们训练我们的 VGG 网络并测量它的性能。

4.9 利用 VGG 研究 CIFAR-10

我们现在将使用 VGG 网络在 CIFAR-10 上开发一个分类解决方案。

由于这里的大多数步骤与上一节中的步骤相似,所以我们不会解释所有的代码片段,但是只要与前面的讨论有差异,我们都会详细说明。

  1. 导入库。

    import keras
    from keras.datasets import cifar10
    from keras.preprocessing.image import ImageDataGenerator
    from keras.models import Sequential
    from keras.callbacks import ModelCheckpoint
    from keras.layers import Dense, Dropout, Activation, Flatten from keras.layers import Conv2D, MaxPooling2D, BatchNormalization
    from keras import optimizers
    import numpy as np
    from keras.layers.core import Lambda
    from keras import backend as K
    from keras import regularizers
    import matplotlib.pyplot as plt
    import warnings warnings.filterwarnings("ignore")
    
    

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  2. 设置你的超参数。

    number_classes = 10
    wght_decay = 0.00005
    x_shape = [32,32,3]
    batch_size = 64
    maxepoches = 30
    learning_rate = 0.1
    learning_decay = 1e-6
    learning_drop = 20
    
    

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  3. 加载数据并生成一些图像。

    (x_train, y_train), (x_test, y_test) = cifar10.load_data()
    x_train = x_train.astype('float32')
    x_test = x_test.astype('float32')
    fig = plt.figure(figsize=(18, 8))
    columns = 5
    rows = 5
    for i in range(1, columns*rows + 1):
       fig.add_subplot(rows, columns, i)
       plt.imshow(x_train[i], interpolation="lanczos")
    
    

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  4. 标准化图像数据。

    mean = np.mean(x_train,axis=(0,1,2,3))
    std = np.std(x_train, axis=(0, 1, 2, 3))
    x_train = (x_train-mean)/(std+1e-7)
    x_test = (x_test-mean)/(std+1e-7)
    y_train = keras.utils.to_categorical(y_train, number_classes)
    y_test = keras.utils.to_categorical(y_test, number_classes)
    
    

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  5. 立即创建您的 VGG16 模型。

    model = Sequential()
    model.add(Conv2D(64, (3, 3), padding="same",
    input_shape=x_shape,kernel_regularizer=regularizers.l2(wght_decay))) model.add(Activation('relu'))
    model.add(BatchNormalization())
    model.add(Dropout(0.3))
    model.add(Conv2D(64, (3, 3), padding="same",kernel_regularizer=regularizers.l2(wght_decay)))
    model.add(Activation('relu'))
    model.add(BatchNormalization())
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Conv2D(128, (3, 3), padding="same",kernel_regularizer=regularizers.l2(wght_decay)))
    model.add(Activation('relu'))
    model.add(BatchNormalization())
    model.add(Dropout(0.4))
    model.add(Conv2D(128, (3, 3), padding="same",kernel_regularizer=regularizers.l2(wght_decay)))
    model.add(Activation('relu'))
    model.add(BatchNormalization())
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Conv2D(256, (3, 3), padding="same",kernel_regularizer=regularizers.l2(wght_decay))) model.add(Activation('relu'))
    model.add(BatchNormalization())
    model.add(Dropout(0.4))
    model.add(Conv2D(256, (3, 3), padding="same",kernel_regularizer=regularizers.l2(wght_decay))) model.add(Activation('relu'))
    model.add(BatchNormalization())
    model.add(Dropout(0.4))
    model.add(Conv2D(256, (3, 3), padding="same",kernel_regularizer=regularizers.l2(wght_decay))) model.add(Activation('relu'))
    model.add(BatchNormalization())
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Conv2D(512, (3, 3), padding="same",kernel_regularizer=regularizers.l2(wght_decay))) model.add(Activation('relu'))
    model.add(BatchNormalization())
    model.add(Dropout(0.4))
    model.add(Conv2D(512, (3, 3), padding="same",kernel_regularizer=regularizers.l2(wght_decay))) model.add(Activation('relu'))
    model.add(BatchNormalization())
    model.add(Dropout(0.4))
    model.add(Conv2D(512, (3, 3), padding="same",kernel_regularizer=regularizers.l2(wght_decay))) model.add(Activation('relu'))
    model.add(BatchNormalization())
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Conv2D(512, (3, 3), padding="same",kernel_regularizer=regularizers.l2(wght_decay)))
    model.add(Activation('relu'))
    model.add(BatchNormalization())
    model.add(Dropout(0.4))
    model.add(Conv2D(512, (3, 3), padding="same",kernel_regularizer=regularizers.l2(wght_decay)))
    model.add(Activation('relu'))
    model.add(BatchNormalization())
    model.add(Dropout(0.4))
    model.add(Conv2D(512, (3, 3), padding="same",kernel_regularizer=regularizers.l2(wght_decay))) model.add(Activation('relu'))
    model.add(BatchNormalization())
    model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.5))
    model.add(Flatten()) model.add(Dense(512,kernel_regularizer=regularizers.l2(wght_decay))) model.add(Activation('relu'))
    model.add(BatchNormalization())
    model.add(Dropout(0.5)) model.add(Dense(number_classes)) model.add(Activation('softmax'))
    
    

    深度学习与计算机视觉教程(二)

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
  6. 生成模型摘要。

  7. 从图像增强步骤到拟合 VGG 网络,步骤与上一节中的 AlexNet 示例完全相同。

    image_augm = ImageDataGenerator( featurewise_center=False, samplewise_center=False, featurewise_std_normalization=False, samplewise_std_normalization=False, zca_whitening=False, rotation_range=12, width_shift_range=0.2, height_shift_range=0.1, horizontal_flip=True, vertical_flip=False)
    image_augm.fit(x_train)
    sgd = optimizers.SGD(lr=learning_rate, decay=learning_decay, momentum=0.9, nesterov=True)
    model.compile(loss='categorical_crossentropy', optimizer=sgd,metrics=['accuracy'])
    filepath="weights.best.hdf5"
    checkpoint = ModelCheckpoint(filepath, monitor="val_acc", verbose=1,
    save_best_only=True, mode="max")
    callbacks_list = [checkpoint]
    trained_model = model.fit_generator(image_augm.flow(x_train, y_train, batch_size=batch_size), steps_per_epoch=x_train.shape[0]//batch_size,
    epochs=maxepoches,
    validation_data=(x_test, y_test),callbacks=callbacks_list,verbose=1)
    
    

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
model.summary()

  • 1
  • 2

当你训练你的模型时,你将得到这个输出。我们只展示最后的时代。

深度学习与计算机视觉教程(二)

最佳验证准确率为 82.74%。

  1. 画出精确度量,最后画出混淆矩阵。代码和 AlexNet 用的一模一样。

这是精度图(图 4-10 )。

深度学习与计算机视觉教程(二)

图 4-10

VGG 网络的训练和验证准确度图

这里是损失图(图 4-11 )。

深度学习与计算机视觉教程(二)

图 4-11

VGG 网络的训练和验证损失图

这是混淆矩阵:

深度学习与计算机视觉教程(二)

下面是混淆矩阵图(图 4-12 )。

深度学习与计算机视觉教程(二)

图 4-12

为 VGG 图生成的混淆矩阵

请注意,验证损失比训练准确度小。我们通常期望训练精度高于测试精度,但是这里我们看到了相反的情况。其原因如下:

  1. 当我们使用 Keras 生成深度学习解决方案时,有两种模式,即训练和测试。在测试阶段,辍学或 L1/L2 体重调整被关闭。

  2. 我们计算每批训练数据的平均损失以获得训练损失,而为了计算测试损失,使用模型并在时期结束时进行;因此,测试损失较低。

  3. 此外,随着时间的推移,我们通过训练不断地改变模型,因此最初批次中计算的损失大多高于最终批次。

一般来说,你会发现测试精度低于训练精度。如果测试精度远低于训练精度,我们称之为过拟合,我们已经在第一章中讨论过。我们将在本书后面的章节中再次详细探讨这个概念。

我们现在将比较这两种模型的性能。

4.10 比较 AlexNet 和 VGG

如果我们对比 AlexNet 和 VGGNet 的性能,我们会发现

  1. VGG 的验证准确率高于 AlexNet(分别为 82.74%和 74.80%)。

  2. 混淆矩阵显示,与 AlexNet 相比,VGG 预测了更高数量的正确类别。

但这并不意味着我们可以概括 VGG 的表现总是比 AlexNet 更好。根据数据集和手头的业务问题,我们测试这两个网络。

注意,对于我们讨论的例子,AlexNet 被训练了 50 个时期,相比之下,VGGNet 被训练了 30 个时期。试着训练相同的历元数,分析差异。

在决定挑选网络时,我们比较了这两种体系结构。我们比较了所需的训练时间、数据集要求、历元方向的移动以及最终的精度 KPI。然后我们得出结论。

这两种架构在深度学习社区中都经常被引用和推崇。

4.11 使用 CIFAR-100

建议您对 CIFAR-100 数据集使用类似的代码。代码中有一些变化,如下所示。

导入库时,不要导入 cifar10,而是导入 cifar100。

from keras.datasets import cifar100

  • 1
  • 2

类似地,加载 CIFAR-100 的数据集。

(x_train, y_train), (x_test, y_test) = cifar100.load_data()

  • 1
  • 2

班级人数是 100 人而不是 10 人。凡是提到班级人数的地方,都改成 100。然后拟合模型,分析结果。

至此,我们已经使用数据集完成了 Python 代码的实现。我们现在可以进行本章的总结了。

4.12 摘要

在本章中,我们介绍了 AlexNet 和 VGGNet,并使用 CIFAR 数据集创建了解决方案。

这两个网络通常用于测试任何基于计算机视觉的解决方案。它们易于理解,实施起来也很快。

AlexNet 和 VGG 在论文和文献中被多次引用。它们也广泛应用于许多实际解决方案中。在实际实施过程中,数据集的质量将决定网络的预测能力。因此,如果您使用网络在自定义数据集上实现,则需要对图像的收集进行尽职调查。

需要注意的重要一点是,数据集应该代表真实世界的业务场景。如果数据集没有捕获真实世界的业务场景,解决方案将无法解决业务问题。而且大量的时间和资源都花在了获取数据集上。我们将在本书的最后一章中再次讨论这些概念——在这一章中,我们将详细讨论这些需求。

现在,让我们转向下一个架构,开发更多实际的用例。让我们继续这段旅程吧!

你现在应该能回答练习中的问题了!

Review Exercises

  1. AlexNet 和 VGG16 有什么区别?

  2. 解释检查点的意义。

  3. 使用上一章中使用的德国交通标志数据集,并对其拟合 VGG16 和 VGG19。比较两个模型的精确度。

  4. 了解 VGG16 和 AlexNet 的行业实现。

  5. https://data.mendeley.com/datasets/4drtyfjtfy/1 下载用于影像分类的多类天气数据集。开发 AlexNet 和 VGG 网络并比较其准确性。

  6. https://data.mendeley.com/datasets/5y9wdsg2zt/2 获取用于分类的混凝土裂缝图像数据集,并开发 VGG16 和 VGG19 解决方案。

进一步阅读

  1. AlexNet 原创论文在 https://papers.nips.cc/paper/4824-imagenet- classification-with-deep-convolutional-neural-networks.pdf

  2. VGG16 原纸在 https://arxiv.org/pdf/1409.1556.pdf

  3. 关于 CIFAR-10 的论文可在 https://paperswithcode.com/sota/image-classification-on-cifar-10 查阅。

  4. 关于 CIFAR-100 的论文可在 https://paperswithcode.com/sota/image-classification-on-cifar-100 查阅。

五、使用深度学习的对象检测

仅仅因为某样东西没有按照你的计划去做,并不意味着它是无用的。—托马斯·爱迪生

为了解决一个特定的问题,我们会尝试多种解决方案,很多时候经过几次迭代,我们会找到最佳解决方案。机器学习和深度学习没什么区别。在发现阶段,进行改进和不断修改,以提高先前版本算法的性能。在最后一个阶段观察到的弱点、计算的缓慢、分类的不正确——都为更好的解决方案铺平了道路。

在上两章中,我们了解并创建了将图像分类为二进制或多类的解决方案。但是大多数图像中只有一个物体。我们没有识别出图像中物体的位置。我们只是简单地说物体是否出现在图像中。

在这一章中,我们将识别任何图像中的对象。同时,它的位置也将通过在其周围创建一个边界框来确定。这比我们迄今为止开发的图像分类解决方案领先了一步。

有相当多的对象检测架构,如 R-CNN、快速 R-CNN、更快 R-CNN、SSD(单次多盒检测器)和 YOLO(你只看一次)。

我们将在本章中研究这些网络架构,并为其创建 Python 解决方案。

我们将在本章中讨论以下主题:

  1. 对象检测和用例

  2. R-CNN、快速 R-CNN 和更快 R-CNN 架构

  3. 单发多盒探测器

  4. 你只看一次(YOLO)

  5. 算法的 Python 实现

欢迎来到第五章,祝一切顺利!

5.1 技术要求

本章的代码和数据集上传到本书的 GitHub 链接 https://github.com/Apress/computer-vision-using-deep-learning/tree/main/Chapter5 。一如既往,本章我们将使用 Python Jupyter 笔记本。对于这一章,可能需要一个 GPU 来执行代码,您可以使用 Google Colaboratory。设置 Google Colab 的说明。与本书最后一章相同。

让我们在下一部分继续深入学习架构。

5.2 物体检测

对象检测是机器学习和深度学习领域中被引用和认可最多的解决方案之一。这是一个相当新颖的解决方案,解决起来非常有趣。对象检测的用例相当多。因此,组织和研究人员正在花费大量的时间和资源来发现这种能力。顾名思义,目标检测是一种在图像或视频中定位目标的计算机视觉技术。该检测也可以在实况流视频中进行。当我们人类看图片时,我们可以快速识别图像中的对象及其各自的位置。我们可以快速分类它是苹果还是汽车还是人。我们也可以从任何角度确定。原因是我们的大脑被训练成能够识别各种物体。即使一个物体的尺寸变得越来越小或越来越大,我们也能够定位和探测它们。

目标是使用机器学习和深度学习来复制这种决策智能。我们将研究对象检测、定位和分类的概念,并开发 Python 代码。

但是在研究对象检测的基础知识之前,我们应该首先研究对象分类、对象定位和对象检测之间的区别。它们是对象检测的构建概念。现在,我们将在下一节研究这三个组成部分的区别。

5.2.1 物体分类、物体定位和物体检测

请看图 5-1 中吸尘器的图片。在前面的章节中,我们开发了图像分类解决方案,将这些图像分类为“真空吸尘器”或“非真空吸尘器”因此,我们可以很容易地将第一张图片标记为真空吸尘器。

另一方面,定位是指在图像中找到对象的位置。因此,当我们进行图像定位时,这意味着该算法具有双重职责:对图像进行分类,并在其周围绘制一个边界框,如第二幅图像所示。在图 5-1 的第一张图中,我们有一个真空吸尘器,在第二张图中,我们已经对它进行了定位。特小号

深度学习与计算机视觉教程(二)

图 5-1

物体检测意味着物体的识别和定位。在第一张图中,我们可以分类它是否是吸尘器,而在第二张图中,我们在它周围画一个方框,这是图像的定位

xs

为了扩展解决方案,我们可以在同一幅图像中有多个对象,甚至在同一幅图像中有多个不同类别的对象,我们必须识别所有这些对象。并在它们周围绘制边界框。一个例子可以是被训练来检测汽车的解决方案。在繁忙的道路上,会有许多汽车,因此解决方案应该能够检测到每一辆汽车,并在它们周围绘制边界框。

物体检测无疑是一个奇妙的解决方案。我们现在将在下一节讨论主要的对象检测用例。

5.2.2 物体检测的使用案例

深度学习已经扩展了跨领域和跨组织的许多能力。物体检测是一个关键的问题,也是一个非常强大的解决方案,它在我们的商业和个人世界中产生了巨大的影响。对象检测的主要用例有

  1. 物体检测是自动驾驶技术背后的关键智能。它允许用户检测汽车,行人,背景,摩托车,等等,以提高道路安全。

  2. 我们可以检测人们手中的物体,该解决方案可用于安全和监控目的。监控系统可以变得更加智能和精确。人群控制系统可以变得更加复杂,反应时间将会缩短。

  3. 一种解决方案可以用于检测购物篮中的对象,并且它可以被零售商用于自动交易。这将加快整个过程,减少人工干预。

  4. 物体检测也用于测试机械系统和生产线。我们可以检测产品上可能污染产品质量的物体。

  5. 在医学界,通过分析身体部位的图像来识别疾病将有助于更快地治疗疾病。

没有预见到这种用途的领域非常少。这是一个被高度研究的领域,每天都有新的进展。全球各地的组织和研究人员正在这一领域掀起巨大的波澜,并创造出突破性的解决方案。

我们已经研究了对象检测的主要用例。现在让我们来看一些物体检测的方法。

5.3 物体检测方法

我们可以使用机器学习和深度学习来执行对象检测。我们将在本书中讨论深度学习方法,但对于好奇的读者,这里有几个解决方案:

  1. 使用简单属性(如对象的形状、大小和颜色)进行图像分割。

  2. 我们可以使用聚合通道功能(ACF ),它是通道功能的变体。ACF 不计算不同位置或比例的矩形和。相反,它直接以像素值的形式提取特征。

  3. Viola-Jones 算法可以用于人脸检测。建议的论文在本章末尾。

还有其他解决方案,如 RANSAC(随机样本一致性)、基于 Haar 特征的级联分类器、使用 HOG 特征的 SVM 分类器等等,可以用于对象检测。在本书中,我们将重点放在深度学习方法上。

以下深度学习架构通常用于对象检测:

  1. R-CNN:有 CNN 特色的地区。它将地区提案与 CNN 结合起来。

  2. 快速 R-CNN:一种基于快速区域的卷积神经网络。

  3. 更快的 R-CNN:基于区域提议算法的目标检测网络,以假设目标位置。

  4. 屏蔽 R-CNN:这个网络通过在每个感兴趣的区域上增加分段屏蔽的预测来扩展更快的 R-CNN。

  5. YOLO:你只能看一次建筑。它提出了一个单一的神经网络来预测边界框和分类概率的图像在一个单一的评估。

  6. SSD:单次多盒探测器。它提出了一种使用单一深度神经网络来预测图像中的对象的模型。

现在,我们将在下一节中检查深度学习框架。有一些基本概念需要研究,它们构成了目标检测技术的基础。我们也会研究他们。在深度学习框架的讨论之后,我们将创建 Python 解决方案。

5.4 用于对象检测的深度学习框架

我们现在将从基于深度学习的对象检测算法和架构开始。它们由一些组件和概念组成。在深入研究架构之前,让我们先认识一下物体检测的几个重要组成部分。其中最关键的是

  • 用于目标检测的滑动窗口方法

  • 包围盒方法

  • 并集上的交集

  • 非最大抑制

  • 锚盒概念

在下一节中,我们将从滑动窗口方法开始。

5.4.1 用于对象检测的滑动窗口方法

当我们想要检测对象时,一个非常简单的方法可以是:为什么不将图像划分为区域或特定区域,然后对它们中的每一个进行分类。这种物体检测的方法是滑动窗口。顾名思义,它是一个可以在整个图像中滑动的矩形框。该框具有固定的长度和宽度,可以在整个图像上移动一大步。

请看图 5-2 中真空吸尘器的图像。我们在图像的每个部分使用滑动窗口。红色方框在吸尘器的整个图像上滑动。从左到右,然后垂直,我们可以观察到图像的不同部分正在成为观察点。因为窗口是滑动的,所以它被称为滑动窗口方法。

深度学习与计算机视觉教程(二)

图 5-2

滑动窗口方法检测并识别目标。请注意滑动框是如何在整个图像中移动的;该过程能够检测到,但实际上是一个耗时的过程,并且计算量也很大

然后,对于这些被裁剪的区域中的每一个,我们可以分类该区域是否包含我们感兴趣的对象。然后我们增加滑动窗口的大小,继续这个过程。

滑动窗口已经被证明是可行的,但是它是一种计算量非常大的技术,并且由于我们要对图像中的所有区域进行分类,所以实现起来会很慢。此外,为了定位对象,我们需要一个小窗口大小和小步幅。但这仍然是一个简单的理解方法。

下一种方法是包围盒方法。

5.5 包围盒方法

我们讨论了滑动窗口方法。它输出不太精确的边界框,因为它依赖于窗口的大小。因此,我们有另一种方法,其中我们将整个图像分成网格(x 乘 x),然后为每个网格定义我们的目标标签。我们可以在图 5-3 中显示一个边框。

深度学习与计算机视觉教程(二)

图 5-3

边界框可以生成边界框的 x 坐标、y 坐标、高度和宽度以及类别概率分数

边界框可以给我们以下细节:

  • Pc:网格单元中有对象的概率(0:无对象,1:有对象)。

  • Bx:如果 Pc 为 1,则为包围盒的 x 坐标。

  • By:如果 Pc 为 1,则为边界框的 y 坐标。

  • Bh:如果 Pc 是 1,就是包围盒的高度。

  • Bw:如果 Pc 为 1,则为边界框的宽度。

  • C1:它是物体属于第一类的概率。

  • C2:这是物体属于第二类的概率。

Note

类别的数量取决于手头的问题是二元分类还是多标分类。

如果一个对象位于多个网格上,那么包含该对象中点的网格负责检测该对象。

Info

通常建议使用 19×19 的网格。此外,一个对象的中点位于两个独立的网格中的可能性更小。

到目前为止,我们正在研究确定对象的方法;下一个主题是测量并的交集检测的性能。

5.6 联合交集(IoU)

我们已经研究了一些用于物体检测的方法。在随后的章节中,我们也将研究深度学习架构。但是,我们仍然需要确定我们在物体探测中预测的准确性。交集超过并集是一个测试,以确定我们的预测有多接近实际真相。

用等式 5-1 表示,如图 5-4 所示。

深度学习与计算机视觉教程(二)

图 5-4

并集上的交集被用来衡量检测的性能。分子是公共面积,而分母是两个面积的完全并集。借据的价值越高越好

IoU =重叠区域/合并的整个区域(等式 5-1)

因此,如果交集比并集的值高,就意味着重叠更好。因此,预测更准确、更好。在图 5-5 中的示例中对其进行了描述,以便形象化。

深度学习与计算机视觉教程(二)

图 5-5

重叠模块不同位置的 IoU 值。如果该值更接近 1.0,则意味着与值 0.15 相比,检测更准确

正如我们在图 5-5 中看到的,对于 0.15 的 IoU,与 0.85 或 0.90 相比,两个方框之间的重叠非常少。这意味着 0.85 IoU 的解决方案优于 0.15 IoU 的解决方案。因此,可以直接比较检测解决方案。

交集/并集允许我们测量和比较各种解决方案的性能。这也让我们更容易区分有用的边界框和不太重要的边界框。并集上的交是一个有着广泛用途的重要概念。使用它,我们可以比较和对比所有可能解决方案的可接受性,并从中选择最佳方案。

我们现在将研究非最大值抑制技术,这对于过滤重要的边界框是有用的。

5.7 非最大抑制

当我们试图检测图像中的一个对象时,我们可以将一个对象放在多个网格中。可以用图 5-6 来表示。显然,概率最高的网格将是该物体的最终预测。

深度学习与计算机视觉教程(二)

图 5-6

一个对象可以跨越多个网格,并且无论哪一个给出最佳结果,都是最终用于检测目的的所选网格

整个过程按以下步骤完成:

  1. 得到所有网格各自的概率。

  2. 设置概率阈值和 IoU 阈值。

  3. 丢弃低于该阈值的那些。

  4. 选择概率最大的方框。

  5. 计算剩余盒子的借据。

  6. 丢弃低于 IoU 阈值的那些。

使用非最大值抑制,我们删除了大多数低于某一阈值水平的边界框。

Info

一般情况下,该值保持在 0.5。建议您使用不同的值进行迭代来分析差异。

因此,该算法保留了重要的和有意义的噪声,而去除了较大的噪声。我们现在将着手研究锚盒,这是物体检测过程中的另一个重要因素。

5.8 锚箱

我们希望在深度学习中检测对象,我们需要一种快速准确的方法来获得对象的位置和大小。锚盒是一个有助于检测对象的概念。

锚定框用于捕捉我们希望检测的对象的缩放比例和纵横比。它们具有预定义的大小(高度和宽度),并且基于我们想要检测的对象的大小来确定大小。我们在图 5-7 中展示了锚盒。

深度学习与计算机视觉教程(二)

图 5-7

锚定框用于捕捉缩放比例和纵横比。我们可以平铺锚盒,神经网络将输出一组独特的预测

在对象检测过程中,每个锚框平铺在图像上,神经网络为每个锚框输出一组唯一的预测。输出由每个锚定框的概率得分、IoU、背景和偏移组成。基于所做的预测,可以进一步改进锚盒。

我们可以有多种尺寸的锚盒来检测不同尺寸的物体。使用锚盒,我们可以探测不同尺度的物体。甚至到了可以使用锚定框来检测多个或重叠对象的程度。这无疑是对滑动窗口的巨大改进,因为我们现在可以在一次拍摄中处理整个图像,从而使更快的实时物体检测成为可能。要注意的是,网络不预测边界框。网络预测平铺锚定框的概率得分和改进。

现在,我们已经研究了关键部件;现在我们从深度学习架构开始。

5.9 深度学习架构

深度学习有助于物体检测。我们可以在图像、视频甚至实时视频流中检测感兴趣的对象。在本章的后面,我们将创建一个实时视频流解决方案。

我们之前已经看到滑动窗口方法存在一些问题。对象在图像中可以有不同的位置,并且可以有不同的纵横比或大小。物体可能覆盖整个区域;另一方面,在某些地方,它将只覆盖一小部分。图像中可能存在不止一个对象。物体可以是各种角度或尺寸。或者一个对象可以位于多个网格中。此外,一些用例需要实时预测。这导致具有非常大量的区域,并因此具有巨大的计算能力。这也需要相当长的时间。在这种情况下,传统的图像分析和检测方法没有多大帮助。因此,我们需要基于深度学习的解决方案来解决和开发鲁棒的对象检测解决方案。

基于深度学习的解决方案使我们能够更好地训练,从而获得更好的结果。我们现在正在讨论架构。

让我们从 R-CNN 作为第一个架构开始吧!

5.9.1 基于区域的 CNN (R-CNN)

我们知道拥有大量区域是一项挑战。Ross Girshick 等人提出了 R-CNN 来解决选择大量区域的问题。R-CNN 是基于区域的 CNN 架构。该解决方案建议使用选择性搜索并从图像中仅提取 2000 个区域,而不是对大量区域进行分类。它们被称为“地区提案”

R-CNN 的架构如图 5-8 所示。

深度学习与计算机视觉教程(二)

图 5-8

R-CNN 的流程。这里,我们从输入图像中提取区域建议,计算 CNN 特征,然后对区域进行分类。图片来源: https://arxiv.org/pdf/1311.2524.pdf 并经研究者许可在此发表

参照图 5-8 所示的过程,现在让我们详细了解整个过程:

  1. 第一步是输入图像,如图 5-8 中的步骤 1 所示。

  2. 然后得到我们感兴趣的区域,如图 5-8 中步骤 2 所示。这些是 2000 年提议的区域。使用以下步骤检测它们:

    1. 我们为图像创建初始分割。

    2. 然后,我们为图像生成各种候选区域。

    3. 我们迭代地将相似的区域组合成更大的区域。贪婪搜索方法被用于它。

    4. 最后,我们使用生成的区域来输出最终的区域建议。

  3. 然后在下一步中,我们根据 CNN 中的实现重塑所有的 2000 个区域。

  4. 然后,我们通过 CNN 遍历每个区域,以获得每个区域的特征。

  5. 所提取的特征现在通过支持向量机来对所提议的区域中对象的存在进行分类。

  6. 然后,我们使用包围盒回归来预测物体的包围盒。这意味着我们正在对图像进行最终预测。如上一步所示,我们正在预测图像是飞机、人还是电视显示器。

R-CNN 使用前面的过程来检测图像中的对象。这无疑是一个创新的架构,它提出了一个感兴趣的区域作为检测对象的有效概念。

但是 R-CNN 也有一些挑战,它们是

  1. R-CNN 实现了三种算法(CNN 用于提取特征,SVM 用于对象分类,边界框回归用于获得边界框)。这使得 R-CNN 解决方案的训练速度相当慢。

  2. 它使用 CNN 为每个图像区域提取特征。区域数是 2000。这意味着,如果我们有 1000 张图像,要提取的特征数量是 1000 乘以 2000,这又会使速度变慢。

  3. 由于这些原因,对图像进行预测需要 40-50 秒,因此这对大型数据集来说是个问题。

  4. 还有,选择性搜索算法是固定的,不能做太多改进。

由于 R-CNN 不是很快,并且对于大型数据集来说很难实现,相同的作者提出了快速 R-CNN 来克服这些问题。让我们了解下一节建议的改进!

5.10 快速 R-CNN

在 R-CNN 中,由于我们为一幅图像提取 2000 个区域提议,因此训练或测试图像在计算上是一个挑战。为了解决这个问题,Ross Girshick 等人提出,不是对每个图像执行 CNN 2000 次,而是对一个图像只运行一次,并得到所有感兴趣的区域。

网络的架构如图 5-9 所示。

深度学习与计算机视觉教程(二)

图 5-9

快速 R-CNN 中的过程。我们获取感兴趣区域,然后通过应用感兴趣区域合并图层来重塑所有输入。然后由 FC 层对它们进行评估,最后由 softmax 进行分类。图片来源: https://arxiv.org/pdf/1504.08083.pdf 并经研究者许可在此发表

除了几处变化之外,该方法与其前身相似:

  1. 该图像是如图 5-9 所示的输入。

  2. 图像被传递到卷积网络,卷积网络返回相应的感兴趣区域。

  3. 在下一步中,我们应用利息池区域图层。它导致按照卷积的输入对所有区域进行整形。因此,它通过应用 ROI 池层使所有感兴趣区域的大小相同。

  4. 现在,这些区域中的每一个都被传递给完全连接的网络。

  5. 最后由 softmax 层完成分类。并行地,使用边界框回归器来识别边界框的坐标。

快速 R-CNN 比 R-CNN 有几个优点:

  1. 快速 R-CNN 不需要每次向 CNN 馈送 2000 个区域提议,因此它比 R-CNN 更快。

  2. 它对每幅图像只使用一次卷积运算,而不是 R-CNN 中使用的三次(提取特征、分类和生成边界框)。因此不需要存储特征映射,从而节省了磁盘空间。

  3. 通常,softmax 图层比 SVM 具有更好的精度,并且执行时间更快。

快速的 R-CNN 大大减少了训练时间,并且被证明更加准确。但是由于使用选择性搜索作为获得感兴趣区域的建议方法,性能仍然不够快。因此,对于大型数据集,预测速度不够快。这就是为什么我们有更快的 R-CNN,这是我们接下来要讨论的。

5.11 更快的 R-CNN

为了克服 R-CNN 和快速 R-CNN 的慢,邵青·兰等人提出了更快的 R-CNN。更快的 R-CNN 背后的直觉是取代缓慢且耗时的选择性搜索。更快的 R-CNN 使用区域提议网络或 RPN。论文可在 https://papers.nips.cc/paper/2015/file/14bfa6bb14875e45bba028a21ed38046-Paper.pdf 访问

更快的 R-CNN 的架构如图 5-10 所示。

深度学习与计算机视觉教程(二)

图 5-10

更快的 R-CNN 是对以前版本的改进。它由两个模块组成——一个是深度卷积网络,另一个是快速 R-CNN 检测器

引用原文 https://papers.nips.cc/paper/2015/file/14bfa6bb14875e45bba028a21ed38046-Paper.pdf:

我们的物体探测系统,叫做更快的 R-CNN,由两个模块组成。第一个模块是提出区域的深度全卷积网络,第二个模块是使用所提出区域的快速 R-CNN 检测器。整个系统是一个单一的、统一的目标检测网络。

让我们深入了解一下架构。更快的 R-CNN 的工作方式如下:

  1. 我们获取一个输入图像,并使其通过 CNN,如图 5-10 所示。

  2. From the feature maps received, we apply Region Proposal Networks (RPNs). The way an RPN works can be understood by referring to Figure 5-11.

    深度学习与计算机视觉教程(二)

    图 5-11

    区域建议网络用于更快的 R-CNN。图像取自原始文件

    The substeps followed are

    1. RPN 采用上一步生成的特征地图。

    2. RPN 应用滑动窗口并生成 k 个锚盒。我们已经在上一节讨论了锚盒。

    3. 生成的锚盒具有不同的形状和大小。

    4. RPN 还将预测锚点是否是对象。

    5. 它还会给出边界框回归量来调整锚点。

    6. 要注意的是,RPN 没有建议对象的类别。

    7. 我们将获得目标提案和相应的目标得分。

  3. 应用投资回报池,使所有提案的规模相同。

  4. 最后,我们用 softmax 和线性回归将它们输入到完全连接的层中。

  5. 我们将接收预测的对象分类和各自的边界框。

快速 R-CNN 能够结合智能并使用深度卷积完全连接层和使用建议区域的快速 R-CNN。整个解决方案是用于对象检测的单一且统一的解决方案。

虽然相对于 R-CNN 和快速 R-CNN,更快的 R-CNN 在性能上确实有所改进,但是该算法仍然不能同时分析图像的所有部分。相反,图像的每个部分都是按顺序分析的。因此,需要在单个图像上进行大量的传递来识别所有的对象。此外,由于许多系统按顺序工作,一个系统的性能取决于前面步骤的性能。

在下一节中,我们将继续讨论最著名的算法之一——YOLO 或你只看一次。

5.12 你只看一次(YOLO)

你只看一次或 YOLO 是实时对象检测的目标。我们之前讨论的算法使用区域来定位图像中的对象。这些算法着眼于图像的一部分,而不是完整的图像,而在 YOLO,一个单一的 CNN 预测边界框和各自的类别概率。YOLO 于 2016 年由约瑟夫·雷德蒙、桑托什·迪夫瓦拉、罗斯·吉斯克和阿里·法尔哈迪提出。实际论文可在 https://arxiv.org/pdf/1506.02640v5.pdf 访问。

引用实际论文中的话,“我们将对象检测重新定义为一个单一的回归问题,直接从图像像素到边界框坐标和类别概率。”

如图 5-12 所示,YOLO 将一幅图像划分为一个网格状的单元(用 S 表示)。每个单元预测边界框(由 B 表示)。然后,YOLO 对每个边界框进行处理,并生成一个关于该框形状好坏的置信度得分。还预测了对象的分类概率。最后,选择具有上述类别概率分数的边界框,并且使用它们来定位该图像内的对象。

深度学习与计算机视觉教程(二)

图 5-12

YOLO 过程很简单;图片摘自原纸 https://arxiv.org/pdf/1506.02640v5.pdf

5 . 12 . 1 YOLO 的显著特征

深度学习与计算机视觉教程(二))

(方程式 5-2)

  1. YOLO 把输入图像分成一个 SxS 网格。要注意的是,每个网格只负责预测一个对象。如果对象的中心落在网格单元中,则该网格单元负责检测该对象。

  2. 对于每个网格单元,它预测边界框(B)。每个边界框都有五个属性——x 坐标、y 坐标、宽度、高度和置信度。换句话说,它有(x,y,w,h)和一个分数。这个置信度得分是盒子里有一个物体的置信度。它也反映了边界框的准确性。

  3. 宽度 w 和高度 h 被标准化为图像的宽度和高度。x 和 y 坐标表示相对于网格单元边界的中心。

  4. 置信度定义为概率(目标)乘以 IoU。如果没有对象,置信度为零。否则,置信度等于预测框和实际情况之间的 IoU。

  5. 每个网格单元预测 C 个条件类概率–Pr(class I | Object)。这些概率取决于包含对象的网格单元。我们只预测每个网格单元的一组类别概率,而不考虑盒子 b 的数量。

  6. 在测试时,我们将条件类概率和单个类预测相乘。它给出了每个盒子的特定类别的置信度得分。它可以用等式 5-2 来表示:

我们现在将研究如何计算 YOLO 的损失函数。在我们能够详细研究整个架构之前,获得损失函数计算函数是很重要的。

YOLO 的损失函数

我们在上一节已经看到,YOLO 为每个单元格预测了多个边界框。我们选择具有最大 IoU 的包围盒。为了计算损耗,YOLO 优化了模型中输出的平方和误差,因为平方和误差易于优化。

损失函数如等式 5-3 所示,包括定位损失、置信度损失和分类损失。我们首先表示完整的损失函数,然后详细描述各项。

深度学习与计算机视觉教程(二)(方程式 5-3)

在等式 5-3 中,我们有定位损失、置信度损失和分类损失,其中 1 obj i 表示对象是否出现在单元 i 中,1objij 表示单元 i 中的 j边界框预测器“负责”该预测。

让我们来描述一下前面等式中的各项。在这里,我们有

  1. 局部化损失是为了测量预测边界框的误差。它测量它们的位置和尺寸误差。在上式中,前两项代表定位损耗。1obj?? I 如果单元格 I 中的第 j 边界框负责检测物体,则为 1,否则为 0。λ coord 负责边界框坐标损失的权重增加。λ 坐标的默认值为 5。

  2. 置信度损失是在盒子中检测到物体时的损失。这是所示等式中的第二个损失项。在前一学期,我们有

深度学习与计算机视觉教程(二)

  1. 下一项是如果没有检测到物体的置信度损失。在前一学期,我们有

深度学习与计算机视觉教程(二)

  1. 最后一项是分类损失。如果对象确实被检测到,那么对于每个单元,它是每个类的类概率的平方误差。

深度学习与计算机视觉教程(二)

最终损失是所有这些部分的总和。作为任何深度学习解决方案的目标,目标将是最小化该损失值。

现在我们已经理解了 YOLO 和损失函数的属性;我们现在将着手研究 YOLO 的实际建筑。

YOLO 建筑

网络设计如图 5-13 所示,取自 https://arxiv.org/pdf/1506.02640v5.pdf 的实际纸张。

深度学习与计算机视觉教程(二)

图 5-13

完整的 YOLO 建筑;图片取自 https://arxiv.org/pdf/1506.02640v5.pdf 的原纸

在论文中,作者提到该网络受到了谷歌网的启发。该网络有 24 个卷积层,后面是 2 个全连接层。YOLO 没有使用 GoogLeNet 使用的 Inception 模块,而是使用 1×1 缩减层,然后是 3×3 卷积层。YOLO 可能会发现同一物体的复制品。为此,实施了非最大抑制。这将删除重复的较低置信度得分。

在图 5-14 中,我们有一个 13×13 网格的图形。总共有 169 个网格,其中每个网格预测 5 个边界框。因此,总共有 169*5 = 845 个边界框。当我们应用 30%或更多的阈值时,我们得到 3 个边界框,如图 5-14 所示。

深度学习与计算机视觉教程(二)

图 5-14

YOLO 过程将该区域划分成 SxS 网格。每个网格预测五个包围盒,基于阈值设置(这里是 30%),我们得到最终的三个包围盒;图像取自原始文件

所以,YOLO 只看了图像一次,但看的方式很巧妙。这是一种非常快速的实时处理算法。引用原文:

  1. YOLO 简单得令人耳目一新。

  2. YOLO 速度极快。因为我们把检测框架作为一个回归问题,所以我们不需要复杂的流水线。我们只是在测试时对新图像运行我们的神经网络来预测检测。我们的基础网络在 Titan X GPU 上以每秒 45 帧的速度运行,没有批处理,快速版本的运行速度超过 150 fps。这意味着我们可以实时处理流视频,延迟不到 25 毫秒。此外,YOLO 的平均精度是其他实时系统的两倍多。

  3. YOLO 在做预测时会对图像进行全局推理。与基于滑动窗口和区域提议的技术不同,YOLO 在训练和测试期间看到整个图像,因此它隐式地编码了关于类及其外观的上下文信息。

  4. YOLO 学习物体的概括表示。当在自然图像上训练和在艺术品上测试时,YOLO 远远超过 DPM 和 R-CNN 等顶级检测方法。由于 YOLO 是高度概括的,当应用于新的领域或意想不到的输入时,它不太可能崩溃。

YOLO 也面临一些挑战。它遭受高定位误差。此外,由于每个网格单元只能预测两个盒子,并且只能输出一个类,因此 YOLO 只能预测有限数量的附近物体。它也有召回率低的问题。因此在 YOLOv2 和 YOLOv3 的下一个版本中,这些问题都得到了解决。有兴趣的读者可以在 https://pjreddie.com/darknet/yolo/ 官网获取深入的知识。

YOLO 是最广泛使用的对象检测解决方案之一。它的独特之处在于简单快捷。我们将在下一部分研究的下一个深度学习架构是单次多盒检测器或 SSD。

5.13 单次多盒探测器(SSD)

到目前为止,我们已经在上一节讨论了 R-CNN、快速 R-CNN、更快 R-CNN 和 YOLO。为了克服网络在实时对象检测中工作的缓慢性,C. Szegedy 等人在 2016 年 11 月提出了 SSD(单次多盒检测器)网络。论文可在 https://arxiv.org/pdf/1512.02325.pdf 访问。

固态硬盘使用我们在前面章节讨论过的 VGG16 架构,但做了一些修改。通过使用 SSD,只需要一次拍摄就可以检测到一个对象中的多个图像。它因此被称为单次拍摄,因为它利用单次向前传递来进行物体定位和分类。基于区域提案网络(RPN)的解决方案,如 R-CNN、Fast R-CNN,需要两次拍摄,第一次拍摄获取区域提案,第二次拍摄检测每个提案的对象。因此 SSD 被证明比基于 RPN 的方法快得多。Szegedy 等人称之为多盒,探测器这个词的意义显而易见。让我们深入探讨多盒检测机的概念。

参见图 5-15 。我们可以说,在应用和通过一系列卷积之后,我们获得了尺寸为 m×n 和 p 通道的特征层。

深度学习与计算机视觉教程(二)

图 5-15

SSD 工艺如图。我们有一个与地面真相(燃气轮机)的原始图像。8×8 卷积完成。我们得到不同大小和位置的包围盒;图像取自原始文件

对于每个位置,我们将得到 k 个边界框,它们可能具有不同的大小和纵横比。并且对于这 k 个边界框中的每一个,我们计算 c 类分数和相对于原始默认边界框的四个偏移,以最终接收(c+4)个 kmn 输出。

SSD 实现平滑 L1 范数来计算位置损失。它可能没有 L1 那么精确,但仍然相当精确。

更多关于多框方法的信息可以在 https://arxiv.org/abs/1412.1441 阅读。

完整的网络如图 5-16 所示,作为与 YOLO 的对比。经作者许可,图片取自同一篇论文。

深度学习与计算机视觉教程(二)

图 5-16

此处显示了 YOLO 和固态硬盘的对比。图片取自 https://arxiv.org/pdf/1512.02325.pdf 的原纸

在 SSD 中,不同层的特征图正在通过 3×3 卷积层来提高精度。如果我们分析前面的结构,我们可以观察到,对于对象检测的第一层(conv4_3),它具有 38×38 的空间维度,这在尺寸上是相当大的缩减,导致预测较小尺寸的对象的准确度较低。对于同一个 conv4_3,我们可以使用前面讨论的公式来计算输出。对于 conv4_3,输出将是 38x38x4x(c+4 ),其中 c 是要预测的类的数量。

SSD 使用两个损失函数来计算损失——置信度损失(L conf )和本地化损失(L loc )。L conf 是进行类预测的损失,而 L loc 是地面真实值和预测框之间的不匹配。前面提到的论文中给出了这两种损耗的数学公式,它们的推导超出了本书的范围。

固态硬盘还遵循其他一些重要流程:

  1. 通过翻转、裁剪和颜色失真来增加数据以提高准确性。每个训练示例都是随机抽样的,如下所示:
    1. 利用原始图像。

    2. 对 IoU 为 0.1、0.3、0.5、0.7 或 0.9 的贴片进行采样。

    3. 随机抽取一个补丁。

    4. 采样的面片具有 0.5 和 2 之间的纵横比,并且每个采样的面片的大小是原始大小的[0.1,1]。

然后,将每个采样图像调整到固定大小,并水平翻转。照片失真也用于图像增强。

  1. SSD 实现非最大值抑制来移除重复的预测。我们已经在本章开始时讨论了非最大抑制。

  2. SSD 产生的预测数量比实际的对象数量多。我们负面的比正面的多,造成阶层失衡。引用实际的论文:“我们没有使用所有的负面例子,而是使用每个默认框的最高置信损失对它们进行排序,并选择顶部的例子,这样负面和正面的比例最多为 3:1。我们发现这导致了更快的优化和更稳定的训练。”

基于上述架构,我们可以总结出关于 SSD 的几点:

  1. 检测小型物体可能是一项挑战。为了解决这个问题,我们可以提高图像分辨率。

  2. 精度与速度成反比;如果我们希望提高速度,我们可以增加边界框的数量。

  3. SSD 比 R-CNN 具有更高的分类误差,但是定位误差较小。

  4. 它很好地利用较小的卷积滤波器来预测检测的类别和多尺度特征图。这有助于提高准确性。

  5. 引用原始论文:“SSD 的核心是使用应用于特征地图的小卷积过滤器来预测一组固定的默认边界框的类别分数和框偏移。”

SSD 的精度可以进一步提高。它混淆了具有相似类别的对象。而且是基于 VGG16 构建的,消耗了大量的训练时间。但是 SSD 是一个很棒的解决方案,可以很容易地用于端到端培训。它速度很快,可以实时运行,性能优于更快的 R-CNN。

由此,我们总结出用于对象检测的深度学习架构。我们已经讨论了主要的算法,在接下来的章节中,我们将开发实际的 Python 代码来实现这个解决方案。但在此之前,我们将考察迁移学习的概念。这是一个创新的解决方案,允许我们建立在由专家训练的最先进的算法之上。迁移学习是我们讨论的下一个话题。

5.14 迁移学习

顾名思义,迁移学习就是分享知识或将学习成果转移给他人。在深度学习的世界中,研究人员和组织创新并创建了新的神经网络架构。他们使用多代码强大处理器的最先进功能,并在精心策划和选择的大型数据集上训练算法。

对我们来说,创造这样的智能就像是重新发明轮子。因此,通过迁移学习,我们可以利用那些经过数百万个数据点训练的网络。这使我们能够使用研究人员产生的智能,并在真实世界的数据集上实现相同的功能。这个过程被称为迁移学习

在迁移学习中,我们使用一个预先训练好的模型来达到我们的目的。预训练模型具有来自原始模型的最终权重。使用预训练模型的基本思想是,网络的初始层检测基本特征,随着我们深入,这些特征开始成形。基本特征提取可以用于任何类型的图像。所以,如果一个模型被训练来区分手机,它就可以用来区分汽车。

我们可以在图 5-17 中展示这个过程。在这里,网络的第一层被冻结,将用于提取边、线等低级要素。最后一层可以根据业务问题进行定制。

深度学习与计算机视觉教程(二)

图 5-17

迁移学习利用预先训练好的网络。第一层负责提取低级特征并被冻结。最后一层是为手头的问题定制的

迁移学习使得学习比传统的机器学习更快,并且需要更少的训练数据。我们将在第八章中讨论预训练模型的更多细节。

迁移学习是通过利用在不同环境下开发的解决方案来解决现实世界的业务问题。我们将在下一节和本书的后续章节中使用迁移学习。在前面的章节中,我们可以利用迁移学习来使用这些网络。

理论已经讲得够多了,是时候开发我们的目标检测解决方案了。该打代码了!

5.15 Python 实现

我们将使用 YOLO 实现实时对象检测。必须下载预先训练的重量。代码、权重、标签和预期输出可以从本章开头给出的 GitHub 资源库链接下载。

步骤 1:导入所有需要的库。

import cv2
from imutils.video import VideoStream
import os
import numpy as np

  • 1
  • 2
  • 3
  • 4
  • 5

步骤 2:从本地路径加载配置。我们正在加载权重、配置和标签。我们也在设置检测的设置。

localPath_labels = "coco.names"
localPath_weights = "yolov3.weights"
localPath_config = "yolov3.cfg"
labels = open(localPath_labels).read().strip().split("
")
scaling = 0.005
confidence_threshold = 0.5
nms_threshold = 0.005  # Non Maxima Supression Threshold Vlue
model = cv2.dnn.readNetFromDarknet(localPath_config, localPath_weights)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

第三步:现在我们从这一步的视频开始。然后,我们通过从对象模型访问来配置未连接的层。

cap = VideoStream(src=0).start()
layers_name = model.getLayerNames()
output_layer = [layers_name[i[0] - 1] for i in model.getUnconnectedOutLayers()]

  • 1
  • 2
  • 3
  • 4

Note

建议您研究模型的组件并打印出来,以便更好地理解。

步骤 4:现在我们准备执行对象的检测。此步骤是解决方案的核心步骤。它检测对象和边界框,并在框的顶部添加文本。

我们从 while 循环开始。然后我们在读框架。下一步,设置框架的宽度和高度。然后我们将循环遍历每一帧。如果置信度高于我们之前设置的置信度阈值,则对象将被检测到。

然后,我们进行标记,并且在检测到的边界框上的框中显示对象的相应置信度得分。输出如图 5-18 所示。

while True:
    frame = cap.read()
    (h, w) = frame.shape[:2]
    blob = cv2.dnn.blobFromImage(frame, 1 / 255.0, (416, 416), swapRB=True, crop=False)
    model.setInput(blob)
    nnoutputs = model.forward(output_layer)
    confidence_scores = []
    box_dimensions = []
    class_ids = []

    for output in nnoutputs:
        for detection in output:
            scores = detection[5:]
            class_id = np.argmax(scores)
            confidence = scores[class_id]
            if confidence > 0.5 :
                box = detection[0:4] * np.array([w, h, w, h])
                (center_x, center_y, width, height) = box.astype("int")
                x = int(center_x - (width / 2))
                y = int(center_y - (height / 2))
                box_dimensions.append([x, y, int(width), int(height)])
                confidence_scores.append(float(confidence))
                class_ids.append(class_id)
    ind = cv2.dnn.NMSBoxes(box_dimensions, confidence_scores, confidence_threshold, nms_threshold)
    for i in ind:
        i = i[0]
        (x, y, w, h) = (box_dimensions[i][0], box_dimensions[i][1],box_dimensions[i][2], box_dimensions[i][3])
        cv2.rectangle(frame,(x, y), (x + w, y + h), (0, 255, 255), 2)
        label = "{}: {:.4f}".format(labels[class_ids[i]], confidence_scores[i])
        cv2.putText(frame, label, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,0,255), 2)
    cv2.imshow("Yolo", frame)
    if cv2.waitKey(1) & 0xFF == ord("q"):
        break
cv2.destroyAllWindows()
cap.stop()

深度学习与计算机视觉教程(二)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

输出如图 5-18 所示。我们能够以 99.79%的准确率实时检测手机。

深度学习与计算机视觉教程(二)

图 5-18

这里显示了实时对象检测。我们能够以 99.79%的准确率检测手机

在这个解决方案中,可以识别真实世界的对象。并且围绕该对象以及名称和置信度得分创建边界框。

该解决方案可用于多种使用情形。相同的代码可以为数据集定制,也可以用于检测图像和视频中的对象。

我们现在可以进行本章的总结了。

5.16 摘要

对象检测是一个非常强大的解决方案。它被应用在许多领域和操作中,几乎所有的行业都可以从目标检测中受益。它可以用于光学字符识别、自动驾驶、跟踪物体和人、人群监视、安全机制等等。这种计算机视觉技术正在真正改变实时能力的面貌。

在本章中,我们讨论了对象检测架构——R-CNN、快速 R-CNN、更快 R-CNN、YOLO 和固态硬盘。所有的网络都是基于深度学习的,在设计和架构上都很新颖。然而,有些表现优于其他人。并且通常在速度和准确性之间有一个折衷。所以基于手头的业务问题,我们必须谨慎选择网络。

我们在本章中还讨论了迁移学习。迁移学习是一种新颖的解决方案,它使用已经在数百万幅图像上训练过的预训练网络。迁移学习允许我们通过使用强大的处理器来使用研究人员和作者产生的智能。这是一个工具,使每个人都能够使用这些真正的深度网络,并根据需要定制它们。我们使用迁移学习来使用预先训练的 YOLO 来实时检测对象。我们将在其他章节中继续使用迁移学习方法。

对象检测可能存在于许多实际解决方案中,但输入数据集决定了解决方案的最终精度。因此,如果您使用网络来实现自定义数据集,请在数据收集阶段做好准备。数据将决定和定义你的成功!

现在,在下一章,我们将致力于另一个令人兴奋的话题——人脸检测和识别。让我们继续这段旅程吧!

你现在应该能回答练习中的问题了!

Review Exercises

建议您解决以下问题:

  1. 锚箱和非 max 压制是什么概念?

  2. 边界框对物体检测有多重要?

  3. R-CNN、快速 R-CNN、更快速 R-CNN 有何不同,有何改进?

  4. 迁移学习如何提高神经网络解?

  5. www.kaggle.com/c/open-images-2019-object-detection 下载 Open Images 2019 数据集,并使用它创建一个使用 YOLO 的解决方案。

  6. https://public.roboflow.com/object-detection/chess-full 下载象棋数据集,并使用它根据本章中使用的网络来定位棋子。

  7. https://public.roboflow.com/object-detection/raccoon 获取浣熊数据集,并使用它来创建对象检测解决方案。

  8. https://cocodataset.org/#home 获取 COCO 数据集,并使用它来比较使用不同网络的性能。

  9. https://public.roboflow.com/object-detection/vehicles-openimages 下载 Vehicles-OpenImages 数据集,并创建目标检测解决方案。

进一步阅读

  1. 探索论文《基于深度学习的物体检测》: https://ieeexplore.ieee.org/document/8110383

  2. 探索论文《MobileNets:用于移动视觉应用的高效卷积神经网络》: https://arxiv.org/pdf/1704.04861v1.pdf

  3. 探索论文《MobileNetV2:反向残差与线性瓶颈》: https://arxiv.org/pdf/1801.04381v4.pdf

  4. 探索论文《寻找 MobileNetV3》:https://arxiv.org/pdf/1905.02244v5.pdf

六、人脸识别和手势识别

谁能正确地看到人脸:摄影师、镜子还是画家?—毕加索

这一章延续了巴勃罗·毕加索的思想。我们人类对自己的脸和他人的脸、我们的微笑、我们的情绪、我们做出的不同姿势以及我们的不同表情很感兴趣。我们的手机和相机捕捉到了这一切。当我们认出一个朋友时,我们认出了这张脸——它的形状、眼睛、面部特征。有趣的是,即使我们从侧面看同一张脸,我们也能认出它。

令人惊讶的是,我们人类能够察觉到这张脸,即使我们长时间看着它。我们创造了一张脸的属性的心理位置,我们能够很容易地回忆起它。同时,我们用手做出的手势很容易被识别。深度学习能够帮助重建这种能力。人脸识别的使用非常具有创新性——它可以跨安全、监控、自动化和客户体验等领域使用——使用案例很多。这个领域正在进行大量的研究。

本章试图将同样的魔法传授给算法。

在本章中,我们将学习以下主题:

  1. 人脸识别

  2. 人脸识别过程

  3. 深层建筑

  4. FaceNet 架构

  5. 人脸识别的 Python 实现

  6. 使用 OpenCV 进行手势识别

所以,我们继续讨论吧!

6.1 技术工具包

本章的代码和数据集上传到本书的 GitHub 链接 https://github.com/Apress/computer-vision-using-deep-learning/tree/main/Chapter6 。对于这一章,GPU 足以执行代码,你可以使用谷歌合作实验室。我们将使用 Python Jupyter 笔记本。

让我们在下一部分继续深入学习架构。

6.2 人脸识别

人脸识别并不是什么新鲜事。我们生来就有辨别和识别面孔的能力。对我们来说,这是一项微不足道的任务。我们可以在任何背景下认出我们认识的人,不同的灯光,头发的颜色,带帽子或太阳镜,等等。即使一个人变老了或者长了胡子,我们也能认出他们。太神奇了!

现在,我们试图训练深度学习算法来实现同样的壮举。对我们来说如此琐碎和不费力的任务对机器来说并不容易。在图 6-1 中,我们有一张脸,然后我们检测一张脸,然后识别一张脸。

深度学习与计算机视觉教程(二)

图 6-1

我们最初有一张脸。在第二张图片中,检测到一张脸,最后我们能够识别出一张有特定名字的脸

回想一下,在上一章中,我们学习了对象检测。我们可以将人脸识别视为对象检测的特例。我们不是在发现汽车和猫,而是在识别人。但问题更简单;我们只有要检测的对象类别——“面部”但是人脸检测不是最终状态。我们还得给这张脸起个名字,这可不是小事。而且,脸可以是任何角度;一张脸可以有不同的背景。所以,这不是一件容易的事。此外,我们可能会在照片或视频中发现面孔。深度学习算法可以帮助我们开发这样的能力。基于深度学习的算法可以利用计算能力、高级数学基础和数百万个数据点或人脸来训练更好的人脸识别模型。

在我们深入研究人脸识别的概念和实现之前,我们将探索这项功能的各种用例。

6.2.1 人脸识别的应用

人脸识别是一项非常激动人心的技术,可以跨领域和跨过程应用。一些主要用途是

  1. 安全管理:人脸识别解决方案适用于在线和离线安全系统。安全服务、警察部门和秘密服务利用基于机器学习的面部识别技术来追踪反社会元素。护照验证可以更快地进行,并且更加可靠。许多国家都有罪犯照片数据库,作为追踪罪犯的起点。这项技术确实节省了大量的时间和精力,并使研究人员能够将精力集中在其他领域。

  2. 身份验证是使用人脸识别技术的另一个重要领域。身份验证最著名的例子之一就是智能手机。Face ID 用于 iphone 和手机解锁。在线渠道和社交媒体正在使用人脸识别来检查试图访问帐户的人的身份。

  3. 零售商使用它来了解历史记录不太好的个人何时进入了商店。当商店扒手、罪犯或诈骗犯进入商店时,他们会构成威胁。零售商可以识别它们,并立即采取措施防止任何犯罪。

  4. 如果企业知道顾客的年龄、性别和面部表情,营销就会变得更加敏锐。可以安装巨型屏幕(事实上已经这样做了)来识别目标观众。

  5. 当分析消费者-产品交互时,消费者体验得到改善。人们触摸或尝试产品时的表情捕捉到了真实世界的互动。这些数据是产品团队对产品特性进行必要修改的金矿。同时,运营和店内团队可以让整体购物体验更加愉悦和有趣。

  6. 进入办公室、机场、大楼、仓库和停车场可以自动化,无需人工干预。安全摄像头拍下照片,并与数据库进行比较,以确保真实性。

前面讨论的用例只是人脸识别功能的许多应用中的一部分。因此解决方案大致为人脸认证或人脸验证或人脸识别。许多组织和国家正在创建庞大的员工/个人数据库,并投资进一步提高技能。

我们现在将着手研究面部识别的过程。它可以分为一步一步的过程,我们将在下一节讨论。

6.2.2 人脸识别过程

我们点击手机和相机中的图片。这些照片拍摄于各种场合——结婚、毕业、旅行、度假、会议等等。当我们在社交媒体上上传图片时,它会自动检测人脸,并识别出这个人是谁。一种算法在后台工作,并发挥神奇的作用。该算法不仅能够检测到人脸,还能从背景中的所有其他人脸中为其命名。我们在这一节研究类似的过程。

大致来说,我们可以围绕人脸识别进行这四个步骤,如图 6-2 所示。

深度学习与计算机视觉教程(二)

图 6-2

人脸检测的流程——从检测到识别。我们检测人脸,进行比对,提取特征,最后识别人脸

  1. 人脸检测简单来说就是定位一张照片中是否有一张或多张人脸。我们将围绕它创建一个边界框。回想一下在第一章中,我们使用 OpenCV 做了同样的事情。如图 6-1 所示,我们在照片中检测到了人脸的存在。

  2. 一旦我们检测到面部,我们归一化面部的属性,如大小和几何形状。这样做是为了与我们的面部数据库相匹配。我们还减少了光照、头部运动等的影响。

  3. 接下来,我们从面部提取特征。一些有区别的特征是眼睛、眉毛、鼻孔、嘴角等等。

  4. 然后我们执行人脸识别。这意味着我们把这张脸和数据库里现有的匹配起来。我们可能会执行以下两项中的一项:

    1. 用已知的身份验证给定的人脸。简单来说,我们想知道“这是 X 先生吗?”。这是一对一的关系。

    2. 或者我们可能想知道“这个人是谁?,“而在这样的情况下我们会有一对多的关系。

因此,这个问题看起来像一个监督学习分类问题。

在第一章中,我们使用 OpenCV 创建了一个人脸检测解决方案。在那里,我们简单地识别是否存在人脸。人脸识别就是给那张脸起一个名字。需要注意的是,如果没有具体的人脸检测,人脸识别的尝试将是徒劳的。毕竟,首先我们应该知道一张脸是否存在,然后只有我们才能给这张脸命名。换句话说,首先要进行检测,然后指定一个名称。如果照片中有不止一个人,我们将为照片中检测到的所有人指定名字。

这就是人脸识别的整个过程。我们现在将研究用于实现相同功能的深度学习解决方案。

人脸识别的 6.2.2.1 深度学习模式

深度学习也让人脸识别感受到它的存在。回想一下,人脸识别类似于任何其他图像分类解决方案。但是人脸和特征属性使得人脸识别和检测变得非常特殊。

我们也可以使用标准的卷积神经网络进行人脸识别。网络各层的行为和处理数据的方式与任何其他图像分析问题类似。

有太多的解决方案可用,但最著名的是作为深度学习算法的 DeepFace、VGGFace、DeepID 和 FaceNet。我们将在本章中深入研究 DeepFace 和 FaceNet,并使用它们创建 Python 解决方案。

我们现在将检查 DeepFace 架构。

6.2.3 脸书的 DeepFace 解决方案

DeepFace 是由脸书人工智能研究所(FAIR)的研究人员在 2014 年提出的。实际论文可在 www.cs.toronto.edu/~ranzato/publications/taigman_cvpr14.pdf 访问。

图 6-3 显示了 DeepFace 的实际架构,摘自之前提到的同一篇论文。

深度学习与计算机视觉教程(二)

图 6-3

这里展示了 DeepFace 架构。该图摘自 https://www.cs.toronto.edu/~ranzato/publications/taigman_cvpr14.pdf 的原始论文

在前面显示的体系结构中,我们可以分析网络的各个层和流程。DeepFace 期望输入图像是 152×152 的 3D 对齐的 RGB 图像。我们现在将详细探讨 3D 对齐的概念。

对准的目的是从输入图像生成正面。完整的过程如图 6-4 所示,取自同一张纸。

在第一步中,我们使用六个基准点来检测人脸。这六个基准点是两只眼睛、鼻尖和嘴唇上的三个点。在图 6-4 中,在步骤(a)中描述。该步骤检测图像中的面部。

深度学习与计算机视觉教程(二)

图 6-4

DeepFace 中使用的人脸对齐过程。图像取自原始文件。我们应该注意人脸是如何逐步分析的

在第二步中,如步骤(b)所示,我们从原始图像中裁剪并生成 2D 人脸。我们应该注意到,在这一步中,人脸是如何从原始图像中裁剪出来的。

在接下来的步骤中,三角形被添加到轮廓上以避免不连续。我们在 2D 对齐的作物上应用 67 个基准点及其相应的 Delaunay 三角测量。使用 2D 到 3D 生成器生成 3D 模型,并绘制 67 个点。它还允许我们对齐平面外旋转。步骤(e)示出了相对于装配的 2D-3D 相机的可见性,并且在步骤(f)中,我们可以观察由 3D 模型引起的 67 个基准点,这些基准点用于引导分段仿射包裹。

我们现在将简要讨论 Delaunay 三角剖分。对于平面上给定的离散点集“P ”,在三角剖分 DT 中,没有一个点在 Delaunay 三角剖分中任何三角形的外接圆内。因此,它最大化了三角测量中所有三角形的最小角度。我们在图 6-5 中展示了这一现象。

深度学习与计算机视觉教程(二)

图 6-5

Delaunay 三角法。影像来源:common。维基媒体。页:1。PHP?PHP?curid= 18929097

最后,我们做最后的临街作物。这是实现三维临街化目标的最后一步。

3D 临街化步骤完成后,图像就可以用于网络中的后续步骤。152×152 大小的图像是输入图像,它被馈送到下一层。

下一层是卷积层(C1 ),有 32 个大小为 11x11x3 的过滤器,后面是跨度为 2 的 3×3 最大池层。然后下一层是另一个卷积,16 个滤镜,大小 9x9x16。

深度学习与计算机视觉教程(二)

图 6-6

这里显示了完整的 DeepFace 架构。图片来自原始论文。我们可以观察到,在正面化完成后,下一步是卷积过程

然后我们有三个局部相连的层。我们将简要讨论局部连接的层,因为它们与完全连接的层有些不同。

局部连接的层与完全连接的层的行为不同。对于完全连接的层,第一层的每个神经元都连接到下一层。对于本地连接的图层,我们在不同的要素地图中有不同类型的过滤器。例如,当我们分类图像是否是人脸时,我们可以只在图像的底部搜索嘴。因此,如果我们知道一个特征应该被限制在一个小空间内,并且没有必要在整个图像中搜索该特征,那么局部连接的层是很方便的。

在 DeepFace 中,我们有局部连接的层,因此我们可以改进模型,因为我们可以基于不同类型的特征地图来区分面部区域。

倒数第二层是一个完全连接的层,用于面表示。最后一层是 softmax 全连接层,用于分类。

参数总数为 1.2 亿。断开被用作一种正则化技术,但仅用于最终完全连接的图层。我们还归一化 0 和 1 之间的特征,并进行 L2 归一化。该网络在训练期间生成相当稀疏的特征映射,主要是因为 ReLU 已经被用作激活函数。

验证是在 LFW(标记为野生人脸)数据集和 SFC 数据集上进行的。LFW 包含了 5700 多位名人的 13000 多张网络图片。SFC 是脸书自己的数据集,有大约 440 万张 4030 人的图像,每个人有 800 到 1200 张面部图像。两个数据集的 ROC 曲线如图 6-7 所示。

深度学习与计算机视觉教程(二)

图 6-7

取自 https://www.cs.toronto.edu/~ranzato/publications/taigman_cvpr14.pdf 原始论文的 LFW 数据集和 YTF 数据集的 ROC 曲线

DeepFace 是一种新颖的人脸识别模式。它在 LFW 数据集上的准确率超过 99.5%。它能够解决背景中的姿势、表情或光线强度问题。3D 对齐是一种非常独特的方法,可进一步提高精确度。该架构在 LFW 和 YouTube 人脸数据集(YTF)上表现非常好。

我们现在已经讨论完了 DeepFace 架构。我们现在将讨论称为 FaceNet 的下一个架构。

6.2.4 用于人脸识别的 FaceNet

在上一节中,我们学习了 DeepFace。现在我们正在研究第二种架构,叫做 FaceNet。它是由谷歌研究人员弗洛里安·施罗夫、德米特里·卡列尼琴科和詹姆斯·菲尔宾在 2015 年提出的。原论文为“FaceNet:人脸识别和聚类的统一嵌入”,可在 https://arxiv.org/abs/1503.03832 访问。

FaceNet 不推荐一套全新的算法或复杂的数学计算来执行面部识别任务。这个概念相当简单。

所有的人脸图像首先被表示在欧几里得空间中。然后我们通过计算各自的距离来计算人脸之间的相似度。考虑一下,如果我们有一个图像,X 先生的图像 1 ,那么 X 先生的所有图像或面部将更接近于图像 1 而不是 y 先生的图像 2 ,概念如图 6-8 所示。

深度学习与计算机视觉教程(二)

图 6-8

爱因斯坦的图像将彼此相似,因此它们之间的距离将更小,而甘地的图像将在远处

前面的概念更容易理解。我们现在将详细了解该架构。如图 6-9 所示,我们可以检查完整的架构。图像取自原始文件本身。

深度学习与计算机视觉教程(二)

图 6-9

FaceNet 架构。图片取自 https://arxiv.org/abs/1503.03832 的原纸

网络从图像的批量输入层开始。然后是深度 CNN 架构。该网络利用类似 ZFNet 或 Inception network 的架构。我们将在本书的下一章讨论盗梦空间网络。

FaceNet 实现 1×1 卷积来减少参数的数量。1×1 将在下一章中再次详细讨论。这些深度学习模型的输出是图像的嵌入。对输出执行 L2 归一化。这些嵌入是非常有用的补充。FaceNet 从面部图像中理解各自的映射,然后创建嵌入。

一旦嵌入成功完成,我们就可以简单地继续进行,并且在新创建的嵌入作为特征向量的帮助下,我们可以使用任何标准的机器学习技术。嵌入的使用是 FaceNet 和其他方法之间的主要区别,因为其他解决方案通常实现面部验证的定制层。

然后,创建的嵌入被馈送以计算损耗。如前所述,图像在欧几里得空间中表示。损失函数旨在使相似图像的两个图像嵌入之间的平方距离较小,而不同图像之间的平方距离较大。换句话说,各个嵌入之间的平方距离将决定人脸之间的相似性。

FaceNet 中实现了一个重要的概念—三重态丢失函数。

三重态损耗如图 6-10 所示;图像取自原始文件本身。

深度学习与计算机视觉教程(二)

图 6-10

在 FaceNet 中使用的三重损失。图片取自 https://arxiv.org/abs/1503.03832 的原纸

三重态损耗基于我们在 FaceNet 讨论开始时在图 6-8 中讨论的概念。直觉是,我们希望同一个人 X 先生的图像彼此更接近。让我们称那个图像 1 为锚图像。X 先生的所有其他形象称为正面形象。Y 先生的图像被称为负像。

因此,根据三重损失,我们希望锚图像和正图像的嵌入之间的距离小于锚图像和负图像的嵌入之间的距离。

我们就想实现方程 6-1:

深度学习与计算机视觉教程(二))

(方程 6-1)

在哪里

主播形象是 xI?? a。

正象是 xIp。

负像是 xIn,所以基本上 x i 就是一个像。

⍺是正负对之间的一个边界。这是我们设置的阈值,它表示相应图像对之间的差异。

t 是训练集中所有可能三元组的集合,基数为 n。

数学上,三重态损耗可以表示为等式 6-2。这是我们希望尽量减少的损失。

深度学习与计算机视觉教程(二))

(方程式 6-2)

在前面的等式中,图像的嵌入由 f(x)表示,使得 x ∈ℝ.它将图像 x 嵌入到 d 维欧氏空间中。f(x i )是以大小为 128 的向量的形式嵌入的图像。

解决方案取决于图像对的选择。可能存在网络能够通过的图像对。换句话说,他们会满足损失的条件。这些图像对可能不会给学习增加太多,也可能导致收敛缓慢。

为了更好的结果和更快的收敛,我们应该选择不符合方程 6-1 中条件的三元组。

数学上,对于锚图像 x i a ,我们想要选择正图像 xIp 使得相似性最大,并且选择负图像 xIn 使得相似性最小。换句话说,我们希望有 arg maxxip| | f(xIa)–f(xIp)|22这意味着给定一个锚图像 xIa 我们希望有一个正图像 xI??

同样,对于给定的锚图像 x i a ,我们希望得到一个负图像 x i n 使得距离最小,表示为 arg minXin| | f(xIa)-f(xIn)|22

现在,做出这个选择并不是一件容易的事情。在训练期间,确保根据之前在小批量中给出的最大和最小函数选择阳性和阴性。使用带有 Adagrad 的 SGD(随机梯度下降)进行训练。已经使用的两个网络如下所示(ZF 网络和 Inception)。ZF 网络中有 1.4 亿个参数,初始阶段有 750 万个参数。

深度学习与计算机视觉教程(二)

深度学习与计算机视觉教程(二)

使用前 100 帧,该模型表现非常好,准确率为 95.12%,标准误差为 0.39。

在 LFW 数据集上,引用论文中的话:

我们的模型有两种评估模式:1 .固定中心作物的 LFW 提供。2.在提供的 LFW 缩略图上运行专有的人脸检测器(类似于 Picasa [3])。如果未能对齐面部(两幅图像都会出现这种情况),则使用 LFW 对齐。

当使用(1)中描述的固定中心裁剪时,我们实现了 98.87%±0.15 的分类准确度,并且当使用额外的面部对齐时,我们实现了破纪录的 99.63%±0.09 的平均值标准误差(2)。

FaceNet 是一种新颖的解决方案,因为它直接学习嵌入到欧几里德空间中用于人脸验证。该模型足够健壮,不受姿势、光照、遮挡或人脸年龄的影响。

我们现在将看看使用 Python 实现 FaceNet。

6.2.5 使用 FaceNet 的 Python 实现

本节中的代码是不言自明的。我们使用预训练的面网模型及其权重,并计算欧几里德距离来测量两张脸之间的相似性。我们使用的是 Sefik Ilkin Serengil 从 https://drive.google.com/file/d/1971Xk5RwedbudGgTIrGAL4F7Aifu7id1/view 公开发布的 facenet_weights。模型从 Tensorflow 转换到 Keras。基本型号可在 https://github.com/davidsandberg/facenet 找到。

步骤 1:加载库。

from keras.models import model_from_json
from inception_resnet_v1 import *
import numpy as np

from keras.models import Sequential
from keras.models import load_model
from keras.models import model_from_json
from keras.layers.core import Dense, Activation
from keras.utils import np_utils

from keras.preprocessing.image import load_img, save_img, img_to_array
from keras.applications.imagenet_utils import preprocess_input

import matplotlib.pyplot as plt
from keras.preprocessing import image

深度学习与计算机视觉教程(二)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

步骤 2:现在加载模型。

face_model = InceptionResNetV1()
face_model.load_weights('facenet_weights.h5')

  • 1
  • 2
  • 3

步骤 3:我们现在将定义三个函数——标准化数据集、计算欧几里德距离和预处理数据集。

def normalize(x):
    return x / np.sqrt(np.sum(np.multiply(x, x)))

def getEuclideanDistance(source, validate):
    euclidean_dist = source - validate
    euclidean_dist = np.sum(np.multiply(euclidean_dist, euclidean_dist))
    euclidean_dist = np.sqrt(euclidean_dist)
    return euclidean_dist

def preprocess_data(image_path):
    image = load_img(image_path, target_size=(160, 160))
    image = img_to_array(image)
    image = np.expand_dims(image, axis=0)
    image = preprocess_input(image)
    return image

深度学习与计算机视觉教程(二)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

第四步:现在我们将计算两幅图像的相似度。

在这里,我们拍摄了这两张著名板球明星的照片——Sachin Tendulkar。这两张图片取自互联网。

img1_representation = normalize(face_model.predict(preprocess_data('image_1.jpeg'))[0,:])
img2_representation = normalize(face_model.predict(preprocess_data('image_2.jpeg'))[0,:])

euclidean_distance = getEuclideanDistance(img1_representation, img2_representation)

  • 1
  • 2
  • 3
  • 4
  • 5

深度学习与计算机视觉教程(二)

欧几里德距离相似度是 0.70。我们还可以实现余弦相似性来测试两幅图像之间的相似性。

在下一节中,我们将使用 OpenCV 实现一个手势识别解决方案。

6.2.6 手势识别的 Python 解决方案

手势识别是帮助人类与系统对话的最具创新性的解决方案之一。手势识别意味着系统可以捕捉手势或面部手势,并且系统可以采取相应的动作。它由检测、跟踪和识别等关键部件组成。

  1. 在检测中,提取视觉部分,如手或手指或身体部分。视觉部分应该在摄像机的视野范围内。

  2. 然后我们追踪视觉部分。它确保逐帧捕捉和分析数据。

  3. 最后,我们识别一个或一组手势。基于我们已经完成的算法设置、使用的训练数据,系统将能够识别已经做出的手势的类型。

手势识别是一种颇具开创性的解决方案,可用于自动化、医疗设备、增强现实、虚拟现实、游戏等领域。用例很多,目前在这个领域正在进行大量的研究。

在本书中,我们将使用 OpenCV 实现一个手指计数解决方案。解决方案视频可在 www.linkedin.com/posts/vaibhavverdhan_counting-number-of-fingers-activity-6409176532576722944-Ln-R/ 访问。

步骤 1:在这里导入所有的库。

#  import all the necessary libraries
import cv2
import imutils
import numpy as np
from sklearn.metrics import pairwise

# global variables
bg = None

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

步骤 2:我们现在将编写一个函数来查找背景上的移动平均值。

#--------------------------------------------------------------
def run_avg(image, accumWeight):
    global bg
    # initialize the background
    if bg is None:
        bg = image.copy().astype("float")
        return

    # compute weighted average, accumulate it and update the background
    cv2.accumulateWeighted(image, bg, accumWeight)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

第三步:在这一步中,分割功能开始分割图像中的手部区域。

def segment(image, threshold=25):
    global bg
    # find the absolute difference between background and current frame
    diff = cv2.absdiff(bg.astype("uint8"), image)

    # threshold the diff image so that we get the foreground
    thresholded = cv2.threshold(diff, threshold, 255, cv2.THRESH_BINARY)[1]

    # get the contours in the thresholded image
    (_, cnts, _) = cv2.findContours(thresholded.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    # return None, if no contours detected
    if len(cnts) == 0:
        return
    else:
        # based on contour area, get the maximum contour which is the hand
        segmented = max(cnts, key=cv2.contourArea)
        return (thresholded, segmented)

深度学习与计算机视觉教程(二)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

第四步:这个代码用于计算手指的数量。

from sklearn.metrics import pairwise
def count(thresholded, segmented):
     # find the convex hull of the segmented hand region
     chull = cv2.convexHull(segmented)

     # find the most extreme points in the convex hull
     extreme_top    = tuple(chull[chull[:, :, 1].argmin()][0])
     extreme_bottom = tuple(chull[chull[:, :, 1].argmax()][0])
     extreme_left   = tuple(chull[chull[:, :, 0].argmin()][0])
     extreme_right  = tuple(chull[chull[:, :, 0].argmax()][0])

     # find the center of the palm
     cX = int((extreme_left[0] + extreme_right[0]) / 2)
     cY = int((extreme_top[1] + extreme_bottom[1]) / 2)

     # find the maximum euclidean distance between the center of the palm
     # and the most extreme points of the convex hull
     distance = pairwise.euclidean_distances([(cX, cY)], Y=[extreme_left, extreme_right, extreme_top, extreme_bottom])[0]
     maximum_distance = distance[distance.argmax()]

     # calculate the radius of the circle with 80% of the max euclidean distance obtained
     radius = int(0.8 * maximum_distance)

     # find the circumference of the circle
     circumference = (2 * np.pi * radius)

     # take out the circular region of interest which has
     # the palm and the fingers
     circular_roi = np.zeros(thresholded.shape[:2], dtype="uint8")

     # draw the circular ROI
     cv2.circle(circular_roi, (cX, cY), radius, 255, 1)

     # take bit-wise AND between thresholded hand using the circular ROI as the mask
     # which gives the cuts obtained using mask on the thresholded hand image
     circular_roi = cv2.bitwise_and(thresholded, thresholded, mask=circular_roi)

     # compute the contours in the circular ROI
     (_, cnts, _) = cv2.findContours(circular_roi.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

     # initalize the finger count
     count = 0

     # loop through the contours found
     for c in cnts:
          # compute the bounding box of the contour
          (x, y, w, h) = cv2.boundingRect(c)

          # increment the count of fingers only if -
          # 1. The contour region is not the wrist (bottom area)
          # 2. The number of points along the contour does not exceed
          #     20% of the circumference of the circular ROI
          if ((cY + (cY * 0.20)) > (y + h)) and ((circumference * 0.20) > c.shape[0]):
               count += 1

     return count

深度学习与计算机视觉教程(二)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

第五步:主要功能如下:

#--------------------------------------------------------------
# Main function
#--------------------------------------------------------------
if __name__ == "__main__":
    # initialize accumulated weight
    accumWeight = 0.5

    # get the reference to the webcam
    camera = cv2.VideoCapture(0)

    # region of interest (ROI) coordinates
    top, right, bottom, left = 20, 450, 325, 690

    # initialize num of frames
    num_frames = 0

    # calibration indicator
    calibrated = False

    # keep looping, until interrupted
    while(True):
        # get the current frame
        (grabbed, frame) = camera.read()

        # resize the frame
        frame = imutils.resize(frame, width=700)

        # flip the frame so that it is not the mirror view
        frame = cv2.flip(frame, 1)

        # clone the frame
        clone = frame.copy()

        # get the height and width of the frame
        (height, width) = frame.shape[:2]

        # get the ROI
        roi = frame[top:bottom, right:left]

        # convert the roi to grayscale and blur it
        gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
        gray = cv2.GaussianBlur(gray, (7, 7), 0)

        # to get the background, keep looking till a threshold is reached
        # so that our weighted average model gets calibrated
        if num_frames < 30:
            run_avg(gray, accumWeight)
            if num_frames == 1:
                 print ("Calibration is in progress...")
            elif num_frames == 29:
                print ("Calibration is successful...")
        else:
            # segment the hand region
            hand = segment(gray)

            # check whether hand region is segmented
            if hand is not None:
                # if yes, unpack the thresholded image and
                # segmented region
                (thresholded, segmented) = hand

                # draw the segmented region and display the frame
                cv2.drawContours(clone, [segmented + (right, top)], -1, (0, 0, 255))

                # count the number of fingers
                fingers = count(thresholded, segmented)

                cv2.putText(clone, str(fingers), (70, 45), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2)

                # show the thresholded image
                cv2.imshow("Thesholded", thresholded)

        # draw the segmented hand
        cv2.rectangle(clone, (left, top), (right, bottom), (0,255,0), 2)

        # increment the number of frames
        num_frames += 1

        # display the frame with segmented hand
        cv2.imshow("Video Feed", clone)

        # observe the keypress by the user
        keypress = cv2.waitKey(1) & 0xFF

        # if the user pressed "q", then stop looping
        if keypress == ord("q"):
            break

深度学习与计算机视觉教程(二)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

第六步:现在释放内存。

# free up memory
camera.release()
cv2.destroyAllWindows()

  • 1
  • 2
  • 3
  • 4

该代码的输出将是一个现场视频-一些截图如下所示。

深度学习与计算机视觉教程(二)

据此,我们利用深度学习和 OpenCV 创建了人脸识别和手势识别技术。我们现在开始对本章进行总结。

6.3 总结

人脸检测和识别是一个有趣的领域。面部属性相当独特。对人类来说很容易的能力很难教给机器。我们已经在本章中详细讨论了检测人脸的许多用途。它们在许多领域都有应用,并且非常容易理解。深度学习正在帮助我们实现它。路还很长,我们要在这段旅程中提高很多。有了更好的机器和复杂的算法,进步总是有可能的。

同时,捕获的面部数据集必须是干净的、有代表性的和完整的。如果图像中有大量的背景噪声,或者图像模糊或者有任何其他缺陷,那么训练网络将会非常困难。

面部和手势识别还可以有其他扩展。年龄检测、性别检测和情绪检测是一些已经在组织和研究机构中进行和开发的技术。

在这一章中,我们研究了人脸识别方法和深度学习架构。在本章中,我们研究了 DeepFace 和 FaceNet,并使用预先训练好的网络创建了一个 Python 解决方案。我们还使用 OpenCV 创建了一个手势识别解决方案。

下一章涉及计算机视觉的另一个有趣领域——视频分析。在下一章中,我们还将研究 ResNet 和初始网络这两种最新的算法。所以,敬请期待!

你现在可以开始提问了。

Review Exercises

  1. 人脸识别系统中的各种过程是什么?

  2. 面部对齐是什么概念?

  3. 三重态损失是什么概念?

  4. 手势识别的各种用例有哪些?

  5. www.kaggle.com/kpvisionlab/tufts-face-database 下载塔夫茨数据集,并通过对其进行处理来开发人脸识别系统。

  6. https://research.google/tools/datasets/google-facial-expression/ 下载谷歌面部表情对比数据集,开发一个分析面部表情的系统。

  7. http://vis-www.cs.umass.edu/lfw/ 下载野生数据集中的标记人脸,使用 FaceNet 和 DeepFace 创建人脸验证解决方案。

  8. www.kaggle.com/selfishgene/youtube-faces-with-facial-keypoints 下载带有面部关键点的 YouTube 人脸数据集,并使用它来识别无约束视频中的人脸。如果需要,视频分析的概念可以在下一章学习。

进一步阅读

  1. https://arxiv.org/pdf/1812.00408v3.pdf 浏览论文《大规模多模态人脸反欺骗数据集及基准》。

  2. 浏览 https://arxiv.org/pdf/1904.09658.pdf 的论文《概率人脸嵌入》。

  3. https://arxiv.org/pdf/1710.08092v2.pdf 浏览论文《VGGFace2:跨姿势跨年龄人脸识别数据集》。

  4. https://arxiv.org/pdf/1807.11649v1.pdf 浏览论文《人脸识别的魔鬼就在噪音中》。

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...