上一篇Hello, TensorFlow!中的代碼還未解釋,本篇介紹TensorFlow核心編程的幾個基本概念后,那些Python代碼就很容易理解了。
與TensorFlow核心(Core)相對的是TensorFlow提供的高級API。后者是基于前者構建的。對于機器學習研究以及需要對編程、模型完全控制的場景,TensorFlow核心編程是首選。如果為了快速、便捷的執行一個模型訓練任務,那么高級的API更容易使用,輸出也更具一致性。作為研究學習,顯然需要從更基礎的TensorFlow核心編程開始。
張量
Tensor(張量)是TensorFlow中最核心的數據結構單元,它可以表示任意維數的數組,維度用rank(階)表示。
可以通過下面的例子來理解張量:
- 3 # 一個0階的張量;它是一個標量,形狀為shape[];
- [1. ,2., 3.] # 一個1階的張量;它是一個向量,形狀為shape[3];
- [[1., 2., 3.], [4., 5., 6.]] # 一個2階的張量;它是一個矩陣,形狀為shape[2,3];
- [[[1., 2., 3.]], [[7., 8., 9.]]] # 一個3階的張量;形狀為shape[2, 1, 3]。
由此可見,如果要做矩陣運算,使用上面第3種方式來表達即可。
注意,張量(Tensor)并非TensorFlow的內部概念,它是一個通用的數學概念,有非常豐富的內涵。
計算圖
TensorFlow核心編程,通常是由兩大階段組成:
- 1 構建計算圖
- 2 運行計算圖
計算圖,The Computational Graph,是由計算節點(node)構成的圖。
節點,node,代表一種運算操作,輸入≥0個張量,輸出1個張量,下圖右側是一個加法節點,接受兩個輸入:
TensorFlow提供了很多的API。在Python中使用它,只需要一行導入語句,即可訪問TensorFlow的所有類和方法:
import tensorflow as tf
上面就是由3個節點構建的計算圖,Python代碼如下:
import tensorflow as tf
node1 = tf.constant(3.0, tf.float32)
node2 = tf.constant(4.0) # also tf.float32 implicitly
node3 = tf.add(node1, node2)
sess = tf.Session()
print("node3: ", node3)
print("sess.run(node3): ",sess.run(node3))
程序輸出:
node3: Tensor("Add_2:0", shape=(), dtype=float32)
sess.run(node3): 7.0
一些說明:
- 代碼分3塊:導入語句、構建計算圖和運行計算圖;
- node1和node2是常量節點,常量節點:沒有輸入,輸出是事先存儲在其內部的值;
- node3是一個加法操作,2個輸入分別是node1和node2的輸出,輸出是1個相加后的結果;
- 構建好計算圖之后,如果直接打印node3,只會打印出該節點的相關信息,但是計算并沒有執行;
- 只有通過sess.run運行計算圖,才會看到node3真正的輸出張量的值。
Session
上節的示例代碼中,計算圖構建完成后,最后讓計算圖執行運算的是Session的Run方法。Session封裝了對TensorFlow運行時的控制,及其狀態,為用戶提供了交互的接口。
計算圖,Why?
當了解了張量和計算圖,也許覺得它不難理解,Python的Numpy不也可以提供矩陣運算嗎?
的確,Numpy提供了大量處理矩陣的函數,而且其內部是由最高效的C語言實現的。但是要注意C語言與Python之間的交互是有成本的。舉個例子:
import numpy as np
a = np.array([[1, 2, 3],[4, 5, 6],[7, 8, 9]])
b = a.copy()
c = a * b
d = np.dot(a, c)
print(c)
print(d)
程序輸出:
[[1 4 9]
[16 25 36]
[49 64 81]]
[[180 246 324]
[378 525 702]
[576 804 1080]]
上面的代碼,首先構建了兩個矩陣a和b,然后a和b進行Hadamard乘積得到c,最后用a和c進行矩陣乘法得到d。代碼上十分干凈利落,接下來分析下C語言和Python的交互成本:
每進行一次矩陣操作,python層都要獲得計算結果,所以每行矩陣操作代碼都造成一次C語言和Python之間的交互。numpy不僅要計算,還得“折返跑”。
到現在,你或許已經猜到TF的計算圖的工作方式了。在構建計算圖時,每一步操作的返回值并不是計算結果,而是一個節點。直到運行sess.run
,TF會一口氣從頭到尾(目標節點)把運算做完后進行一次輸出,中間路過的節點根本“不停車”。而這種連續的管線操作才有可能讓其充分的利用GPU,以及分布式處理帶來的加速。這就是計算圖的先進之處。
盡管TensorFlow被廣泛的應用于深度神經網絡方面,但是正如上面的分析,它更是一個基于計算圖的通用數值計算庫。TF官方如此寫道:
TensorFlow is a powerful library for doing large-scale numerical computation. One of the tasks at which it excels is implementing and training deep neural networks.
其他類型節點
前面的代碼中,包含了兩種類型的節點,常量節點和操作節點,本節再介紹幾個重要的節點:
- 占位節點
- 變量節點
占位節點
占位節點,可以在構建計算圖階段先定義節點(只需定義類型),而在稍后的運行計算圖時提供節點的值。使用tf.placeholder
生成。
a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
adder_node = a + b
print(sess.run(adder_node, {a: 3, b:4.5}))
print(sess.run(adder_node, {a: [1,3], b: [2, 4]}))
程序輸出:
7.5
[ 3. 7.]
一些說明:
- a和b都是float32類型的占位節點;
- adder_node = a + b,會產生一個操作節點,同tf.add(a, b);
- 運行計算圖時,必須提供占位節點的值;
- 占位節點只負責占位,但是無記憶,運行計算圖提供的值是臨時性的。
變量節點
比占位節點更加靈活的、即可以動態修改、又具有記憶的節點是變量節點,使用tf.Variable
生成。
W = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
x = tf.placeholder(tf.float32)
linear_model = W * x + b
init = tf.global_variables_initializer()
sess.run(init)
print(sess.run(linear_model, {x:[1,2,3,4]}))
fixW = tf.assign(W, [-1.])
fixb = tf.assign(b, [1.])
sess.run([fixW, fixb])
print(sess.run(linear_model, {x:[1,2,3,4]}))
程序輸出:
[ 0. 0.30000001 0.60000002 0.90000004]
[ 0. -1. -2. -3.]
一些說明:
- 變量節點在定義時,需提供初始值和類型;
- 通過tf.global_variables_initializer得到初始化器,需要sess.run后才完成初始化;
- 通過tf.assign動態改變變量節點的值,需要sess.run完成賦值。
詞匯表
- rank: 階,表示張量的維數;
- scalar: 標量,相對于向量而言;
- tensor: 張量,TensorFlow定義的核心的數據單元;
附完整代碼
import tensorflow as tf
node1 = tf.constant(3.0, tf.float32)
node2 = tf.constant(4.0) # also tf.float32 implicitly
node3 = tf.add(node1, node2)
sess = tf.Session()
print("node3: ", node3)
print("sess.run(node3): ", sess.run(node3))
a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
adder_node = a + b # + provides a shortcut for tf.add(a, b)
print(sess.run(adder_node, {a: 3, b: 4.5}))
print(sess.run(adder_node, {a: [1, 3], b: [2, 4]}))
W = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
x = tf.placeholder(tf.float32)
linear_model = W * x + b
init = tf.global_variables_initializer()
sess.run(init)
print(sess.run(linear_model, {x: [1, 2, 3, 4]}))
fixW = tf.assign(W, [-1.])
fixb = tf.assign(b, [1.])
sess.run([fixW, fixb])
print(sess.run(linear_model, {x: [1, 2, 3, 4]}))
共享協議:署名-非商業性使用-禁止演繹(CC BY-NC-ND 3.0 CN)
轉載請注明:作者黑猿大叔(簡書)