Tensorflow 實現卷積操作
恰逢新年假期,家裏的事情變得多了起來。但是十幾天零星的時間如果拿來積累,總是有些用的。近期着手於手部關鍵點檢測,讀了一篇論文 ICCV2017 Learning to Estimate 3D Hand Pose from Single RGB Images 並且查看了源代碼。之前在實驗室裏自己錄製了視頻進行了簡單的測試,但是結果差強人意。說跑題了,作者的代碼功底應該是很深厚的,下面我引用一下作者書寫的代碼,來簡單說明一下Tensorflow是如何實現卷積操作的。
這裏我羅列一下作者的代碼:
import tensorflow as tf
neg_slope_of_relu = 0.01
//Relu operation
def leaky_relu(tensor, name='relu'):
out_tensor = tf.maximum(tensor, neg_slope_of_relu * tensor, name=name)
return out_tensor
//Conv operation
def conv(in_tensor, layer_name, kernel_size, stride, out_chan, trainable=True):
with tf.variable_scope(layer_name):
in_size = in_tensor.get_shape().as_list()
strides = [1, stride, stride, 1]
kernel_shape = [kernel_size, kernel_size, in_size[3], out_chan]
kernel = tf.get_variable('weights', kernel_shape, tf.float32,
tf.contrib.layers.xavier_initializer_conv2d(), trainable=True)
tmp_result = tf.nn.conv2d(in_tensor, kernel, strides, padding='SAME')
biases = tf.get_variable('biases', [kernel_shape[3]], tf.float32, tf.constant_initializer(0.0001),
trainable=True)
out_tensor = tf.nn.bias_add(tmp_result, biases, name='out')
return out_tensor
//Conv + Relu
def conv_relu(in_tensor, layer_name, kernel_size, stride, out_chan, trainable=True):
tensor = conv(in_tensor, layer_name, kernel_size, stride, out_chan, trainable)
out_tensor = leaky_relu(tensor, name='out')
return conv_relu
代碼解釋
哎,眼神兒越來越不好使了,我就在下面解釋一下代碼的含義吧(大神請忽略…)
代碼分爲三塊:
- ReLU的實現
這個函數定義十分簡單,輸入就是一個tensor,在tensorflow中,輸入卷積神經網絡的往往是圖片,簡言之,輸入的尺寸是[batch_size,height,width,channels], 分別對應[一個batch中的圖片數量, 圖片的高, 圖片的寬,圖片的通道數]。以普通的RGB圖片爲例,假設我進行單張圖片inference操作的時候,我輸入的tensor的尺寸就是[1,256,256,3]。當輸入到ReLU層中,經過了tf.maximum()這個函數,這個函數定義在這裏,很好理解。 - 卷積的實現
s作爲一個列表,先獲取一下輸入tensor的形狀。such as [1, 256, 256, 3] ~
輸入參數:tensor,卷積層名稱,卷積核尺寸,步長,輸出tensor的通道數,是否可訓練
首先獲取輸入tensor的尺寸 (BHWC)。
其次是卷積核尺寸,[kernel_size, kernel_size, 輸入的tensor的通道數/厚度, 輸出的通道數],在本例中,卷積核的尺寸爲[kernel_size, kernel_size, 3, out_chan]。然後利用tf.get_variable()函數和類似init()的函數根據卷積核尺寸初始化卷積核。
之後是根據輸入的步長參數形成步長矩陣,一般是[1, stride, stride, 1]這樣的shape。
最後是核心函數 tf.nn.conv2d() 根據前面已經定義好或初始化完成的變量進行卷積操作。
此處還有一個biases操作,這個更好理解,獲取進行卷積之後的tensor的尺寸,然後進行add操作即可。in_tensor在經過卷積之後,channels變成了out_chan,所以此時biases的channels就是kernel_shape[3]。加上偏置就可以啦~
- 卷積 + ReLU
還是那些參數,衆所周知,經過卷積之後往往跟着池化操作,這兒就一步完成啦~
測試
import tensorflow as tf
def main():
image = tf.random_normal([1, 256, 256, 3], mean=0.0, stddev=1.0, dtype=tf.float32)
//Conv + ReLU
image_result = conv_relu(image, 'ConvReLU', 3, 1, 64)
print(image_result)
with tf.Session() as sess:
print(sess.run(image_result))
if __name__ == '__main__':
main()
//Result
Tensor('out:0', shape=(1, 256, 256, 64), dtype = float32)
很簡單吧!
明天將繼續更新後續工作哦~