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

基于视网膜图像的EfficientNet多标签分类模型sklearn指标异常问题求助

问题分析与解决方案

咱们先拆解你遇到的两个核心问题:指标数值完全相同和混淆矩阵异常(模型输出单一类别),逐个来解决:


一、指标数值完全相同的原因与修复

你在计算sklearn指标时用了average='micro',但对每个标签单独做二分类评估的场景来说,这个参数用错了:

问题根源

average='micro'是多分类任务的全局平均方式,它会把所有类别的TP、FP、FN合并计算。但你现在是针对每个标签单独判断"是/否",属于二分类场景,应该用average='binary'(这也是sklearn二分类的默认值)。

当模型所有预测结果都是同一类别时(比如第一个类全预测为0),micro平均下的准确率、精确率、召回率、F1值会巧合地相等——因为此时全局正确数就是TN(或TP),而其他指标在遇到除以零的情况时被sklearn做了特殊处理,最终和准确率趋同。

修复代码

把每个指标的average='micro'去掉(默认就是binary),或者显式指定average='binary':

from sklearn.metrics import confusion_matrix, accuracy_score, f1_score, precision_score, recall_score

# 针对第一个标签(DR)计算指标
y_true = val_generator.labels[:,0]
y_pred = [round(x) for x in val_pred[:,0]]

print(f'Accuracy = {accuracy_score(y_true, y_pred)}')
# 使用二分类默认的average方式
print(f"F1 = {f1_score(y_true, y_pred)}")
print(f"Precision = {precision_score(y_true, y_pred)}")
print(f"Recall = {recall_score(y_true, y_pred)}")
print('Confusion matrix =')
print(confusion_matrix(y_true, y_pred))

这样就能得到每个指标的正确数值,不会再全部相等。


二、混淆矩阵异常(模型输出单一类别)的原因与修复

你的混淆矩阵显示模型要么全输出0(前两个类),要么全输出1(第三个类),说明模型根本没学到有效特征,核心原因可能有这几点:

1. 预训练模型未加载权重

你初始化EfficientNetB7时用了weights=None,这意味着模型是随机初始化的,没有用ImageNet预训练权重。医学图像数据量通常不大,随机初始化的大模型很难快速收敛,甚至根本学不到东西。

修复:加载预训练权重

把weights=None改成weights='imagenet':

conv_base = EfficientNetB7(weights='imagenet', include_top=False, input_shape=input_shape)

2. 训练参数设置不合理

  • steps_per_epoch=10:这个值太小了,如果你的训练数据集较大,每轮只训练10个batch,模型得不到足够的训练信号。建议设置为len(train_generator),让模型每轮遍历全部训练数据。
  • 学习率衰减设置:ExponentialDecay的decay_steps=100000,结合小的steps_per_epoch,会导致学习率几乎不会下降,加上只有100轮,模型可能还没收敛。

修复训练参数:

history = en_model.fit(
    train_generator,
    steps_per_epoch=len(train_generator),  # 遍历全部训练数据
    epochs=100,
    validation_data=val_generator,
    validation_freq=1,
    verbose=1,
    callbacks=[tensorboard_callbacks],
    use_multiprocessing=True,
    workers=4
)

3. 数据生成器正确性验证

需要确认几个关键点:

  • val_generator.labels是否是正确的多标签格式?比如每个样本的标签是[0,1,0]这样的数组,代表该样本属于青光眼类。
  • val_pred是否是模型对验证集的正确输出?可以打印几个样本的val_pred值,看看是否都趋近于0或1(如果是,说明模型输出极端,没有区分度)。
  • 数据是否存在严重类别不平衡?比如第一个类的正样本太少,导致模型倾向于预测负类。可以用val_generator.labels[:,0].sum()查看正样本数量是否合理。

4. 模型微调的可能性

当模型用预训练权重训练一段时间后,可以尝试解冻卷积基的部分层进行微调,让模型更好适配你的视网膜图像数据:

# 训练几轮后解冻卷积基的顶部几层
conv_base.trainable = True
# 冻结前N层,只训练顶部的层
for layer in conv_base.layers[:-20]:
    layer.trainable = False

# 重新编译模型,使用更小的学习率
en_model.compile(
    loss='binary_crossentropy',
    optimizer=optimizers.Adam(learning_rate=1e-6),  # 微调时学习率要更小
    metrics=['accuracy']
)

# 继续训练
history_fine = en_model.fit(
    train_generator,
    steps_per_epoch=len(train_generator),
    epochs=150,  # 多训练几轮
    initial_epoch=history.epoch[-1],
    validation_data=val_generator,
    verbose=1
)

三、Keras内置指标的替代方案

如果你怀疑是sklearn指标库的问题,可以直接用Keras的内置指标在训练时监控,或者在验证后计算:

1. 训练时添加更多指标

from tensorflow.keras import metrics

en_model.compile(
    loss='binary_crossentropy',
    optimizer=optimizers.Adam(learning_rate=lr_schedule),
    metrics=['accuracy', 
             metrics.Precision(name='precision'),
             metrics.Recall(name='recall'),
             metrics.F1Score(name='f1', average='macro', num_classes=3)]
)

2. 验证后计算单标签指标

# 计算每个标签的精确率和召回率
precision = metrics.Precision()
recall = metrics.Recall()

# 针对第一个标签
precision.update_state(val_generator.labels[:,0], val_pred[:,0])
recall.update_state(val_generator.labels[:,0], val_pred[:,0])

print(f"Precision for DR: {precision.result().numpy()}")
print(f"Recall for DR: {recall.result().numpy()}")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 21:17:40