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

TensorFlow 2.0 Keras自定义Hamming Loss指标打印值与返回值不匹配

问题:自定义Hamming Loss手动打印值与Keras训练指标值不一致

我正在开发一个输出多种疾病标签的深度学习模型,输入为文本(医生病历)和标量临床数据,属于稀疏多标签分类任务。由于朴素准确率测试(生成与真实标签同形状的零矩阵)准确率约92%,因此选用Hamming Loss(错误标签数/总标签数)作为评估指标。

为验证自定义Hamming Loss函数正确性,我在训练中使用tf.print输出计算值,但设置batch_size=1时,手动打印的Hamming Loss值与函数返回的训练显示值不匹配。以下是我的代码及训练输出:

自定义Hamming Loss函数

def hamming_loss(y, y_hat, thresh=0.8, use_thresh=True, mode='multilabel'): 
    if use_thresh is False: 
        threshold = tf.reduce_max(y_hat, axis=-1, keepdims=True) 
        # make sure [0, 0, 0] doesn't become [1, 1, 1] 
        # Use abs(x) > eps, instead of x != 0 to check for zero 
        y_pred = tf.logical_and(y_hat >= threshold, tf.abs(y_hat) > 1e-12) 
    else: 
        y_pred = y_hat > thresh 
    y_true = tf.cast(y, tf.int32) 
    y_pred = tf.cast(y_pred, tf.int32) 
    shape = tf.cast(tf.shape(y)[-1], tf.int32) 
    #tf.print(y, summarize = -1) 
    #tf.print('\n') 
    #tf.print(y_pred, summarize = -1) 
    #tf.print(shape) 
    #tf.print(y_hat, summarize = -1) 
    if mode == 'multiclass': 
        nonzero = tf.cast( tf.math.count_nonzero(y_true * y_pred, axis=-1), tf.float32) 
        return 1.0 - nonzero 
    else: 
        nonzero = tf.cast( tf.math.count_nonzero(y_true - y_pred, axis=-1), tf.int32) 
        #tf.print(nonzero, summarize=-1) 
        tf.print(nonzero/shape, summarize=-1) 
        return nonzero/shape

网络结构

nlp_input = Input(shape=(1024,), name='nlp_input') 
lr = LeakyReLU(alpha=0.1) 
meta_input = Input(shape=(24,), name='meta_input') 
hidden_m1 = Dense(2000, activation=lr,kernel_regularizer = L1L2(l1=0.0001) )(meta_input) 
hidden_m2 = Dense(200, activation=lr, )(hidden_m1) 
hidden_m3 = Dense(100, activation=lr, )(hidden_m2) 
hidden_m4 = Dense(10, activation='relu', )(hidden_m3) 
hidden1 = Dense(5000, activation=lr,kernel_regularizer = L1L2(l1=0.0001) )(nlp_input) 
hidden2 = Dense(3000, activation=lr )(hidden1) 
hidden3 = Dense(3000, activation=lr, )(hidden2) 
hidden4 = Dense(768, activation=lr, )(hidden3) 
hidden5 = Dense(500, activation=lr, )(hidden4) 
hidden6 = Dense(100, activation='relu')(hidden5) 
conc = concatenate([hidden6, hidden_m4]) 
conch1 = Dense(1000, activation =lr, kernel_regularizer = L1L2(l1=0.0001))(conc) 
conch2 = Dense(500, activation =lr)(conch1) 
conch3 = Dense(500, activation =lr)(conch2) 
conch4 = Dense(100, activation ='relu')(conch3) 
acti = Dense(15, 'sigmoid')(conch4) 
model = Model(inputs=[nlp_input, meta_input], outputs=[acti]) 
opt = tf.keras.optimizers.Adam(learning_rate=1e-4) 
model.compile(loss=focal_loss(), optimizer=opt,metrics=[tf_prec,tf_rec, hamming_loss]) 
print(model.summary())

训练输出示例

Train on 1315 samples, validate on 328 samples 
0.066666666666666666 
1/1315 [..............................] - ETA: 12:54 - loss: 6.6111 - tf_prec: 0.2000 - tf_rec: 1.0000 - hamming_loss: 0.0667
0.066666666666666666 
2/1315 [..............................] - ETA: 8:16 - loss: 6.5891 - tf_prec: 0.2000 - tf_rec: 1.0000 - hamming_loss: 0.0667 
0.13333333333333333 
0.13333333333333333 
4/1315 [..............................] - ETA: 4:27 - loss: 6.5504 - tf_prec: 0.2500 - tf_rec: 1.0000 - hamming_loss: 0.1000
0.26666666666666666 
0.2 
6/1315 [..............................] - ETA: 3:10 - loss: 6.5065 - tf_prec: 0.3000 - tf_rec: 1.0000 - hamming_loss: 0.1444
0.066666666666666666 
0.2 
8/1315 [..............................] - ETA: 2:31 - loss: 6.4611 - tf_prec: 0.3187 - tf_rec: 1.0000 - hamming_loss: 0.1417
0.2 
0.066666666666666666 
10/1315 [..............................] - ETA: 2:08 - loss: 6.4265 - tf_prec: 0.2800 - tf_rec: 0.9000 - hamming_loss: 0.1400
0.066666666666666666 
0.066666666666666666 
12/1315 [..............................] - ETA: 1:52 - loss: 6.3848 - tf_prec: 0.2542 - tf_rec: 0.8333 - hamming_loss: 0.1278
0.066666666666666666 
0.066666666666666666 

为什么手动打印的Hamming Loss与函数返回的训练指标值不一致?


回答

这是一个很典型的Keras metrics工作机制误解,我来帮你拆解原因:

核心原因:打印的是单样本Loss,训练日志显示的是累积样本平均值

你的自定义hamming_loss函数里,tf.print(nonzero/shape)打印的是每个单独样本的Hamming Loss值;而Keras在训练过程中显示的hamming_loss指标,是从训练开始到当前步骤所有已处理样本的平均值。

我们拿你的训练输出验证一下:

  • 第4步(已处理4个样本),你打印了4个值:0.0666、0.0666、0.1333、0.1333
  • 计算这4个值的平均值:(0.0666+0.0666+0.1333+0.1333)/4 = 0.4/4 = 0.1,正好对应训练日志里的hamming_loss: 0.1000

再看第6步:

  • 6个样本的Loss值分别是:0.0666、0.0666、0.1333、0.1333、0.2666、0.2
  • 平均值:(0.0666*2 + 0.1333*2 + 0.2666 + 0.2)/6 = 0.8664/6 = 0.1444,和日志里的hamming_loss: 0.1444完全匹配

额外注意点:整数除法的潜在问题

你的代码里shape被定义为tf.int32,nonzero也是tf.int32,虽然在TensorFlow中整数除以整数会自动转成浮点数,但为了避免意外(比如某些版本或场景下的整数截断),建议把shape转成tf.float32:

shape = tf.cast(tf.shape(y)[-1], tf.float32)

如何让打印值和日志显示一致?

如果你想让tf.print输出的内容和训练日志里的指标一致,可以在函数里计算当前批次的平均值,或者直接打印累积平均值(不过累积平均值需要借助Keras的metrics状态,更简单的是打印批次平均):

修改后的hamming_loss函数:

def hamming_loss(y, y_hat, thresh=0.8, use_thresh=True, mode='multilabel'): 
    if use_thresh is False: 
        threshold = tf.reduce_max(y_hat, axis=-1, keepdims=True) 
        y_pred = tf.logical_and(y_hat >= threshold, tf.abs(y_hat) > 1e-12) 
    else: 
        y_pred = y_hat > thresh 
    y_true = tf.cast(y, tf.int32) 
    y_pred = tf.cast(y_pred, tf.int32) 
    shape = tf.cast(tf.shape(y)[-1], tf.float32)  # 改为float32
    if mode == 'multiclass': 
        nonzero = tf.cast( tf.math.count_nonzero(y_true * y_pred, axis=-1), tf.float32) 
        return 1.0 - nonzero 
    else: 
        nonzero = tf.cast( tf.math.count_nonzero(y_true - y_pred, axis=-1), tf.float32) 
        sample_loss = nonzero / shape
        # 打印当前批次的平均Loss
        batch_avg = tf.reduce_mean(sample_loss)
        tf.print("Batch avg Hamming Loss:", batch_avg, summarize=-1)
        return sample_loss  # Keras会自动处理累积平均

如果你的batch_size=1,那么批次平均就是单样本值,但日志显示的还是累积平均;如果想让日志显示单样本值,你需要自定义一个Metric类,每次只返回当前样本的值而不累积,但这通常不是我们想要的训练指标展示方式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 16:32:31