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

TensorFlow Keras类别精度计算报错:重复指标名称acc1

问题:编译Keras模型时出现重复指标名称错误

在训练数据上计算类别级准确率,已安装最新版TensorFlow和Keras,但编译模型时抛出错误:ValueError: Found two metrics with the same name: acc1

错误信息

raise ValueError('Found two metrics with the same name: {}'.format(
    ValueError: Found two metrics with the same name: acc1

相关代码

resnet_model.summary()
from keras import backend as K

#interesting_class_id = 0  # Choose the class of interest

def single_class_accuracy(interesting_class_id):

    def acc1(y_true, y_pred):

        class_id_true = K.argmax(y_true)
        class_id_preds = K.argmax(y_pred)
        accuracy_mask = K.cast(K.equal(class_id_preds, interesting_class_id), 'int32')

        class_acc_tensor = K.cast(K.equal(class_id_true, class_id_preds), 'int32') * accuracy_mask
        class_acc = K.cast(K.sum(class_acc_tensor), 'float32') / K.cast(K.maximum(K.sum(accuracy_mask), 1), 'float32')
        return class_acc
    return acc1


def single_class_recall(interesting_class_id):

    def recall(y_true, y_pred):

        class_id_true = K.argmax(y_true, axis=-1)
        class_id_pred = K.argmax(y_pred, axis=-1)
        recall_mask = K.cast(K.equal(class_id_true, interesting_class_id), 'int32')

        class_recall_tensor = K.cast(K.equal(class_id_true, class_id_pred), 'int32') * recall_mask
        class_recall = K.cast(K.sum(class_recall_tensor), 'float32') / K.cast(K.maximum(K.sum(recall_mask), 1), 'float32')
        return recall
    return recall


def single_class_precision(interesting_class_id):
    def prec(y_true, y_pred):
        class_id_true = K.argmax(y_true, axis=-1)
        class_id_pred = K.argmax(y_pred, axis=-1)
        precision_mask = K.cast(K.equal(class_id_pred, interesting_class_id), 'int32') 
        class_prec_tensor = K.cast(K.equal(class_id_true, class_id_pred), 'int32') * precision_mask 
        class_prec = K.cast(K.sum(class_prec_tensor), 'float32') / K.cast(K.maximum(K.sum(precision_mask), 1), 'float32')
        return class_prec
    return prec

resnet_model.compile(optimizer=Adam(lr=0.01),loss='binary_crossentropy',metrics=[                                          
                         'accuracy',
                         single_class_accuracy(0),  
                         single_class_accuracy(1),
                         single_class_recall(0),
                         single_class_recall(1),
                         single_class_precision(0),  
                        single_class_precision(1)
                       ])


resnet_model.save('my_model')

history = resnet_model.fit(train_ds, validation_data=val_ds, epochs=20)

解决方案

问题根源

调用single_class_accuracy(0)和single_class_accuracy(1)时,返回的内部函数名称都是acc1,Keras默认用函数名作为指标的唯一标识,导致重复命名冲突。同理,single_class_recall和single_class_precision也存在相同隐患。

两种修复方法

方法1:给内部函数设置唯一名称

修改自定义指标函数,为每个返回的内部函数添加包含类别ID的唯一名称:

def single_class_accuracy(interesting_class_id):
    def acc(y_true, y_pred):
        class_id_true = K.argmax(y_true)
        class_id_preds = K.argmax(y_pred)
        accuracy_mask = K.cast(K.equal(class_id_preds, interesting_class_id), 'int32')

        class_acc_tensor = K.cast(K.equal(class_id_true, class_id_preds), 'int32') * accuracy_mask
        class_acc = K.cast(K.sum(class_acc_tensor), 'float32') / K.cast(K.maximum(K.sum(accuracy_mask), 1), 'float32')
        return class_acc
    # 设置唯一函数名
    acc.__name__ = f'single_class_acc_{interesting_class_id}'
    return acc

def single_class_recall(interesting_class_id):
    def recall(y_true, y_pred):
        class_id_true = K.argmax(y_true, axis=-1)
        class_id_pred = K.argmax(y_pred, axis=-1)
        recall_mask = K.cast(K.equal(class_id_true, interesting_class_id), 'int32')

        class_recall_tensor = K.cast(K.equal(class_id_true, class_id_pred), 'int32') * recall_mask
        class_recall = K.cast(K.sum(class_recall_tensor), 'float32') / K.cast(K.maximum(K.sum(recall_mask), 1), 'float32')
        return class_recall
    recall.__name__ = f'single_class_recall_{interesting_class_id}'
    return recall

def single_class_precision(interesting_class_id):
    def prec(y_true, y_pred):
        class_id_true = K.argmax(y_true, axis=-1)
        class_id_pred = K.argmax(y_pred, axis=-1)
        precision_mask = K.cast(K.equal(class_id_pred, interesting_class_id), 'int32') 
        class_prec_tensor = K.cast(K.equal(class_id_true, class_id_pred), 'int32') * precision_mask 
        class_prec = K.cast(K.sum(class_prec_tensor), 'float32') / K.cast(K.maximum(K.sum(precision_mask), 1), 'float32')
        return class_prec
    prec.__name__ = f'single_class_prec_{interesting_class_id}'
    return prec

方法2:编译时用字典指定指标名称

无需修改指标函数,直接在compile的metrics参数中用字典自定义每个指标的名称,键为唯一名称,值为指标函数:

resnet_model.compile(optimizer=Adam(learning_rate=0.01), loss='binary_crossentropy', metrics={
    'accuracy': 'accuracy',
    'class_0_acc': single_class_accuracy(0),  
    'class_1_acc': single_class_accuracy(1),
    'class_0_recall': single_class_recall(0),
    'class_1_recall': single_class_recall(1),
    'class_0_prec': single_class_precision(0),  
    'class_1_prec': single_class_precision(1)
})

这种方式更直观,能直接控制训练日志中显示的指标名称,推荐使用。

额外提示

  • TensorFlow 2.x+版本中,Adam(lr=0.01)已弃用,需改为Adam(learning_rate=0.01),避免警告。
  • 当前使用binary_crossentropy损失,但指标计算中调用了K.argmax,若为二分类任务,输出应为单节点sigmoid,此时K.argmax会因维度不匹配报错;若为多分类任务,建议改用categorical_crossentropy或sparse_categorical_crossentropy,需确认标签格式与损失函数匹配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 23:55:23