TensorFlow 計算模型 — — 計算圖
TensorFlow:
* Tensor:張量,可以被簡單地理解為多維度數組。
* Flow:流,表達張量之間通過計算相互轉化的過程。
計算圖Computational Graph
1. 構造計算圖
2. 運行計算圖
計算圖是什麼?
計算圖是一系列的計算操作抽象為圖中的節點。
構造一個簡單的計算圖:每個節點將0或多個tensor作為輸入,輸出一個tensor。一種類型的節點是常量節點constant,就如同tensorflow中的常數,它有0個輸入,輸出一個值。
通過 tf.get_default_graph() 函數就可以獲得當前默認的計算圖。
Tensorflow 數據模型 — — 張量
張量可以被簡單理解為多維數組。
* 零階張量表示標量(Scalar),也就是一個數。
* 一階張量表示向量(Vector),也就是一個一維數組
* n 階張量可以理解為一個 n 維數組。
在張量中並沒有真正保存數字,它保存的是如何得到這些數字的計算過程。
a = tf.constant([1.0, 2.0], name=’a’)
b = tf.constant([2.0, 3.0], name=’b’)
result = tf.add(a, b, name=’add’)
print(result)
Result:
Tensor(“add_1:0”, shape=(2,), dtype=float32)
Tensorflow 計算的結果不是一個具體的數字,而是一個張量的結構。
張量主要保存三個屬性:
1. 名字(name)
2. 維度(shape)
3. 類型(type)
張量的第三個屬性是類型(type),每一個張量會有一個唯一的類型。TensorFlow 會對參與運算的所有張量進行類型的檢查,當發現類型不匹配時會報錯。
a = tf.constant([1, 2], name=’a’)
b = tf.constant([2.0, 3.0], name=’b’)
result = tf.add(a, b, name=’add’)
Error:
ValueError: Tensor conversion requested dtype int32 for Tensor with dtype float32: ‘Tensor(“b_2:0”, shape=(2,), dtype=float32)’
不帶小數點被默認為 int32,帶小數點會默認為 float32。
在張量有一個小技巧是 result.get_shpae(),通過張量來儲存中間結果可以方便獲取中間結果。比如在 CNN 中,convolution layer 或是 max pooling layer有可能改變張量的維度,通過 result.get_shape() 來獲取結果的維度訊息可以免去人工計算的麻煩。
result.get_shape()
Result:
TensorShape([Dimension(2)])
TensorFlow 運行模型 — — 會話()
Session 擁有管理 TensorFlow 程式運行時的所有資源。所有計算完成之後需要關閉 Session 來幫助系統回收資源,否則就可能出現資源洩漏的問題。
sess = tf.Session()print(sess.run(result))sess.close()
Result:
[3. 5.]
在 python 中可以通過上下文管理器來使用 Session。
with tf.Session() as sess:
print(sess.run(result))
Result:
[3. 5.]
以下兩種寫法會有相同的結果:
with tf.Session() as sess:
print(sess.run(result))
print(result.eval())
tf.constant
在 TensorFlow 中計算圖的運算相當嚴謹,如果節點中的數值型態不一樣就會報錯。
node1 = tf.constant(3.0, tf.float32)
node2 = tf.constant(4)
tf.add(node1, node2)
Error:
Input ‘y’ of ‘Add’ Op has type int32 that does not match type float32 of argument ‘x’.
tf.placeholder
我們可以利用 tf.placeholder 在計算圖中建立空的 node,在運行計算圖時,可以將資料餵進去 tf.placeholder
# 建構計算圖
a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
add_node = a + b
輸出看看 add_node 長什麼樣子:
print(add_node)
# 運行計算圖
print(sess.run(add_node, feed_dict={a: 1, b: 3}))
print(sess.run(add_node, feed_dict={a: [1, 2], b: [3, 4]}))
輸出結果:
<tf.Tensor ‘add:0’ shape=<unknown> dtype=float32>
4.0
[4. 6.]
我們可以添加一個操作,使計算圖更加複雜:
add_and_triple = add_node * 3
add_and_triple
# 運行計算圖
print(sess.run(add_and_triple, feed_dict={a: [1, 2], b: [3, 4]}))
輸出結果:
<tf.Tensor ‘mul:0’ shape=<unknown> dtype=float32>
[12. 18.]
所以我們可以利用 tf.placeholder 來定義網路結構,後續只要在運行計算圖時,將資料餵進去 node 後,就可以運行網路。
tf.Variable
在 Machine Learning 中,需要模型可以任意輸入,為了模型具有可訓練能力,需要修正計算圖,使對於同樣的輸入得到新的輸出。tf.Variable 允許我們為計算圖添加訓練參數。
我們來建構 Variable,提供型態 (type) 和初始值:
n1 = tf.Variable(3.0, tf.float32)
常數節點在調用 tf.constant 時就被初始化,而 variable node 在調用 tf.Variable 時並不初始化,必須顯性的執行如下操作:
init = tf.global_variables_initializer()
sess.run(init)
意識到 init 對像是 Tensorflow 子圖初始化所有 global variable 是重要的,在調用 sess.run(init)
方法之前,所有 Variable 都是未初始化的。
sess.run(n1)
輸出結果:
3.0
重點來了,我們來用 tf.Variable 和 tf.placeholder 建立線型模型 (linear model):
W = tf.Variable([1., 3, 5.])
b = tf.Variable([1.], dtype=tf.float32)
X = tf.placeholder(dtype=tf.float32, shape=(3,))
net = W * X + binit = tf.global_variables_initializer()
sess.run(init)
sess.run(net, feed_dict={X: [1., 2., 3]})
這邊要特別注意的是,在定義完 tf.Variable
時,要重新初始化 Variable。
輸出結果:
array([ 2., 7., 16.], dtype=float32)
tf.train
Tensorflow提供了優化器 Optimizer 慢慢改變每個參數來最小化損失函數。最簡單的Optimizer是梯度下降gradient descent,它根據損失函數相對於該變量的導數大小來修改參數值,一般來講,手動計算導數是乏味且易出錯的,Tensorflow可以使用方法tf.gradients自動的為給定模型計算導數。優化器通常做這個工作。
# optimizer
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)
train = optimizer.minimize(loss)# training
epoch = 1000
for _ in range(epoch):
sess.run(train, feed_dict={X: [1., 2., 3.], y: [3., 6., 15.]})
以下是完整的程式碼:
W = tf.Variable([1., 3, 5.])
b = tf.Variable([1.], dtype=tf.float32)
X = tf.placeholder(dtype=tf.float32, shape=(3,))
net = W * X + binit = tf.global_variables_initializer()
sess.run(init)# label
y = tf.placeholder(dtype=tf.float32)
# loss function
square_errors = tf.square(net-y)
loss = tf.reduce_sum(square_errors)
# optimizer
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)
train = optimizer.minimize(loss)# training
epoch = 1000
for _ in range(epoch):
sess.run(train, feed_dict={X: [1., 2., 3.], y: [3., 6., 15.]})curr_W, curr_b, curr_loss = sess.run([W, b, loss],{X:[ 1 , 2 , 3], y: [3., 6., 15.]})
print ( “W: {} b: {} loss: {}”.format(curr_W, curr_b, curr_loss))
輸出結果:
W: [1.7294136 2.3647099 4.576473 ] b: [1.2705834] loss: 2.4556356947869062e-11