用Pytorch理解深度學(xué)習(xí)模型中的張量維度

在使用Pytorch構(gòu)建卷積神經(jīng)網(wǎng)絡(luò)(CNN)時可能會遇到有關(guān)張量維度的錯誤。你或許是個勤快的人絞盡腦汁在網(wǎng)上找到了解決辦法,但是如果對輸入和輸出形狀沒有深入了解,這個錯誤還會再犯。本文將幫助大家理解函數(shù)要求的維度,如torch.nn.con2d層和torch.nn.linear層,它們有不同的輸入和輸出維度。
01 nn.Conv2d和nn.Linear的輸出形狀
首先我們要知道深度學(xué)習(xí)模型,如CNN和autoencoder,可以用于不同類型的輸入數(shù)據(jù):
視頻,是三維的;形狀(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),以防我們將其傳遞給循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)。
一般來說,我們注意到batch size和channels大多數(shù)時候都是第一個size,并且是為了滿足卷積層的函數(shù)條件而添加的:
nn.Conv1d?需要一個 3d 張量
nn.Conv2d?需要一個 4d 張量
nn.Conv3d需要一個 5d 張量

Illustration by Author. Two ways to visualize an image with shape (3,4,4)
在這些第一個size之后,以下維度會根據(jù)輸入類型和任務(wù)的不同而變化。在CNN中,最常見的情況是將圖像作為輸入執(zhí)行分類任務(wù)。所以我們將重點(diǎn)關(guān)注:
nn.Conv2d(in_channels,out_channels,kernel_size,stride=1,padding=0,dilation=1)
在這里:
in_channels是輸入圖像中的通道數(shù),out_channels是卷積產(chǎn)生的通道數(shù)
處理圖像時有三種可能情況:
1.如果圖像是灰度的,則輸入通道為1。
2.如果圖像是彩色的,則輸入通道為 3。
3.如果有額外的alpha通道,我們就有4個輸入通道。
為了計算每個卷積層的高度和寬度的輸出維度,應(yīng)用池化層后,需要記住這兩個公式:

上面我們看到了兩個公式,但通常兩者的公式是相同的。這取決于填充、膨脹和內(nèi)核大小。在 CNN 中,卷積核/濾波器通常是3x3,而池化通常應(yīng)用2x2窗口、步長2和無填充。因此,對于這些值,輸出的寬度和高度的公式將相同。
在最后一個卷積層+池化層之后,一個或多個全連接層被添加到CNN架構(gòu)中。卷積層和池化層產(chǎn)生的輸出是3維的,但全連接層需要一個一維數(shù)組。因此,我們使用以下函數(shù)將輸出平面化為一維向量:
torch.nn.Linear(in_features,out_features, bias=True)
在這里:
in_features構(gòu)成了每個輸入樣本的大小
out_features構(gòu)成每個輸出樣本的大小
有兩個主要功能可以使輸出形狀變平:
image=image.view(image.size(0),-1)其中批量大小為image.size(0)。
image=torch.flatten(image.size(0),start_dim=1)
02 nn.ConvTranspose2d的輸出形狀
如果我們改變?nèi)蝿?wù),假設(shè)我們想要構(gòu)建一個模型,能夠在給定數(shù)據(jù)流形的情況下重建圖像,這是具有相關(guān)特征的壓縮輸入。
一種特定類型的前饋神經(jīng)網(wǎng)絡(luò)專門用于此任務(wù),稱為Autoencoder。它由兩個網(wǎng)絡(luò)組成:Encoder和Decoder。
第一個網(wǎng)絡(luò)Encoder,壓縮輸入數(shù)據(jù)以提取最相關(guān)的信息,這些信息將包含在一個簡化的空間中,稱為編碼空間。
第二個網(wǎng)絡(luò)Decoder,是相反的過程。它從這個編碼空間中恢復(fù)數(shù)據(jù)并重建原始圖像。
有torch.nn.Conv2d層和torch.nn.linear層,接下來還有另一種類型的層:
nn.ConvTranspose2d(in_channels,out_channels,kernel_size,stride=1,out_padding=0,padding=0,dilation=1)
這些參數(shù)與nn.Conv2d函數(shù)中的相同。Decoder由轉(zhuǎn)置卷積層組成,這些層學(xué)習(xí)“上采樣”壓縮表示。所以這些函數(shù)是卷積運(yùn)算的逆運(yùn)算。因此,通道數(shù)、寬度和高度將逐層增加,而不是減少。
計算每個卷積層的高度和寬度的輸出維度,應(yīng)用池化層后,需要記住這兩個公式:
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關(guān)于CIFAR10的例子

我們來看一個摘自Pytorch官網(wǎng)cifar_tutorial的例子:
在CNN架構(gòu)中,我們有兩個卷積層和三個線性層。每個卷積層后跟一個最大池化層和作為非線性激活函數(shù)的ReLU。可以快速觀察到第一個卷積層的輸出通道數(shù)等于第二個卷積層的輸入通道數(shù)。
同樣,如果你檢查前一個線性層的輸出特征的數(shù)量和下一個線性層的輸入特征的數(shù)量。
接下來逐步解釋輸入和輸出形狀。
在此之前首先需要問自己:我的圖像的輸入形狀是什么?它是彩色的還是不彩色的?
在示例中,CIFAR10數(shù)據(jù)集包含大小為3 x 32 x 32和3通道彩色的圖像。要檢查訓(xùn)練集的維度,在應(yīng)用 DataLoader之前,先編寫:
trainset[0][0].shape

要在創(chuàng)建DataLoader對象后查看形狀:
返回的第一個形狀是圖像的形狀,而另一個是目標(biāo)的形狀。
函數(shù)iter提供可迭代的數(shù)據(jù)集,而next用于獲取第一次迭代的第一項。
這樣我們就可以看到第一張圖片的形狀,(4, 3, 32, 32),其中4是選擇的batch size,3是通道數(shù),寬高都是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 的圖像進(jìn)行多次卷積,每次使用不同的5 x 5大小的過濾器,獲得6 x 28 x 28的輸出。
應(yīng)用max-pooling后,窗口大小和stride配置將產(chǎn)生的輸出大小減半,即6 x 14 x 14輸出。
經(jīng)過第二個卷積層+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
最后,需要在最后一個全連接層(稱為輸出層)中指定輸出隱藏單元的數(shù)量。目標(biāo)是將圖像分類為10個類別之一,所以輸出特征的數(shù)量將是 10。
04 CIFAR10上的自動編碼器示例
讓我們看一個來自analyticsindiamag網(wǎng)站的示例,稍有改動。卷積自編碼器再次應(yīng)用于 CIFAR10 數(shù)據(jù)集。
我們可以逐步計算形狀,首先,原始圖像是 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
一旦定義了編碼器,解碼器將恢復(fù)編碼空間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
總結(jié)
圖像的輸入形狀是(batch_size,channels,depth,height,width)。
out_width=out_height=(in_width-2*padding-kernel_size)/stride+1在nn.Conv2d的大多數(shù)情況下。
第一個完全連接的層將接收一個一維矢量形狀(out_channels x out_height x out_width)。
out_width=out_height=(in_width–1)*stride+kernel_size-2*padding+out_padding+1 在大多數(shù)的情況下ConvTranspose2d。
學(xué)姐希望本教程能讓大家學(xué)會使用Pytorch構(gòu)建CNN架構(gòu)。有什么學(xué)習(xí)路上的難題隨時來找學(xué)姐呀!

如有問題或錯誤可以在評論區(qū)討論哈!
參考資料:?
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/
三連一下,鼓勵學(xué)姐一下吧!