TensorFlow 核心——數據流圖


1 計算模型 —— 計算圖(Graph)

更多參考:數據流圖

TensorFlow 中的所有計算都會被轉化為計算圖上的節點。TensorFlow 是一個通過計算圖的形式來表述計算的編程系統。TensorFlow中的每個計算都是計算圖的一個節點,而節點之間的描述了計算之間的依賴關系。

import sys
sys.path.append('E:/zlab/')

from plotnet import draw_feed_forward, DynamicShow

TensorFlow 的計算模型是有向圖,采用數據流圖 (Data Flow Graphs),其中每個節點代表一些函數或計算,而代表了數值、矩陣或張量。

數據流圖

數據流圖是用於定義計算結構的。在 TensorFlow 中,數據流圖本質上是一組鏈接在一起的函數,每個函數都會將其輸出傳遞給 \(0\) 個、\(1\) 個或多個位於這個級聯鏈上的其他函數。

with DynamicShow((6, 3), '計算圖.png') as d:  # 隱藏坐標軸
    seq_list = draw_feed_forward(d.ax, num_node_list=[2, 1, 1])
    seq_list[0][0].text('$1$') 
    seq_list[0][1].text('$2$')
    seq_list[1][0].text('$+$')  
    seq_list[2][0].text('$3$')

如上圖,我們使用數據流圖表示了 \(1 + 2 = 3\) 這一個運算。

IMG20180922175053

我們也可以將其抽象化:

with DynamicShow((6, 3), '計算圖1.png') as d:  # 隱藏坐標軸
    seq_list = draw_feed_forward(d.ax, num_node_list=[2, 1, 1])
    seq_list[0][0].text('$a$') 
    seq_list[0][1].text('$b$')
    seq_list[1][0].text('$c$')  
    seq_list[2][0].text('$d$')

我們也可以將上述過程簡化為:

將節點 \(c\)\(d\) 合並,若 \(c\) 表示求和運算,\(d\) 表示非線性變換,則上圖可以看作是一個神經元

除了節點和邊的概念,數據流圖還有一個十分關鍵的概念:依賴關系

我們一般地,像 \(z_0^{(0)}\)\(z_0^{(1)}\) 直接連接,稱為直接依賴,而像 \(z_0^{(0)}\)\(z_0^{(2)}\),稱為間接依賴。即 \(z_0^{(1)}\) 直接依賴於 \(z_0^{(0)}\)\(z_1^{(0)}\),而 \(z_0^{(2)}\) 間接依賴依賴於 \(z_0^{(0)}\)\(z_1^{(0)}\)


1.1 計算圖的使用

  1. 定義計算圖的所有節點;
  2. 執行計算。

1.1.1 使用默認圖

TensorFlow程序中,系統會自動維護一個默認的計算圖,通過tf.get_default_graph()函數可以獲取當前默認的計算圖。
通過a.graph可以查看張量所屬的計算圖。

import tensorflow as tf
a = tf.constant([1., 2.], name = 'a')
b = tf.constant([2., 3.], name = 'b')
result = a + b
a.graph is tf.get_default_graph()
True

1.1.2 tf.Graph函數可以生成新的計算圖

不同計算圖上的張量和運算均不會共享。

g1 = tf.Graph()
with g1.as_default():
    # 在計算圖 g1 中定義變量 “v” ,並設置初始值為 0。
    v = tf.get_variable("v", [1], initializer = tf.zeros_initializer()) # 設置初始值為0,shape 為 1
    
g2 = tf.Graph()
with g2.as_default():
    # 在計算圖 g2 中定義變量 “v” ,並設置初始值為 1。
    v = tf.get_variable("v", [1], initializer = tf.ones_initializer()) # 設置初始值為1
# 在計算圖 g1 中讀取變量“v” 的取值
with tf.Session(graph = g1) as sess:
    tf.global_variables_initializer().run()
    with tf.variable_scope("", reuse=True):
        print(sess.run(tf.get_variable("v")))

# 在計算圖 g2 中讀取變量“v” 的取值
with tf.Session(graph = g2) as sess:
    tf.global_variables_initializer().run()
    with tf.variable_scope("", reuse=True):
        print(sess.run(tf.get_variable("v")))
[ 0.]
[ 1.]

TensorFlow的計算圖不僅僅可以用來隔離張量和計算,它還提供了管理張量和計算的機制。

計算圖可以通過 tf.Graph.device函數來指定運行計算的設備。

g = tf.Graph()
# 指定計算運行的設備
with g.device('/gpu:0'):
    result = a + b

有效整理TensorFlow 程序的資源也是計算圖的一個重要功能。

在一個計算圖中,可以通過集合(collection)來管理不同類別的資源。比如通過tf.add_to_collection函數可以將資源加入一個
或多個集合中,然后通過tf.get_collection獲取一個集合里面的所有資源(如張量,變量,或者運行TensorFlow程序所需的隊列資源等等)

TensorFlow中維護的集合列表

集合名稱 集合內容 使用場景
tf.GraphKeys.VARIABLES 所有變量 持久化TensorFlow模型
tf.GraphKeys.TRAINABLE_VARIABLES 可學習的變量(一般指神經網絡中的參數) 模型訓練、生成模型可視化內容
tf.GraphKeys.SUMMARIES 日志生成相關的張量 TensorFlow計算可視化
tf.GraphKeys.QUEUE_RUNNERS 處理輸入的QueueRunner 輸入處理
tf.GraphKeys.MOVING_AVERAGE_VARIABLES 所有計算了滑動平均值的變量 計算變量的滑動平均值

2 數據模型——張量(Tensor)

在TensorFlow程序中,所有的數據都是通過張量的形式來表示的。

從功能角度來看張量可理解為多維數組。但是張量在TensorFlow的實現並不是直接采用數組的形式,它只是對TensorFlow中運算結果的引用。在張量中並沒有真正保存數字,它保存的是如何得到這些數字的計算過程。

import tensorflow as tf
# tf.constant 是一個計算,這個計算的結果為一個張量,保存在變量 a 中
a = tf.constant([1., 2.], name = 'a')
b = tf.constant([3., 4.], name = 'b')
result = tf.add(a, b, name = 'add')
print(result)
Tensor("add_3:0", shape=(2,), dtype=float32)

張量的屬性值主要有三個:名字(name)、維度(shape)和類型(type)

name

  1. 張量的唯一標識符;
  2. 給出了張量是如何計算出來的。

張量和計算圖上的每一個節點所有代表的結果是對應的。張量的命名:node:src_output
其中node為節點的名稱,src_output表示當前張量來自節點的第幾個輸出。

type

每一個張量會有一個唯一的類型。

import tensorflow as tf
a = tf.constant([1, 2], name = 'a', dtype = tf.float32)
b = tf.constant([3., 4.], name = 'b')
result = tf.add(a, b, name = 'add')
print(result)
Tensor("add_4:0", shape=(2,), dtype=float32)

TensorFlow支持14種類型:
實數(tf.float32, tf.float64)、整數(tf.int8, tf.int16, tf.int32, tf.int64, tf.uint8)、布爾型(tf.bool)、復數(tf.complex64, tf.complex128)。

張量的用途

對中間結果的引用,提高代碼可讀性。

# 使用張量記錄中間結果
a = tf.constant([1, 2], name = 'a', dtype = tf.float32)
b = tf.constant([3., 4.], name = 'b')
result = a + b

# 直接計算向量的和,這樣可讀性很差
result = tf.constant([1., 2.], name = 'a') + tf.constant([3., 4.], name = 'b')
<tf.Tensor 'add_5:0' shape=(2,) dtype=float32>
result.get_shape() # 獲取張量的維度信息
TensorShape([Dimension(2)])

當計算圖構造完成后,張量可用來獲取計算結果。

通過run計算結果;
或者在會話(Session())中運行。

3 運行模型——會話(Session)

執行已經定義好的運算。

會話擁有並管理TensorFlow程序運行時的所有資源。當所有計算完成后需要關閉會話來幫助系統回收資源,否則會出現資源泄露現象。

模式一

# 創建一個會話
sess = tf.Session()
# 使用這個創建好的會話得到關心的運算結果
sess.run(result)
# 關閉會話
sess.close()

模式二:通過Python上下文管理機制

with tf.Session() as sess:
    sess.run(result)
# 當上下文退出時會話關閉和資源可以被釋放。

指定默認會話(默認會話不會自動生成),通過 tf.Tensor.eval 函數計算張量取值。

sess = tf.Session()
with sess.as_default():
    print(result.eval())
[ 4.  6.]

以下代碼實現相同的功能:

sess = tf.Session()

# 下面的兩個命令有相同的功能
print(sess.run(result))
print(result.eval(session = sess))
[ 4.  6.]
[ 4.  6.]

使用tf.InteractiveSession構建會話

在交互的環境下(如Jupyter Notebook)可以直接構建默認會話,即使用tf.InteractiveSession函數(此函數會自動將生成的會話注冊為默認會話)。

sess = tf.InteractiveSession()
print(result.eval())
sess.close()
[ 4.  6.]

通過ConfigProto配置會話

config=tf.ConfigProto(allow_soft_placement=True, log_device_placement=True)
sess1 = tf.InteractiveSession(config=config)
sess2 = tf.Session(config=config)

allow_soft_placement=True 在下面的任意一個條件成立,GPU的運算可以放到CPU上進行:

  1. 運算無法在GPU上執行;
  2. 沒有GPU資源(比如運算被指定在第二個GPU上運行,但是機器只有一個GPU);
  3. 運算輸入包含對CPU計算結果的引用。

log_device_placement=True日志中將會記錄每個節點被安排在了哪個設備上以方便調試。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM