简体   繁体   English

张量流中的自定义 f1_score 指标

[英]Custom f1_score metric in tensorflow

I want to implement the f1_score metric for tf.keras.我想为 tf.keras 实现 f1_score 指标。

from tensorflow.keras.models import Model, Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.losses import binary_crossentropy
from tensorflow.keras.metrics import Accuracy, BinaryAccuracy
from sklearn.metrics import accuracy_score
import numpy as np
import tensorflow as tf

class F1_Score(tf.keras.metrics.Metric):

    def __init__(self, name='f1_score', **kwargs):
        super().__init__(name=name, **kwargs)
        self.f1 = self.add_weight(name='f1', initializer='zeros')

    def update_state(self, y_true, y_pred, sample_weight=None):
        p = Precision(thresholds=0.5)(y_true, y_pred)
        r = Recall(thresholds=0.5)(y_true, y_pred)
        self.f1 = 2 * ((p * r) / (p + r + 1e-6))

    def result(self):
        return self.f1

    def reset_states(self):
        self.f1.assign(0)
        
model = Sequential([
  Dense(64, activation='relu', input_shape=(784,)),
  Dense(64, activation='relu'),
  Dense(4, activation='sigmoid'),
])
x = np.random.normal(size=(10, 784))
y = np.random.choice(2, size=(10, 4))
model.compile(optimizer=Adam(0.001), loss='binary_crossentropy',
                  metrics=['accuracy', , F1_Score()])
model.fit(x[:1], y[:1], batch_size=1, epochs=1, verbose=1)

I got an error:我有一个错误:

ValueError: tf.function-decorated function tried to create variables on non-first call. ValueError: tf.function-decorated 函数试图在非第一次调用时创建变量。

You get this error because you want to instantiate some tf.Variable s during the update_state function.您收到此错误是因为您想在 update_state 函数期间实例化一些tf.Variable When instantiate object from the class Precision and Recall, you are creating some tf.Variable s.从 Precision 和 Recall 类实例化对象时,您正在创建一些tf.Variable

Instantiate the objects in the constructor, and call them in the update_state function:在构造函数中实例化对象,并在 update_state 函数中调用它们:

class F1_Score(tf.keras.metrics.Metric):

    def __init__(self, name='f1_score', **kwargs):
        super().__init__(name=name, **kwargs)
        self.f1 = self.add_weight(name='f1', initializer='zeros')
        self.precision_fn = Precision(thresholds=0.5)
        self.recall_fn = Recall(thresholds=0.5)

    def update_state(self, y_true, y_pred, sample_weight=None):
        p = self.precision_fn(y_true, y_pred)
        r = self.recall_fn(y_true, y_pred)
        # since f1 is a variable, we use assign
        self.f1.assign(2 * ((p * r) / (p + r + 1e-6)))

    def result(self):
        return self.f1

    def reset_states(self):
        # we also need to reset the state of the precision and recall objects
        self.precision_fn.reset_states()
        self.recall_fn.reset_states()
        self.f1.assign(0)

Explanation of the behavior :行为说明:

Tensorflow allow to create Variable only on the first call of a tf.function , see the documentation : Tensorflow允许只在第一次调用创建变量tf.function ,请参阅文档

tf.function only allows creating new tf.Variable objects when it is called for the first time tf.function 只允许在第一次调用时创建新的 tf.Variable 对象

Keras metrics are wrapped in a tf.function to allow compatibility with tensorflow v1. Keras 指标包含在 tf.function 中以允许与 tensorflow v1 兼容。 You can find this comment in the code您可以在代码中找到此注释

If update_state is not in eager/tf.function and it is not from a built-in metric, wrap it in tf.function .如果update_state不在eager/tf.function 中并且它不是来自内置指标,请将其包装在tf.function This is so that users writing custom metrics in v1 need not worry about control dependencies and return ops.这是为了让用户在 v1 中编写自定义指标无需担心控制依赖和返回操作。

You also have another bug in your class, is that you override the f1 tf.Variable that you created with the calculation of your f1 score.您的班级中还有另一个错误,即您覆盖了通过计算 f1 分数创建的f1 tf.Variable To update the value of a variable, you need to use assign .要更新变量的值,您需要使用assign And we must not forget to reset the states of the Precision and Recall Metrics objects in use!我们一定不要忘记重置正在使用的 Precision 和 Recall Metrics 对象的状态!

You can use tensorflow-addons which has a built-in method for F1-Score.您可以使用tensorflow-addons ,它具有 F1-Score 的内置方法。 (don't forget to pip install tensorflow-addons ) (不要忘记pip install tensorflow-addons

Have a look below:看看下面:

  model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.00001),
                  loss=tf.keras.losses.CategoricalCrossentropy(),
                  metrics=[tf.keras.metrics.CategoricalAccuracy(),
                           tfa.metrics.F1Score(num_classes=n_classes, average='macro'),
                           tfa.metrics.FBetaScore(beta=2.0, num_classes=n_classes, average='macro')])

If you do have a multi-label classification problem, you can change it to:如果您确实有多标签分类问题,可以将其更改为:

  model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.00001),
                      loss=tf.keras.losses.BinaryCrossentropy(),
                      metrics=[tf.keras.metrics.BinaryAccuracy(),
                               tfa.metrics.F1Score(num_classes=1, average='macro',threshold=0.5),
                               tfa.metrics.FBetaScore(beta=2.0, num_classes=1, average='macro',threshold=0.5)])

This is my code scoring f1 for Tensorflow 2.0:这是我为 Tensorflow 2.0 评分 f1 的代码:

class F1Score(tf.keras.metrics.Metric):
  def __init__(self, name='F1Score', **kwargs):
    super(F1Score, self).__init__(name=name, **kwargs)
    self.f1score = self.add_weight(name='F1Score', initializer='zeros')
    self.count = self.add_weight(name='F1ScoreCount', initializer='zeros')

  def update_state(self, y_true, y_pred, sample_weight=None):
    y_true = tf.cast(y_true, tf.bool)
    y_pred = tf.cast(y_pred, tf.bool)

    true_positives = tf.logical_and(tf.equal(y_true, True), tf.equal(y_pred, True))
    true_positives = tf.cast(true_positives, self.dtype)
    count_true_positives = tf.reduce_sum(true_positives)

    possible_positives = tf.cast(y_true, self.dtype)
    count_possible_positives = tf.reduce_sum(possible_positives)

    predicted_positives = tf.cast(y_pred, self.dtype)
    count_predicted_positives = tf.reduce_sum(predicted_positives)

    precision = count_true_positives / (count_predicted_positives + K.epsilon())
    recall = count_true_positives / (count_possible_positives + K.epsilon())
    f1_cal = 2*(precision*recall)/(precision + recall + K.epsilon())

    self.count.assign_add(1)
    a = 1.0 / self.count
    b = 1.0 - a
    self.f1score.assign(a*f1_cal+b*self.f1score)

  def result(self):
    return self.f1score

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM