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

乳腺X光图像二分类:训练与验证准确率均停滞在50%

乳腺X光二分类任务准确率停滞50%的问题排查与修复方案

核心问题分析与修复

1. 输出层连接错误

你的代码中,最终输出层直接连接到了Flatten后的特征x,完全跳过了中间定义的x_class全连接层和Dropout层,这导致模型分类能力被严重削弱,相当于只用了一个简单线性分类器,无法有效学习复杂特征。

修正:
将输出层改为从x_class连接:

output = Dense(1, activation='sigmoid')(x_class)  # 原代码是(x),改为(x_class)

2. 损失函数参数不匹配

你使用了sigmoid激活函数,但损失函数设置了from_logits=True,这会导致损失计算逻辑错误——from_logits=True是针对未经过激活函数的原始输出设计的,而sigmoid已经将输出压缩到0-1区间,两者冲突会导致模型无法正确优化。

修正:
将损失函数的from_logits改为False:

model.compile(
    loss=tf.keras.losses.BinaryCrossentropy(from_logits=False),  # 修改为False
    optimizer=optimizer,
    metrics=["accuracy"],
)

3. 输入图像未归一化

EfficientNetV2的预训练权重是基于归一化到0-1区间的图像训练的,但你的数据生成器注释掉了rescale=1./255,导致输入图像还是0-255的原始像素值,这会让预训练模型的特征提取模块无法正常工作,输出的特征毫无区分度。

修正:
在训练和验证数据生成器中启用归一化:

train_datagen = ImageDataGenerator(
    rescale=1./255,  # 取消注释
    rotation_range=20,
    width_shift_range=0.1,
    height_shift_range=0.1,
    zoom_range=0.2,
    horizontal_flip=True,
    vertical_flip=False,
    fill_mode='nearest'
)

valid_datagen = ImageDataGenerator(
    rescale=1./255  # 仅做归一化,不做数据增强
)

4. 验证集不应使用数据增强

验证集的作用是评估模型的泛化能力,使用数据增强会导致验证数据分布与真实测试场景不符,同时增加不必要的计算开销。

修正:
验证集生成器仅保留归一化操作,移除所有数据增强参数(旋转、平移、缩放等)。

5. 学习率过低

1e-6的学习率太小,模型参数几乎无法更新,导致训练停滞。对于预训练模型的微调,初始学习率建议设置为1e-4或1e-5,后续可根据训练情况调整。

修正:
调整优化器的学习率:

optimizer = Adam(learning_rate=1e-4, beta_1=0.9, beta_2=0.999)

6. 预训练模型的训练策略优化

默认情况下,EfficientNetV2的所有层都是可训练的,直接训练可能导致梯度不稳定或预训练特征被破坏。建议先冻结底层特征提取层,仅训练顶层分类器,再解冻部分层进行微调。

示例:

# 冻结预训练模型的所有层
efficientnet_model.trainable = False

# 先训练顶层分类器
history = model.fit(train_generator, epochs=10, validation_data=valid_generator)

# 解冻最后10层进行微调
efficientnet_model.trainable = True
fine_tune_at = len(efficientnet_model.layers) - 10
for layer in efficientnet_model.layers[:fine_tune_at]:
    layer.trainable = False

# 微调时降低学习率
optimizer = Adam(learning_rate=1e-5)
model.compile(
    loss=tf.keras.losses.BinaryCrossentropy(from_logits=False),
    optimizer=optimizer,
    metrics=["accuracy"],
)

history_fine = model.fit(train_generator, epochs=20, initial_epoch=history.epoch[-1], validation_data=valid_generator)

7. 训练轮数不足

10轮训练对于预训练模型的微调来说太少,尤其是在初始学习率较低的情况下,模型还未进入有效收敛阶段。建议先训练10-20轮顶层,再微调20-30轮。

完整修正后的代码

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.layers import Input, Flatten, Dense, Dropout
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam

# Loading the Data
train_df = new_df.sample(frac=0.7, random_state=0)  # 70% of the data for training
valid_df = new_df.drop(train_df.index)  # remaining 30% for validation and testing

batch_size = 32

# Create a data generator for training data
train_datagen = ImageDataGenerator(
    rescale=1./255,
    rotation_range=20,
    width_shift_range=0.1,
    height_shift_range=0.1,
    zoom_range=0.2,
    horizontal_flip=True,
    vertical_flip=False,  # 乳腺X光通常不使用垂直翻转
    fill_mode='nearest'
)
train_generator = train_datagen.flow_from_dataframe(
    dataframe=train_df,
    x_col='images_filepath',
    y_col='labels',
    target_size=(224, 224),
    batch_size=batch_size,
    shuffle=True,
    class_mode='binary'
)

# Create a data generator for validation data
valid_datagen = ImageDataGenerator(
    rescale=1./255  # 仅做归一化,不做数据增强
)

valid_generator = valid_datagen.flow_from_dataframe(
    dataframe=valid_df,
    x_col='images_filepath',
    y_col='labels',
    target_size=(224, 224),
    batch_size=batch_size,
    shuffle=False,  # 验证集不需要打乱
    class_mode='binary'
)

# Build the model
efficientnet_model = keras.applications.EfficientNetV2S(
    include_top=False,
    weights="imagenet",
    input_tensor=Input(shape=(224, 224, 3))
)

# 先冻结预训练层
efficientnet_model.trainable = False

x = Flatten()(efficientnet_model.output)
x_class = Dense(256, activation='relu')(x)
x_class = Dropout(0.5)(x_class)
output = Dense(1, activation='sigmoid')(x_class)

model = Model(inputs=efficientnet_model.input, outputs=output)

# 编译并训练顶层分类器
optimizer = Adam(learning_rate=1e-4)
model.compile(
    loss=tf.keras.losses.BinaryCrossentropy(from_logits=False),
    optimizer=optimizer,
    metrics=["accuracy"],
)

history = model.fit(train_generator, epochs=10, validation_data=valid_generator)

# 解冻部分层进行微调
efficientnet_model.trainable = True
fine_tune_at = len(efficientnet_model.layers) - 10
for layer in efficientnet_model.layers[:fine_tune_at]:
    layer.trainable = False

# 微调时使用更小的学习率重新编译
optimizer = Adam(learning_rate=1e-5)
model.compile(
    loss=tf.keras.losses.BinaryCrossentropy(from_logits=False),
    optimizer=optimizer,
    metrics=["accuracy"],
)

# 继续训练
history_fine = model.fit(
    train_generator,
    epochs=30,
    initial_epoch=history.epoch[-1],
    validation_data=valid_generator
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 09:17:08