1. 程式人生 > >轉置卷積

轉置卷積

deconv解卷積,實際是叫做conv_transpose, conv_transpose實際是卷積的一個逆向過程,tf 中, 編寫conv_transpose程式碼的時候,心中想著一個正向的卷積過程會很有幫助。

想象一下我們有一個正向卷積:
input_shape = [1,5,5,3]
kernel_shape=[2,2,3,1]
strides=[1,2,2,1]
padding = "SAME"

那麼,卷積啟用後,我們會得到 x(就是上面程式碼的x)。那麼,我們已知x,要想得到input_shape 形狀的 tensor,我們應該如何使用conv2d_transpose函式呢?
就用下面的程式碼

import tensorflow as tf
tf.set_random_seed(1)
x = tf.random_normal(shape=[1,3,3,1])
#正向卷積的kernel的模樣
kernel = tf.random_normal(shape=[2,2,3,1])

# strides 和padding也是假想中 正向卷積的模樣。當然,x是正向卷積後的模樣
y = tf.nn.conv2d_transpose(x,kernel,output_shape=[1,5,5,3],
    strides=[1,2,2,1],padding="SAME")
# 在這裡,output_shape=[1,6,6,3]也可以,考慮正向過程,[1,6,6,3]
# 通過kernel_shape:[2,2,3,1],strides:[1,2,2,1]也可以 # 獲得x_shape:[1,3,3,1] # output_shape 也可以是一個 tensor sess = tf.Session() tf.global_variables_initializer().run(session=sess) print(y.eval(session=sess))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

conv2d_transpose 中會計算 output_shape 能否通過給定的引數計算出 inputs的維度,如果不能,則報錯

import
tensorflow as tf from tensorflow.contrib import slim inputs = tf.random_normal(shape=[3, 97, 97, 10]) conv1 = slim.conv2d(inputs, num_outputs=20, kernel_size=3, stride=4) de_weight = tf.get_variable('de_weight', shape=[3, 3, 10, 20]) deconv1 = tf.nn.conv2d_transpose(conv1, filter=de_weight, output_shape=tf.shape(inputs), strides=[1, 3, 3, 1], padding='SAME') # ValueError: Shapes (3, 33, 33, 20) and (3, 25, 25, 20) are not compatible
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

上面錯誤的意思是:

  • conv1 的 shape 是 (3, 25, 25, 20)
  • 但是 deconv1 對 conv1 求導的時候,得到的導數 shape 卻是 [3, 33, 33, 20],這個和 conv1 的shape 不匹配,當然要報錯咯。
import tensorflow as tf
from tensorflow.contrib import slim
import numpy as np

inputs = tf.placeholder(tf.float32, shape=[None, None, None, 3])

conv1 = slim.conv2d(inputs, num_outputs=20, kernel_size=3, stride=4)

de_weight = tf.get_variable('de_weight', shape=[3, 3, 3, 20])

deconv1 = tf.nn.conv2d_transpose(conv1, filter=de_weight, output_shape=tf.shape(inputs),
                                 strides=[1, 3, 3, 1], padding='SAME')

loss = deconv1 - inputs
train_op = tf.train.GradientDescentOptimizer(0.001).minimize(loss)

with tf.Session() as sess:
    tf.global_variables_initializer().run()

    for i in range(10):
        data_in = np.random.normal(size=[3, 97, 97, 3])
        _, los_ = sess.run([train_op, loss], feed_dict={inputs: data_in})
        print(los_)
# InvalidArgumentError (see above for traceback): Conv2DSlowBackpropInput: Size of out_backprop doesn't match computed: actual = 25, computed = 33
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

如果 輸入的 shape 有好多 None 的話,那就是另外一種 報錯方式了,如上所示:
這個錯誤的意思是:

  • conv1 的 shape 第二維或第三維的 shape 是 25
  • 但是 deconv1 對 conv1 求導的時候,得到的 倒數 shape 的第二位或第三維卻是 33

至於為什麼會這樣,因為 deconv 的計算方式就是 conv 求導的計算方式,conv 的計算方式,就是 decov 求導的方式。

deconv 求導就相當於 拿著 conv_transpose 中的引數對 deconv 輸出的值的導數做卷積。

如何靈活的控制 deconv 的output shape

conv2d_transpose() 中,有一個引數,叫 output_shape, 如果對它傳入一個 int list 的話,那麼在執行的過程中,output_shape 將無法改變(傳入int list已經可以滿足大部分應用的需要),但是如何更靈活的控制 output_shape 呢?

  • 傳入 tensor
# 可以用 placeholder
outputs_shape = tf.placeholder(dtype=tf.int32, shape=[4])
deconv1 = tf.nn.conv2d_transpose(conv1, filter=de_weight, output_shape=output_shape,
                                 strides=[1, 3, 3, 1], padding='SAME')

# 可以用 inputs 的shape,但是有點改變
inputs_shape = tf.shape(inputs)
outputs_shape = [inputs_shape[0], inputs_shape[1], inputs_shape[2], some_value]
deconv1 = tf.nn.conv2d_transpose(conv1, filter=de_weight, output_shape=outputs_shape,
                                 strides=[1, 3, 3, 1], padding='SAME')                                 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10