点击上方“AI算法与图像处理”,选择加"星标"或“置顶”
重磅干货,第一时间送达
文 |AI_study
原标题:CNN Output Size Formula - Bonus Neural Network Debugging Session
准备数据
建立模型
了解前向传递的转换
训练模型
分析模型的结果
网络概述
我们将使用的CNN是我们在过去几篇文章中一直使用的,它有六层。
输入层
隐藏的卷积层
隐藏的卷积层
隐藏的 linear 层
隐藏的 linear 层
输出层
我们使用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
传递一个 batch大小为1(单张图像 )
在前一节中,我们了解了如何使用PyTorch的unsqueeze()方法添加批处理维度来传递单个图像。我们将再次将这个张量传递给网络,但是这次我们将使用调试器逐步执行forward()方法。这将允许我们在进行变换时检查我们的张量。
让我们开始:
> network = Network()
> network(image.unsqueeze(0))
# 1 输入层
当张量进入输入层,有如下:
> t.shape
torch.Size([1, 1, 28, 28])
在这些维度中的各个值代表下面的含义:
(batch size, color channels, height, width)
因为输入层只是恒等函数,所以输出形状不会改变。
The input layer can be regarded as the trivial identity function, output of the layer is equal to the input.
#2 卷积层 1
当张量进入这一层时,我们有:
> t.shape
torch.Size([1, 1, 28, 28])
在第一个卷积操作 self.conv1后 ,我们有:
> t.shape
torch.Size([1, 6, 24, 24])
批大小仍然是1。这是有意义的,因为我们不会期望我们的批大小会改变,这将是整个前向传递的情况。
The
batch_size
is fixed as we move through the forward pass.
彩色通道的数量从1个增加到6个。在我们通过了第一个卷积层之后,我们不再认为通道是彩色通道。我们只是把它们当做输出通道。我们有6个输出通道的原因是由于在创建self.conv1时指定的out_channels数量。
使用滤波器的卷积运算
如我们所见,这个数字6是任意的。out_channels参数指示nn.Conv2d层类生成六个过滤器(也称为卷积核),形状为5×5,并具有随机初始化的值。这些滤波器用于生成六个输出通道。
The
out_channels
parameter determines how many filters will be created.
过滤器是张量,当张量传递到层实例self.conv1时,它们用于对输入张量进行卷积。滤波器张量内部的随机值是卷积层的权重。不过请记住,实际上我们没有六个不同的张量。所有六个过滤器都包装在一个高度和宽度为五个的单个权重张量中。
The filters are the weight tensors.
在使用权重张量(滤波器)对输入张量进行卷积后,结果就是输出通道。
引用输出通道的另一种方法是调用特征图(feature map)。这是由于以下事实:随着权重的更新而出现的图案检测代表了诸如边缘和其他更复杂图案的特征。
算法:
颜色通道已传入。
使用权重张量(滤波器)进行卷积。
生成要素图并将其前向传递。
从概念上讲,我们可以认为权重张量是不同的。但是,我们在代码中真正拥有的是具有out_channels(过滤器)维的单个权重张量。我们可以通过检查权重张量的形状来看到这一点:
> self.conv1.weight.shape
torch.Size([6, 1, 5, 5])
张量的形状由下式给出:
(number of filters, number of input channels, filter height, filter width)
对relu() 函数的调用将删除所有负值并将其替换为零。我们可以通过在调用之前和之后检查张量的min()来验证这一点。
> t.min().item()
-1.1849982738494873
> t = F.relu(t)
> t.min().item()
0.0
relu() 函数可以用数学方式表示为
池化操作通过从张量中的每个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])
卷积层输入和输出的张量的形状由下式给出:
输入形状:[1, 1, 28, 28]
输出形状:[1, 6, 12, 12]
发生的每个操作的摘要:
卷积层使用六个随机初始化的5x5滤波器对输入张量进行卷积。
这样可以将高度和宽度尺寸减少四倍。
relu激活功能操作将所有负值映射为0。
这意味着张量中的所有值现在都为正。
最大池化操作从由卷积创建的六个特征图的每个2x2部分中提取最大值。
这样将高度和宽度尺寸减少了十二。
让我们看一下在执行卷积和池化操作之后计算张量的输出大小的公式。
假设有一个 n * n 输入。
假设有一个 f*f 的滤波器。
假设填充大小为 p 和步长为 s
输出尺寸 O 由以下公式给出:
假设有一个 nh×nw 的输入
假设有一个 fh×fw 的滤波器
假设填充大小为 p 和步长为 s
输出大小Oh 的高度由以下公式给出:
输出大小Ow 的高度由以下公式给出:
#3 卷积层(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 层
现在,我们只有一系列线性层,然后是非线性激活函数,直到到达输出层。
> 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]])
下表总结了形状更改操作以及每种形状的结果形状:
现在,我们应该对卷积神经网络如何转换输入张量,如何在PyTorch中调试神经网络以及如何检查所有层的权重张量有一个很好的了解。
文章中内容都是经过仔细研究的,本人水平有限,翻译无法做到完美,但是真的是费了很大功夫,希望小伙伴能动动你性感的小手,分享朋友圈或点个“在看”,支持一下我 ^_^
英文原文链接是:
https://deeplizard.com/learn/video/cin4YcGBh3Q
加群交流
欢迎小伙伴加群交流,目前已有交流群的方向包括:AI学习交流群,目标检测,秋招互助,资料下载等等;加群可扫描并回复感兴趣方向即可(注明:地区+学校/企业+研究方向+昵称)
谢谢你看到这里! ????