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

Keras自定义图像分类数据生成器报错排查求助

解决Keras Sequence数据生成器返回空batch的问题

针对你的需求,先明确核心逻辑:每轮训练用180张样本(30张原图+每张原图生成的5张增强图),分6个batch(每个batch30张)训练。下面是修正后的实现方案,以及对你遇到问题的分析:

问题根源分析

你遇到的生成器返回形状为(0,)的元素错误,大概率是以下原因:

  1. __len__方法返回值错误:没有返回预期的6步,导致Keras计算训练步数时出错,调用__getitem__时索引越界
  2. 未正确在epoch开始前生成完整的180张样本:__getitem__被调用时没有可用数据,返回空数组
  3. 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)

关键注意点

  1. 确保_load_and_preprocess返回的图片形状和模型输入完全匹配,比如模型输入是(224,224,3),就必须resize到对应尺寸
  2. 如果是多分类任务,要将标签转换为one-hot编码(可以在on_epoch_end中处理)
  3. 若原图数量不足30张,可将np.random.choice中的replace=False改为replace=True,允许重复选取原图
  4. on_epoch_end是Sequence的生命周期方法,会在每个epoch结束后调用,正好用来生成下一轮的样本数据,避免重复生成

内容的提问来源于stack exchange,提问作者Pythonnorra

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 21:40:38