Keras训练二分类模型时数组形状不均ValueError问题求助
Keras/TensorFlow训练二分类模型时的数组形状不匹配错误
问题描述
尝试使用Keras和TensorFlow在包含约21000张图像的数据集上训练二分类模型,运行训练代码时抛出如下错误:
Traceback (most recent call last): File "train.py", line 83, in <module> training_images = np.array([i[0] for i in training_data]) ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 3 dimensions. The detected shape was (21527, 300, 300) + inhomogeneous part.
之前使用过相同数据集和代码,不清楚当前问题成因。以下是加载数据集并训练模型的代码:
from keras.models import Sequential, load_model from keras.layers import Dense, Dropout, Flatten from keras.layers import Conv2D, MaxPooling2D from keras.layers import BatchNormalization from PIL import Image from random import shuffle, choice import numpy as np import os from keras.callbacks import ModelCheckpoint from keras.preprocessing.image import ImageDataGenerator from keras.models import Sequential, Model from sklearn.model_selection import train_test_split from keras import optimizers IMAGE_SIZE = 300 epochs_num = 50 batch_size = 64 IMAGE_DIRECTORY = './data' retrain_from_prior_model=True def label_img(name): if name == 'object': return np.array([1, 0]) elif name == 'none' : return np.array([0, 1]) def load_data(): print("Loading images...") train_data = [] directories = next(os.walk(IMAGE_DIRECTORY))[1] for dirname in directories: print("Loading {0}".format(dirname)) file_names = next(os.walk(os.path.join(IMAGE_DIRECTORY, dirname)))[2] for i in range(len(file_names)): image_name = choice(file_names) image_path = os.path.join(IMAGE_DIRECTORY, dirname, image_name) label = label_img(dirname) if "DS_Store" not in image_path: try: img = Image.open(image_path) img = img.resize((IMAGE_SIZE, IMAGE_SIZE), Image.LANCZOS) train_data.append([np.array(img), label]) except Exception as e: print(f"Error processing image: {image_path}") print(f"Error message: {str(e)}") continue # Skip this image and continue with the next one shuffle(train_data) return train_data def create_model(): channels = 3 model = Sequential() #change first one to 64 model.add(Conv2D(32, kernel_size = (5, 5), activation='relu', input_shape=(IMAGE_SIZE, IMAGE_SIZE, channels))) model.add(MaxPooling2D(pool_size=(2,2))) model.add(BatchNormalization()) model.add(Conv2D(64, kernel_size=(3,3), activation='relu')) model.add(MaxPooling2D(pool_size=(2,2))) model.add(BatchNormalization()) model.add(Conv2D(128, kernel_size=(3,3), activation='relu')) model.add(MaxPooling2D(pool_size=(2,2))) model.add(BatchNormalization()) model.add(Conv2D(256, kernel_size=(3,3), activation='relu')) model.add(MaxPooling2D(pool_size=(2,2))) model.add(BatchNormalization()) model.add(Conv2D(64, kernel_size=(3,3), activation='relu')) model.add(MaxPooling2D(pool_size=(2,2))) model.add(BatchNormalization()) model.add(Dropout(0.2)) model.add(Flatten()) model.add(Dense(256, activation='relu')) model.add(Dropout(0.2)) model.add(Dense(128, activation='relu')) model.add(Dense(2, activation = 'softmax')) return model training_data = load_data() training_images = np.array([i[0] for i in training_data]) training_labels = np.array([i[1] for i in training_data]) print(str(len(training_images))) # Split the data training_images, validation_images, training_labels, validation_labels = train_test_split(training_images, training_labels, test_size=0.2, shuffle= True) print(str(len(training_images))) print('creating model') #======================== if retrain_from_prior_model == False: model = create_model() model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) print('training model...model.count_params: '+str(model.count_params()) + '...model.count_layers: '+ str(len(model.layers))) else: model = load_model("model_0.989.h5") model.compile(loss='binary_crossentropy', optimizer= optimizers.Adam(learning_rate=0.0001), metrics=['accuracy']) filepath="./checkpoints/model_{epoch:03d}_{accuracy:.4f}_{val_accuracy:.4f}_{val_loss:.7f}.h5" checkpoint = ModelCheckpoint(filepath, monitor=["accuracy"], verbose=1, mode='max', save_weights_only=False) callbacks_list = [checkpoint] # if you want data augmentation: rotation_range=20, width_shift_range=0.2, height_shift_range=0.2, brightness_range=[0.9,1.1] datagen = ImageDataGenerator(zoom_range=0.2, horizontal_flip=True) datagen.fit(training_images) train_gen=datagen.flow(training_images, training_labels, batch_size=batch_size) #validation val_datagen = ImageDataGenerator(horizontal_flip=True) val_datagen.fit(training_images) val_gen=datagen.flow(validation_images, validation_labels, batch_size=batch_size) model.fit(train_gen, validation_data=val_gen, epochs=epochs_num, verbose=1, callbacks=callbacks_list) print('Training finished. Model saved.')
问题原因
错误核心是图像数据形状不一致:模型输入要求是(300,300,3)的RGB图像,但数据集中存在单通道灰度图(形状为(300,300)),导致无法将所有图像统一构造成4维numpy数组(样本数, 300, 300, 3)。
另外,load_data函数存在逻辑漏洞:用choice(file_names)循环选取图片,会重复加载同一张图片,同时可能遗漏部分图像,但这不是当前报错的直接原因。
修复方法
1. 统一图像通道数
在加载图像后,强制转换为RGB格式,确保所有图像都是3通道。修改load_data函数中的图像处理部分:
img = Image.open(image_path) img = img.resize((IMAGE_SIZE, IMAGE_SIZE), Image.LANCZOS) # 强制转为RGB,解决灰度图通道数不足的问题 img = img.convert('RGB') train_data.append([np.array(img), label])
2. 修复重复加载图片的逻辑
原循环中用choice随机选图会导致重复,改为直接遍历所有文件名:
# 替换原循环代码 for image_name in file_names: image_path = os.path.join(IMAGE_DIRECTORY, dirname, image_name) label = label_img(dirname) if "DS_Store" not in image_path: try: img = Image.open(image_path) img = img.resize((IMAGE_SIZE, IMAGE_SIZE), Image.LANCZOS) img = img.convert('RGB') train_data.append([np.array(img), label]) except Exception as e: print(f"Error processing image: {image_path}") print(f"Error message: {str(e)}") continue
3. 可选:添加形状验证(调试用)
如果问题仍然存在,可以在添加数据前验证图像形状,输出异常路径:
img_array = np.array(img) if img_array.shape != (IMAGE_SIZE, IMAGE_SIZE, 3): print(f"Invalid shape {img_array.shape} for image: {image_path}") continue train_data.append([img_array, label])
内容的提问来源于stack exchange,提问作者angel_30
相关产品推荐
相关产品推荐

