1. 程式人生 > >斯坦福CS20SI TensorFlow學習筆記1——graph、session和op

斯坦福CS20SI TensorFlow學習筆記1——graph、session和op

efault constant 例如 sub 否則 我們 vector 安全 出現

graph即tf.Graph(),session即tf.Session(),很多人經常將兩者混淆,其實二者完全不是同一個東西。

  • graph定義了計算方式,是一些加減乘除等運算的組合,類似於一個函數。它本身不會進行任何計算,也不保存任何中間計算結果。
  • session用來運行一個graph,或者運行graph的一部分。它類似於一個執行者,給graph灌入輸入數據,得到輸出,並保存中間的計算結果。同時它也給graph分配計算資源(如內存、顯卡等)。

TensorFlow是一種符號式編程框架,首先要構造一個圖(graph),然後在這個圖上做運算。打個比方,graph就像一條生產線,session就像生產者。生產線具有一系列的加工步驟(加減乘除等運算),生產者把原料投進去,就能得到產品。不同生產者都可以使用這條生產線,只要他們的加工步驟是一樣的就行。同樣的,一個graph可以供多個session使用,而一個session不一定需要使用graph的全部,可以只使用其中的一部分。

關於graph

定義一個圖:graph

g = tf.Graph()
a = tf.constant(2)
b = tf.constant(3)
x = tf.add(a, b) 

上面就定義了一個graph。tensorflow會默認給我們建立一個graph,所以g = tf.Graph()這句其實是可以省略的。上面的graph包含3個操作,即op,但凡是op,都需要通過session運行之後,才能得到結果。如果你直接執行print(a),那麽輸出結果是:

Tensor("a:0", shape=(), dtype=int32)

是一個張量(Tensor)。如果你執行print(tf.Session().run(a))

,才能得到2.

關於子圖:subgraph

你可以定義多個graph,例如一個graph實現z = x + y,另一個graph實現u = 2 * v

g1 = tf.Graph()
g2 = tf.Graph()
with g1.as_default():
    x = tf.constant(2)
    y = tf.constant(3)
    z = tf.add(x, y)
with g2.as_default():
    v = tf.constant(4)
    u = tf.mul(2, v)

但通常不建議這麽做,原因如下:

  • 運行多個graph需要多個session,而每個session會試圖耗盡所有的計算資源,開銷太大;
  • graph之間沒有數據通道,要人為通過python/numpy傳數據;

事實上,你可以把所有的op都定義在一個graph中:

x = tf.constant(2)
y = tf.constant(3)
z = tf.add(x, y)
v = tf.constant(4)
u = tf.mul(2, v)

從上面graph的定義可以看到,x/y/z是一波,u/v是另一波,二者沒有任何交集。這相當於在一個graph裏有兩個獨立的subgraph。當你要計算z = x + y時,執行tf.Session().run(z);當你想計算u = 2 * v,就執行tf.Session().run(u),二者完全獨立。但更重要的是,二者在同一個session上運行,系統會均衡地給兩個subgraph分配合適的計算資源。

關於session

通常我們會顯示地定義一個session來運行graph:

x = tf.constant(2)
y = tf.constant(3)
z = tf.add(x, y)

with tf.Session() as sess:
    result = sess.run(z)
    print(result)

輸出結果是5。

關於op

tensorflow是一個符號式編程的框架,首先要定義一個graph,然後用一個session來運行這個graph得到結果。graph就是由一系列op構成的。上面的tf.constant()tf.add()tf.mul()都是op,都要現用session運行,才能得到結果。

很多人會以為tf.Variable()也是op,其實不是的。tensorflow裏,首字母大寫的類,首字母小寫的才是op。tf.Variable()就是一個類,不過它包含了各種op,比如你定義了x = tf.Variable([2, 3], name = ‘vector‘),那麽x就具有如下op:

  • x.initializer # 對x做初始化,即賦值為初始值[2, 3]
  • x.value() # 獲取x的值
  • x.assign(...) # 賦值操作
  • x.assign_add(...) # 加法操作

tf.Variable()必須先初始化,再做運算,否則會報錯。下面的寫法就不是很安全,容易導致錯誤:

W = tf.Variable(tf.truncated_normal([700, 10]))
U = tf.Variable(2 * W)

要把W賦值給U,必須現把W初始化。但很多人往往忘記初始化,從而出錯。保險起見,應該按照下面這樣寫:

W = tf.Variable(tf.truncated_normal([700, 10]))
U = tf.Variable(2 * W.intialized_value())

一個特殊的op: tf.placeholder()

placeholder,翻譯過來就是占位符。其實它類似於函數裏的自變量。比如z = x + y,那麽xy就可以定義成占位符。占位符,顧名思義,就這是占一個位子,平時不用關心它們的值,當你做運算的時候,你再把你的數據灌進去就行了。是不是和自變量很像?看下面的代碼:

a = tf.placeholder(tf.float32, shape=[3]) # a是一個3維向量
b = tf.constant([5, 5, 5], tf.float32)
c = a + b
with tf.Session() as sess:
    print sess.run(c, feed_dict = {a: [1, 2, 3]}) # 把[1, 2, 3]灌到a裏去

輸出結果是[6, 7, 8]。上面代碼中出現了feed_dict的概念,其實就是用[1, 2, 3]代替a的意思。相當於在本輪計算中,自變量a的取值為[1, 2, 3]。其實不僅僅是tf.placeholder才可以用feed_dict,很多op都可以。只要tf.Graph.is_feedable(tensor)返回值是True,那麽這個tensor就可用用feed_dict來灌入數據。

tf.constant()是直接定義在graph裏的,它是graph的一部分,會隨著graph一起加載。如果通過tf.constant()定義了一個維度很高的張量,那麽graph占用的內存就會變大,加載也會變慢。而tf.placeholder就沒有這個問題,所以如果數據維度很高的話,定義成tf.placeholder是更好的選擇。

斯坦福CS20SI TensorFlow學習筆記1——graph、session和op