Tensorflow 實現卷積操作

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)

很簡單吧!

明天將繼續更新後續工作哦~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章