1. 程式人生 > >[深度學習]轉置卷積(Transposed Convolution)

[深度學習]轉置卷積(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=⌈(WF+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這個概念的原因。

五.總結

這是對於轉置卷積的基本理解。