TensorFlow 2.5下指定类别F1 Score无自定义函数实现问题
方案1:使用TensorFlow Addons F1Score 单独统计指定类别(无需自定义函数)
你可以直接和之前的Precision/Recall用法一致,为每个目标类别单独实例化F1Score指标,指定class_id参数即可,和原有指标的使用逻辑完全对齐:
import tensorflow as tf import tensorflow_addons as tfa metrics = [ # 原有已在使用的召回、精确率指标 tf.keras.metrics.Recall(class_id=1, name='Bkwd_R'), tf.keras.metrics.Recall(class_id=2, name='Fwd_R'), tf.keras.metrics.Precision(class_id=1, name='Bkwd_P'), tf.keras.metrics.Precision(class_id=2, name='Fwd_P'), # 新增类别1、2的单独F1指标 tfa.metrics.F1Score(num_classes=3, class_id=1, name='Bkwd_F1'), tfa.metrics.F1Score(num_classes=3, class_id=2, name='Fwd_F1') ]
注意:如果你的模型输出是未经过softmax的logits,需要给F1Score参数加上
from_logits=True,否则预测结果判定错误会导致F1值为0。
这个方式会在训练、验证、评估阶段单独输出每个类别的F1值,自动忽略类别0的统计,也不会出现多类别结果被默认全局平均的问题。
方案2:统计类别1和2的平均F1
如果你需要这两个类别的综合平均F1(默认macro平均,排除类别0),可以用以下兼容TensorFlow训练流程的自定义指标实现:
import tensorflow as tf from tensorflow.keras import metrics class TargetClassesF1(metrics.Metric): def __init__(self, target_class_ids=[1,2], num_classes=3, average='macro', name='target_f1', **kwargs): super().__init__(name=name, **kwargs) self.target_class_ids = target_class_ids self.num_classes = num_classes self.average = average # 初始化计数变量 self.tp = self.add_weight(name='tp', shape=(num_classes,), initializer='zeros') self.fp = self.add_weight(name='fp', shape=(num_classes,), initializer='zeros') self.fn = self.add_weight(name='fn', shape=(num_classes,), initializer='zeros') def update_state(self, y_true, y_pred, sample_weight=None): y_true = tf.argmax(y_true, axis=-1) if len(y_true.shape) > 1 and y_true.shape[-1] == self.num_classes else y_true y_pred = tf.argmax(y_pred, axis=-1) for cls in self.target_class_ids: true_pos = tf.reduce_sum(tf.cast(tf.logical_and(y_true == cls, y_pred == cls), self.dtype)) false_pos = tf.reduce_sum(tf.cast(tf.logical_and(y_true != cls, y_pred == cls), self.dtype)) false_neg = tf.reduce_sum(tf.cast(tf.logical_and(y_true == cls, y_pred != cls), self.dtype)) self.tp[cls].assign_add(true_pos) self.fp[cls].assign_add(false_pos) self.fn[cls].assign_add(false_neg) def result(self): f1_scores = [] eps = tf.keras.backend.epsilon() for cls in self.target_class_ids: precision = self.tp[cls] / (self.tp[cls] + self.fp[cls] + eps) recall = self.tp[cls] / (self.tp[cls] + self.fn[cls] + eps) f1 = 2 * (precision * recall) / (precision + recall + eps) f1_scores.append(f1) if self.average == 'macro': return tf.reduce_mean(f1_scores) return f1_scores def reset_state(self): self.tp.assign(tf.zeros_like(self.tp)) self.fp.assign(tf.zeros_like(self.fp)) self.fn.assign(tf.zeros_like(self.fn))
调用方式直接加入metrics列表即可,适配model.fit、model.evaluate全流程:
metrics = [ # 原有指标 tf.keras.metrics.Recall(class_id=1, name='Bkwd_R'), tf.keras.metrics.Recall(class_id=2, name='Fwd_R'), tf.keras.metrics.Precision(class_id=1, name='Bkwd_P'), tf.keras.metrics.Precision(class_id=2, name='Fwd_P'), # 新增目标类别平均F1 TargetClassesF1(target_class_ids=[1,2], num_classes=3, average='macro', name='target_f1') ]
内容的提问来源于stack exchange,提问作者Panda
相关产品推荐
相关产品推荐

