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

Keras自定义多分类指标编写疑问:张量处理异常解析

Keras自定义多分类F1指标问题解析

问题背景

我想在Keras中编写自定义多分类指标,尝试时遇到了问题。核心原因是不熟悉训练时的张量运作(Graph模式),但我能用NumPy或Python列表生成混淆矩阵并计算F1分数。

我使用的函数

def f1_scores(y_true,y_pred):

    y_true = K.print_tensor(y_true, message='y_true = ')
    y_pred = K.print_tensor(y_pred, message='y_pred = ')
    print(f"y_true_shape:{K.int_shape(y_true)}")
    print(f"y_pred_shape:{K.int_shape(y_pred)}")

    y_true_f = K.flatten(y_true)
    y_pred_f = K.flatten(y_pred)

    gt = K.argmax(y_true_f)
    pred = K.argmax(y_pred_f)

    print(f"pred_print:{pred}")
    print(f"gt_print:{gt}")

    pred = K.print_tensor(pred, message='pred= ')
    gt = K.print_tensor(gt, message='gt =')
    print(f"pred_shape:{K.int_shape(pred)}")
    print(f"gt_shape:{K.int_shape(gt)}")

    pred_f = K.flatten(pred)
    gt_f = K.flatten(gt)

    pred_f = K.print_tensor(pred_f, message='pred_f= ')
    gt_f = K.print_tensor(gt_f, message='gt_f =')
    print(f"pred_f_shape:{K.int_shape(pred_f)}")
    print(f"gt_f_shape:{K.int_shape(gt_f)}")

    conf_mat = tf.math.confusion_matrix(y_true_f,y_pred_f, num_classes = 14)

    """
    add codes to find F1 score for each class
    """

    # return an arbitrary number, as F1 scores not found yet.
    return 1

输出情况

第1轮epoch初始输出

y_true_shape:(None, 256, 256, 14)
y_pred_shape:(None, 256, 256, 14)
pred_print:Tensor("ArgMax_1:0", shape=(), dtype=int64)
gt_print:Tensor("ArgMax:0", shape=(), dtype=int64)
pred_shape:()
gt_shape:()
pred_f_shape:(1,)
gt_f_shape:(1,)

后续epoch输出示例

y_true =  [[[[1 0 0 ... 0 0 0]
   [1 0 0 ... 0 0 0]
   [1 0 0 ... 0 0 0]
   ...

y_pred =  [[[[0.0889623 0.0624801107 0.0729747042 ... 0.0816219151 0.0735477135 0.0698677748]
   [0.0857798532 0.0721047595 0.0754121244 ... 0.0723947287 0.0728530064 0.0676521733]
   [0.0825942457 0.0670698211 0.0879610255 ... 0.0721599609 0.0845924541 0.0638583601]
   ...

pred=  1283828
gt = 0
pred_f=  [1283828]
gt_f = [0]

我的疑问

  • 为什么pred是单个数字而非代表类别索引的列表?pred_f为什么也是仅含一个数字的列表?
  • gt及gt_f的值为什么是0?我预期它们应该是类别索引的列表。

问题根源与解决方法

核心错误分析

  1. 错误的扁平化操作:
    你的输入形状是(None, 256, 256, 14),对应图像分割任务的输出格式(批次大小、图像高、图像宽、类别数)。直接用K.flatten()把整个张量压成一维,会把每个像素的14个类别概率摊平成一个长向量,完全破坏了“每个像素对应一个类别分布”的结构。

  2. K.argmax()轴参数缺失:
    默认情况下K.argmax()会在整个张量的所有轴上取最大值的索引,而非在类别轴(axis=3)上。这导致你得到的不是每个像素的类别索引,而是整个扁平化张量中最大值的位置(1283828就是这个位置);而gt的值为0,是因为y_true的第一个元素是1,是扁平化张量里最大值的位置。

修正后的代码

import tensorflow as tf
from tensorflow.keras import backend as K

def f1_scores(y_true, y_pred):
    # 在类别轴(axis=3)上获取每个像素的真实类别和预测类别索引
    y_true_idx = K.argmax(y_true, axis=3)
    y_pred_idx = K.argmax(y_pred, axis=3)
    
    # 扁平化索引张量,得到一维的类别索引列表
    y_true_flat = K.flatten(y_true_idx)
    y_pred_flat = K.flatten(y_pred_idx)
    
    # 计算混淆矩阵
    conf_mat = tf.math.confusion_matrix(y_true_flat, y_pred_flat, num_classes=14)
    
    # 计算每个类别的Precision、Recall与F1,加入epsilon避免除以0
    epsilon = K.epsilon()
    true_positives = K.diag(conf_mat)
    predicted_positives = K.sum(conf_mat, axis=0)
    actual_positives = K.sum(conf_mat, axis=1)
    
    precision = true_positives / (predicted_positives + epsilon)
    recall = true_positives / (actual_positives + epsilon)
    
    f1 = 2 * (precision * recall) / (precision + recall + epsilon)
    
    # 返回平均F1,若需每个类别的F1可直接return f1(需适配Keras多输出指标逻辑)
    return K.mean(f1)

补充说明

  • Graph模式下,所有操作都是张量运算,print()只能打印张量的形状信息,K.print_tensor()可在运行时打印张量内容,但正式训练时建议移除,避免拖慢速度。
  • 若要返回每个类别的F1分数,可将return K.mean(f1)改为return f1,但Keras默认仅支持标量指标,这种情况需要自定义tf.keras.metrics.Metric类来实现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 08:25:28