用Pytorch理解深度学习模型中的张量维度

在使用Pytorch构建卷积神经网络(CNN)时可能会遇到有关张量维度的错误。你或许是个勤快的人绞尽脑汁在网上找到了解决办法,但是如果对输入和输出形状没有深入了解,这个错误还会再犯。本文将帮助大家理解函数要求的维度,如torch.nn.con2d层和torch.nn.linear层,它们有不同的输入和输出维度。
01 nn.Conv2d和nn.Linear的输出形状
首先我们要知道深度学习模型,如CNN和autoencoder,可以用于不同类型的输入数据:
视频,是三维的;形状(batch_size、channels、depth、height、width)用于nn.Conv3d输入。
图像,是二维的;形状(batch_size、channels、height、width)用于nn.Conv2d输入。
文本和音频,都是一维的;形状(batch_size、channels、num_features)用于nn.Conv1d输入。输入形状也可以是(seq_len, batch_size,num_features),以防我们将其传递给循环神经网络(RNN)。
一般来说,我们注意到batch size和channels大多数时候都是第一个size,并且是为了满足卷积层的函数条件而添加的:
nn.Conv1d 需要一个 3d 张量
nn.Conv2d 需要一个 4d 张量
nn.Conv3d需要一个 5d 张量

Illustration by Author. Two ways to visualize an image with shape (3,4,4)
在这些第一个size之后,以下维度会根据输入类型和任务的不同而变化。在CNN中,最常见的情况是将图像作为输入执行分类任务。所以我们将重点关注:
nn.Conv2d(in_channels,out_channels,kernel_size,stride=1,padding=0,dilation=1)
在这里:
in_channels是输入图像中的通道数,out_channels是卷积产生的通道数
处理图像时有三种可能情况:
1.如果图像是灰度的,则输入通道为1。
2.如果图像是彩色的,则输入通道为 3。
3.如果有额外的alpha通道,我们就有4个输入通道。
为了计算每个卷积层的高度和宽度的输出维度,应用池化层后,需要记住这两个公式:

上面我们看到了两个公式,但通常两者的公式是相同的。这取决于填充、膨胀和内核大小。在 CNN 中,卷积核/滤波器通常是3x3,而池化通常应用2x2窗口、步长2和无填充。因此,对于这些值,输出的宽度和高度的公式将相同。
在最后一个卷积层+池化层之后,一个或多个全连接层被添加到CNN架构中。卷积层和池化层产生的输出是3维的,但全连接层需要一个一维数组。因此,我们使用以下函数将输出平面化为一维向量:
torch.nn.Linear(in_features,out_features, bias=True)
在这里:
in_features构成了每个输入样本的大小
out_features构成每个输出样本的大小
有两个主要功能可以使输出形状变平:
image=image.view(image.size(0),-1)其中批量大小为image.size(0)。
image=torch.flatten(image.size(0),start_dim=1)
02 nn.ConvTranspose2d的输出形状
如果我们改变任务,假设我们想要构建一个模型,能够在给定数据流形的情况下重建图像,这是具有相关特征的压缩输入。
一种特定类型的前馈神经网络专门用于此任务,称为Autoencoder。它由两个网络组成:Encoder和Decoder。
第一个网络Encoder,压缩输入数据以提取最相关的信息,这些信息将包含在一个简化的空间中,称为编码空间。
第二个网络Decoder,是相反的过程。它从这个编码空间中恢复数据并重建原始图像。
有torch.nn.Conv2d层和torch.nn.linear层,接下来还有另一种类型的层:
nn.ConvTranspose2d(in_channels,out_channels,kernel_size,stride=1,out_padding=0,padding=0,dilation=1)
这些参数与nn.Conv2d函数中的相同。Decoder由转置卷积层组成,这些层学习“上采样”压缩表示。所以这些函数是卷积运算的逆运算。因此,通道数、宽度和高度将逐层增加,而不是减少。
计算每个卷积层的高度和宽度的输出维度,应用池化层后,需要记住这两个公式:
out_height=(in_height-1)*stride[0]-2*padding[0]+dilation[0]*(kernel_size[0]-1)+output_padding[0]+1
out_width=(in_width-1)*stride[1]-2*padding[1]+dilation[1]*(kernel_size[1]-1)+output_padding[1]+1
03 CNN关于CIFAR10的例子

我们来看一个摘自Pytorch官网cifar_tutorial的例子:
在CNN架构中,我们有两个卷积层和三个线性层。每个卷积层后跟一个最大池化层和作为非线性激活函数的ReLU。可以快速观察到第一个卷积层的输出通道数等于第二个卷积层的输入通道数。
同样,如果你检查前一个线性层的输出特征的数量和下一个线性层的输入特征的数量。
接下来逐步解释输入和输出形状。
在此之前首先需要问自己:我的图像的输入形状是什么?它是彩色的还是不彩色的?
在示例中,CIFAR10数据集包含大小为3 x 32 x 32和3通道彩色的图像。要检查训练集的维度,在应用 DataLoader之前,先编写:
trainset[0][0].shape

要在创建DataLoader对象后查看形状:
返回的第一个形状是图像的形状,而另一个是目标的形状。
函数iter提供可迭代的数据集,而next用于获取第一次迭代的第一项。
这样我们就可以看到第一张图片的形状,(4, 3, 32, 32),其中4是选择的batch size,3是通道数,宽高都是32。
在第一个卷积层+最大池化层之后,计算输出形状:
out_width=out_height=(in_dim-kernel_size)/stride+1=(32–5)/1+1=28
out_width=out_height=28/2=14
我们对3 x 32 x 32 的图像进行多次卷积,每次使用不同的5 x 5大小的过滤器,获得6 x 28 x 28的输出。
应用max-pooling后,窗口大小和stride配置将产生的输出大小减半,即6 x 14 x 14输出。
经过第二个卷积层+maxpooling,得到:
out_width=out_height=(in_dim-kernel_size)/stride+1=(14–5)/1+1=10
out_width=out_height=10/2=5
在第二个卷积层+maxpooling之后,我们将3D输出平面化为一维向量:
out_dim = out_channels x out_height x out_width = 16 x 5 x 5
最后,需要在最后一个全连接层(称为输出层)中指定输出隐藏单元的数量。目标是将图像分类为10个类别之一,所以输出特征的数量将是 10。
04 CIFAR10上的自动编码器示例
让我们看一个来自analyticsindiamag网站的示例,稍有改动。卷积自编码器再次应用于 CIFAR10 数据集。
我们可以逐步计算形状,首先,原始图像是 3 x 32 x 32。
先看Encoder的形状:
[CONV1]: out_width=out_height=(32–3)/2+1=29/2+1 = 15
[CONV2]: out_width=out_height=(15–3)/2+1=7
[flatten]: 7x7x16
[LINEAR]: d=2
一旦定义了编码器,解码器将恢复编码空间d中的信息,我们将重建图像,需要3x32x32大小:
[LINEAR]: 7x7x16
[unflatten]: (16,7,7)
[CONV1]: out_width=out_height=(7–1)*2+3=15
[CONV2]: out_width=out_height=(15–1)*2+3=28+1+3=32
总结
图像的输入形状是(batch_size,channels,depth,height,width)。
out_width=out_height=(in_width-2*padding-kernel_size)/stride+1在nn.Conv2d的大多数情况下。
第一个完全连接的层将接收一个一维矢量形状(out_channels x out_height x out_width)。
out_width=out_height=(in_width–1)*stride+kernel_size-2*padding+out_padding+1 在大多数的情况下ConvTranspose2d。
学姐希望本教程能让大家学会使用Pytorch构建CNN架构。有什么学习路上的难题随时来找学姐呀!

如有问题或错误可以在评论区讨论哈!
参考资料:
https://pub.towardsai.net/understanding-tensor-dimensions-in-deep-learning-models-with-pytorch-4ee828693826
https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html
https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html
https://analyticsindiamag.com/how-to-implement-convolutional-autoencoder-in-pytorch-with-cuda/
三连一下,鼓励学姐一下吧!