ResNet50瓶颈层迁移学习准确率极低(仅6%)问题排查求助
看起来你遇到的核心问题是瓶颈特征和标签的对应关系错位,以及一处可能的变量名拼写错误,导致模型完全无法学习到有效的分类特征。下面一步步拆解问题并给出修复方案:
1. 变量名拼写错误:num_class vs num_classes
你的代码中定义标签时用的是num_classes=num_classes(对应120个犬种),但在构建顶层模型时写成了:
model.add(Dense(num_class, activation='softmax'))
如果num_class是未正确定义的变量(比如误写了num_classes),输出层的神经元数量会和实际类别数不匹配——比如如果num_class被默认赋值为1,模型只能输出1类,自然无法正确分类120个犬种。优先修正这个笔误:
# 把num_class改为num_classes,和标签定义保持一致 model.add(Dense(num_classes, activation='softmax'))
2. 瓶颈特征与标签的样本数量不匹配
当使用flow_from_directory+predict_generator时,如果数据集的样本总数不是batch_size的整数倍,predict_generator默认只会返回floor(样本数/batch_size)批数据,剩余的样本会被丢弃。但train_generator.classes会包含所有样本的标签,这就导致bottleneck_features_train的样本数少于train_labels,训练时模型会循环复用特征,最终特征和标签完全错位,模型学不到任何有效信息。
修复方案:
手动指定predict_generator的steps参数,确保覆盖所有样本:
# 计算训练集和验证集的完整步数 train_steps = train_generator.samples // train_generator.batch_size # 如果有剩余样本,加1步 if train_generator.samples % train_generator.batch_size != 0: train_steps += 1 val_steps = validation_generator.samples // validation_generator.batch_size if validation_generator.samples % validation_generator.batch_size != 0: val_steps += 1 # 生成瓶颈特征时指定steps bottleneck_features_train = tr_model.predict_generator(train_generator, steps=train_steps) bottleneck_features_validation = tr_model.predict_generator(validation_generator, steps=val_steps)
同时可以添加断言,确保特征和标签样本数一致:
assert bottleneck_features_train.shape[0] == train_labels.shape[0], "特征与标签样本数不匹配!" assert bottleneck_features_validation.shape[0] == validation_labels.shape[0], "验证特征与标签样本数不匹配!"
3. 顶层模型的训练策略优化
仅用Flatten+Dense的结构可能容易过拟合,而且rmsprop的学习率可能不适合小模型。可以尝试:
- 添加Dropout层抑制过拟合
- 改用Adam优化器,设置合适的学习率
示例修正后的顶层模型:
model = Sequential() model.add(Flatten(input_shape=bottleneck_features_train.shape[1:])) model.add(Dropout(0.5)) # 添加Dropout层缓解过拟合 model.add(Dense(num_classes, activation='softmax')) # 改用Adam优化器,设置较小的学习率 model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4), loss='categorical_crossentropy', metrics=['accuracy'])
修正后的完整代码片段
from tensorflow.keras.applications import ResNet50 from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.utils import to_categorical from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Flatten, Dense, Dropout from tensorflow.keras.optimizers import Adam import tensorflow as tf # 假设已定义这些变量 train_data_dir = "path/to/train" validation_data_dir = "path/to/val" image_size = 224 batch_size = 32 num_classes = 120 # 加载预训练ResNet50,不包含顶层 tr_model = ResNet50(include_top=False, weights='imagenet', input_shape=(224, 224, 3)) datagen = ImageDataGenerator(rescale=1. / 255) #### Training #### train_generator = datagen.flow_from_directory( train_data_dir, target_size=(image_size, image_size), class_mode=None, batch_size=batch_size, shuffle=False ) # 计算完整步数 train_steps = train_generator.samples // batch_size if train_generator.samples % batch_size != 0: train_steps += 1 bottleneck_features_train = tr_model.predict_generator(train_generator, steps=train_steps) train_labels = to_categorical(train_generator.classes, num_classes=num_classes) # 确保特征和标签样本数一致 assert bottleneck_features_train.shape[0] == train_labels.shape[0], "特征与标签样本数不匹配!" #### Validation #### validation_generator = datagen.flow_from_directory( validation_data_dir, target_size=(image_size, image_size), class_mode=None, batch_size=batch_size, shuffle=False ) val_steps = validation_generator.samples // batch_size if validation_generator.samples % batch_size != 0: val_steps += 1 bottleneck_features_validation = tr_model.predict_generator(validation_generator, steps=val_steps) validation_labels = to_categorical(validation_generator.classes, num_classes=num_classes) assert bottleneck_features_validation.shape[0] == validation_labels.shape[0], "验证特征与标签样本数不匹配!" #### Model creation #### model = Sequential() model.add(Flatten(input_shape=bottleneck_features_train.shape[1:])) model.add(Dropout(0.5)) model.add(Dense(num_classes, activation='softmax')) model.compile( optimizer=Adam(learning_rate=1e-4), loss='categorical_crossentropy', metrics=['accuracy'] ) history = model.fit( bottleneck_features_train, train_labels, epochs=30, batch_size=batch_size, validation_data=(bottleneck_features_validation, validation_labels) )
按照上面的修正,你的模型应该能学习到有效的特征,准确率会接近带顶层的ResNet50水平。
内容的提问来源于stack exchange,提问作者Laurent R

