必须使用非常少的数据训练图像分类模型是一种常见情况,如果您在专业环境中进行计算机视觉,则在实践中可能会遇到这种情况。“少数”样本可以表示从几百到几万个图像的任何地方。作为一个实际例子,我们将重点放在将图像分类为狗或猫的数据集中,其中包含4,000张猫狗图片(2,000只猫,2,000只狗)。我们将使用2,000张图片进行培训 - 1,000张用于验证,1,000张用于测试。
深度学习与小数据问题的相关性
您有时会听到深度学习仅在有大量数据可用时才有效。这部分是有效的:深度学习的一个基本特征是它可以自己在训练数据中找到有趣的特征,而不需要手动特征工程,这只有在有大量训练样例可用时才能实现。对于输入样本非常高维的问题(如图像)尤其如此。
但是,构成大量样本的是相对的 - 相对于您尝试训练的网络的大小和深度,对于初学者而言。仅用几十个样本就不可能训练一个回旋来解决一个复杂的问题,但如果模型很小且规范化并且任务很简单,那么几百个就足够了。因为convnets学习本地的,翻译不变的特征,所以它们在感知问题上具有高度数据效率。尽管相对缺乏数据,但无需任何自定义功能工程,即使在非常小的图像数据集上从头开始训练预测仍将产生合理的结果。您将在本节中看到这一点。
更重要的是,深度学习模型本质上是高度可再利用的:例如,您可以采用在大规模数据集上训练的图像分类或语音到文本模型,并将其重新用于一个显着不同的问题,只需进行微小的更改。具体而言,在计算机视觉的情况下,许多预训练模型(通常在ImageNet数据集上训练)现在可公开下载,并可用于从非常少的数据中引导强大的视觉模型。这就是你将在下一节做的。让我们从掌握数据开始吧。
下载数据
您将使用的Dogs vs. Cats数据集未与Keras打包在一起。它是由Kaggle在2013年底作为计算机视觉竞赛的一部分提供的,当时回归不是主流。您可以从下载原始数据集(如果您还没有,则需要创建一个Kaggle帐户 - 不用担心,这个过程是无痛的)。
图片是中等分辨率的彩色JPEG。这里有些例子:
不出所料,2013年的狗与猫之间的Kaggle比赛是由使用过小行星的参赛者赢得的。最佳条目达到了95%的准确率。即使你将用不到10%的竞争对手可用数据训练你的模型,你的最终精度也会达到97%。
该数据集包含25,000张狗和猫的图像(每类12,500张),543 MB(压缩)。下载并解压缩后,您将创建一个包含三个子集的新数据集:每个类包含1,000个样本的训练集,每个类500个样本的验证集,以及每个类500个样本的测试集。
以下是执行此操作的代码:
使用预训练的convnet
在小图像数据集上深入学习的一种常见且高效的方法是使用预训练网络。一个预训练的网络是一个先前在大型数据集上训练的已保存网络,通常是在大规模图像分类任务上。如果这个原始数据集足够大且足够通用,则预训练网络学习的特征的空间层次结构可以有效地充当视觉世界的通用模型,因此其特征可以证明对许多不同的计算机视觉问题有用,甚至虽然这些新问题可能涉及与原始任务完全不同的类。例如,您可以在ImageNet上训练网络(其中类主要是动物和日常物品),然后将这个训练有素的网络重新用于远程识别图像中的家具物品。与许多老年人相比,学习功能在不同问题上的这种可移植性是深度学习的关键优势,
在这种情况下,让我们考虑一个在ImageNet数据集上训练的大型预测网(140万个标记图像和1,000个不同类)。ImageNet包含许多动物类别,包括不同种类的猫和狗,因此您可以期望在狗与猫的分类问题上表现良好。
您将使用由Karen Simonyan和Andrew Zisserman于2014年开发的 ; 它是ImageNet的简单且广泛使用的convnet架构。虽然它是一个较旧的模型,远离当前的技术水平,并且比其他许多近期模型稍微重一些,但我之所以选择它,是因为它的架构类似于您已经熟悉的并且在不引入任何新概念的情况下易于理解。这可能是您第一次遇到这些可爱的模型名称之一 - VGG,ResNet,Inception,Inception-ResNet,Xception等等; 你会习惯他们,因为如果你继续深入学习计算机视觉,他们会经常出现。
有两种方法可以使用预训练网络:特征提取和微调。我们将涵盖它们。让我们从特征提取开始。
特征提取
特征提取包括使用先前网络学习的表示来从新样本中提取感兴趣的特征。然后,这些功能将通过一个新的分类器运行,该分类器从头开始训练。
如前所述,用于图像分类的网络包含两部分:它们以一系列池化和卷积层开始,它们以密集连接的分类器结束。第一部分称为模型的卷积基。在小行星的情况下,特征提取包括获取先前训练的网络的卷积基础,通过它运行新数据,以及在输出之上训练新的分类器。
为什么只重用卷积基数?您是否可以重复使用密集连接的分类器?一般来说,应该避免这样做。原因是卷积基础学习的表示可能更通用,因此更具可重用性:信号网的特征图是图片上一般概念的存在图,无论计算机视觉如何,这都可能是有用的手头的问题。但是,分类器学习的表示必然特定于训练模型的类集 - 它们将仅包含关于整个图像中该类或该类的存在概率的信息。此外,在密集连接的图层中找到的表示不再包含有关位置的任何信息对象位于输入图像中:这些图层摆脱了空间的概念,而对象位置仍然由卷积特征图描述。对于对象位置很重要的问题,密集连接的特征在很大程度上是无用的。
注意,由特定卷积层提取的表示的一般性(以及因此可重用性)的级别取决于模型中的层的深度。模型中较早出现的图层会提取局部的,高度通用的特征贴图(例如可视边缘,颜色和纹理),而较高层的图层会提取更抽象的概念(例如“猫耳朵”或“狗眼”) 。因此,如果您的新数据集与训练原始模型的数据集有很大不同,那么最好只使用模型的前几层来进行特征提取,而不是使用整个卷积基础。
在这种情况下,因为ImageNet类集包含多个dog和cat类,所以重用原始模型的密集连接层中包含的信息可能是有益的。但是我们将选择不这样做,以便涵盖新问题的类集不与原始模型的类集重叠的更一般情况。
让我们通过使用在ImageNet上训练的VGG16网络的卷积基础来实现这一点,从猫和狗图像中提取有趣的特征,然后在这些特征之上训练狗与猫的分类器。
除其他外,VGG16型号还预装了Keras。以下是作为Keras一部分提供的图像分类模型列表(所有在ImageNet数据集上预训练):
-
Xception
-
初始V3
-
ResNet50
-
VGG16
-
VGG19
-
MobileNet
让我们实例化VGG16模型。
您将三个参数传递给函数:
-
weights 指定从中初始化模型的权重检查点。
-
include_top“密集连接”是指在网络顶部包括(或不包括)密集连接的分类器。默认情况下,此密集连接的分类器对应于ImageNet的1,000个类。因为您打算使用自己的密集连接分类器(只有两个类:cat和dog),所以您不需要包含它。
-
input_shape是您将提供给网络的图像张量的形状。这个参数纯粹是可选的:如果你不传递它,网络将能够处理任何大小的输入。
以下是VGG16卷积基础架构的详细信息。它类似于你已经熟悉的简单的网络:
最终的特征图具有形状(4, 4, 512)。这是最重要的功能,你将坚持密集连接的分类器。
此时,有两种方法可以继续:
-
在数据集上运行卷积基,将其输出记录到磁盘上的数组,然后将此数据用作独立的,密集连接的分类器的输入,类似于本书第1部分中的分类。这种解决方案运行快速且便宜,因为它只需要为每个输入图像运行一次卷积基数,而卷积基数是管道中最昂贵的部分。但出于同样的原因,这种技术不允许您使用数据扩充。
-
conv_base通过在顶部添加密集层来扩展您拥有的模型(),并在输入数据上端到端地运行整个事物。这将允许您使用数据增强,因为每次输入图像都会在模型看到时通过卷积基础。但出于同样的原因,这种技术比第一种技术昂贵得多。
在这篇文章中,我们将详细介绍第二种技术(在我们涵盖两者中的书中)。请注意,这种技术非常昂贵,只有在您可以访问GPU时才应该尝试它 - 它在CPU上绝对难以处理。
使用数据扩充进行特征提取
由于模型的行为与图层类似,因此您可以像添加图层一样将模型(如conv_base)添加到顺序模型中。
这就是模型现在的样子:
如您所见,VGG16的卷积基数有14,714,688个参数,非常大。您在顶部添加的分类器有200万个参数。
在编译和训练模型之前,冻结卷积基数非常重要。冻结一层或一组图层意味着在训练期间防止其权重被更新。如果不这样做,则在训练期间将修改先前由卷积基础学习的表示。因为顶部的密集层是随机初始化的,所以非常大的权重更新将通过网络传播,从而有效地破坏先前学习的表示。
在Keras中,您使用以下freeze_weights()函数冻结网络:
使用此设置,只会训练您添加的两个密集层的权重。这是总共四个权重张量:每层两个(主要权重矩阵和偏向量)。请注意,为了使这些更改生效,您必须首先编译模型。如果您在编译后修改了重量训练,则应重新编译模型,否则将忽略这些更改。
使用数据扩充
过度拟合是由于过多的样本需要学习,导致无法训练可以推广到新数据的模型。给定无限数据,您的模型将暴露于手头数据分布的每个可能方面:您永远不会过度拟合。数据增强采用从现有训练样本生成更多训练数据的方法,通过大量随机变换来增加样本,从而产生可信的图像。目标是在训练时,你的模型永远不会看到两次完全相同的图片。这有助于将模型暴露于数据的更多方面并更好地概括。
在Keras中,这可以通过配置要对由a读取的图像执行的多个随机变换来完成image_data_generator()。例如:
这些只是一些可用选项(更多信息,请参阅Keras文档)。让我们快速浏览一下这段代码:
-
rotation_range 是一个度数(0-180)的值,一个随机旋转图片的范围。
-
width_shift并且height_shift是在垂直或水平方向上随机平移图片的范围(作为总宽度或高度的一部分)。
-
shear_range 用于随机应用剪切变换。
-
zoom_range 用于随机缩放图片内部。
-
horizontal_flip 用于水平地随机翻转一半图像 - 当没有水平不对称假设时相关(例如,真实世界的图片)。
-
fill_mode 是用于填充新创建的像素的策略,可以在旋转或宽度/高度偏移后出现。
现在我们可以使用图像数据生成器训练我们的模型:
让我们绘制结果。如您所见,您的验证准确率达到约90%。
微调
另一种广泛使用的模型重用技术,是对特征提取的补充,是微调微调包括解冻用于特征提取的冻结模型库的一些顶层,并联合训练模型的新添加部分(在这种情况下,完全连接的分类器)和这些顶层。这称为微调,因为它稍微调整了重复使用的模型的抽象表示,以使它们与手头的问题更相关。
我之前说过,有必要冻结VGG16的卷积基础,以便能够在顶部训练一个随机初始化的分类器。出于同样的原因,只有在顶部的分类器已经训练之后,才有可能微调卷积基的顶层。如果分类器尚未经过训练,那么在训练期间通过网络传播的误差信号将太大,并且先前由被微调的层学习的表示将被破坏。因此,微调网络的步骤如下:
-
在已经训练过的基础网络上添加自定义网络。
-
冻结基础网络。
-
训练你添加的部分。
-
解冻基础网络中的某些层。
-
联合训练这些层和您添加的部分。
在进行特征提取时,您已经完成了前三个步骤。让我们继续第4步:您将解冻您的内容conv_base,然后冻结其中的各个图层。
提醒一下,这就是你的卷积基础:
您将从中和之后微调所有图层block3_conv1。为什么不微调整个卷积基数?你可以。但您需要考虑以下事项:
-
卷积基础中的早期层编码更通用,可重复使用的特征,而更高层的层编码更专用的特征。微调更专业的功能更有用,因为这些功能需要在新问题上重新使用。微调下层会有快速下降的回报。
-
您训练的参数越多,您就越有可能过度拟合。卷积基数有1500万个参数,因此尝试在小数据集上训练它会有风险。
因此,在这种情况下,仅对微卷基础中的一些层进行微调是一种很好的策略。让我们设置一下,从上一个示例中的中断处开始。
unfreeze_weights(conv_base,from =“block3_conv1”)
现在您可以开始微调网络了。您将使用RMSProp优化器以非常低的学习速率执行此操作。使用低学习率的原因是您希望限制对您正在微调的三个图层的表示所做的修改的幅度。更新太大可能会损害这些表示。
让我们绘制结果:
你可以看到准确度有6%的绝对提升,从大约90%到高于96%。
请注意,损失曲线没有显示任何真正的改善(事实上,它正在恶化)。您可能想知道,如果损失没有减少,准确度如何保持稳定或改善?答案很简单:你展示的是点数损失值的平均值; 但是对于准确性而言重要的是损失值的分布,而不是它们的平均值,因为精度是模型预测的类概率的二元阈值的结果。即使没有反映在平均损失中,该模型仍可能会有所改善。
您现在可以最终在测试数据上评估此模型:
在这里,您可以获得96.5%的测试精度。在围绕这个数据集的原始Kaggle比赛中,这将是最好的结果之一。
还有问题吗?联系我们!
-中国专业的第三方数据服务提供商,提供定制化的一站式数据挖掘和统计分析咨询服务
统计分析和数据挖掘咨询服务:(咨询服务请联系)
【服务场景】
科研项目; 公司项目外包;线上线下一对一培训;数据采集;学术研究;报告撰写;市场调查。
【大数据部落】提供定制化的一站式数据挖掘和统计分析咨询服务