1. 程式人生 > 實用技巧 >轉置卷積的理解

轉置卷積的理解

摘要

本文探討了卷積和轉置卷積的尺寸計算過程,並通過 Pytorch 進行了驗證。

卷積

如上圖所示,輸入特徵圖為 5×5 ,補零為1( padding = 1 ), 使用 3×3 卷積核,卷積核移動的步幅為1( stride = 1 ),則經過卷積操作後,輸出特徵圖的尺寸仍為 5×5 。可以看到,特徵圖的尺寸未發生變化,而通道數則取決於卷積核的個數。正是這個良好的特性, 使得 3×3 卷積被廣泛地應用於卷積神經網路的前端特徵提取部分。

以上所涉及到的特徵圖尺寸計算,可用下面的公式來表示:

\[W_{\text {out }}=\left\lfloor\frac{W_{\text {in }}+2 P-F}{S}\right\rfloor+1 \]

其中:

  • $ W_{\text {in }} $ 為輸入的特徵圖寬度或者高度
  • $ W_{\text {out }} $ 為輸出的特徵圖寬度或者高度
  • \(P\) 為 padding取值
  • $ F $ 為卷積核的大小(一般卷積核為方形的)
  • \(S\) 為卷積核移動的步幅
  • \(\lfloor\cdot\rfloor\) 為向下取整函式

一般情況下,很少出現需要用到向下取整的情況,所以,上式又可以寫作:

\[W_{\text {out }}=\frac{W_{\text {in }}+2 P-F}{S}+1 \]

對上面的圖,進行驗證:

\[5=\frac{5 + 2*1 - 3}{1}+1 \]

所以,只要合理地設定卷積核、補零、以及移動步幅,就能利用卷積對特徵圖的尺寸大小進行調整。

轉置卷積

對於卷積來說,似乎無論怎麼卷積,輸出特徵圖的大小隻能保持在輸入特徵圖大小的範圍內,是一種下采樣操作。

但是,其實只要調整卷積的一些引數,就可以將小的特徵圖通過 "卷積" 變成大的特徵圖,實現上取樣操作。這便是所謂的 轉置卷積

如上圖所示,輸入特徵圖為 5×5 ,補零為2( padding = 2 ), 使用 3×3 卷積核,卷積核移動的步幅為1( stride = 1 ),則經過卷積操作後,輸出特徵圖的尺寸為 7×7。

關於轉置卷積這個名詞的解釋,借鑑的是線性代數中的轉置操作。假設輸入特徵圖為 \(X\) , 輸出特徵圖為 \(Y\) ,卷積操作為 \(C\),那麼從輸出特徵圖和輸入特徵圖的關係可以表示為:

\[Y=CX \]

如果反過來,已知輸出特徵圖 \(Y\),要求輸入特徵圖 \(X\),則可以由下式得到:

\[X=C^TY \]

這裡的 \(C^T\) 就是我們所謂的轉置卷積,或者說反捲積。

上述轉置卷積過程,輸入特徵圖與輸出特徵圖之間的尺寸換算關係仍然可以用前面的公式來計算,即:

\[W_{\text {out }}=\frac{W_{\text {in }}+2 P-F}{S}+1 \]

對上面的圖進行驗證:

\[7=\frac{5 + 2*2 - 3}{1}+1 \]

轉置卷積一般可用於上取樣操作,放大特徵圖。假設需要完成 n 倍上取樣操作,則這些引數之間應該滿足:

\[W=\frac{nW+2 P-F}{S}+1 \]

然後可以推算出:

\[\left\{ {\begin{array}{*{20}{l}} {S = n} \\ {F - S - 2P = 0} \end{array}} \right. \]

上取樣倍數一般是2的倍數,於是有:

\[\left\{ {\begin{array}{*{20}{l}} {S = 2k} \\ {P = t} \\ {F = 2\left( {k + t} \right)} \end{array}} \right.\ \]

例項驗證

卷積與轉置卷積在 Pytorch 中 均為內建的函式,我們使用 Pytorch 對上面的結論進行驗證,加深理解。

import torch

# ------ 隨機初始化一個 256*256 的 輸入特徵圖 --------  #

x = torch.randn((1,1,256,256))
x.shape

# torch.Size([1, 1, 256, 256])

# ------ 驗證 3×3 卷積 --------  #

Conv_3_by_3 = torch.nn.Conv2d(in_channels=1,out_channels=1,kernel_size=3,padding=1,stride=1)
y1 = Conv_3_by_3(x)
y1.shape

# torch.Size([1, 1, 256, 256])

# ------ 驗證2倍上取樣 F=2, P=0, S=2 --------  #

Conv_transpose_1 = torch.nn.ConvTranspose2d(in_channels=1,out_channels=1,kernel_size=2, padding=0, stride=2)
y2 = Conv_transpose_1(x)
y2.shape

# torch.Size([1, 1, 512, 512])

# ------ 驗證2倍上取樣 F=4, P=1, S=2 --------  #

Conv_transpose_2 = torch.nn.ConvTranspose2d(in_channels=1,out_channels=1,kernel_size=4, padding=1, stride=2)
y3 = Conv_transpose_2(x)
y3.shape

# torch.Size([1, 1, 512, 512])

可以看到,執行得到的結果與前面所分析的一樣。

參考文獻

[1] A guide to convolution arithmetic for deeplearning