从tf.keras.preprocessing.image迁移至image_dataset_from_directory提取特征遇异常
公共代码部分
两种方案共用的代码如下:
BATCH_SIZE = 32 IM_DIR = '/content/drive/My Drive/101_ObjectCategories/' IM_HEIGHT = 224 IM_WIDTH = 224 NUM_IM = 8686 NUM_EPOCHS = int(math.ceil(NUM_IM / BATCH_SIZE)) # 加载预训练基础模型 model = ResNet50(weights='imagenet', include_top=False, input_shape=(IM_WIDTH, IM_HEIGHT, CH), pooling='max')
原有效方案
我曾用tf.keras.preprocessing.image模块成功提取图像特征,代码如下:
datagen = tf.keras.preprocessing.image.ImageDataGenerator(preprocessing_function=preprocess_input) dataset = datagen.flow_from_directory(IM_DIR, target_size=(IM_HEIGHT, IM_WIDTH), class_mode=None, shuffle=False) feature_list = [] feature_list = model.predict(dataset, num_epochs)
基于该特征训练暴力近邻模型后,能准确检索到相似图像。
新方案(效果异常)
由于tf.keras.preprocessing.image已废弃,我按文档建议改用tf.keras.utils.image_dataset_from_directory加载数据并转换,代码如下:
# 加载图像 dataset = tf.keras.utils.image_dataset_from_directory( IM_DIR, labels='inferred', # 'inferred', None label_mode='categorical', # 'int', 'categorical', 'binary' or None class_names=None, color_mode='rgb', # 'grayscale', 'rgb' or 'rgba' batch_size=BATCH_SIZE, image_size=(IM_HEIGHT, IM_WIDTH), shuffle=True, seed=51719, validation_split=None, subset=None, # 'training', 'validation' or 'both' interpolation='bilinear', # 'bilinear', 'nearest', 'bicubic', 'area', 'lanczos3', 'lanczos5', 'gaussian' or 'mitchellcubic' follow_links=False, crop_to_aspect_ratio=False ) # "用预处理层转换输出" # 缩放(归一化)数据集 rescale_layer = tf.keras.layers.Rescaling(1./255) rescaled_dataset = dataset.map(lambda x, y: (rescale_layer(x), y)) im_batch, labels_batch = next(iter(rescaled_dataset)) # 配置数据集以提升性能 AUTOTUNE = tf.data.AUTOTUNE tuned_dataset = dataset.cache().prefetch(buffer_size=AUTOTUNE)
特征提取代码:
# 特征提取 feature_list = [] feature_list = model.predict( tuned_dataset, batch_size=None, verbose='auto', steps=None, callbacks=None, max_queue_size=10, workers=1, use_multiprocessing=False ) # 保存特征 pickle.dump( feature_list, open(DATA_DIR + 'features.pickle', 'wb'))
但基于该特征训练的近邻模型检索效果极差。
补充对比
使用同一查询图像,原方案的近邻距离为:
0.0 185.60701 185.75049 195.71657 196.4056
新方案的近邻距离为:
0.0 0.81401 0.88622 0.92734 0.9346
新方案的距离数值不符合预期,相似图像距离未趋近于0。
请问我哪里操作错误导致结果差异如此之大?
问题出在图像预处理不匹配和数据集使用错误两个核心点,具体分析及修复步骤如下:
1. 核心问题:未遵循ResNet的预处理要求
原方案中使用preprocess_input作为预处理函数,这是为预训练ResNet专门设计的逻辑——它会将[0,255]区间的像素值转换为[-1,1]区间(完全匹配ImageNet训练时的输入标准)。
而新方案仅做了Rescaling(1./255),把像素值缩到[0,1],完全不符合模型的输入要求。预训练模型只有在输入匹配训练时的预处理逻辑时,才能输出有效的特征向量,这直接导致新方案提取的特征失效。
2. 额外问题:特征提取用了未预处理的原始数据集
你创建了rescaled_dataset但未实际使用,反而基于未做任何预处理的原始dataset生成tuned_dataset,最后用这个原始数据集喂给模型提取特征,进一步加剧了特征的无效性。
3. 次要问题:数据集打乱破坏特征与图像的对应关系
原方案设置shuffle=False,特征列表顺序与图像目录顺序完全对应,这对后续近邻检索的标签匹配至关重要。但新方案开启shuffle=True,若未同步记录标签顺序,会导致检索时特征与标签不对应,进一步影响效果。
修复步骤
步骤1:替换预处理逻辑,使用preprocess_input
from tensorflow.keras.applications.resnet50 import preprocess_input # 对数据集应用ResNet要求的预处理 preprocessed_dataset = dataset.map(lambda x, y: (preprocess_input(x), y))
注意:preprocess_input会自动处理[0,255]的像素值,无需提前做Rescaling(1./255)。
步骤2:使用预处理后的数据集做特征提取
# 配置性能优化时使用预处理后的数据集 AUTOTUNE = tf.data.AUTOTUNE tuned_dataset = preprocessed_dataset.cache().prefetch(buffer_size=AUTOTUNE) # 提取特征 feature_list = model.predict(tuned_dataset)
步骤3:关闭数据集打乱(可选但建议)
若需要保持特征与图像顺序一致,修改加载数据集的参数:
dataset = tf.keras.utils.image_dataset_from_directory( # 其他参数不变 shuffle=False, # 关闭打乱 # seed=51719, # 关闭打乱后可注释种子参数 )
修复后,提取的特征分布会与原方案一致,近邻距离也会回到正常范围,检索效果即可恢复。
内容的提问来源于stack exchange,提问作者Aloysia de Argenteuil

