ITPub博客

首页 > 人工智能 > 人工智能 > 别做空想家!学好PyTorch,你的对象识别项目稳了

别做空想家!学好PyTorch,你的对象识别项目稳了

原创 人工智能 作者:李佳惠 时间:2018-11-30 11:29:10 0 删除 编辑

Keras是一个很棒的库,它提供了一个简单的API来构建神经网络,但最近对PyTorch的兴奋感最终让我对探索这个库产生了兴趣。虽然我是一个"盲目追随炒作"的人,但是研究人员的采用和fast.ai的推崇使我确信在这个深度学习的新入口中必定有新的东西值得我去探寻。

由于学习新技术的最佳方法是使用它来解决问题,所以我学习PyTorch的工作始于一个简单的项目:使用预先训练的卷积神经网络进行对象识别任务。在本文中,我们将看到如何使用PyTorch来实现这一目标,并在此过程中学习一些关于库和迁移学习的重要概念。

别做空想家!学好PyTorch,你的对象识别项目稳了


虽然PyTorch可能不适合所有人,但在这一点上,很难说出哪个深度学习库会脱颖而出,而能够快速学习和使用不同的工具对于成为数据科学家来说至关重要。

该项目的完整代码在GitHub上以Jupyter Notebook的形式提供(%20Learning%20in%20PyTorch.ipynb)。这个项目源于我参加Udacity PyTorch奖学金挑战()。

别做空想家!学好PyTorch,你的对象识别项目稳了

从受过训练的网络预测

迁移学习法

我们的任务是训练可以识别图像中物体的卷积神经网络(CNN)。我们将使用Caltech 101数据集(),该数据集包含101个类别的图像。大多数类别只有50个图像,这些图像通常不足以让神经网络学会高精度。因此,我们将使用预先构建和预先训练的模型来应用迁移学习,而不是从头开始构建和训练CNN。

迁移学习的基本前提很简单:采用在大型数据集上训练的模型,并将其转移到较小的数据集上。对于使用CNN的对象识别,我们冻结网络的早期卷积层,并且仅训练进行预测的最后几层。这个想法是卷积层提取适用于图像的一般,低级特征(例如边缘、图案、渐变)后面的图层识别图像中的特定特征,如眼睛或车轮。

因此,我们可以使用在大规模数据集(通常是Imagenet)中训练不相关类别的网络,并将其应用于我们自己的问题中,因为图像之间共享通用的低级特征。Caltech 101数据集中的图像与Imagenet数据集中的图像非常相似,模型在Imagenet上学习的知识应该很容易转移到此任务中。(http://www.image-net.org/)

别做空想家!学好PyTorch,你的对象识别项目稳了


迁移学习背后的理念

以下是物体识别的迁移学习的概要:

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

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

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

  4. 训练可用于任务的训练数据的分类器层

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

事实证明,这种方法适用于广泛的领域。这是一个很好的工具,通常是面对新的图像识别问题时应该尝试的第一种方法。

数据设置

对于所有数据科学问题,正确格式化数据将决定项目的成功或失败。幸运的是,Caltech 101数据集图像清晰,并以正确的格式存储。如果我们正确设置数据目录,PyTorch可以很容易地将正确的标签与每个类关联起来。我将数据分为训练,验证和测试集,分别为50%,25%,25%,然后按如下方式构建目录:

别做空想家!学好PyTorch,你的对象识别项目稳了


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

别做空想家!学好PyTorch,你的对象识别项目稳了

按类别划分的训练图像数量

我们希望模型在具有更多示例的类上做得更好,因为它可以更好地学习将特性映射到标签。为了处理有限数量的训练样例,我们将在训练期间使用数据增加。

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

别做空想家!学好PyTorch,你的对象识别项目稳了

按类别分布平均图像大小(以像素为单位)

Imagenet模型需要224 x 224的输入大小,因此其中一个预处理步骤将是调整图像大小。预处理也是我们为训练数据实施数据增强的地方。

数据增强

数据增强的想法是通过对图像应用随机变换来人为地增加模型看到的训练图像的数量。例如,我们可以随机旋转或裁剪图像或水平翻转它们。我们希望我们的模型能够区分对象,而不管方向如何,数据增强也可以使模型对输入数据的转换不变。

无论大象朝哪个方向走,大象仍然是大象!

别做空想家!学好PyTorch,你的对象识别项目稳了

训练数据的图像变换

通常仅在训练期间进行增强(尽管在fast.ai库中可以进行测试时间增加)。每个时期 - 通过所有训练图像的一次迭代 - 对每个训练图像应用不同的随机变换。这意味着如果我们迭代数据20次,我们的模型将看到每个图像的20个略有不同的版本。整体结果应该是一个模型,它可以学习对象本身,而不是如何呈现它们或图像中的工件。

图像预处理

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

要在PyTorch中处理图像,我们使用迁移,即应用于数组的简单操作。验证(和测试)迁移如下:

  • 调整

  • 中心裁剪为224 x 224

  • 迁移为张量

  • 用均值和标准差标准化

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

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

别做空想家!学好PyTorch,你的对象识别项目稳了


别做空想家!学好PyTorch,你的对象识别项目稳了


然后,我们创建数据集和数据阅读器。ImageFolder创建数据集,PyTorch将自动将图像与正确的标签关联,前提是我们的目录设置如上述。然后将数据集传递给DataLoader,这是一个产生批量图像和标签的迭代器。

别做空想家!学好PyTorch,你的对象识别项目稳了


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

别做空想家!学好PyTorch,你的对象识别项目稳了


批处理的形状是(batch_size,color_channels,height,width)。在训练、验证和最终测试期间,我们将遍历DataLoaders,一次通过包含一个时期的完整数据集。每个时期,训练DataLoader将对图像应用稍微不同的随机变换以进行训练数据增强。

用于图像识别的预训练模型

随着我们的数据的成形,我们接下来将注意力转向模型。为此,我们将使用预先训练的卷积神经网络。PyTorch有许多模型已经在Imagenet的1000个类中训练了数百万个图像。完整的模型列表可以在这里看到()。这些模型在Imagenet上的性能如下所示:

别做空想家!学好PyTorch,你的对象识别项目稳了

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

对于此实现,我们将使用VGG-16。虽然它没有记录最低的错误,但我发现它适用于任务,并且比其他模型训练得更快。使用预训练模型的过程已经建立:

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

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

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

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

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

别做空想家!学好PyTorch,你的对象识别项目稳了


这个模型有超过1.3亿个参数,但我们只训练最后几个完全连接的层。首先,我们冻结所有模型的权重:

别做空想家!学好PyTorch,你的对象识别项目稳了


然后,我们使用以下图层添加我们自己的自定义分类器:

  • 与ReLU激活完全连接,shape =(n_inputs,256)

  • Dropout有40%的可能性下降

  • 与log softmax输出完全连接,shape =(256,n_classes)

别做空想家!学好PyTorch,你的对象识别项目稳了


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

别做空想家!学好PyTorch,你的对象识别项目稳了


网络的最终输出是我们数据集中100个类中每个类的对数概率。 该模型共有1.35亿个参数,其中只有100多万个将被训练。

别做空想家!学好PyTorch,你的对象识别项目稳了


将模型移动到GPU(s)

PyTorch的最佳方面之一是可以轻松地将模型的不同部分移动到一个或多个gpus(https://pytorch.org/docs/stable/notes/cuda.html),以便你可以充分利用你的硬件。由于我使用2 gpus进行训练,我首先将模型移动到cuda,然后创建一个分布在gpus上的DataParallel模型:

别做空想家!学好PyTorch,你的对象识别项目稳了


(这个笔记本应该在一个gpu上运行,以便在合理的时间内完成。对CPU的加速可以轻松达到10倍或更多。)

训练损失和优化

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

这在实践中意味着损失不仅跟踪误差,而且跟踪模型中每个权重和偏差对误差的贡献。在我们计算损失后,我们可以找到相对于每个模型参数的损失梯度,这个过程称为反向传播。一旦我们获得了梯度,我们就会使用它们来更新参数和优化器。

优化器是Adam(),梯度下降的有效变体,通常不需要手动调整学习速率。在训练期间,优化器使用损失的梯度来尝试通过调整参数来减少模型输出的误差("优化")。只会优化我们在自定义分类器中添加的参数。

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

别做空想家!学好PyTorch,你的对象识别项目稳了


通过预先训练的模型,自定义分类器,损失,优化器以及最重要的数据,我们已准备好进行训练。

训练

PyTorch中的模型训练比Keras中的实际操作多一些,因为我们必须自己进行反向传播和参数更新步骤。主循环迭代多个时期,并且在每个时期迭代通过DataLoader。 DataLoader生成一批我们通过模型的数据和目标。在每个训练批次之后,我们计算损失,相对于模型参数反向传播损失的梯度,然后用优化器更新参数。

我建议你查看笔记本上的完整训练详细信息(%20Learning%20in%20PyTorch.ipynb),但基本的伪代码如下:

别做空想家!学好PyTorch,你的对象识别项目稳了


我们可以继续迭代数据,直到达到给定数量的时期。然而,这种方法的一个问题是,我们的模型最终将过度拟合训练数据。为了防止这种情况,我们使用验证数据并早期停止。

早期停止

早期停止()意味着当验证损失在许多时期没有减少时停止训练。在我们继续训练时,训练损失只会减少,但验证损失最终会达到最低限度并达到稳定水平或开始增加。理想情况下,当验证损失最小时,我们希望停止训练,希望此模型能够最好地推广到测试数据。当使用早期停止时,验证损失减少的每个时期,我们保存参数,以便我们以后可以检索具有最佳验证性能的那些。

我们通过在每个训练时期结束时迭代验证DataLoader来实现早期停止。我们计算验证损失并将其与最低验证损失进行比较。如果到目前为止损失最小,我们保存模型。如果在一定数量的时期内损失没有改善,我们停止训练并返回已保存到磁盘的最佳模型。

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

别做空想家!学好PyTorch,你的对象识别项目稳了


别做空想家!学好PyTorch,你的对象识别项目稳了


别做空想家!学好PyTorch,你的对象识别项目稳了


别做空想家!学好PyTorch,你的对象识别项目稳了


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

别做空想家!学好PyTorch,你的对象识别项目稳了


别做空想家!学好PyTorch,你的对象识别项目稳了

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

正如预期的那样,随着进一步的训练,训练损失只会继续减少。另一方面,验证损失达到最低和稳定的状态。在某一时期,进一步训练是没有回报的(甚至是负回报)。我们的模型将仅开始记忆训练数据,并且无法推广到测试数据。

如果没有早期停止,我们的模型将训练超过必要的时间并且将过度训练数据。

我们从训练曲线中可以看到的另一点是我们的模型并没有过度拟合。总是存在一些过度拟合,但是在第一个可训练的完全连接层之后的退出可以防止训练和验证损失过多。

做出预测:推论

在笔记本中我处理了一些无聊但必要的保存和加载PyTorch模型的细节,但在这里我们将移动到最佳部分:对新图像进行预测。我们知道我们的模型在训练甚至验证数据方面做得很好,但最终的测试是它如何在一个前所未见的保持测试集上的执行。我们保存了25%的数据,以确定我们的模型是否可以推广到新数据。

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

别做空想家!学好PyTorch,你的对象识别项目稳了


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

别做空想家!学好PyTorch,你的对象识别项目稳了


在诊断用于对象识别的网络时(),查看测试集的整体性能和单个预测会很有帮助。

模型结果

以下是模型的两个预测:

别做空想家!学好PyTorch,你的对象识别项目稳了


别做空想家!学好PyTorch,你的对象识别项目稳了

这些都很简单,所以我很高兴模型没有问题!

我们不仅仅想关注正确的预测,我们还将很快就会看到一些错误的输出。现在让我们评估整个测试集的性能。为此,我们希望迭代测试DataLoader并计算每个示例的损失和准确性。

用于对象识别的卷积神经网络通常根据topk精度(https://stats.stackexchange.com/questions/95391/what-is-the-definition-of-top-n-accuracy)来测量。这是指真实的类是否属于k最可能预测的类中。例如,前5个准确度是5个最高概率预测中正确等级的百分比。你可以从PyTorch张量中获取topk最可能的概率和类,如下所示:

别做空想家!学好PyTorch,你的对象识别项目稳了


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

别做空想家!学好PyTorch,你的对象识别项目稳了


这些与验证数据中接近90%的top1精度相比是有利的。总的来说,我们得出结论,我们的预训练模型能够成功地将其知识从Imagenet转移到我们较小的数据集。

模型调查

尽管该模型表现良好,但仍有可能采取一些步骤可以使其变得更好。通常,弄清楚如何改进模型的最佳方法是调查其错误(注意:这也是一种有效的自我改进方法。)

我们的模型不太适合识别鳄鱼,所以我们来看看这个类别的一些测试预测:

别做空想家!学好PyTorch,你的对象识别项目稳了


别做空想家!学好PyTorch,你的对象识别项目稳了


别做空想家!学好PyTorch,你的对象识别项目稳了


考虑到鳄鱼和鳄鱼头之间的微妙区别,以及第二张图像的难度,我会说我们的模型在这些预测中并非完全不合理。图像识别的最终目标是超越人类的能力,我们的模型几乎已经接近了!

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

别做空想家!学好PyTorch,你的对象识别项目稳了


在训练图像的数量和前一个测试精度之间似乎存在正相关关系。这表明更多的训练数据增加是有所帮助的,或者我们应该对测试时间进行增加。我们还可以尝试不同的预训练模型,或者构建另一个自定义分类器。目前,深度学习仍然是一个经验领域,这意味着经常需要实验!

结论

虽然有更容易使用的深度学习库,但PyTorch的优点是速度快,对模型架构/训练的各个方面的控制好,能使张量自动区分的反向传播,以及由于PyTorch图的动态特性而易于调试的代码。对于生产代码或你自己的项目,我不确定使用PyTorch而不是具有更温和学习曲线的库(例如Keras)还存在令人信服的论据,但知道如何使用不同选项会很有帮助。

通过这个项目,我们能够看到使用PyTorch的基础知识以及迁移学习的概念,这是一种有效的对象识别方法。我们可以使用已在大型数据集上进行过训练的现有体系结构,然后根据我们的任务调整它们,而不是从头开始训练模型。这无疑减少了训练的时间并且通常导致更好的整体性能。这个项目的成果是对迁移学习和PyTorch一些知识的应用,我们可以构建它来构建更复杂的应用程序。

我们确实生活在一个令人难以置信的深度学习时代,任何人都可以利用轻松可用的资源建立深度学习模型!现在是时候,通过构建自己的项目来更好的利用这些资源了。


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/31545819/viewspace-2222242/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2018-09-19

  • 博文量
    107
  • 访问量
    211779