TensorFlowを用いて機械学習を行う際に、
model.compile(optimizer=‘Adam’, loss=‘mse’, metrics=[my_metrics])
のように指定することで自分で作成した評価関数my_metricsを使用することができる。

シグニチャは以下の通り

引数
y_true: 真のラベル.Theano/TensorFlowのテンソル
y_pred: 予測値.y_trueと同じshapeのTheano/TensorFlowのテンソル

戻り値
 全データ点の平均値を表すスカラ.

https://keras.io/ja/metrics

ここで引数y_trueとy_predはどちらも「TensorFlowのテンソル型(tensorflow.python.framework.ops.Tensor)」が入ってくる事に注意。numpyのndarrayではない。

Eager ExecutionとGraph Execution

TensorFlowには、即時に計算を行うEager Executionと、計算をまとめて実行することで最適化、高速化を行うGraph Executionが存在する。(参照)

Eager Executionは、リアルタイムに結果がわかるためデバッグが簡単に行なえ、numpy.ndarrayにも変換できるという特徴がある。
TensorFlow2.0からデフォルトで有効になった。

Tensorimport tensorflow as tf
a = tf.constant([0,1,2])
print(type(a))
# 実行結果
# <class 'tensorflow.python.framework.ops.EagerTensor'>

また、Eager Executionの場合はnumpy()というメソッドが利用できる

import tensorflow as tf
a = tf.constant([0, 1, 2])
print(type(a.numpy()))
# 実行結果
# <class 'numpy.ndarray'>

Graph ExecutionはTensorFlowによるの高速化、並列化、および効率化を期待することができる。
機械学習を行う場合はこちらが有効になる。

def metrics(y_true, y_pred):
    print(type(y_true))
    return 1.0
# 実行結果
# <class 'tensorflow.python.framework.ops.Tensor'>

また、tf.config.run_functions_eagerly(True)を実行することで強制的にEager Executionを利用することもできるが、速度が目に見えて低下するのでおすすめしない。

metricsにはtfのAPIを使おう

numpyが使えないのは少し辛いが、幸いにも「tf max」などでググれば出てくるし、ドキュメントもしっかりしている。
示:内積を計算するtf.math.multiplyのドキュメント


例えばオセロの次の手を予測する学習で、8*8マス結果を評価するコード。

def metrics(y_true, y_pred):
    # (None, 8, 8) to (None, 64)
    reshaped_t = tf.reshape(y_true, (-1, 64))
    reshaped_p = tf.reshape(y_pred, (-1, 64))
    # (None): 64個のマスのうち、一番大きい要素の添字を格納
    argmax_t = tf.math.argmax(reshaped_t, 1)
    argmax_p = tf.math.argmax(reshaped_p, 1)
    # 添字が同じ場所をTrueにした行列へ
    is_equal = tf.math.equal(argmax_t, argmax_p)
    # booleanをfloat32へ
    is_equal_float = tf.cast(is_equal, tf.float32)
    # 平均を取る
    mean = tf.reduce_mean(is_equal_float)
    return mean

気をつけなければ行けないのは、y_trueとy_predの引数のshapeは(None, 8, 8)になる。

本来ならば最初の要素がバッチサイズに一致すると考えるべきだが、y_true, y_predがGraph Executionであるため、実行して結果が確定するまでは未定(=None)になっている。

reshapeしたい場合は、Noneの代わりに-1を使うことで、自動的に保管してくれる。

対面したエラーと自戒メモ

NotImplementedError: Cannot convert a symbolic Tensor (while/strided_slice:0) to a numpy array. This error may indicate that you’re trying to pass a Tensor to a NumPy call, which is not supported

tf.Tensor型をnp.argmax()の引数に与えたことで起こった。
tf.Tensor型はnumpyと併用できない。


tensorflow.python.framework.ops.Tensor to numpy

.numpy()というtf.Tensorをnumpyに変換できる便利なメソッドがあるとググったら出てきたが、Graph Executionの場合はこのメソッドは使えない。