前一篇主要對(duì)TensorFlow的常量,如簡(jiǎn)單的scalar, vector, matrix等Tensor,以及線性序列(Sequence),基本的抽樣函數(shù)做了介紹,本篇主要介紹TensorFlow中的變量,數(shù)據(jù)類型,基本的數(shù)學(xué)操作,并結(jié)合TensorBoard,來介紹一些變量依賴的組織。
搞機(jī)器學(xué)習(xí)或是數(shù)值計(jì)算程序的人估計(jì)都了解NumPy,或者整個(gè)python-based的數(shù)理科學(xué)計(jì)算的生態(tài)系統(tǒng),SciPy。
數(shù)據(jù)類型
TensorFlow的基本數(shù)據(jù)類型在借鑒NumPy(且當(dāng)前對(duì)NumPy的類型幾乎完全兼容)的基礎(chǔ)上,也有些自己原生的一些數(shù)據(jù)類型,完整的數(shù)據(jù)類型列表可以參見官網(wǎng),下表給出一些基本的數(shù)據(jù)類型:
| Data Type | Python type | Description |
|---|---|---|
| DT_FLOAT | tf.float32 | 32 bits floating point |
| DT_DOUBLE | tf.float64 | 64 bits floating point |
| DT_INT32 | tf.int32 | 32 bits signed int |
| DT_INT64 | tf.int64 | 64 bits signed int |
| DT_STRING | tf.string | Variable length byte array |
| DT_BOOL | tf.bool | Boolean |
因?yàn)門ensorFlow數(shù)據(jù)類型保持了跟NumPy的無縫集成,大多數(shù)時(shí)候你可以把NumPy的數(shù)據(jù)類型當(dāng)成TensorFlow的來用,但通常的practice是如果可以用TensorFlow原生數(shù)據(jù)類型的地方,我們就直接用原生的。一方面是誰(shuí)也不知道現(xiàn)在的版本是兼容的,以后會(huì)不會(huì)久不兼容了呢?最重要的是,原生的數(shù)據(jù)類型對(duì)TensorFlow的build-in函數(shù),求導(dǎo)優(yōu)化計(jì)算等都有天然的優(yōu)勢(shì)。
變量
前一篇我們介紹了常量,常量跟變量的區(qū)別自然不必贅述,就跟任何語(yǔ)言類似。其存儲(chǔ)的地方也不同,比如Java中的常量是放在堆區(qū),而變量是在棧區(qū)。在TensorFlow里,常量和變量也是分開存儲(chǔ)的。常量的值是放在graph的definition里的,在分布式環(huán)境下,每個(gè)節(jié)點(diǎn)上整個(gè)graph都是replicated的,相應(yīng)的常量也會(huì)replicated一份。對(duì)于TensorFlow而言,Graph的definition用protocol buffer來表述:
import tensorflow as tf
vector = tf.constant([2.0, 8], name="vector")
print tf.get_default_graph().as_graph_def()
而變量則不同,往往是放在一些獨(dú)立的集群上<label for="sn-1" class="margin-toggle sidenote-number" style="box-sizing: border-box; margin: 0px 0px 5px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 700; font-stretch: inherit; font-size: inherit; line-height: inherit; font-family: inherit; vertical-align: baseline; display: inline; max-width: 100%; counter-increment: sidenote-counter 1;"></label>
即我們通常所說的Parameter Server,是目前主流的分布式機(jī)器學(xué)習(xí)框架,原始論文參見
,其需要和worker節(jié)點(diǎn)進(jìn)行跨網(wǎng)絡(luò)或者RPC通信。
變量聲明
和一般的面向?qū)ο笳Z(yǔ)言一樣,聲明一個(gè)變量就是創(chuàng)建一個(gè)tf.Variable類的實(shí)例。和常量不同的是,我們用tf.constant來聲明一個(gè)常量。前者是一個(gè)Class,而后者是一個(gè)operator(可以看作是一個(gè)類里的方法),一個(gè)tf.Variable類里可以有多個(gè)operator。
import tensorflow as tf
# use InteractiveSession
sess = tf.InteractiveSession()
cons = tf.constant(8, name="consant")
var = tf.Variable(8, name="Variable")
# ==> Tensor("consant:0", shape=(), dtype=int32)
print cons
# ==> <tf.Variable 'Variable:0' shape=() dtype=int32_ref>
print var
變量初始化
在使用一個(gè)變量之前,必須先初始化變量,如果使用了一個(gè)未初始化的變量,會(huì)報(bào)一個(gè)FailedPreconditionError的錯(cuò)誤,如下所示。值得注意的時(shí),和一般的面向?qū)ο笳Z(yǔ)言不同的是,第3行中,tf.Variable(8, name="Variable"),雖然第一個(gè)參數(shù)是8,似乎是做了初始化,其實(shí)不然。前面我們說過,TensorFlow作為一門工具語(yǔ)言,一個(gè)鮮明的特點(diǎn)便是函數(shù)(這里是Graph)的定義和執(zhí)行是分開的。初始化函數(shù)也需要顯示的聲明和執(zhí)行。
全局初始化函數(shù)
import tensorflow as tf
cons = tf.constant(8, name="consant")
var = tf.Variable(8, name="Variable")
init = tf.global_variables_initializer() ## 全局初始化函數(shù)聲明
with tf.Session() as sess:
sess.run(init) # 執(zhí)行全局初始化
print sess.run(cons) # OK, result is 8
# 未初始化: FailedPreconditionError: Attempting to use uninitialized value Variable_2
# 初始化:result is 8.
print sess.run(var)
選擇性初始化
import tensorflow as tf
var1 = tf.Variable(8, name="var1")
var2 = tf.Variable(8, name="var2")
init = tf.variables_initializer([var1]) ## 初始化var1
with tf.Session() as sess:
sess.run(init) # 執(zhí)行全局初始化
# OK, result is 8
print sess.run(var1)
# 未初始化: FailedPreconditionError: Attempting to use uninitialized value var2_2
print sess.run(var2)
單個(gè)變量初始化
import tensorflow as tf
var2 = tf.Variable(8, name="var1")
with tf.Session() as sess:
sess.run(var2.initializer)
print sess.run(var2) # OK, result is 8.
變量評(píng)估和賦值
賦值函數(shù)assign()
變量聲明之后,我們可以通過在Session中的sess.run()來查看一個(gè)變量的值,還有一個(gè)eval()函數(shù)也可以實(shí)現(xiàn)該功能。變量的賦值則是通過assign()函數(shù)來實(shí)現(xiàn),值得注意的是這里的賦值是非引用式的,函數(shù)返回賦值后的值,但該變量的值不會(huì)改變。
import tensorflow as tf
var1 = tf.Variable(8, name="var1")
var1.assign(100) # 變量賦值,var1 任然是8
init = tf.variables_initializer([var1])
with tf.Session() as sess:
print var1.eval() # OK, result is 8.
值得注意的是在使用了賦值函數(shù)后,我們并沒有對(duì)var2進(jìn)行初始化卻可以正確使用。
查看源碼會(huì)發(fā)現(xiàn),其實(shí)是assign()函數(shù)替我們做了。當(dāng)我們通過賦值函數(shù)聲明一個(gè)變量,但這個(gè)變量依賴于另外一個(gè)變量時(shí),情況便變得很有趣了。如下所示:
import tensorflow as tf
var1 = tf.Variable(8, name="var1")
var2 = var1.assign(var1*2)
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(var1.initializer)
print var2.eval() # result is 16
print var2.eval() # result is 32
print var2.eval() # result is 64
var2被assign的不是一個(gè)值,而是一個(gè)assign的operator,因此在Session里,每次run時(shí)就會(huì)做一次評(píng)估,這就好比C語(yǔ)言中的宏擴(kuò)展。
自增和自減函數(shù)
TensorFlow提供了assign_add()和assgin_sub()函數(shù)來實(shí)現(xiàn)函數(shù)的自增自減功能。和assign()函數(shù)會(huì)自動(dòng)幫你初始化變量不同,自增、自減函數(shù)并不會(huì)幫你賦值,你需要自己賦值
原因其均依賴于當(dāng)前的值做assign,因此需要對(duì)var1和var2都要做初始化。
TensorBoard
數(shù)據(jù)可視化在ML里一直是被忽視但又非常重要的一部分。記得Andrew Ng最常說的一句話便是在做任何數(shù)據(jù)分析處理之前一定要對(duì)數(shù)據(jù)有個(gè)直觀的sense,數(shù)據(jù)可視化便是重要的方法,因?yàn)槠鋵?duì)模型,參數(shù)選擇都至關(guān)重要。TensorFlow Dev Summit 2017中用很大的篇幅介紹了TensorBoard,如MNIST手寫辨識(shí)的demo中長(zhǎng)這樣

用Google自己的話說,
"The computations you'll use TensorFlow for -like training a massive deep neural network - can be complex and confusing. To make it easier to understand, debug, and optimize TensorFlow programs, we've included a suite of visualization tools call TensorBoard".
更詳盡的介紹和使用將根據(jù)具體的的機(jī)器學(xué)習(xí)的例子來介紹更具有直觀性。最簡(jiǎn)單的使用僅兩步:首先在Session里首行加上,
writer = tf.summary.FileWriter('./graphs', sess.graph)
然后在shell里run如下命令。打開瀏覽器,http://localhost:6006,就可以看見針對(duì)當(dāng)前Session的TensorFlow可視化效果。
$ tensorboard --logdir="./graphs"
參考
[2]