Пользовательская метрика, основанная на метриках 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
?
Возможно, произошел конфликт где-то, потому что параметр и значение имеют одинаковое имя.