提交 22b26079 编写于 作者: W wizardforcel

2021-01-18 17:26:51

上级 573cbfbe
......@@ -62,7 +62,7 @@ PyTorch 为计算机视觉提供了几个便捷功能,其中包括卷积层和
我们正在开发 CNN 以执行简单的分类任务。 使用简单 CNN 的想法是为了了解 CNN 的工作原理。 弄清基础知识后,我们将转到高级网络设计,在其中使用高级 PyTorch 函数,该函数与该应用程序具有相同的功能,但效率更高。
我们将使用 CIFAR10 作为输入数据集,它由 10 类 60,000 张 32x32 彩色图像组成,每类 6,000 张图像。 `torchvision`具有更高级别的功能,可下载和处理数据集。 如我们在第 3 章,“深度学习工作流”中看到的示例一样,我们下载数据集,然后使用转换对其进行转换,并将其包装在`get_data()`函数下。
我们将使用 CIFAR10 作为输入数据集,它由 10 类 60,000 张`32x32`彩色图像组成,每类 6,000 张图像。 `torchvision`具有更高级别的功能,可下载和处理数据集。 如我们在第 3 章,“深度学习工作流”中看到的示例一样,我们下载数据集,然后使用转换对其进行转换,并将其包装在`get_data()`函数下。
```py
def get_data():
......@@ -83,7 +83,7 @@ def get_data():
函数的第一部分对来自 CIFAR10 数据集的 NumPy 数组进行转换。 首先将其转换为 Torch 张量,然后进行归一化转换。 `ToTensor`不仅将 NumPy 数组转换为 Torch 张量,而且还更改了维度的顺序和值的范围。
PyTorch 的所有更高层 API 都希望通道(张量的深度)成为批量大小之后的第一维。 因此,形状(高度 x 宽度 x 通道(RGB))在[0,255]范围内的输入将转换为形状(通道(RGB)x 高度 x 宽度)在[0.0,255]之间的`torch.FloatTensor` 1.0]。 然后,将每个通道(RGB)的平均值和标准差设置为 0.5,进行标准化。 `torchvision`转换完成的规范化操作与以下 Python 函数相同:
PyTorch 的所有更高层 API 都希望通道(张量的深度)成为批量大小之后的第一维。 因此,形状`(高度 x 宽度 x 通道 (RGB))``[0, 255]`范围内的输入将转换为形状`(通道 (RGB) x 高度 x 宽度)``[0.0, 1.0]`之间的`torch.FloatTensor`。 然后,将每个通道(RGB)的平均值和标准差设置为 0.5,进行标准化。 `torchvision`转换完成的规范化操作与以下 Python 函数相同:
```py
def normalize(image, mean, std):
......@@ -140,7 +140,7 @@ class Conv(nn.Module):
self.bias = Parameter(torch.zeros(out_channels))
```
图像上的卷积运算使用滤波器对输入图像进行乘法和加法运算,并创建单个输出值。 因此,现在我们有了一个输入映像和一个内核。 为简单起见,让我们考虑输入图像为大小为 7x7 的单通道(灰度)图像,并假设内核的大小为 3x3,如下图所示。 我们将内核的中间值称为锚点,因为我们将锚点保留在图像中的某些值上进行卷积。
图像上的卷积运算使用滤波器对输入图像进行乘法和加法运算,并创建单个输出值。 因此,现在我们有了一个输入映像和一个内核。 为简单起见,让我们考虑输入图像为大小为`7x7`的单通道(灰度)图像,并假设内核的大小为`3x3`,如下图所示。 我们将内核的中间值称为锚点,因为我们将锚点保留在图像中的某些值上进行卷积。
![Model](img/B09475_04_03.jpg)
......@@ -169,7 +169,7 @@ out = torch.zeros(batch_size, new_depth, new_height, new_width)
out[:, nf, h, w] += self.bias[nf]
```
PyTorch 中的`functional`模块具有帮助我们进行填充的方法。 `F.pad`接受每一侧的输入张量和填充大小。 在这种情况下,我们需要对图像的所有四个边进行恒定的填充,因此我们创建了一个大小为 4 的元组。 如果您想知道填充的工作原理,下面的示例显示在对大小为(2,2,2,2)的大小(1、1)的张量进行`F.pad`后将大小更改为(5,5)
PyTorch 中的`functional`模块具有帮助我们进行填充的方法。 `F.pad`接受每一侧的输入张量和填充大小。 在这种情况下,我们需要对图像的所有四个边进行恒定的填充,因此我们创建了一个大小为 4 的元组。 如果您想知道填充的工作原理,下面的示例显示在对大小为`(2, 2, 2, 2)`的大小`(1, 1)`的张量进行`F.pad`后将大小更改为`(5, 5)`
```py
>>> F.pad(torch.zeros(1,1), (2,) * 4)
......@@ -195,7 +195,7 @@ Variable containing:
前面的示例使用了一个单通道输入并创建了一个单通道输出。 我们可以将其扩展为使用`n`个输入通道来创建`n`个输出通道,这是卷积网络的基本构建块。 通过进行两次更改,可以推断出相同的概念以处理任意数量的输入通道以创建任意数量的输出通道:
* 由于输入图像具有多个通道,因此用于与相应元素相乘的内核必须为`n`维。 如果输入通道为三个,并且内核大小为五个,则内核形状应为 5 x 5 x 3
* 由于输入图像具有多个通道,因此用于与相应元素相乘的内核必须为`n`维。 如果输入通道为三个,并且内核大小为五个,则内核形状应为`5 x 5 x 3`
* 但是,如何创建`n`个输出通道? 现在我们知道,不管输入通道有多少,一次卷积都会创建一个单值输出,而完整的滑动窗口会话会创建一个二维矩阵作为输出。 因此,如果我们有两个内核做完全相同的事情,那就是:滑动输入并创建二维输出。 然后,我们将获得两个二维输出,并将它们堆叠在一起将为我们提供具有两个通道的输出。 随着输出中需要更多通道,我们增加了内核数量。
我们拥有的自定义卷积层可以完成卷积。 它接受输入和输出通道的数量,内核大小,步幅和填充作为参数。 内核的形状为`[kernel_size, kernel_size, input_channels]`。 我们没有创建`n`个内核并将输出堆叠在一起以获得多通道输出,而是创建了一个大小为`output_channel, input_channel, kernal_size, kernal_size`的单个权重张量,这给出了我们想要的。
......@@ -302,7 +302,7 @@ running_loss += loss.item()
图 4.6:语义分割示例
语义分割为现实世界中的几个主要应用提供支持,从闭路电视摄像机和自动驾驶汽车到分割不同的对象。 在本章中,我们将实现一种称为 LinkNet [2] [7]的最快的语义分割架构。
语义分割为现实世界中的几个主要应用提供支持,从闭路电视摄像机和自动驾驶汽车到分割不同的对象。 在本章中,我们将实现一种称为 LinkNet [2][7]的最快的语义分割架构。
在本章中,我们将 CamVid 数据集用于我们的 LinkNet 实现。 CamVid 是一个地面真实数据集,由高质量视频组成,这些高质量视频转换为手动分割和标记的帧。 手动标记的输出图像将颜色用作对象的标识。 例如,数据集输出目录中的所有图像都将洋红色用于道路。
......@@ -382,7 +382,7 @@ class ConvBlock(nn.Module):
LinkNet 中的所有卷积都紧随其后的是批量规范化和 ReLU 层,但是有一些例外,没有 ReLU 层。 这就是`ConvBlock`的目标。 如前所述,`ConvBlock``torch.nn.Module`的子类,可以根据正向传递中发生的任何事情进行反向传播。 `__init__`接受输入和输出尺寸,内核大小,步幅值,填充宽度,表示是否需要偏置的布尔值和表示是否需要激活(ReLU)的布尔值。
我们使用`torch.nn.Conv2d``torch.nn.BatchNorm2d``torch.nn.ReLu`来配置`ConvBlock`。 PyTorch 的 Conv2D 接受`ConvBlock``__init__`的所有参数,但表示类似激活要求的布尔值除外。 除此之外,Conv2D 还接受另外两个用于 dilation 和 group 的可选参数。 `torch.nn`的 ReLU 函数仅接受一个称为`inplace`的可选参数,默认为`False`。 如果`inplace``True`,则 ReLU 将应用于原地数据,而不是创建另一个存储位置。 在许多情况下,这可能会稍微节省内存,但会导致问题,因为我们正在破坏输入。 经验法则是:除非您迫切需要内存优化,否则请远离它。
我们使用`torch.nn.Conv2d``torch.nn.BatchNorm2d``torch.nn.ReLu`来配置`ConvBlock`。 PyTorch 的 Conv2D 接受`ConvBlock``__init__`的所有参数,但表示类似激活要求的布尔值除外。 除此之外,Conv2D 还接受另外两个用于`dilation``group`的可选参数。 `torch.nn`的 ReLU 函数仅接受一个称为`inplace`的可选参数,默认为`False`。 如果`inplace``True`,则 ReLU 将应用于原地数据,而不是创建另一个存储位置。 在许多情况下,这可能会稍微节省内存,但会导致问题,因为我们正在破坏输入。 经验法则是:除非您迫切需要内存优化,否则请远离它。
批量规范化用于规范每个批量中的数据,而不是一开始只进行一次。 在开始时,标准化对于获得相等比例的输入至关重要,这反过来又可以提高精度。 但是,随着数据流经网络,非线性和权重和偏差的增加可能导致内部数据规模不同。
......@@ -392,7 +392,7 @@ LinkNet 中的所有卷积都紧随其后的是批量规范化和 ReLU 层,但
ε值将添加到平方根内的分母中以保持数值稳定性,而动量因子决定应从上一层获得多少动量以加快操作速度。
`__init__`检查是否需要激活并创建层。 这是`torch.nn.Sequential`有用的地方。 将三个不同的层(卷积,批量规范化和 ReLU)定义为单个`ConvBlock`层的明显方法是为所有三个层创建 Python 属性,并将第一层的输出传递给第二层,然后将该输出传递给第三层 in。但是使用`nn.Sequential`,我们可以将它们链接在一起并创建一个 Python 属性。 这样做的缺点是,随着网络的增长,您将为所有小模块提供额外的`Sequential`包装器,这将使解释网络图变得困难。 存储库中的可用代码(带有`nn.Sequential`包装器)将生成类似“图 4.10a”的图形,而没有使用`Sequential`包装器构建的层将生成类似“图 4.10b”的图形。
`__init__`检查是否需要激活并创建层。 这是`torch.nn.Sequential`有用的地方。 将三个不同的层(卷积,批量规范化和 ReLU)定义为单个`ConvBlock`层的明显方法是为所有三个层创建 Python 属性,并将第一层的输出传递给第二层,然后将该输出传递给第三层。但是使用`nn.Sequential`,我们可以将它们链接在一起并创建一个 Python 属性。 这样做的缺点是,随着网络的增长,您将为所有小模块提供额外的`Sequential`包装器,这将使解释网络图变得困难。 存储库中的可用代码(带有`nn.Sequential`包装器)将生成类似“图 4.10a”的图形,而没有使用`Sequential`包装器构建的层将生成类似“图 4.10b”的图形。
```py
class ConvBlockWithoutSequential(nn.Module):
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册