Python
TensorFlow
Tensorboard
Projector

【TensorBoard入門:Projector編】TensorFlow処理をかっこよく見える化

More than 1 year has passed since last update.

前から使ってみたかったTensorBoardのProjectorを表示してみたので、記事にします。見た目かっこいいです:v:少し古い資料ではProjectorという言葉ではなく、Embedded Visualizationと書かれていますが同じ意味です。
他TensorBoard関連記事として下記2つも参照ください。

出力イメージ

画面上でこんな感じです。MNISTのデータを使っています。TensorBoardの起動方法は【TensorBoard入門】TensorFlow処理を見える化して理解を深めるをご覧くください。
TensorBoardProjector.gif

環境

実行環境は以下のとおりです。

種類 バージョン
OS Windows 10 64bit Pro
Anaconda Anaconda 4.4.0
Python Python3.5
TensorFlow TensorFlow 1.3

出力方法

通常のTensorBoardの出力に追加して以下の4点が必要です。
1. 画面出力する変数の指定
2. Projectorの初期設定
3. 実行結果保存
4. TSV(タブ区切りテキスト)ファイルの作成
5. Sprite Imageの作成(Option)

コード全体は後で載せますが、パーツごとに解説します。

1. 画面出力する変数の指定

まずは画面出力する変数を指定します。今回はこんな感じでやってますが、実際には関数tf.get_variableを使うのが一般的かと思います。

# embedding変数定義
embedding_var  = tf.Variable(batch_xs, name=NAME_TO_VISUALISE_VARIABLE)

2. Projectorの初期設定

Projectorの初期設定をします。「1. 画面出力する変数の指定」で定義した変数をひもづけ、「4. TSV(タブ区切りテキスト)ファイルの作成」と「5. Sprite Imageの作成」で作成するファイルパスを指定します。

# Projector設定
config = projector.ProjectorConfig()
embedding = config.embeddings.add()
embedding.tensor_name = embedding_var.name

# メタデータ(CSV)パス
embedding.metadata_path = os.path.join(CUR_DIR,META)

# Sprite Imageパスと設定
embedding.sprite.image_path = os.path.join(CUR_DIR,SPRITES)
embedding.sprite.single_image_dim.extend([28,28])

# Projectorに出力
projector.visualize_embeddings(summary_writer, config)

3. 実行結果保存

セッション実行とその実行結果保存です。TensorBoardのProjector固有処理ではないですが、使う場合には必要です。

# 保存準備
saver = tf.train.Saver()

# セッション実行と保存
_ = saver.save(sess, os.path.join(LOG_DIR, "model.ckpt"), 1)

4. TSV(タブ区切りテキスト)ファイルの作成

使ったデータをTSV(タブ区切りテキスト)ファイルに出力します。

with open(os.path.join(LOG_DIR,META),'w') as file:

    # File Header
    file.write("Index\tLabel\n")

    # タブ区切りでラベル(正解の数字)書き込み
    for index, label in enumerate(batch_ys):
        file.write("%d\t%d\n" % (index, label))

5. Sprite Imageの作成(Option)

同じく使った画像をSprite Imageに出力します。なくてもProjectorは動きますが、あったほうが画像出力されるのでわかりやすいですね。
Sprite Imageは公式サイトに記載されているとおり、画像をマトリックス上に並べたものです(Webの世界では常識?)。今回のMNISTではこんな画像を作っています。

mnistdigits.png

def create_sprite_image(images):
    """Returns a sprite image consisting of images passed as argument. Images should be count x width x height"""
    # MNISTは28ピクセル四方 
    img_h = images.shape[1]
    img_w = images.shape[2]

    # 画像数の平方根(切上)を計算(Sprite Imageの1辺の長さに使用)
    n_plots = int(np.ceil(np.sqrt(images.shape[0])))

    # 全要素0の配列作成
    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

            # 画像がある限り実行(n_plotsが割り切れないためSprite Imageは少し余る)
            if this_filter < images.shape[0]:

                # Sprite Imageの所定の配列に画像を挿入
                spriteimage[i * img_h:(i + 1) * img_h, j * img_w:(j + 1) * img_w] = images[this_filter]

    return spriteimage

# 画像をベクトルから配列に変換して、白黒反転(TendorBoard Projectorで見やすいように)
to_visualise = 1 - np.reshape(batch_xs,(-1,28,28))

# Sprite Image生成
sprite_image = create_sprite_image(to_visualise)

# Sprite Image保存
plt.imsave(os.path.join(LOG_DIR,SPRITES), sprite_image,cmap='gray')

Pythonプログラム全体

%matplotlib inline
import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np
import os

from tensorflow.contrib.tensorboard.plugins import projector
from tensorflow.examples.tutorials.mnist    import input_data

LOG_DIR = './minimalsample'
CUR_DIR = './'
SPRITES = 'mnistdigits.png'
META    = 'metadata.tsv'
NAME_TO_VISUALISE_VARIABLE = "mnistembedding"
TO_EMBED_COUNT = 500

path_for_mnist_sprites  = os.path.join(LOG_DIR,SPRITES)
path_for_mnist_metadata = os.path.join(LOG_DIR,META)

# MNISTデータ取得
mnist = input_data.read_data_sets("MNIST_data/", one_hot=False)
batch_xs, batch_ys = mnist.train.next_batch(TO_EMBED_COUNT)

# embedding変数定義
embedding_var  = tf.Variable(batch_xs, name=NAME_TO_VISUALISE_VARIABLE)

# TensorBoardに書き込む前準備
summary_writer = tf.summary.FileWriter(LOG_DIR)

# Projector設定
config = projector.ProjectorConfig()
embedding = config.embeddings.add()
embedding.tensor_name = embedding_var.name

# メタデータ(CSV)パス
embedding.metadata_path = os.path.join(CUR_DIR,META)

# Sprite Imageパスと設定
embedding.sprite.image_path = os.path.join(CUR_DIR,SPRITES)
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.ckpt"), 1)

def create_sprite_image(images):
    """Returns a sprite image consisting of images passed as argument. Images should be count x width x height"""
    # MNISTは28ピクセル四方 
    img_h = images.shape[1]
    img_w = images.shape[2]

    # 画像数の平方根(切上)を計算(Sprite Imageの1辺の長さに使用)
    n_plots = int(np.ceil(np.sqrt(images.shape[0])))

    # 全要素0の配列作成
    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

            # 画像がある限り実行(n_plotsが割り切れないためSprite Imageは少し余る)
            if this_filter < images.shape[0]:

                # Sprite Imageの所定の配列に画像を挿入
                spriteimage[i * img_h:(i + 1) * img_h, j * img_w:(j + 1) * img_w] = images[this_filter]

    return spriteimage

# 画像をベクトルから配列に変換して、白黒反転(TendorBoard Projectorで見やすいように)
to_visualise = 1 - np.reshape(batch_xs,(-1,28,28))

# Sprite Image生成
sprite_image = create_sprite_image(to_visualise)

# Sprite Image保存
plt.imsave(os.path.join(LOG_DIR,SPRITES), sprite_image,cmap='gray')

# Sprite Image表示
plt.imshow(sprite_image,cmap='gray')

# CSVファイル作成
with open(os.path.join(LOG_DIR,META),'w') as file:

    # File Header
    file.write("Index\tLabel\n")

    # タブ区切りでラベル(正解の数字)書き込み
    for index, label in enumerate(batch_ys):
        file.write("%d\t%d\n" % (index, label))

参考リンク

以下のリンクを参考にしました
公式サイト
Simple Introduction to Tensorboard Embedding Visualisation