You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在TensorFlow中计算F1分数?附准确率代码求对应实现

如何在TensorFlow中实现对应准确率代码的F1分数计算

嘿,刚接触TensorFlow的话,从准确率转F1分数其实不难,先理清楚F1的核心逻辑——它是**精确率(Precision)和召回率(Recall)**的调和平均,公式是:
F1 = 2 * (Precision * Recall) / (Precision + Recall)

结合你现有的准确率代码,我们可以复用其中的标签提取逻辑,再逐步计算F1需要的指标:

第一步:提取预测标签和真实标签

和你计算准确率时的逻辑完全一致,先把模型输出和真实标签转换成离散的类别索引:

# 提取output_1和y_1对应的标签
preds_1 = tf.argmax(output_1, axis=-1)
true_1 = tf.argmax(y_1, axis=-1)

# 同理处理output_2和y_2
preds_2 = tf.argmax(output_2, axis=-1)
true_2 = tf.argmax(y_2, axis=-1)

第二步:二分类场景下的F1计算

如果你的任务是二分类(比如只有0和1两个类别),可以直接计算真阳性(TP)、假阳性(FP)、假阴性(FN)这三个核心指标,再推导F1:

# 定义一个小的epsilon,防止除以0出现NaN
epsilon = 1e-7

# 计算output_1对应的TP/FP/FN(假设类别1是正类)
tp_1 = tf.reduce_sum(tf.cast(tf.logical_and(tf.equal(preds_1, 1), tf.equal(true_1, 1)), tf.float32))
fp_1 = tf.reduce_sum(tf.cast(tf.logical_and(tf.equal(preds_1, 1), tf.equal(true_1, 0)), tf.float32))
fn_1 = tf.reduce_sum(tf.cast(tf.logical_and(tf.equal(preds_1, 0), tf.equal(true_1, 1)), tf.float32))

# 计算精确率和召回率
precision_1 = tp_1 / (tp_1 + fp_1 + epsilon)
recall_1 = tp_1 / (tp_1 + fn_1 + epsilon)

# 最终计算F1分数,和准确率一样给个name方便后续调用
f1_1 = 2 * (precision_1 * recall_1) / (precision_1 + recall_1 + epsilon)
f1_1 = tf.identity(f1_1, name="f1_1")

# 同理复制上述代码,替换变量为preds_2/true_2即可得到f1_2
tp_2 = tf.reduce_sum(tf.cast(tf.logical_and(tf.equal(preds_2, 1), tf.equal(true_2, 1)), tf.float32))
fp_2 = tf.reduce_sum(tf.cast(tf.logical_and(tf.equal(preds_2, 1), tf.equal(true_2, 0)), tf.float32))
fn_2 = tf.reduce_sum(tf.cast(tf.logical_and(tf.equal(preds_2, 0), tf.equal(true_2, 1)), tf.float32))

precision_2 = tp_2 / (tp_2 + fp_2 + epsilon)
recall_2 = tp_2 / (tp_2 + fn_2 + epsilon)

f1_2 = 2 * (precision_2 * recall_2) / (precision_2 + recall_2 + epsilon)
f1_2 = tf.identity(f1_2, name="f1_2")

第三步:多分类场景下的F1计算

如果是多分类任务,通常会计算Macro-F1(每个类别的F1取平均),实现思路是遍历每个类别单独计算TP/FP/FN,再求平均:

epsilon = 1e-7
# 获取类别数量(从真实标签的最后一维维度获取)
num_classes = tf.shape(y_1)[-1]

# 初始化存储每个类别TP/FP/FN的数组
tp_1 = tf.zeros(num_classes, dtype=tf.float32)
fp_1 = tf.zeros(num_classes, dtype=tf.float32)
fn_1 = tf.zeros(num_classes, dtype=tf.float32)

# 遍历每个类别计算指标
for cls in range(num_classes):
    # 判断当前样本是否被预测为该类别、是否真实属于该类别
    is_pred_cls = tf.equal(preds_1, cls)
    is_true_cls = tf.equal(true_1, cls)
    
    # 更新对应类别的TP/FP/FN
    tp_1 = tf.tensor_scatter_nd_update(tp_1, [[cls]], [tf.reduce_sum(tf.cast(tf.logical_and(is_pred_cls, is_true_cls), tf.float32))])
    fp_1 = tf.tensor_scatter_nd_update(fp_1, [[cls]], [tf.reduce_sum(tf.cast(tf.logical_and(is_pred_cls, tf.logical_not(is_true_cls)), tf.float32))])
    fn_1 = tf.tensor_scatter_nd_update(fn_1, [[cls]], [tf.reduce_sum(tf.cast(tf.logical_and(tf.logical_not(is_pred_cls), is_true_cls), tf.float32))])

# 计算每个类别的精确率、召回率和F1
precision_per_cls_1 = tp_1 / (tp_1 + fp_1 + epsilon)
recall_per_cls_1 = tp_1 / (tp_1 + fn_1 + epsilon)
f1_per_cls_1 = 2 * (precision_per_cls_1 * recall_per_cls_1) / (precision_per_cls_1 + recall_per_cls_1 + epsilon)

# 取平均得到Macro-F1
macro_f1_1 = tf.reduce_mean(f1_per_cls_1, name="macro_f1_1")

# 同样的逻辑可以复制得到macro_f1_2

小提示:用TensorFlow内置指标简化代码

如果你不想手动实现,也可以用TF内置的tf.keras.metrics.F1Score,不过要注意它的输入格式(需要是one-hot编码的标签或类别索引),比如:

# 初始化F1指标(多分类选"macro",二分类选"binary")
f1_metric_1 = tf.keras.metrics.F1Score(num_classes=num_classes, average='macro')
# 更新指标(注意如果y_1已经是one-hot,直接传;如果是类别索引,要设置y_true的格式)
f1_metric_1.update_state(y_1, output_1)
# 获取结果
f1_1 = f1_metric_1.result()

不过这种方式更适合在训练循环中使用,和你现有手写准确率的风格略有不同,所以优先推荐前面的手动实现,更贴合你当前的代码逻辑。

内容的提问来源于stack exchange,提问作者William Scott

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:49:36