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
相关产品推荐
相关产品推荐

