六、图像识别与分类
知识投资永远是最大的利益。
——本杰明·富兰克林
在计算机视觉的保护下,图像识别是一个活跃的跨学科研究领域。 顾名思义,图像或对象识别是识别图像或视频序列中的对象的任务。 传统上,该领域利用数学和计算机辅助建模以及对象设计方面的进步。 这些年来,已经开发了一些手工标注的数据集,以测试和评估图像识别系统。 我们现在称它们为传统技术,一直统治着整个场景,并且不断地改进这项任务,直到最近。 2012 年,深度学习参加了 ImageNet 竞赛,为快速改善和进步计算机视觉和深度学习技术打开了闸门。
在本章中,我们将从深度学习(尤其是迁移学习)的角度介绍图像识别和分类的概念。 本章将涵盖以下方面:
- 深度学习图像分类简介
- 基准数据集
- 最新的深度图像分类模型
- 图像分类和迁移学习用例
本章从本书的第三部分开始。 在本书的这一部分中,我们将涵盖涉及前两部分中讨论的概念和技术的案例研究。 这些用例将呈现现实世界的主题/研究领域,并帮助您了解如何在不同的环境中利用迁移学习。 您可以在 GitHub 存储库中的Chapter 6
文件夹中快速阅读本章的代码。 可以根据需要参考本章。
基于深度学习的图像分类
卷积神经网络(CNN)是这项深度学习革命的核心,旨在改善图像分类任务。 CNN 是处理图像数据的专用神经网络。 作为快速补充,CNN 可以通过它们共享的权重架构帮助我们推断出位移和空间不变特征,并且基本上是前馈网络的变体。 在第 3 章“了解深度学习架构”和第 5 章中,我们已经详细介绍了 CNN 的基础知识。 在继续进行之前,我们鼓励读者快速复习以更好地理解。 下图展示了运行中的典型 CNN:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A2rbW2CN-1681567330241)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/e6d21542-8a77-4a59-8dbe-58e16803c0be.png)]
典型的 CNN,来自这个页面
神经网络最早出现在 2011 年的图像分类竞赛中。受 GPU 训练的网络开始赢得竞赛。 在 2012 年,深层的 CNN 在 ImageNet 图像分类任务上的表现从以前的最好水平提高到 83%,这是世界上第一次注意到。 结果令人惊讶,足以引起全球关注,并有助于通过深度学习解决用例的泛滥。
基准数据集
图像分类,或就此而言,任何分类任务,本质上都是监督学习任务。 受监督的任务通过可用的基础训练集了解不同的类别。
即使 CNN 是共享权重的优化前馈网络,但要在深层 ConvNet 中训练的参数数量可能仍然很大。 这就是为什么需要大量的训练才能获得表现更好的网络的原因之一。 幸运的是,全球研究小组一直在努力收集,手工标注和众包不同的数据集。 这些数据集可用于对不同算法的表现进行基准测试,以及识别不同比赛中的获胜者。
以下是图像分类领域中广泛接受的基准数据集的简要列表:
- ImageNet:这是一个具有黄金标准的可视数据集,具有超过 1400 万个带有手工标注的高分辨率彩色图像,涵盖了 20,000 个类别。 它是 2009 年由普林斯顿大学计算机科学系设计用于视觉对象识别任务的。此后,此数据集(在其 1000 个非重叠类的修整版本中)已用作 ImageNet 大规模视觉识别挑战的基础。
- 8000 万个微小图像数据集:顾名思义,该 MIT 数据集包含从互联网收集的 8000 万个图像,并标记了 75,000 多个不同的非抽象英语名词。 该数据集还为其他广泛使用的数据集(包括 CIFAR 数据集)奠定了基础。
- CIFAR-10:由加拿大高级研究所开发,CIFAR-10 是机器学习(ML)研究中使用最广泛的数据集之一。 该数据集包含 60,000 张横跨 10 个非重叠类的低分辨率图像。
- CIFAR-100:来自同一研究组,该数据集包含 60,000 张图像,均匀分布在 100 个不同的类别中。
- 上下文中的公共对象:上下文中的公共对象(COCO)是用于对象标识,分段和字幕的大型可视数据库。 该数据集包含超过 200,000 张跨越不同类别的标记图像。
- 开放图像:这可能是最大的可用标注数据集之一。 该数据集的版本 4 包含超过 900 万个带标注的图像。
- Caltech 101 和 Caltech 256:这些数据集包含分别跨越 101 和 256 个类别的带标注的图像。 加州理工学院 101 包含约 9,000 张图像,而加州理工学院 256 包含近 30,000 张图像。
- 斯坦福犬数据集:这是一个有趣的数据集,特定于不同的犬种。 它包含 20,000 个彩色图像,涵盖 120 个不同的犬种。
- MNIST:MNIST 是有史以来最著名的视觉数据集之一,已成为 ML 爱好者的事实上的 HelloWorld 数据集。 它包含超过 60,000 个手工标记的数字(零到九个数字)。
上面的列表仅是冰山一角。 还有许多其他数据集可以捕获世界的不同方面。 准备这些数据集是一个痛苦且耗时的过程,但是这些数据集使深度学习在当前形式下如此成功。 鼓励读者详细研究这些和其他此类数据集,以了解它们背后的细微差别以及这些数据集为我们解决的挑战。 在本章和后续章节中,我们将利用其中的一些数据集来理解迁移学习的概念。
最新的深度图像分类模型
多年来,深度学习已引起了广泛的关注和炒作。 不足为奇的是,在全球范围内以深度学习为中心的知名竞赛,会议和期刊上共享了大量研究工作。 尤其是图像分类架构已经引起人们的关注,这几年来,定期进行迭代改进一直是人们关注的重点。 让我们快速了解一些表现最佳,最流行的最新深度图像分类架构:
- AlexNet:这是可以归功于打开闸门的网络。 由深度学习的先驱之一 Geoffrey Hinton 和团队设计,该网络将前五名的错误率降低到了 15.3%。 它也是最早利用 GPU 加快学习过程的架构之一。
- VGG-16:牛津大学视觉几何小组的网络是表现最好的架构之一,广泛用于对其他设计进行基准测试。 VGG-16 采用了一个简单的架构,该架构是基于
3 x 3
个卷积层(一个 16 层)相互堆叠,然后是一个最大池化层,以实现强大的表现。 该模型由稍微更复杂的模型 VGG19 继承。 - Inception:也称为 GoogleNet ,该网络是在 ImageNet 大规模视觉识别挑战赛(ILSVRC)在 2014 年实现了前五名的错误率为 6.67%。 它是最早实现接近人类表现的架构之一。 该网络背后的新颖之处在于使用了起始层,该起始层包括了在同一级别将不同大小的内核连接在一起的过程。
- ResNet:由 Microsoft Research Asia 引入,残差网络(ResNet)是一种新颖的架构,利用批量规范化和跳过连接来实现仅仅 3.57% 的前五位的错误率。 它比诸如 VGG 之类的简单架构要深很多(152 层)并且要复杂得多。
- MobileNet:尽管大多数架构都在竞争中胜过其他架构,但每个新的复杂网络都需要更多的计算能力和数据资源。 MobileNet 偏离了此类架构,并被设计为适用于移动和嵌入式系统。 该网络利用了一种新颖的思想,即使用深度可分离卷积来减少训练网络所需的参数总数。
我们提供了基于深度学习的图像分类空间中一些最新架构的快速概述和概述。 有关详细讨论,读者可以查看第 3 章,“了解深度学习架构”中的“卷积神经网络”部分。
图像分类与迁移学习
到目前为止,我们已经讨论了什么是图像分类。 在本节中,我们将通过构建自己的分类器来弄清手。 在本章的较早部分中,我们简要提到了著名的基准测试数据集,包括 CIFAR-10 和 Stanford Dogs 数据集,我们将在接下来的部分中重点介绍这些数据集。 我们还将利用预先训练的模型来了解我们如何利用迁移学习来改进我们的模型。
CIFAR-10
CIFAR-10 是深度学习领域中使用最广泛的图像数据集之一。 由加拿大高级研究所准备,这是一个相当不错的数据集。 该数据集的主要优点是它包含 10 个非重叠类别的平衡分布。 图像的分辨率和尺寸较低,因此可以将数据集用于在较小的内存占用量系统上进行训练。
建立图像分类器
CIFAR-10 是少数可用的平衡数据集之一。 它的整体大小为 60,000 张图像。 以下代码段加载 CIFAR-10 数据集,并设置训练变量和测试变量:
# load CIFAR dataset (X_train, y_train), (X_test, y_test) = cifar10.load_data()
数据集中的图像分辨率很低,有时甚至很难贴上标签。 IPython 笔记本CIFAR-10_CNN_Classifier.ipynb
中提供了本节中共享的代码。
我们已经讨论了 CNN 以及如何针对视觉数据集对其进行优化。 CNN 遵循权重分配原则以减少参数数量; 从头开始开发它们不仅需要强大的深度学习技能,还需要巨大的基础架构需求。 牢记这一点,从头开始开发 CNN 并测试我们的技能将很有趣。
以下代码片段展示了使用 Keras 构建的非常简单的 CNN,它只有五层(两层卷积,一层最大池化,一层密集和一层最终 softmax 层):
model = Sequential() model.add(Conv2D(16, kernel_size=(3, 3), activation='relu', input_shape=INPUT_SHAPE)) model.add(Conv2D(32, (3,3), padding='same', kernel_regularizer=regularizers.l2(WEIGHT_DECAY), activation='relu')) model.add(BatchNormalization()) model.add(MaxPooling2D(pool_size=(2,2))) model.add(Dropout(0.2)) model.add(Flatten()) model.add(Dense(128, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(NUM_CLASSES, activation='softmax'))
为了提高总体泛化表现,该模型还包含BatchNormalization
层和DropOut
层。 这些层有助于我们控制过拟合,还可以防止网络存储数据集本身。
我们仅用 25 个周期来运行模型,以在验证集上达到约 65% 的准确率。 以下屏幕快照显示了训练后的模型的输出预测:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gr4PGkr1-1681567330242)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/80bb52c2-ee64-4f17-9739-0102fa0a8713.png)]
来自基于 CNN 的 CIFAR-10 分类器的预测
尽管绝不是最先进的结果,但结果足够不错。 读者应该记住,这个 CNN 只是为了展示手头的巨大潜力,我们鼓励您尝试在同一行上进行实验。
迁移知识
由于本章和书着重于迁移学习,因此让我们快速进行利用和迁移所学信息的实际任务。 在上一节中,我们讨论了不同的最新 CNN 架构。 现在,让我们利用在 ImageNet 上训练的 VGG-16 模型对 CIFAR-10 数据集中的图像进行分类。 该部分的代码在 IPython 笔记本CIFAR10_VGG16_Transfer_Learning_Classifier.ipynb
中可用。
ImageNet 是一个庞大的视觉数据集,具有 20,000 多个不同类别。 另一方面,CIFAR-10 仅限于 10 个非重叠类别。 像 VGG-16 这样的强大网络需要巨大的计算能力和时间来训练,以达到比人类更好的表现。 这将迁移学习带入了画面。 由于我们大多数人都无法访问无限的计算,因此我们可以在两种不同的设置下利用这些网络:
- 使用经过预训练的最新网络作为特征提取器。 这是通过删除顶部分类层并使用倒数第二层的输出来完成的。
- 在新数据集上微调最新的网络。
我们将利用 VGG-16 作为特征提取器,并在其之上构建自定义分类器。 以下代码段加载并准备了 CIFAR-10 数据集以供使用:
# extract data (X_train, y_train), (X_test, y_test) = cifar10.load_data() #split train into train and validation sets X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.15, stratify=np.array (y_train), random_state=42) # perform one hot encoding Y_train = np_utils.to_categorical(y_train, NUM_CLASSES) Y_val = np_utils.to_categorical(y_val, NUM_CLASSES) Y_test = np_utils.to_categorical(y_test, NUM_CLASSES) # Scale up images to 48x48 X_train = np.array([sp.misc.imresize(x, (48, 48)) for x in X_train]) X_val = np.array([sp.misc.imresize(x, (48, 48)) for x in X_val]) X_test = np.array([sp.misc.imresize(x, (48, 48)) for x in X_test])
前面的代码片段不仅将训练数据集拆分为训练集和验证集,还将目标变量转换为单热编码形式。 我们还将图像尺寸从32 x 32
调整为48 x 48
,以符合 VGG-16 输入要求。 训练,验证和测试数据集准备就绪后,我们可以朝着准备分类器的方向努力。
以下代码段显示了我们如何轻松地在现有模型之上附加新层。 因为我们的目标是仅训练分类层,所以通过将可训练参数设置为False
冻结其余层。 这使我们即使在功能较弱的基础架构上也可以利用现有架构,并将学到的权重从一个域转移到另一个域:
base_model = vgg.VGG16(weights='imagenet', include_top=False, input_shape=(48, 48, 3)) # Extract the last layer from third block of vgg16 model last = base_model.get_layer('block3_pool').output # Add classification layers on top of it x = GlobalAveragePooling2D()(last) x= BatchNormalization()(x) x = Dense(64, activation='relu')(x) x = Dense(64, activation='relu')(x) x = Dropout(0.6)(x) pred = Dense(NUM_CLASSES, activation='softmax')(x) model = Model(base_model.input, pred) for layer in base_model.layers: layer.trainable = False
我们有基本的成分。 整个流水线中剩下的最后一块是数据扩充。 整个数据集仅包含 60,000 张图像; 数据扩充非常方便,可以为手头的样本集添加某些变化。 这些变体使网络能够学习比其他方法更通用的功能。 以下代码段利用ImageDataGenerator()
实用工具准备训练和验证增强对象:
# prepare data augmentation configuration train_datagen = ImageDataGenerator(rescale=1\. / 255, horizontal_flip=False) train_datagen.fit(X_train) train_generator = train_datagen.flow(X_train, Y_train, batch_size=BATCH_SIZE) val_datagen = ImageDataGenerator(rescale=1\. / 255, horizontal_flip=False) val_datagen.fit(X_val) val_generator = val_datagen.flow(X_val, Y_val, batch_size=BATCH_SIZE)
现在让我们训练模型几个周期并衡量其表现。 以下代码段调用fit_generator()
函数将新添加的层训练到模型中:
train_steps_per_epoch = X_train.shape[0] // BATCH_SIZE val_steps_per_epoch = X_val.shape[0] // BATCH_SIZE history = model.fit_generator(train_generator, steps_per_epoch=train_steps_per_epoch, validation_data=val_generator, validation_steps=val_steps_per_epoch, epochs=EPOCHS, verbose=1)
fit_generator()
返回的历史对象包含有关每个周期的详细信息。 我们利用这些来绘制模型在精度和损失方面的整体表现。 结果如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zbSRNjap-1681567330242)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/db41fbac-3130-4690-af32-deff0262e31d.png)]
训练验证表现
如我们所见,与从头开始开发的模型相比,迁移学习帮助我们在整体表现上实现了惊人的提升。 这项改进利用了训练有素的 VGG-16 权重将学习到的特征转移到该域。 读者可以使用相同的工具plot_predictions()
可视化随机样本上的分类结果,如以下屏幕截图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N8jATRCJ-1681567330242)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/fb2a9e1f-dc9f-47c7-9e67-26b712c100ab.png)]
使用基于 VGG-16 的迁移学习构建的分类器的预测结果
神经网络是相当复杂的学习机器,很难调试和优化。 尽管有许多可用的技术,但需要经验来微调网络。 在当前情况下,使用像 VGG-16 这样的深层 CNN 可能对如此小尺寸的图像来说是过大的杀伤力,但它显示了巨大的潜力。 明智地使用它!
这是迁移学习的快速简单的应用,其中我们利用了像 VGG-16 这样令人惊讶的复杂深度 CNN 来准备 CIFAR-10 分类器。 鼓励读者不仅尝试定制分类器的不同配置,而且尝试甚至使用不同的预训练网络来理解所涉及的复杂性。
狗品种鉴定数据集
在上一节中,我们利用低分辨率图像数据集将图像分类为 10 个非重叠类别。 这绝非易事,但我们以最小的努力获得了不错的表现。
现在让我们升级游戏,并使图像分类的任务更加令人兴奋。 在本节中,我们将专注于细粒度图像分类的任务。 与常规图像分类任务不同,细粒度图像分类是指识别更高级别类别中不同子类别的任务。
为了更好地理解此任务,我们将围绕斯坦福犬数据集进行讨论。 顾名思义,该数据集包含不同犬种的图像。 在这种情况下,任务是识别每种犬种。 因此,高级概念是狗本身,而任务是正确分类不同的子概念或子类(在这种情况下为品种)。 该数据集包含来自 ImageNet 数据集的 20,000 个带有标签的图像,该图像集由 120 个不同的犬种组成。 为了便于讨论,我们将利用 Kaggle 提供的数据集。 该数据集位于以下链接中。
让我们开始构建狗分类器的任务。 但是,在实际模型之前,让我们对数据集本身进行快速探索性分析,以更好地理解。
探索性分析
我们不能足够强调理解底层数据集的重要性。 在当前情况下,我们正在处理一个视觉数据集,该数据集由 10,000 个样本组成,分布在 120 个类别(狗的品种)中。 读者可以在名为dog_breed_eda.ipynb
的 IPython 笔记本中参考与探索性分析有关的所有步骤。
由于这是一个可视数据集,因此我们首先将数据集中的一些样本可视化。 有多种方法可以在 Python 中提取和可视化图像数据。 我们将依靠 SciPy 和与 matplotlib 相关的工具来做到这一点。 以下代码片段导入所需的库:
In [1]: import os ...: import scipy as sp ...: import numpy as np ...: import pandas as pd ...: ...: import PIL ...: import scipy.ndimage as spi ...: ...: import matplotlib.pyplot as plt ...: import seaborn as sns ...: ...:np.random.seed(42)
由于数据集很大,因此我们准备了几个工具来加载随机的图像批量并显示选定的批量。 工具的标题为load_batch()
和plot_batch()
; 这些细节在 IPython 笔记本中可用。 以下代码段绘制了随机批次以供参考:
In [7]:batch_df = load_batch(dataset_df, ...: batch_size=36) In [8]:plot_batch(batch_df, grid_width=6, grid_height=6 ...: ,im_scale_x=64, im_scale_y=64)
生成的输出如下网格所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lI7Wta62-1681567330242)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/b2f44b73-b96f-423b-ae01-abc016a16f8a.png)]
狗品种识别数据集的样本图像
从前面的网格中,我们可以看到在分辨率,照明,缩放级别等方面存在很多变化,并且图像不仅包含一只狗,而且还包含其他狗和周围环境。 项目。 我们还需要了解图像尺寸的差异。 使用以下代码段,我们生成了一个散点图来理解它们:
In [12]: plt.plot(file_dimension_list[:, 0], file_dimension_list[:, 1], "ro") ...: plt.title("Image sizes") ...: plt.xlabel("width") ...: plt.ylabel("height")
生成的散点图如下所示。 我们可以清楚地看到最大图像数量在500 x 500
尺寸之内,但是形状确实有所不同:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W9JaMTDl-1681567330243)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/3fadb37c-895c-49fa-b6ad-3239d4c43071.png)]
图像大小的散点图
还需要检查狗的品种分布,以了解我们正在处理什么。 由于我们具有标记的数据集,因此我们可以轻松地进行检查。 以下代码段使用pandas
绘制品种分布:
In [13]: fig = plt.figure(figsize = (12,5)) ...: ...: ax1 = fig.add_subplot(1,2, 1) ...:dataset_df.breed.value_counts().tail().plot('bar', ...: ax=ax1,color='gray', ...: title="Breeds with Lowest Counts") ...: ...: ax2 = fig.add_subplot(1,2, 2) ...:dataset_df.breed.value_counts().head().plot('bar', ...: ax=ax2,color='black', ...: title="Breeds with Highest Counts")
数据集没有被平均分割; 与其他品种相比,某些品种的样本更多。 从下图中的图可以明显看出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NQule6hM-1681567330243)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/9973f689-d94a-49b1-97ec-90e66790785c.png)]
最高和最低犬种样本数
这样的数据集需要彻底的探索。 我们已经在本节中介绍了一些探索性步骤。 进一步的步骤在引用的 IPython 笔记本中列出/执行。 鼓励读者逐步了解图像大小调整的影响,不同层如何检测不同特征,灰度等。
数据准备
探索性分析有助于我们更好地了解手头的数据集。 下一个任务是为数据集构建一个实际的分类器。 众所周知,对于任何分类问题,第一步都是将数据集分为训练集和验证集。 由于我们正在使用 Keras,因此我们将从其工具中获取帮助以帮助准备我们的数据集。 以下代码段展示了将原始数据集组织为训练集和验证集的过程:
# Prepare column to store image path data_labels['image_path'] = data_labels.apply( lambda row: (train_folder + row["id"] + ".jpg" ), axis=1) # load image data as arrays of defined size train_data = np.array([img_to_array(load_img(img, target_size=(299, 299))) for img in data_labels['image_path'].values.tolist() ]).astype('float32') # split data into train and test x_train, x_test, y_train, y_test = train_test_split(train_data, target_labels, test_size=0.3, stratify=np.array(target_labels), random_state=42) # split train dataset into train and validation sets x_train, x_val, y_train, y_val = train_test_split(x_train, y_train, test_size=0.15, stratify= np.array(y_train), random_state=42)
前面显示的第一步是准备标签dataframe
中的派生列以保存实际图像路径。 然后,我们继续简单地将数据集分为训练,验证和测试数据集。 下一步是在将标签输入模型之前,将标签快速转换为一次性编码形式。 以下代码段准备了目标变量的一键编码形式:
y_train_ohe = pd.get_dummies(y_train.reset_index( drop=True) ).as_matrix() y_val_ohe = pd.get_dummies(y_val.reset_index( drop=True) ).as_matrix() y_test_ohe = pd.get_dummies(y_test.reset_index( drop=True) ).as_matrix()
众所周知,深度学习算法需要大量数据。 在这种情况下,即使我们总共有 10,000 张图像,每个类别的计数也不是很大。 为了对此进行改进,我们执行增强。 简单来说,数据增强是利用策展的数据集通过生成现有数据点的变体来扩大自身的过程。 在这种情况下,我们利用keras
中的ImageDataGenerator
来扩充训练和验证数据集,如以下代码片段所示:
# Create train generator. train_datagen = ImageDataGenerator(rescale=1./255, rotation_range=30, width_shift_range=0.2, height_shift_range=0.2, horizontal_flip = 'true') train_generator = train_datagen.flow(x_train, y_train_ohe, shuffle=False, batch_size=BATCH_SIZE, seed=1) # Prepare Validation data augmentation val_datagen = ImageDataGenerator(rescale = 1./255) val_generator = train_datagen.flow(x_val, y_val_ohe, shuffle=False, batch_size=BATCH_SIZE, seed=1)
现在我们已经准备好数据,下一步是准备实际的分类器。
使用迁移学习的狗分类器
现在我们的数据集已经准备好,让我们开始建模过程。 我们已经知道如何从头开始构建深度的卷积网络。 我们也了解达到良好表现所需的微调数量。 对于此任务,我们将利用迁移学习的概念。
预训练模型是开始进行迁移学习任务所需的基本特征。 如前几章所述,可以通过在当前任务上微调预训练网络的权重,或将预训练模型用作特征提取器,来利用迁移学习。
在这种用例中,我们将集中精力利用预训练的模型作为特征提取器。 众所周知,深度学习模型基本上是神经元相互连接的层的堆叠,最后一层充当分类器。 这种架构使深度神经网络能够捕获网络中不同级别的不同特征。 因此,我们可以利用此属性将它们用作特征提取器。 通过删除最后一层或使用倒数第二层的输出,可以做到这一点。 然后,将倒数第二层的输出馈送到其他一组层,然后是分类层。 以下代码段展示了基于 InceptionV3 预训练模型的特征提取,并堆叠了其他层以准备分类器:
# Get the InceptionV3 model so we can do transfer learning base_inception = InceptionV3(weights='imagenet', include_top = False, input_shape=(299, 299, 3)) # Add a global spatial average pooling layer out = base_inception.output out = GlobalAveragePooling2D()(out) out = Dense(512, activation='relu')(out) out = Dense(512, activation='relu')(out) total_classes = y_train_ohe.shape[1] predictions = Dense(total_classes, activation='softmax')(out)
如前面的代码片段所示, Keras 提供了用于处理许多预训练模型的简单工具,将它们用作特征提取器就像将标志include_top
设置为False
一样简单。 在下面的代码片段中,我们通过将两组层堆叠在一起,然后冻结 InceptionV3 中的层来准备最终模型:
model = Model(inputs=base_inception.input, outputs=predictions) # only if we want to freeze layers for layer in base_inception.layers: layer.trainable = False
现在,我们有了模型,所有模型都将在“狗品种识别”数据集中进行训练。 我们使用fit_generator()
方法训练模型,以利用上一步中准备的数据增强。 我们将批次大小设置为 32,并训练模型 13 个周期。 以下代码片段设定了滚动的方向:
batch_size = BATCH_SIZE train_steps_per_epoch = x_train.shape[0] // batch_size val_steps_per_epoch = x_val.shape[0] // batch_size history = model.fit_generator(train_generator, steps_per_epoch=train_steps_per_epoch, validation_data=val_generator, validation_steps=val_steps_per_epoch, epochs=15, verbose=1)
由于我们在每个周期(history
对象)之后都保存了模型参数和表现的输出,因此我们现在将利用它来了解模型表现。 下图绘制了模型的训练和测试精度以及其损失表现:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ea57dWBR-1681567330243)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/499881f7-60ac-48f4-acdc-9b0b63f1ec1d.png)]
狗品种分类器表现
该模型在训练集和验证集上仅在 15 个周期内就获得了 80% 精度以上的可嘉表现。 右侧的图显示了损耗下降并收敛到 0.5 的速度。 这是一个很好的例子,说明了迁移学习的强大而简单。
训练和验证的表现相当不错,但是对看不见的数据的表现如何? 由于我们已经将原始数据集分为三个单独的部分。 这里要记住的重要一点是,测试数据集必须经过与训练数据集相似的预处理。 为了解决这个问题,在将测试数据集输入到函数之前,我们还对它进行了缩放。
该模型在测试数据集上实现了惊人的 85% 准确率以及 0.85 F1 得分。 鉴于我们仅用最少的投入就训练了 15 个周期,迁移学习帮助我们实现了一个不错的分类器:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UOwVboBZ-1681567330244)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/37a13513-298a-414a-beeb-76c68f9c1a36.png)]
狗的品种分类器预测
上图显示了模型表现的视觉证明。 正如我们所看到的,在大多数情况下,该模型不仅可以预测正确的犬种,而且还可以非常有把握地进行预测。
总结
在本书的前两个部分中,我们讨论了很多理论。 建立了强大的概念和技术基础后,我们在本章中开始了用例驱动的旅程。 本章是一系列后续章节中的第一章,这些章节将展示不同场景和领域中迁移学习的实际用例。 在本章中,我们将迁移学习应用于视觉对象识别或俗称图像分类的领域。
我们从围绕 CNN 的快速更新开始,并随着 2012 年深度学习模型的出现,计算机辅助对象识别的整个过程将一劳永逸。我们简要介绍了各种最新的图像分类模型 ,它已经超越了人类的表现。 我们还快速浏览了学术界和行业专家用于训练和调整模型的不同基准数据集。 设置上下文后,我们从 CIFAR-10 数据集开始。 我们使用 Keras 和 TensorFlow 作为后端从头开始构建了一个分类器。 我们利用 VGG-16 作为特征提取的预训练模型,利用迁移学习来改善此表现。
在本章的最后部分,我们利用迁移学习来解决稍微复杂的问题。 我们准备了一个犬种分类器,而不是基于类别不重叠的数据集(CIFAR-10),以基于 Stanford Dogs 数据集识别 120 种不同的犬种。 只需几行代码,我们就可以实现近乎最新的表现。 第二种用例也称为细粒度图像分类任务,并且比通常的图像分类任务复杂。 在本章中,我们展示了通过迁移学习获得惊人结果的强大功能和简便性。 在接下来的章节中,请继续关注来自计算机视觉,音频分析等领域的惊人用例。
七、文本文件分类
在本章中,我们讨论了迁移学习在文本文档分类中的应用。 文本分类是一种非常流行的自然语言处理任务。 关键目标是根据文档的文本内容将文档分配到一个或多个类别或类别。 这在行业中得到了广泛的应用,包括将电子邮件分类为垃圾邮件/非垃圾邮件,审阅和评级分类,情感分析,电子邮件或事件路由,在此我们将电子邮件\事件分类,以便可以将其自动分配给相应的人员。 以下是本章将涉及的主要主题:
- 文本分类概述,行业应用和挑战
- 基准文本分类数据集和传统模型的表现
- 密集向量的单词表示 — 深度学习模型
- CNN 文档模型-单词到句子的嵌入,然后进行文档嵌入
- 源和目标域分布不同的迁移学习的应用; 也就是说,源域由重叠较少的类组成,目标域具有许多混合类
- 源和目标域本身不同的迁移学习的应用(例如,源是新闻,目标是电影评论,依此类推)
- 训练有素的模型在完成其他文本分析任务(例如文档摘要)中的应用-解释为什么将评论归类为负面/正面
我们将通过动手示例来关注概念和实际实现。 您可以在 GitHub 存储库中的Chapter 7
文件夹中快速阅读本章的代码。 可以根据需要参考本章。
文本分类
给定一组文本文档和一组预定义类别,文本分类的目的是将每个文档分配给一个类别。 根据问题,输出可以是软分配或硬分配。 软分配意味着将类别分配定义为所有类别上的概率分布。
文本分类在工业中有广泛的应用。 以下是一些示例:
- 垃圾邮件过滤:给定电子邮件,将其分类为垃圾邮件或合法电子邮件。
- 情感分类:给定评论文本(电影评论,产品评论),请确定用户的极性-无论是正面评论,负面评论还是神经评论。
- 问题单分配:通常,在任何行业中,每当用户遇到有关任何 IT 应用或软件/硬件产品的问题时,第一步就是创建问题单。 这些票证是描述用户所面临问题的文本文档。 下一个合乎逻辑的步骤是,某人必须阅读说明并将其分配给具有适当专业知识的团队才能解决问题。 现在,给定一些历史故障单和解决方案团队类别,可以构建文本分类器以自动对问题故障单进行分类。
- 问题单的自动解决方案:在某些情况下,问题的解决方案也是预先定义的; 也就是说,专家团队知道解决该问题应遵循的步骤。 因此,在这种情况下,如果可以高精度地构建文本分类器来对票证进行分类,则一旦预测了票证类别,便可以运行自动脚本来直接解决问题。 这是未来 IT 运营人工智能(AIOps)的目标之一。
- 有针对性的营销:营销人员可以监视社交媒体中的用户,并将其分类为促进者或破坏者,并基于此,对在线产品发表评论。
- 体裁分类:自动文本体裁分类对于分类和检索非常重要。 即使一组文档属于同一类别,因为它们共享一个共同的主题,但它们通常具有不同的用途,属于不同的流派类别。 如果可以检测到搜索数据库中每个文档的类型,则可以根据用户的喜好更好地向用户呈现信息检索结果。
- 索赔中的欺诈检测:分析保险索赔文本文档并检测索赔是否为欺诈。
传统文本分类
构建文本分类算法/模型涉及一组预处理步骤以及将文本数据正确表示为数值向量。 以下是一般的预处理步骤:
- 句子拆分:将文档拆分为一组句子。
- 分词:将句子拆分为组成词。
- 词干或词根去除:单词标记被简化为它们的基本形式。 例如,诸如演奏,演奏和演奏之类的单词具有一个基数:演奏。 词干的基本单词输出不必是词典中的单词。 而来自残词化的根词,也称为引理,将始终存在于字典中。
- 文本清除:大小写转换,更正拼写并删除停用词和其他不必要的单词。
给定文本文档的语料库,我们可以应用前面的步骤,然后获得构成语料库的单词的纯净词汇。 下一步是文本表示。 词袋(BoW)模型是从文本文档中提取特征并创建文本向量表示的最简单但功能最强大的技术之一。 如果我们在提取的词汇表中有N
个单词,则任何文档都可以表示为D = {w[1], w[2], ...
,其中w[i]
代表文档中单词出现的频率。 这种文本作为稀疏向量的表示称为 BoW 模型。 在这里,我们不考虑文本数据的顺序性质。 一种部分捕获顺序信息的方法是在构建词汇表时考虑单词短语或 n-gram 和单个单词特征。 但是,挑战之一是我们的代表人数。 也就是说,我们的词汇量爆炸了。
文档向量也可以表示为二元向量,其中每个w[i] ∈ {0, 1}
表示文档中单词的存在或不存在。 最受欢迎的表示形式是单词频率的归一化表示形式,称为词频-逆文档频率(TF-IDF)表示形式。 通过将我们语料库中的文档总数除以每个单词的文档频率,然后对结果应用对数缩放,可以计算出 IDF 表示的文档逆频率。 TF-IDF 值是词频与逆文档频率的乘积。 它与单词在文档中出现的次数成正比地增加,并根据语料库中单词的频率按比例缩小,这有助于调整某些单词通常更频繁出现的事实。
现在,我们都准备建立一个分类模型。 我们需要一套带有标签的文件或训练数据。 以下是一些流行的文本分类算法:
- 多项式朴素贝叶斯
- 支持向量机
- K 最近邻
具有线性核的支持向量机(SVM)与用于文本分类的基准数据集相比,通常显示出更高的准确率。
BoW 模型的缺点
使用基于单词计数的 BoW 模型,我们将丢失其他信息,例如每个文本文档中附近单词周围的语义,结构,序列和上下文。 在 BoW 中,具有相似含义的单词将得到不同的对待。 其他文本模型是潜在语义索引(LSI),其中文档以低维度(k 远小于词汇量)-隐藏的主题空间表示。 在 LSI 中,文档中的组成词也可以表示为k
维密集向量。 据观察,在 LSI 模型中,具有相似语义的单词具有紧密的表示形式。 而且,单词的这种密集表示是将深度学习模型应用于文本的第一步,被称为单词嵌入。 基于神经网络的语言模型试图通过查看语料库中的单词序列来预测其相邻单词的单词,并在此过程中学习分布式表示,从而使我们能够密集地嵌入单词。
基准数据集
以下是大多数文本分类研究中使用的基准数据集的列表:
- IMDB 电影评论数据集:这是用于二元情感分类的数据集。 它包含一组用于训练的 25,000 条电影评论和用于测试的 25,000 条电影。 也有其他未标记的数据可供使用。 该数据集可从这个链接下载。
- 路透数据集:此数据集包含 90 个类别,9,584 个训练文档和 3,744 个测试文档。 它是包
nltk.corpus
的一部分。 该数据集中文档的类分布非常不正确,其中两个最常见的类包含大约所有文档的 70%。 即使仅考虑 10 个最频繁的类,该数据集中的两个最频繁的类也拥有大约 80% 的文档。 因此,大多数分类结果都是在这些最常见的类别的子集上进行评估的,它们在训练集中的最常见的 8、10 和 52 类别分别命名为 R8,R10 和 R52。 - 20 个新闻组数据集:此数据被组织成 20 个不同的新闻组,每个新闻组对应一个不同的主题。 一些新闻组彼此之间有着非常密切的关联(例如:
comp.sys.ibm.pc.hardware
/comp.sys.mac.hardware
),而其他新闻组则是高度不相关的(例如:misc.forsale
/soc.religion.christian
)。 这是 20 个新闻组的列表,根据主题分为六个主要类别。 该数据集在sklearn.datasets
中可用:
comp.graphics ,comp.os.ms-windows.misc ,comp.sys.ibm.pc.hardware ,comp.sys.mac.hardware ,comp.windows.x |
rec.autos ,rec.motorcycles ,rec.sport.baseball ,rec.sport.hockey |
sci.crypt ,sci.electronics ,sci.med ,sci.space |
misc.forsale |
talk.politics.misc ,talk.politics.guns ,talk.politics.mideast |
talk.religion.misc ,alt.atheism ,soc.religion.christian |
稍后我们将讨论如何加载此数据集以进行进一步分析。
单词表示
让我们看一下这些用于处理文本数据并从中提取有意义的特征或单词嵌入的高级策略,这些策略可用于其他机器学习(ML)系统中,以执行更高级的任务,例如分类,摘要和翻译。 我们可以将学习到的单词表示形式转移到另一个模型中。 如果我们拥有大量的训练数据,则可以与最终任务一起共同学习单词嵌入。
Word2vec 模型
该模型由 Google 于 2013 年创建,是一种基于深度学习的预测模型,该模型可计算并生成高质量,分布式和连续密集的单词向量表示,从而捕获上下文和语义相似性。 从本质上讲,这些是无监督模型,可以吸收大量文本语料库,创建可能单词的词汇表,并为代表该词汇表的向量空间中的每个单词生成密集的单词嵌入。 通常,您可以指定单词嵌入向量的大小,向量的总数本质上是词汇表的大小。 这使得该密集向量空间的维数大大低于使用传统 BoW 模型构建的高维稀疏向量空间。
Word2vec 可以利用两种不同的模型架构来创建这些词嵌入表示。 这些是:
- 连续词袋(CBOW)模型
- SkipGram模型
CBOW 模型架构尝试根据源上下文单词(环绕单词)来预测当前的目标单词(中心单词)。 考虑一个简单的句子the quick brown fox jumps over the lazy dog
,这可以是一对context_window
和target_word
,如果我们考虑一个大小为 2 的上下文窗口,我们有一些例子,像([quick, fox], brown)
,([the, brown], quick)
,([the, dog], lazy)
,依此类推。 因此,该模型尝试根据上下文窗口词来预测目标词。 Word2vec 系列模型是不受监管的; 这意味着您可以给它一个语料库,而无需附加标签或信息,并且它可以从语料库构建密集的单词嵌入。 但是,一旦有了这个语料库,您仍然需要利用监督分类方法。 但是我们将在没有任何辅助信息的情况下从语料库内部进行操作。 我们可以将此 CBOW 架构建模为深度学习分类模型,以便将上下文词作为输入X
,并尝试预测目标词Y
。 实际上,构建这种架构比跳过语法模型更简单,在该模型中,我们尝试从源目标词预测一大堆上下文词。
跳过语法模型架构通常尝试实现与 CBOW 模型相反的功能。 它尝试在给定目标词(中心词)的情况下预测源上下文词(环绕词)。 考虑一下前面的简单句子the quick brown fox jumps over the lazy dog
。 如果我们使用 CBOW 模型,则会得到(context_window
和target_word
)对,其中,如果考虑大小为 2 的上下文窗口,则有示例,类似([quick, fox], brown)
,([the, brown], quick)
,([the, dog], lazy)
等。 现在,考虑到跳过语法模型的目的是根据目标单词预测上下文,该模型通常会反转上下文和目标,并尝试根据其目标单词预测每个上下文单词。
因此,任务变为给定目标单词brown
来预测上下文[quick, fox]
,或给定目标单词quick
来预测上下文[the, brown]
,依此类推。 因此,模型试图基于target_word
来预测context_window
单词。
以下是前两个模型的架构图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-idoDL8V2-1681567330244)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-tl-py/img/03d09bc9-6d58-44b0-ae56-9fca79a2c304.png)]
我们可以在以下博客文章中找到这些模型在 Keras 中的实现。
使用 Gensim 的 Word2vec
Radim Rehurek 创建的 gensim 框架由 Word2vec 模型的可靠,高效且可扩展的实现组成。 它使我们可以选择跳跃语法模型或 CBOW 模型之一。 让我们尝试学习和可视化 IMDB 语料库的词嵌入。 如前所述,它有 50,000 个带标签的文档和 50,000 个无标签的文档。 对于学习单词表示,我们不需要任何标签,因此可以使用所有可用的 100,000 个文档。
首先加载完整的语料库。 下载的文档分为train
,test
和unsup
文件夹:
def load_imdb_data(directory = 'train', datafile = None): ''' Parse IMDB review data sets from Dataset from http://ai.stanford.edu/~amaas/data/sentiment/ and save to csv. ''' labels = {'pos': 1, 'neg': 0} df = pd.DataFrame() for sentiment in ('pos', 'neg'): path =r'{}/{}/{}'.format(config.IMDB_DATA, directory, sentiment) for review_file in os.listdir(path): with open(os.path.join(path, review_file), 'r', encoding= 'utf-8') as input_file: review = input_file.read() df = df.append([[utils.strip_html_tags(review), labels[sentiment]]], ignore_index=True) df.columns = ['review', 'sentiment'] indices = df.index.tolist() np.random.shuffle(indices) indices = np.array(indices) df = df.reindex(index=indices) if datafile is not None: df.to_csv(os.path.join(config.IMDB_DATA_CSV, datafile), index=False) return df
我们可以将所有三个数据源结合起来,得到 100,000 个文档的列表,如下所示:
corpus = unsupervised['review'].tolist() + train_df['review'].tolist() + test_df['review'].tolist()
Python 迁移学习实用指南:6~11(2)https://developer.aliyun.com/article/1426854