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

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

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

使用以下代码将它们连接在一起:

>>> ae = Model(inputs=input_layer, outputs=decoder)
>>> print(ae.summary())
_______________________________________________________________
Layer (type)              Output Shape        Param #
=================================================================
input_1 (InputLayer)      (None, 29)           0
_______________________________________________________________
dense_1 (Dense)           (None, 40)           1200
_______________________________________________________________
dense_2 (Dense)           (None, 29)           1189
=================================================================
Total params: 2,389
Trainable params: 2,389
Non-trainable params: 0
_________________________________________________________________

然后,我们使用 Adam(学习率0.0001)作为优化器来编译模型,如下所示:

>>> optimizer = optimizers.Adam(lr=0.0001)
>>> ae.compile(optimizer=optimizer, loss='mean_squared_error')

除了模型检查点之外,我们还使用 TensorBoard 作为回调函数。 TensorBoard 是 TensorFlow 的表现可视化工具,可提供训练和验证指标的动态图,例如:

>>> tensorboard = TensorBoard(log_dir='./logs/run1/',
                       write_graph=True, write_images=False)
>>> model_file = "model_ae.h5"
>>> checkpoint = ModelCheckpoint(model_file, monitor='loss',
                         verbose=1, save_best_only=True, mode='min')

最后,我们使用数据(X_train, X_train)对模型进行拟合,并使用数据(X_test, X_test)作为自编码器进行验证,并尝试产生与输入相同的输出:

>>> num_epoch = 30
>>> batch_size = 64
>>> ae.fit(X_train, X_train, epochs=num_epoch, batch_size=batch_size,
            shuffle=True, validation_data=(X_test, X_test),
            verbose=1, callbacks=[checkpoint, tensorboard])

以下是第一个和最后一个3周期的结果:

Train on 227440 samples, validate on 57367 samples
Epoch 1/30
227440/227440 [==============================] - 4s 17us/step - loss: 0.6690 - val_loss: 0.4297
Epoch 00001: loss improved from inf to 0.66903, saving model to model_ae.h5
Epoch 2/30
227440/227440 [==============================] - 4s 18us/step - loss: 0.1667 - val_loss: 0.2057
Epoch 00002: loss improved from 0.66903 to 0.16668, saving model to model_ae.h5
Epoch 3/30
227440/227440 [==============================] - 4s 17us/step - loss: 0.0582 - val_loss: 0.1124
......
......
Epoch 28/30
227440/227440 [==============================] - 3s 15us/step - loss: 1.4541e-05 - val_loss: 0.0011
Epoch 00028: loss improved from 0.00001 to 0.00001, saving model to model_ae.h5
Epoch 29/30
227440/227440 [==============================] - 4s 15us/step - loss: 1.2951e-05 - val_loss: 0.0011
Epoch 00029: loss improved from 0.00001 to 0.00001, saving model to model_ae.h5
Epoch 30/30
227440/227440 [==============================] - 4s 16us/step - loss: 1.9115e-05 - val_loss: 0.0010
Epoch 00030: loss did not improve from 0.00001

我们可以在终端中输入以下命令来检出 TensorBoard:

tensorboard --logdir=logs

它返回以下内容:

Starting TensorBoard b'41' on port 6006
(You can navigate to http://192.168.0.12:6006)

通过转到http://192.168.0.12:6006(主机可能有所不同,具体取决于您的环境),我们可以看到随着时间的推移训练损失和验证损失。

下图显示了平滑为 0(无指数平滑)时的训练损失:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mHeaSbfM-1681704767267)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/7a055f42-2a52-4db7-824e-8a4cc5ab5a1e.png)]

此处显示了平滑为 0(无指数平滑)时的验证损失:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MxfesJqb-1681704767267)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/a441f9d5-c897-4251-abb4-0647eb5536be.png)]

现在,我们可以将测试集提供给训练有素的模型,并计算由均方误差测量的重构误差,如下所示:

>>> recon = ae.predict(X_test)
>>> recon_error = np.mean(np.power(X_test - recon, 2), axis=1)

通常,我们将计算 ROC 曲线下的面积,以评估不平衡数据的二分类表现,如下所示:

>>> from sklearn.metrics import (roc_auc_score,
                      precision_recall_curve, auc, confusion_matrix)
>>> roc_auc = roc_auc_score(Y_test, recon_error)
>>> print('Area under ROC curve:', roc_auc)
Area under ROC curve: 0.9548928080050032

实现了 ROC 0.95的 AUC。 但是,由于少数类别很少发生(在测试集中约为 0.87%),因此在这种情况下并不一定表示表现良好。 ROC 的 AUC 可以轻松达到0.9以上,而无需任何智能模型。 相反,我们应该通过精确调用曲线下的面积来衡量表现,该曲线绘制如下:

>>> import matplotlib.pyplot as plt
>>> precision, recall, th =
               precision_recall_curve(Y_test, recon_error)
>>> plt.plot(recall, precision, 'b')
>>> plt.title('Precision-Recall Curve')
>>> plt.xlabel('Recall')
>>> plt.ylabel('Precision')
>>> plt.show()

请参考以下曲线图,以得到精确的召回曲线:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zNUNKU4N-1681704767267)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/a81f053f-76a4-4f5f-9901-f625bf18162d.png)]

精确调用曲线下的面积计算如下:

>>> area = auc(recall, precision)
>>> print('Area under precision-recall curve:', area)
Area under precision-recall curve: 0.8217824584439969

精确召回曲线下的面积为 0.82。 我们还可以绘制各种决策阈值下的精度和召回率,如下所示:

>>> plt.plot(th, precision[1:], 'k')
>>> plt.plot(th, recall[1:], 'b', label='Threshold-Recall curve')
>>> plt.title('Precision (black) and recall (blue) for different
               threshold values')
>>> plt.xlabel('Threshold of reconstruction error')
>>> plt.ylabel('Precision or recall')
>>> plt.show()

请参考以下图表以获得预期结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MevFi6xz-1681704767270)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/3c455bf0-56c6-4d87-87de-8fdf6dbd4094.png)]

可以看出,我们设置的阈值越高,精度越高,但是召回率却越低。 我们将选择0.000001作为决策阈值并计算混淆矩阵,如下所示:

>>> threshold = 0.000001
>>> Y_pred = [1 if e > threshold else 0 for e in recon_error]
>>> conf_matrix = confusion_matrix(Y_test, Y_pred)
>>> print(conf_matrix)
[[55078 1797]
[ 73 419]]

基于 AE 的异常检测器成功捕获了大多数欺诈交易,并且仅错误地拒绝了一些正常交易。 您可以根据特定的折衷考虑其他决策阈值。

深度自编码器

除了一个隐藏层,输出层可以是通过几个隐藏层对输入的重构。 例如,以下是分别具有804080单元的三个隐藏层的模型:

>>> hidden_sizes = [80, 40, 80]
>>> input_layer = Input(shape=(input_size,))
>>> encoder = Dense(hidden_sizes[0], activation="relu")(input_layer)
>>> encoder = Dense(hidden_sizes[1], activation="relu")(encoder)
>>> decoder = Dense(hidden_sizes[2], activation='relu')(encoder)
>>> decoder = Dense(input_size)(decoder)
>>> deep_ae = Model(inputs=input_layer, outputs=decoder)
>>> print(deep_ae.summary())
_______________________________________________________________
Layer (type)            Output Shape      Param #
===============================================================
input_1 (InputLayer)     (None, 29)       0
_______________________________________________________________
dense_1 (Dense)          (None, 80)       2400
_______________________________________________________________
dense_2 (Dense)          (None, 40)       3240
_______________________________________________________________
dense_3 (Dense)          (None, 80)       3280
_______________________________________________________________
dense_4 (Dense)          (None, 29) 2349
===============================================================
Total params: 11,269
Trainable params: 11,269
Non-trainable params: 0
_________________________________________________________________

由于要训练的参数更多,我们将学习率降低到0.00005,并增加了周期数,如下所示:

>>> optimizer = optimizers.Adam(lr=0.00005)
>>> num_epoch = 50

其余代码与普通解决方案相同,在此不再赘述。 但是,这是前两个周期的结果:

Epoch 1/50
227440/227440 [==============================] - 6s 25us/step - loss: 0.5392 - val_loss: 0.3506
Epoch 00001: loss improved from inf to 0.53922, saving model to model_deep_ae.h5
......
......
Epoch 49/50
227440/227440 [==============================] - 6s 26us/step - loss: 3.3581e-05 - val_loss: 0.0045
Epoch 00049: loss improved from 0.00004 to 0.00003, saving model to model_deep_ae.h5
Epoch 50/50
227440/227440 [==============================] - 6s 25us/step - loss: 3.4013e-05 - val_loss: 0.0047
Epoch 00050: loss did not improve from 0.00003

同样,我们通过精确调用曲线下的面积来测量表现,这次完成了0.83,这比原始版本略好:

>>> print('Area under precision-recall curve:', area)
Area under precision-recall curve: 0.8279249913991501

稀疏自编码器

在训练神经网络时,我们通常会在损失目标函数中施加约束,以控制网络的容量并防止过拟合。 自编码器也不例外。 我们可以在自编码器的损失函数中添加 L1 范数正则化项,从而引入稀疏约束。 这种自编码器称为稀疏自编码器。

当训练样本很多时,例如我们的案例超过 220,000,很难说出稀疏性的影响。 因此,我们仅将 5% 的数据用于训练,如下所示:

>>> data_train, data_test = train_test_split(data, test_size=0.95)

我们将快速通过常规自编码器进行基准测试,如下所示:

>>> hidden_sizes = [80, 40, 80]
>>> input_layer = Input(shape=(input_size,))
>>> encoder = Dense(hidden_sizes[0], activation="relu")(input_layer)
>>> encoder = Dense(hidden_sizes[1], activation="relu")(encoder)
>>> decoder = Dense(hidden_sizes[2], activation='relu')(encoder)
>>> decoder = Dense(input_size)(decoder)
>>> ae = Model(inputs=input_layer, outputs=decoder)

除了0.000830周期的学习率外,其余代码与上一节相同:

>>> optimizer = optimizers.Adam(lr=0.0008)
>>> num_epoch = 30

以下是前两个周期的结果:

Train on 14222 samples, validate on 270585 samples
Epoch 1/30
14222/14222 [==============================] - 3s 204us/step - loss: 0.5800 - val_loss: 0.2497
Epoch 00001: loss improved from inf to 0.57999, saving model to model_ae.h5
Epoch 2/30
14222/14222 [==============================] - 3s 194us/step - loss: 0.1422 - val_loss: 0.1175
Epoch 00002: loss improved from 0.57999 to 0.14224, saving model to model_ae.h5
......
......
Epoch 29/30
14222/14222 [==============================] - 3s 196us/step - loss: 0.0016 - val_loss: 0.0054
Epoch 00029: loss did not improve from 0.00148
Epoch 30/30
14222/14222 [==============================] - 3s 195us/step - loss: 0.0013 - val_loss: 0.0079
Epoch 00030: loss improved from 0.00148 to 0.00132, saving model to model_ae.h5
>>> print('Area under precision-recall curve:', area)
Area under precision-recall curve: 0.6628715223813105

我们在0.66的精确调用曲线下获得了面积。

现在,让我们使用 L1 正则化因子0.00003的稀疏版本,如下所示:

>>> from keras import regularizers
>>> input_layer = Input(shape=(input_size,))
>>> encoder = Dense(hidden_sizes[0], activation="relu",
           activity_regularizer=regularizers.l1(3e-5))(input_layer)
>>> encoder = Dense(hidden_sizes[1], activation="relu")(encoder)
>>> decoder = Dense(hidden_sizes[2], activation='relu')(encoder)
>>> decoder = Dense(input_size)(decoder)
>>> sparse_ae = Model(inputs=input_layer, outputs=decoder)

前两个周期的结果如下:

Epoch 1/30
14222/14222 [==============================] - 3s 208us/step - loss: 0.6295 - val_loss: 0.3061
Epoch 00001: loss improved from inf to 0.62952, saving model to model_sparse_ae.h5
Epoch 2/30
14222/14222 [==============================] - 3s 197us/step - loss: 0.1959 - val_loss: 0.1697
Epoch 00002: loss improved from 0.62952 to 0.19588, saving model to model_sparse_ae.h5
......
......
Epoch 29/30
14222/14222 [==============================] - 3s 209us/step - loss: 0.0168 - val_loss: 0.0277
Epoch 00029: loss improved from 0.01801 to 0.01681, saving model to model_sparse_ae.h5
Epoch 30/30
14222/14222 [==============================] - 3s 213us/step - loss: 0.0220 - val_loss: 0.0496
Epoch 00030: loss did not improve from 0.01681

使用稀疏自编码器可以实现精确调用曲线0.70下更大的区域,该稀疏自编码器学习输入数据的稀疏表示和放大表示:

>>> print('Area under precision-recall curve:', area)
Area under precision-recall curve: 0.6955808468297678

去噪自编码器

去噪自编码器DAE)是自编码器的另一种规范化版本,但是该规范化是在输入数据上添加的,而不是损失函数。 自编码器被迫从损坏的输入数据中重建原始数据,以期希望学习到更强大的特征。

对于每个输入样本,将随机选择一组特征进行更改。 建议将腐败率定为 30% 至 50%。 通常,训练样本越多,腐败率越低; 样本越少,腐败率越高。

有两种典型的方法来生成损坏的数据:

  • 为所选数据分配零
  • 将高斯噪声添加到所选数据

下图演示了 DAE 的工作方式:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JoVyN7FE-1681704767270)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/bb8c5846-e12f-43d0-84c8-6990c4688891.png)]

DAE 通常用于神经网络预训练,其中提取的鲁棒表示形式用作下游监督学习的输入特征。 因此,它们不适用于我们的无监督解决方案。 您可以通过这个页面中的图像分类示例进行进一步研究。

压缩自编码器

我们将学习的最后一种自编码器是压缩自编码器。 它们与稀疏兄弟相似,因为它们增加了惩罚项以学习更强大的表示形式。 但是,惩罚项更为复杂,可以如下推导,其中h[j]是隐藏层第j个单元的输出,W是编码器的权重,W[ij]是连接第i个输入单元,以及第j个隐藏单元的权重:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0F8mndfD-1681704767271)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/3819616b-4468-43f3-b392-72d7523ae417.png)]

我们在上一节中定义的原始自编码器的顶部添加压缩项,如下所示:

>>> hidden_size = 40
>>> input_layer = Input(shape=(input_size,))
>>> encoder = Dense(hidden_size, activation="relu")(input_layer)
>>> decoder = Dense(input_size)(encoder)
>>> contractive_ae = Model(inputs=input_layer, outputs=decoder)

损失函数现在变为:

>>> factor = 1e-5
>>> def contractive_loss(y_pred, y_true):
...     mse = K.mean(K.square(y_true - y_pred), axis=1)
...     W = K.variable(
                  value=contractive_ae.layers[1].get_weights()[0])
...     W_T = K.transpose(W)
...     W_T_sq_sum = K.sum(W_T ** 2, axis=1)
...     h = contractive_ae.layers[1].output
...     contractive = factor *
                    K.sum((h * (1 - h)) ** 2 * W_T_sq_sum, axis=1)
...     return mse + contractive

我们使用这种收缩损失来编译模型,如下所示:

>>> contractive_ae.compile(optimizer=optimizer, loss=contractive_loss)

其余代码保持不变,但是这次使用0.0003optimizer = optimizers.Adam(lr=0.0003))的学习率。

我们在此介绍前两个周期的结果:

Train on 227440 samples, validate on 57367 samples
Epoch 1/30
227440/227440 [==============================] - 6s 27us/step - loss: 0.3298 - val_loss: 0.1680
Epoch 00001: loss improved from inf to 0.32978, saving model to model_contractive_ae.h5
Epoch 2/30
227440/227440 [==============================] - 5s 24us/step - loss: 0.0421 - val_loss: 0.0465
Epoch 00002: loss improved from 0.32978 to 0.04207, saving model to model_contractive_ae.h5
......
......
Epoch 29/30
227440/227440 [==============================] - 5s 23us/step - loss: 3.8961e-04 - val_loss: 0.0045
Epoch 00029: loss did not improve from 0.00037
Epoch 30/30
227440/227440 [==============================] - 5s 22us/step - loss: 4.7208e-04 - val_loss: 0.0057
Epoch 00030: loss did not improve from 0.00037

该模型以0.83的精确召回曲线下的面积胜过原始模型:

>>> print('Area under precision-recall curve:', area)
Area under precision-recall curve: 0.8311662962345293

到目前为止,我们已经研究了五种不同类型的自编码器,包括基本的原始编码,深度编码,稀疏编码,去噪编码和收缩编码。 每种类型的自编码器的特长来自某些架构或不同形式的强制约束。 尽管架构或处罚有所不同,但它们具有相同的目标,即学习更强大的表示形式。

总结

我们刚刚使用受限的玻尔兹曼机和自编码器完成了 DL 架构的重要学习旅程! 在本章中,我们更加熟悉 RBM 及其变体。 我们从 RBM 是什么,RBM 的演变路径以及它们如何成为推荐系统的最新解决方案入手。 我们从零开始在 TensorFlow 中实现了 RBM,并构建了基于 RBM 的电影推荐器。 除了浅层架构之外,我们还探索了称为深度信念网络的 RBM 的堆叠版本,并将其用于图像分类,该分类从零开始在 TensorFlow 中实现。

学习自编码器是旅程的后半部分,因为它们具有相似的想法,即通过输入数据重建来寻找潜在的输入表示形式。 在讨论了什么是自编码器并讨论了它们的发展路径之后,我们说明了各种自编码器,这些编码器按其架构或正则化形式进行了分类。 我们还在信用卡欺诈检测中应用了不同类型的自编码器。 每种类型的自编码器都打算提取某些结构或强制形式的鲁棒表示。

练习

您可以使用自编码器构建电影推荐器吗?

致谢

感谢 Shyong Lam 和 Jon Herlocker 清理并生成了 MovieLens 数据集:

F. Maxwell Harper and Joseph A. Konstan. 2015. The MovieLens Datasets: History and Context. ACM Transactions on Interactive Intelligent Systems (TiiS) 5, 4, Article 19 (December 2015), 19 pages. DOI=http://dx.doi.org/10.1145/2827872

第 2 节:卷积神经网络

在本节中,我们将学习一类用于图像的深度学习网络,称为卷积神经网络CNN),以及为什么 CNN 比深度前馈网络更好 。 然后,我们将研究如何减少深度学习网络所需的计算成本,并看到移动神经网络不过是经过修改以具有更少参数和更少内存的 CNN。

本节将介绍以下章节:

  • “第 4 章”,“CNN 架构”
  • “第 5 章”,“移动神经网络和 CNN”

四、CNN 架构

在本章中,我们将讨论一类重要的图像深度学习网络,称为卷积神经网络CNN)。 针对图像相关任务(例如图像识别,分类,对象检测等)而构建的大多数深度学习模型都将 CNN 作为其主要网络。 CNN 允许我们以三维体积而不是单维向量处理传入的数据。 尽管 CNN 是一类神经网络(由权重,层和损失函数组成),但深层前馈网络在结构上有很多差异,我们将在本章中进行解释。 为了让您了解 CNN 的功能强大,ResNet CNN 架构在世界著名的图像分类挑战 ILSVRC 上实现了 3.57% 的最高错误率。 这种表现优于 ImageNet 强大数据集上的人类视觉感知。 我们将在本章后面讨论 ImageNet 和 ILSVRC。 以下是本章将要学习的主题:

  • 深度前馈网络的问题以及对 CNN 的需求
  • CNN 的演进之路
  • CNN 的架构
  • CNN 的不同层及其作用
  • 使用 CNN 的图像分类
  • 一些著名的图像分类 CNN 架构
  • 您的第一个具有 CIFAR-10 数据集的 CNN 图像分类器
  • 使用 CNN 的对象检测
  • 使用 CNN 的著名物体检测器
  • 您的第一个 TensorFlow 对象检测器

深度前馈网络存在的问题

在“第 2 章”,“深度前馈网络”中,我们学习了使用深度前馈网络识别(分类)时尚商品的图像。 每个图像的大小为28 x 28,我们将一个神经元连接到每个像素。 这样,我们在第一层本身就有28 x 28 = 784个神经元。 但是在现实世界中,图像几乎没有这么小。 让我们考虑一个大小为500 x 500的中型图像。因此,现在,在第一层中,我们将需要 250,000 个神经元。 对于这样大小的图像,第一层中有大量的神经元。 因此,网络对于该任务而言在计算上变得过于昂贵。 那么,我们如何解决这个问题呢? 同样,生物学的灵感来了! 在下一节中,我们将详细介绍 CNN 的发展。

CNN 的演进之路

在 1960 年代,人们发现动物的视觉皮层并没有像深度前馈网络那样处理图像。 而是,视觉皮层中的单个神经元连接到一个小的区域(而不是单个像素),这称为感受野。 感受野中的任何活动都会触发相应的神经元。

受视觉皮层感受野的启发,科学家提出了局部连接的想法,以减少处理图像所需的人工神经元数量。 深度前馈网络的这种修改版本称为 CNN(在本书中,CNN 均指卷积神经网络)。 1989 年,Yann LeCun 开发了可训练的 CNN,能够识别手写数字。 1998 年,Yann LeCun 的 LeNet-5 模型再次成功地使用了七个堆叠的卷积层(例如深前馈网络中的层)对大小为32 x 32的数字进行分类。输入图像尺寸的增加由于缺乏处理能力而受到限制。 当时可用。 但是在 2000 年代初,GPU 能够执行并行计算,因此大大减少了深度学习网络所需的处理时间。 更深的 CNN 的开发始于 GPU。 在深入研究细节之前,我们将向您介绍 ImageNet。 它是一个开放源代码数据集,其中包含 1500 万张贴有大约 22,000 个不同对象的图像。 ImageNet 的建立旨在帮助在对象识别领域下开发具有手动标记图像的模型,以进行模型训练。 每年都会举办一场名为 ImageNet 大规模视觉识别挑战赛ILSVRC)的竞赛,该竞赛使用 ImageNet 数据集的一个子集,以挑战为对象提供更准确的识别方法,俗称图像分类。 有关更多详细信息,请参考 ImageNet 网站

CNN 中有很多新方面,例如权重共享,批量操作和本地连接。 在下一节中,我们将讨论所有这些以及架构。

CNN 的架构

CNN 当然是像深度前馈网络一样的神经网络。 CNN 以可学习的权重逐层构建,并且像任何典型的深度学习网络一样受到训练:通过最小化成本函数和反向传播误差。 区别在于神经元的连接方式。 CNN 旨在处理图像。 图像数据具有 CNN 用来减少神经元数量并获得更好学习的两个独特特点:

  • 图像是三维体积-宽度,高度和通道(通道有时称为深度)。 因此,卷积层以三维体积而不是单维向量进行输入和输出。
  • 邻域中的像素具有彼此相关的值。 这称为空间关系。 CNN 通过过滤器使用此功能,以提供与附近像素的神经元的本地连接。

在以下小节中,我们将研究 CNN 涉及的各层以及每一层的独特功能。

输入层

输入层由 3D 数组而不是一维向量组成。 该层以像素在图像中的方式保存像素。 因此,输入层具有形状(批大小,宽度,高度和通道)。 例如,如果我们具有尺寸为32 x 32的图像和三个通道的 RGB,且批大小为 64,则输入层的形状将为(64, 32, 32, 3)。 在下图中可以看到这一点:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4dJ605vV-1681704767271)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/19387290-7362-4924-9ce7-791916ed6e8a.png)]

输入层的形状由粉红色的立方体表示(图像来自 Wikipedia,CS231n 课程的一部分)

这里要注意的重要一点是,输入层用于将结构保持在三维空间中。 在下一节中,我们将看到卷积层如何利用此三维空间。

卷积层

我们将在这里讨论的第一件事是过滤器。 可以将过滤器视为由可学习的权重值组成的图像的较小版本。 就像我们在深层前馈网络中从一个神经元到另一个神经元具有权重连接一样,权重也存在于卷积层中,不同之处在于权重是以过滤器的形式将连接器覆盖的空间区域连接到神经元。 让我们考虑一个大小为5 x 5(宽度和高度)的过滤器的示例。 过滤器也将延伸到图像的第三维(通道)。 对于三通道图像,过滤器尺寸为5 x 5 x 3,对于单通道图像,过滤器尺寸为5 x 5 x 1。下图显示了5 x 5 x 1过滤器:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NAijzA1Z-1681704767271)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/3e990b97-ec1f-423b-8c5b-c84ab3dec1ae.png)]

那么,过滤器在卷积层中做什么? 过滤器在卷积层中执行两项非常重要的任务-本地连接和参数共享。 之前,我们讨论了 CNN 中的感受野,这意味着仅将神经元连接到其邻域图像像素。 该邻域由过滤器定义。 我们在图像上滑动过滤器,过滤器中的每个权重都连接到特定幻灯片的特定神经元。 然后,神经元使用该位置处过滤器覆盖的图像像素的权重和值来计算卷积输出。 换句话说,我们可以说卷积层中的每个神经元都局部地连接到由过滤器定义的图像的局部区域。 这称为本地连接。 下图显示了本地连接:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w5JskLJ4-1681704767271)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/9e061d11-a5a7-409f-9d2d-34ad6722b6ae.png)]

观察感受野(输入层中的深粉红色斑块)如何连接到下一层的单个神经元

神经元如何通过卷积计算过滤器的输出? 为了理解这一点,让我们考虑在6 x 6 x 1图像上放置3 x 3 x 1过滤器的第一张幻灯片的场景。 计算过滤器中每个权重值与对应位置的像素值的点积。 对一个位置上所有权重值的点积求和,此计算出的总和就是卷积的输出。 激活函数(例如 ReLU)用于神经元的输出中。

接下来,我们将看到过滤器如何在图像上滑动以生成卷积输出。 对于过滤器的每张幻灯片,都会将新的神经元连接到过滤器输出。 因此,滑动所涉及的参数也趋向于控制卷积层的输出尺寸。 过滤器的滑动涉及三个重要参数-跨步,零填充和深度:

  • 跨步决定了过滤器从一个位置滑动到另一位置时跳跃的像素数。 通常,跨步值保持为 1。过滤器在每张幻灯片中跳一个像素。 跨度也可以大于 1,但通常不使用。
  • 通常,如果过滤器从图像的左上角开始滑动,则所有幻灯片的最终生成输出趋向于具有较小的尺寸。 但是,通常,我们希望卷积层的输出具有与输入图像相同的宽度和高度。 零填充在图像的边界上添加了 0 的额外填充,从而为过滤器提供了额外的滑动空间,使得最终输出的尺寸与输入的尺寸相同。 当我们加 0 时,这不会影响卷积运算的值。
  • 通常,CNN 在层中不使用单个过滤器。 我们使用一组过滤器(例如 12 个过滤器)。 这样做是因为具有不同权重集的每个过滤器趋向于捕获图像的不同特征。 来自每个过滤器的响应被一个接一个地堆叠,并且每个响应被称为激活映射。 例如,如果我们使用32 x 32 x 1的图像和四个大小为3 x 3 x 1的过滤器,且跨步为 2,填充为 1,则卷积层的输出尺寸为(16 x 16 x 4)。 在这里,最后一个维度将等于激活图的数量,该数量将等于过滤器的数量。 可以使用以下公式计算输出的宽度和高度:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UssxEeuF-1681704767271)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/4a40c8d8-8ea1-4317-890e-70a3b19d3297.png)]

在这里,W是输入大小(W = 32),F是过滤器大小(F = 3),P为填充(P = 1),而S为跨步(S = 1)。

您可能已经观察到,我们正在整个图像上制作相同的过滤器幻灯片。 这意味着在幻灯片中使用相同的权重,而不是为每个幻灯片创建不同的权重集。 由于图像中不同位置的像素值高度相关,因此使卷积幻灯片共享权重会产生良好的效果。 如果证明过滤器在图像中的某个位置有用,则该过滤器也将在不同位置有用。 整个图像上的过滤器权重共享称为参数共享,大大减少了网络中所需参数的数量。

下一层称为最大池化层。 最大池化层用于减小激活图的大小。

最大池化层

CNN 的总体思想是保持通过过滤器提取特征并增加激活图的深度,同时减小宽度和高度尺寸,以便最终剩下高度压缩的特征向量。 为了减小激活图的尺寸,CNN 使用连续卷积层之间的最大池化层。

最大池化层具有两个主要参数-核大小和跨步。 最大池化还在其连接到的上一层的激活图上滑动一个窗口。 该窗口通常称为核。 核在任何幻灯片上的工作都是比较核所覆盖的值,并且仅保留最大值作为该位置的输出。 最常用的核大小是2 x 2。使用超出此大小的核大小会导致各层之间的信息大量丢失。 同样,跨步是决定核在幻灯片中跳转多少像素的参数。 下图演示了核在大小为2 x 2,跨步为 2 的4 x 4激活图上执行最大池化的过程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6PyFUDU9-1681704767272)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/1083fec4-6e27-4b6f-9133-bab4b3af0908.png)]

来自 CS231n 的图像

下图显示了如何使用最大池化层来减小图像和特征映射的大小:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SdHRXJGw-1681704767272)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/78bd04c3-e196-4683-a117-02a039840a43.png)]

图片来自该课程 CS231n 课程

全连接层

在卷积网络的末端,我们需要对图像进行分类并训练网络。 这必须使用 softmax 概率和交叉熵损失来完成。 到现在为止,特征已与卷积层一起提取。 这里的想法是将最后一个卷积或最大池化层的 4D 张量输出压缩到 2D 张量中,其中第一个维度仍将代表批量大小,第二个维度将包含来自最后一层的所有输出值(像数组一样被压缩) 。 此压缩操作通常称为展开操作。 展平的目的是我们现在可以在前添加前馈层,展平后将它们连接到所有值,然后像在深前馈网络中那样使用 softmax 概率和交叉熵损失训练网络。 该层与前馈层相同,但称为全连接层,因为与卷积层不同,因为它仅具有本地连接性,所以该层连接到来自最后一层的每个值。 通常,如果展平后的参数数量很大,我们可以添加一系列全连接层。

现在我们已经了解了卷积网络中的架构和层,在下一节中,我们将使用卷积网络进行图像分类。

使用 CNN 的图像分类

在本节中,我们将介绍一些用于图像分类任务的最成功的 CNN 架构,例如 VGGNet,InceptionNet 和 ResNet。 这些网络由于其强大的特征提取功能,还被用作对象检测模型中的特征提取器。 我们将在以下小节中简要讨论网络。

VGGNet

VGGNet 由牛津大学的 K. Simonyan 和 A. Zisserman 开发。 该网络在 ILSVRC 2014 上获得亚军。VGGNet 是 AlexNet 的改进,用较小的3 x 3卷积代替了 11 和 5 的较高卷积,在多个堆叠层上保持一致。 尽管 VGGNet 并非 ILSVRC 的赢家,但其简单,易于实现的架构及其强大的特征提取功能使 VGGNet 成为对象检测或分割任务中基础网络的明智选择。

VGGNet 具有许多基于堆叠层数的变体。 分别具有 16 层和 19 层的 VGG16 和 VGG19 是最常用的架构。 下图演示了具有3 x 3卷积层,maxpooling 和全连接层的 VGG16 架构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JGdUj7To-1681704767272)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/8e22521e-6e42-4d7a-a142-27d8ccec557e.png)]

如果您希望参考 VGGNet 的原始论文,请访问以下链接

接下来,我们将讨论 ILSVRC 2014 的获奖架构-InceptionNet。

GoogLeNet

GoogLeNet(通常称为 InceptionNet)是 2014 年 ILSVRC 竞赛的获胜者。 让我们在以下几点进行讨论:

  • 在卷积神经网络中,选择合适的核大小进行卷积总是一件大事。 同一对象在不同的图像中可以具有各种大小。 为了捕获不同大小的特征,我们当然需要相应地设置核大小。 当感兴趣的对象覆盖大部分区域时,较大的核通常是好的,而较小的核则适合于本地放置的对象。
  • 网络越深,越好! 但是,堆叠很多层会使梯度流变得困难,并导致过拟合。 简而言之,网络的深度在一定程度上受到限制。 超出此限制,网络不再训练。 它只是过拟合。
  • 建立网络时,我们需要检查其大小。 建立非常大的网络需要巨大的计算能力,这是非常昂贵的。 建立网络的大量费用可能无法满足成本与效用之间的折衷。

Google 的研究人员为了解决这些问题,设计了一个复杂的层,他们将其称为 Inception 模块。

这个想法是对卷积核并行使用不同大小,而不是在层中使用单个核大小。 这样,网络现在可以选择核大小,并且网络现在可以通过核学习最适合该工作的特征。 相互并行排列核也使架构稀疏,这有助于简化对更深层网络的训练。

典型的 InceptionNet 使用三个大小分别为1 x 13 x 35 x 5的卷积核。 将所有这三个核的结果连接起来,形成一个输出向量,该向量充当下一层的输入。 初始层还在5 x 55 x 5核之前添加了5 x 5卷积,以减小大小。 下图显示了 Inception 模块:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nOXcqj5L-1681704767272)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/7ce8e06b-b573-4d4f-adba-f78d4ac2dbea.png)]

图片取自原始论文《随着卷积一起深入》

可以在这个页面上找到原始论文《随着卷积一起深入》的链接。

接下来,我们将研究一种称为 ResNet 的架构,该架构在对图像进行分类方面声称比人类的感知甚至更好。

ResNet

ResNet 是 ILSVRC 2015 的获奖架构。关于 ResNet 的最令人惊讶的事实是,它在 ILSVRC 上实现了 3.57% 的前五位错误率,超过了人类的视觉感知!

ResNet 暴露了一个问题,该问题一直限制着非常深层网络的训练。 在训练深度网络时,精度达到一定的极限,然后迅速下降。 无论架构如何深入,这种现象都将准确率限制在一定的阈值内。 微软研究公司在名为《深度残差学习的图像识别》论文中介绍了 ResNet,该论文可在这个页面中找到。

在整篇论文中,研究人员声称,与其让网络直接通过函数(例如H(x))学习从xy的映射, 它使用残差函数F(x) = H(x) - x。 可以将函数F(x)视为代表网络的层,并且可以将其重写为H(x) = F(x) + x。 作者声称优化间接残差函数F(x)比获得xy的直接优化映射H(x)容易。

在此,将x作为该层的输入,将H(x)作为该层的输出,并将F(x)作为该层的函数, 我们可以很容易地观察到输入x将添加到层的输出中,以使最终输出H(x) = F(x) + x。 这种创建从该层的输入到输出的连接,称为残差连接跳跃连接。 下图显示了具有跳过连接的 ResNet 的构建块:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jGnc2i5t-1681704767272)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/117181dc-715d-412a-8c93-1279b908aac8.png)]

图片来源:深度残差学习,用于图像识别

跳过连接的添加解决了深度网络中的饱和和准确率降低的问题,使架构可以具有更多的层而不饱和。 该架构由 34 层组成,大部分包含3 x 3卷积过滤器。 为了减小特征映射的宽度和高度,使用了第 2 步卷积。 最后,使用全局平均池,然后使用 1,000 个单元的全连接层。 在下图中,与典型的 VGG-19 和没有残留连接的架构相比,您可以观察到 ResNet 架构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5jrs6MeD-1681704767273)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/52e9c62f-3040-47b7-a838-4e0c19477c8c.png)]

图片来源:深度残差学习,用于图像识别

现在是时候建立我们自己的 CNN 网络进行图像分类了。

建立我们的第一个 CNN

在这里,我们将使用著名的 CIFAR-10 数据集来演示使用 CNN 的分类。 如果您不了解 CIFAR 数据集,则以下小节将提供简要说明。

CIFAR

CIFAR 数据集包含近 8000 万张图像。 该数据集是开源的,并由 Alex Krizhevsky,Vinod Nair 和 Geoffrey Hinton 进行整理。 数据集分为两个子集-CIFFAR-10 和 CIFAR-100。 CIFAR-10 数据集具有属于 10 类的图像-飞机,汽车,鸟,马,猫,狗,鹿,青蛙,船和卡车。 CIFAR-10 每个类别中有 6,000 张图像。 这意味着它总共有 60,000 张图像。 50,000 张图像用于训练,10,000 张图像用于测试。 每个图像的尺寸为 32 x 32 x 3,并且每个图像都是 RGB 颜色。 CIFAR-100 数据集与 CIFAR-10 相似,除了有 100 个类别而不是 10 个类别。我们将在这里使用 CIFAR-10 数据集,因为它具有较少的类别。 您可以从 CIFAR 网站下载 CIFAR-10 Python 版本。

数据下载并提取后,您将在提取的文件夹中找到以下文件:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s6WGnjOU-1681704767273)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/fe70c84a-8ab4-422a-811f-f26d1d005a10.png)]

由于数据集很大,因此分为五个批量-data_batch_1data_batch_2data_batch_3data_batch_4data_batch_5,因此我们不需要将完整的数据集加载到内存中。 每个文件中的数据都使用 Python 中的pickle模块转储。 要从文件中检索数据,我们可以使用 Python 的pickle模块中的load方法。 每个批量文件包含 10,000 张图像。 因此,转储向量的尺寸为(10,000 x 3,072)。 RGB 通道图像被展平为单个尺寸。 (32 x 32 x 3 = 3,072)。 我们将需要以所需的形式重塑数据。 接下来,我们将看到如何加载和预处理数据以供使用。

数据加载和预处理

让我们首先编写函数来加载数据批并将其重塑为三维图像。 我们还将使用在原始 CIFAR 网站中也提到过的pickle操作加载数据。 让我们创建一个名为data的类,该类将包含与数据加载和预处理有关的函数。 我们还将定义一个名为load_data_batch的函数,用于将数据批量加载到内存中。 在类属性中,我们将创建一个名为labelsDicti的字典,该字典会将数字标签映射到其实际类。 还创建了逆字典inverseLabelsDicti,以将实际的类映射到数字标签。 这将有助于我们进行预测:

# first import some essential modules
import numpy as np
import pickle
import matplotlib.pyplot as plt
import os
import sys
import tensorflow as tf
from sklearn.utils import shuffle
# define the path to the directory where you have extracted the zipped data
DATA_DIR = 'cifar-10-batches-py'
#hyper-parameters for the model
BATCH_SIZE = 128
CLASS_NUM = 10
EPOCHS = 20
DROPOUT = 0.5
LEARNING_RATE = 0.001
IMAGE_SIZE = (32, 32)
SEED = 2 
class data:
  def __init__(self, dataDir, fileName, batchSize, seed, classNum = 10):
    self.dataDir = dataDir
    self.fileName = fileName
    self.classNum = classNum
    self.batchSize = batchSize
    self.seed = seed
    self.labelsDicti = {0:'airplane',1:'automobile',2:'bird',3:'cat',4:'deer',5:'dog',6:'frog',7:'horse',8:'ship',9:'truck'}
    self.inverseLabelsDicti = {v:k for k,v in self.labelsDicti.items()}
  def load_data_batch(self):
    with open(os.path.join(self.dataDir, self.fileName), 'rb') as f:
      dataBatch = pickle.load(f, encoding = 'latin1') 
      #print(dataBatch['data'].shape)
      # latin1 encoding has been used to dump the data.
      # we don't need filename and other details,
      # we will keep only labels and images
    self.images = dataBatch['data']
    self.labels = dataBatch['labels']

在这里,dataBatch将是一个包含以下键的字典:

  • batch_label:表示文件在5批量中的哪个批量
  • labels:从09的图像的数字标签
  • datanumpy形状的数组(10,000 x 3,072),表示数据
  • filenames:包含相应图像的名称

我们仅将datalabels保留在两个单独的命名属性中,而忽略其他所有内容。 接下来,我们需要将图像重塑为原始形式。 为此,我们首先需要从 10,000 张图像中分离出三个通道。 除了将它们分为三个通道外,我们还将图像重塑为宽度和高度尺寸。 也就是32 x 32。这里要注意的重要一点是,我们需要先将图像分成通道,然后再分为宽度和高度。 因此,一旦将图像重塑为(1e4, 3, 32, 32),我们将需要交换轴。 可以通过numpy数组上的transpose函数完成交换:

def reshape_data(self):
    # function to reshape and transpose
    self.images = self.images.reshape(len(self.images), 3, 32, 32).transpose(0, 2, 3, 1)

现在,我们可以可视化一些图像并查看它们的相应标签。 我们将向data类添加visualise_data函数,该函数将获取4索引列表,并将图像绘制在子图中的这些索引处,并将图像的类别显示为标题:

由于数据集中图像的尺寸较小,绘制的图像将非常模糊。

def visualise_data(self, indices):
 plt.figure(figsize = (5, 5))
    for i in range(len(indices)):
      # take out the ith image in indices 
      img = self.images[indices[i]]
      # it's corresponding label
      label =self.labels[indices[i]]
      plt.subplot(2,2,i+1)
      plt.imshow(img)
      plt.title(self.labelsDicti[label])
plt.show()

您可以创建data类的对象,并调用我们在该对象上构建的函数以以下方式将其可视化:

dataObj = data(DATA_DIR, 'data_batch_1')
dataObj.load_data_batch()
dataObj.reshape_data()
dataObj.visualise_data([100, 4000, 2, 8000])
# here we have chosen indices 100, 4000, 2, 8000

下面的屏幕快照显示了运行上述代码的输出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7s015RJy-1681704767273)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/069ef92a-0fec-4796-b1f3-178201d59131.png)]

接下来,我们将标签转换为单一编码形式。 我们已经在“第 2 章”,“深度前馈网络”中讨论了单热编码。 如果您不记得它,可以返回参考“第 2 章”,“深度前馈网络”下的单热编码。 CIFAR-10 的类数为 10,并且类属性classNum的默认值为10。 以下函数将标签转换为单热编码:

def one_hot_encoder(self):
    # this function will convert the labels into one-hot vectors
    # initially the label vector is a list, we will convert it to numpy array,
    self.labels = np.array(self.labels, dtype = np.int32)
    #converting to one-hot
    self.labels = np.eye(self.classNum)[self.labels]
    #print(self.labels.shape)

我们对图像进行归一化。 在这里,通过归一化,我们意味着将像素值设置在 0 到 1 之间。这很有用,因为激活函数在 0 到 1 之间时很敏感。每个通道中的像素值在 0 到 255 之间。 因此,我们将图像数组除以 255(这是可能的最大值),以使所有内容介于 0 和 1 之间:

def normalize_images(self):
  # just simply dividing by 255
  self.images = self.images / 255

为了促进适当的训练,我们需要调出随机样本。 因此,我们将使用 sklearn 的shuffle函数对数据进行混洗:

def shuffle_data(self):
  # shuffle the data so that training is better
  self.images, self.labels = shuffle(self.images, self.labels, random_state =         self.seed)

下一个函数将是data类的重要函数。 该函数将从加载的文件中生成一批数据和标签。 我们知道我们是分批训练模型的,并且我们已经声明了一个超参数BATCH_SIZE,它决定了一批图像的数量。 因此,该函数将继续循环遍历从文件加载的数据,并每次产生一批大小BATCH_SIZE。 在这里,使用yield代替return,因为yield保留了函数控制,并且我们创建了生成器对象,而不是一旦使用它们便被销毁的列表,从而节省了我们的内存:

def generate_batches(self):
    # function to yield out batches of batchSize from the loaded file
    for i in range(0, len(self.images), self.batchSize):
          last = min(i + self.batchSize, len(self.images))
          yield (self.images[i: last], self.labels[i: last])

现在,我们将专注于构建 CNN 模型。 让我们定义另一个类model,它将包含我们的模型图。 我们还将超参数定义为类的属性:

class model:
  def __init__(self, batchSize, classNum, dropOut, learningRate, epochs, imageSize, savePath):
    self.batchSize = batchSize
    self.classNum = classNum
    self.dropOut = dropOut
    self.imageSize = imageSize
    self.learningRate = learningRate
    self.epochs = epochs
    self.savePath = savePath

首先,我们将使占位符保留数据和标签。 在这里,请注意,占位符的尺寸将为 4。第一个尺寸将代表批量大小,并且正如我们前面所讨论的,输入层将数据保存为 3D 体积。 其余三个尺寸分别是宽度,高度和图像通道。

这里要做的另一件事是为dropOut值创建另一个占位符。 由于 TensorFlow 将所有内容都视为张量,因此dropOut的值也必须是张量。 因此,通过keepProb,我们将添加dropOut占位符值:

with tf.name_scope('placeholders') as scope:
      self.x = tf.placeholder(shape = [None, self.imageSize[0], self.imageSize[1], 3], dtype = tf.float32, name = 'inp_x')
      self.y = tf.placeholder(shape = [None, self.classNum], dtype = tf.float32, name = 'true_y')
      self.keepProb = tf.placeholder(tf.float32)

您可以使用不同的过滤器号,核大小和网络中不同数量的层来试用网络架构。 让我们定义模型的第一层。 我们将在第一层中使用 64 个过滤器,核大小为3 x 3

#first conv layer with 64 filters
    with tf.name_scope('conv_1') as scope:
      #tensorflow takes the kernel as a 4D tensor. We can initialize the values     with tf.zeros
      filter1 = tf.Variable(tf.zeros([3, 3, 3, 64], dtype=tf.float32), name='filter_1')
      conv1 = tf.nn.relu(tf.nn.conv2d(self.x, filter1, [1, 1, 1, 1], padding='SAME', name = 'convo_1'))

在 TensorFlow 中,我们需要将过滤器定义为可变 4D 张量。 前三个维度代表过滤器的宽度,高度和深度,第四个维度是我们想要的过滤器的输出数量。 在这里,第三维必须是当前深度,而第四维必须是我们想要的过滤器数量(此处为 64)。

接下来,我们将在网络中添加一个最大池化层:

with tf.name_scope('pool_1') as scope:
      pool1 = tf.nn.max_pool(conv1, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1],padding='SAME', name = 'maxPool_1')

在此,第二维和第三维表示池化核的宽度和高度。 同样,我们将定义网络的其他层。 我们将逐渐增加深度,并减小宽度和高度:

with tf.name_scope('conv_2') as scope:
      filter2 = tf.Variable(tf.zeros([2, 2, 64, 128], dtype=tf.float32), name='filter_2')
      conv2 = tf.nn.relu(tf.nn.conv2d(pool1, filter2, [1, 1, 1, 1], padding='SAME', name = 'convo_2'))
    with tf.name_scope('conv_3') as scope:
      filter3 = tf.Variable(tf.zeros([2, 2, 128, 128], dtype=tf.float32), name='filter_3')
      conv3 = tf.nn.relu(tf.nn.conv2d(conv2, filter3, [1, 1, 1, 1], padding='SAME', name = 'convo_3'))
    with tf.name_scope('pool_2') as scope:
      pool2 = tf.nn.max_pool(conv3, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1],
                   padding='SAME', name = 'maxPool_2')
    with tf.name_scope('conv_4') as scope:
      filter4 = tf.Variable(tf.zeros([1, 1, 128, 256], dtype=tf.float32), name='filter_4')
      conv4 = tf.nn.relu(tf.nn.conv2d(pool2, filter4, [1, 1, 1, 1], padding='SAME', name = 'convo_4'))
    with tf.name_scope('pool_3') as scope:
      pool3 = tf.nn.max_pool(conv4, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1],
                   padding='SAME', name = 'maxPool_3')
    with tf.name_scope('conv_5') as scope:
      filter5 = tf.Variable(tf.zeros([1, 1, 256, 512], dtype=tf.float32), name='filter_5')
      conv5 = tf.nn.relu(tf.nn.conv2d(pool3, filter5, [1, 1, 1, 1], padding='SAME', name = 'convo_5'))

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

相关实践学习
基于函数计算实现AI推理
本场景基于函数计算建立一个TensorFlow Serverless AI推理平台。
相关文章
|
10天前
|
机器学习/深度学习 API 语音技术
|
机器学习/深度学习 数据可视化 定位技术
Python 深度学习第二版(GPT 重译)(四)(4)
Python 深度学习第二版(GPT 重译)(四)
|
机器学习/深度学习 算法 算法框架/工具
Python 深度学习第二版(GPT 重译)(四)(2)
Python 深度学习第二版(GPT 重译)(四)
|
机器学习/深度学习 存储 计算机视觉
Python 深度学习第二版(GPT 重译)(四)(1)
Python 深度学习第二版(GPT 重译)(四)
|
机器学习/深度学习 API 算法框架/工具
Python 深度学习第二版(GPT 重译)(三)(3)
Python 深度学习第二版(GPT 重译)(三)
|
机器学习/深度学习 监控 算法框架/工具
Python 深度学习第二版(GPT 重译)(三)(2)
Python 深度学习第二版(GPT 重译)(三)
|
机器学习/深度学习 TensorFlow API
Python 深度学习第二版(GPT 重译)(一)(4)
Python 深度学习第二版(GPT 重译)(一)
|
4天前
|
机器学习/深度学习 自然语言处理 算法框架/工具
用于NLP的Python:使用Keras进行深度学习文本生成
用于NLP的Python:使用Keras进行深度学习文本生成
16 2
|
5天前
|
机器学习/深度学习 人工智能 分布式计算
R和Python机器学习:广义线性回归glm,样条glm,梯度增强,随机森林和深度学习模型分析
R和Python机器学习:广义线性回归glm,样条glm,梯度增强,随机森林和深度学习模型分析
|
10天前
|
机器学习/深度学习 PyTorch API