Press "Enter" to skip to content

使用PyTorch进行高效图像分割:第二部分

基于CNN的模型

这是使用PyTorch从头开始逐步实现深度学习技术进行图像分割的4部分系列中的第二部分。本部分将重点介绍实现基线图像分割卷积神经网络(CNN)模型。

与Naresh Singh合作

图1:使用CNN运行图像分割的结果。从上到下依次为输入图像,地面真实分割掩码,预测分割掩码。来源:作者

文章大纲

在本文中,我们将实现一种名为SegNet的基于卷积神经网络(CNN)的体系结构,该体系结构将为输入图像中的每个像素分配相应的宠物,例如猫或狗。不属于任何宠物的像素将被分类为背景像素。我们将使用PyTorch在Oxford Pets数据集上构建和训练此模型,以了解成功执行图像分割任务所需的内容。模型构建过程将是实践性的,我们将详细讨论模型中每个层的作用。本文将包含大量参考文献和文章,供进一步学习。

在本文中,我们将引用来自该笔记本的代码和结果。如果您希望重现结果,则需要GPU以确保笔记本在合理的时间内完成运行。

本系列文章

本系列文章适合所有经验水平的深度学习读者。如果您想学习深度学习和视觉AI的实践以及一些扎实的理论和实践经验,您来对了地方!这预计是一个由以下文章组成的4部分系列:

  1. 概念与思想
  2. 基于CNN的模型(本文)
  3. 深度可分离卷积
  4. 基于Vision Transformer的模型

让我们从简要介绍卷积层和其他通常一起使用的层,如卷积块开始这个讨论。

Conv-BatchNorm-ReLU和最大池化/反池化

卷积,批量归一化,ReLU块是视觉AI的三位一体。您将经常看到它与基于CNN的视觉AI模型一起使用。每个这些术语都代表PyTorch中实现的不同层。卷积层负责对输入张量执行学习过滤器的交叉相关操作。批量归一化将批次中的元素居中到零均值和单位方差,而ReLU是一种非线性激活函数,仅保留输入中的正值。

典型的CNN随着层叠加而逐渐减小输入空间维度。减小空间维度的动机在下一节中讨论。通过使用简单的函数,例如max或average,汇聚相邻值来实现该减小。我们将在最大池化部分进一步讨论这一点。在分类问题中,Conv-BN-ReLU-Pool块的堆栈后面跟随一个分类头,该分类头预测输入属于目标类别之一的概率。对于诸如语义分割之类的某些问题,需要每像素预测。对于这种情况,在降采样块之后附加一堆上采样块,以将它们的输出投影到所需的空间维度。上采样块只是Conv-BN-ReLU-Unpool块,它们用反池化层替换了池化层。我们将在最大池化部分进一步讨论反池化。

现在,让我们进一步阐述卷积层背后的动机。

卷积

卷积是视觉AI模型的基本构建块。它们在计算机视觉中得到广泛应用,并且历史上曾用于实现视觉变换,例如:

  1. 边缘检测
  2. 图像模糊和锐化
  3. 浮雕
  4. 强化

卷积操作是两个矩阵的逐元素乘法和聚合。图2显示了一个示例卷积操作。

图2:卷积操作的示意图。来源:作者

在深度学习环境下,卷积是在较大的输入上通过滑动一个称为滤波器或内核的n维参数矩阵来进行的。这是通过滑动滤波器并对应部分进行卷积运算实现的。滑动范围是使用步幅参数来配置的。步幅为1表示内核向前滑动一步以在下一个部分上运行。与传统方法使用固定滤波器不同,深度学习使用反向传播从数据中学习滤波器。

那么卷积如何辅助深度学习呢?

在深度学习中,卷积层用于检测视觉特征。典型的CNN模型包含一堆这样的层。堆栈中的底层检测简单的特征,例如线条和边缘。随着堆栈的上移,层逐渐检测越来越复杂的特征。堆栈中的中间层检测线条和边缘的组合,而顶层则检测复杂的形状,例如汽车、脸部或飞机。图3直观地显示了受过训练模型的顶部和底部层的输出。

图3:卷积滤波器学习识别的内容。来源:可扩展无监督学习分层表示卷积深度置信网络

卷积层有一组可学习的滤波器,这些滤波器对输入中的小区域进行操作,为每个区域产生一个代表性输出值。例如,一个3×3的滤波器在一个3×3的区域上进行操作并产生代表该区域的值。重复应用滤波器来处理输入区域产生的输出成为堆栈中下一层的输入。直观地说,越往上的层可以“看到”更大的输入区域。例如,第二个卷积层中的3×3滤波器在第一个卷积层的输出上进行操作,其中每个单元格包含有关输入中3×3大小区域的信息。如果我们假设步幅为1的卷积操作,则第二个层中的滤波器将“看到”原始输入的5×5大小的区域。这被称为卷积的感受野。重复应用卷积层逐渐减小输入图像的空间维度并增加滤波器的视野,从而使它们能够“看到”复杂的形状。图4显示了1D输入通过卷积网络的处理过程。输出层中的一个元素是一个相对较大的输入块的代表。

图4:使用内核大小=3的1D卷积的感受野,应用3次。假设步幅=1且没有填充。在连续应用第3次卷积核之后,单个像素能够查看原始输入图像中的7个像素。来源:作者

一旦卷积层能够检测到这些对象并能够生成它们的表示,我们就可以将这些表示用于图像分类、图像分割和对象检测和定位。广义上说,CNN遵循以下一般原则:

  1. 卷积层将输出通道数(C)保持不变或将其加倍。
  2. 它使用步幅=1保持空间维度不变或使用步幅=2将其减少一半。
  3. 通常汇集卷积块的输出以更改图像的空间维度。

卷积层将内核独立地应用于每个输入。这可能会导致其输出因不同输入而异。Batch Normalization层通常紧随卷积层以解决这个问题。让我们在下一节中详细了解其作用。

批归一化

批归一化层将批次输入中的通道值归一化为零均值和单位方差。这种归一化是针对批次中的每个通道独立地执行的,以确保输入的通道值具有相同的分布。批归一化具有以下好处:

  1. 通过防止梯度过小,稳定训练过程。
  2. 在我们的任务上实现更快的收敛。

如果我们只有一堆卷积层,由于线性转换的级联效应,它本质上等价于单个卷积层网络。换句话说,线性变换序列可以被替换为具有相同效果的单个线性变换。直观地说,如果我们先将向量乘以常数k₁,然后再乘以另一个常数k₂,它等价于单个常数k₁k₂的乘法。因此,为了使网络真正深入,它们必须具有非线性,以防止它们的崩溃。接下来,我们将讨论经常用作非线性的ReLU。

ReLU

ReLU是一种简单的非线性激活函数,它将最低的输入值剪切为大于或等于0。它还有助于解决消失的梯度问题,将输出限制为大于或等于0。ReLU层通常紧随其后具有池化层,用于缩小下采样子网络中的空间维度,或具有解卷积层,用于增加上采样子网络中的空间维度。详情请参见下一节。

池化

池化层用于缩小输入的空间维度。具有stride=2的池化将把具有空间维度(H,W)的输入转换为(H/2,W/2)。最大池化是深度CNN中最常用的池化技术。它将(例如)2×2网格中的最大值投射到输出上。然后,我们根据卷积类似的步幅将2×2池化窗口滑动到下一个部分。反复使用步幅为2进行此操作会产生一个输出,其高度和宽度均为输入的一半。另一种常用的池化层是平均池化层,它计算平均值而不是最大值。

池化层的反转称为解池化层。它需要将(H、W)维度输入转换为stride=2的(2H、2W)维度输出。此转换的必要组成部分是选择输出部分中的位置以投影输入值。为此,我们需要一个最大解池化索引映射,它告诉我们输出部分中的目标位置。该解池化映射是由先前的最大池化操作生成的。图5显示了池化和解池化操作的示例。

图5:最大池化和解池化。来源:DeepPainter:Painter Classification Using Deep Convolutional Autoencoders

我们可以将最大池化视为一种非线性激活函数。但是,据报道,使用它替换诸如ReLU之类的非线性会影响网络的性能。相反,无法将平均池化视为非线性函数,因为它使用其所有输入来产生其输入的线性组合的输出。

这涵盖了深度CNN的所有基本构建块。现在,让我们将它们组合起来创建模型。我们为此练习选择的模型称为SegNet。接下来我们将讨论它。

SegNet:基于CNN的模型

SegNet是一个基于我们在本文中讨论的基本块的深度CNN模型。它有两个不同的部分。底部部分,也称为编码器,将输入下采样以生成代表输入的特征。顶部解码器部分将特征上采样以创建每个像素的分类。每个部分由一系列Conv-BN-ReLU块组成。这些块还在下采样和上采样路径中包括池化或解池化层。图6更详细地显示了层的排列方式。SegNet使用来自编码器中的最大池化操作的池化索引来确定在解码器中进行最大解池化操作时要复制哪些值。虽然激活张量的每个元素都是4字节(32位),但是2×2正方形内的偏移量仅可以使用2位存储。由于这些激活(或在SegNet的情况下是索引)需要在模型运行时存储,因此在使用内存方面更有效率。

Figure 6: 图像分割的SegNet模型架构。来源:SegNet:用于图像分割的深度卷积编码器-解码器架构

本笔记本包含此部分的所有代码。

此模型具有1527万个可训练参数。

模型训练和验证期间使用以下配置。

  1. 对训练集应用随机水平翻转和颜色扭曲数据增强,以防止过拟合
  2. 在非保持纵横比大小的调整操作中将图像调整为128×128像素
  3. 未对图像应用输入规范化;而是将批归一化层用作模型的第一层
  4. 使用Adam优化器进行20次训练,LR为0.001,使用StepLR Scheduler,每7个周期将学习率衰减0.7倍
  5. 使用交叉熵损失函数将像素分类为宠物、背景或宠物边缘

该模型在20次训练周期后实现了88.28%的验证准确性。

我们制作了一个gif,展示了模型如何学习预测21个验证集中的分割掩模。

Figure 6: 展示SegNet模型如何学习预测验证集中21个图像的分割掩模的gif。来源:作者

所有验证指标的定义在本系列的第1部分中描述。

如果您想看到使用Tensorflow实现的用于分割宠物图像的全卷积模型,请参见《高效深度学习书》的第4章:高效架构。

模型学习的观察

根据经过每个周期的训练后训练模型所做出的预测的发展,我们可以观察到以下结果。

  1. 即使在1个训练周期的早期阶段,模型也能够学习到使输出看起来正确的宠物图像的水平
  2. 边框像素更难进行分割,因为我们使用了未加权的损失函数,该函数将每个成功(或失败)的像素都视为同等,因此错误地处理边框像素并不会对模型造成太大的损失。我们建议您进行调查并检查您可以尝试修复此问题的策略。尝试使用Focal Loss并查看其表现
  3. 即使在20个训练周期后,模型似乎仍在学习。这表明,如果我们训练模型的时间更长,我们可以提高验证准确性
  4. 一些地面实况标签本身很难弄清楚-例如,中间行,最后一列的狗的掩模在植物遮挡狗的身体部位的区域中有许多未知像素。这对于模型来说非常困难,因此对于这些示例,总是应该期望准确性下降。但是,这并不意味着模型表现不佳。在查看整体验证指标之外,应始终检查预测以了解模型的行为。
Figure 7: 一个包含许多未知像素的地面实况分割掩模的示例。这对于任何ML模型来说都是一个非常困难的输入。来源:作者

结论

在本系列的第2部分中,我们了解了深度CNN用于视觉AI的基本构建块。我们看到如何在PyTorch中从头开始实现SegNet模型,并可视化模型在21个验证图像上的连续训练表现。这应该有助于您欣赏模型学习足够快,以使输出看起来大致在正确范围内的速度。在这种情况下,我们可以看到即使在第一个训练周期中,大致类似于实际分割掩模的分割掩模!

在该系列的下一部分中,我们将介绍如何优化模型以进行设备内推断,并减少可训练参数的数量(从而减小模型大小),同时保持验证准确率大致相同。

进一步阅读

在此处阅读更多关于卷积的信息:

  1. 由Joseph Redmon教授授课的华盛顿大学的“计算机视觉的古老秘密”课程具有卓越的卷积视频(特别是第4、5和13章),强烈推荐观看
  2. 深度学习卷积算术指南(强烈推荐)
  3. https://towardsdatascience.com/computer-vision-convolution-basics-2d0ae3b79346
  4. PyTorch中的Conv2d层(文档)
  5. 卷积学习了解什么?
  6. 卷积可视化工具

在此处阅读更多关于批标准化的信息:

  1. 批标准化:维基百科
  2. 批标准化:机器学习大师
  3. 在此处查看PyTorch中的BatchNorm2d层。

在此处阅读更多关于激活函数和ReLU的信息:

  1. ReLU:机器学习大师
  2. ReLU:维基百科
  3. ReLU:Quora
  4. PyTorch中的ReLU API
Leave a Reply

Your email address will not be published. Required fields are marked *