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

训练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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 14:32:47