TensorFlow從0到1 - 2 - TensorFlow核心編程

TensorFlow從0到1系列回顧

上一篇Hello, TensorFlow!中的代碼還未解釋,本篇介紹TensorFlow核心編程的幾個基本概念后,那些Python代碼就很容易理解了。

與TensorFlow核心(Core)相對的是TensorFlow提供的高級API。后者是基于前者構建的。對于機器學習研究以及需要對編程、模型完全控制的場景,TensorFlow核心編程是首選。如果為了快速、便捷的執行一個模型訓練任務,那么高級的API更容易使用,輸出也更具一致性。作為研究學習,顯然需要從更基礎的TensorFlow核心編程開始。

Computational Graph

張量

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的交互成本:

overhead

每進行一次矩陣操作,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]}))

下載 tf_2_manual.py

上一篇 1 Hello, TensorFlow!
下一篇 3 人類學習的啟示


共享協議:署名-非商業性使用-禁止演繹(CC BY-NC-ND 3.0 CN)
轉載請注明:作者黑猿大叔(簡書)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • TF API數學計算tf...... :math(1)剛開始先給一個運行實例。tf是基于圖(Graph)的計算系統...
    MachineLP閱讀 3,537評論 0 1
  • 大家有沒有這種想法?不知道干什么。 可能還是沒有壓力吧。 有的作者,沒了經濟來源,壓力巨大,瘋狂的看書,聽文字廣播...
    阿好在德國閱讀 453評論 7 5
  • 學院現有在編教職工13人,其中高級職稱5人,全部具有碩士以上學位,平均年齡36歲,是一個充滿激情和夢想的團隊。教師...
    小高素閱讀 128評論 0 0
  • 完美是什么?一切不過只是外人的評價而已。真正重要的,是那些被小說或電影刪節的生活細節。猶猶豫豫、迷迷茫茫中頂住生活...
    高舒曼閱讀 130評論 0 0