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

使用Data Generator类训练CNN模型时准确率低下的问题排查

自定义Data Generator导致模型准确率极低的问题排查

我有两个模型架构:模型1使用自定义Data Generator类拟合数据,模型2直接使用训练集和测试集的numpy数组拟合数据。模型1的准确率极低,而模型2的准确率表现良好。需要排查Data Generator类导致的问题。模型2的数据增强层包含将图像resize至(160,160,3)的操作。

代码实现

自定义Data Generator类

class DataGenerator(keras.utils.Sequence):
    def __init__(self, images, labels=None, mode='fit', batch_size=64, dim=(160, 160), 
                 channels=3, n_classes=10, shuffle=True, augment=False):
        # 初始化生成器配置
        self.images = images
        self.labels = labels
        self.mode = mode
        self.batch_size = batch_size
        self.dim = dim
        self.channels = channels
        self.n_classes = n_classes
        self.shuffle = shuffle
        self.augment = augment
        self.on_epoch_end()

        if augment:
            self.augmenter=albu.Compose([
                albu.HorizontalFlip(0.5),
                albu.VerticalFlip(0.5),
                albu.RandomRotate90(0.2),
                albu.ElasticTransform(p=0.2),
                albu.Resize(height=dim[0], width=dim[1])
            ])
        else:
            self.augmenter=albu.Resize(height=dim[0],width=dim[1])

    # 每个epoch结束后调用的方法
    def on_epoch_end(self):
        self.indexes = np.arange(self.images.shape[0])
        if self.shuffle == True:
            np.random.shuffle(self.indexes)

    # 返回一个epoch中的步数(即batch数量)
    def __len__(self):
        return int(np.floor(len(self.images) / self.batch_size))

    # 根据batch索引获取对应批次的数据
    def __getitem__(self, index):
        # 生成一个batch的索引
        batch_indexes = self.indexes[index * self.batch_size:(index+1) * self.batch_size]

        # 生成X的mini-batch
        X = np.empty((self.batch_size, *self.dim, self.channels))
        for i, ID in enumerate(batch_indexes):
            img = self.images[ID]
            # 图像归一化到0-1
            img = img.astype(np.float32)/255.0
            # 训练集数据增强
            img = self.augmenter(image=img)['image']
            X[i] = img

        # 生成y的mini-batch
        if self.mode == 'fit':
            y = self.labels[batch_indexes]
            return X, y

        elif self.mode == 'predict':
            return X

        else:
            raise AttributeError("The mode should be set to either 'fit' or 'predict'.")

模型1(使用Data Generator)

def model_1():
    base_model = tf.keras.applications.MobileNetV2(input_shape=(160,160,3),
                                                   include_top=False,
                                                   weights='imagenet')
    base_model.trainable=False # 冻结预训练模型
    inputs=tf.keras.Input(shape=((160,160,3)))
    x=preprocess_input(inputs)
    x=base_model(x,training=False)# 保持BN层处于推理模式
    x=layers.GlobalAveragePooling2D()(x)
    x=layers.Dropout(0.5)(x)
    outputs=layers.Dense(units=10,activation='softmax')(x)
    model=Model(inputs,outputs)
    return model

model_1=model_1()
history1=model_1.fit(train_data_generator,validation_data=valid_data_generator,epochs=20)

模型2(直接使用numpy数组)

def model_2():
    base_model = tf.keras.applications.MobileNetV2(input_shape=(160,160,3),
                                                   include_top=False,
                                                   weights='imagenet')
    base_model.trainable=False # 冻结预训练模型
    inputs=tf.keras.Input(shape=((32,32,3)))
    x=data_augmentation(inputs)
    x=preprocess_input(x)
    x=base_model(x,training=False)# 保持BN层处于推理模式
    x=layers.GlobalAveragePooling2D()(x)
    x=layers.Dropout(0.5)(x)
    outputs=layers.Dense(units=10,activation='softmax')(x)
    model=Model(inputs,outputs)
    return model

model2=model_2()
history2=model2.fit(X_train,y_train,batch_size=64,epochs=5,validation_data=(X_test,y_test))

问题排查关键点

  • 预处理顺序错误(核心问题):
    MobileNetV2的preprocess_input要求输入是0-255范围的RGB图像,会将其转换为[-1,1]的归一化范围。但Generator中先执行了img = img.astype(np.float32)/255.0,把图像提前归一化到0-1,再做增强,最后模型的preprocess_input处理的是不符合要求的数据,完全打乱了预训练模型的输入分布。而模型2的流程是原始图像(0-255)→ 数据增强→ preprocess_input,顺序完全正确。

  • 标签格式不匹配:
    检查train_data_generator传入的labels格式:如果模型用categorical_crossentropy损失函数,标签需要是one-hot编码;如果是sparse_categorical_crossentropy,标签可以是整数。若模型2的y_train是one-hot格式,而Generator返回的是原始整数标签,会导致损失计算错误,直接拉低准确率。

  • 训练样本丢失:
    Generator的__len__用np.floor计算步数,会丢弃最后一个不足batch_size的样本。虽然不会直接导致准确率极低,但会减少训练数据量,影响模型收敛。

  • 输入尺寸匹配问题:
    模型2输入是(32,32,3),通过data_augmentationresize到160x160。需确认Generator接收的images原始尺寸是否为32x32,若尺寸不匹配,resize后的图像会失真,影响模型识别。

修复建议

  1. 调整预处理顺序:去掉Generator中的提前归一化,让模型的preprocess_input处理原始0-255图像:

    def __getitem__(self, index):
        batch_indexes = self.indexes[index * self.batch_size:(index+1) * self.batch_size]
        actual_batch_size = len(batch_indexes)
        X = np.empty((actual_batch_size, *self.dim, self.channels))
        for i, ID in enumerate(batch_indexes):
            img = self.images[ID]
            # 先做增强,再交给模型预处理
            img = self.augmenter(image=img)['image']
            X[i] = img
    
        if self.mode == 'fit':
            y = self.labels[batch_indexes]
            return X, y
    
  2. 统一标签格式:若模型用categorical_crossentropy,在Generator中把标签转为one-hot:

    y = tf.keras.utils.to_categorical(self.labels[batch_indexes], self.n_classes)
    
  3. 修正步数计算:避免丢失最后一批样本:

    def __len__(self):
        return int(np.ceil(len(self.images) / self.batch_size))
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 14:44:57