【Tensorflow_DL_Note5】Tensorflow中計算圖、會話、feed和fetch

馬衛飛發表於2018-04-17

一 Tensorflow的基本使用

      在Tensorflow進行深度神經網路模型的具體設計之前,我們必須明白以下幾點:

      【1】在Tensorflow中,使用【圖:Graph】來表示計算任務。

      【2】在被稱之為【會話:Session】的上下文執行【圖:Graph】

      【3】使用Tensor表示【資料】

      【4】使用變數Variable維護狀態

      【5】使用feed和fecth可以為任意的操作賦值或者從中獲取資料

二 綜述

      Tensorflow是一個程式設計系統,使用【圖】來表示【計算任務】。圖中的【節點】被稱為op(operating的縮寫),一個op獲得0個或者多個Tensor,執行計算,產生0個或者多個Tensor。每個Tensor是一個型別化的【多維陣列】,例如:你可以將一小組影象集合表示為一個四維浮點數陣列,這四個維度分別是[batch,height,width,channels]

      一個Tensorflow圖描述了計算的過程。為了進行計算,圖必須在【會話】裡面啟動。【會話】將圖的【op】分發到諸如CPU或者GPU之類的裝置上,同時提供執行【op】的方法。在這些方法執行後,將產生的Tensor返回。在Python語言中,返回的Tensor是numpy中的ndarry物件;在C/C++語言中,返回的Tensor是tensorflow::Tensor的例項。

三 計算圖Graph的概念

3.1 圖Graph的綜述

       Tensorflow中的程式通常被組織成一個【構建階段】和一個【執行階段】。在Tensorflow程式碼的【構建階段】,op的執行步驟被描述成一個圖的執行階段,使用Session執行圖中的op。例如:通常在構建階段建立一個圖來表示和訓練神經網路,然後,在執行階段,再反覆執行圖中的訓練op。Tensorflow支援C/C++、python等。目前,Tensorflow的Python庫更加容易使用,它提供了大量的輔助函式來簡化構建圖的工作,這些函式尚未被C和C++庫支援。

      Tensorflow的名字已經說明了它最重要的兩個概念---Tensor和Flow。Tensor就是張量。在Tensorflow中,張量可以被簡單的理解為【多維陣列】。如果說Tensorflow的第一個單詞【Tensor】表明了它的【資料結構】,那麼【Flow】則體現了它的【 】計算模型。Flow翻譯成中文,就是“流”的意思,它直觀的表達了Tensor之間 通過計算相互轉化的過程。Tensorflow是一個通過【計算圖】的形式來表達【計算】的【程式設計系統】。Tensorflow中的每一個計算都是計算圖Graph上的一個節點,而節點op之間的邊則描述了計算之間的依賴關係

3.2 圖Graph的使用

       在Tensorflow中,圖的使用一般分為兩個階段。

              【1】構建圖:第一階段為構建圖,在這個階段中,需要定義計算圖中所有的計算。

              【2】執行圖:在圖的構建完成之後,才能啟動圖。啟動圖的第一步是建立一個Session物件,如果沒有任何建立引數,會話Session構建函式將啟動預設的圖。

#========================================================================================================
#詳細說明:
#      【1】下面這個建立圖的過程,Tensorflow會自動的將定義轉化為計算圖Graph上的節點Node.在Tensorflow程式中,系統會
#           自動的維護一個預設的計算圖,通過tf.get_default_graph()函式可以獲得當前預設的計算圖。
#      【2】下面的程式碼給出了圖建立階段的定義樣例
#      【3】示意瞭如何獲取預設的計算圖,以及如何檢視一個運算所屬的計算圖
#========================================================================================================
import tensorflow as tf

a = tf.constant([1.0,2.0],name="a")
b = tf.constant([2.0,3.0],name="b")
result = a+b
#通過a.graph可以檢視張量所屬的計算圖。因為沒有特意的指定,所以這個計算圖應該等於當前預設的計算圖。所以下面的這個結果應
#該輸出為True
print(a.graph is tf.get_default_graph())

         除了使用預設的計算圖,Tensorflow也支援通過tf.Graph函式來生成新的計算圖。不同計算圖上的張量和運算都不會共享。下面的程式碼示意瞭如何在不同的計算圖上定義和使用變數Variable.

#========================================================================================================
#詳細說明:
#      【1】下面這個建立圖的過程,Tensorflow會自動的將定義轉化為計算圖Graph上的節點Node.在Tensorflow程式中,系統會
#           自動的維護一個預設的計算圖,通過tf.get_default_graph()函式可以獲得當前預設的計算圖。
#      【2】下面的程式碼給出了圖建立階段的定義樣例
#      【3】示意瞭如何獲取預設的計算圖,以及如何檢視一個運算所屬的計算圖
#========================================================================================================
import  tensorflow as tf
#【0】建立一個新的空的【計算圖】【圖:Graph】
#【1】在【計算圖:Graph】中定義變數"Var",並設定其初始值為0.
globalGraph = tf.Graph()
with globalGraph.as_default():
    var = tf.get_variable("var",shape=[1],initializer=tf.zeros_initializer)


#【0】建立一個新的空的【計算圖】【圖:Graph】
globalGraph2 = tf.Graph()
with globalGraph2.as_default():
    #【2】計算圖globalGraph2中定義的變數“var”,並設定初始值為1
    var = tf.get_variable("var",shape=[1],initializer=tf.ones_initializer)


#【0】在圖globalGraph1中讀取變數var的取值
with tf.Session(graph=globalGraph) as sess:
     tf.global_variables_initializer().run()
     with tf.variable_scope("",reuse=True):
        #   在計算圖g1中,變數“v”的取值應該為0,所以下面這行會輸出[0.]
        print(sess.run(tf.get_variable("var")))


#【0】在圖global2中讀取變數var的取值
with tf.Session(graph=globalGraph2) as sess:
    tf.global_variables_initializer().run()
    with tf.variable_scope("",reuse=True):
        #【4】在圖globalGraph2中,變數“var”的取值應該為1,所以下面這行的輸出應該為[1.]
        print(sess.run(tf.get_variable("var")))
#========================================================================================================
#詳細說明:
#      【1】上面的程式碼產生了兩個【圖】,每個【圖】中定義了一個名字為“var”的變數。在【圖】globalGraph中,將變數初始化
#           為0;在【圖】globalGraph中,將圖初始化為1。
#      【2】可以看到,當執行不同的【圖】時,變數var的值也是不一樣的。
#      【3】Tensorflow中的【圖】不僅僅可以用來【隔離】【張量】和【計算】,它還提供了【管理張量】和【計算的機制】。【圖】
#           可以通過【tf.Graph.device函式】來【指定】【執行計算的裝置】。這為Tensorflow使用GPU提供了機制。下面的程式
#           可以將【加法】計算跑在GPU上。
#========================================================================================================
globalVal = tf.Graph()
#【1】指定計算執行的裝置
with globalVal.device(‘/gpu:0’):
    result = a+b
#========================================================================================================
#詳細說明:
#      【1】有效的整理Tensorflow程式中的資源也是【圖】的一個非常重要的功能。
#      【2】在一個【圖】中,可以通過集合cellection來管理不同類別的資源。比如通過tf.add_to_collection函式可以將資源
#           加入到一個或者多個集合中,然後,通過tf.get_collection獲取一個集合裡面的所有的資源。這裡的資源可以是張量、
#           變數或者執行Tensorflow程式所需的【佇列資源】
#========================================================================================================

四 Tensorflow的資料模型---張量

4.1 張量的概念

      Tensorflow程式使用Tensor資料結構來代表所有的資料,圖中,操作間傳遞的資料都是tensor,你可以把Tensor看做是一個能為的陣列或者python中的列表。一個Tensor包含一個靜態型別的rank和一個shape。雖然,可以講tensor看成是一個n維的陣列,但是Tensor在Tensorflow中的實現並不是直接採用資料的形式實現的,它只是對Tensorflow中運算結果的引用。在Tensor中並沒有儲存真正的數字,它儲存的是如何得到這些數字的計算過程。還是以向量的加法 為例,當執行下面的程式碼時,並不會得到具體的數值結果,而會得到對結果的一個引用。

#========================================================================================================
#函式原型:
#       constant(value, dtype=None, shape=None, name="Const", verify_shape=False)
#函式說明:
#       Tensorflow中常量的常見函式
#引數說明:
#       【1】value------初始化常量變數的值
#       【2】dtype------常量的資料型別
#       【3】shape------常量的維度
#========================================================================================================
import tensorflow as tf
#【0】tf.constant()是Tensorflow中常量的建立函式
a=tf.constant([1.0,2.0],name="a")
b=tf.constant([2.0,3.0],name="b")

result = tf.add(a,b,name="add")
print(result)
#========================================================================================================
#模組說明:
#       【1】從上面的程式碼可以看出Tensorflow中的【張量】Tensor和Numpy中的【陣列】不同,Tensorflow計算的結果不是一個
#           具體的數字,而是一個【張量】的【結構】。從上面程式碼執行的結果可以看出,一個張量中主要儲存的三個屬性為:名字、
#           維度和型別
#       【2】張量的三個屬性:
#               【1】name :名字
#               【2】shape: 維度
#               【3】type :型別
#       【3】張量的第一個屬性名字不僅是一個張量的唯一識別符號,它同樣也給出了這個張量是如何計算出來的,在前面,我們知道
#           Tensorfloe中的計算都可以通過【圖模型】來建立,而【圖】上的每一個節點Node帶一個計算,計算的結果就儲存在
#           張量之中。所以,張量和圖上節點所代表的計算結果是對應的。這樣張量的命名就可以通過node:src_output的形式給
#           出。其中node為節點的名稱,src_output代表當前張量來自節點的第幾個輸出。比如上面程式的輸出add:0就說明了
#           result這個張量是計算節點add輸出的第一個結果。
#       【4】張量的第二個屬性是張量的維度(shape)。這個屬性描述了一個張量的維度資訊,比如上面樣例中的shape=(2,),說明了
#           張是一個一維陣列,這個陣列的長度為2,【維度】是【張量】一個非常重要的屬性。圍繞張量的維度Tensorflow也給出了
#           很多有用的運算。
#       【5】張量的第三個屬性是型別(dtype),每一個張量都會有一個唯一的型別。Tensorflow會對參與運算的所有張量進行型別
#           的檢查;當發現型別不匹配是時,就會報錯。
#========================================================================================================

4.2 張量的使用

        和Tensorflow的計算模型相比,Tensorflow的資料模型相對比較簡單,張量的使用主要歸結為兩大類:

        第一類用途是:對中間計算結果的引用。當一個計算包含很多中間結果時,使用張量可以大大提高程式碼的可讀性。

       第二類情況是:當【圖】構造完成之後,張量可以用來獲得計算結果,也就是得到真實的數字。雖然張量本身並沒有儲存具體數字的能力,但是通過【會話】Session,就可以得到這些具體的數字,如下面的程式碼所示:

import tensorflow as tf
#【0】tf.constant()是Tensorflow中常量的建立函式
a=tf.constant([1.0,2.0],name="a")
b=tf.constant([2.0,3.0],name="b")

result = a+b
print(tf.Session().run(result))

五 Tensorflow執行模型---會話Session

      此塊,我們將介紹如何使用Tensorflow中的會話Session來執行定義好的運算。【會話】Session擁有並管理Tensorflow程式 執行時的【所有資源】。當所有的計算完成之後,需要關閉【會話】Session來幫助系統回收資源,否則,就可能會出現資源洩露的問題。

      Tensorflow中使用【會話】Session的模式一般有兩種模式,第一種模式需要明確呼叫【會話生成函式】和【關閉會話函式】,這種模式的程式碼流程遵循【三步驟戰略】,具體程式碼如下所示:

import tensorflow as tf
#【1】建立一個會話
sess = tf.Session()
#【2】使用這個建立好的會話Session來得到關心的運算的結果,比如可以呼叫sess.run(result)
sess.run(result)
#【3】關閉會話使得本次執行中使用的資源可以被釋放
sess.close()
          使用這種模式時,在所有計算完成之後,需要明確的呼叫Session.close()會話關閉函式來關閉會話,並釋放掉資源。然而,當程式因為異常退出時,關閉會話的函式可能就不會被執行而導致資源洩露。為了解決異常退出時,資源能夠正確釋放的問題,Tensorflow可以通過Python中的上下文管理器來使用會話Session。以下程式碼展示瞭如何使用這種模式。
import tensorflow as tf
#【1】建立一個會話,並通過Python中的上下文管理器來管理這個會話Session
with tf.Session() as sess:
    #【2】使用建立好的會話Session來計算關心的結果
    sess.run()
    #【3】不需要再呼叫Session.close()函式來關閉會話,當上下文退出時會關閉會話,也會釋放掉資源

       通過python的【上下文管理器機制】,只要將所有的計算放在【with語句】的內部就可以。當上下文管理器退出的時候,會自動的釋放掉資源。這樣既解決了因為異常退出時,資源釋放的問題,同事也解決了忘記呼叫Session.close()函式而產生的資源洩露問題。

       前面提到過,Tensorflow會自動的生成一個預設的【圖】,如果沒有特殊的指定,運算會自動的加入到這個預設的【圖】總,tf.get_default_graph()。Tensorflow中的會話Session也有類似的機制,但Tensorflow不會自動的生成預設的會話,而是需要手動的指定。當預設的會話被指定之後,可以通過tf.Tensor.eval函式來計算一個張量的取值。下面的程式碼展示了通過設定預設的會話計算張量的取值:

import tensorflow as tf

sess = tf.Session()
with sess.as_default():
    print(result.eval())

     在互動式環境下,比如說python指令碼或者Jupyter的編輯器下,通過設定【預設會話】的方式來獲取張量的取值更加的方便。所以,Tensorflow提供了一種在互動式環境下直接構建會話的函式,這個函式就是tf.InteractiveSession。使用這個函式會自動將生成的【會話】註冊為【預設的會話】。下面的程式碼展示了這個函式的使用:

import tensorflow as tf

sess = tf.InteractiveSession()
print(result.eval())
sess.close()

       無論使用那種方法,都可以通過ConfigProto Protocol Buffer來配置需要生成的會話。下面給出了通過ConfigProto配置會話的方法:

import tensorflow as tf

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

      通過ConfigProto可以配置類似並行的【執行緒數】、【GPU分配策略】、【運算超時時間】等引數,在這些引數中,最常用的有兩個。第一個是allow_soft_placement,這是一個布林型的引數,當它為True時,在以下任意一個條件成立的時候,GPU上的運算可以放到CPU上執行。

     【1】運算無法在GPU上執行

     【2】沒有GPU資源(比如計算被指定到了第二個GPU上,但是計算機只有一個GPU)

     【3】運算的輸入中,包含對CPU計算結果的引用


相關文章