轉置卷積
阿新 • • 發佈:2019-02-01
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