Tensorboard教程:高維向量可視化

Tensorflow高維向量可視化

覺得有用的話,歡迎一起討論相互學習~Follow Me

參考文獻
強烈推薦Tensorflow實戰Google深度學習框架
實驗平臺:
Tensorflow1.4.0
python3.5.0
MNIST數據集將四個文件下載後放到當前目錄下的MNIST_data文件夾下

高維向量表示

  • 爲了更加直觀的瞭解embedding 向量的效果,TensorBoard 提供了PROJECTOR 界面來可視化高維向量之間的關係。PROJECTOR 界面可以非常方便地可視化多個高維向量之間的關係。比如在圖像遷移學習中可以將一組目標問題的圖片通過訓練好的卷積層得到瓶頸層 ,這些瓶頸層向量就是多個高維向量。如果在目標問題圖像數據集上同一種類的圖片在經過卷積層之後得到的瓶頸層向量在空間中比較接近,那麼這樣遷移學習得到的結果就有可能會更好。類似地,在訓練單詞向量時,如果語義相近的單詞所對應的向量在空間中的距離也比較接近的話,那麼自然語言模型的效果也有可能會更好。

  • 爲了更直觀地介紹TensorBoard PROJECTOR 的使用方法,本節將給出一個MNIST的樣例程序。這個樣例程序在MNIST 數據上訓練了一個簡單的全連接神經網絡。本節將展示在訓練100輪和10000輪之後,測試數據經過整個神經網絡得到的輸出層向量通過PROJECTOR 得到的可視化結果。爲了在PROJECTOR中更好地展示MNIST 圖片信息以及每張圖片對應的真實標籤,PROJECTOR 要求用戶準備一個sprite 圖像(所謂sprite 圖像就是將一組圖片組合成一整張大圖片)和一個tsv文件給出每張圖片對應的標籤信息。以下代碼給出瞭如何使用MNIST測試數據生成PROJECTOR所需要的這兩個文件。

示例代碼

# get_ipython().run_line_magic('matplotlib', 'inline')
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 = 'log'
SPRITE_FILE = 'mnist_sprite.jpg'
META_FIEL = "mnist_meta.tsv"


# 使用給出的MNIST圖片列表生成sprite圖像
def create_sprite_image(images):
    """Returns a sprite image consisting of images passed as argument. Images should be count x width x height"""
    if isinstance(images, list):
        images = np.array(images)
    img_h = images.shape[1]
    img_w = images.shape[2]
    # sprite圖像可以理解成是小圖片平成的大正方形矩陣,大正方形矩陣中的每一個元素就是原來的小圖片。於是這個正方形的邊長就是sqrt(n),其中n爲小圖片的數量。
    n_plots = int(np.ceil(np.sqrt(images.shape[0])))

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

    for i in range(n_plots):
        for j in range(n_plots):
            # 計算當前圖片的編號
            this_filter = i*n_plots + j
            if this_filter < images.shape[0]:
                # 將當前小圖片的內容複製到最終的sprite圖像
                this_img = images[this_filter]
                spriteimage[i*img_h:(i + 1)*img_h,
                j*img_w:(j + 1)*img_w] = this_img

    return spriteimage


# 加載MNIST數據。這裏指定了one_hot=False,於是得到的labels就是一個數字,表示當前圖片所表示的數字。
mnist = input_data.read_data_sets(".TensoTensorbotensorboard/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_FIEL)
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))

運行以上代碼可以得到兩個文件, 一個是MNIST 測試數據sprite圖像,這個圖像包含了所有的MNIST測試數據圖像。另一個是mnist meta.tsv ,下面給出了這個tsv 文件的前面幾行。可以看出,這個文件的第一行是每一列的說明,以後的每一行代表一張圖片,在這個文件中給出了每一張圖對應的真實標籤。



  • 在生成好輔助數據之後,以下代碼展示瞭如何使用TensorFlow 代碼生成PROJECTOR
    所需要的日誌文件來可視化MNIST測試數據在最後的輸出層向量。
import tensorflow as tf
import mnist_inference
import os
import tqdm

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

BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.8
LEARNING_RATE_DECAY = 0.99
REGULARIZATION_RATE = 0.0001
TRAINING_STEPS = 10000
MOVING_AVERAGE_DECAY = 0.99

LOG_DIR = 'log_2'
SPRITE_FILE = 'mnist_sprite.jpg'
META_FIEL = "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(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())

    # 計算損失函數的命名空間。
    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)
        # 由於使用L2正則化,此處需要加上'losses'集合
        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, variables_averages_op]):
            train_op = tf.no_op(name='train')

    # 訓練模型。
    with tf.Session() as sess:
        tf.global_variables_initializer().run()
        for i in tqdm.tqdm(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)

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

    # Specify where you find the metadata
    # 指定embedding結果所對應的原始數據信息。比如這裏指定的就是每一張MNIST測試圖片對應的真實類別。在單詞向量中可以是單詞ID對應的單詞。
    # 這個文件是可選的,如果沒有指定那麼向量就沒有標籤。
    embedding.metadata_path = META_FIEL

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

    # Say that you want to visualise the embeddings
    # 將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(argv=None):
    mnist = input_data.read_data_sets("./MNIST_data", one_hot=True)
    final_result = train(mnist)
    visualisation(final_result)


if __name__ == '__main__':
    main()

訓練過程

實驗結果

# 使用cmd命令行定位到py項目文件夾啓動tensorboard
tensorboard --logdir=項目絕對地址
  • 使用T-SNE方式顯示高維向量,這是一個動態的過程,其中隨着iteration的增加,會發現結果向量會逐漸分開。相同類別的會聚攏在一起,我們可以選擇不同顏色作爲區分,發現不同顏色的預測結果的區分度逐漸拉大。

  • 其中我們使用Label作爲color map的區分,Label by index

  • 在PROJECTOR 界面的左上角有三個選項,第一個”FINAL LOGITS”選項是選擇需要可視化的Tensor,這裏默認選擇的是通過ProjectorConfig 中指定的tensor_name,也就是名爲FINAL LOGITS 的張量。雖然PROJECTOR也可以可視化這些向量,但是在這個場景下意義不大。中間的”Label by”選項可以控制當鼠標移到一個向量上時鼠標附近顯示的標籤。比如這裏選定的是”Index”,那麼當鼠標移到某個點上之後顯示的就是這個點對應的編號。

  • 在PROJECTOR界面的左下角提供了不同的高維向量的可視化方法,目前主要支持的就是T-SNE和PCA。無論是T-SNE還是PCA都可以將一個高維向量轉化成一個低維向量,井儘量保證轉化後向量中的信息不受影響。
  • 在PROJECTOR 的右側還提供了高亮功能。
  • 下圖展示了搜索所有代表數字3的圖片,可以看出所有代表數字33的圖片都被高亮標出來了,而且大部分的圖片都集中在一個比較小的區域,只有少數離中心區域比較遠。通過這種方式可以很快地找到每個類別中比較難分的圖片,加速錯誤案例分析的過程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章