卷積圖解_深入解讀反捲積網路(附實現程式碼)
技術標籤:卷積圖解
一、反捲積的基本概念
反捲積網路在許多領域諸如卷積網路視覺化(參考卷積神經網路視覺化的探索)、語義分割(參考Michael:一文弄懂文字檢測演算法Corner(附原始碼))有應用。
vdumoulin/conv_arithmetic裡有反捲積的動圖。
參考如何理解深度學習中的deconvolution networks?,反捲積更準確來說是轉置卷積,比如在TensorFlow中的呼叫就是tf/nn/conv2d_transpose,該函式這麼形容反捲積:
This operation is sometimes called "deconvolution" afterDeconvolutional Networks, but is really the transpose (gradient) ofconv2d
rather than an actual deconvolution.
用公式可以這麼表達:
卷積運算可表示為:
,如果是數學關係上的逆卷積則應該表示為: ,但是反捲積真實的關係應該為: 。至於反捲積層的核引數初始化及訓練中的變化情況,以及與卷積層的核引數有沒有關係,我認為 @愚蠢隊長 的話還是有點意思的(這裡推一篇我的文章神經網路是怎麼“卷積”的):):
先要理解卷積,理解卷積之後,自然就理解反捲積了。
下面就使用Numpy復現反捲積的工作原理。
二、使用Numpy復現反捲積
首先我們去TensorFlow原始碼裡找答案吧,API提示原始碼位於nn_ops.py#L2080-L2147,經過一番查詢發現函式指向nn_ops.py#L2017:
filter = deprecation.deprecated_argument_lookup( "filters", filters, "filter", filter) padding, explicit_paddings = _convert_padding(padding) return gen_nn_ops.conv2d_backprop_input( input_sizes, filter, out_backprop, strides, padding, use_cudnn_on_gpu, explicit_paddings, data_format, dilations, name)
注意這裡只改變了pading,filter是沒有改變的,關鍵的是,最終指向了卷積的反向推導部分,又應了本文開頭的那句話:
先要理解卷積,理解卷積之後,自然就理解反捲積了。
這裡我們還是先推導卷積的公式吧,深入解讀卷積網路的工作原理從im2col演算法推導了卷積的執行原理,但這個實際上在本文是執行不了的,畢竟卷積運算可表示為:
,構造的卷積矩陣 ,而不是輸入矩陣 。因此,本文參考抽絲剝繭,帶你理解轉置卷積(反捲積),首先構造一個演算法實現卷積運算
,這裡考慮工作量,只考慮batch_size、輸入channel、輸出channel均為1 的情況,且padding為"VALID",相關的程式碼位於test_conv_transpose.py。首先需要對輸入展開成列:
input_np_flattern = np.reshape(input_np, newshape=[input_size*input_size, 1])
之後需要構造卷積矩陣:
filter_np_matrix = np.zeros((output_size, output_size, input_size, input_size))
for h in range(output_size):
for w in range(output_size):
start_h = h*stride
start_w = w*stride
end_h = start_h + filter_size
end_w = start_w + filter_size
filter_np_matrix[h, w, start_h:end_h, start_w:end_w] = filter_np
原理還是很簡單的,根據步長等確立卷積區域,當然,最後要reshape成合適的維度:
filter_np_matrix = np.reshape(filter_np_matrix, newshape=[output_size*output_size, input_size*input_size])
最後矩陣相乘就得到輸出矩陣了,reshape成合適的規格即可:
output_np = np.dot(filter_np_matrix, input_np_flattern)
output_np = np.reshape(output_np, newshape=[output_size, output_size])
實驗部分與深入解讀卷積網路的工作原理是一樣的,輸入是
,卷積核為 ,stride為2,padding為"VALID"。使用Numpy的輸出為:
output_np = [[312. 384.],[672. 744.]]
output_np.shape = (2, 2)
使用TensorFlow的輸出為:
output_tf = [[[[312.],[384.]],[[672.],[744.]]]]
驗證環節與深入解讀卷積網路的工作原理相同,這裡就不驗證了。
下面開始講反捲積部分。
首先與卷積部分相同,需要對輸入展開成列:
output_np_flattern = np.reshape(output_np, newshape=[output_size*output_size, 1])
之後執行
操作即可:output_np_transpose = np.dot(filter_np_matrix.T, output_np_flattern)
output_np_transpose = np.reshape(output_np_transpose, newshape=[input_size, input_size])
得到的輸出是:
output_np_transpose = [[ 0. 312. 624. 384. 768.]
[ 936. 1248. 2712. 1536. 1920.]
[1872. 2856. 6144. 3432. 4560.]
[2016. 2688. 5592. 2976. 3720.]
[4032. 4704. 9840. 5208. 5952.]]
output_np_transpose.shape = (5, 5)
至於TensorFlow的程式碼,首先需要構造output_shape,這裡參考conv_arithmetic.html#transposed-convolution-arithmetic,可得到輸出尺寸為:
這個公式實際上是卷積輸出尺寸的逆公式,之所以不唯一原因在於向下取整這個操作,這裡使得輸出尺寸由卷積操作的輸入決定:
output_shape = [batch_size, input_size, input_size, input_channel]
然後進行反捲積操作:
output_tf_transpose = tf.nn.conv2d_transpose(output_tf, filter_tf, output_shape, [1, stride, stride, 1], padding)
得到的輸出是:
output_tf_transpose = [[[[ 0.],[ 312.],[ 624.],[ 384.],[ 768.]],
[[ 936.],[1248.],[2712.],[1536.],[1920.]]
[[1872.],[2856.],[6144.],[3432.],[4560.]]
[[2016.],[2688.],[5592.],[2976.],[3720.]]
[[4032.],[4704.],[9840.],[5208.],[5952.]]]]
可見兩者是完全相同的。
抽絲剝繭,帶你理解轉置卷積(反捲積)還提供了一個形象的圖解去解釋反捲積的工作原理,並在TensorFlow下驗證了這個想法,也是值得一讀的。
孫小也:反捲積(Transposed Convolution)詳細推導裡面關於反捲積的內容也相當不錯。
【已完結】