训练VGG16犬种识别模型后加载失败:Dense层输入数量错误
VGG16犬种识别模型训练后加载报错:ValueError: Layer 'dense' expected 1 input(s). Received 2 instead
问题现象
训练VGG16犬种识别模型后,执行load_model时触发上述错误。测试发现:
- 注释掉
model.fit(...)语句,模型可正常加载 - 在
model.fit(...)执行前保存并加载模型,同样触发错误
相关代码:
from keras.src.legacy.preprocessing.image import ImageDataGenerator from keras.src.saving import load_model from sklearn.preprocessing import LabelEncoder from keras.utils import to_categorical from keras.applications import VGG16 from keras import layers, models import os import numpy as np from PIL import Image import xml.etree.ElementTree as ET def load_and_crop_image(image_path, annotation_path, save_dir='cropped_images'): # Parse the XML file for bounding box tree = ET.parse(annotation_path) root = tree.getroot() bndbox = root.find(".//object/bndbox") xmin = int(bndbox.find('xmin').text) ymin = int(bndbox.find('ymin').text) xmax = int(bndbox.find('xmax').text) ymax = int(bndbox.find('ymax').text) # Load and crop the image image = Image.open(image_path) cropped_image = image.crop((xmin, ymin, xmax, ymax)) cropped_image_resized = cropped_image.resize((224, 224)) # Resize to fit VGG16 input size if not os.path.exists(save_dir): os.makedirs(save_dir) image_file_name = os.path.basename(image_path) save_path = os.path.join(save_dir, image_file_name) return np.array(cropped_image_resized) def load_dataset(images_dir, annotations_dir): images = [] labels = [] for breed in os.listdir(annotations_dir): breed_annotations_dir = os.path.join(annotations_dir, breed) breed_images_dir = os.path.join(images_dir, breed) for annotation_file in os.listdir(breed_annotations_dir): annotation_path = os.path.join(breed_annotations_dir, annotation_file) image_file_name = annotation_file.split('.')[0] + '.jpg' image_path = os.path.join(breed_images_dir, image_file_name) if os.path.exists(image_path): image = load_and_crop_image(image_path, annotation_path) images.append(image) labels.append(breed) return np.array(images), np.array(labels) images_dir = 'images' annotations_dir = 'annotations' images, labels = load_dataset(images_dir, annotations_dir) # Encode labels label_encoder = LabelEncoder() encoded_labels = label_encoder.fit_transform(labels) categorical_labels = to_categorical(encoded_labels) def create_model(num_classes): base_model = VGG16(weights='imagenet', include_top=False, input_shape=(224, 224, 3)) base_model.trainable = False # Freeze base model model = models.Sequential([ base_model, layers.GlobalAveragePooling2D(), layers.Dense(1024, activation='relu'), layers.Dropout(0.5), layers.Dense(num_classes, activation='softmax') ]) model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) return model model = create_model(num_classes=categorical_labels.shape[1]) datagen = ImageDataGenerator(rescale=1. / 255, validation_split=0.1, rotation_range=10, width_shift_range=0.1, height_shift_range=0.1, shear_range=0.1, zoom_range=0.1, horizontal_flip=True, vertical_flip=True ) train_generator = datagen.flow(images, categorical_labels, batch_size=32, subset='training') validation_generator = datagen.flow(images, categorical_labels, batch_size=32, subset='validation') history = model.fit(train_generator, epochs=1, validation_data=validation_generator) model.save('dog_breed_classifier.keras', overwrite=True) print("Model saved successfully.") try: model = load_model('dog_breed_classifier.keras') print("Model loaded successfully.") except Exception as e: print(f"An error occurred while loading the model: {e}")
原因分析
这个错误的核心是模型训练后输入签名被意外修改:
使用ImageDataGenerator.flow()时,生成器返回(图像数据, 标签数据)的元组,训练过程中Keras可能错误地将模型的输入绑定为接收两个张量;同时Sequential模型结合预训练模型时,输入层的自动注册逻辑也可能引发输入签名歧义,最终导致保存后的模型加载时,输入层错误地期望接收两个输入。
解决方案
方案1:改用函数式API构建模型
函数式API能明确指定模型的输入输出结构,避免Sequential模型的输入签名歧义:
def create_model(num_classes): base_model = VGG16(weights='imagenet', include_top=False, input_shape=(224, 224, 3)) base_model.trainable = False # 冻结预训练层 # 明确定义输入张量 inputs = layers.Input(shape=(224, 224, 3)) # 传递输入到预训练模型 x = base_model(inputs, training=False) x = layers.GlobalAveragePooling2D()(x) x = layers.Dense(1024, activation='relu')(x) x = layers.Dropout(0.5)(x) outputs = layers.Dense(num_classes, activation='softmax')(x) model = models.Model(inputs=inputs, outputs=outputs) model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) return model
方案2:训练后重置模型输入签名
在保存模型前,手动重置模型的输入输出,确保输入层只接收单个张量:
history = model.fit(train_generator, epochs=1, validation_data=validation_generator) # 重置模型输入输出,修正签名 model = models.Model(inputs=model.input, outputs=model.output) model.save('dog_breed_classifier.keras', overwrite=True)
方案3:改用flow_from_directory(可选)
如果数据集按类别文件夹组织,使用flow_from_directory能更稳定地处理数据,避免手动构建数据时的格式问题:
# 假设训练图像按类别存放在`train`文件夹下,每个类别对应子文件夹 train_generator = datagen.flow_from_directory( 'train', target_size=(224, 224), batch_size=32, class_mode='categorical' ) validation_generator = datagen.flow_from_directory( 'val', target_size=(224, 224), batch_size=32, class_mode='categorical' )
验证修改
修改后重新训练并保存模型,执行以下代码验证加载是否正常:
try: model = load_model('dog_breed_classifier.keras') print("模型加载成功") # 测试预测 test_sample = np.random.rand(1, 224, 224, 3) pred = model.predict(test_sample) print("预测测试成功") except Exception as e: print(f"加载模型出错:{e}")
内容的提问来源于stack exchange,提问作者crpgdr
相关产品推荐
相关产品推荐

