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

Keras中flow_from_directory与自定义输入的差异及预处理顺序问询

解决Keras ImageDataGenerator预处理顺序导致的精度差异问题

这个问题我之前也碰到过!核心原因就是Keras的ImageDataGenerator默认的预处理顺序和你想要的搞反了——它会先把图像resize到你指定的target_size,再调用你的preprocess函数,这就导致你写的「先裁剪再resize」逻辑完全没按预期执行,自然生成的输入数据和手动处理的分布不一致,最终引发了约10%的精度差距。

下面给你两个可行的解决方案:

方案1:快速修改参数,让预处理顺序回归预期

最简单的调整方式就是取消flow_from_directory里的target_size设置(或者设为None),让ImageDataGenerator不提前做resize,把所有预处理步骤完全交给你的preprocess函数:

datagen = ImageDataGenerator(
    featurewise_center=False,
    featurewise_std_normalization=False,
    preprocessing_function=preprocess)
generator = datagen.flow_from_directory(
    'numbers_train',
    target_size=None,  # 关键:不提前resize,保留原图尺寸
    batch_size=1024,
    classes=['02'],
    shuffle=False,
    class_mode='sparse')

这样修改后,ImageDataGenerator会直接把原图传给你的preprocess函数,你的「裁剪→resize→归一化」流程就能正常执行,和手动生成inputs2的逻辑完全一致。再运行代码打印均值,应该会得到几乎相同的结果,精度差异也会消失。

方案2:自定义数据生成器(更灵活可控)

如果后续你需要更复杂的数据增强逻辑,或者想完全掌控数据加载的每一步,推荐继承keras.utils.Sequence实现自定义生成器,彻底摆脱ImageDataGenerator的默认流程限制:

import numpy as np
from keras.utils import Sequence
from keras.preprocessing import image
import os

def preprocess(img):
    width, height = img.size
    # Crop 48x48px
    desired_width, desired_height = 48, 48
    if width < 48:
        desired_width = width
    start_x = np.maximum(0, int((width-desired_width)/2))
    img = img.crop((start_x, np.maximum(0, height-desired_height), start_x+desired_width, height))
    img = img.resize((48, 48))
    img = image.img_to_array(img)
    return img / 255.

class CustomImageGenerator(Sequence):
    def __init__(self, folder, batch_size=1024, shuffle=False):
        self.folder = folder
        self.batch_size = batch_size
        self.shuffle = shuffle
        self.files = [os.path.join(folder, f) for f in os.listdir(folder)]
        self.on_epoch_end()

    def __len__(self):
        # 计算每个epoch的批次数
        return int(np.ceil(len(self.files) / self.batch_size))

    def __getitem__(self, idx):
        # 获取当前批次的文件
        batch_files = self.files[idx*self.batch_size : (idx+1)*self.batch_size]
        # 加载并预处理图像
        batch_images = []
        for f in batch_files:
            img = image.load_img(f)
            batch_images.append(preprocess(img))
        # 生成目标标签(这里都是0类,因为只有'02'文件夹)
        batch_targets = np.zeros(len(batch_files))
        return np.array(batch_images), batch_targets

    def on_epoch_end(self):
        # 每个epoch结束后打乱数据(如果需要)
        if self.shuffle:
            np.random.shuffle(self.files)

# 使用自定义生成器
generator = CustomImageGenerator('numbers_train/02', batch_size=1024, shuffle=False)
inputs, targets = generator[0]  # 获取第一个批次

这个生成器完全按照你需要的「加载原图→裁剪→resize→归一化」流程处理数据,和你手动生成inputs2的逻辑100%一致,能彻底解决数据分布不一致的问题,而且后续扩展数据增强也更方便。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:54:30