本教程改编自2019年的Fastai DL第1课,并添加了我的许多补充息争释。

在本教程之后,您将能够在您选择的任何图像数据集上构建和演习图像识别器,同时充分理解底层模型架构和演习过程。

本教程包括:

数据提取数据可视化模范演习:CNN、ResNets、迁移学习结果阐明模型层的冻结和解冻微调:学习速率查找器,一周期策略

本教程紧张针对的是一些深度学习实践者,任何想要利用CNN和ResNets“刷”图像分类根本知识的人,或任何没有利用过fastai库并希望试用它的人。

这是一个关于图像识别的正式教程

本教程的条记本也可以在这里找到。
Fastai-iNotes-iTutorials/Image_Recognition_Basics.ipynb at master · SalChem/Fastai-iNotes-iTutorials · GitHub

要运行条记本,您只需利用Google Colab打开它即可。

条记本电脑都是独立的,没有bug,以是你可以按原样运行它。

进入Colab后,请务必变动以下内容以启用GPU后端,

运行时 - >变动运行时类型 - >硬件加速器 - > GPU

本教程中的代码简要解释。
有关任何类、方法等的进一步文档可以在fastai docs| fastai上找到。

我们开始吧…

设置IPython内核并初始化

导入必要的库,

我们做一些初始化,

bs是我们的批量大小,即一次输入模型的演习图像的数量。
模型参数会在每批迭代之后更新。

例如,如果我们有640个图像,我们的批量大小为64;参数将在1个历元的过程中更新10次。

如果您在本教程中的某个时候碰着内存不敷的情形,则较小的批处理大小可以供应帮助,批次大小常日是2的倍数。

利用特定的值初始化上面的伪随机数天生器可使系统稳定,从而产生可重现的结果。

1.数据提取

我们将利用的数据集是Oxford-IIIT Pet Dataset,它可以利用fastai数据集模块检索。

URLs.PETS是数据集的URL,它有12个猫品种和25个狗品种,untar_data解压并将数据文件下载到我们的路径中。

get_image_files获取images目录中包含的所有文件的路径,并将它们存储到fnames中。
来自fnames的实例如下所示,

PosixPath('/home/jupyter/.fastai/data/oxford-iiit-pet/images/scottish_terrier_119.jpg')

由于每个图像的标签都包含在图像文件名中,我们将利用正则表达式来提取它。
正则表达式(常日缩写为正则表达式)是描述一定量文本的模式。
我们提取图像标模式如下,

末了一步是特定于此数据集的。
例如,如果属于同一类的图像位于同一文件夹中,我们不必担心它。

现在让我们创建我们的演习和验证数据集,

ImageDataBunch根据路径path_img中的图像创建演习数据集train_ds和验证数据集valid_ds。

from_name_re利用在编译表达式模式pat后得到的正则表达式从文件名称列表中获取标签。

df_tfms是动态运用于图像的转换。
在这里,图像将调度为224x224,居中,裁剪和缩放。
这种转换是数据增强的实例,已经证明在打算机视觉中很有前景。
此类转换不会变动图像内部的内容,但会变动其像素值以得到更好的模型泛化。

normalize利用ImageNet图像的标准偏差和均匀值对数据进行标准化。

2.数据可视化

演习数据样本表示为

(Image (3, 224, 224), Category scottish_terrier)

个中第一个元素表示图像3RGB通道、行和列。
第二个元素是图像标签。

这个实例的相应图像是,

len(data.train_ds)和len(data.valid_ds)分别输出演习和验证样本的数量5912和1478。

data.c和data.classes分别输出类及其标签的数量。
有37个种别,以下标签,

['Abyssinian', 'Bengal', 'Birman', 'Bombay', 'British_Shorthair', 'Egyptian_Mau', 'Maine_Coon', 'Persian', 'Ragdoll', 'Russian_Blue', 'Siamese', 'Sphynx', 'american_bulldog', 'american_pit_bull_terrier', 'basset_hound', 'beagle','boxer', 'chihuahua', 'english_cocker_spaniel', 'english_setter', 'german_shorthaired', 'great_pyrenees', 'havanese', 'japanese_chin', 'keeshond', 'leonberger', 'miniature_pinscher', 'newfoundland', 'pomeranian', 'pug', 'saint_bernard', 'samoyed', 'scottish_terrier', 'shiba_inu', 'staffordshire_bull_terrier', 'wheaten_terrier', 'yorkshire_terrier']

show_batch在批处理中显示少量的图像。

3.模型演习

cnn_learner利用来自给定架构的预演习模型构建CNN学习器。
来自预演习模型的学习参数用于初始化我们的模型,许可更快的收敛和高精度。

这里利用的CNN架构是ResNet34,它在过去几年中取得了巨大成功,仍旧被认为是最前辈的。

谈论CNN和ResNets非常有代价,由于这将有助于我们更好地理解我们的演习流程。
我们可以?

CNNs简而言之:

首先,什么是卷积神经网络(CNN或convNet)?我们可以将ConvNet视为将图像卷转换为输出卷的图层列表,就像本教程中的情形一样,它可以是一个类得分。
这些层由连接到前一层的其他神经元的神经元组成。
为了你更好的进行阅读,我这里强烈推举斯坦福大学CS231课程的卷积神经网络。

范例的CNN架构

该图展示了一个范例的convNet架构。
我们可以将所有CNN架构视为不同可微函数(卷积、下采样和仿射变换)的各种组合。
上图只有很少的层,但深层网络有几十到几百层。

ResNets简而言之:

深度网络中一个非常普遍的问题是降级问题,个中模型精度达到饱,然后迅速降级。
这是违反直觉的,由于我们期望附加层该当能够实现更详细和抽象的表示。
这个问题正是ResNets旨在办理的问题,由于它们可以安全地优化演习更深层次的网络,而不必担心降级问题。

ResNets办理降级问题的方法是引入“身份快捷连接”,跳过一个或多个层。
跳过连接的输出被添加到堆叠层的输出中,如下图所示。
跳过连接有效地跳过某些层上的学习过程,使深层网络在某种程度上也充当浅层网络。

Skip函数创建所谓的残差块,图中的F(x),这便是残差网这个名称的由来。
传统网络的目标旨在直接学习输出H(x),而ResNets旨在学习残差F(x)。
使F(x)= 0许可网络跳过该子网,如H(x)= x。

已经表明,添加这些身份映射许可模型更深入而不降落性能,并且这种网络比普通堆叠层更随意马虎优化。
ResNets有几种变体,例如ResNet50、ResNet101、ResNet152; ResNet编号表示ResNet网络的层数(深度)。

在本教程中,我们利用的是ResNet34,如下所示,

ResNet34的体系构造和卷积内核

在图中,底部数字表示输入或特色舆图大小(高度x宽度),上面的数字表示通道数(过滤器数量)。
例如,第一个左块表示输入图像(224 x 224 x 3)。
图中的每个“层”都包含很少的残余块,这些残余块又包含具有不同可微函数的堆叠层,从而导致34层端到端。
下面是ResNet34架构的完全底层布局,与类似的普通架构比较;侧箭头表示身份连接。

平面34层CNN(左)和34层ResNet(右)

您可以随意考试测验任何其他resnet,只需更换模型即可。
resnet34by模型。
resnet50或任何其他所需的架构。
请记住,增加层数将须要更多的GPU内存。

我们上面描述的利用预演习模型并使其适应我们的数据集的内容称为迁移学习。
但为什么要利用迁移学习呢?

迁移学习:

深度神经网络具有大量参数,常日在数百万的范围内。
在小型数据集(一个小于参数数量的数据集)上演习此类网络会极大地影响网络的推广能力,从而导致过度拟合。
因此在实践中,通过随机权重初始化从头开始演习网络是很少见的。

预演习模型常日在非常大的数据集上演习,例如ImageNet,其包含具有1000个类别的120万个图像。
因此,预演习模型已经学会捕获其早期层中的曲线、颜色梯度和边缘等通用特色,这对付大多数其他打算机视觉分类问题是干系且有用的。
迁移学习也被证明在其他领域也是有效的,例如NLP和语音识别。

现在,通过迁移学习,我们的模型已经在ImageNet上进行了预演习,我们只须要使其更详细地节制我们数据集的细节。
我们有两个选项可以做到这一点,我们只能更新末了一层的参数,或者我们可以更新所有模型的图层。
第一个选项常日称为特色提取,而第二个选项称为微调。
在这两种方法中,由于ImageNet预演习模型的输出层的大小为1000,因此,主要的是首先对终极层进行重新塑造,使数据集中的类数量相同。

到目前为止,我们已涵盖了许多核心观点

我们连续......

现在让我们在数据集上演习模型,

epochs数字表示模型查看全体图像集的次数。
但是,在每个epoch,我们的数据增加后,相同的图像略有不同。

常日,度量偏差将随着每个epoch而低落。
只要验证集的精度不断提高,增加epoch数便是个好主张。
然而,大量的epoch可能导致学习特定的图像而不是普通的类,这是我们想要避免的。

我们刚刚在这里进行的演习便是我们所说的特色提取,以是只更新了我们模型的头部(末了一层)的参数。
接下来我们将考试测验微调所有层。

恭喜!
该模型已成功演习,以识别狗和猫品种。

我们达到的准确率是≈93.5%

我们能做得更好吗?微调后我们会看到。

让我们保存当前的模型参数,以防我们稍后想要重新加载。

4.结果阐明

现在让我们看看如何精确阐明当前的模型结果。

Classification Interpretation供应缺点分类图像的可视化。

plot_top_losses显示带有top loss的图像及其:

预测标签/实际标签/丢失/实际图像类别的概率

高丢失意味着对缺点答案的高度信赖。
绘制最高丢失是可视化和解释分类结果的好方法。

具有最高丢失的缺点分类图像

分类稠浊矩阵

在稠浊矩阵中,对角线元素表示预测标签即是真实标签的图像的数量,而非对角线元素是由分类器缺点标记的元素。

most_confused大略地捉住预测和实际种别中最混乱的组合;换句话说,最常常出错的那些。
我们可以看到它常常将斯塔福郡斗牛犬缺点分类为美国斗牛梗,它们实际上看起来是有点相似的。

[('Siamese', 'Birman', 6), ('american_pit_bull_terrier', 'staffordshire_bull_terrier', 5), ('staffordshire_bull_terrier', 'american_pit_bull_terrier', 5), ('Maine_Coon', 'Ragdoll', 4), ('beagle', 'basset_hound', 4), ('chihuahua', 'miniature_pinscher', 3), ('staffordshire_bull_terrier', 'american_bulldog', 3), ('Birman', 'Ragdoll', 2), ('British_Shorthair', 'Russian_Blue', 2), ('Egyptian_Mau', 'Abyssinian', 2), ('Ragdoll', 'Birman', 2), ('american_bulldog', 'staffordshire_bull_terrier', 2), ('boxer', 'american_pit_bull_terrier', 2), ('chihuahua', 'shiba_inu', 2), ('miniature_pinscher', 'american_pit_bull_terrier', 2), ('yorkshire_terrier', 'havanese', 2)]5.冷冻和解冻

默认情形下,在fastai中,利用预先演习的模型会冻结较早的层,以便网络只能变动末了一层的参数,如上所述。
冻结第一层并仅演习较深层可以显著减少大量打算。

我们总是可以通过调用unfreeze函数来演习所有网络层,然后是fit或fit_one_cycle。
这便是我们所谓的微调,由于我们正在调度全体网络的参数。
我们开始做吧,

现在的准确度比以前差一点。
这是为什么?

这是由于我们以相同的速率更新所有层的参数,这不是我们想要的,由于第一层不须要像末了一层那样须要做太多改变。
掌握权重更新量的超参数称为学习率,也称为步长。
它根据丢失的梯度调度权重,目的是减少丢失。
例如,在最常见的梯度低落优化器中,权重和学习率之间的关系如下,

顺便说一下,梯度只是一个向量,它是导数的多变量泛化。

因此,对模型进行微调的更好方法是对较低层和较高层利用不同的学习速率,常日称为差异或判别学习速率。

顺便说一句,我在本教程中可以互换利用参数和权重。
更准确地说,参数是权重和偏差,但我们不须要担心这里的细微之处。
但请把稳,超参数和参数不同;超参数无法在演习中估计。

6.微调

为了找到最适宜微调模型的学习率,我们利用学习速率查找器,个中学习速率逐渐增加并且在每批之后记录相应的丢失。
fastai库在lr_find中实现了这一点。
如需进一步阅读,请查看@GuggerSylvain如何找到良好的学习率。

让我们加载之前保存的模型并运行lr_find,

recorder.plot方法可用于绘制丢失与学习率的关系。
当丢失开始不应时,情节会停滞。

从得到的图中,我们同等认为适当的学习率约为1e-4或更低,在丢失开始增加并且失落去掌握之前。
我们将1e-4分配给末了的层,将1e-6分配给较早的层。
同样,这是由于早期的层已经演习有素,可以捕获通用功能,并且不须要那么多的更新。

如果您想知道我们之前的实验中利用的学习率,由于我们没有明确声明它,它是0.003,这是库中默认设置的。

在我们利用这些判别性学习率演习我们的模型之前,让我们揭开fit_one_cycle和fitmethods之间的差异,由于两者都是演习模型的合理选择。
这个谈论对付理解演习过程非常有代价,但可以直接跳到结果。

fit_one_cycle vs fit:

简而言之,不同之处在于fit_one_cycle实现了Leslie Smith 1循环策略,它不是利用固定或降落的学习速率来更新网络的参数,而是在两个合理的较低和较高学习速率范围之间振荡。
让我们再深入理解一下这对我们的演习有何帮助。

演习中的学习率超参数

在调度我们的深度神经网络时,良好的学习率超参数是至关主要的。
高学习速率许可网络更快地学习,但是太高的学习速率可能使模型无法收敛。
另一方面,较小的学习率会使演习进度非常缓慢。

各种学习率对收敛的影响

在我们的案例中,我们通过查看不同学习率下记录的丢失来估算适当的学习率(lr)。
在更新网络参数时,可以将此学习速率用作固定值;换句话说,将通过所有演习迭代运用相同的学习率。
这便是learn.fit(lr)所做的。
一种更好的方法是随着演习的进行改变学习率。
有两种方法可以做到这一点,即学习速率操持(基于韶光的衰减、逐步衰减、指数衰减等)或自适应学习速率方法(Adagrad,RMSprop,Adam等)。
有关此内容的更多信息,请查看有关参数更新的CS230 Stanford类注释。
另一个很好的资源是@Sebastian Ruder对梯度低落优化算法的概述。

简而言之,一个周期策略

一个周期策略是一种学习速率调度器,其许可学习速率在合理的最小和最大边界之间振荡。
这两个界线的代价是什么?上限是我们从学习速率查找器得到的,而最小界线可以小10倍。
这种方法的优点是它可以战胜局部极小点和鞍点,鞍点是平面上具有范例小梯度的点。
事实证明,1cycle策略比其他调度或自适应学习方法更快、更准确。
Fastai在fit_one_cycle中实现了1cycle策略,该策略在内部调用fit方法和1cycle Scheduler回调。

在fastai实现中对1cycle策略的一个小修正包括在从lr_max到0的第二阶段中的余弦退火。

1cycle策略创造

莱斯利史密斯首先创造了一种他称为循环学习率(CLR)的方法,他表明CLR在打算上并不昂贵,并且它们不须要找到最佳学习速率值,由于最佳学习速率将介于最小和最大界线之间。
然后,他利用另一种规范的神经网络超参数方法来跟踪该论文:第1部分 - 学习率、批量大小、动量和权重衰减,他强调了各种评论和建议,以便加快网络演习以产生最佳结果。
个中一个建议是利用仅一个周期的CLR来实现最佳和快速的结果。
作者将方法命名为1cycle策略。

下图展示了如何利用56层残差网络架构,在Cifar-10更少的迭代中,超收敛方法比范例的(分段常数)演习机制达到更高的精度。

在Cifar-10上,超收敛精度测试与具有相同架构的范例演习机制的比较[Source]

如果你选择跳过阅读莱斯利史密斯的论文,我仍旧会建议阅读这篇文章@GuggerSylvain的1cycle策略。

关键时候

现在,我们为层选择了有差异的学习率,我们可以解冻模型并相应地进行演习。

切片函数将1e-4赋值给末了一层,将1e-6赋值给第一层;中间的层在此范围内以相等的增量得到学习率。

我们看到准确度有所改进,但并不多,以是我们想知道是否须要对模型进行微调?

在微调任何模型之前始终要考虑的两个关键成分,数据集的大小及其与预演习模型的数据集的相似性。
查看斯坦福大学关于何时以及如何微调的CS231条记?在我们的例子中,我们的Pet数据集类似于ImageNet中的图像,并且它相对较小,这便是为什么我们从一开始就实现了高分类精度而没有对全体网络进行微调。

只管如此,我们仍旧能够提高我们的结果并学到很多东西,以是非常棒的事情

下图解释了利用和微调预演习模型的三种合理方法。
在本教程中,我们考试测验了第一个和第三个策略。
策略2在数据集较小但与预演习模型的数据集不同或者数据集较大但与预演习模型的数据集相似的情形下也很常见。

在预先演习的模型上微调策略

恭喜,我们已经成功地利用最前辈的CNN覆盖了图像分类,并具有根本构造和演习流程的坚实根本。

您已准备好在自己的数据集上构建图像识别器。
如果您还没有,可以从Google图像中删除图像并组成数据集。
我为此做了一个非常简短的教程。
「链接」