Пользовательская метрика, основанная на метриках streamorflow streaming, возвращает NaN

Я пытаюсь определить показатель F1 как пользовательскую метрику в TensorFlow для DNNClassifier. Для этого я написал функцию

def metric_fn(predictions=[], labels=[], weights=[]):
    P, _ = tf.contrib.metrics.streaming_precision(predictions, labels)
    R, _ = tf.contrib.metrics.streaming_recall(predictions, labels)
    if P + R == 0:
        return 0
    return 2*(P*R)/(P+R)

который использует streaming_precision и streaming_recall из TensorFlow для калибровки оценки F1. После этого я сделал новую запись в validation_metrics:

validation_metrics = {
    "accuracy":
        tf.contrib.learn.MetricSpec(
            metric_fn=tf.contrib.metrics.streaming_accuracy,
            prediction_key=tf.contrib.learn.PredictionKey.CLASSES),
    "precision":
        tf.contrib.learn.MetricSpec(
            metric_fn=tf.contrib.metrics.streaming_precision,
            prediction_key=tf.contrib.learn.PredictionKey.CLASSES),
    "recall":
        tf.contrib.learn.MetricSpec(
            metric_fn=tf.contrib.metrics.streaming_recall,
            prediction_key=tf.contrib.learn.PredictionKey.CLASSES),
    "f1score":
        tf.contrib.learn.MetricSpec(
            metric_fn=metric_fn,
            prediction_key=tf.contrib.learn.PredictionKey.CLASSES)
}

Однако, хотя я получаю правильные значения точности и возврата, f1score всегда nan:

INFO:tensorflow:Saving dict for global step 151: accuracy = 0.982456, accuracy/baseline_label_mean = 0.397661, accuracy/threshold_0.500000_mean = 0.982456, auc = 0.982867, f1score = nan, global_step = 151, labels/actual_label_mean = 0.397661, labels/prediction_mean = 0.406118, loss = 0.310612, precision = 0.971014, precision/positive_threshold_0.500000_mean = 0.971014, recall = 0.985294, recall/positive_threshold_0.500000_mean = 0.985294

Что-то не так с моим metric_fn, но я не могу понять это. Значения P и R, полученные metric_fn, имеют вид Tensor("precision/value:0", shape=(), dtype=float32). Я нахожу это немного странным. Я ожидал скалярный тензор.

Любая помощь приветствуется.

Ответ 1

Я думаю, проблема может быть связана с тем, что потоковые показатели, которые вы используете в своем metric_fn, не получают никакого обновления.

Попробуйте следующее (я также включил незначительные изменения на мой вкус):

def metric_fn(predictions=None, labels=None, weights=None):
    P, update_op1 = tf.contrib.metrics.streaming_precision(predictions, labels)
    R, update_op2 = tf.contrib.metrics.streaming_recall(predictions, labels)
    eps = 1e-5;
    return (2*(P*R)/(P+R+eps), tf.group(update_op1, update_op2))

Ответ 2

tf.learn.MetricSpec __init__ первый аргумент metric_fn.

В документации написано:

metric_fn: функция, используемая в качестве показателя. См. _adapt_metric_fn для правил о том, как прогнозы, метки и веса передаются этой функции. Это должно возвращать либо один Тензор, который интерпретируется как значение этой метрики, либо пара (value_op, update_op), где value_op - это op для вызова, чтобы получить значение метрики, а update_op следует запускать для каждой партии для обновления внутреннего состояния.

Поскольку вы хотите использовать потоковые операции в metric_fn, вы не можете вернуть один тензор, но вы должны принять во внимание, что потоковые операции имеют внутреннее состояние, которое необходимо обновить.

Таким образом, первая часть вашего metric_fn должна быть:

def metric_fn(predictions=[], labels=[], weights=[]):
    P, update_precision = tf.contrib.metrics.streaming_precision(predictions, labels)
    R, update_recall = tf.contrib.metrics.streaming_recall(predictions, labels)

Затем, если вы хотите вернуть 0 при выполнении условия, вы не можете использовать оператор python if (который не вычисляется в соответствии с графиком тензорного потока), но вы должны использовать tf.cond (вычисление внутри графика).

Кроме того, вы хотите проверить значение P и R только после операции обновления (в противном случае первое значение будет undefined или nan).

Чтобы принудительно выполнить оценку tf.cond после обновления P и R, вы можете использовать tf.control_dependencies

def metric_fn(predictions=[], labels=[], weights=[]):
    P, update_precision = tf.contrib.metrics.streaming_precision(predictions, labels)
    R, update_recall = tf.contrib.metrics.streaming_recall(predictions, labels)

    with tf.control_dependencies([P, update_precision, R, update_recall]):
        score = tf.cond(tf.equal(P + R, 0.), lambda: 0, lambda: 2*(P*R)/(P+R))
    return score, tf.group(update_precision, update_recall)

Ответ 3

Если бы предложенный выше ответ не помог...

Я не так много знаю о том, как настраиваемые показатели работают в TF, но как насчет того, как вы меняете свое имя функции на что-то еще, f1score?

Возможно, произошел конфликт где-то, потому что параметр и значение имеют одинаковое имя.