TensorBoard可視化


TensorBoard可視化

0. 寫在前面

參考書

《TensorFlow:實戰Google深度學習框架》(第2版)

工具

python3.5.1,pycharm

1. TensorBoard簡介

一個簡單的TensorFlow程序,在這個程序中完成了TensorBoard日志輸出的功能。

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8 

"""
@author: Li Tian
@contact: 694317828@qq.com
@software: pycharm
@file: tensorboard_test1.py
@time: 2019/5/10 9:27
@desc: TensorBoard簡介。一個簡單的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,並將當前的TensorFlow計算圖寫入日志。TensorFlow提供了
# 多種寫日志文件的API,在后面詳細介紹。
writer = tf.summary.FileWriter('./log/', tf.get_default_graph())
writer.close()

運行之后輸入:tensorboard --logdir=./log查看TensorBoard。

img

然后在瀏覽器中輸入下面的網址。

img

2. TensorFlow計算圖可視化

2.1 命名空間與TensorBoard圖上節點

tf.variable_scope與tf.name_scope函數的區別

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8 

"""
@author: Li Tian
@contact: 694317828@qq.com
@software: pycharm
@file: tensorboard_test2.py
@time: 2019/5/10 10:26
@desc: tf.variable_scope與tf.name_scope函數的區別
"""

import tensorflow as tf


with tf.variable_scope("foo"):
    # 在命名空間foo下獲取變量"bar",於是得到的變量名稱為“foo/bar”。
    a = tf.get_variable("bar", [1])
    # 輸出:foo/bar: 0
    print(a.name)

with tf.variable_scope("bar"):
    # 在命名空間bar下獲取變量“bar”,於是得到的變量名稱為“bar/bar”。此時變量
    # “bar/bar”和變量“foo/bar”並不沖突,於是可以正常運行。
    b = tf.get_variable("bar", [1])
    # 輸出:bar/bar:0

with tf.name_scope("a"):
    # 使用tf.Variable函數生成變量會受到tf.name_scope影響,於是這個變量的名稱為“a/Variable”。
    a = tf.Variable([1])
    # 輸出:a/Variable:0
    print(a.name)

    # tf.get_variable函數不受tf.name_scope函數的影響。
    # 於是變量並不在a這個命名空間中。
    a = tf.get_variable("b", [1])
    # 輸出:b:0
    print(a.name)

with tf.name_scope("b"):
    # 因為tf.get_variable不受tf.name_scope影響,所以這里試圖獲取名稱為
    # “a”的變量。然而這個變量已經被聲明了,於是這里會報重復聲明的錯誤
    tf.get_variable("b", [1])

對不起,這一段代碼,我知道作者想要表達什么意思。。。但我實在是覺得不知所雲。

img

改進向量相加的樣例代碼

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8 

"""
@author: Li Tian
@contact: 694317828@qq.com
@software: pycharm
@file: tensorboard_test3.py
@time: 2019/5/10 10:41
@desc: 改進向量相加的樣例代碼
"""

import tensorflow as tf


# 將輸入定義放入各自的命名空間中,從而使得TensorBoard可以根據命名空間來整理可視化效果圖上的節點。
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("./log", tf.get_default_graph())
writer.close()

得到改進后的圖:

img\

展開input2節點的可視化效果圖:

img

可視化一個真實的神經網絡結構圖

我是真的佛了。。。這里原諒我真的又要噴。。。首先是用之前的mnist_inference文件就已經炸了,然后下面還有一句跟前面一樣的方式訓練神經網絡。。。我特么。。。。你你聽,這說的是人話嗎?我已經無力吐槽了。。。這本書用來作為我的TensorFlow啟蒙書,真的是后悔死了。。。

下面的代碼,依然是我自己憑借自己的理解,改后的,這本書是真的垃圾。

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8 

"""
@author: Li Tian
@contact: 694317828@qq.com
@software: pycharm
@file: tensorboard_test4.py
@time: 2019/5/10 11:06
@desc: 可視化一個真實的神經網絡結構圖。
"""

import tensorflow as tf
import os
from tensorflow.examples.tutorials.mnist import input_data
# mnist_inference中定義的常量和前向傳播的函數不需要改變,因為前向傳播已經通過
# tf.variable_scope實現了計算節點按照網絡結構的划分。
import BookStudy.book2.mnist_inference as mnist_inference


INPUT_NODE = 784
OUTPUT_NODE = 10
LAYER1_NODE = 500

# 配置神經網絡的參數。
BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.8
LEARNING_RATE_DECAY = 0.99
REGULARAZTION_RATE = 0.0001
TRAINING_STEPS = 30000
MOVING_AVERAGE_DECAY = 0.99
# 模型保存的路徑和文件名。
MODEL_SAVE_PATH = './model/'
MODEL_NAME = 'model.ckpt'


def train(mnist):
    # 將處理輸入數據的計算都放在名字為“input”的命名空間下。
    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(REGULARAZTION_RATE)
    y = mnist_inference.inference(x, regularizer)
    global_step = tf.Variable(0, trainable=False)

    # 將處理滑動平均相關的計算都放在名為moving_average的命名空間下。
    with tf.name_scope("moving_average"):
        variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
        variable_averages_op = variable_averages.apply(tf.trainable_variables())

    # 將計算損失函數相關的計算都放在名為loss_function的命名空間下。
    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'))

    # 將定義學習率、優化方法以及每一輪訓練需要訓練的操作都放在名字為“train_step”的命名空間下。
    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, variable_averages_op]):
            train_op = tf.no_op(name='train')

    # 初始化Tensorflow持久化類。
    saver = tf.train.Saver()
    with tf.Session() as sess:
        tf.global_variables_initializer().run()

        # 在訓練過程中不再測試模型在驗證數據上的表現,驗證和測試的過程將會有一個獨立的程序來完成。
        for i in range(TRAINING_STEPS):
            xs, ys = mnist.train.next_batch(BATCH_SIZE)
            _, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: xs, y_: ys})

            # 每1000輪保存一次模型。
            if i % 1000 == 0:
                # 輸出當前的訓練情況。這里只輸出了模型在當前訓練batch上的損失函數大小。通過損失函數的大小可以大概了解到
                # 訓練的情況。在驗證數據集上的正確率信息會有一個獨立的程序來生成。
                print("After %d training step(s), loss on training batch is %g." % (step, loss_value))

                # 保存當前的模型。注意這里給出了global_step參數,這樣可以讓每個被保存的模型的文件名末尾加上訓練的輪數,
                # 比如“model.ckpt-1000” 表示訓練1000輪之后得到的模型。
                saver.save(sess, os.path.join(MODEL_SAVE_PATH, MODEL_NAME), global_step=global_step)

    # 將當前的計算圖輸出到TensorBoard日志文件。
    writer = tf.summary.FileWriter("./log", tf.get_default_graph())
    writer.close()


def main(argv = None):
    mnist = input_data.read_data_sets("D:/Python3Space/BookStudy/book2/MNIST_data", one_hot=True)
    train(mnist)


if __name__ == '__main__':
    tf.app.run()

運行之后:

img

召喚tensorboard:

img

改進后的MNIST樣例程序TensorFlow計算圖可視化效果圖:

img

2.2 節點信息

修改前面的代碼,將不同迭代輪數的每個TensorFlow計算節點的運行時間和消耗的內存寫入TensorBoard的日志文件中。

果然。。。這次又是只給出一部分代碼。。。並且這個train_writer是什么啊,在哪里定義也沒看到,拿來就用,真的是服了。。。(寫的也太爛了,佛了,刷完這本書我就燒了。。。)

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8 

"""
@author: Li Tian
@contact: 694317828@qq.com
@software: pycharm
@file: tensorboard_test5.py
@time: 2019/5/10 21:13
@desc: 修改前面的代碼,將不同迭代輪數的每個TensorFlow計算節點的運行時間和消耗的內存寫入TensorBoard的日志文件中。
"""

import tensorflow as tf
import os
from tensorflow.examples.tutorials.mnist import input_data
# mnist_inference中定義的常量和前向傳播的函數不需要改變,因為前向傳播已經通過
# tf.variable_scope實現了計算節點按照網絡結構的划分。
import BookStudy.book2.mnist_inference as mnist_inference


INPUT_NODE = 784
OUTPUT_NODE = 10
LAYER1_NODE = 500

# 配置神經網絡的參數。
BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.8
LEARNING_RATE_DECAY = 0.99
REGULARAZTION_RATE = 0.0001
TRAINING_STEPS = 30000
MOVING_AVERAGE_DECAY = 0.99
# 模型保存的路徑和文件名。
MODEL_SAVE_PATH = './model/'
MODEL_NAME = 'model.ckpt'


def train(mnist):
    # 將處理輸入數據的計算都放在名字為“input”的命名空間下。
    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(REGULARAZTION_RATE)
    y = mnist_inference.inference(x, regularizer)
    global_step = tf.Variable(0, trainable=False)

    # 將處理滑動平均相關的計算都放在名為moving_average的命名空間下。
    with tf.name_scope("moving_average"):
        variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
        variable_averages_op = variable_averages.apply(tf.trainable_variables())

    # 將計算損失函數相關的計算都放在名為loss_function的命名空間下。
    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'))

    # 將定義學習率、優化方法以及每一輪訓練需要訓練的操作都放在名字為“train_step”的命名空間下。
    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, variable_averages_op]):
            train_op = tf.no_op(name='train')

    # 初始化Tensorflow持久化類。
    saver = tf.train.Saver()
    # 將當前的計算圖輸出到TensorBoard日志文件。
    train_writer = tf.summary.FileWriter("./log", tf.get_default_graph())
    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輪保存一次模型。
            if i % 1000 == 0:

                # 配置運行時需要記錄的信息。
                run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
                # 運行時記錄運行信息的proto。
                run_metadata = tf.RunMetadata()
                # 將配置信息和記錄運行信息的proto傳入運行的過程,從而記錄運行時每一個節點的時間、空間開銷信息。
                _, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: xs, y_: ys}, options=run_options, run_metadata=run_metadata)
                # 將節點在運行時的信息寫入日志文件。
                train_writer.add_run_metadata(run_metadata, 'step%03d' % i)

                # 輸出當前的訓練情況。這里只輸出了模型在當前訓練batch上的損失函數大小。通過損失函數的大小可以大概了解到
                # 訓練的情況。在驗證數據集上的正確率信息會有一個獨立的程序來生成。
                print("After %d training step(s), loss on training batch is %g." % (step, loss_value))

                # 保存當前的模型。注意這里給出了global_step參數,這樣可以讓每個被保存的模型的文件名末尾加上訓練的輪數,
                # 比如“model.ckpt-1000” 表示訓練1000輪之后得到的模型。
                saver.save(sess, os.path.join(MODEL_SAVE_PATH, MODEL_NAME), global_step=global_step)
            else:
                _, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: xs, y_: ys})

    train_writer.close()


def main(argv = None):
    mnist = input_data.read_data_sets("D:/Python3Space/BookStudy/book2/MNIST_data", one_hot=True)
    train(mnist)


if __name__ == '__main__':
    tf.app.run()

運行后:

img

啟動TensorBoard,這樣就可以可視化每個TensorFlow計算節點在某一次運行時所消耗的時間和空間。

img

img

img

TensorBoard左側的Color欄中Compute和Memory這兩個選項將可以被選擇。

img

img

顏色越深的節點,時間和空間的消耗越大。

img

  1. 空心的小橢圓對應了TensorFlow計算圖上的一個計算節點。
  2. 一個矩形對應了計算圖上的一個命名空間。

3. 監控指標可視化

將TensorFlow程序運行時的信息輸出到TensorBoard日志文件中。

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8 

"""
@author: Li Tian
@contact: 694317828@qq.com
@software: pycharm
@file: tensorboard_test6.py
@time: 2019/5/11 15:27
@desc: 將TensorFlow程序運行時的信息輸出到TensorBoard日志文件中。
"""

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data


SUMMARY_DIR = './log'
BATCH_SIZE = 100
TRAIN_STEPS = 3000


# 生成變量監控信息並定義生成監控信息日志的操作。其中var給出了需要記錄的張量,name給出了
# 在可視化結果中顯示的圖標名稱,這個名稱一般與變量名一致。
def variable_summaries(var, name):
    # 將生成監控信息的操作放到同一個命名空間下。
    with tf.name_scope('summaries'):
        # 通過tf.summary.histogram函數記錄張量中元素的取值分布。對於給出的圖表
        # 名稱和張量,tf.summary.histogram函數會生成一個Summary protocol buffer。
        # 將Summary寫入TensorBoard日志文件后,在HISTOGRAMS欄和DISTRIBUTION欄
        # 下都會出現對應名稱的圖標。和TensorFlow中其他操作類似。tf.summary.histogram
        # 函數不會立刻被執行,只有當sess.run函數明確調用這個操作時,TensorFlow才會真正
        # 生成並輸出Summary protocol buffer。下文將更加詳細地介紹如何理解HISTOGRAMS欄
        # 和DISTRIBUTION欄下的信息。
        tf.summary.histogram(name, var)

        # 計算變量的平均值,並定義間生成平均值信息日志的操作。記錄變量平均值信息的日志標簽名
        # 為'mean/' + name,其中mean為命名空間,/是命名空間的分隔符。從圖中可以看出,在相同
        # 命名空間中的監控指標會被整合到同一欄中。name則給出了當前監控指標屬於哪一個變量。
        mean = tf.reduce_mean(var)
        tf.summary.scalar('mean/' + name, mean)
        # 計算變量的標准差,並定義生成其日志的操作。
        stddev = tf.sqrt(tf.reduce_mean(tf.square(var-mean)))
        tf.summary.scalar('stddev/' + name, stddev)


# 生成一層全連接層神經網絡。
def nn_layer(input_tensor, input_dim, output_dim, layer_name, act=tf.nn.relu):
    # 將同一層神經網絡放在一個統一的命名空間下。
    with tf.name_scope(layer_name):
        # 聲明神經網絡邊上的權重,並調用生成權重監控信息日志的函數。
        with tf.name_scope('weights'):
            weights = tf.Variable(tf.truncated_normal([input_dim, output_dim], stddev=0.1))
            variable_summaries(weights, layer_name + '/weights')

        # 聲明神經網絡的偏置項,並調用生成偏置項監控信息日志的函數。
        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
            # 記錄神經網絡輸出節點在經過激活函數之前的分布。
            tf.summary.histogram(layer_name + '/pre_activations', preactivate)

        activations = act(preactivate, name='activation')

        # 記錄神經網絡輸出節點在經過激活函數之后的分布。在圖中,對於layer1,因為
        # 使用了ReLU函數作為激活函數,所以所有小於0的值都被設為了0。於是在激活后的
        # layer1/activations圖上所有的值都是大於0的。而對於layer2,因為沒有使用
        # 激活函數,所以layer2/activations和layer2/pre_activations一樣。
        tf.summary.histogram(layer_name + '/activations', activations)
        return activations


def main(_):
    mnist = input_data.read_data_sets('D:/Python3Space/BookStudy/book2/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函數定義將當前的圖片信息寫入日志的操作。
    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)

    # 計算交叉熵並定義生成交叉熵監控日志的操作。
    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)

    # 計算模型在當前給定數據上的正確率,並定義生成正確率監控日志的操作。如果在sess.run時
    # 給定的數據是訓練batch,那么得到的正確率就是在這個訓練batch上的正確率;如果給定的
    # 數據為驗證或者測試數據,那么得到的正確率就是在當前模型在驗證或者測試數據上的正確率。
    with tf.name_scope('accuracy'):
        with tf.name_scope('correct_prediction'):
            correct_prediction = tf.equal(tf.arg_max(y, 1), tf.arg_max(y_, 1))
        with tf.name_scope('accuracy'):
            accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
        tf.summary.scalar('accuracy', accuracy)

    # 和其他TensorFlow中其他操作類似,tf.summary.scalar、tf.summary.histogram和
    # tf.summary.image函數都不會立即執行,需要通過sess.run來明確調用這些函數。
    # 因為程序中定義的寫日志操作比較多,一一調用非常麻煩,所以TensorFlow提供了
    # tf.summary.merge_all函數來整理所有的日志生成操作。在TensorFlow程序執行的
    # 過程中只需要運行這個操作就可以將代碼中定義的所有日志生成操作執行一次,從而將所有日志寫入文件。
    merged = tf.summary.merge_all()

    with tf.Session() as sess:
        # 初始化寫日志的writer,並將當前TensorFlow計算圖寫入日志
        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)
            # 運行訓練步驟以及所有的日志生成操作,得到這次運行的日志。
            summary, _ = sess.run([merged, train_step], feed_dict={x: xs, y_: ys})
            # 將所有日志寫入文件,TensorBoard程序就可以拿到這次運行所對應的運行信息。
            summary_writer.add_summary(summary, i)

    summary_writer.close()


if __name__ == '__main__':
    tf.app.run()

運行之后打開tensorboard:

img

點擊IMAGES欄可以可視化當前輪訓練使用的圖像信息。

img

TensorBoard的DISTRIBUTION一欄提供了對張量取值分布的可視化界面,通過這個界面可以直觀地觀察到不同層神經網絡中參數的取值變化。

img

為了更加清晰地展示參數取值分布和訓練迭代輪數之間的關系,TensorBoard提供了HISTOGRAMS視圖。

img

顏色越深的平面代表迭代輪數越小的取值分布。

點擊“OVERLAY”后,可以看到如下效果:

img

4. 高維向量可視化

使用MNIST測試數據生成PROJECTOR所需要的兩個文件。(一個sprite圖像,一個tsv文件)

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8 

"""
@author: Li Tian
@contact: 694317828@qq.com
@software: pycharm
@file: tensorboard_test7.py
@time: 2019/5/12 20:45
@desc: 使用MNIST測試數據生成PROJECTOR所需要的兩個文件。(一個sprite圖像,一個tsv文件)
"""

import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np
import os
from tensorflow.examples.tutorials.mnist import input_data


# PROJECTOR需要的日志文件名和地址相關參數。
LOG_DIR = './log2'
SPRITE_FILE = 'mnist_sprite.jpg'
META_FILE = "mnist_meta.tsv"


# 使用給出的MNIST圖片列表生成sprite圖像。
def create_sprite_image(images):
    if isinstance(images, list):
        images = np.array(images)
    img_h = images.shape[1]
    img_w = images.shape[2]
    # sprite圖像可以理解成是所有小圖片拼成的大正方形矩陣,大正方形矩陣中的每一個
    # 元素就是原來的小圖片。於是這個正方形的邊長就是sqrt(n),其中n為小圖片的數量。
    # np.ceil向上取整。np.floor向下取整。
    m = int(np.ceil(np.sqrt(images.shape[0])))

    # 使用全1來初始化最終的大圖片。
    sprite_image = np.ones((img_h*m, img_w*m))

    for i in range(m):
        for j in range(m):
            # 計算當前圖片的編號
            cur = i * m + j
            if cur < images.shape[0]:
                # 將當前小圖片的內容復制到最終的sprite圖像。
                sprite_image[i*img_h: (i+1)*img_h, j*img_w: (j+1)*img_w] = images[cur]

    return sprite_image


# 加載MNIST數據。這里指定了one_hot=False,於是得到的labels就是一個數字,表示當前圖片所表示的數字。
mnist = input_data.read_data_sets('D:/Python3Space/BookStudy/book2/MNIST_data', one_hot=False)

# 生成sprite圖像
to_visualise = 1 - np.reshape(mnist.test.images, (-1, 28, 28))
sprite_image = create_sprite_image(to_visualise)

# 將生成的sprite圖像放到相應的日志目錄下。
path_for_mnist_sprites = os.path.join(LOG_DIR, SPRITE_FILE)
plt.imsave(path_for_mnist_sprites, sprite_image, cmap='gray')
plt.imshow(sprite_image, cmap='gray')

# 生成每張圖片對應的標簽文件並寫到相應的日志目錄下。
path_for_mnist_metadata = os.path.join(LOG_DIR, META_FILE)
with open(path_for_mnist_metadata, 'w') as f:
    f.write('Index\tLabel\n')
    for index, label in enumerate(mnist.test.labels):
        f.write("%d\t%d\n" % (index, label))

這里寫點小tips關於np.ceilnp.floorenumerate

np.ceil:向上取整。

np.floor:向下取整。

enumerate:該函數用於將一個可遍歷的數據對象(如列表、元組或字符串)組合為一個索引序列,同時列出數據和數據下標。

img

回到正題,運行代碼之后可以得到兩個文件,一個是sprite圖,一個是mnist_meta.csv文件。

img

img


在生成好輔助數據之后,以下代碼展示了如何使用TensorFlow代碼生成PROJECTOR所需要的日志文件來可視化MNIST測試數據在最后的輸出層向量。

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8 

"""
@author: Li Tian
@contact: 694317828@qq.com
@software: pycharm
@file: tensorboard_test8.py
@time: 2019/5/13 9:34
@desc: 在生成好輔助數據之后,以下代碼展示了如何使用TensorFlow代碼生成PROJECTOR所需要的日志文件來可視化MNIST測試數據在最后的輸出層向量。
"""

import tensorflow as tf
from BookStudy.book2 import mnist_inference
import os

# 加載用於生成PROJECTOR日志的幫助函數。
from tensorflow.contrib.tensorboard.plugins import projector
from tensorflow.examples.tutorials.mnist import input_data


# 和前面中類似地定義訓練模型需要的參數。這里我們同樣是復用mnist_inference過程。
BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.8
LEARNING_RATE_DECAY = 0.99
REGULARAZTION_RATE = 0.0001
# 可以通過調整這個參數來控制訓練迭代輪數。
TRAINING_STEPS = 10000
MOVING_AVERAGE_DECAY = 0.99

# 和日志文件相關的文件名及目錄地址。
LOG_DIR = './log3'
SPRITE_FILE = 'D:/Python3Space/BookStudy/book2/tensorboard_test/log2/mnist_sprite.jpg'
META_FILE = 'D:/Python3Space/BookStudy/book2/tensorboard_test/log2/mnist_meta.tsv'
TENSOR_NAME = 'FINAL_LOGITS'


# 訓練過程和前面給出的基本一致,唯一不同的是這里還需要返回最后測試數據經過整個
# 神經網絡得到的輸出矩陣(因為有很多張測試圖片,每張圖片對應了一個輸出層向量,
# 所以返回的結果是這些向量組成的矩陣。
def train(mnist):
    # 輸入數據的命名空間。
    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(REGULARAZTION_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)
        variable_averages_op = variable_averages.apply(tf.trainable_variables())

    # 計算損失函數的命名空間。
    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'))

    # 定義學習率、優化方法及每一輪執行訓練操作的命名空間。
    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, variable_averages_op]):
            train_op = tf.no_op(name='train')

    # 訓練模型
    with tf.Session() as sess:
        tf.global_variables_initializer().run()
        for i in range(TRAINING_STEPS):
            xs, ys = mnist.train.next_batch(BATCH_SIZE)
            _, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: xs, y_: ys})

            if i % 1000 == 0:
                print("After %d training step(s), loss on training batch is %g." % (i, loss_value))

        # 計算MNIST測試數據對應的輸出層矩陣。
        final_result = sess.run(y, feed_dict={x: mnist.test.images})

    # 返回輸出層矩陣的值。
    return final_result


# 生成可視化最終輸出層向量所需要的日志文件。
def visualisation(final_result):
    # 使用一個新的變量來保存最終輸出層向量的結果。因為embedding是通過TensorFlow中
    # 變量完成的,所以PROJECTOR可視化的都是TensorFlow中的變量。於是這里需要新定義
    # 一個變量來保存輸出層向量的取值。
    y = tf.Variable(final_result, name=TENSOR_NAME)
    summary_writer = tf.summary.FileWriter(LOG_DIR)

    # 通過projector.ProjectorConfig類來幫助生成日志文件。
    config = projector.ProjectorConfig()
    # 增加一個需要可視化的embedding結果。
    embedding = config.embeddings.add()
    # 指定這個embedding結果對應的TensorFlow變量名稱。
    embedding.tensor_name = y.name

    # 指定embedding結果所對應的原始數據信息。比如這里指定的就是每一張MNIST測試圖片
    # 對應的真實類別。在單詞向量中可以是單詞ID對應的單詞。這個文件是可選的,如果沒有指定
    # 那么向量就沒有標簽。
    embedding.metadata_path = META_FILE

    # 指定sprite圖像。這個也是可選的,如果沒有提供sprite圖像,那么可視化的結果
    # 每一個點就是一個小圓點,而不是具體的圖片。
    embedding.sprite.image_path = SPRITE_FILE
    # 在提供sprite圖像時,通過single_image_dim可以指定單張圖片的大小。
    # 這將用於從sprite圖像中截取正確的原始圖片。
    embedding.sprite.single_image_dim.extend([28, 28])

    # 將PROJECTOR所需要的內容寫入日志文件。
    projector.visualize_embeddings(summary_writer, config)

    # 生成會話,初始化新聲明的變量並將需要的日志信息寫入文件。
    sess = tf.InteractiveSession()
    sess.run(tf.global_variables_initializer())
    saver = tf.train.Saver()
    saver.save(sess, os.path.join(LOG_DIR, "model"), TRAINING_STEPS)

    summary_writer.close()


# 主函數先調用模型訓練的過程,再使用訓練好的模型來處理MNIST測試數據,
# 最后將得到的輸出層矩陣輸出到PROJECTOR需要的日志文件中。
def main(argc=None):
    mnist = input_data.read_data_sets('D:/Python3Space/BookStudy/book2/MNIST_data', one_hot=True)
    final_result = train(mnist)
    visualisation(final_result)


if __name__ == '__main__':
    main()

運行結果:

img

然后打開tensorboard:

img

然后就GG了,網上百度了一萬種方法。。。

參考鏈接:https://blog.csdn.net/weixin_42769131/article/details/84870558

tensorboard --logdir=./log3 --host=127.0.0.1

解決!然而!我可以打開前面的log,這次模型的生成的不知道是不是需要太大的內存,界面一致卡在Computing PCA…

img

就很頭大。。。

然后我換了台電腦(MacBook),就能直接出來。。。

img

還是三圍旋轉的。。。旋轉~ 跳躍~


我的CSDN:https://blog.csdn.net/qq_21579045

我的博客園:https://www.cnblogs.com/lyjun/

我的Github:https://github.com/TinyHandsome

紙上得來終覺淺,絕知此事要躬行~

歡迎大家過來OB~

by 李英俊小朋友


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM