卷积神经网络 (CNN) 与 TensorFlow 教程Convolutional Neural Networks (CNN) with TensorFlow Tutorial

想象一下在动物园里试图识别给定的动物是猎豹还是豹子。作为人类,您的大脑可以毫不费力地分析身体和面部特征以得出有效结论。同样,无论模式多么复杂,都可以训练卷积神经网络 (CNN) 执行相同的识别任务。这使得它们在计算机视觉领域非常强大。 

这个概念性的 CNN 教程将从概述什么是 CNN 及其在机器学习中的重要性开始。然后它将引导您在 TensorFlow Framework 2 中逐步实施 CNN。

什么是 CNN?

卷积神经网络(CNN 或 ConvNet)是一种深度学习算法,专为对象识别至关重要的任何任务而设计,例如图像分类、检测和分割。许多现实生活中的应用程序,例如自动驾驶汽车、监控摄像头等,都使用了 CNN。 

CNN 的重要性

这些是 CNN 重要的几个原因,如下所示:

  • 与需要手动提取特征的 SVM 和决策树等传统机器学习模型不同,CNN 可以大规模执行自动特征提取,从而提高效率。 
  • 卷积层使 CNN 具有平移不变性,这意味着它们可以从数据中识别模式并提取特征,而不管它们的位置如何,无论图像是旋转、缩放还是移动。
  • VGG-16、ResNet50、Inceptionv3 和 EfficientNet 等多个预训练的 CNN 模型被证明已达到最先进的结果,并且可以使用相对少量的数据在新闻任务上进行微调。 
  • CNN 还可以用于非图像分类问题,并且不限于自然语言处理、时间序列分析和语音识别。

CNN 的架构

CNN 的架构试图模仿由多层组成的人类视觉系统中的神经元结构,其中每一层都负责检测数据中的特定特征。如下图所示,典型的 CNN 由四个主要层组合而成: 

  • 卷积层
  • 整流线性单元(简称ReLU)
  • 池化层
  • 全连接层 

让我们使用以下手写数字分类示例来了解这些层中的每一层是如何工作的。

卷积层

这是 CNN 的第一个构建块。顾名思义,执行的主要数学任务称为卷积,它是将滑动窗口函数应用于表示图像的像素矩阵。应用于矩阵的滑动函数称为内核或过滤器,两者可以互换使用。 

在卷积层中,应用了几个大小相等的过滤器,每个过滤器用于从图像中识别特定的图案,例如数字的弯曲、边缘、数字的整体形状等。 

让我们考虑一下手写数字的这个 32x32 灰度图像。给出矩阵中的值用于说明目的。

另外,让我们考虑一下用于卷积的内核。它是一个维度为 3x3 的矩阵。内核的每个元素的权重在网格中表示。黑色网格表示零权重,白色网格表示零权重。 

我们必须手动找到这些权重吗? 

在现实生活中,内核的权重是在神经网络的训练过程中确定的。 

使用这两个矩阵,我们可以通过应用点积来执行卷积运算,并且工作如下: 

  1. 从左上角向右应用核矩阵。
  2. 执行逐元素乘法。  
  3. 对产品的价值求和。
  4. 结果值对应于卷积矩阵中的第一个值(左上角)。
  5. 根据滑动窗口的大小向下移动内核。
  6. 重复步骤 1 到 5,直到图像矩阵被完全覆盖。

卷积矩阵的维数取决于滑动窗口的大小。滑动窗口越高,尺寸越小。

文献中与内核相关的另一个名称是特征检测器,因为可以微调权重以检测输入图像中的特定特征。 

例如: 

  • 平均相邻像素内核可用于模糊输入图像。
  • 减去相邻内核用于执行边缘检测。

网络的卷积层越多,该层在检测更抽象的特征方面就越好。 

激活函数

ReLU 激活函数在每个卷积操作之后应用。此功能有助于网络学习图像中特征之间的非线性关系,从而使网络在识别不同模式方面更加稳健。它还有助于缓解梯度消失问题。 

池化层

池化层的目标是从卷积矩阵中提取最重要的特征。这是通过应用一些聚合操作来完成的,这些操作减少了特征映射(卷积矩阵)的维度,从而减少了训练网络时使用的内存。池化也与减轻过度拟合有关。

可以应用的最常见的聚合函数是: 

  • 最大池化,即特征图的最大值
  • sum pooling对应于feature map所有值的总和
  • 平均池是所有值的平均值。 

下面是前面每个示例的说明: 

此外,特征图的维度随着轮询功能的应用而变小。 

最后一个池化层将其特征图展平,以便它可以由全连接层处理。 

全连接层

这些层位于卷积神经网络的最后一层,它们的输入对应于最后一个池化层生成的扁平化一维矩阵。ReLU 激活函数应用于它们以实现非线性。 

最后,使用 softmax 预测层为每个可能的输出标签生成概率值,最终预测的标签是概率得分最高的标签。 

辍学 

Dropout 是一种正则化技术,用于提高具有大量参数的神经网络的泛化能力。它包括在训练过程中随机丢弃一些神经元,这会迫使剩余的神经元从输入数据中学习新特征。  

由于技术实现将使用 TensorFlow 2 执行,下一节旨在提供该框架不同组件的完整概述,以有效地构建深度学习模型。 

什么是 TensorFlow 框架

谷歌于 2015 年 11 月开发了 TensorFlow。出于多种原因,他们将其定义为面向所有人的开源机器学习框架。 

  • 开源:在 Apache 2.0 开源许可下发布。这使得研究人员、组织和开发人员可以不受任何限制地在图书馆的基础上进行建设,从而为图书馆做出贡献。 
  • 机器学习框架:意味着它有一组支持机器学习模型构建过程的库和工具。
  • 对于所有人:使用 TensorFlow 可以更轻松地通过 Python 等通用编程语言实现机器学习模型。此外,Keras 等内置库使创建强大的深度学习模型变得更加容易。 

所有这些功能使 Tensorflow 成为构建神经网络的理想选择。 

此外,安装 Tensorflow 2 非常简单,可以使用 Python 包管理器pip按如下方式执行,如官方文档中所述。 

安装完成后可以看到使用的版本是2.9.1



import tensorflow as tf print("TensorFlow version:", tf.__version__)

现在,让我们进一步探讨创建这些网络的主要组件。 

张量

我们在构建机器学习和深度学习模型时主要处理高维数据。张量是具有统一类型的多维数组,用于表示数据的不同特征。 

下面是张量不同类型维度的图形表示。 

  • 0 维张量包含单个值。
  • 一维张量,也称为“rank-1”张量,是值列表。 
  • 二维张量是“rank-2”张量。
  • 最后,我们可以得到一个 N 维张量,其中 N 表示张量内的维数。在前面的例子中,N 分别为 0、1 和 2。

下面是零到 3 维张量的图示。每个张量都是使用 TensorFlow 中的 constant() 函数创建的。 



# Zero dimensional tensor zero_dim_tensor = tf.constant(20) print(zero_dim_tensor) # One dimensional tensor one_dim_tensor = tf.constant([12, 20, 53, 26, 11, 56]) print(one_dim_tensor) # Two dimensional tensor two_dim_array = [[3, 6, 7, 5], [9, 2, 3, 4], [7, 1, 10,6], [0, 8, 11,2]] two_dim_tensor = tf.constant(two_dim_array) print(two_dim_tensor)

成功执行前面的代码应该会生成下面的输出,我们可以注意到关键字“tf.Tensor”表示结果是张量。它具有三个参数:

  •  张量的实际值。
  • 张量的shape () ,对于第一张量、第二张量和第三张量分别为0、6乘1和4乘4。
  • dtype属性表示的数据类型,所有张量都是int32。

我们的TensorFlow 初学者教程提供了 TensorFlow 的完整概述,并教授如何构建和训练模型。

张量与矩阵:差异

许多人将张量与矩阵混淆。尽管这两个对象看起来很相似,但它们具有完全不同的属性。本节提供了对矩阵和张量之间差异的更好理解。

  • 我们可以将矩阵视为只有两个维度的张量。 
  • 另一方面,张量是一种更通用的格式,可以具有任意数量的维度。

与矩阵相反,张量更适合深度学习问题,原因如下: 

  • 它们可以处理任意数量的维度,这使得它们更适合多维数据。
  • 张量兼容各种数据类型、形状和维度的能力使它们比矩阵更通用。
  • Tensorflow 提供 GPU 和 TPU 支持以加速计算。使用张量,机器学习工程师可以自动利用这些优势。
  • 张量本身支持广播,它包括在不同形状的张量之间进行算术运算,这在处理矩阵时并不总是可行的。  

TensorFlow:常量、变量和占位符

常数不是张量的唯一类型。还有变量和占位符,它们都是计算图的构建块。 

计算图基本上是一系列操作和它们之间的数据流的表示。 

现在,让我们了解这些类型的张量之间的区别。

常量

常量是张量,其值在计算图的执行过程中不会改变。它们是使用tf.constant()函数创建的,主要用于存储在模型训练期间不需要任何更改的固定参数。

变量

变量是张量,其值可以在计算图的执行期间更改,它们是使用tf.Variable()函数创建的。例如,在神经网络的情况下,权重和偏差可以定义为变量,因为它们需要在训练过程中更新。 

占位符

这些在 Tensorflow 的第一个版本中用作没有特定值的空容器。它们只是用来反转一个点,以便将来使用数据。这使用户可以在模型训练和验证期间自由使用不同的数据集和批量大小。

在 Tensorflow 版本 2 中,占位符已被tf.function()函数取代,这是一种更加 Pythonic 和动态的方法,用于将数据馈送到计算图中。 

CNN 逐步实施

让我们将之前所学的一切付诸实践。本节将说明在 CIFAR-10 数据集上应用 TensorFlow 中的卷积神经网络的端到端实现,该数据集是具有以下属性的内置数据集: 

  • 它包含 60.000 个 32 x 32 彩色图像
  • 数据集有 10 个不同的类别
  • 每个类有 6000 张图像
  • 总共有 50.000 张训练图像 
  • 以及总共 10.000 张测试图像

文章的源代码可以在DataCamp 的工作区找到

网络架构

在进入技术实现之前,我们先来了解一下正在实现的网络的整体架构。 

  • 模型的输入是一个32x32x3的张量,分别为宽、高、通道。 
  • 我们将有两个卷积层。第一层应用 32 个大小为 3x3 的过滤器和一个 ReLU 激活函数。第二个应用 64 个大小为 3x3 的过滤器
  • 第一个池化层将应用 2x2 最大池化
  • 第二个池化层也将应用 2x2 最大池化
  • 全连接层将有 128 个单元和一个 ReLU 激活函数
  • 最后,输出将是对应于 10 个类的 10 个单元,激活函数是一个 softmax 来生成概率分布。

加载数据集

内置数据集从 keras.datasets() 加载,如下所示: 



(train_images, train_labels), (test_images, test_labels) = cf10.load_data()

探索性数据分析

在本节中,我们将只关注显示一些示例图像,因为我们已经知道每个类在训练和测试数据中的比例。 

辅助函数 show_images() 默认显示一共 12 张图像,并接受三个主要参数: 

  • 训练图像
  • 类名
  • 和训练标签。


import matplotlib.pyplot as plt def show_images(train_images, class_names, train_labels, nb_samples = 12, nb_row = 4): plt.figure(figsize=(12, 12)) for i in range(nb_samples): plt.subplot(nb_row, nb_row, i + 1) plt.xticks([]) plt.yticks([]) plt.grid(False) plt.imshow(train_images[i], cmap=plt.cm.binary) plt.xlabel(class_names[train_labels[i][0]]) plt.show()

现在,我们可以使用所需的参数调用该函数。 



class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck'] show_images(train_images, class_names, train_labels)

成功执行前面的代码会生成下面的图像。 

数据预处理

在训练模型之前,我们需要将数据的像素值归一化在同一范围内(例如 0 到 1)。这是处理图像时常见的预处理步骤,以确保尺度不变性,并在训练过程中更快收敛。 



max_pixel_value = 255 train_images = train_images / max_pixel_value test_images = test_images / max_pixel_value

此外,我们注意到标签以分类格式表示,如猫、马、鸟等。我们需要将它们转换成数字格式,以便神经网络可以轻松处理它们。



from tensorflow.keras.utils import to_categorical train_labels = to_categorical(train_labels, len(class_names)) test_labels = to_categorical(test_labels, len(class_names))

模型架构实现

下一步是根据前面的描述实现网络的架构。 

首先,我们使用Sequential()类定义模型,并使用add()函数将每一层添加到模型中。 



from tensorflow.keras import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense # Variables INPUT_SHAPE = (32, 32, 3) FILTER1_SIZE = 32 FILTER2_SIZE = 64 FILTER_SHAPE = (3, 3) POOL_SHAPE = (2, 2) FULLY_CONNECT_NUM = 128 NUM_CLASSES = len(class_names) # Model architecture implementation model = Sequential() model.add(Conv2D(FILTER1_SIZE, FILTER_SHAPE, activation='relu', input_shape=INPUT_SHAPE)) model.add(MaxPooling2D(POOL_SHAPE)) model.add(Conv2D(FILTER2_SIZE, FILTER_SHAPE, activation='relu')) model.add(MaxPooling2D(POOL_SHAPE)) model.add(Flatten()) model.add(Dense(FULLY_CONNECT_NUM, activation='relu')) model.add(Dense(NUM_CLASSES, activation='softmax'))

将 summary() 函数应用于模型后,我们对模型的架构进行了全面的总结,其中包含有关每一层、其类型、输出形状和可训练参数总数的信息。 

模型训练

所有资源最终都可用于配置和触发模型的训练。这是分别使用带有以下参数的 compile()fit()函数完成的:

  • 优化器负责更新模型的权重和偏差。在我们的例子中,我们使用的是 Adam 优化器。 
  • 损失函数用于衡量误分类错误,我们使用的是 Croentropy()。
  • 最后,指标用于衡量模型的性能,准确率、精确率和召回率将在我们的用例中显示。


from tensorflow.keras.metrics import Precision, Recall BATCH_SIZE = 32 EPOCHS = 30 METRICS = metrics=['accuracy', Precision(name='precision'), Recall(name='recall')] model.compile(optimizer='adam', loss='categorical_crossentropy', metrics = METRICS) # Train the model training_history = model.fit(train_images, train_labels, epochs=EPOCHS, batch_size=BATCH_SIZE, validation_data=(test_images, test_labels))

模型评估

模型训练后,我们可以通过使用 show_performance_curve( )辅助函数在二维中绘制上述指标来比较其在训练和测试数据集上的性能。 

  • 横轴(x)是epochs的数量
  • 垂直方向 (y) 是模型的基础性能。 
  • 曲线表示特定时期的指标值。

为了更好地可视化,通过训练和验证性能值与最佳值的交点绘制了一条垂直的红线。



def show_performance_curve(training_result, metric, metric_label): train_perf = training_result.history[str(metric)] validation_perf = training_result.history['val_'+str(metric)] intersection_idx = np.argwhere(np.isclose(train_perf, validation_perf, atol=1e-2)).flatten()[0] intersection_value = train_perf[intersection_idx] plt.plot(train_perf, label=metric_label) plt.plot(validation_perf, label = 'val_'+str(metric)) plt.axvline(x=intersection_idx, color='r', linestyle='--', label='Intersection') plt.annotate(f'Optimal Value: {intersection_value:.4f}', xy=(intersection_idx, intersection_value), xycoords='data', fontsize=10, color='green') plt.xlabel('Epoch') plt.ylabel(metric_label) plt.legend(loc='lower right')

然后,该函数应用于模型的准确度和精确度。



show_performance_curve(training_history, 'accuracy', 'accuracy')



show_performance_curve(training_history, 'precision', 'precision')

在没有任何微调和预处理的情况下训练模型后,我们最终得到:  

  • 准确率为 67.09%,这意味着模型对每 100 个样本中的 67% 的样本进行了正确分类。 
  • 并且,精度为 76.55%,这意味着在每 100 个阳性预测中,几乎有 77 个是真阳性,其余 23 个是假阳性。 
  • 这些分数分别在第三和第二个时期获得准确度和精确度。

这两个指标给出了对模型行为的全局理解。 

如果我们想知道每个类中哪些是模型擅长预测的,哪些是模型难以预测的?

这可以通过混淆矩阵来实现,它显示了每个类的正确和错误预测的数量。下面给出实现。我们首先对测试数据进行预测,然后计算混淆矩阵并显示最终结果。



from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay test_predictions = model.predict(test_images) test_predicted_labels = np.argmax(test_predictions, axis=1) test_true_labels = np.argmax(test_labels, axis=1) cm = confusion_matrix(test_true_labels, test_predicted_labels) cmd = ConfusionMatrixDisplay(confusion_matrix=cm) cmd.plot(include_values=True, cmap='viridis', ax=None, xticks_rotation='horizontal') plt.show()

  • 飞机、汽车、青蛙、马、轮船卡车的 0、1、6、7、8、9 类分别在对角线上具有最高值。这意味着该模型更擅长预测这些类别。 
  • 另一方面,它似乎在与其余类作斗争:  
  • 具有最高非对角线值的类是模型将好类与其混淆的类。例如,它将鸟类(第 2 类)飞机混淆,将汽车卡车(第 9 类)混淆。

从我们的教程中了解有关混淆矩阵的更多信息了解 R 中的混淆矩阵,该教程采用 DataCamp 的机器学习工具箱课程中的课程材料。

可以通过其他任务改进此模型,例如:  

  • 图像增强
  • 使用 ResNet、MobileNet 或 VGG 等预训练模型进行迁移学习。我们的迁移学习教程解释了迁移学习是什么以及它在现实生活中的一些应用。
  • 应用不同的正则化技术,例如 L1、L2 或 dropout。  
  • 微调不同的超参数,例如学习率、批量大小、网络中的层数。 

结论

本文全面概述了 TensorFlow 中的 CNN,提供了有关 CNN 架构每一层的详细信息。此外,它还简要介绍了 TensorFlow 以及它如何帮助机器学习工程师和研究人员构建复杂的神经网络。 

我们将所有这些技能集应用到与多类分类任务相关的真实场景中

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值