全文5179字,预计学习时间10分钟。
深度学习正迅速成为人工智能应用的关键工具。比如在计算机视觉、自然语言处理、语音识别等领域,深度学习已经取得了令人瞩目的成就。因此,人们对深度学习越来越感兴趣。
深度学习最突出的问题之一是图像分类。图像分类的目的是根据潜在的类别对特定的图像进行分类。图像分类的一个经典例子是在一组图像中识别猫和狗。
从深度学习的角度来看,图像分类的问题可以通过迁移学习来解决。事实上,图像分类中的几个最新研究成果都是基于迁移学习方案的。
本文将介绍如何在图像分类中实现迁移学习解决方案。本文提供的实现基于Keras,使用Python编程语言。通过实施这一方案,你将能够快速而容易地解决任何图像分类问题。
目录
1.迁移学习
2.卷积神经网络
3.重用预训练模型。
4.转移学习过程
5.深度卷积神经网络分类器
6.例子
7.摘要
1.迁移学习
迁移学习是计算机视觉中一种流行的学习方法。通过迁移学习,可以在节省时间的基础上建立准确的模型。在迁移学习中,可以从解决不同问题时遇到的已知模式开始学习,而不是从零开始。这样就可以学以致用,避免从头开始。学习可以看作是沙特尔所说的“站在巨人肩膀上”的深度学习版本。
在计算机视觉中,迁移学习通常采用预训练模型来实现。预训练模型是指在大型基准数据集上进行问题解决训练的模型,用于训练的问题与我们实际想要解决的问题相似。由于这类模型的计算量和训练成本都很低,所以比较常见的应用是在已发表的文献中导入和使用这类模型。
2.卷积神经网络
迁移学习的几种预训练模型是基于大型卷积神经网络的。总的来说,CNN在各种计算机视觉任务中表现良好。其高性能和易于训练是近年来CNN受欢迎的两个主要因素。
典型的CNN由两部分组成:
1.卷积基,由一堆卷积层和池层组成。卷积的主要目的是从图像中生成特征。
2.分类器,通常由全连接层组成。分类器的主要功能是根据检测到的特征对图像进行分类。全层是指神经元与前一层的所有激活完全连接的层。
图1是基于CNN的模型结构。请注意,这是一个简化版本,符合本文的目的。实际上,这种模型的结构比这里列出的要复杂得多。
图1基于CNN的模型结构
这些深度学习模型的一个重要特征是,它们可以自动学习分层特征表示。这意味着第一层计算的特征是通用的,可以在不同的问题域中重用,而最后一层计算的特征是特定的,取决于选择的数据集和任务。有专家认为:“如果第一层特征是通用的,最后一层特征是特定的,那么在网络的某个地方,就需要从通用特征过渡到特定特征。”
所以CNN的卷积基础,尤其是它的下层,指的是一般的特征;CNN的分类器部分和卷积基中的一些更高层指的是特殊特征。
3.重用预训练模型。
当您根据自己的需要重用预训练模型时,必须先删除原来的分类器,然后添加一个新的符合您需要的分类器。最后,您必须根据以下三种策略之一微调模型:
1.训练整个模型。在这种情况下,您将使用预训练模型的体系结构,并根据您的数据集对其进行训练。由于您将从头开始学习模型,因此需要一个大型数据集。
2.训练一些层,保持其他层冷冻。如前所述,下层指的是一般特性,上层指的是具体特性。这里,我们通过调整网络权重来实现这种二分法。通常,如果你有一个小的数据集和大量的参数,你需要留下更多的冻结层,以避免过拟合。相反,如果数据集大,参数个数少,可以在新任务中训练更多的层来改进模型,因为这种情况下不会出现过拟合。
3.冻结卷积基数。这种情况对应的是训练权衡或冻结权衡的极端情况。主要思想是保持卷积基的原始形式,然后将其输出提供给分类器。当您使用预训练模型作为固定的特征提取机制时,如果您缺乏计算能力,数据集很小,和/或预训练模型解决了您想要解决的问题,这种方法将非常有用。
图2是这三种策略的流程图:
图2微调策略
与直接策略3不同,使用策略1和策略2时,需要注意卷积部分使用的学习速率。学习率是一个超级参数,控制着你调整网络权重的大小。当你使用基于CNN的预训练模型时,最好保持较低的学习率,因为较高的学习率会增加丢失之前获得的知识的风险。假设预训练模型训练良好,保持较低的学习速率将确保您不会过早和过度地扭曲CNN权重。
4.转移学习过程
从实践的角度来看,整个迁移过程可以总结如下:
1.选择预训练模型。您可以从各种可用的预培训模型中选择合适的模型。例如,如果您正在使用Keras,您可以立即访问VGG、InceptionV3和ResNet5等模型。
2.根据大小相似矩阵对问题进行分类。图3中的“矩阵”会影响您的选择。该矩阵根据数据集的大小以及与用于训练预训练模型的数据集的相似性对计算机视觉问题进行分类。根据经验法则,如果每个类中的图像少于1000个,则数据集被认为是小的。而数据集的相似度可以用常识来判断。例如,如果你的任务是识别猫和狗,那么ImageNet就是一个类似的数据集,因为它可以识别猫和狗的图像。但是,如果你的任务是识别癌细胞,ImageNet就不能算是类似的数据集。
3.微调模型。这里可以使用大小-相似度矩阵来辅助选择,然后参考前面提到的关于重用预训练模型的三个选项。图4是以下文本的图像摘要。
象限1:大型数据集,但不同于预训练模型的数据集。在这种情况下,需要采用策略1。由于您有一个大型数据集,您可以从头开始训练模型,并执行任何您想要的操作。尽管数据集不同,但在实践中,通过使用架构和权重来初始化预训练模型仍然是有用的。
象限2:大数据集和类似于预训练模型的数据集。此时,任何选项都适用,但最有效的选项很可能是策略2。因为数据集大,不会有过拟合,想学多少就学多少。但是,由于数据集是相似的,所以利用以前的知识,我们可以省去很多工作。所以我们只需要训练分类器和卷积基的顶层。
象限3:小数据集与预训练模型的数据集不同。这样的数据集必然会引起计算机视觉问题。一切都和你作对。这个时候抱怨是没有用的。唯一的希望是选择策略2。你很难平衡需要训练和冷冻的层数。如果你学得太深,你的模型可能会被拟合。如果停留在模型的浅层,学不到什么有用的东西。或许,与象限2相比,你需要更进一步,使用数据增强技术。
象限4:小数据集,但类似于预训练模型的数据集。当我问尤达大师对此有何看法时,他告诉我,“策略3是这种数据集的最佳选择”。虽然我不认识他,但我不会低估他的“力”。因此,我选择策略3。你只需要去掉最后一个全连通层,运行预训练模型作为固定特征提取器,然后用之前获得的特征训练一个新的分类器。
图3和图4用于微调预训练模型的大小相似性矩阵和决策图
5.深度卷积神经网络分类器
如上所述,基于预训练卷积神经网络的迁移学习方法生成的图像分类模型通常由两部分组成:
1.卷积基础,用于执行特征提取。
2.分类器,根据卷积基提取的特征对输入图像进行分类。
由于我们在本节中主要关注分类器,我们必须首先说明有许多方法可以构建分类器,其中最常用的有:
1.全连接层。处理图像分类问题最好的方法就是用一堆全连通的图层,然后用Softmax激活图层。MAX层输出每个可能类别标签上的概率分布,然后我们只需要按照最可能的类别对图像进行分类。
2.全球平均池。有人提出了基于全局平均的池化方法。使用这种方法,我们不需要在卷积基础上添加全连接层,而是添加全局平均池层,并将其输出直接馈入softmax激活层。
3.线性支持向量机。线性支持向量机是另一种可以用来建立分类器的方法。通过训练线性SVM分类器对卷积基提取的特征进行分类,可以提高分类精度。本文将进一步详细介绍SVM法的优缺点。
6.例子
在这个例子中,我们将探讨如何将每个分类器应用到图像分类的迁移学习解决方案中。“比较不同分类器在深度卷积神经网络上的性能仍需进一步研究,从而形成一个有趣的研究方向”。因此,观察每个分类器在标准图像分类问题中的性能将是有趣的。
6.1.准备数据
在这个例子中,我们将使用一个较小版本的原始数据集。这将帮助我们更快地运行模型,对于计算能力有限的人来说也是一件好事。
为了构建一个较小版本的数据集,我们可以修改前面的代码,如代码1所示:
#为狗和猫创建更小的数据集
导入操作系统,shutil
original _ dataset _ dir = '/Users/macbook/dogs _ cats _ dataset/train/'
base _ dir = '/Users/macbook/book/dogs _ cats/data '
如果不是os.path.exists:
os.mkdir
#创建目录
train_dir = os.path.join
如果不是os.path.exists:
os.mkdir
validation_dir = os.path.join
如果不是os.path.exists:
os.mkdir
test_dir = os.path.join
如果不是os.path.exists:
os.mkdir
train_cats_dir = os.path.join
如果不是os.path.exists:
os.mkdir
train_dogs_dir = os.path.join
如果不是os.path.exists:
os.mkdir
validation _ cats _ dir = OS . path . join
如果不是os.path.exists:
os.mkdir
validation _ dogs _ dir = OS . path . join
如果不是os.path.exists:
os.mkdir
test_cats_dir = os.path.join
如果不是os.path.exists:
os.mkdir
test_dogs_dir = os.path.join
如果不是os.path.exists:
os.mkdir
#将前1000张猫图片复制到train_cats_dir
fnames = ['猫。{}.jpg。I在范围内的格式]
对于fnames中的fname:
src = os.path.join
dst = os.path.join
shutil.copyfile
#将接下来的500张猫图片复制到validation_cats_dir
fnames = ['猫。{}.jpg。I在范围内的格式]
对于fnames中的fname:
src = os.path.join
dst = os.path.join
shutil.copyfile
#将接下来的500张猫图片复制到test_cats_dir
fnames = ['猫。{}.jpg。I在范围内的格式]
对于fnames中的fname:
src = os.path.join
dst = os.path.join
shutil.copyfile
#将前1000张狗图片复制到train_dogs_dir
fnames = ['狗。{}.jpg。I在范围内的格式]
对于fnames中的fname:
src = os.path.join
dst = os.path.join
shutil.copyfile
#将接下来的500张狗图片复制到validation_dogs_dir
fnames = ['狗。{}.jpg。I在范围内的格式]
对于fnames中的fname:
src = os.path.join
dst = os.path.join
shutil.copyfile
#将接下来的500张狗图片复制到test_dogs_dir
fnames = ['狗。{}.jpg。I在范围内的格式]
对于fnames中的fname:
src = os.path.join
dst = os.path.join
shutil.copyfile
#健全性检查
打印))
打印))
打印))
打印))
打印))
打印))
1个代码,为猫狗大战构建一个更小的数据集
6.2.从卷积基中提取特征
卷积基础将用于提取特征。这些特征会被输入到我们要训练的分类器中,这样我们就可以识别出图像中是有狗还是有猫。
同样,我们修改了代码。详情见代码2:
# Extrac特性
导入操作系统,shutil
来自keras.preprocessing.image导入图像数据生成器
datagen = ImageDataGenerator
batch_size = 32
定义提取_特征:
features = np.zeros) #必须等于卷积基的输出
标签= np.zeros)
#预处理数据
generator = data gen . flow _ from _ directory,
批处理大小=批处理大小,
class_mode='binary ')
#通过卷积基数传递数据
i = 0
对于生成器中的inputs_batch、labels_batch:
features _ batch = conv _基地.预测
功能[i *批处理大小:*批处理大小] =功能批处理
标签[i *批量大小:*批量大小] =标签批量
i += 1
如果i *批量大小> =样本计数:
破裂
返回特征、标签
train_features,train_labels = extract_features #符合我们的小数据集规模
验证_要素,验证_标签=提取_要素
测试特征,测试标签=提取特征
从代码卷积库中提取的2个特征
6.3.分类者
6.3.1.全连接层
我们的第一个解决方案基于全连接层。一堆完全连接的层被添加到分类器,并且这些层由从卷积基提取的特征给出。
为简单起见,我们将使用Chollet提出的解决方案,并稍加修改。
Code 3是这个方案中使用的代码,图5和图6是它的学习曲线。
#定义模型
从keras导入模型
从keras导入层
从keras进口优化
纪元= 100
模特=模特。连续的
model.add))
model.add))
model.add)
model.add)
模型.摘要
#编译模型
模型.编译,
损失= '二元交叉熵',
度量=['acc'])
#列车型号
历史=模型.拟合)
3代码全连接层解决方案
图5全连接层解决方案的精度
图6全连接层解决方案的损失函数
结果的简要总结:
1.验证精度约为0.85。考虑到数据集的大小,这个结果还不错。
2.这个模型太合身了。训练曲线和验证曲线差距很大。
3.由于我们已经使用了dropout层,我们应该增加数据集的大小来改善结果。
6.3.2.全球平均池
与前一种情况不同,这里我们将添加一个全局平均池层,并将其输出提供给一个sigmoid激活层,而不是添加一堆完全连接的层。
注意,我们讨论的是sigmoid激活层,而不是softmax激活层。我们选择sigmoid作为激活函数,因为在Keras中,为了执行二元分类,sigmoid应该被用作激活函数,而binary_crossentropy应该被用作损失函数。
代码4是构建分类器的代码,图7和图8是结果学习曲线。
#定义模型
从keras导入模型
从keras导入层
从keras进口优化
纪元= 100
模特=模特。连续的
model.add))
model.add)
模型.摘要
#编译模型
模型.编译,
损失= '二元交叉熵',
度量=['acc'])
#列车型号
历史=模型.拟合)
4代码全球平均池解决方案
图7全球平均池解决方案的准确性
图8全球平均池解决方案的损失函数
结果的简要总结:
1.验证精度类似于通过全连接层解决方案获得的精度。
2.这款没有之前的过拟合现象。
3.当模型停止训练时,损失函数仍在减小。也许可以通过增加历元数来改进模型。
6.3.3.线性支持向量机
这里我们将根据卷积基提取的特征训练一个线性支持向量机分类器。
对于这种分类器的训练,传统的机器学习方法是首选。因此,我们将使用K倍交叉验证方法来估计分类器的误差。在使用K折交叉验证方法时,我们可以将训练集与验证集连接起来,以扩展训练数据。代码5显示了连接数据的过程。
#连接训练集和验证集
svm_features = np.concatenate)
svm_labels = np.concatenate)
5代码数据连接
最后,我们必须知道SVM分类器有一个超级参数。这个超级参数是误差项的惩罚参数C。为了优化这个超级参数的选择,我们将使用穷举网格搜索。代码6是用来构建这个分类器的代码,图9是它的学习曲线。
#构建模型
导入sklearn
从sklearn.cross_validation导入train_test_split
从sklearn.grid_search导入GridSearchCV
从sklearn.svm导入LinearSVC
X_train,y _ train = svm _ features.reshape,svm_labels
param = [{
" C": [0.01,0.1,1,10,100]
}]
svm = LinearSVC #与Tang中的一样
clf = GridSearchCV
clf.fit
6码线性SVM解
图9线性SVM解的精度
结果的简要总结:
1.模型的精度约为0.86,与之前的解接近。
2.过度装配发生在拐角处。此外,训练精度总是1.0,这是不寻常的,可以视为过度拟合的标志。
3.模型的准确性应该随着训练样本数量的增加而增加。然而,事实并非如此,这可能是由于过度拟合。当数据集增加时,模型将如何反应?这是一个有趣的问题。
7.摘要
在本文中,我们:
1.介绍了迁移学习、卷积神经网络和预训练模型的概念。
2.定义了重新调整预训练模型的基本微调策略。
3.描述了一种基于数据集的大小和相似性来决定应该使用哪种微调策略的结构化方法。
4.列出了可用于从卷积基提取的特征之上的三种分类器。
5.为本文中列出的三个分类器提供端到端的分类示例。
留言发朋友圈。
我们一起分享AI学习和开发的干货。
王玲、魏编译
如需转载,请在后台留言,遵循转载规范。