You need to enable JavaScript to run this app. Keras构建分块图像训练生成器:超大规模数据集多轮训练异常
优惠活动
大模型
产品
解决方案
定价
更多

Keras构建分块图像训练生成器:超大规模数据集多轮训练异常

问题根因定位
  • 训练逻辑设计错误:原代码在for X,y in monGenerateur循环内调用model.fit,每接收一个批次就启动一次全新的训练流程,自然每次日志都从Epoch 1/10开始,完全违背Keras训练接口的设计逻辑。Keras的model.fit支持接收生成器作为数据源,由fit本身负责控制遍历节奏、计数epoch,不需要用户手动循环遍历生成器。
  • 自定义生成器逻辑缺失:原生成器是仅单次遍历DataFrame的有限生成器,没有无限循环结构,一旦跑完全部行就抛出StopIteration信号,fit接收到终止信号就直接结束训练,无法自动进入下一轮epoch。
  • 功能逻辑bug:
    • 单图读取函数中存在未定义变量red的笔误,正常运行会触发NameError,实际逻辑应该是对读取到的图像img做resize
    • 标签提取逻辑v[3:]和DataFrame实际列索引不匹配,image_path是第0列,后面紧跟OHE标签列,用v[3:]会漏取前两列标签
    • 没有实现epoch结束后的洗牌逻辑,原代码仅按原始DataFrame顺序遍历,没有打乱数据顺序的步骤
  • 数据增强逻辑嵌套层级错误:原代码在每个单独批次外又套了一层ImageDataGenerator.flow,导致批次数据加载和增强流程割裂,无法实现逐批次在线处理的目标。
修正后的完整实现

1. 基础功能函数修正

先修复单图读取、预处理的原有bug,保持原有功能的同时修正笔误:

import numpy as np
import cv2
import matplotlib.pyplot as plt
from sklearn.utils import shuffle
from tensorflow.keras.preprocessing.image import ImageDataGenerator

def open_multilayer_image_aug_hflip(path, SIZE, test=False):
    fullpath = path
    if test:
        img = plt.imread(fullpath)
        img = cv2.resize(img, SIZE)
        ni = np.zeros((SIZE[0], SIZE[1], 3), 'uint8')
        ni[..., 0] = img * 255
    else:
        if fullpath[-3:] == '.png':
            img = plt.imread(fullpath)
            # 修复原代码未定义变量red的笔误
            img = cv2.resize(img, SIZE)
            ni = np.zeros((SIZE[0], SIZE[1], 3), 'uint8')
            ni[..., 0] = img * 255
    return ni

def preprocess_hflip(size, train_img_names, test=False):
    x_train = []
    for i in train_img_names:
        x_train.append(open_multilayer_image_aug_hflip(i, size, test))
    x_train = np.array(x_train)
    x_train = x_train.astype('float32')
    x_train /= 255
    return x_train

2. 自定义无限循环生成器实现

核心满足要求:无限输出批次、每轮epoch开始前洗牌、逐批次完成预处理+增强、批次生成后自动清空内存缓存:

# 按需配置训练集在线增强参数
train_augmenter = ImageDataGenerator(
    horizontal_flip=True,
    rotation_range=15,
    zoom_range=0.2
)

def generate_arrays_from_file(df, batchsize, image_size=(128,128), is_test=False):
    # 无限循环结构,支撑多epoch训练
    while True:
        # 每个epoch开始前对全量数据洗牌
        df_shuffled = shuffle(df, random_state=None)
        inputs = []
        targets = []
        batchcount = 0

        for k, v in df_shuffled.iterrows():
            inputs.append(v['image_path'])
            # 修复原标签提取逻辑,从image_path列后提取所有OHE标签
            targets.append(v.iloc[1:].values.astype('float32'))
            batchcount += 1

            # 凑齐完整批次后执行后续流程
            if batchcount >= batchsize:
                # 批次内完成图像读取、resize、归一化
                X = preprocess_hflip(image_size, inputs, test=is_test)
                y = np.array(targets, dtype='float32')
                # 批次内执行数据增强
                if not is_test:
                    X, y = next(train_augmenter.flow(X, y, batch_size=batchsize, shuffle=False))
                # 输出批次
                yield (X, y)
                # 清空缓存,避免占用常驻内存
                inputs = []
                targets = []
                batchcount = 0
        
        # 处理数据集尾部不足一个batchsize的残留数据
        if len(inputs) > 0:
            X = preprocess_hflip(image_size, inputs, test=is_test)
            y = np.array(targets, dtype='float32')
            if not is_test:
                X, y = next(train_augmenter.flow(X, y, batch_size=len(inputs), shuffle=False))
            yield (X, y)

3. 正确的训练流程调用

直接将生成器传入model.fit,由fit负责完成10轮epoch的遍历控制,不需要手动写循环遍历生成器:

# 初始化训练、验证集生成器
train_generator = generate_arrays_from_file(
    df=train_df,
    batchsize=32,  # 根据显存调整批次大小
    image_size=(128,128),
    is_test=False
)
valid_generator = generate_arrays_from_file(
    df=test_df,
    batchsize=32,
    image_size=(128,128),
    is_test=True
)

# 计算每轮epoch需要的批次量
steps_per_epoch = int(np.ceil(len(train_df) / 32))
validation_steps = int(np.ceil(len(test_df) / 32))

# 启动训练,由fit自动完成10轮epoch、每epoch结束后触发回调
hist = model.fit(
    train_generator,
    epochs=10,
    steps_per_epoch=steps_per_epoch,
    validation_data=valid_generator,
    validation_steps=validation_steps,
    callbacks=[rlr, ckp, es],
    verbose=2
)
关键约束说明
  • 生成器必须保留while True无限循环结构:Keras的fit依赖生成器持续输出数据,一旦生成器抛出StopIteration就会终止训练,不会自动重启遍历流程。
  • 洗牌操作必须嵌入生成器内层:每跑完一轮全量数据后,在进入下一轮遍历前重新打乱DataFrame顺序,不要依赖外部逻辑控制洗牌时机。
  • 批次输出后必须清空临时缓存:每次yield批次数据后,立刻清空路径列表、临时图像数组的引用,避免400GB规模的数据集累计占用运行内存。
  • 不要在fit外层嵌套生成器遍历循环:Keras的fit会自动按steps_per_epoch的计数规则完成epoch切换,手动循环会导致每个批次都触发一次独立的fit流程,无法累计训练进度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.28 10:36:04