[乾貨|實踐] TensorBoard可視化

/* 版權聲明:可以任意轉載,轉載時請務必標明文章原始出處和作者信息 .*/

faiculty: 機器學習、計算機視覺交流群:451429116nn微信公眾號、知乎專欄、簡書,請搜索: faicultynn本專欄主要為學習記錄,可能綜合了網上各類文章或博客,如有侵權請聯繫本人進行刪除。 nQQ:769412850.nn非常期待各位同學投稿,一起學習。n

TensorBoard

  1. 簡介:TensorBoard是tensorflow官方推出的可視化工具,它可以將模型訓練過程中的各種匯總數據展示出來,包括標量(Scalars)、圖片(Images)、音頻(Audio)、計算圖(Graphs)、數據分布(Distributions)、直方圖(Histograms)和潛入向量(Embeddigngs)。
  2. 作用:tensorflow代碼執行過程是先構建圖,然後在執行,所以對中間過程的調試不太方便;除此之外,在使用tensorflow訓練大型深度學習神經網路時,中間的計算過程可能非常複雜,因此為了理解、調試和優化網路,可以使用TensorBoard觀察訓練過程中的各種可視化數據。

TensorBoard可視化過程

在將可視化過程之前,為了方便理解,對之中設計的一些概念做一個簡要介紹。

什麼是Graph和Session

graph定義了computation,它不計算任何東西,不包含任何值,只是定義了你在代碼中指定的操作。關於graph的官方文檔地址:tf.Graph。若不建立graph,TensorFlow在載入庫的時候會地創建圖,並且將這個圖指定為默認圖。可以通過使用tf.get_default_graph()函數獲得默認圖的句柄。在大多數的TensorFlow程序中,都只是用默認圖(graph)來處理。不過,當你定義的多個模型沒有相互內在的依賴的情況下,創建多個圖的時候很有用。下面,我們一個變數和三個操作定義一個圖形:variable返回變數的當前值。 initialize將42的初始值賦給那個變數。 assign給該變數賦值13的新值。

#Defining the Graphngraph = tf.Graph()nwith graph.as_default():n variable = tf.Variable(42, name=foo)n initialize = tf.global_variables_initializer()n assign = variable.assign(13)n

Session會話允許執行graph或graph的一部分。它為此分配資源(在一台或多台機器上)並保存中間結果和變數的實際值。要運行上面三個定義的操作中的任何一個時,我們需要為該graph創建一個會話Session。 因此會話Session需要分配內存來存儲變數的當前值。

#Running Computations in a Sessionnwith tf.Session(graph=graph) as sess:n sess.run(initialize)n sess.run(assign)n print(sess.run(variable))n# Output: 13n

可視化過程

  1. 先建立一個graph
  2. 確定要在graph中的哪些節點放置summary operations以記錄信息

    使用tf.summary.scalar記錄標量

    使用tf.summary.histogram記錄數據的直方圖

    使用tf.summary.distribution記錄數據的分布圖

    使用tf.summary.image記錄圖像數據

    ......
  3. operations並不會去真的執行計算,除非你告訴他們需要去run,或者它被其他的需要run的operation所依賴。而我們上一步創建的這些summary operations其實並不被其他節點依賴,因此,我們需要特地去運行所有的summary節點。但是呢,一份程序下來可能有超多這樣的summary 節點,要手動一個一個去啟動自然是及其繁瑣的,因此我們可以使用tf.summary.merge_all去將所有summary節點合併成一個節點,只要運行這個節點,就能產生所有我們之前設置的summary data。
  4. 使用tf.summary.FileWriter將運行後輸出的數據都保存到本地磁碟中
  5. 運行整個程序,並在命令行輸入運行tensorboard的指令,之後打開web端可查看可視化的結果

Tensorboard使用案例

使用最基礎的識別手寫字體的案例,建立一個簡單的神經網路,讓大家了解如何使用Tensorboard。可以從github獲得源碼。

導入包,定義超參數,載入數據

  1. 首先還是導入需要的包:

from __future__ import absolute_importnfrom __future__ import divisionnfrom __future__ import print_functionnnimport argparsenimport sysnimport tensorflow as tfnfrom tensorflow.examples.tutorials.mnist import input_datan

  1. 定義固定的超參數,方便待使用時直接傳入。如果你問,這個超參數為啥要這樣設定,如何選擇最優的超參數?這個問題此處先不討論,超參數的選擇在機器學習建模中最常用的方法就是「交叉驗證法」。而現在假設我們已經獲得了最優的超參數,設置學利率為0.001,dropout的保留節點比例為0.9,最大循環次數為1000.

    另外,還要設置兩個路徑,第一個是數據下載下來存放的地方,一個是summary輸出保存的地方。

max_step = 1000 # 最大迭代次數nlearning_rate = 0.001 # 學習率ndropout = 0.9 # dropout時隨機保留神經元的比例nndata_dir = os.path.join(data, mnist)# 樣本數據存儲的路徑nif not os.path.exists(log):n os.mkdir(log)nlog_dir = log # 輸出日誌保存的路徑n

3.接著載入數據,下載數據是直接調用了tensorflow提供的函數read_data_sets,輸入兩個參數,第一個是下載到數據存儲的路徑,第二個one_hot表示是否要將類別標籤進行獨熱編碼。它首先回去找制定目錄下有沒有這個數據文件,沒有的話才去下載,有的話就直接讀取。所以第一次執行這個命令,速度會比較慢。

mnist = input_data.read_data_sets(data_dir,one_hot=True)n

創建特徵與標籤的佔位符,保存輸入的圖片數據到summary

  1. 創建tensorflow的默認會話:

sess = tf.InteractiveSession()n

  1. 創建輸入數據的佔位符,分別創建特徵數據x,標籤數據y_ 在tf.placeholder()函數中傳入了3個參數,第一個是定義數據類型為float32;第二個是數據的大小,特徵數據是大小784的向量,標籤數據是大小為10的向量,None表示不定死大小,到時候可以傳入任何數量的樣本;第3個參數是這個佔位符的名稱。

with tf.name_scope(input):n x = tf.placeholder(tf.float32, [None, 784], name=x-input)n y_ = tf.placeholder(tf.float32, [None, 10], name=y-input)n

  1. 使用tf.summary.image保存圖像信息 特徵數據其實就是圖像的像素數據拉升成一個1*784的向量,現在如果想在tensorboard上還原出輸入的特徵數據對應的圖片,就需要將拉升的向量轉變成28 * 28 * 1的原始像素了,於是可以用tf.reshape()直接重新調整特徵數據的維度: 將輸入的數據轉換成[28 * 28 * 1]的shape,存儲成另一個tensor,命名為image_shaped_input。 為了能使圖片在tensorbord上展示出來,使用tf.summary.image將圖片數據匯總給tensorbord。 tf.summary.image()中傳入的第一個參數是命名,第二個是圖片數據,第三個是最多展示的張數,此處為10張

with tf.name_scope(input_reshape):n image_shaped_input = tf.reshape(x, [-1, 28, 28, 1])n tf.summary.image(input, image_shaped_input, 10)n

創建初始化參數的方法,與參數信息匯總到summary的方法

  1. 在構建神經網路模型中,每一層中都需要去初始化參數w,b,為了使代碼簡介美觀,最好將初始化參數的過程封裝成方法function。 創建初始化權重w的方法,生成大小等於傳入的shape參數,標準差為0.1,正態分布的隨機數,並且將它轉換成tensorflow中的variable返回。

def weight_variable(shape):n """Create a weight variable with appropriate initialization."""n initial = tf.truncated_normal(shape, stddev=0.1)n return tf.Variable(initial)n

創建初始換偏執項b的方法,生成大小為傳入參數shape的常數0.1,並將其轉換成tensorflow的variable並返回

def bias_variable(shape):n """Create a bias variable with appropriate initialization."""n initial = tf.constant(0.1, shape=shape)n return tf.Variable(initial)n

  1. 我們知道,在訓練的過程在參數是不斷地在改變和優化的,我們往往想知道每次迭代後參數都做了哪些變化,可以將參數的信息展現在tenorbord上,因此我們專門寫一個方法來收錄每次的參數信息。

def variable_summaries(var):n """Attach a lot of summaries to a Tensor (for TensorBoard visualization)."""n with tf.name_scope(summaries):n # 計算參數的均值,並使用tf.summary.scaler記錄n mean = tf.reduce_mean(var)n tf.summary.scalar(mean, mean)nn # 計算參數的標準差n with tf.name_scope(stddev):n stddev = tf.sqrt(tf.reduce_mean(tf.square(var - mean)))n # 使用tf.summary.scaler記錄記錄下標準差,最大值,最小值n tf.summary.scalar(stddev, stddev)n tf.summary.scalar(max, tf.reduce_max(var))n tf.summary.scalar(min, tf.reduce_min(var))n # 用直方圖記錄參數的分布n tf.summary.histogram(histogram, var)n

構建神經網路層

  1. 創建第一層隱藏層

    創建一個構建隱藏層的方法,輸入的參數有:

    input_tensor:特徵數據

    input_dim:輸入數據的維度大小

    output_dim:輸出數據的維度大小(=隱層神經元個數)

    layer_name:命名空間

    act=tf.nn.relu:激活函數(默認是relu)

def nn_layer(input_tensor, input_dim, output_dim, layer_name, act=tf.nn.relu):n """Reusable code for making a simple neural net layer.n It does a matrix multiply, bias add, and then uses relu to nonlinearize.n It also sets up name scoping so that the resultant graph is easy to read,n and adds a number of summary ops.n """n # 設置命名空間n with tf.name_scope(layer_name):n # 調用之前的方法初始化權重w,並且調用參數信息的記錄方法,記錄w的信息n with tf.name_scope(weights):n weights = weight_variable([input_dim, output_dim])n variable_summaries(weights)n # 調用之前的方法初始化權重b,並且調用參數信息的記錄方法,記錄b的信息n with tf.name_scope(biases):n biases = bias_variable([output_dim])n variable_summaries(biases)n # 執行wx+b的線性計算,並且用直方圖記錄下來n with tf.name_scope(linear_compute):n preactivate = tf.matmul(input_tensor, weights) + biasesn tf.summary.histogram(linear, preactivate)n # 將線性輸出經過激勵函數,並將輸出也用直方圖記錄下來n activations = act(preactivate, name=activation)n tf.summary.histogram(activations, activations)nn # 返回激勵層的最終輸出n return activationsn

調用隱層創建函數創建一個隱藏層:輸入的維度是特徵的維度784,神經元個數是500,也就是輸出的維度。

hidden1 = nn_layer(x, 784, 500, layer1)n

  1. 創建一個dropout層,,隨機關閉掉hidden1的一些神經元,並記錄keep_prob

with tf.name_scope(dropout):n keep_prob = tf.placeholder(tf.float32)n tf.summary.scalar(dropout_keep_probability, keep_prob)n dropped = tf.nn.dropout(hidden1, keep_prob)n

  1. 創建一個輸出層,輸入的維度是上一層的輸出:500,輸出的維度是分類的類別種類:10,激活函數設置為全等映射identity.(暫且先別使用softmax,會放在之後的損失函數中一起計算)

y = nn_layer(dropped, 500, 10, layer2, act=tf.identity)n

創建損失函數

使用tf.nn.softmax_cross_entropy_with_logits來計算softmax並計算交叉熵損失,並且求均值作為最終的損失值。

with tf.name_scope(loss):n # 計算交叉熵損失(每個樣本都會有一個損失)n diff = tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y)n with tf.name_scope(total):n # 計算所有樣本交叉熵損失的均值n cross_entropy = tf.reduce_mean(diff)nntf.summary.scalar(loss, cross_entropy)n

訓練,並計算準確率

  1. 使用AdamOptimizer優化器訓練模型,最小化交叉熵損失

with tf.name_scope(train):n train_step = tf.train.AdamOptimizer(learning_rate).minimize(n cross_entropy)n

  1. 計算準確率,並用tf.summary.scalar記錄準確率

with tf.name_scope(accuracy):n with tf.name_scope(correct_prediction):n # 分別將預測和真實的標籤中取出最大值的索引,弱相同則返回1(true),不同則返回0(false)n correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))n with tf.name_scope(accuracy):n # 求均值即為準確率n accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))ntf.summary.scalar(accuracy, accuracy)n

合併summary operation, 運行初始化變數

將所有的summaries合併,並且將它們寫到之前定義的log_dir路徑

# summaries合併nmerged = tf.summary.merge_all()n# 寫到指定的磁碟路徑中ntrain_writer = tf.summary.FileWriter(log_dir + /train, sess.graph)ntest_writer = tf.summary.FileWriter(log_dir + /test)nn# 運行初始化所有變數ntf.global_variables_initializer().run()n

準備訓練與測試的兩個數據,循環執行整個graph進行訓練與評估

  1. 現在我們要獲取之後要喂入的數據. 如果是train==true,就從mnist.train中獲取一個batch樣本,並且設置dropout值; 如果是不是train=false,則獲取minist.test的測試數據,並且設置keep_prob為1,即保留所有神經元開啟。

def feed_dict(train):n """Make a TensorFlow feed_dict: maps data onto Tensor placeholders."""n if train:n xs, ys = mnist.train.next_batch(100)n k = dropoutn else:n xs, ys = mnist.test.images, mnist.test.labelsn k = 1.0n return {x: xs, y_: ys, keep_prob: k}n

  1. 開始訓練模型。 每隔10步,就進行一次merge, 並列印一次測試數據集的準確率,然後將測試數據集的各種summary信息寫進日誌中。 每隔100步,記錄原信息 其他每一步時都記錄下訓練集的summary信息並寫到日誌中。

for i in range(max_steps):n if i % 10 == 0: # 記錄測試集的summary與accuracyn summary, acc = sess.run([merged, accuracy], feed_dict=feed_dict(False))n test_writer.add_summary(summary, i)n print(Accuracy at step %s: %s % (i, acc))n else: # 記錄訓練集的summaryn if i % 100 == 99: # Record execution statsn run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)n run_metadata = tf.RunMetadata()n summary, _ = sess.run([merged, train_step],n feed_dict=feed_dict(True),n options=run_options,n run_metadata=run_metadata)n train_writer.add_run_metadata(run_metadata, step%03d % i)n train_writer.add_summary(summary, i)n print(Adding run metadata for, i)n else: # Record a summaryn summary, _ = sess.run([merged, train_step], feed_dict=feed_dict(True))n train_writer.add_summary(summary, i)ntrain_writer.close()ntest_writer.close()n

執行程序,tensorboard生成可視化

  1. 運行整個程序,在程序中定義的summary node就會將要記錄的信息全部保存在指定的logdir路徑中了,訓練的記錄會存一份文件,測試的記錄會存一份文件。
  2. 進入linux命令行,運行以下代碼,等號後面加上summary日誌保存的路徑(在程序第一步中就事先自定義了)

tensorboard --logdir=logn

執行命令之後會出現一條信息,上面有網址,將網址在瀏覽器中打開就可以看到我們定義的可視化信息了。可視化後的展示詳情見GitHub中README部分。

參考資料

[1] github代碼鏈接


推薦閱讀:

YJango的前饋神經網路--代碼LV1
TensorFlow與中文手寫漢字識別
深入淺出Tensorflow(四):卷積神經網路

TAG:深度学习DeepLearning | TensorFlow | 数据可视化 |