斯坦福CS20SI TensorFlow學習筆記1——graph、session和op
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))
關於子圖: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
,那麽x
和y
就可以定義成占位符。占位符,顧名思義,就這是占一個位子,平時不用關心它們的值,當你做運算的時候,你再把你的數據灌進去就行了。是不是和自變量很像?看下面的代碼:
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