资源行业动态卷积神经网络迁移学习之PyTorch实战

卷积神经网络迁移学习之PyTorch实战

2019-11-22 | |  150 |   0

原标题:卷积神经网络迁移学习之PyTorch实战

来源:AI 研习社          链接:https://www.yanxishe.com/TextTranslation/1838


尽管Keras是一个很棒的库,它具有用于构建神经网络的简单API,但是最近有关PyTorch的研究热潮终于使我对探索该库产生了兴趣。尽管我是盲目地大肆宣传,但研究人员的采用以及在fast.ai库中的使用使我确信,深度学习这一新工具背后必定有某些东西。

由于学习新技术的最佳方法是使用新技术来解决问题,因此我学习PyTorch始于一个简单的项目:将预训练的卷积神经网络用于目标识别任务中。在本文中,我们将了解如何使用PyTorch来实现此目标,并一路学习一些关于该库和迁移学习的重要概念的知识。

尽管PyTorch可能并不适合每个人,但目前尚无法确定哪个深度学习库将脱颖而出,而快速学习和使用其他工具对于成为数据科学家的成功至关重要。

该项目的完整代码可在GitHub上的Jupyter Notebook中找到。这个项目源于我参与Udacity PyTorch奖学金挑战赛。

1574364755468046.png

由训练过的网络进行预测


迁移学习的途径

我们的任务是训练可以识别图像中目标的卷积神经网络(CNN)。我们将使用包含101个类别的图像的Caltech 101数据集。大多数类别仅包含50张图像,通常不足以使神经网络具备高精度。因此,我们将使用迁移学习的预先构建和预先训练的模型,而不是从头开始构建和训练CNN。
迁移学习很简单:采用在大型数据集上训练的模型,并将其知识迁移到较小的数据集。对于使用CNN的目标识别,我们冻结了网络的前面的卷积层,仅训练了进行预测的最后几层。想法是,卷积层提取适用于整个图像的通用低级特征,例如边缘,图案,渐变等,随后的层识别图像中特定的特征,例如眼睛或车轮。

因此,我们可以使用在大量数据集中(通常是Imagenet)中不相关类别进行训练的网络,并将其应用于我们自己的问题,因为图像之间共享通用的低级功能。加州理工学院101数据集中的图像与Imagenet数据集中的图像非常相似,并且模型在Imagenet上学习的知识应轻松地迁移到此任务中。

02.png

转移学习背后的想法(来源


以下是用于目标识别迁移学习的一般概述:

1.加载在大型数据集上训练的预训练CNN模型

2.在模型的较低卷积层中冻结参数(权重)

3.添加具有多层可训练参数的自定义分类器

4.利用当前任务中的训练数据训练分类器层

5.微调超参数并根据需要解冻更多层

事实证明,这种方法在很多领域中都是成功的。 它是您绝佳工具,通常是遇到新的图像识别问题时应尝试的第一种方法。

数据设置

对于所有数据科学问题,正确的数据格式将决定项目成败。 幸运的是,Caltech 101数据集图像是干净的,并以正确的格式存储。 如果我们正确设置了数据目录,PyTorch可以轻松地将正确的标签与每个类相关联。 我将数据按50%,25%,25%的比例分为训练集,验证集和测试集,然后按以下方式构建目录:

/datadir
    /train
        /class1
        /class2
        .
        .
    /valid
        /class1
        /class2
        .
        .
    /test        /class1
        /class2
        .
        .

以下是按类别划分的训练图像数量(我可以互换地使用术语“类”和“类别”):

03.png

  分类训练图像的数量 


我们希望该模型在带有更多示例的类上做得更好,因为它可以更好地学习将特征映射到标签。 为了处理数量有限的训练示例,我们将在训练期间使用数据增强(稍后再介绍)。

作为数据探索的另一点,我们还可以查看大小分布。

04.png

按类别分布的平均图像尺寸(以像素为单位) 


Imagenet模型需要224 x 224的输入大小,因此预处理步骤之一将是调整图像大小。 预处理也是我们将对训练数据进行数据扩充的地方。

数据增强

数据增强的想法是通过对图像进行随机变换来人为地增加模型使用的训练图像的数量。 例如,我们可以随机旋转或裁剪图像或水平翻转图像。 我们希望我们的模型能够区分目标而无需考虑方向,而数据扩充也可以使模型对输入数据的转换保持不变。

无论面对哪种方式,大象仍然是大象!

05.png

训练数据的图像转换


增强通常仅在训练期间进行(尽管可以在fast.ai库测试时也可以使用)。 每个训练轮次-“遍历所有训练图像”-将不同的随机变换应用于每个训练图像。 这意味着,如果我们对数据进行20次迭代,我们的模型将看到每个图像的20个略有不同的版本。 总体结果应该是一个学习目标本身的模型,而不是学习目标的表示方式或图像中的伪像。

图像处理

这是处理图像数据最重要的步骤。 在图像预处理期间,我们同时为网络准备图像,并将数据扩充应用于训练集。 每个模型都有不同的输入要求,但是如果我们通读Imagenet的要求,就会发现我们的图像需要大小为224x224并规范化为一定范围。

为了在PyTorch中处理图像,我们使用变换,将简单的操作应用于数组。 验证(和测试)转换如下:

1.调整大小

2.中心裁切为224 x 224

3.转换为张量

4.用均值和标准差进行归一化

通过这些转换的最终结果是可以进入我们网络的张量。 训练变换是相似的,但增加了随机增强。

首先,我们定义训练和验证变换:

06.png

然后,我们创建数据集和DataLoader。 通过使用datasets.ImageFolder创建数据集,只要我们的目录如上所述设置,PyTorch就会自动将图像与正确的标签相关联。 然后将数据集传递到DataLoader,这是一个迭代器,它产生一批图像和标签。

07.png

我们可以使用以下内容查看DataLoader的迭代行为:

# Iterate through the dataloader oncetrainiter = iter(dataloaders['train'])
features, labels = next(trainiter)
features.shape, labels.shape
(torch.Size([128, 3, 224, 224]), torch.Size([128]))

批次的形状为(batch_size,color_channels,height,width)。 在培训,验证和最终测试过程中,我们将遍历DataLoader,一次遍历包含一个轮次的完整数据集。 在每个时期,训练数据加载器都会对图像应用稍有不同的随机变换,以增强训练数据。

预训练模型用于图像识别

有了数据,我们接下来将注意力转移到模型上。 为此,我们将使用预训练的卷积神经网络。 PyTorch拥有许多模型,这些模型已经针对Imagenet中1000类的数百万张图像进行了训练。 可以在此处查看模型的完整列表。 这些模型在Imagenet上的性能如下所示:

08.png

                 PyTorch中的预训练模型和Imagenet上的性能(


对于此实施方案,我们将使用VGG-16。 尽管它的错误率并不是最低,但是我发现它可以很好地完成任务,并且比其他模型更快地进行训练。 使用预先训练的模型的过程已经建立:

1.从在大型数据集上训练的网络中加载预先训练的权重

2.冻结较低(卷积)层中的所有权重:要冻结的层根据新任务与原始数据集的相似性进行调整

3.用自定义分类器替换网络的上层:输出数量必须设置为等于分类数量

4.仅训练任务的自定义分类器层,从而针对较小的数据集优化模型

在PyTorch中预先训练的模型中加载很简单:


from torchvision import models
model = model.vgg16(pretrained=True)

该模型具有超过1.3亿个参数,但是我们只会训练最后几个完全连接的层。 最初,我们冻结模型的所有权重

# Freeze model weightsfor param in model.parameters():
    param.requires_grad = False


然后,我们添加具有以下几层的自定义分类器:

1.与ReLU激活完全连接,形状=(n_inputs,256)

2.随机失活值有40%

3.与log softmax输出全连接,形状=(256,n_classes)

import torch.nn as nn# Add on classifiermodel.classifier[6] = nn.Sequential(
                      nn.Linear(n_inputs, 256), 
                      nn.ReLU(), 
                      nn.Dropout(0.4),
                      nn.Linear(256, n_classes),                   
                      nn.LogSoftmax(dim=1))

将额外的图层添加到模型后,默认情况下会将它们设置为可训练的(require_grad = True)。 对于VGG-16,我们仅更改了最后一个原始的全连接层。 卷积层和前5个完全连接的层中的所有权重均不可训练。

# Only training classifier[6]model.classifier

Sequential(
  (0): Linear(in_features=25088, out_features=4096, bias=True)
  (1): ReLU(inplace)
  (2): Dropout(p=0.5)
  (3): Linear(in_features=4096, out_features=4096, bias=True)
  (4): ReLU(inplace)
  (5): Dropout(p=0.5)
  (6): Sequential(
    (0): Linear(in_features=4096, out_features=256, bias=True)
    (1): ReLU()
    (2): Dropout(p=0.4)
    (3): Linear(in_features=256, out_features=100, bias=True)
    (4): LogSoftmax()
  )
)

网络的最终输出是数据集中100个类别中每个类别的对数概率。 该模型总共有1.35亿个参数,其中将训练超过100万个参数。

# Find total parameters and trainable parameterstotal_params = sum(p.numel() for p in model.parameters())
print(f'{total_params:,} total parameters.')
total_trainable_params = sum(
    p.numel() for p in model.parameters() if p.requires_grad)
print(f'{total_trainable_params:,} training parameters.')135,335,076 total parameters.1,074,532 training parameters.

将模型放在GPU上运算

PyTorch最好的方面之一是易于将模型的不同部分放在一个或多个GPU上训练,以便您可以充分利用硬件。 由于我使用2个GPU进行训练,因此我先将模型移至cuda,然后创建分布在GPU上的DataParallel模型:

# Move to gpumodel = model.to('cuda')# Distribute across 2 gpusmodel = nn.DataParallel(model)

(应在此笔记本gpu上运行,在合理的时间内就能完成。比cpu进行的运算可以轻松达到10倍或更多倍。)


训练损失和优化器

训练损失(预测值与真实值之间的误差或差)是对数似然率(NLL)。 (PyTorch中的NLL损失期望对数概率,因此我们传入模型最后一层的原始输出。)PyTorch使用自动微分,这意味着张量不仅跟踪其值,而且还跟踪每个操作(乘,加,激活)等)。这意味着我们可以计算网络中任何张量相对于任何先前张量的梯度。

实际上,这意味着损失不仅跟踪误差,而且还跟踪模型中每个权重和偏差对误差的贡献。计算完损失后,我们可以找到每个模型参数的 损失梯度,此过程称为反向传播。一旦有了梯度,就可以使用它们来使用优化器更新参数。 (如果一开始没有想到,不要担心,这需要一点时间来掌握!此简报有助于阐明一些要点。)

优化器是Adam,梯度下降的一种有效变体,通常不需要手动调整学习速率。在训练过程中,优化器使用损失的梯度来尝试通过调整参数来减少模型输出的误差(这个过程称为“优化”)。仅优化我们添加到自定义分类器中的参数。

损失和优化器的初始化如下:

from torch import optim# Loss and optimizercriteration = nn.NLLLoss()
optimizer = optim.Adam(model.parameters())

有了预先训练的模型,自定义分类器,损失,优化器,最重要的是数据,我们就可以进行训练了。

训练

PyTorch中的模型训练比Keras中的动手操作要多一些,因为我们必须自己进行反向传播和参数更新步骤。 主循环遍历多个轮次,在每个轮中,我们遍历trainDataLoader。 DataLoader产生一批数据和对应的目标。 在每个训练批次之后,我们计算损失,相对于模型参数反向传播损失的梯度,然后使用优化器更新参数。

我鼓励您查看笔记本以获取完整的训练详细信息,但是基本的伪代码如下:

09.png

我们可以继续遍历数据,直到达到给定的轮次数为止。 但是,这种方法的一个问题是我们的模型最终将开始过拟合训练数据。 为了防止这种情况,我们使用了验证数据并提早停止。


提前停止


当验证损失在多个epcoh都没有减少时,提前停止就是停止训练。随着我们继续训练,训练损失只会减少,但验证损失最终将达到最小值,并达到平稳或开始增加。理想情况下,我们希望在验证损失降至最低时停止训练,以期此模型能最好地推广到测试数据。使用提前停止时,验证损失减少的每个epoch,我们都会保存参数,以便以后可以搜素具有最佳验证性能的参数。

我们通过在每个训练epoch结束时遍历验证DataLoader来实现提前停止。我们计算验证损失并将其与最低验证损失进行比较。如果损失是迄今为止最低的,则保存模型。如果损失在一定时期内没有得到改善,我们将停止训练并返回已保存到磁盘的最佳模型。

同样,完整的代码在笔记本中,但伪代码为:

10.png

要了解尽早停的好处,我们可以查看显示训练和验证损失以及准确性的训练曲线:

 1574365318462285.png 1574365334100247.png

  负对数似然度和准确性训练曲线


不出所料,训练损失只会随着进一步的训练而继续减少。 另一方面,验证损失达到最小值和稳定水平。 在某个时期,没有进一步训练的意义(甚至是损失会增加)。 我们的模型开始只会记住训练数据,而不能推广到测试数据。


如果不及早停止,我们的模型将训练的时间超过必要的时间,并且将过度拟合训练数据。


从训练曲线中可以看到的另一点是,我们的模型并没有太大地拟合。 总是存在一些过拟合现象,但是在第一个可训练的全连接层之后的随机失活会阻止训练和验证损失之间差异进一步增大。


做出预测:前向推断


在笔记本中,我会处理一些无聊的操作(但很有必要),保存和加载PyTorch模型的细节,但是在这里,我们将转到最佳部分:对新图像进行预测。 我们知道我们的模型在训练甚至验证数据上都表现出色,但是最终的测试是它如何在前所未有的保持测试集上执行。 为了确定我们的模型是否可以推广到新数据,我们保存了25%的数据。


使用经过训练的模型进行预测非常简单。 我们使用与训练和验证相同的语法:

for data, targets in testloader:
    log_ps = model(data)
    # Convert to probabilities    ps = torch.exp(log_ps)
    
ps.shape()

(128, 100)

我们的概率形状为(batch_size,n_classes),因为我们每个类都有一个概率。 我们可以通过找到每个示例的最高概率并将它们与标签进行比较来找到准确性:

诊断用于目标识别的网络时,同时查看测试集的整体性能和单个预测会很有帮助。


模型结果


这是模型的两个预测:

13.png

14.png

这些非常容易,所以我很高兴模型没有问题!


我们不仅要专注于正确的预测,而且很快就会看到一些错误的输出。 现在,让我们评估整个测试集的性能。 为此,我们要遍历测试DataLoader并计算每个示例的损失和准确性。

用于物体识别的卷积神经网络通常是根据top-k精度进行评价的。 这是指真实类别是否在k个最可能的预测类别中。 例如,前5个准确度是5个最高概率预测中正确类别的百分比。 您可以从PyTorch张量获取top-k最可能的概率和类,如下所示:


top_5_ps, top_5_classes = ps.topk(5, dim=1)
top_5_ps.shape
(128, 5)


在整个测试集中评估模型,我们计算指标:


Final test top 1 weighted accuracy = 88.65%
Final test top 5 weighted accuracy = 98.00%
Final test cross entropy per image = 0.3772.


与验证数据上近90%的top 1准确性相比,它们具有优势。 总体而言,我们认为经过预训练的模型能够成功地将其知识从Imagenet迁移到较小的数据集。


模型调查


尽管该模型效果不错,但仍有可能会采取一些措施使其变得更好。 通常,弄清楚如何改进模型的最佳方法是调查其错误(请注意:这也是一种有效的自我完善方法。)

我们的模型不能很好地识别鳄鱼,因此让我们看一下有关该类别的一些测试预测:

15.png

16.png

 17.png

鉴于crocodile和crocodile_head之间的微妙区别以及第二张图片的难度,我想说我们的模型在这些预测中并非完全不合理。 图像识别的最终目标是超越人类的能力,而我们的模型就已经差不多了!


最后,我们希望模型在包含更多类别上的图像表现更好,因此我们可以查看给定类别的准确度与该类别中训练图像数量的关系图:

18.png  

训练图像的数量与前1个测试准确性之间确实存在正相关关系。 这表明更多的训练数据扩充可能会有所帮助,或者甚至我们应该使用测试时间扩充。 我们也可以尝试使用其他预先训练的模型,或构建另一个自定义分类器。 目前,深度学习仍然是一个经验领域,这意味着经常需要进行实验!


结论

使用了更简单的深度学习库,PyTorch图的动态特性给PyTorch的速度,可控制模型体系结构/训练的各个方面,通过张量自动微分的反向传播的高效实现以及调试代码的便利性均带来了好处。对于生产应用的代码或您自己的项目,我不确定使用PyTorch能够像Keras这样的学习曲线更柔和的库,但是了解如何使用不同的选项会有所帮助。


通过这个项目,我们能够了解使用PyTorch的基础知识以及迁移学习的概念,迁移学习是一种有效的目标识别方法。无需从头开始训练模型,我们可以使用已经在大型数据集上训练过的现有体系结构,然后针对我们的任务对其进行调整。这样可以减少训练时间,并且通常可以提高整体性能。该项目的结果是我们可以在构建更复杂的应用程序的基础上获得一些有关迁移学习和PyTorch的知识。


我们真正生活在一个令人难以置信的深度学习时代,任何人都可以使用容易获得的资源来构建深度学习模型!现在到那里去,通过构建自己的项目来利用这些资源。

一如既往,我欢迎反馈和建设性的批评。可以通过Twitter @koehrsen_will或通过我的个人网站willk.online与我联系。

----------------------------------------------------------------------

发起:唐里 校对:唐里 审核:鸢尾

参与翻译(1人):天字一号

英文原文:Transfer Learning with Convolutional Neural Networks in PyTorch

THE END

免责声明:本文来自互联网新闻客户端自媒体,不代表本网的观点和立场。

合作及投稿邮箱:E-mail:editor@tusaishared.com

上一篇:影响强化学习的三个博弈论创新趋势

下一篇:这 5 件事情让模型“有用”

用户评价
全部评价

热门资源

  • 国内人才报告:机...

    近日,BOSS 直聘职业科学实验室 &BOSS 直聘研究院...

  • AI使物联网更智能...

    看到微软对物联网和人工智能的结合感兴趣是一个明...

  • 推荐一批学习自然...

    这里推荐一批学习自然语言处理相关的书籍,当然,...

  • 安防智能化大势下...

    大部分传统安防设备不仅拍摄视野有限,而且无法事...

  • 20亿创业基金、10...

    近日,杭州举办了建设国家新一代人工智能创新发展...