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

基于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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 02:00:55