基于EfficientNet的二分类迁移学习准确率停滞在50%的问题
问题描述
使用EfficientNet进行二分类迁移学习时,模型训练的准确率与验证准确率始终维持在50%左右(接近随机猜测水平),尝试调整学习率、批量大小、添加Dropout/全连接层等操作均无改善。
环境与代码信息
目录结构
training ├── label0 └── label1 validation ├── label0 └── label1
图像生成器代码
from tensorflow.keras.preprocessing.image import ImageDataGenerator train_datagen = ImageDataGenerator( rescale=1./255, rotation_range=30, width_shift_range=0.0, height_shift_range=0.0, shear_range=0.0, zoom_range=0.0, horizontal_flip=True, fill_mode='nearest' ) # 验证集无数据增强 validation_datagen = ImageDataGenerator(rescale=1./255) train_generator = train_datagen.flow_from_directory( 'training', target_size=(240, 240), batch_size=128, class_mode='binary', # 二分类 shuffle=True ) validation_generator = validation_datagen.flow_from_directory( 'validation', target_size=(240, 240), batch_size=128, class_mode='binary', shuffle=True )
生成器输出:
Found 19747 images belonging to 2 classes. Found 4938 images belonging to 2 classes.
模型构建代码
from tensorflow.keras.layers import GlobalAveragePooling2D, Dense from tensorflow.keras.applications import EfficientNetB1 from tensorflow.keras.models import Model from tensorflow.keras import layers import tensorflow as tf NUM_CLASSES = 2 IMG_SIZE = 240 size = (IMG_SIZE, IMG_SIZE) def build_model(): inputs = layers.Input(shape=(IMG_SIZE, IMG_SIZE, 3)) model = EfficientNetB1(include_top=False, input_tensor=inputs, weights="imagenet") model.trainable = False x = layers.GlobalAveragePooling2D(name="avg_pool")(model.output) x = layers.BatchNormalization()(x) # 尝试过的代码(已注释) # x = layers.Dense(128, activation="relu")(x) # top_dropout_rate = 0.2 # x = layers.Dropout(top_dropout_rate, name="dropout")(x) outputs = layers.Dense(1, activation="sigmoid", name="pred")(x) model = tf.keras.Model(inputs, outputs, name="EfficientNet") optimizer = tf.keras.optimizers.Adam(learning_rate=1e-4) model.compile( optimizer=optimizer, loss="binary_crossentropy", metrics=["accuracy"] ) return model
训练代码
model = build_model() epochs = 50 hist = model.fit(train_generator, epochs=epochs, steps_per_epoch=len(train_generator), validation_data=validation_generator, validation_steps=len(validation_generator))
解决思路与步骤
1. 排查数据标注与类别分布
- 确认
label0和label1文件夹内的图片标注完全正确,无标反、错标情况。 - 统计两类样本数量,避免极端类别不平衡(如某类占比超过90%)导致模型偏向多数类:
import os def count_samples(dir_path): for label in os.listdir(dir_path): label_path = os.path.join(dir_path, label) if os.path.isdir(label_path): print(f"{label}: {len(os.listdir(label_path))}") count_samples("training") count_samples("validation")
2. 验证图像生成器的正确性
- 取出一批数据,检查图片与标签是否匹配:
import matplotlib.pyplot as plt # 获取一批训练数据 x_batch, y_batch = next(train_generator) # 查看前5张图及对应标签 for i in range(5): plt.imshow(x_batch[i]) plt.title(f"Label: {y_batch[i]}") plt.show()
- 确认
flow_from_directory的标签映射符合预期(默认按文件夹字母顺序分配0/1)。
3. 修正模型预处理逻辑
EfficientNet预训练时使用的是[-1, 1]区间的像素值,而非[0,1],需替换预处理方式:
from tensorflow.keras.applications.efficientnet import preprocess_input # 替换原有的rescale为官方预处理函数 train_datagen = ImageDataGenerator( preprocessing_function=preprocess_input, rotation_range=30, horizontal_flip=True, fill_mode='nearest' ) validation_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)
4. 调整模型训练策略
- 尝试解冻EfficientNet顶层部分权重,而非完全冻结:
def build_model(): inputs = layers.Input(shape=(IMG_SIZE, IMG_SIZE, 3)) model = EfficientNetB1(include_top=False, input_tensor=inputs, weights="imagenet") # 解冻顶层20%的层,保留底层预训练特征 for layer in model.layers[int(len(model.layers)*0.8):]: layer.trainable = True x = layers.GlobalAveragePooling2D(name="avg_pool")(model.output) x = layers.BatchNormalization()(x) outputs = layers.Dense(1, activation="sigmoid", name="pred")(x) model = tf.keras.Model(inputs, outputs, name="EfficientNet") # 解冻后使用更小的学习率,避免破坏预训练权重 optimizer = tf.keras.optimizers.Adam(learning_rate=1e-5) model.compile( optimizer=optimizer, loss="binary_crossentropy", metrics=["accuracy"] ) return model
- 若仍无改善,先冻结整个EfficientNet,将学习率调至
1e-3训练5-10个epoch,确认新增的全连接层是否能正常学习。
5. 调整数据增强强度
暂时关闭所有数据增强,只保留官方预处理,观察准确率是否上升:
train_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)
若关闭后准确率提升,说明当前增强强度过大,可逐步降低旋转范围(如改为10度)再重新训练。
6. 更换优化器尝试
改用SGD优化器,有时比Adam更稳定:
optimizer = tf.keras.optimizers.SGD(learning_rate=1e-3, momentum=0.9)
内容的提问来源于stack exchange,提问作者aspiring-frustrated-researcher
相关产品推荐
相关产品推荐

