Keras自定义图像分类数据生成器报错排查求助
解决Keras Sequence数据生成器返回空batch的问题
针对你的需求,先明确核心逻辑:每轮训练用180张样本(30张原图+每张原图生成的5张增强图),分6个batch(每个batch30张)训练。下面是修正后的实现方案,以及对你遇到问题的分析:
问题根源分析
你遇到的生成器返回形状为(0,)的元素错误,大概率是以下原因:
__len__方法返回值错误:没有返回预期的6步,导致Keras计算训练步数时出错,调用__getitem__时索引越界- 未正确在epoch开始前生成完整的180张样本:
__getitem__被调用时没有可用数据,返回空数组 - Keras在epoch前调用3次
__getitem__是正常行为,用于验证生成器的输出形状和类型,必须确保任何时候__getitem__都能返回有效batch
修正后的CustomDataGen实现
import tensorflow as tf import numpy as np from tensorflow.keras.utils import Sequence class CustomDataGen(Sequence): def __init__(self, image_paths, labels, batch_size=30, augment=None): self.image_paths = image_paths self.labels = labels self.batch_size = batch_size self.augment = augment self.epoch_total_samples = 180 # 每轮固定180张样本 self.current_samples = None self.current_labels = None # 校验原图数量是否足够:每轮需要30张原图(每张生成5张增强) assert len(self.image_paths) >= self.epoch_total_samples // 6, "原图数量不足,无法生成每轮180张样本" def __len__(self): # 每epoch的训练步数:180//30=6 return self.epoch_total_samples // self.batch_size def on_epoch_end(self): # 每个epoch开始时生成完整的180张样本(原图+增强图) # 随机挑选30张原图 selected_idx = np.random.choice(len(self.image_paths), size=30, replace=False) selected_paths = [self.image_paths[i] for i in selected_idx] selected_labels = [self.labels[i] for i in selected_idx] self.current_samples = [] self.current_labels = [] for path, label in zip(selected_paths, selected_labels): # 读取并预处理原图 img = self._load_and_preprocess(path) self.current_samples.append(img) self.current_labels.append(label) # 生成5张增强图 for _ in range(5): aug_img = self.augment(img) if self.augment else img self.current_samples.append(aug_img) self.current_labels.append(label) # 转换为numpy数组,确保形状统一 self.current_samples = np.array(self.current_samples) self.current_labels = np.array(self.current_labels) # 打乱样本顺序(可选,提升训练效果) shuffle_idx = np.random.permutation(len(self.current_samples)) self.current_samples = self.current_samples[shuffle_idx] self.current_labels = self.current_labels[shuffle_idx] def _load_and_preprocess(self, img_path): # 替换为你的图片读取逻辑,确保输出形状匹配模型输入 img = tf.io.read_file(img_path) img = tf.image.decode_jpeg(img, channels=3) img = tf.image.resize(img, (224, 224)) # 改成你的模型输入尺寸 img = img / 255.0 # 归一化到[0,1] return img.numpy() def __getitem__(self, index): # 计算当前batch的起始和结束索引 start = index * self.batch_size end = start + self.batch_size # 防止索引越界(极端情况,比如样本数不是batch_size的整数倍) end = min(end, len(self.current_samples)) batch_x = self.current_samples[start:end] batch_y = self.current_labels[start:end] # 调试用:确保batch非空 if batch_x.shape[0] == 0: raise ValueError(f"Empty batch at index {index}! Check sample generation logic.") return batch_x, batch_y
使用示例
# 假设你有原图路径和对应的标签列表 image_paths = ["img1.jpg", "img2.jpg", ...] # 至少30张原图 labels = [0, 1, 0, 1, ...] # 对应标签 # 定义数据增强函数 def image_augment(img): img_tensor = tf.convert_to_tensor(img) img_tensor = tf.image.random_flip_left_right(img_tensor) img_tensor = tf.image.random_brightness(img_tensor, max_delta=0.2) img_tensor = tf.image.random_contrast(img_tensor, lower=0.8, upper=1.2) return img_tensor.numpy() # 初始化生成器 train_gen = CustomDataGen(image_paths, labels, batch_size=30, augment=image_augment) # 构建并编译你的CNN模型 model = tf.keras.Sequential([ tf.keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=(224,224,3)), tf.keras.layers.MaxPooling2D(), tf.keras.layers.Conv2D(64, (3,3), activation='relu'), tf.keras.layers.MaxPooling2D(), tf.keras.layers.Flatten(), tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dense(1, activation='sigmoid') # 二分类示例,根据你的任务调整 ]) model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # 开始训练 model.fit(train_gen, epochs=20)
关键注意点
- 确保
_load_and_preprocess返回的图片形状和模型输入完全匹配,比如模型输入是(224,224,3),就必须resize到对应尺寸 - 如果是多分类任务,要将标签转换为one-hot编码(可以在
on_epoch_end中处理) - 若原图数量不足30张,可将
np.random.choice中的replace=False改为replace=True,允许重复选取原图 on_epoch_end是Sequence的生命周期方法,会在每个epoch结束后调用,正好用来生成下一轮的样本数据,避免重复生成
内容的提问来源于stack exchange,提问作者Pythonnorra
相关产品推荐
相关产品推荐

