前面的章節(jié)已經(jīng)介紹了如何使用TensorFlow實(shí)現(xiàn)常用的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)帘腹。在將這些神經(jīng)網(wǎng)絡(luò)用于實(shí)際問題之前,需要先優(yōu)化網(wǎng)絡(luò)中的參數(shù)许饿。這就是訓(xùn)練神經(jīng)網(wǎng)絡(luò)的過程阳欲。訓(xùn)練神經(jīng)網(wǎng)絡(luò)十分復(fù)雜,有時需要幾天甚至幾周的時間陋率。為了更好地管理球化、調(diào)試和優(yōu)化神經(jīng)網(wǎng)絡(luò)的訓(xùn)練過程,TensorFlow提供了一個可視化工具TensorBoard瓦糟。TensorBoard可以有效地展示TensorFlow在運(yùn)行過程中的計(jì)算圖筒愚、各種指標(biāo)隨著時間的變化趨勢以及訓(xùn)練中使用到的圖像等信息。
本章將詳細(xì)介紹TensorBoard的使用方法菩浙。
1.將介紹TensorBoard的基礎(chǔ)知識巢掺,并通過TensorBoard來可視化一個簡單的TensorFlow樣例程序。在這一節(jié)中將介紹如何啟動TensorBoard劲蜻,并大致講解TensorBoard提供的幾類可視化信息陆淀。
2.介紹通過TensorBoard得到的TensorFlow計(jì)算圖的可視化結(jié)果。TensorFlow計(jì)算圖保存了TensorFlow程序計(jì)算過程的所有信息先嬉。因?yàn)門ensorFlow計(jì)算圖中的信息含量較多轧苫,所以TensorBoard設(shè)計(jì)了一套交互過程來更加清晰地呈現(xiàn)這些信息。
3.最后將詳細(xì)講解如何使用在這一節(jié)中將詳細(xì)講解如何使用TensorBoard對訓(xùn)練過程進(jìn)行監(jiān)控疫蔓,以及如何通過TensorFlow指定對訓(xùn)練過程進(jìn)行監(jiān)控含懊,以及如何通過TensorFlow指定需要可視化的指標(biāo)。并給出完整的樣例程序介紹如何得到可視化結(jié)果衅胀。
1.TensorBoard簡介
TensorBoard是TensorFlow的可視化工具绢要,它可以通過TensorFlow程序運(yùn)行過程中輸出的日志文件可視化TensorFlow程序的運(yùn)行狀態(tài)。TensorBoard和TensorFlow程序跑在不同的進(jìn)程中拗小,TensorBoard會自動讀取最新的TensorFlow日志文件重罪,并呈現(xiàn)當(dāng)前TensorFlow程序運(yùn)行的最新狀態(tài)。以下代碼展示了一個簡單的TensorFlow程序哀九,這個程序中完成了TensorBoard日志輸出的功能:
import tensorflow as tf
input1 = tf.constant([1.0, 2.0, 3.0], name="input1")
input2 = tf.Variable(tf.random_uniform([3]), name="input2")
output = tf.add_n([input1, input2], name="add")
writer = tf.summary.FileWriter("C:\\log", tf.get_default_graph())
writer.close()
以上程序輸出了TensorFlow計(jì)算圖的信息剿配,所以運(yùn)行Tensorboard時,可以看到這個向量相加程序計(jì)算圖可視化之后的結(jié)果阅束。TensorBoard不需要額外的安裝過程呼胚,在TensorFlow安裝完成時,TensorBoard會被自動安裝息裸。運(yùn)行下面的命令可以啟動TensorBoard:
運(yùn)行TensorBoard蝇更,并將日志的地址指向上面程序日志輸出的地址
tensorboard --logdir=C:\\log
運(yùn)行上面的命令會啟動一個服務(wù)沪编,這個服務(wù)的端口默認(rèn)為6006.通過瀏覽器打開localhost:6006,可以看到下圖所示的界面:
如上圖,可以看到上面程序TensorFlow計(jì)算圖的可視化結(jié)果年扩。
在界面右上方蚁廓,點(diǎn)擊下拉菜單可以看到有12欄,每一欄都對應(yīng)了一類信息的可視化結(jié)果厨幻,在下面的章節(jié)中將具體介紹每一欄的功能相嵌。如圖,打開tensorboard界面會默認(rèn)進(jìn)入SCALARS欄况脆。因?yàn)樯厦娴某绦驔]有輸出任何由EVENTS欄可視化的信息饭宾,所以這個界面顯示的是“NO scalar data was found”(沒有發(fā)現(xiàn)標(biāo)量數(shù)據(jù))。
2.TensorFlow計(jì)算圖可視化
在上圖中給出了一個TensorFlow計(jì)算圖的可視化效果圖格了。然而看铆,從TensorBoard可視化結(jié)果中可以獲取的信息遠(yuǎn)不止上圖所示的這些。這一節(jié)將詳細(xì)介紹如何更好地利用TensorFlow計(jì)算圖的可視化結(jié)果盛末。
1.首先將介紹通過TensorFlow節(jié)點(diǎn)的命名空間整理Tensorboard可視化得到的TensorFlow計(jì)算圖性湿。在前面介紹過,TensorFlow會將所有的計(jì)算以圖的形式組織起來满败。TensorBoard可視化得到的圖并不僅是將TensorFlow計(jì)算圖中的節(jié)點(diǎn)和邊直接可視化,它會根據(jù)每個TensorFlow計(jì)算節(jié)點(diǎn)的命名空間來整理可視化得到的效果叹括,使得神經(jīng)網(wǎng)絡(luò)的整體結(jié)構(gòu)不會被過多的細(xì)節(jié)所淹沒算墨。除了顯示TensorFlow計(jì)算圖的結(jié)構(gòu),TensorBoard還可以展示TensorFlow計(jì)算節(jié)點(diǎn)上的其他信息汁雷。
2.第二小節(jié)將介紹如何從可視化結(jié)果中獲取TensorFlow計(jì)算圖中的這些信息净嘀。
2.1命名空間與TensorBoard圖上節(jié)點(diǎn)
在上一節(jié)給出的樣例程序中只定義了一個加法操作,然而從圖中可以看到侠讯,將這個TensorFlow計(jì)算圖可視化得到的效果圖上卻有
多個節(jié)點(diǎn)挖藏,可以想象,一個復(fù)雜的神經(jīng)網(wǎng)絡(luò)模型所對應(yīng)的TensorFlow計(jì)算圖會比上圖中簡單的向量加法樣例程序的計(jì)算圖復(fù)雜很多厢漩,那么沒有經(jīng)過整理得到的可視化效果圖并不能幫助很好地理解神經(jīng)網(wǎng)絡(luò)模型的結(jié)構(gòu)膜眠。
為了更好地組織可視化效果圖中的計(jì)算節(jié)點(diǎn),TensorBoard支持通過TensorFlow命名空間來整理可視化效果圖上的節(jié)點(diǎn)溜嗜。在TensorBoard的默認(rèn)視圖中宵膨,TensorFlow計(jì)算圖中同一個命名空間下的所有節(jié)點(diǎn)會被縮略成一個節(jié)點(diǎn),只有頂層命名空間中的節(jié)點(diǎn)才會被顯示在TensorBoard可視化效果圖上炸宵。在前面介紹過變量的命名空間辟躏,以及如何通過tf.variable_scope函數(shù)管理變量的命名空間。除了tf.variable_scope函數(shù)土全,tf.name_scope函數(shù)也提供了命名空間管理的功能捎琐。這兩個函數(shù)在大部分情況下是等價(jià)的会涎,唯一的區(qū)別是在使用tf.get_variable函數(shù)時,以下代碼簡單地說明了這兩個函數(shù)的區(qū)別:
1.不同的命名空間
import tensorflow as tf
with tf.variable_scope("foo"):
a = tf.get_variable("bar", [1])
print a.name
with tf.variable_scope("bar"):
b = tf.get_variable("bar", [1])
print b.name
輸出:
- tf.Variable和tf.get_variable的區(qū)別瑞凑。
with tf.name_scope("a"):
a = tf.Variable([1])
print a.name
a = tf.get_variable("b", [1])
print a.name
輸出:
結(jié)果分析:
使用tf.Variable函數(shù)生成變量會受到tf.name_scope影響末秃,于是這個變量的名稱為“a/Variable”
而使用tf.get_variable函數(shù)不受tf.name_scope函數(shù)的影響,于是變量并不在b這個命名空間中拨黔。所以這個變量名稱為b
通過對命名空間管理蛔溃,可以改進(jìn)上一節(jié)中向量加法的樣例代碼,使得可視化得到的效果圖更加清晰篱蝇。以下代碼展示了改進(jìn)的方法:
with tf.name_scope("input1"):
input1 = tf.constant([1.0, 2.0, 3.0], name="input1")
with tf.name_scope("input2"):
input2 = tf.Variable(tf.random_uniform([3]), name="input2")
output = tf.add_n([input1, input2], name="add")
writer = tf.summary.FileWriter("C:\\log", tf.get_default_graph())
writer.close()
下圖顯示了改進(jìn)后的可視化效果圖:
從圖中可以看到贺待,上圖中很多的節(jié)點(diǎn)都被縮略到了圖中input2節(jié)點(diǎn)。這樣TensorFlow程序中定義的加法運(yùn)算被清晰地展示了出來零截。需要查看input2節(jié)點(diǎn)中具體包含了哪些運(yùn)算時麸塞,可以將鼠標(biāo)移動到input2節(jié)點(diǎn),并點(diǎn)開右上角加好“+”涧衙。下圖展示了input2節(jié)點(diǎn)之后的視圖:
在input2的展開圖中可以看到數(shù)據(jù)初始化相關(guān)的操作都被整理到了一起哪工。下面將給出一個樣例程序來展示如何很好地可視化一個真實(shí)的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)圖。本節(jié)將繼續(xù)采用之前的MNIST數(shù)字識別問題哪一章的架構(gòu)弧哎,以下代碼給出了改造后的mnist_train.py程序:
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import mnist_inference
- 定義神經(jīng)網(wǎng)絡(luò)的參數(shù)雁比。
BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.8
LEARNING_RATE_DECAY = 0.99
REGULARIZATION_RATE = 0.0001
TRAINING_STEPS = 3000
MOVING_AVERAGE_DECAY = 0.99
- 定義訓(xùn)練的過程并保存TensorBoard的log文件。
def train(mnist):
# 輸入數(shù)據(jù)的命名空間撤嫩。
with tf.name_scope('input'):
x = tf.placeholder(tf.float32, [None, mnist_inference.INPUT_NODE], name='x-input')
y_ = tf.placeholder(tf.float32, [None, mnist_inference.OUTPUT_NODE], name='y-input')
regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
y = mnist_inference.inference(x, regularizer)
global_step = tf.Variable(0, trainable=False)
# 處理滑動平均的命名空間偎捎。
with tf.name_scope("moving_average"):
variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
variables_averages_op = variable_averages.apply(tf.trainable_variables())
# 計(jì)算損失函數(shù)的命名空間。
with tf.name_scope("loss_function"):
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
cross_entropy_mean = tf.reduce_mean(cross_entropy)
loss = cross_entropy_mean + tf.add_n(tf.get_collection('losses'))
# 定義學(xué)習(xí)率序攘、優(yōu)化方法及每一輪執(zhí)行訓(xùn)練的操作的命名空間茴她。
with tf.name_scope("train_step"):
learning_rate = tf.train.exponential_decay(
LEARNING_RATE_BASE,
global_step,
mnist.train.num_examples / BATCH_SIZE, LEARNING_RATE_DECAY,
staircase=True)
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
with tf.control_dependencies([train_step, variables_averages_op]):
train_op = tf.no_op(name='train')
writer = tf.summary.FileWriter("log", tf.get_default_graph())
# 訓(xùn)練模型。
with tf.Session() as sess:
tf.global_variables_initializer().run()
for i in range(TRAINING_STEPS):
xs, ys = mnist.train.next_batch(BATCH_SIZE)
if i % 1000 == 0:
# 配置運(yùn)行時需要記錄的信息程奠。
run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
# 運(yùn)行時記錄運(yùn)行信息的proto丈牢。
run_metadata = tf.RunMetadata()
_, loss_value, step = sess.run(
[train_op, loss, global_step], feed_dict={x: xs, y_: ys},
options=run_options, run_metadata=run_metadata)
writer.add_run_metadata(run_metadata=run_metadata, tag=("tag%d" % i), global_step=i)
print("After %d training step(s), loss on training batch is %g." % (step, loss_value))
else:
_, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: xs, y_: ys})
writer.close()
- 主函數(shù)。
def main(argv=None):
mnist = input_data.read_data_sets("../../datasets/MNIST_data", one_hot=True)
train(mnist)
if __name__ == '__main__':
main()
4.運(yùn)行結(jié)果如下圖:
相比前面給出的mnist_train.py程序瞄沙,上面程序最大的改變就是將完成類似功能的計(jì)算放到了由tf.name_scope函數(shù)生成的上下文管理器中己沛。這樣TensorBoard可以將這些節(jié)點(diǎn)有效地合并,從而突出神經(jīng)網(wǎng)絡(luò)的整體結(jié)構(gòu)距境。因?yàn)樵趍nist_inference.py程序中已經(jīng)使用了tf.variable_scope來管理變量的命名空間泛粹,所以這里不需要再做調(diào)整。下圖展示了新的MNIST程序的TensorFlow計(jì)算圖可視化得到的效果圖:
從圖中可以看到肮疗,TensorBoard可視化效果圖很好地展示了整個神經(jīng)網(wǎng)絡(luò)的結(jié)構(gòu)晶姊,在圖中,input節(jié)點(diǎn)代表了訓(xùn)練神經(jīng)網(wǎng)絡(luò)需要的輸入數(shù)據(jù)伪货,這些輸入數(shù)據(jù)會提供給神經(jīng)網(wǎng)絡(luò)的第一層layer1.然后神經(jīng)網(wǎng)絡(luò)第一層layer1的結(jié)果會被傳到第二層layer2们衙,經(jīng)過layer2的計(jì)算得到前向傳播的結(jié)果钾怔。loss_function節(jié)點(diǎn)表示計(jì)算損失函數(shù)的過程,這個過程既依賴于前向傳播的結(jié)果來計(jì)算交叉熵(layer2到loss_function的邊)蒙挑,又依賴于每一層中所定義的變量來計(jì)算L2正則化損失(layer1和layer2到loss_function的邊)宗侦。loss_function的計(jì)算結(jié)果會提供給神經(jīng)網(wǎng)絡(luò)的優(yōu)化過程,也就是圖中train_step所代表的節(jié)點(diǎn)忆蚀。綜上所述矾利,通過TensorBoard可視化得到的效果圖可以對整個神經(jīng)網(wǎng)絡(luò)的網(wǎng)絡(luò)結(jié)構(gòu)有一個大致了解。
在圖中可以發(fā)現(xiàn)節(jié)點(diǎn)之間有兩種不同的邊馋袜。一種邊是通過實(shí)線表示的男旗,這種邊刻畫了數(shù)據(jù)傳輸,邊上的箭頭方向表達(dá)了數(shù)據(jù)傳輸?shù)姆较蛐辣睢1热鏻ayer1和layer2之間的邊表示了layer1的輸出將會作為layer2的輸入察皇。有些邊上的箭頭是雙向的,比如節(jié)點(diǎn)Variable和train_step之間的邊泽台。這表明train_step會修改Variable的狀態(tài)什荣,在這里也就是表明訓(xùn)練過程會修改記錄訓(xùn)練迭代輪數(shù)的變量。
TensorBoard可視化效果圖的邊上還標(biāo)注了張量的維度信息怀酷。下圖放大了可視化得到的效果圖:
從圖中可以看出稻爬,節(jié)點(diǎn)input和layer1之間傳輸?shù)膹埩康木S度為100*784.這說明了訓(xùn)練時提供的batch大小為100,輸入層節(jié)點(diǎn)的個數(shù)為784.當(dāng)兩個節(jié)點(diǎn)之間傳輸?shù)膹埩慷嘤?個時蜕依,可視化效果圖上將只顯示張量的個數(shù)桅锄。效果圖上邊的粗細(xì)表示的是兩個節(jié)點(diǎn)之間傳輸?shù)臉?biāo)量維度的總大小,而不是傳輸?shù)臉?biāo)量個數(shù)笔横。比如layer2和loss_function之間雖然傳輸了兩個張量,但其維度都比較小咐吼,所以這條邊比layer1和layer2之間的邊還要細(xì)吹缔。
TensorBoard可視化效果圖上另外一種邊是通過虛線表示的,比如圖中所示moving_average和train_step之間的邊锯茄。虛邊表達(dá)了計(jì)算之間的依賴關(guān)系厢塘,比如在程序中,通過tf.control_dependencies函數(shù)指定了更新參數(shù)滑動平均值的操作和通過反方向傳播更新變量的操作需要同時進(jìn)行肌幽,于是moving_average與train_step之間存在一條虛邊晚碾。
除了手動的通過TensorFlow中的命名空間來調(diào)整TensorBoard的可視化效果圖,TensorBoard也會智能地調(diào)整可視化效果圖上的節(jié)點(diǎn)喂急。TensorFlow中部分計(jì)算節(jié)點(diǎn)會有比較多的依賴關(guān)系格嘁,如果全部畫在一張圖上會使可視化得到的效果圖非常擁擠。于是TensorBoard將TensorFlow計(jì)算圖分成了主圖(Main Graph)和輔助圖(Auxiliary nodes)兩個部分來呈現(xiàn)廊移。上面第一個圖中糕簿,左側(cè)展示的是計(jì)算圖的主要部分探入,也就是主圖;右側(cè)展示的是一些單獨(dú)列出來的節(jié)點(diǎn)懂诗,也就是輔助圖蜂嗽。TensorBoard會自動將連接比較多的節(jié)點(diǎn)放在輔助圖中,使得主圖的結(jié)構(gòu)更加清晰殃恒。
除了自動的方式走哺,TensorBoard也支持手工的方式來調(diào)整可視化效果挤茄,如下圖所示:
如圖,右鍵單擊可視化效果圖上的節(jié)點(diǎn)會彈出一個選項(xiàng),這個選項(xiàng)可以將節(jié)點(diǎn)加入主圖或者從主圖中刪除谈秫。左鍵選擇一個節(jié)點(diǎn)并點(diǎn)擊信息框下部的選項(xiàng)也可以完成類似的功能。
2.2節(jié)點(diǎn)信息
除了展示TensorFlow計(jì)算圖的結(jié)構(gòu)膝昆,TensorBoard還可以展示TensorFlow計(jì)算圖上每個節(jié)點(diǎn)的基本信息以及運(yùn)行時消耗的時間和空間以清。本小節(jié)將進(jìn)一步講解如何通過TensorBoard展現(xiàn)TensorFlow計(jì)算圖節(jié)點(diǎn)上的這些信息。TensorFlow計(jì)算節(jié)點(diǎn)的運(yùn)行時間都是非常有用的信息贮竟,它可以幫助更加有針對性地優(yōu)化TensorFlow程序丽焊,使得整個程序的運(yùn)行速度更快。使用TensorBoard可以非常直觀地展現(xiàn)所有TensorFlow計(jì)算節(jié)點(diǎn)在某一次運(yùn)行時所消耗的時間和內(nèi)存咕别。將以下代碼加入上一小節(jié)中修改后的mnist_train.py神經(jīng)網(wǎng)絡(luò)訓(xùn)練部分技健,就可以將不同迭代輪數(shù)時每一個TensorFlow計(jì)算節(jié)點(diǎn)的運(yùn)行時間和消耗的內(nèi)存寫入TensorBoard的日志文件中:
# 訓(xùn)練模型惰拱。
with tf.Session() as sess:
tf.global_variables_initializer().run()
for i in range(TRAINING_STEPS):
xs, ys = mnist.train.next_batch(BATCH_SIZE)
# 每1000輪記錄一次運(yùn)行狀態(tài)
if i % 1000 == 0:
# 配置運(yùn)行時需要記錄的信息雌贱。
run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
# 運(yùn)行時記錄運(yùn)行信息的proto。
run_metadata = tf.RunMetadata()
# 將配置信息和記錄運(yùn)行信息的proto傳入運(yùn)行的過程,從而記錄運(yùn)行時每一個節(jié)點(diǎn)的時間、空間開銷信息
_, loss_value, step = sess.run(
[train_op, loss, global_step], feed_dict={x: xs, y_: ys},
options=run_options, run_metadata=run_metadata)
# 將節(jié)點(diǎn)在運(yùn)行時的信息寫入日志文件
writer.add_run_metadata(run_metadata=run_metadata, tag=("tag%d" % i), global_step=i)
print("After %d training step(s), loss on training batch is %g." % (step, loss_value))
else:
_, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: xs, y_: ys})
運(yùn)行以上程序,并使用這個程序輸出的日志啟動TensorBoard,就可以可視化每個TensorFlow計(jì)算節(jié)點(diǎn)在某一次運(yùn)行時所消耗的時間和空間了孝治。進(jìn)入GRAPHS欄后瘩蚪,需要先選擇一次運(yùn)行來查看呢灶。
如下圖所示:
如圖所示奖年,點(diǎn)擊頁面左側(cè)的Session.runs選項(xiàng)會出現(xiàn)一個下拉單,在這個下拉單中會出現(xiàn)所有通過train_writer.add_run_metadata函數(shù)記錄的運(yùn)行數(shù)據(jù)。選擇一次運(yùn)行后祭往,TensorBoard左側(cè)的Color欄中將會新出現(xiàn)Computetime和Memory這兩個選項(xiàng)伦意。
在Color欄中選擇Compute time可以看到在這次運(yùn)行中每個TensorFlow計(jì)算節(jié)點(diǎn)的運(yùn)行時間。類似的硼补,選擇Memory可以看到這次運(yùn)行中每個TensorFlow計(jì)算節(jié)點(diǎn)所消耗的內(nèi)存驮肉。下圖展示了在第2000輪迭代時,不同TensorFlow計(jì)算節(jié)點(diǎn)時間消耗的可視化效果圖:
上圖中顏色越深的節(jié)點(diǎn)表示時間消耗越大已骇。從圖中可以看出离钝,代表訓(xùn)練神經(jīng)網(wǎng)絡(luò)的train_step節(jié)點(diǎn)消耗的時間是最多的。通過對每一個計(jì)算節(jié)點(diǎn)消耗時間的可視化褪储,可以很容易地找到TensorFlow計(jì)算圖上的性能瓶頸卵渴,這大大方便了算法優(yōu)化的工作。在性能調(diào)優(yōu)時鲤竹,一般會選擇迭代輪數(shù)較大時的數(shù)據(jù)(比如上圖中第2000輪迭代時的數(shù)據(jù))作為不同計(jì)算節(jié)點(diǎn)時間/空間消耗的標(biāo)準(zhǔn)浪读,因?yàn)檫@樣可以減少TensorFlow初始化對性能的影響。
在TensorBoard界面左側(cè)的Color欄中,除了Compute time和Memory碘橘,還有Structure和Device兩個選項(xiàng)互订。除了上一圖,在上面幾個圖中痘拆,展示的可視化效果圖都是使用默認(rèn)的Structure選項(xiàng)仰禽。在這個視圖中,灰色的節(jié)點(diǎn)表示沒有其他節(jié)點(diǎn)和它擁有相同結(jié)構(gòu)纺蛆。如果有兩個節(jié)點(diǎn)的結(jié)構(gòu)相同坟瓢,那么它們會被涂上相同的顏色。下圖展示了一個擁有相同結(jié)構(gòu)節(jié)點(diǎn)的卷積神經(jīng)網(wǎng)絡(luò)可視化得到的效果圖:
在上圖中犹撒,兩個卷積層的結(jié)構(gòu)是一樣的折联,所以他們都被涂上了相同的顏色。最后识颊,Color欄還可以選擇Device選項(xiàng)诚镰,這個選項(xiàng)可以根據(jù)TensorFlow計(jì)算節(jié)點(diǎn)運(yùn)行的機(jī)器給可視化效果圖上的節(jié)點(diǎn)染色。在使用GPU時祥款,可以通過這種方式直觀地看到哪些計(jì)算節(jié)點(diǎn)被放到了GPU上清笨。下圖給出了一個使用了GPU的TensorFlow計(jì)算圖的可視化效果:
上圖中深灰色的節(jié)點(diǎn)表示對應(yīng)的計(jì)算放在了GPU上,淺灰色的節(jié)點(diǎn)表示對應(yīng)的計(jì)算放在了CPU上刃跛。具體如何使用GPU將在下一章介紹抠艾。
當(dāng)點(diǎn)擊TensorBoard可視化效果圖中的節(jié)點(diǎn)時,界面的右上角會彈出一個信息卡片顯示這個節(jié)點(diǎn)的基本信息桨昙,如下圖:
當(dāng)點(diǎn)擊的節(jié)點(diǎn)為一個命名空間時检号,TensorBoard展示的信息有這個命名空間內(nèi)所有計(jì)算節(jié)點(diǎn)的輸入、輸出以及依賴關(guān)系蛙酪。雖然屬性(Attribute)也會展示在卡片中齐苛,但是在代表命名空間的屬性下不會有任何內(nèi)容。當(dāng)Session.runs選擇了某一次運(yùn)行時桂塞,節(jié)點(diǎn)的信息卡片上會出現(xiàn)這個節(jié)點(diǎn)運(yùn)行所消耗的時間和內(nèi)存等信息凹蜂。
當(dāng)點(diǎn)擊的TensorBoard可視化效果圖上的節(jié)點(diǎn)對應(yīng)一個TensorFlow計(jì)算節(jié)點(diǎn)時,TensorBoard也會展示類似的信息阁危。下圖展示了一個TensorFlow計(jì)算節(jié)點(diǎn)所對應(yīng)的信息卡片:
3.監(jiān)控指標(biāo)可視化
在上一節(jié)中著重介紹了通過TensorBoard的GRAPHS欄可視化TensorFlow計(jì)算圖的結(jié)構(gòu)以及在計(jì)算圖上的信息玛痊。TensorBoard除了可以可視化TensorFlow的計(jì)算圖,還可以可視化TensorFlow程序運(yùn)行過程中各種有助于了解程序運(yùn)行狀態(tài)的監(jiān)控指標(biāo)狂打。在本節(jié)中將介紹如何利用TensorBoard中其他欄目可視化這些監(jiān)控指標(biāo)擂煞。除了Graphs欄,TensorBoard界面中還提供了EVENTS/IMAGES/AUDIO和HISTOGRAMS四個欄目來可視化其他的監(jiān)控指標(biāo)菱父。下面的程序展示了如何將TensorFlow程序運(yùn)行時的信息輸出到TensorBoard日志文件中:
1.定義變量
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
2.生成變量監(jiān)控信息并定義生成監(jiān)控信息日志的操作颈娜。
SUMMARY_DIR = "C:/log/supervisor.log"
BATCH_SIZE = 100
TRAIN_STEPS = 3000
# var:需要記錄的張量
# name:可視化結(jié)果中顯示的圖表名稱,這個名稱一般與變量名一致
def variable_summaries(var, name):
# 將生成監(jiān)控信息的操作放到同一個命名空間下
with tf.name_scope('summaries'):
# tf.summary.histogram:記錄張量中元素的取值分布
tf.summary.histogram(name, var)
# 計(jì)算變量的平均值
mean = tf.reduce_mean(var)
tf.summary.scalar('mean/' + name, mean)
# 計(jì)算變量的標(biāo)準(zhǔn)差
stddev = tf.sqrt(tf.reduce_mean(tf.square(var - mean)))
tf.summary.scalar('stddev/' + name, stddev)
3.生成一層全鏈接的神經(jīng)網(wǎng)絡(luò)
def nn_layer(input_tensor, input_dim, output_dim, layer_name, act=tf.nn.relu):
# 將同一層神經(jīng)網(wǎng)絡(luò)放在一個統(tǒng)一的命名空間下
with tf.name_scope(layer_name):
# 聲明神經(jīng)網(wǎng)絡(luò)邊上的權(quán)重浙宜,并調(diào)用生成權(quán)重監(jiān)控信息日志的函數(shù)
with tf.name_scope('weights'):
weights = tf.Variable(tf.truncated_normal([input_dim, output_dim], stddev=0.1))
variable_summaries(weights, layer_name + '/weights')
# 聲明神經(jīng)網(wǎng)絡(luò)的偏置項(xiàng)官辽,并調(diào)用生成偏置項(xiàng)監(jiān)控信息日志的函數(shù)
with tf.name_scope('biases'):
biases = tf.Variable(tf.constant(0.0, shape=[output_dim]))
variable_summaries(biases, layer_name + '/biases')
with tf.name_scope('Wx_plus_b'):
preactivate = tf.matmul(input_tensor, weights) + biases
# 記錄神經(jīng)網(wǎng)絡(luò)輸出節(jié)點(diǎn)在經(jīng)過激活函數(shù)之前的分布
tf.summary.histogram(layer_name + '/pre_activations', preactivate)
activations = act(preactivate, name='activation')
# 記錄神經(jīng)網(wǎng)絡(luò)節(jié)點(diǎn)輸出在經(jīng)過激活函數(shù)之后的分布。
tf.summary.histogram(layer_name + '/activations', activations)
return activations
def main():
mnist = input_data.read_data_sets("../../datasets/MNIST_data", one_hot=True)
# 定義輸入
with tf.name_scope('input'):
x = tf.placeholder(tf.float32, [None, 784], name='x-input')
y_ = tf.placeholder(tf.float32, [None, 10], name='y-input')
# 將輸入向量還原成圖片的像素矩陣粟瞬,并通過tf.summary.image將當(dāng)前的圖片信息寫入日志的操作
with tf.name_scope('input_reshape'):
image_shaped_input = tf.reshape(x, [-1, 28, 28, 1])
tf.summary.image('input', image_shaped_input, 10)
hidden1 = nn_layer(x, 784, 500, 'layer1')
y = nn_layer(hidden1, 500, 10, 'layer2', act=tf.identity)
# 計(jì)算交叉熵并定義生成交叉熵監(jiān)控日志的操作
with tf.name_scope('cross_entropy'):
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=y, labels=y_))
tf.summary.scalar('cross_entropy', cross_entropy)
with tf.name_scope('train'):
train_step = tf.train.AdamOptimizer(0.001).minimize(cross_entropy)
# 計(jì)算模型在當(dāng)前給定數(shù)據(jù)上的正確率
with tf.name_scope('accuracy'):
with tf.name_scope('correct_prediction'):
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
with tf.name_scope('accuracy'):
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
tf.summary.scalar('accuracy', accuracy)
# tf.summary.merge_all:整理所有的日志生成操作
merged = tf.summary.merge_all()
with tf.Session() as sess:
# 初始化寫日志的writer同仆,并將當(dāng)前TensorFlow計(jì)算圖寫入日志
summary_writer = tf.summary.FileWriter(SUMMARY_DIR, sess.graph)
tf.global_variables_initializer().run()
for i in range(TRAIN_STEPS):
xs, ys = mnist.train.next_batch(BATCH_SIZE)
# 運(yùn)行訓(xùn)練步驟以及所有的日志生成操作,得到這次運(yùn)行的日志裙品。
summary, _ = sess.run([merged, train_step], feed_dict={x: xs, y_: ys})
# 將得到的所有日志寫入日志文件俗批,這樣TensorBoard程序就可以拿到這次運(yùn)行所對應(yīng)的
# 運(yùn)行信息。
summary_writer.add_summary(summary, i)
summary_writer.close()
if __name__ == '__main__':
main()
運(yùn)行代碼市怎,如下圖所示:
運(yùn)行完之后岁忘,在C:/log/supervisor.log目錄,會發(fā)現(xiàn)有個文件events.out.tfevents.1567588311.1002330QL区匠,在命令行執(zhí)行命令:
tensorboard --logdir=C:/log/supervisor.log
再到瀏覽器訪問:http://localhost:6006
可以得到如下圖所示的結(jié)果:
從程序中可以看出干像,除了GRSPHS之外,TensorBoard中的每一欄對應(yīng)了TensorFlow中一種日志生成函數(shù)驰弄,下表總結(jié)了這個對應(yīng)關(guān)系: