2017-06-26 1 views
10

Ich versuche den F1-Score als benutzerdefinierte Metrik in TensorFlow für eine DNNClassifier zu definieren. Um das zu tun, schrieb ich eine FunktionBenutzerdefinierte Metrik basierend auf Streaming-Metriken von Tensorflow gibt NaN zurück

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) 

die streaming_precision und streaming_recall von TensorFlow verwendet die F1-Score zu calulate. Danach machte ich einen neuen Eintrag in die 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) 
} 

Doch obwohl ich richtig Precision und Recall-Werte zu erhalten, f1score ist immer 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 

Irgend etwas stimmt nicht mit meinem metric_fn, aber ich kann es nicht herausfinden. Die Werte P und R, die durch metric_fn erhalten werden, haben die Form Tensor("precision/value:0", shape=(), dtype=float32). Ich finde das ein bisschen komisch. Ich habe einen skalaren Tensor erwartet.

Jede Hilfe wird geschätzt.

Antwort

6

Ich denke, das Problem kann von der Tatsache kommen, dass die Streaming-Metriken, die Sie innerhalb Ihrer metric_fn verwenden, keine Aktualisierung erhalten.

Versuchen Sie, die folgenden (I auch kleinere Änderungen an meinen Geschmack enthalten):

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)) 
+0

das funktioniert können, danke. Es gibt jedoch einen kleinen Syntaxfehler (fehlt '') '' im ersten Teil der return-Anweisung. – TheWaveLad

+0

@TheWaveLad erledigt, thx – user1735003

-1

Wenn das, was die oben genannte Antwort vorgeschlagen hat nicht funktioniert ...

Ich weiß nicht so viel über wie benutzerdefinierte Metriken in TF funktionieren, aber wie wäre es, wenn Sie Ihren Funktionsnamen in etwas anderes wie f1score ändern?

Möglicherweise ist irgendwo ein Konflikt aufgetreten, weil der Parameter und der Wert den gleichen Namen haben.

1

tf.learn.MetricSpec__init__ erstes Argument ist metric_fn.

Die Dokumentation sagt:

metric_fn: Eine Funktion als Metrik zu verwenden. Anweisungen zur Übergabe von Vorhersagen, Beschriftungen und Gewichtungen an diese Funktion finden Sie unter _adapt_metric_fn. Dies muss entweder einen einzelnen Tensor, der als Wert dieser Metrik interpretiert wird, oder ein Paar (value_op, update_op) zurückgeben, wobei value_op die zu rufende Op ist, um den Wert der Metrik zu erhalten, und update_op für jeden Batch ausgeführt werden sollte um den internen Status zu aktualisieren.

Da Sie Streaming-Operationen in Ihrem metric_fn verwenden möchten, können Sie nicht einen einzigen Tensor zurückkehren, aber man muss berücksichtigen, dass die Streaming-Operationen einen inneren Zustand haben, die aktualisiert werden muss.

Somit ist der erste Teil Ihrer metric_fn sollte sein:

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) 

Dann, wenn Sie 0 zurückkehren wollen, wenn eine Bedingung erfüllt ist nicht Python if Anweisung verwenden (das ist nicht insede der tensorflow berechnet Grafik), aber Sie müssen tf.cond (Berechnung innerhalb der Grafik) verwenden.

Außerdem Sie den Wert P und R nur nach den Aktualisierungsvorgang überprüfen möchten (sonst der erste Wert ist nicht definiert oder nan).

Um die Auswertung von tf.cond nach der P und R Aktualisierung zu erzwingen, Sie 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) 
+0

Sie benötigen die 'control_dependencies' nicht, da dies bereits in den Streaming-Metriken berücksichtigt wird. Also läuft es am Ende auf meine Antwort hinaus. – user1735003