概要
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。
トレーニングデータのloss。
validationデータのaccuracy。
validationデータのloss。
リアルタイムで学習状況が確認できるのでとても便利。
metircsの追加
metricsの関数を自作する場合は、Keras自身のコードを見るとわかりやすい。
たとえば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])
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が終わるたびに更新される各種グラフが表示される。
クラスごとの指標を表示
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が取得できる。
上図では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に変換する記述については下記を参照した。
これを実行するとTensorBoard上部のメニューにImageが追加される。
Imageタブを選択すると、下記のようにどの画像にどのラベルが推測されたかが表示される。Pはpredict, Lはlabelの略。
左下の画像がラベルはShirtだが予測はCoatになっている。
改定履歴
Author: Masato Watanabe, Date: 2019-08-10, 記事投稿