[深度學習]轉置卷積(Transposed Convolution)
一.寫在前面
在GAN(Generative Adversarial Nets, 直譯為生成式對抗網路)中,生成器G利用隨機噪聲Z,生成資料。那麼,在DCGAN中,這部分是如何實現呢?這裡就利用到了Transposed Convolution(直譯為轉置卷積),也稱為Fractional Strided Convolution。那麼,接下來,從初學者的角度,用最簡單的方式介紹什麼是轉置卷積,以及在Tensorflow中如何實現轉置卷積。
二.卷積與矩陣相乘
考慮如下卷積層運算,其引數為(i=4,k=3,s=1,p=0),輸出o=2。
輸入:4 × 4 --> 16 × 1
輸入矩陣的大小為4×4,將矩陣按照從左到右,從上到下的方式,變形為長度為16的一維向量。
示意圖:
a00 | a01 | a02 | a03 |
a10 | a11 | a12 | a13 |
a20 | a21 | a22 | a23 |
a30 | a31 | a32 | a33 |
=>
a00 |
a01 |
a02 |
a03 |
a10 |
a11 |
a12 |
a13 |
a20 |
a21 |
a22 |
a23 |
a30 |
a31 |
a32 |
a33 |
卷積核:3 × 3 --> 4 × 16
按照卷積操作的原理,將3 × 3的矩陣,變形為4 × 16 的矩陣。
示意圖:
w00 | w01 | w02 |
w10 | w11 | w21 |
w20 | w21 | w22 |
=>
w00 | w01 | w02 | 0 | w10 | w11 | w12 | 0 | w20 | w21 | w22 | 0 | 0 | 0 | 0 | 0 |
0 | w00 | w01 | w02 | 0 | w10 | w11 | w12 | 0 | w20 | w21 | w22 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | w00 | w01 | w02 | 0 | w10 | w11 | w12 | 0 | w20 | w21 | w22 | 0 |
0 | 0 | 0 | 0 | 0 | w00 | w01 | w02 | 0 | w10 | w11 | w12 | 0 | w20 | w21 | w22 |
輸出:Y = CX, (4×16) × (16×1) = (4×1),則是一個[4,1]的輸出特徵矩陣,把它重新排列為2×2的輸出特徵矩陣,就可以得到最終的結果。
因此,卷積層的計算可以轉換為矩陣之間相乘。對於同一個卷積核,卷積操作是Y=C × X,那麼轉置卷積操作可以理解為Y=Transposed(C) × T。
輸入:2 × 2 --> 4 × 1
矩陣C的轉置:16 × 4
輸出: Y = CX, (16×4) × (4×1) = (16×1),則是一個[16,1]的輸出特徵矩陣,把它重新排列為4×4的輸出特徵矩陣,就可以達到轉置卷積的效果。
三.直觀理解
下面只考慮No zero padding, unit strides的情況。
舉例,輸入影象大小為2×2,想得到輸出影象大小為4×4。
思維模式1:假設輸入影象大小為4×4,輸出影象大小為2×2。在正向卷積中,卷積核的高度和寬度均為3,步長s=1,邊距p=0。將該卷積過程轉置即可。
思維模式2:直接卷積。輸入影象大小為2×2,卷積核的大小為3×3,步長s=1,邊距p=2。
示意圖如下:
此時,卷積核和步長均沒有變化。只有邊距變為2。
如何理解邊距p=2?
可以通過卷積操作中輸入與輸出影象的聯絡來理解。例如,輸出影象的左上角的畫素只與輸入影象的左上角的畫素有關,輸出影象的右下角的畫素只與輸入影象的右下角的畫素有關。因此,卷積核在做卷積時,要輸出最右最上角的一個畫素,只會利用輸入影象的最右最上角的一個畫素,其他區域均會填充0。因此,邊距p的大小為(卷積核的大小-1)。
四.在Tensorflow中實現轉置卷積
[API]:
conv2d_transpose(value,
filter,
output_shape,
strides,
padding="SAME",
data_format="NHWC",
name=None)
Args:
value: 四維tensor,型別為float,預設shape為[batch, height, width, in_channels]。`NHWC`格式,shape為[batch, height, width, in_channels];`NCHW` 格式,shape為[batch, in_channels, height, width]。
filter: 四維tensor,型別與value相同,shape為[height, width, output_channels, in_channels]。in_channels必須與value中的in_channels相同。
output_shape: 一維tensor,表示轉置卷積操作輸出的shape。取值為,[batch, height, width, in_channels]。
strides:步長。
padding:`'VALID'` 或者`'SAME'`.
令W為輸入的size,F為filter的size, S為步長,⌈⌉為向上取整符號。
對於‘VALID’,輸出的形狀計算如下:
new_height=new_width=⌈(W–F+1)S⌉
對於‘SAME’,輸出的形狀計算如下:
new_height=new_width=⌈WS⌉
舉例,當步長為2時,餘下的視窗只有一列。此時,’VALID‘會將剩餘的列進行捨棄,’SAME‘會用0將不夠的列進行填充。
data_format: 'NHWC'或者 'NCHW'。
name: 返回的tensor的名稱(可選)。
Returns:
轉置卷積操作的輸出結果,與value具有相同型別的tensor。
需要注意的是:
1.output的shape不能隨意指定,需要是可以經過filter,strides,padding可以得到的shape。
2.tf.nn.conv2d中的filter引數為[filter_height, filter_width, in_channels, out_channels],與tf.nn.conv2d_transpose中的filter的引數順序不同。
3.conv2d_transpose會計算output_shape能否通過給定的filter,strides,padding計算出inputs的維度,如果不能,則報錯。
也就是說,conv2d_transpose中的filter,strides,padding引數,與反過程中的conv2d的引數相同。
舉例:
# coding:utf-8
import tensorflow as tf
def main(_):
# 輸入4×4的單通道影象
input_ = tf.constant(1., shape = [1,4,4,1])
# 卷積核的大小為3×3×1,個數為1
w = tf.constant(1., shape = [3,3,1,1])
# 卷積:輸出2×2的單通道影象
result= tf.nn.conv2d(input_, w, strides=[1, 1, 1, 1], padding='VALID')
# 轉置卷積:輸出4×4的單通道影象
result2= tf.nn.conv2d_transpose(result, w, output_shape=[1,4,4,1], strides=[1, 1, 1, 1], padding='VALID')
with tf.Session() as sess:
init = tf.global_variables_initializer()
sess.run(init)
print '輸入4×4的單通道影象'
print sess.run(input_)
print '卷積:輸出2×2的單通道影象'
print sess.run(result)
print '轉置卷積:輸出4×4的單通道影象'
print sess.run(result2)
if __name__ == '__main__':
tf.app.run()
執行結果:
先卷積,再進行轉置卷積,得到的結果和輸入不一樣,但是shape是一樣的,說明了卷積和轉置卷積並不是完全對稱的兩個過程。這也是現在不使用deconvolution這個概念的原因。
五.總結
這是對於轉置卷積的基本理解。