學習TensorFlow,TensorBoard可視化網絡結構和參數

在學習深度網絡框架的過程當中,咱們發現一個問題,就是如何輸出各層網絡參數,用於更好地理解,調試和優化網絡?針對這個問題,TensorFlow開發了一個特別有用的可視化工具包:TensorBoard,既能夠顯示網絡結構,又能夠顯示訓練和測試過程當中各層參數的變化狀況。本博文分爲四個部分,第一部分介紹相關函數,第二部分是代碼測試,第三部分是運行結果,第四部分介紹相關參考資料。
html

一. 相關函數

TensorBoard的輸入是tensorflow保存summary data的日誌文件。日誌文件名的形式如:events.out.tfevents.1467809796.lei-All-Series 或 events.out.tfevents.1467809800.lei-All-Series。TensorBoard可讀的summary data有scalar,images,audio,histogram和graph。那麼怎麼把這些summary data保存在日誌文件中呢?node

數值如學習率,損失函數用scalar_summary函數。tf.scalar_summary(節點名稱,獲取的數據)python


accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))  
tf.scalar_summary('accuracy', accuracy)

各層網絡權重,偏置的分佈,用histogram_summary函數react

preactivate = tf.matmul(input_tensor, weights) + biases  
tf.histogram_summary(layer_name + '/pre_activations', preactivate)

其餘幾種summary data也是一樣的方式獲取,只是對應的獲取函數名稱換一下。這些獲取summary data函數節點和graph是獨立的,調用的時候也須要運行session。當須要獲取的數據較多的時候,咱們一個一個去保存獲取到的數據,以及一個一個去運行會顯得比較麻煩。tensorflow提供了一個簡單的方法,就是合併全部的summary data的獲取函數,保存和運行只對一個對象進行操做。好比,寫入默認路徑中,好比/tmp/mnist_logs (by default)linux

merged = tf.merge_all_summaries()  

train_writer = tf.train.SummaryWriter(FLAGS.summaries_dir + '/train', sess.graph)  

test_writer = tf.train.SummaryWriter(FLAGS.summaries_dir + '/test')

SummaryWriter從tensorflow獲取summary data,而後保存到指定路徑的日誌文件中。以上是在創建graph的過程當中,接下來執行,每隔必定step,寫入網絡參數到默認路徑中,造成最開始的文件:events.out.tfevents.1467809796.lei-All-Series 或 events.out.tfevents.1467809800.lei-All-Series。git

for i in range(FLAGS.max_steps):  
if i % 10 == 0:  # Record summaries and test-set accuracy
summary, acc = sess.run([merged, accuracy], feed_dict=feed_dict(False))  
      test_writer.add_summary(summary, i)  
 print('Accuracy at step %s: %s' % (i, acc))  
 else: # Record train set summarieis, and train
      summary, _ = sess.run([merged, train_step], feed_dict=feed_dict(True))  
      train_writer.add_summary(summary, i)



tensorflow 可視化

tensorflow的可視化是使用summarytensorboard合做完成的.github

基本用法

首先明確一點,summary也是op.算法

輸出網絡結構api

 
 
 
 
  • 1
  • 2
  • 1
  • 2
with tf.Session() as sess: writer = tf.summary.FileWriter(your_dir, sess.graph)

命令行運行tensorboard --logdir=your_dir,而後瀏覽器輸入127.0.1.1:6006
這樣你就能夠在tensorboard中看到你的網絡結構圖了瀏覽器

可視化參數

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
#ops loss = ... tf.summary.scalar("loss", loss) merged_summary = tf.summary.merge_all() init = tf.global_variable_initializer() with tf.Session() as sess: writer = tf.summary.FileWriter(your_dir, sess.graph) sess.run(init) for i in xrange(100): _,summary = sess.run([train_op,merged_summary], feed_dict) writer.add_summary(summary, i)

這時,打開tensorboard,在EVENTS能夠看到loss隨着i的變化了,若是看不到的話,能夠在代碼最後加上writer.flush()試一下,緣由後面說明。

函數介紹

  • tf.summary.merge_all: 將以前定義的全部summary op整合到一塊兒

  • FileWriter: 建立一個file writer用來向硬盤寫summary數據,

  • tf.summary.scalar(summary_tags, Tensor/variable): 用於標量的 summary

  • tf.summary.image(tag, tensor, max_images=3, collections=None, name=None):tensor,必須4維,形狀[batch_size, height, width, channels],max_images(最多隻能生成3張圖片的summary),覺着這個用在卷積中的kernel可視化很好用.max_images肯定了生成的圖片是[-max_images: ,height, width, channels],還有一點就是,TensorBord中看到的image summary永遠是最後一個global step

  • tf.summary.histogram(tag, values, collections=None, name=None):values,任意形狀的tensor,生成直方圖summary

  • tf.summary.audio(tag, tensor, sample_rate, max_outputs=3, collections=None, name=None)

FileWriter

注意:add_summary僅僅是向FileWriter對象的緩存中存放event data。而向disk上寫數據是由FileWrite對象控制的。下面經過FileWriter的構造函數來介紹這一點!!!

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5
tf.summary.FileWriter.__init__(logdir, graph=None, max_queue=10, flush_secs=120, graph_def=None) Creates a FileWriter and an event file. # max_queue: 在向disk寫數據以前,最大可以緩存event的個數 # flush_secs: 每多少秒像disk中寫數據,並清空對象緩存

注意

  1. 若是使用writer.add_summary(summary,global_step)時沒有傳global_step參數,會使scarlar_summary變成一條直線。

  2. 只要是在計算圖上的Summary op,都會被merge_all捕捉到, 不須要考慮變量生存空間問題!

  3. 若是執行一次,disk上沒有保存Summary數據的話,能夠嘗試下file_writer.flush()

小技巧

若是想要生成的summary有層次的話,記得在summary外面加一個name_scope

 
 
 
 
  • 1
  • 2
  • 1
  • 2
with tf.name_scope("summary_gradients"): tf.summary.histgram("name", gradients)

這樣,tensorboard在顯示的時候,就會有一個sumary_gradients一集目錄。


主旨

在TensorFlow中每開發一個模型,均可以使用可視化調試工具TensorBoard獲得這個session的Graph,這張圖的結構和內容都不一樣於機器學習教材上介紹的典型神經網絡結構圖。本文試圖經過代碼實驗理解Graph的含義,用以指導平常調試。

代碼和運行環境

代碼:https://github.com/wangyaobupt/TF_Graph singleNerualNode.py

運行環境:
Python 2.7.12 (default, Nov 19 2016, 06:48:10)
[GCC 5.4.0 20160609] on linux2
>>> tf.__version__
‘1.0.0-rc2’

問題介紹

在TensorFlow開發中,TensorBoard是一項頗有用的可視化調試工具。在TensorBoard中,除了開發者自定義輸出的數據結構以外,還包括表徵神經網絡模型的GRAPH。

在常見的機器學習教材中,神經網絡的結構通常經過相似於以下的圖形表示,下圖引用自http://www.extremetech.com/extreme/215170-artificial-neural-networks-are-changing-the-world-what-are-they
這裏寫圖片描述

可是,TensorBoard生成的Graph與上述形態徹底不一樣,我本身開發的某個神經網絡生成的Graph以下圖所示
這裏寫圖片描述

如何理解這張圖?本文試圖經過代碼實驗作一些嘗試。

案例1:單神經元

爲了簡化分析場景,咱們設計一個由單個神經元構成的神經網絡,這個神經元存在numberOfInputDims輸入,神經元的每條輸入邊都有權重因子wi,此外神經元還有bias偏置項,激活函數爲sigmoid. 這個結構能夠用以下的結構圖描述
這裏寫圖片描述

在TensorFlow中,以下代碼便可定義出知足上述結構的神經網絡,其中a就是上圖中的Y

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
inputTensor = tf.placeholder(tf.float32, [None, numberOfInputDims], name='inputTensor') labelTensor=tf.placeholder(tf.float32, [None, 1], name='LabelTensor') W = tf.Variable(tf.random_uniform([numberOfInputDims, 1], -1.0, 1.0), name='weights') b = tf.Variable(tf.zeros([1]), name='biases') a = tf.nn.sigmoid(tf.matmul(inputTensor, W) + b, name='activation')

使用TensorBoard生成的Graph以下圖所示
Fig 0

問題1:Graph中的邊(Edge)表明什麼

根據TF官方文檔對於Graph的說明,上圖中實線表示數據依賴(tensor在運算符之間的流動關係),而虛線表示控制依賴關係,原文引用以下

TensorFlow graphs have two kinds of connections: data dependencies and control dependencies. Data dependencies show the flow of tensors between two ops and are shown as solid arrows, while control dependencies use dotted lines.

可是,咱們獲得的Graph中全部的實線都沒有標出箭頭方向,他們之間是誰依賴誰呢?

回答這個問題須要回到代碼中,從代碼能夠知道:weight是由tf.random_uniform([numberOfInputDims, 1]初始化獲得的;weight和inputTensor作矩陣乘法獲得中間變量;中間變量再加上bias獲得激活函數的輸入;以此類推。

所以,TensorBoard Graph的上下方位表明了數據依賴的方向:數據老是從下方的節點流向上方的節點,上方節點依賴於下方節點

接下來討論控制依賴。

上圖中weight和bias節點都存在依賴於init運算的虛線,這說明weight和bias節點都須要初始化。虛線指向的運算符(op)是被依賴的運算符(op)

問題2:Graph中的節點表明什麼

官方文檔對於Graph的說明給出了以下圖例表格
這裏寫圖片描述

從中能夠看出,不考慮summary node的狀況下,節點要麼是常數Constant,要麼是運算符Operation Node,要麼是前二者的組合。

回到咱們這個具體問題,random_uniform/weight/bias就是組合節點,而其餘節點就是運算符。注意:從上圖能夠看到,inputTensor也是被視做一個獨立的運算符。

放大其中一個節點weights,咱們能夠看到其內部結構以下,包含:賦值(assign)運算符、(weight)運算符、讀取(read)運算符

這裏寫圖片描述

結合上述實驗和分析,能夠初步判斷運算符Operation Node包含如下狀況

  • 具體的運算操做:例如矩陣乘法(上圖中的matmul),賦值(assign),讀取(read)
  • 某個Tensor自己:例如上圖中的inputTensor和(weights)節點

關於Operation Node表明常量的狀況,在本文的案例2中會有體現

案例2:單神經元+損失函數+偏差反向傳播梯度降低調整參數

案例1中的神經網絡只包含了前向計算邏輯,做爲神經網絡,其最主要的功能在於被訓練知足某一目標,所以損失函數和偏差反向傳播梯度降低調整參數是必不可少的。

在此前的代碼基礎上新增以下兩行就能夠實現上述功能。這裏損失函數使用預測值與目標值的L2距離

 
 
 
 
  • 1
  • 2
loss = tf.nn.l2_loss(a - labels, name='L2Loss') train_step = tf.train.AdamOptimizer(1e-4).minimize(loss)

加入這兩個功能後,新生成的Graph以下所示
這裏寫圖片描述

對比上圖和案例1中的Graph,在案例2 Graph的Main View(圖像左側)新增了以下信息

  • 節點
    • 常量y:表明label
    • sub運算符
    • L2Loss Tensor
    • gradient命名空間,這個命名空間與除了bias/add/L2Loss以外全部節點都有數據依賴
    • Weights和Bias都新增了對Adam節點的控制依賴

接下來重點討論Gradient(梯度)和Adam(尋優算法)節點的內部結構

Gradient(梯度)節點
這裏寫圖片描述
Gradient(梯度)節點內部是一個鏈式結構,分爲對L2Loss求導、對減法求導、對激活函數求導……
總結起來,這就是數學上求導數的鏈式法則的圖形化表示,咱們的最終目的是求出損失函數對某個參數的導數,那麼根據鏈式法則,只要從損失函數L2Loss出發,將每一層求導結果相乘就能夠獲得,筆者前一個系列利用Python實現神經網絡中也使用了相似的求導方法

Adam(尋優算法)節點
這裏寫圖片描述
Adam算法自己的原理和實現能夠參閱筆者此前的文章:Python實現神經網絡Part 2: 訓練單個神經元找到最優解
上述圖形就是根據Adam算法原理,使用梯度、beta1\beta2,結合每一輪訓練中weights和bias的原始值,計算weight和bias的更新值,經過控制依賴關係進一步調節weights和bias

總結

TensorBoard中的Graph不一樣於通常的神經網絡結構圖,它是一種計算圖。圖中每一個節點要麼是Tensor自己,要麼是運算符,每一條邊要麼表明Tensor的流動,要麼表明控制關係。這張圖完備的表達了經過代碼定義的神經網絡中全部計算步驟,能夠據此說明前向計算、偏差反相傳播、梯度降低調整參數等過程。

在實際工做中,理解了上述含義,就能夠將Graph利用起來,在Debug過程當中可視化的發現網絡計算流程中的問題。複雜程序的調試老是困難的,引入可視化工具對於調試效率會很是有幫助。


image

image

image

image

image

image








參考文獻:

http://www.360doc.com/content/17/0414/13/10408243_645540404.shtml

http://blog.csdn.net/helei001/article/details/51842531

http://www.studyai.com/course/play/bb9611e4ca824f9ebbc756fb240f1da8

相關文章
相關標籤/搜索