1. 程式人生 > >Tensorflow Graphs and Sessions

Tensorflow Graphs and Sessions

dataflow的優勢

dataflow
dataflow是平行計算的通用程式設計模型。在dataflow中,節點表示計算單位,edge表示計算消耗或產生的資料。例如,在TensorFlow圖中,tf.matmul操作將對應於具有兩個輸入邊(要乘以的矩陣)和一個輸出邊(乘法的結果)的單個節點。

並行:通過使用顯式邊來表示操作之間的依賴關係,系統很容易識別並行執行的操作。
分散式執行:通過使用顯式邊來表示在操作之間流動的值,TensorFlow可以跨越連線到不同機器的多個裝置(CPU,GPU和TPU)對程式進行分割槽。TensorFlow在裝置之間插入必要的通訊和協調。
彙編:TensorFlow的XLA編譯器可以使用資料流圖中的資訊來生成更快的程式碼,例如通過將相鄰操作相互融合在一起。
可移植性:資料流圖是模型中程式碼的語言無關表示。你可以在Python中構建資料流圖,將其儲存在SavedModel中,並將其還原為C++程式,以實現低延遲推理。

建立一個tf.graph()

大多數TensorFlow程式從資料流圖構建階段開始。 在此階段,您呼叫構造新的tf.Operation(node)和tf.Tensor(edge)物件的TensorFlow API函式,並將它們新增到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物件,在執行時,更新儲存的值。(有關變數的更多資訊,請參見Variables。)
呼叫tf.train.Optimizer.minimize會將運算和張量新增到計算梯度的預設圖形上,並返回一個tf.Operation,當執行時,將這些梯度應用於一組變數。

大多數程式僅依賴於使用預設圖。但是,更多高階用例處理多個圖。高階API(如tf.estimator.Estimator API)代表你管理預設圖形,比如可以為訓練和評估建立不同的圖。

Note:呼叫TensorFlow API中的大多數函式僅將操作和張量新增到預設圖形,但不執行實際計算。而是組合這些函式,直到有一個表示整體計算的tf.Tensor或tf.Operation,例如執行梯度下降的一個步驟,然後將該物件傳遞給tf.Session來執行計算。 有關詳細資訊,請參見“執行tf.Session中的graph”一節。

命名操作

tf.Graph物件為其包含的tf.Operation物件定義一個名稱空間。TensorFlow會自動為圖形中的每個操作選擇一個唯一的名稱,但給出操作描述性名稱可以使程式更易於閱讀和除錯。 TensorFlow API提供了兩種覆蓋名稱操作:

每個API函式建立一個新的tf.Operation或返回一個新的tf.Tensor接受可選的名稱引數。例如,tf.constant(42.0,name=“answer”)建立一個名為“answer”的新tf.Operation,並返回一個名為“answer:0”的tf.Tensor。如果預設圖形已經包含一個名為“answer”的操作,TensorFlow將附加“_1”,“_2”等名稱,以使其唯一。

tf.name_scope函式可以為在特定上下文中建立的所有操作新增名稱範圍字首。 當前名稱範圍字首是所有活動的tf.name_scope上下文管理器的名稱的“/”分隔列表。如果在當前上下文中已經使用了名稱範圍,則TensorFlow會新增“_1”,“_2”等。 例如:

c_0 = tf.constant(0, name="c") # => operation named "c"

# Already-used names will be "uniquified".
c_1 = tf.constant(2, name="c") # => operation named "c_1"

# Name scopes add a prefix to all operations created in the same context.
with tf.name_scope("outer"):
c_2 = tf.constant(2, name="c") # => operation named "outer/c"

# Name scopes nest like paths in a hierarchical file system.
with tf.name_scope("inner"):
c_3 = tf.constant(3, name="c") # => operation named "outer/inner/c"

# Exiting a name scope context will return to the previous prefix.
c_4 = tf.constant(4, name="c") # => operation named "outer/c_1"

# Already-used name scopes will be "uniquified".
with tf.name_scope("inner"):
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.Tensor物件作為引數。 例如,tf.matmul需要兩個tf.Tensor物件,並且tf.add_n獲取n tf.Tensor物件的列表。 為方便起見,這些函式將接受一個類似張量的物件代替一個tf.Tensor,並使用tf.convert_to_tensor方法將其隱式轉換為一個tf.Tensor。類似的包括以下元素:

tf.Tensor
tf.Variable
numpy.ndarray
list(和類似張量的物件的列表)
標量Python型別:bool,float,int,str

Note:預設情況下,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,如下所示:
# Create a default in-process session.
with tf.Session() as sess:
# ...

# Create a remote session.
with tf.Session("grpc://example.org:2222"):
# ...

由於tf.Session佔用物理資源(如GPU和網路連線),因此通常用作在退出塊時自動關閉會話的上下文管理器(使用with塊)。也可以建立一個會話而不使用with塊,但是當你完成它釋放資源後,你應該明確地呼叫tf.Session.close。
tf.Session.init 接受三個可選引數:

target:如果此引數為空(預設值),則session將僅使用本地計算機中的裝置。 但是,您還可以指定一個grpc:// URL來指定TensorFlow伺服器的地址,該伺服器將會話訪問該伺服器所控制的計算機上的所有裝置。 有關如何建立TensorFlow伺服器的詳細資訊,請參閱tf.train.Server。例如,在常見的graph之間複製配置中,tf.Session以與客戶端相同的過程連線到tf.train.Server。分散式TensorFlow部署指南介紹了其他常見情況。

graph: 預設情況下,新的tf.Session將被繫結到---並且只能在---當前的預設圖中執行操作。 如果您在程式中使用多個圖(有關詳細資訊,請參閱使用多個圖程式設計),您可以在構建會話時指定一個顯式的tf.Graph。
config: 此引數允許您指定一個控制會話行為的tf.ConfigProto。 例如,一些配置選項包括:

allow_soft_placement: 將其設定為True以啟用“soft”裝置佈局演算法,忽略嘗試在GPU裝置上放置僅CPU操作的tf.device註釋,並將其放置在CPU上。
cluster_def:使用分散式TensorFlow時,此選項允許您指定計算中要使用的計算機,並提供作業名稱,任務索引和網路地址之間的對映。有關詳細資訊,請參閱tf.train.ClusterSpec.as_cluster_def。
graph_options.optimizer_options:提供了優化tensorflow執行之前執行該控制圖。
gpu_options.allow_growth:將其設定為True以更改GPU記憶體分配器,以便逐漸增加分配的記憶體量,而不是在啟動時分配大部分記憶體。

使用tf.Session執行operation

tf.Session.run方法是執行tf.Operation或評估tf.Tensor的主要機制。 您可以將一個或多個tf.Operation或tf.Tensor物件傳遞給tf.Session.run,並且TensorFlow將執行計算結果所需的操作。
tf.Session.run要求您指定一個取出列表,它確定返回值,並且可以是tf.Operation,tf.Tensor或類似張量的型別,如tf.Variable。這些fetch確定必須執行整個tf.Graph的子圖以產生結果:這是包含在提取列表中命名的所有操作的子圖,以及其輸出用於計算提取值的所有操作。例如,以下程式碼片段顯示了tf.Session.run的不同引數如何導致執行不同的子圖:

x = tf.constant([[37.0, -23.0], [1.0, 4.0]])
w = tf.Variable(tf.random_uniform([2, 2]))
y = tf.matmul(x, w)
output = tf.nn.softmax(y)
init_op = w.initializer

with tf.Session() as sess:
# Run the initializer on w.
sess.run(init_op)

# Evaluate output. sess.run(output) will return a NumPy array containing
# the result of the computation.
print(sess.run(output))

# Evaluate y and output. Note that y will only be computed once, and its
# result used both to return y_val and as an input to the tf.nn.softmax()
# op. Both y_val and output_val will be NumPy arrays.
y_val, output_val = sess.run([y, output])

tf.Session.run還可以選擇使用feed的字典,它是從tf.Tensor物件(通常是tf.placeholder張量)到值(通常是Python標量,列表或NumPy陣列)的對映,將替代這些張量 執行。 例如:

# Define a placeholder that expects a vector of three floating-point values,
'# and a computation that depends on it.
x = tf.placeholder(tf.float32, shape=[3])
y = tf.square(x)

with tf.Session() as sess:
# Feeding a value changes the result that is returned when you evaluate
y.
print(sess.run(y, {x: [1.0, 2.0, 3.0]}) # => "[1.0, 4.0, 9.0]"
print(sess.run(y, {x: [0.0, 0.0t, 5.0]}) # => "[0.0, 0.0, 25.0]"

# Raises tf.errors.InvalidArgumentError, because you must feed a value for
# a tf.placeholder() when evaluating a tensor that depends on it.
sess.run(y)

# Raises ValueError, because the shape of 37.0 does not match the shape
# of placeholder x.
sess.run(y, {x: 37.0})

tf.Session.run還接受一個可選選項引數,使你能夠指定有關呼叫的選項,以及可選的run_metadata引數,可用於收集有關執行的元資料。 例如,您可以一起使用這些選項來收集有關執行的跟蹤資訊:

y = tf.matmul([[37.0, -23.0], [1.0, 4.0]], tf.random_uniform([2, 2]))

with tf.Session() as sess:
# Define options for the sess.run() call.
options = tf.RunOptions()
options.output_partition_graphs = True
options.trace_level = tf.RunOptions.FULL_TRACE

# Define a container for the returned metadata.
metadata = tf.RunMetadata()

sess.run(y, options=options, run_metadata=metadata)

# Print the subgraphs that executed on each device.
print(metadata.partition_graphs)

# Print the timings of each operation that executed.
print(metadata.step_stats)

GraphDef和MetaGraphDef

TensorFlow使用資料流圖作為應用程式的行動式表示。tf.Graph包含兩種相關資訊:

graph structure: 圖形的節點和邊緣,指示單個操作如何組合在一起,但不規定如何使用它們。graph structure就像彙編程式碼:檢查它可以傳達一些有用的資訊,但它不包含原始碼傳達的所有有用的上下文。
graph collection:TensorFlow提供了一種用於在tf.Graph中儲存元資料集合的通用機制。tf.add_to_collection函式使您能夠將list中的object與key(其中tf.GraphKeys定義了一些標準鍵)相關聯,而tf.get_collection可以查詢與一個鍵相關聯的所有物件。 TensorFlow庫的許多部分使用這個功能:例如,當你建立一個tf.Variable時,預設情況下將其新增到表示“全域性變數”和“可訓練變數”的集合中。 當您稍後建立一個tf.train.Saver或tf.train.Optimizer時,這些集合中的變數將用作預設引數。

tf.Graph可以儲存為兩種形式:

tf.GraphDef:這是圖形結構的低階表示,包含其所有操作(如tf.NodeDef協議緩衝區)及其間的邊界的描述。tf.GraphDef表示主要與低階API(例如tensorflow :: Session C ++ API)一起使用,通常需要額外的上下文(例如特定操作的名稱)才能使用它。 tf.Graph.as_graph_def方法將tf.Graph轉換為tf.GraphDef。

tf.train.MetaGraphDef:這是一個數據流圖的更高級別的表示,其中包括一個tf.GraphDef和有助於理解圖形的資訊(如圖形集合的內容)。tf.train.export_meta_graph函式將tf.Graph轉換為tf.train.MetaGraphDef。 tf.train.Saver.save方法還可以寫入一個tf.train.MetaGraphDef,它可以與儲存的檢查點一起使用,以恢復其儲存點的訓練過程的狀態。

在大多數情況下,我們鼓勵您使用tf.train.MetaGraphDef而不是tf.GraphDef。 在某些情況下,tf.GraphDef可能是有用的 - 例如,使用tf.import_graph_def或Graph Transform工具等函式執行低階圖修改時,但tf.train.MetaGraphDef是一個更好的構建塊高階應用程式。例如,SavedModel庫使用tf.train.MetaGraphDef來打包tf.Graph和一組經過訓練的模型引數,以便於服務。
如果你有一個tf.train.MetaGraphDef,tf.train.import_meta_graph函式將它載入到預設圖形中。 呼叫此功能有兩個主要功能:

1.它將從原始圖形還原圖的內容。 諸如tf.global_variables的APIs和API的預設引數(如tf.train.Optimizer.minimize)的工作方式將與原始圖中的操作方式相同。
2.該函式返回一個tf.train.Saver,可用於從檢查點恢復與圖形關聯的狀態(訓練引數等)。 tf.train.latest_checkpoint函式可以幫助從特定的檢查點目錄中找到最新的檢查點。

如果您有一個tf.GraphDef,tf.import_graph_def函式可以將圖形載入到現有的Python tf.Graph物件中。 要使用匯入的圖形,您必須知道tf.GraphDef中的操作或張量的名稱。 tf.import_graph_def函式有兩個主要功能可以幫助您使用匯入的圖形:

1.你可以通過傳遞可選的input_map引數,將匯入的圖形中的張量重新貼上到預設圖中的tf.Tensor物件。 例如,input_map使您能夠匯入在tf.GraphDef中定義的圖片段,並將您正在構建的圖中的張量靜態連線到該片段中的tf.placeholder張量。
2.通過將其名稱傳遞給return_elements列表,可以從匯入的圖形返回tf.Tensor或tf.Operation物件。

另外,你可以使用tf.device和tf.name_scope來控制匯入節點的裝置位置和名稱。

視覺化圖

TensorFlow包括可以幫助您瞭解圖形中的程式碼的工具。圖形視覺化器是TensorBoard的一個元件,可以在瀏覽器中直觀呈現圖形的結構。建立視覺化的最簡單的方法是在建立tf.summary.FileWriter時傳遞一個tf.Graph:
# Build your graph.
x = tf.constant([[37.0, -23.0], [1.0, 4.0]])
w = tf.Variable(tf.random_uniform([2, 2]))
y = tf.matmul(x, w)

# ...
loss = ...

train_op = tf.train.AdagradOptimizer(0.01).minimize(loss)

with tf.Session() as sess:
# sess.graph provides access to the graph used in a tf.Session.
writer = tf.summary.FileWriter("/tmp/log/...", sess.graph)

# Perform your computation...
for i in range(1000):
sess.run(train_op)
# ...

writer.close()

Note:如果使用tf.estimator.Estimator,則圖形(和任何summaries)將自動記錄到在建立estimator時指定的model_dir中。

然後,您可以開啟tensorboard上的日誌,導航到“graph”選項卡,並檢視圖形結構的高階視覺化。 注意,典型的TensorFlow圖 - 特別是具有自動計算梯度的訓練圖 - 具有太多的節點可以立即視覺化。 圖形視覺化器利用名稱範圍將相關操作分組為“super”節點。 您可以點選任何這些super節點上的橙色“+”按鈕來擴充套件子圖。
minist_deep

使用多個圖程式設計

Note:訓練模型時,組織程式碼的常見方法是使用一個圖來訓練您的模型,以及用單獨的圖形評估或執行推理一個訓練模型。 在許多情況下,推理圖將與訓練圖不同:例如,在每種情況下,像dropout和批batch normalization這樣的技術都使用不同的操作。此外,預設情況下,諸如tf.train.Saver這樣的實用程式使用tf.Variable物件的名稱(其名稱基於底層tf.Operation)來標識儲存的檢查點中的每個變數。 程式設計時,您可以使用完全獨立的Python程序構建和執行圖形,也可以在同一過程中使用多個圖形。 本節介紹如何在同一過程中使用多個圖形。

如上所述,TensorFlow提供了在相同上下文中隱式傳遞給所有API函式的“預設圖”。 對於許多應用程式,單個圖形就足夠了。 然而,TensorFlow還提供了操作預設圖形的方法,這在更高階的使用案例中是有用的。 例如:

tf.Graph定義tf.Operation物件的名稱空間:單個圖中的每個操作必須具有唯一的名稱。 如果所請求的名稱已經被使用,TensorFlow將通過將“_1”,“_2”等附加到名稱上來“唯一地”表示操作名稱。 使用多個顯式建立的圖形可以更好地控制每個操作的名稱。
預設圖形儲存有關新增到其中的每個tf.Operation和tf.Tensor的資訊。 如果您的程式建立大量未連線的子圖,則使用不同的tf.Graph構建每個子圖可能會更有效,以便無關的狀態可以被垃圾回收。

你可以使用tf.Graph.as_default上下文管理器來安裝不同的tf.Graph作為預設圖:
g_1 = tf.Graph()
with g_1.as_default():
# Operations created in this scope will be added to g_1.
c = tf.constant("Node in g_1")

# Sessions created in this scope will run operations from g_1.
sess_1 = tf.Session()

g_2 = tf.Graph()
with g_2.as_default():
# Operations created in this scope will be added to
g_2.
d = tf.constant("Node in g_2")

# Alternatively, you can pass a graph when constructing a tf.Session:

# sess_2 will run operations from g_2.
sess_2 = tf.Session(graph=g_2)

assert c.graph is g_1
assert sess_1.graph is g_1

assert d.graph is g_2
assert sess_2.graph is g_2

要檢查當前的預設圖形,請呼叫tf.get_default_graph,返回一個tf.Graph物件:

# Print all of the operations in the default graph.
g = tf.get_defaut_graph()
print(g.get_operations())