在上一篇文章中,我們介紹了使用TensorFlow框架有兩部分組成:構建計算圖,用會話啓動計算圖。今天我們來介紹 tf.placeholder,feed_dict,實現一個 Wx+b 的函數。本文章仍然保持不夠專業的傳統,只求達到效果,可以實現一些東西;或者說“路子比較野”。同時確實不一定保證章節與章節之間條理清晰。
首先還是上一段可執行的代碼。
import tensorflow as tf
# 開始構建計算圖
W = tf.constant(
[[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0]]
)
# 注意 x 和 b 是列向量
x = tf.placeholder(tf.float32, shape=[3, 1])
b = tf.constant([[1.0], [2.0]])
result1 = tf.matmul(W, x) + b
result2 = result1 + b
# 結束構建計算圖
init = tf.global_variables_initializer()
with tf.Session() as sess:
# get_result1 = sess.run(result1, feed_dict={x: [[3.0], [4.0], [5.0]]})
get_result1, get_result2 = sess.run([result1, result2], feed_dict={x: [[3.0], [4.0], [5.0]]})
print(get_result1)
print(get_result2)
這段代碼中構建計算圖的部分比較長,我仍然會細緻地講解。
首先是W矩陣,我把它設爲 2 × 3 的矩陣。根據上一節的講解,它是一個常量。在TensorFlow裏,需要特別注意矩陣和向量的shape,即線性代數裏學的,2 × 3 的矩陣只能和 3 × n 的矩陣相乘,如果在報錯信息裏面看到“shape”的字樣,十有八九是矩陣的大小搞錯了。
講真,我真的很佩服那些學數學的,是如何記住那些矩陣的shape,保證寫出來是對的,當然我確實也記不住大寫的是矩陣,小寫的是向量這樣的規則(Is it right?)。我看公式時總是會想着shape是多少,這樣代碼跑出來是怎樣的效果。
x = tf.placeholder(tf.float32, shape=[3, 1])
b = tf.constant([[1.0], [2.0]])
聲明 x 是一個placeholder,就是說假設這裏有一個矩陣,矩陣的大小是shape決定的。在運行計算圖的時候, x 會被填入相應的數據,這個在稍後會說明。總之它是一個 3 × 1 的列向量。同樣 b 也是一個 2 × 1 的列向量。
如果你需要 b 是行向量呢?如下所示,自行注意其中的差別。記不住的話,寫代碼的時候多試幾次,調試到正確爲止。
b = tf.constant([1.0, 2.0])
然後關於我們希望獲得的結果:
result1 = tf.matmul(W, x) + b
result2 = result1 + b
其實寫result1這一行就可以了,result2只是爲了說明一個技巧。tf.matmul是TensorFlow中的矩陣乘法運算。爲什麼不直接用“*”呢?我也是寫到這邊的時候剛剛想到的,然後測試了一下,“*”好像是向量乘法(就是,[1, 2, 3] * [4, 5, 6] = [4, 10, 18]),沒有嚴格測試,歡迎指正。
總之result1就是Wx+b在計算圖中的結果。注意計算圖中的變量類型都是tensor,必須依靠會話啓動計算圖才能得到結果。(不信可以試試直接print,啥也看不到)
get_result1, get_result2 = sess.run([result1, result2], feed_dict={x: [[3.0], [4.0], [5.0]]})
我們剛剛說了,構建計算圖時假設 x 是一個矩陣,因爲我們也沒告訴它實際的數值。在這行代碼中,我們使用feed_dict這個參數,給 x 傳入一個列向量。這樣可以一次性得到result1和result2的結果。可以使用構建一個帶tensor的list在一行代碼中獲取所有的tensor在計算後對應的值。這裏還有個細節,result2是以來result1的。在獲取result2的時候,我覺得,只是個人覺得,計算圖只會被計算一次。就是說,獲取result2的時候,計算圖不會從頭計算。(我們能想到的這麼簡單的優化,TensorFlow團隊應該也想得到,除非不可爲)只計算一次的好處就是,如果你的計算圖流程特別長,那麼只計算一次的話可以大大提高計算速度,節約計算資源。雖然只是個人猜測,有機會可以測試一下,但直接寫在一行代碼裏會合適一些。
今天就講到這兒吧,建議把代碼手敲一遍,然後嘗試各種改動,調戲一下代碼,下一節我們將講變量和優化器。