PyTorch - 24 - CNN输出大小公式 - 额外的神经网络调试Session

Overview Of Our Network

我们流程的高级概述

  1. 准备数据
  2. 建立模型
    a. 了解前向转换
  3. 训练模型
  4. 分析模型的结果

在过去的六篇文章中,我们将使用的CNN是我们一直在使用的CNN。

  1. 输入层
  2. 隐藏的转换层
  3. 隐藏的转换层
  4. 隐藏的线性层
  5. 隐藏的线性层
  6. 输出层

我们使用PyTorch的nn.Module类构建了该网络,网络类的定义如下:

class Network(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5)
        self.conv2 = nn.Conv2d(in_channels=6, out_channels=12, kernel_size=5)

        self.fc1 = nn.Linear(in_features=12*4*4, out_features=120)
        self.fc2 = nn.Linear(in_features=120, out_features=60)
        self.out = nn.Linear(in_features=60, out_features=10)

    def forward(self, t):
        # (1) input layer
        t = t

        # (2) hidden conv layer
        t = self.conv1(t)
        t = F.relu(t)
        t = F.max_pool2d(t, kernel_size=2, stride=2)

        # (3) hidden conv layer
        t = self.conv2(t)
        t = F.relu(t)
        t = F.max_pool2d(t, kernel_size=2, stride=2)

        # (4) hidden linear layer
        t = t.reshape(-1, 12 * 4 * 4)
        t = self.fc1(t)
        t = F.relu(t)

        # (5) hidden linear layer
        t = self.fc2(t)
        t = F.relu(t)

        # (6) output layer
        t = self.out(t)
        #t = F.softmax(t, dim=1)

        return t

Passing A Batch Of Size One (A Single Image)

在上一集中,我们看到了如何使用PyTorch的unsqueeze()方法添加批处理尺寸来传递单个图像。我们将再次将该张量传递到网络,但是这次我们将使用调试器逐步处理forward()方法。这将允许我们在执行变换时检查张量。

让我们开始:

> network = Network()
> network(image.unsqueeze(0))

#1 Input Layer

当张量进入输入层时,我们有:

> t.shape
torch.Size([1, 1, 28, 28])

这些维度中的每个值都代表以下值:

(批量大小,颜色通道,高度,宽度)

由于输入层只是身份功能,因此输出形状不会改变。

输入层可以看作是平凡的身份函数,该层的输出等于输入。

#2 Convolutional Layer (1)

当张量进入这一层时,我们有:

> t.shape
torch.Size([1, 1, 28, 28])

在第一个卷积运算self.conv1之后,我们有:

> t.shape
torch.Size([1, 6, 24, 24])

批次大小仍为1。这是有道理的,因为我们不希望批次大小发生变化,在整个正向传递中都是如此。

在前进过程中,batch_size是固定的。

颜色通道的数量已从1增加到6。在前进到第一卷积层之后,我们不再将通道视为颜色通道。 我们只是将它们视为输出渠道。 我们有6个输出通道的原因是由于在创建self.conv1时指定的out_channels数量。

Convolution Operations Use Filters

如我们所见,这个数字6是任意的。 out_channels参数指示nn.Conv2d图层类生成六个过滤器(也称为内核),形状为5×5,并具有随机初始化的值。 这些滤波器用于生成六个输出通道。

out_channels参数确定将创建多少个过滤器。

过滤器是张量,当张量传递到图层实例self.conv1时,它们用于对输入张量进行卷积。滤波器张量内部的随机值是卷积层的权重。但是请记住,实际上我们没有六个不同的张量。所有六个过滤器都包装到一个高度和宽度为五个的单个重量张量中。

过滤器是重量张量。

在使用权重张量(滤波器)对输入张量进行卷积后,结果就是输出通道。

输出通道的另一个名称是要素映射。这里的术语可以互换。这是由于以下事实:随着权重的更新而出现的图案检测代表了诸如边缘和其他更复杂图案的特征。

算法:

  1. 颜色通道已传入。
  2. 使用权重张量(滤波器)进行卷积。
  3. 生成特征图并将其转发。

从概念上讲,我们可以认为权重张量是不同的。但是,我们在代码中真正拥有的是具有out_channels(过滤器)维的单个权重张量。我们可以通过检查权重张量的形状来看到这一点:

> self.conv1.weight.shape
torch.Size([6, 1, 5, 5])

张量的形状由下式给出:

(过滤器数,输入通道数,过滤器高度,过滤器宽度)

The relu()Activation Function

调用relu()函数会删除所有负值,并将它们替换为零。 我们可以通过在调用之前和之后检查张量的min()来验证这一点。

> t.min().item()
-1.1849982738494873

> t = F.relu(t)
> t.min().item()
0.0

relu()函数可以用数学方式表示为
在这里插入图片描述

The Max Pooling Operation

合并操作通过从张量中的每个2x2位置提取最大值来进一步减小张量的形状。

> t.shape
torch.Size([1, 6, 24, 24])

> t = F.max_pool2d(t, kernel_size=2, stride=2)
> t.shape
torch.Size([1, 6, 12, 12])

Convolution Layer Summary

卷积层输入和输出的张量的形状由下式给出:

Input shape: [1, 1, 28, 28]
Output shape: [1, 6, 12, 12]

发生的每个操作的摘要:

  1. 卷积层使用六个随机初始化的5x5滤波器对输入张量进行卷积。
    a. 这样可以将高度和宽度尺寸减少四倍。
  2. relu激活功能操作将所有负值映射为0。
    a. 这意味着张量中的所有值现在都为正。
  3. 最大池化操作从由卷积创建的六个特征图的每个2x2部分中提取最大值。
    a. 这样将高度和宽度尺寸减小了十二。

CNN Output Size Formula

让我们看一下在执行卷积和池化操作之后计算张量的输出大小的公式。

CNN Output Size Formula (Square)

  1. 假设我们有一个 n * n 输入。
  2. 假设我们有一个 f * f 过滤器。
  3. 假设我们的填充为 p,跨度为 s

输出大小 O 由以下公式给出:
在这里插入图片描述
该值将是输出的高度和宽度。 但是,如果输入或过滤器不是正方形,则需要两次应用此公式,一次用于宽度,一次用于高度。

CNN Output Size Formula (Non-Square)

  1. 假设我们有一个 nh * nw 输入。
  2. 假设我们有一个 fh * fw 过滤器。
  3. 假设我们的填充为 p,跨度为 s

输出大小Oh 的高度由以下公式给出:
在这里插入图片描述

输出大小Ow 的宽度由以下公式给出:
在这里插入图片描述

#3 Convolutional Layer (2)

第二个隐藏的卷积层self.conv2在与self.conv1相同的条件下转换张量,并进一步减小了高度和宽度尺寸。在进行这些转换之前,让我们检查一下self.conv2的权重张量的形状:

self.conv2.weight.shape
torch.Size([12, 6, 5, 5])

这次,我们的体重张量有12个高度为5且宽度为5的过滤器,但不是只有一个输入通道,而是有6个通道,这为过滤器提供了深度。这说明了来自第一卷积层的六个输出通道。结果输出将具有十二个通道。

现在运行这些操作。

> t.shape
torch.Size([1, 6, 12, 12])

> t = self.conv2(t)
> t.shape
torch.Size([1, 12, 8, 8])

> t.min().item()
-0.39324113726615906

> t = F.relu(t)
> t.min().item()
0.0

> t = F.max_pool2d(t, kernel_size=2, stride=2)
> t.shape
torch.Size([1, 12, 4, 4])

self.conv2输出结果的形状使我们看到了为什么在将张量传递到第一个线性层self.fc1之前使用12 * 4 * 4重整张量。

正如我们过去所看到的,这种特殊的重塑称为拉平张量。展平操作将所有张量元素置于一个维中。输出

> t = t.reshape(-1, 12*4*4)
> t.shape
torch.Size([1, 192])

产生的形状为1x192。在这种情况下,1表示批处理大小,而192表示张量中现在处于相同维度的元素数。

#4 #5 #6 Linear Layers

现在,我们只有一系列线性层,然后是非线性激活函数,直到到达输出层。

> t = self.fc1(t)

> t.shape
torch.Size([1, 120])

> t = self.fc2(t)
> t.shape
torch.Size([1, 60])

> t = self.out(t)
> t.shape
torch.Size([1, 10])

> t
tensor([[ 0.1009, -0.0842,  0.0349, -0.0640,  0.0754, -0.0057,  0.0878,  0.0296,  0.0345,  0.0236]])

下表总结了形状更改操作以及每种形状的结果形状:

操作 输出形状
Identity function torch.Size([1, 1, 28, 28])
Convolution (5 x 5) torch.Size([1, 6, 24, 24])
Max pooling(2 x 2) torch.Size([1, 6, 12, 12])
Convolution (5 x 5) torch.Size([1, 12, 8, 8])
Max pooling(2 x 2) torch.Size([1, 12, 4, 4])
Flatten (reshape) torch.Size([1, 192])
Linear transformation torch.Size([1, 120])
Linear transformation torch.Size([1, 60])
Linear transformation torch.Size([1, 10])

Training The CNN Is Next

现在,我们应该对卷积神经网络如何转换输入张量,如何在PyTorch中调试神经网络以及如何检查所有层的权重张量有一个很好的了解。

在这里插入图片描述

在下一个情节中,我们将开始训练网络,这将导致我们的权重张量值得到更新,以使网络的正向方法将输入映射到正确的输出类。 下一个再见!

猜你喜欢

转载自blog.csdn.net/weixin_48367136/article/details/112546673