TensorFlow低階API(四)—— 圖和會話

簡介

TensorFlow使用數據流圖將計算表示爲獨立的指令之間的依賴關係。這可生成低級別的編程模型,在該模型中,您首先定義數據流圖,而後建立TensorFlow會話,以便在一組本地和遠程設備上運行圖的各個部分。python

若是您計劃直接使用低級別編程模型,,本指南將是您最實用的參考資源。較高階的API(例如tf.estimator.Estimator和Keras)會向最終用戶隱去圖和會話的細節內容,但若是您但願理解這些API的實現方式,本指南仍會對你有所幫助。算法

 

爲何使用數據流圖?

數據流是一種用於並行計算的經常使用編程模型。在數據流圖中,節點表示計算單元,邊表示計算使用或產生的數據。例如,在TensorFlow圖中,tf.matmul操做對應於單個節點,該節點具備兩個傳入邊(要相乘的矩陣)和一個傳出邊(乘法的結果)。編程

在執行您的程序時,數據流能夠爲TensorFlow提供多項優點:c#

  • 並行處理。經過使用明確的邊來表示操做之間的依賴關係,系統能夠輕鬆識別可以並行執行的操做。
  • 分佈式執行。經過使用明確的邊來表示操做之間流動的值,TensorFlow能夠將您的程序劃分到鏈接至不一樣機器的多臺設備(CPU、GPU和TPU)。TensorFlow將在這些設備之間進行必要的通訊和協調。
  • 編譯。TensorFlow的XLA編譯器可使用數據流圖的信息生成更快的代碼,例如將相鄰的操做融合到一塊兒。
  • 可移植性。數據流圖是一種不依賴於語言的模型代碼表示法。您可使用Python構建數據流圖,將其存儲在SaveModel中,並使用C++程序進行恢復,從而實現低延遲的推理。

 

什麼是tf.Graph?

tf.Graph包含兩類相關信息:api

  • 圖結構。圖的節點和邊緣,表示各個操做組合在一塊兒的方式,但不規定它們的使用方式。圖結構與彙編代碼相似:檢查圖結構能夠傳達一些有用的信息,但它不包含源代碼傳達的全部實用上下文信息。
  • 圖集合。TensorFlow提供了一種在tf.Graph中存儲元數據集合的通用機制。tf.add_collection函數容許您將對象列表與一個鍵關聯(其中tf.GraphKeys定義了部分標準鍵),tf.get_collection容許您查詢與某個鍵關聯的全部對象。TensorFlow庫的許多部分會使用此設施資源:例如,當您建立tf.Variable時,系統會默認將其添加到表示「全局變量」和「可訓練變量」的集合中。當您後續建立 tf.train.Saver或 tf.train.Optimizer時,這些集合中的變量將作默認參數。

 

建立tf.Graph

大多數TensorFlow程序都以數據流圖構建階段開始。在此階段,您會調用TensorFlow API函數,這些函數能夠構建新的tf.Operation(節點)和tf.Tensor(邊)對象並將它們添加到tf.Graph實例中。TensorFlow提供了默認圖,此圖是同一上下文中的全部API函數的明確參數。例如:數組

  • 調用tf.constant(42.0)可建立一個tf.Operation,該操做能夠生成值42.0,將該值添加到默認圖中,並返回表示常量值的tf.Tensor。
  • 調用tf.matmul(x,y)可建立單個tf.Operation,該操做會將tf.Tensor對象x和y的值相乘,將其添加到默認圖中,並返回表示乘法結果的tf.Tensor。
  • 執行 v=tf.Variable(0)可向圖添加一個tf.Operation,該操做能夠存儲一個科協入的張量,該值在多個 tf.Session.run調用之間保持恆定。tf.Variable對象會封裝此操做,並能夠像張量同樣使用,即讀取以存儲值的當前值。tf.Variable對象也具備 assign 和 assign_add 等方法,這些方法能夠建立 tf.Operation對象,這些對象在執行時更新已存儲的值。
  • 調用 tf.train.Opitimizer.minimize 可將操做和張量添加到計算梯度的默認圖中,並返回一個tf.Operation,該操做在運行時會將這些梯度應用到一組變量上。

大多數程序僅依賴默認圖。儘管如此,請參閱處理多個圖瞭解更加高級的用例。高階API(好比 tf.estimator.Estimator  API)可替您管理默認圖,而且還具備其它功能,例如建立不一樣的圖以用於訓練和評估。瀏覽器

注意:調用 TensorFlow API 中的大多數函數只會將操做和張量添加到默認圖中,而不會執行實際計算。您應編寫這些函數,直到擁有表示整個計算(例如執行梯度降低法的一步)的 tf.Tensor 或 tf.Operation,而後將該對象傳遞給 tf.Session 以執行計算。更多詳情請參閱「在 tf.Session 中執行圖」部分。緩存

 

命名指令

tf.Graph 對象會定義一個命名空間(爲其包含的 tf.Operation對象)。TensorFlow會自動爲您的圖中的每一個指令選擇一個惟一名稱,但您也能夠指定描述性名稱,使您的程序閱讀和調試起來更輕鬆。TensorFlow  API提供兩種方法來覆蓋操做名稱:服務器

  • 若是API函數會建立新的 tf.Operation或返回新的 tf.Tensor,則會接受可選 name 參數。例如,tf.constant(42.0,name="answer")會建立一個新的 tf.Operation(名爲「answer」)並返回一個 tf.Tensor(名爲"answer:0")。若是默認圖已包含名爲「answer」的操做,則TensorFlow會在名稱上附加「_1」、「_2」等字符,以便讓名稱具備惟一性。
  • 藉助 tf.name_scope函數,您能夠向在特定上下文中建立的全部操做添加名稱做用域前綴。 當前名稱做用域前綴是一個用 「/」 分割的名稱列表,其中包含全部活躍 tf.name_scope 上下文管理器的名稱。若是某個名稱做用域已在當前上下文中被佔用,TensorFlow將在該做用域上附加 「_1」、「_2」等字符。例如:
 1 c_0 = tf.constant(0, name="c")  # => operation named "c"
 2 
 3 # Already-used names will be "uniquified".
 4 c_1 = tf.constant(2, name="c")  # => operation named "c_1"
 5 
 6 # Name scopes add a prefix to all operations created in the same context.
 7 with tf.name_scope("outer"):
 8   c_2 = tf.constant(2, name="c")  # => operation named "outer/c"
 9 
10   # Name scopes nest like paths in a hierarchical file system.
11   with tf.name_scope("inner"):
12     c_3 = tf.constant(3, name="c")  # => operation named "outer/inner/c"
13 
14   # Exiting a name scope context will return to the previous prefix.
15   c_4 = tf.constant(4, name="c")  # => operation named "outer/c_1"
16 
17   # Already-used name scopes will be "uniquified".
18   with tf.name_scope("inner"):
19     c_5 = tf.constant(5, name="c")  # => operation named "outer/inner_1/c"

圖可視化工具使用名稱範圍來爲指令分組並下降圖的視覺複雜性。更多信息請參閱可視化您的圖網絡

請注意,tf.Tensor對象以輸出張量的 tf.Operation明確命名。張量名稱的形式爲 「<OP_NAME>:<i>」,其中:

  • <OP_NAME> 是生成該張量的操做的名稱
  • <i> 是一個整數,表示該張量在操做的輸出中的索引。

 

將操做放置到不一樣的設備上

若是您但願TensorFlow程序使用多臺不一樣的設備,則可使用 tf.device函數輕鬆地請求在特定上下文中建立的全部操做放置到同一設備(或同一類型的設備)上。

設備規範具備如下形式:

1 /job:<JOB_NAME>/task:<TASK_INDEX>/device:<DEVICE_TYPE>:<DEVICE_INDEX>

其中:

  • <JOB_NAME> 是一個字母數字字符串,而且不以數字開頭。
  • <TASK_INDEX> 是一個非負整數,表示名爲<JOB_NAME>的做業中的任務的索引。請參閱 tf.train.ClusterSpec 瞭解做業和任務的說明。
  • <DEVICE_TYPE> 是一種註冊設備類型(例如GPU或CPU)。
  • <DEVICE_INDEX> 是一個非負整數,表示設備索引,例如用於區分同一進程中使用的不一樣GPU設備。

您無需指定設備規範的每一個部分。例如,若是您在單個GPU的單機器配置中運行,您可使用 tf.device 將一些操做固定到CPU和GPU上:

 1 # Operations created outside either context will run on the "best possible"
 2 # device. For example, if you have a GPU and a CPU available, and the operation
 3 # has a GPU implementation, TensorFlow will choose the GPU.
 4 weights = tf.random_normal(...)
 5 
 6 with tf.device("/device:CPU:0"):
 7   # Operations created in this context will be pinned to the CPU.
 8   img = tf.decode_jpeg(tf.read_file("img.jpg"))
 9 
10 with tf.device("/device:GPU:0"):
11   # Operations created in this context will be pinned to the GPU.
12   result = tf.matmul(weights, img)

若是您在典型的分佈式配置中部署TensorFlow,您能夠指定做業名稱和任務ID,以便將變量放到參數服務器做業(「/job:ps」)中的任務上,並將其它操做放置到工做器做業(「job/worker」)中的任務上:

 1 with tf.device("/job:ps/task:0"):
 2   weights_1 = tf.Variable(tf.truncated_normal([784, 100]))
 3   biases_1 = tf.Variable(tf.zeroes([100]))
 4 
 5 with tf.device("/job:ps/task:1"):
 6   weights_2 = tf.Variable(tf.truncated_normal([100, 10]))
 7   biases_2 = tf.Variable(tf.zeroes([10]))
 8 
 9 with tf.device("/job:worker"):
10   layer_1 = tf.matmul(train_batch, weights_1) + biases_1
11   layer_2 = tf.matmul(train_batch, weights_2) + biases_2

藉助 tf.device,您能夠高度靈活地選擇單個操做或TensorFlow圖地各個區域的放置方式。在不少狀況下,簡單的啓發法具備良好的效果。例如,tf.train.replica_device_setter  API可與tf.device 結合使用,以針對數據分佈式訓練放置操做。例如,如下代碼段展現了 tf.train.replica_device_setter如何將不一樣放置策略應用於 tf.Variable對象和其它操做:

 1 with tf.device(tf.train.replica_device_setter(ps_tasks=3)):
 2   # tf.Variable objects are, by default, placed on tasks in "/job:ps" in a
 3   # round-robin fashion.
 4   w_0 = tf.Variable(...)  # placed on "/job:ps/task:0"
 5   b_0 = tf.Variable(...)  # placed on "/job:ps/task:1"
 6   w_1 = tf.Variable(...)  # placed on "/job:ps/task:2"
 7   b_1 = tf.Variable(...)  # placed on "/job:ps/task:0"
 8 
 9   input_data = tf.placeholder(tf.float32)     # placed on "/job:worker"
10   layer_0 = tf.matmul(input_data, w_0) + b_0  # placed on "/job:worker"
11   layer_1 = tf.matmul(layer_0, w_1) + b_1     # placed on "/job:worker"

 

相似於張量的對象

許多TensorFlow操做都會接受一個或多個 tf.Tensor對象做爲參數。例如,tf.matmul 接受兩個tf.Tensor對象,tf.add_n 接受一個具備n個 tfTensor對象的列表。爲了方便起見,這些函數將接受類張量對象來取代tf.Tensor,並將它明確轉換爲tf.Tensor(經過tf.convert_to_tensor 方法)。類張量對象包括如下類型的元素:

  • tf.Tensor
  • tf.Variable
  • numpy.ndarray
  • list(以及相似於張量的對象的列表)
  • 標量Python類型:bool、float、int、str

您可使用 tf.register_tensor_conversion_function 註冊其它類張量類型。

注意:默認狀況下,每次您使用同一個類張量對象時,TensorFlow 將建立新的 tf.Tensor。若是類張量對象很大(例如包含一組訓練樣本的 numpy.ndarray),且您屢次使用該對象,則可能會耗盡內存。要避免出現此問題,請在類張量對象上手動調用 tf.convert_to_tensor 一次,並使用返回的 tf.Tensor

 

在如下會話中執行圖:tf.Session

TensorFlow 使用tf.Session 類來表示客戶端程序(一般爲Python程序,但也提供了其它語言的相似接口)與C++運行時之間的鏈接。tf.Session對象使得咱們可以訪問本地機器中的設備和使用分佈式TensorFlow運行時的遠程設備。它還能夠緩存關於 tf.Graph的信息,使您可以屢次高效地運行同一計算。

建立 tf.Session

若是您使用的是低階TensorFlow  API,您能夠爲當前默認圖建立一個tf.Session,以下所示:

1 # Create a default in-process session.
2 with tf.Session() as sess:
3   # ...
4 
5 # Create a remote session.
6 with tf.Session("grpc://example.org:2222"):
7   # ...

因爲tf.Session擁有物理資源(例如GPU和網絡鏈接),所以一般(在with代碼塊中)用做上下文管理器,並在您退出代碼時自動關閉會話。您也能夠在不使用 with 代碼塊的狀況下建立會話,但應在完成會話時明確調用 tf.Session.close 以便釋放資源。

注意:較高階的 API(例如 tf.train.MonitoredTrainingSession 或 tf.estimator.Estimator)將爲您建立和管理 tf.Session。這些 API 接受可選的 target 和 config 參數(直接接受,或做爲 tf.estimator.RunConfig 對象的一部分),並具備相同的含義,以下所示。

tf.Session.init 接受三個可選參數:

  • target。若是將此參數留空(默認設置),會話將僅使用本地機器中的設備。可是,您也能夠指定 grpc:// 網址,以便指定TensorFlow服務器的地址,這使得會話能夠訪問該服務器控制的機器上的全部設備。請參閱 tf.train.Server以詳細瞭解如何建立TensorFlow服務器。例如,在常見的圖間複製配置中,tf.Session鏈接到tf.train.Server的流程與客戶端相同。分佈式TensorFlow部署指南介紹了其它常見情形。
  • graph。在默認狀況下,新的 tf.Session將綁定到當前的默認圖,而且可以在當前的默認圖中運行操做。若是您在程序中使用了多個圖(更多詳情請參閱使用多個圖進行編程),則能夠在構建會話時指定明確的 tf.Graph。
  • config。此參數容許您指定一個控制會話行爲的 tf.ConfigProto。例如,部分配置選項包括:
    • allow_soft_placement。將此參數設置爲 True 可啓用「軟」設備放置算法,該算法會忽略嘗試將僅限CPU的操做分配到GPU上的 tf.device註解,並將這些操做放置到CPU上
    • cluster_def。使用分佈式TensorFlow時,此選項容許你指定要在計算中使用的機器,並提供做業名稱、任務索引和網絡地址之間的映射。詳情請參閱 tf.train.ClusterSpec.as_cluster_def
    • graph_option.optimizer_options。在執行圖以前使您可以控制TensorFlow對圖實施的優化。
    • gpu_options.allow_growth。此參數設置爲 True 可更改GPU內存分配器,使該分配器逐漸增長分配的內存量,而不是在啓動時分配大多數內存。

使用 tf.Session.run執行操做

 tf.Session.run方法是運行 tf.Operation或評估 tf.Tensor的主要機制。您能夠將一個或多個 tf.Operation或 tf.Tensor對象傳遞到 tf.Session.run,TensorFlow將執行計算結果所須要的操做。

tf.Session.run 要求您指定一組fetch,這些fetch能夠肯定返回值,而且多是 tf.Operation、tf.Tensor或類張量類型,例如tf.Variable。這些fetch決定了必須執行哪些子圖(屬於總體tf.Graph)以生成結果:該子圖包含fetch列表中指定的全部操做,以及其輸出用於計算fetch值的全部操做。例如,如下代碼段說明了 tf.Session.run 的不一樣參數如何致使執行不一樣的子圖:

 1 x = tf.constant([[37.0, -23.0], [1.0, 4.0]])
 2 w = tf.Variable(tf.random_uniform([2, 2]))
 3 y = tf.matmul(x, w)
 4 output = tf.nn.softmax(y)
 5 init_op = w.initializer
 6 
 7 with tf.Session() as sess:
 8   # Run the initializer on `w`.
 9   sess.run(init_op)
10 
11   # Evaluate `output`. `sess.run(output)` will return a NumPy array containing
12   # the result of the computation.
13   print(sess.run(output))
14 
15   # Evaluate `y` and `output`. Note that `y` will only be computed once, and its
16   # result used both to return `y_val` and as an input to the `tf.nn.softmax()`
17   # op. Both `y_val` and `output_val` will be NumPy arrays.
18   y_val, output_val = sess.run([y, output])

tf.Session.run 也能夠選擇接受feed字典,該字典是從 tf.Tensor對象(一般是 tf.placeholder張量)到在執行時會替換這些張量的值(一般是Python標量、列表或Numpy數組)的映射。例如:

 1 # Define a placeholder that expects a vector of three floating-point values,
 2 # and a computation that depends on it.
 3 x = tf.placeholder(tf.float32, shape=[3])
 4 y = tf.square(x)
 5 
 6 with tf.Session() as sess:
 7   # Feeding a value changes the result that is returned when you evaluate `y`.
 8   print(sess.run(y, {x: [1.0, 2.0, 3.0]}))  # => "[1.0, 4.0, 9.0]"
 9   print(sess.run(y, {x: [0.0, 0.0, 5.0]}))  # => "[0.0, 0.0, 25.0]"
10 
11   # Raises <a href="../api_docs/python/tf/errors/InvalidArgumentError"><code>tf.errors.InvalidArgumentError</code></a>, because you must feed a value for
12   # a `tf.placeholder()` when evaluating a tensor that depends on it.
13   sess.run(y)
14 
15   # Raises `ValueError`, because the shape of `37.0` does not match the shape
16   # of placeholder `x`.
17   sess.run(y, {x: 37.0})

tf.Session.run也接受可選的options參數(容許您指定和調用相關的選項)和可選的 run_metadata 參數(容許您收集和執行有關的元數據)。例如,您能夠同時使用這些選項來收集與執行有關的跟蹤信息:

 1 y = tf.matmul([[37.0, -23.0], [1.0, 4.0]], tf.random_uniform([2, 2]))
 2 
 3 with tf.Session() as sess:
 4   # Define options for the `sess.run()` call.
 5   options = tf.RunOptions()
 6   options.output_partition_graphs = True
 7   options.trace_level = tf.RunOptions.FULL_TRACE
 8 
 9   # Define a container for the returned metadata.
10   metadata = tf.RunMetadata()
11 
12   sess.run(y, options=options, run_metadata=metadata)
13 
14   # Print the subgraphs that executed on each device.
15   print(metadata.partition_graphs)
16 
17   # Print the timings of each operation that executed.
18   print(metadata.step_stats)

 

直觀展現您的圖

TensorFlow包含可幫您理解圖中代碼的工具。圖可視化工具是TensorBoard的一個組件,可在瀏覽器中可視化圖的結構。要建立可視化圖表,最簡單的方法是傳遞tf.Graph(在建立 tf.summary.FileWriter時):

 1 # Build your graph.
 2 x = tf.constant([[37.0, -23.0], [1.0, 4.0]])
 3 w = tf.Variable(tf.random_uniform([2, 2]))
 4 y = tf.matmul(x, w)
 5 # ...
 6 loss = ...
 7 train_op = tf.train.AdagradOptimizer(0.01).minimize(loss)
 8 
 9 with tf.Session() as sess:
10   # `sess.graph` provides access to the graph used in a <a href="../api_docs/python/tf/Session"><code>tf.Session</code></a>.
11   writer = tf.summary.FileWriter("/tmp/log/...", sess.graph)
12 
13   # Perform your computation...
14   for i in range(1000):
15     sess.run(train_op)
16     # ...
17 
18   writer.close()

注意:若是您使用的是 tf.estimator.Estimator,圖(以及任何彙總)將自動記錄到您在建立 Estimator 時指定的 model_dir 中。

隨後,您能夠在 tensorboard中打開日誌並轉到「圖"標籤,查看圖結構的概要可視化圖表。請注意,典型的TensorFlow圖(尤爲是具備自動計算的梯度的訓練圖)包含的節點太多,沒法一次性完成直觀的展現。圖可視化工具使用名稱範圍來將相關指令分組到」超級節點「中。您能夠點擊任意超級節點上的橙色」+「按鈕以展開內部的子圖。

要詳細瞭解如何使用TensorBoard可視化TensorFlow應用,請參閱TensorBoard指南

 

使用多個圖進行編程

注意:訓練模型時,整理代碼的一種經常使用方法是使用一個圖訓練模型,而後使用另外一個圖對訓練過的模型進行評估或推理。在許多狀況下,推理圖與訓練圖不一樣:例如,丟棄和批次標準化等技術在每種情形下使用不一樣的操做。此外,默認狀況下,tf.train.Saver 等實用程序使用 tf.Variable 對象的名稱(此類對象的名稱基於底層 tf.Operation)來識別已保存檢查點中的每一個變量。採用這種方式編程時,您可使用徹底獨立的 Python 進程來構建和執行圖,或者在同一進程中使用多個圖。此部分介紹瞭如何在同一進程中使用多個圖。

如上所述,TensorFlow提供了一個」默認圖「,此圖明確傳遞給同一上下文中的全部API函數。對於許多應用而言,單個圖便以足夠。可是,TensorFlow還提供了操做默認圖的方法,在更高級的用例中,這些方法可能有用。例如:

  • tf.Graph會定義tf.Oparation對象的命名空間:單個圖中的每一個操做必須具備惟一名稱。若是請求的名稱被佔用,TensorFlow將在名稱後面附加 "_1"、"_2" 等字符,以便確保名稱的惟一性。經過使用多個明確建立的圖,您能夠更有效的控制爲每一個操做指定什麼樣的名稱。
  • 默認圖會存儲和添加每一個 tf.Operation和 tf.Tensor有關的信息。若是程序建立了大量未鏈接的子圖,更有效的作法是使用另外一個 tf,Graph構建每一個子圖,以便回收不相關的狀態。

您能夠安裝另外一個 tf.Graph做爲默認圖(使用 tf.Graph.as_default 上下文管理器):

 1 g_1 = tf.Graph()
 2 with g_1.as_default():
 3   # Operations created in this scope will be added to `g_1`.
 4   c = tf.constant("Node in g_1")
 5 
 6   # Sessions created in this scope will run operations from `g_1`.
 7   sess_1 = tf.Session()
 8 
 9 g_2 = tf.Graph()
10 with g_2.as_default():
11   # Operations created in this scope will be added to `g_2`.
12   d = tf.constant("Node in g_2")
13 
14 # Alternatively, you can pass a graph when constructing a <a href="../api_docs/python/tf/Session"><code>tf.Session</code></a>:
15 # `sess_2` will run operations from `g_2`.
16 sess_2 = tf.Session(graph=g_2)
17 
18 assert c.graph is g_1
19 assert sess_1.graph is g_1
20 
21 assert d.graph is g_2
22 assert sess_2.graph is g_2

要檢查當前的默認圖,請調用 tf.get_default_graph,它會返回一個tf.Graph對象:

1 # Print all of the operations in the default graph.
2 g = tf.get_default_graph()
3 print(g.get_operations())

 

 

 

參考連接:https://tensorflow.google.cn/guide/graphs#top_of_page

相關文章
相關標籤/搜索