【问题标题】:Custom f1_score metric in tensorflow张量流中的自定义 f1_score 指标
【发布时间】:2023-12-16 05:39:01
【问题描述】:

我想为 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)

我遇到了一个错误:

ValueError: tf.function-decorated 函数试图创建变量 非第一次通话。

【问题讨论】:

    标签: tensorflow machine-learning keras metrics


    【解决方案1】:

    您收到此错误是因为您想在 update_state 函数期间实例化一些 tf.Variables。当从 Precision 和 Recall 类实例化对象时,您正在创建一些 tf.Variables。

    在构造函数中实例化对象,并在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)
    

    行为说明:

    Tensorflow 只允许在第一次调用 tf.function 时创建变量,请参阅 documentation

    tf.function 只允许在第一次调用时创建新的 tf.Variable 对象

    Keras 指标被包装在一个 tf.function 中,以允许与 tensorflow v1 兼容。你可以在code找到这条评论

    如果update_state 不在 eager/tf.function 中并且不是来自 内置指标,将其包装在 tf.function 中。这是为了让用户写作 v1 中的自定义指标无需担心控制依赖和 返回操作。

    您的班级还有另一个错误,就是您覆盖了您在计算 f1 分数时创建的 f1 tf.Variable。要更新变量的值,您需要使用assign。而且我们不能忘记重置正在使用的 Precision 和 Recall Metrics 对象的状态!

    【讨论】:

    • 我在 reset_states() 中收到错误 AttributeError: 'Tensor' object has no attribute 'assign!!
    • 我也使用了tf.compat.v1.assign,得到了同样的错误
    • @BetterEnglish 查看我的更新。我错过了您代码中的一些错误。
    【解决方案2】:

    您可以使用tensorflow-addons,它具有内置的 F1-Score 方法。 (别忘了pip install tensorflow-addons

    请看下面:

      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')])
    

    如果你确实有多标签分类问题,你可以改成:

      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)])
    

    【讨论】:

    • 顺便说一句,您是否尝试过使用内置指标来查看它们的工作原理?
    • 这些指标适用于二元/多类/多标签分类。
    • tfa.metrics.F1ScoreFBetaScore有什么区别?
    • F1 分数是 FBetaScore 的一个特例,其中 Beta == 1。
    • 谢谢你,你能看看这个问题*.com/questions/66420994/…
    【解决方案3】:

    这是我为 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
    

    【讨论】: