Keras で自前の評価関数を作成する

Keras の出力で異なる活性化関数を使いそれぞれの損失関数を指定する - プログラマのメモ書き で、評価関数 を作成してみました。この評価関数で、bbox の評価値として Intersection over Union (IOU) を計算します。

作成したプログラムは、以下のところにあります。

bbox の評価値は、exists の正解値と予測値の組み合わせにより、以下のようになります。

exists の正解値 exists の予測値 bbox の評価値
< 0.5 <0.5 1.0
< 0.5 >=0.5 0.0
>=0.5 <0.5 0.0
>=0.5 >=0.5 bboxの正解値と予測値の IOU

作成した評価関数ですが、exists の関数と bbox の関数があります。
exists の評価関数 exists_acc は、以下のようになります。

from keras.metrics import binary_accuracy

y_acc_true_exists = None
y_acc_pred_exists = None

def exists_acc(y_true, y_pred):
    global y_acc_true_exists, y_acc_pred_exists
    y_acc_true_exists = y_true[:, 0]
    y_acc_pred_exists = y_pred[:, 0]
    return binary_accuracy(y_true, y_pred)

exists_acc では、keras.metrics.binary_accuracy を使って評価値を計算します。
また、この正解値と予測値は bbox の評価値の計算で使用するため、
グローバル変数に保存します:-(

bbox の評価関数 bbox_acc は、次の通りです。

def bboxes_acc(y_true, y_pred):
    global y_acc_true_exists, y_acc_pred_exists

    zeros = tf.zeros_like(y_acc_true_exists)
    ones = tf.ones_like(y_acc_true_exists)

    # 正解と予測の両方が存在しない場合、評価値は 1、それ以外は 0
    both_not_exists = tf.logical_and(
        tf.less(y_acc_true_exists, 0.5),
        tf.less(y_acc_pred_exists, 0.5))
    acc_1 = tf.where(both_not_exists, ones, zeros)

    # 正解と予測の両方が存在する場合、IOU を計算する。
    both_exists = tf.logical_and(
        tf.greater_equal(y_acc_true_exists, 0.5),
        tf.greater_equal(y_acc_pred_exists, 0.5))
    acc_2 = tf.where(both_exists, bboxes_acc_iou(y_true, y_pred), acc_1)
    
    return acc_2

bbox の IOU を計算する関数 bboxes_acc_iou は、次のようになります。

def bboxes_acc_iou(y_true, y_pred):
    x1, y1, w1, h1 = y_true[:, 0], y_true[:, 1], y_true[:, 2], y_true[:, 3] 
    x2, y2, w2, h2 = y_pred[:, 0], y_pred[:, 1], y_pred[:, 2], y_pred[:, 3]
    zeros = tf.zeros_like(x1)
    
    w_I =  tf.maximum(zeros, tf.minimum(x1 + w1, x2 + w2) - tf.maximum(x1, x2))
    h_I =  tf.maximum(zeros, tf.minimum(y1 + h1, y2 + h2) - tf.maximum(y1, y2))

    I = w_I * h_I
    U = w1 * h1 + w2 * h2 - I
    return I / U

w_I と h_I は、二つの bbox の重なりの幅と高さです。
重なっていない場合は 0 になります。