入門者に向けてKerasの評価関数について解説します。
適合率(Precision)や再現率(Recall)を評価関数として追加したときに、理解に時間をかけたので記録しておきます。
TensorBoardも含めてGoogle Colaboratoryを使っているのでローカルでの環境準備すらしていません。Google Colaboratoryについては「Google Colaboratory概要と使用手順(TensorFlowもGPUも使える)」の記事を参照ください。
以下のシリーズ記事です。
- 【Keras入門(1)】単純なディープラーニングモデル定義
- 【Keras入門(2)】訓練モデル保存(KerasモデルとSavedModel)
- 【Keras入門(3)】TensorBoardで見える化
- 【Keras入門(4)】Kerasの評価関数(Metrics) <- 本記事
- 【Keras入門(5)】単純なRNNモデル定義
- 【Keras入門(6)】単純なRNNモデル定義(最終出力のみ使用)
- 【Keras入門(7)】単純なSeq2Seqモデル定義
##使ったPythonライブラリ
Google Colaboratoryでインストール済の以下のライブラリとバージョンを使っています。KerasはTensorFlowに統合されているものを使っているので、ピュアなKerasは使っていません。Pythonは3.6です。
- tensorflow: 1.13.1
- Numpy: 1.16.3
Kerasでの評価関数(Metrics)の基本的な使い方
compile関数で評価関数(Metrics)を指定します。"acc"または"accuracy"を選ぶと、損失関数や出力テンソルの情報から自動で"categorical_accuracy"などを判断してくれるようです。
model.compile(loss="binary_crossentropy", optimizer="sgd", metrics=['acc'])
概要は評価関数の公式文書に書いてあります。TensorFlow1.13以降を使えば、適合率(Precision)や再現率(Recall)なども指定できます(以下のコード参照)。あまりよく調べていないのですが純正Kerasだと使えなそうな気がします。使用可能なMetricsはAPI文書を参照ください。
※2021/09/16 リンク先変更しました。こちらの方がわかりやすい。
from tensorflow.keras.metrics import Precision, Recall
model.compile(loss="binary_crossentropy", optimizer="sgd", metrics=[Precision(), Recall()])
Kerasでのカスタム評価関数(Metrics)の定義の仕方
Tensorの扱い方に慣れていないので、作り方を理解するのに時間がかかりました。公式ガイドを見ても情報が少なくてよくわからない。metrics.pyなどを見てつくりながら確認しました。
(y_true, y_pred) を引数とし,各データ点に対してスカラを返す関数を評価関数として利用できます:
y_true: 正解ラベル.Theano/TensorFlow テンソル
y_pred: 予測.y_trueと同じ形状のTheano/TensorFlow テンソル
カスタム評価関数
カスタム関数の基本とTP定義
今回はOne-Hot-Encodingでない二値分類だとします。
その場合、関数の引数y_true(正解ラベル)は0または1、y_pred(予測値)は0から1までの連続変数です。仮にバッチサイズが4で、TP・TN・FP・FNの全パターンの場合は以下のように引数が渡されます。
TP | TN | FP | FN | |
---|---|---|---|---|
y_true | 1 | 0 | 0 | 1 |
y_pred | 0.8 | 0.2 | 0.8 | 0.2 |
例えばTP(True Positive)のMetricsは以下のように定義します。
import tensorflow.keras.backend as K
def tp(y_true, y_pred):
return K.sum(K.round(y_true * y_pred)) * batches
混合行列で*"K.round(y_true * y_pred)"の部分を表現すると下記のようになり、TP部分の1が"K.sum"でカウントされます("K.round"*は四捨五入)。
実際は正 (Positive) y_true=1 |
実際は負 (Negative) y_true=0 |
|
---|---|---|
予測が正 (Positive) y_pred=0.8 |
TP $1 \times 0.8 \fallingdotseq 1$ |
FP $0 \times 0.8 \fallingdotseq 0$ |
予測が負 (Negative) y_pred=0.2 |
FN $1 \times 0.2 \fallingdotseq 0$ |
TN $0 \times 0.2 \fallingdotseq 0$ |
計算式の末尾の*"* batches"*の部分は、1エポック内でバッチをまわす回数です。Kerasはバッチ間のMetricsが平均により算出されます。そのため、平均化したくない場合にはバッチ回数で乗算が必要です(二度手間)。
例えば、30件のデータに対してバッチサイズを10にして1エポックで3回バッチ実行をします。
model.fit(data, labels, epochs=300, batch_size=10)
その場合、乗算をしないと以下のように計算され、最終的にエポック内でのMetrics出力値は20となります。
関数出力値 | |
---|---|
バッチ1回目 | 10 |
バッチ2回目 | 20 |
バッチ3回目 | 30 |
出力Metrics | (10+20+30)/3 = 20 |
TPのような絶対値を出力したいMetricsでは、平均化したくないので乗算をして以下のように計算をします。
TP関数出力値 | |
---|---|
バッチ1回目 | $10 \times 3 = 30$ |
バッチ2回目 | $20 \times 3 = 60$ |
バッチ3回目 | $30 \times 3 = 90$ |
出力Metrics | $ (30+60+90) / 3 = 60$ $ 10+20+30 = 60$ |
TN定義
TN(True Negative)の場合は以下の計算式で算出します。
def tn(y_true, y_pred):
return K.sum(K.cast(K.equal(K.round(y_true + y_pred), 0), K.floatx())) * batches
混合行列で*"K.round(y_true + y_pred)"*の部分を表現すると下記のようになり、TNは0と計算させます。
実際は正 (Positive) y_true=1 |
実際は負 (Negative) y_true=0 |
|
---|---|---|
予測が正 (Positive) y_pred=0.8 |
TP $1 + 0.8 \fallingdotseq 2$ |
FP $0 + 0.8\fallingdotseq 1$ |
予測が負 (Negative) y_pred=0.2 |
FN $1 + 0.2 \fallingdotseq 1$ |
TN $0 + 0.2 \fallingdotseq 0$ |
そのあとに以下の流れでTNをカウントします。
- *"K.equal"*で0をTrueに変換
- *"K.cast"*でTrueを1に変換
- *"K.sum"*で1を合算
FP定義
FP(False Positive)の場合は以下の計算式で算出します。
def fp(y_true, y_pred):
return K.sum(K.cast(K.equal(K.round(y_pred) - y_true, 1), K.floatx())) * batches
混合行列で*"K.round(y_pred) - y_true"*の部分を表現すると下記のようになり、FPは1と計算させます。
実際は正 (Positive) y_true=1 |
実際は負 (Negative) y_true=0 |
|
---|---|---|
予測が正 (Positive) y_pred=0.8 |
TP $1 - 1 = 0$ |
FP $1 - 0 = 1$ |
予測が負 (Negative) y_pred=0.2 |
FN $0 - 1 = -1$ |
TN $0 - 0 = 0$ |
そのあとに以下の流れでFPをカウントします。
- *"K.equal"*で1をTrueに変換
- *"K.cast"*でTrueを1に変換
- *"K.sum"*で1を合算
FN定義
FN(False Negative)の場合は以下の計算式で算出します。
def fn(y_true, y_pred):
return K.sum(K.cast(K.equal(y_true - K.round(y_pred), 1), K.floatx())) * batches
混合行列で*"y_true - K.round(y_pred)"*の部分を表現すると下記のようになり、FNは1と計算させます。
実際は正 (Positive) y_true=1 |
実際は負 (Negative) y_true=0 |
|
---|---|---|
予測が正 (Positive) y_pred=0.8 |
TP $1 - 1 = 0$ |
FP $0 - 1 = -1$ |
予測が負 (Negative) y_pred=0.2 |
FN $1 - 0 = 1$ |
TN $0 - 0 = 0$ |
そのあとに以下の流れでFNをカウントします。
- *"K.equal"*で1をTrueに変換
- *"K.cast"*でTrueを1に変換
- *"K.sum"*で1を合算
適合率(Precision)と再現率(Recall)
TP、FP、FNが算出できれば適合率(Precision)と再現率(Recall)は簡単です。TPなどの値から計算すればいいだからです。
def custom_precision(y_true, y_pred):
return tp(y_true, y_pred) / (tp(y_true, y_pred) + fp(y_true, y_pred))
def custom_recall(y_true, y_pred):
return tp(y_true, y_pred) / (tp(y_true, y_pred) + fn(y_true, y_pred))
F1スコアを出したい場合には、簡単にできないので注意してください。
Kerasで評価関数にF1スコアを使う方法参照。
Pythonプログラム
プログラム全体はGitHubにあります。
適合率(Precision)や再現率(Recall)を追加するだけなら、TensorFlow1.13以上のバージョンでは、ここで書いてあるような面倒なことは不要です。
TensorBoardに出力すれば、以下のようにMetricsを見ることができます。
※出力方法は記事「【Keras入門(3)】TensorBoardで見える化」参照