iMind Developers Blog

iMind開発者ブログ

TensorBoardを使ってみる(v1.13.1)

概要

MNIST-fashionを例にTensorBoardで各種metrixを表示したり、画像を表示したり。

Keras利用。

バージョン情報

  • tensorboard==1.13.1
  • tensorflow==1.13.1

tf.keras.callbacks.TensorBoardの利用

tf.keras.callbacks.TensorBoard を設定しておくとmetricsの内容がTensorBoardにも送られる。

import datetime
import numpy as np

import tensorflow as tf
from tensorflow import keras
from sklearn import model_selection
from tensorflow.keras import layers

# データの用意
fashion_mnist = keras.datasets.fashion_mnist
(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()
train_images = train_images / 255.0
test_images = test_images / 255.0

# testデータの1/10をvalidationデータに使う
test_images, val_images, test_labels, val_labels = model_selection.train_test_split(
    test_images, test_labels, test_size=0.1, stratify=test_labels)

# 単純な推定器
model = tf.keras.Sequential([
    layers.Flatten(input_shape=(28, 28)),
    layers.Dense(128, activation='relu'),
    layers.Dense(10, activation='softmax')
])

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

# callbackの指定
tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir="logs")

# callbacksを指定してfit実行
model.fit(
    train_images,
    train_labels,
    epochs=50,
    validation_data=(val_images, val_labels),
    callbacks=[tensorboard_callback])

tf.ketf.ras.callbacks.TensorBoard で metricsに指定した値(今回はaccuracy)がTensorBoardのSCALARSに表示されるようになる。

TensorBoardを起動する。

$ tensorboard --logdir=logs

localhost:6006 を開くと各種グラフが確認できる。

こちらはトレーニングデータのaccuracy。

f:id:mwsoft:20190803152139p:plain

トレーニングデータのloss。

f:id:mwsoft:20190803152151p:plain

validationデータのaccuracy。

f:id:mwsoft:20190803152203p:plain

validationデータのloss。

f:id:mwsoft:20190803152214p:plain

リアルタイムで学習状況が確認できるのでとても便利。

サンプルコード全文

metircsの追加

metricsの関数を自作する場合は、Keras自身のコードを見るとわかりやすい。

keras-team/keras metrics.py

たとえばaccuracyの取り方は下記のようなコードになっている。

from keras import backend as K

# binary
def binary_accuracy(y_true, y_pred):
    return K.mean(K.equal(y_true, K.round(y_pred)), axis=-1)

# categorical
def categorical_accuracy(y_true, y_pred):
    return K.cast(K.equal(K.argmax(y_true, axis=-1),
                          K.argmax(y_pred, axis=-1)),
                  K.floatx())

単純な例として、labelが0であると予測した割合を計測するだけの処理。

from keras import backend as K

def pred_zero(y_true, y_pred):
    return K.equal(K.argmax(y_pred, axis=-1), 0)

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy', pred_zero])

f:id:mwsoft:20190803155254p:plain

callbacks内でvalidationデータやtestデータを評価

metricsの中だとf-score等の細かい計算はしづらいので、各種計算はcallbacksに指定して on_epoch_end(epochが終わるたびに呼び出される) あたりで計算する。

下記はvalidation, test双方のデータを引数で取って、evaluateした結果をprintしている。

class MyCallback(tf.keras.callbacks.Callback):
    
    def __init__(self, val_images, val_labels,
             test_images, test_labels):
        self.val_images = val_images
        self.val_labels = val_labels
        self.test_images = test_images
        self.test_labels = test_labels

    def on_epoch_end(self, epoch, logs={}):
        val_loss, val_acc = self.model.evaluate(
            self.val_images, self.val_labels, verbose=0)
        test_loss, test_acc = self.model.evaluate(
            self.test_images, self.test_labels, verbose=0)
        print('\nval_loss={}, val_acc={}, test_loss={}, test_acc={}'.format(
            val_loss, val_acc, test_loss, test_acc))

callbacksに指定して動かしてみる。

my_callback = MyCallback(
    val_images, val_labels, test_images, test_labels)

model.fit(
    train_images,
    train_labels,
    epochs=2,
    validation_data=(val_images, val_labels),
    callbacks=[tensorboard_callback, my_callback])

実行結果

Epoch 1/2
59968/60000 [============================>.] - ETA: 0s - loss: 0.2329 - acc: 0.9138
val_loss=0.20510950005054474, val_acc=0.925000011920929, test_loss=0.2501372572183609, test_acc=0.907444417476654
60000/60000 [==============================] - 13s 220us/sample - loss: 0.2329 - acc: 0.9138 - val_loss: 0.2051 - val_acc: 0.9250
Epoch 2/2
59776/60000 [============================>.] - ETA: 0s - loss: 0.2280 - acc: 0.9160
val_loss=0.2173353750705719, val_acc=0.9190000295639038, test_loss=0.2576948693063524, test_acc=0.9055555462837219
60000/60000 [==============================] - 13s 223us/sample - loss: 0.2278 - acc: 0.9161 - val_loss: 0.2173 - val_acc: 0.9190

print文の内容が(ちょっと思ってたタイミングとは違うけど)出力されている。

続いてTensorBoardに計算結果を出力してみる。

tf.keras.callbacks.TensorBoard を継承して on_epoch_end にprecision, recall, f-scoreを取得するコードを書いてTensorBoardに出力する。

class MyCallback(tf.keras.callbacks.TensorBoard):
    
    def __init__(self, val_images, val_labels,
            test_images, test_labels, **kwargs):
        super().__init__(**kwargs)
        self.val_images = val_images
        self.val_labels = val_labels
        self.test_images = test_images
        self.test_labels = test_labels

    def on_epoch_end(self, epoch, logs={}):
        super().on_epoch_end(epoch, logs)
        val_loss, val_acc = self.model.evaluate(
            self.val_images, self.val_labels, verbose=0)
        test_loss, test_acc = self.model.evaluate(
            self.test_images, self.test_labels, verbose=0)
        val_test_logs = {
            'epoch_end/val_loss': val_loss,
            'epoch_end/val_acc': val_acc,
            'epoch_end/test_loss': test_loss,
            'epoch_end/test_acc': test_acc
        }
        for name, value in val_test_logs.items():
            summary = tf.Summary()
            summary_value = summary.value.add()
            summary_value.tag = name
            summary_value.simple_value = value
            self.writer.add_summary(summary, epoch)
        self.writer.flush()

callbacksに設定する。

my_callback = MyCallback(
    val_images, val_labels, test_images, test_labels,
    log_dir='logs')

model.fit(
    train_images,
    train_labels,
    epochs=50,
    validation_data=(val_images, val_labels),
    callbacks=[my_callback])

これでTensorBoardにはepochが終わるたびに更新される各種グラフが表示される。

f:id:mwsoft:20190803164533p:plain

サンプルコード全文

クラスごとの指標を表示

fashion-mnistのデータはTシャツやコート、サンダルなどの10個のクラスを持っている。

on_epoch_endでクラスごとのprecision, recall, f1-scoreを計算してTensorBoardに出力してみる。

from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix

# fashion-mnistのクラス一覧
LABELS = [
    'T-shirt/top',
    'Trouser',
    'Pullover',
    'Dress',
    'Coat',
    'Sandal',
    'Shirt',
    'Sneaker',
    'Bag',
    'Ankle boot',
]

# callbackの記述
class MyCallback(tf.keras.callbacks.TensorBoard):
    
    def __init__(self, test_images, test_labels, **kwargs):
        super().__init__(**kwargs)
        self.test_images = test_images
        self.test_labels = test_labels

    def on_epoch_end(self, epoch, logs={}):
        super().on_epoch_end(epoch, logs)

        # predict実行
        y_pred = np.argmax(
            model.predict(test_images), axis=1) .astype(np.int8)
        
        # precision/recall/f-score取得
        report = classification_report(
            test_labels, y_pred, output_dict=True)

        # 指定指標のValuesを生成
        def get_values(key):
            return np.array([tf.Summary.Value(
                tag='%s/%s' % (key, LABELS[label]),
                simple_value=report[str(label)][key])
            for label in range(10)])
        
        values = np.array([
            get_values('precision'), 
            get_values('recall'),
            get_values('f1-score'),
        ]).flatten()
        
        # summaryの設定
        summary = tf.Summary(value=values)
        self.writer.add_summary(summary, epoch)

これで下記のようにクラスごとのprecision, recall, f1-scoreが取得できる。

f:id:mwsoft:20190805220006p:plain

上図ではcoatとpull overが0.8付近で推移しており苦戦していることがわかる。

サンプルコード全文

画像の表示

どの画像がどのクラスに判定されたのか視覚的にわかるように、TensorBoardに画像と予測結果を出力する。

これまでは tf.Summary.Value の引数で simple_value に値を入れていたが、画像の場合は image に渡す。

下記は on_epoch_end でランダムに10枚画像を見繕って表示している。4行目のfont指定は環境に合わせて変更してください。

from  PIL import Image, ImageDraw, ImageFont
import six

# フォントの指定
font = ImageFont.truetype("NotoSansCJK-Bold.ttc", 24)

class MyCallback(tf.keras.callbacks.TensorBoard):
    
    def __init__(self, val_images, val_labels, **kwargs):
        super().__init__(**kwargs)
        self.val_images = val_images
        self.val_labels = val_labels
    
    def encode_image_array_as_png_str(self, image, pred, label):
        # 画像をPillowのImageに変換
        # 画像に文字を書込む為にサイズを増やしてRGBに
        image = Image.fromarray(np.uint8(image)) \
            .resize((256, 256)).convert('RGB')
        # 画面下部に赤文字でpred, labelを表示
        message = "P=%s\nL=%s" % (LABELS[pred], LABELS[label])
        draw = ImageDraw.Draw(image)
        draw.text((20, 180), message, (255, 0, 0), font=font)
        # Valueに指定できるよう形式を変換
        with six.BytesIO() as output:
            image.save(output, format='PNG')
            png_string = output.getvalue()
        return png_string

    def on_epoch_end(self, epoch, logs={}):
        super().on_epoch_end(epoch, logs)

        # predict実行
        y_pred = np.argmax(model.predict(test_images), axis=1).astype(np.int8)
        
        # 画像の設定
        random_index = np.random.randint(0, len(test_labels), size=10)
        def get_image_value(i):
            label = test_labels[i]
            pred = y_pred[i]
            image = test_images[i] * 255
            image_str = self.encode_image_array_as_png_str(image, label, pred)
            img_summary = tf.Summary.Image(encoded_image_string=image_str)
            return tf.Summary.Value(tag='image/%d' % i, image=img_summary)
        img_values = [get_image_value(i) for i in random_index]

        summary = tf.Summary(value=img_values)
        self.writer.add_summary(summary, epoch)
        self.writer.flush()

画像をValueに変換する記述については下記を参照した。

https://github.com/tensorflow/models/blob/v1.13.0/research/object_detection/utils/visualization_utils.py#L80

これを実行するとTensorBoard上部のメニューにImageが追加される。

f:id:mwsoft:20190805222042p:plain

Imageタブを選択すると、下記のようにどの画像にどのラベルが推測されたかが表示される。Pはpredict, Lはlabelの略。

f:id:mwsoft:20190805231431p:plain:w600

左下の画像がラベルはShirtだが予測はCoatになっている。

サンプルコード全文

改定履歴

Author: Masato Watanabe, Date: 2019-08-10, 記事投稿