如何针对类别不平衡的二分类任务增强Class1数据优化CNN性能?
类别不平衡下的针对性数据增强解决方案
针对你遇到的Class2数据量是Class1 6倍的类别不平衡问题,通过针对Class1单独做数据增强来平衡数据分布,是解决模型偏向多数类的有效方法,以下是具体实现方案:
方法1:拆分数据集,为Class1配置专属增强策略
Keras原生的flow_from_directory会对所有类别应用相同的增强规则,没法单独提升少数类的增强频次或强度。所以可以手动拆分两类数据,分别处理后再合并:
- 遍历数据集文件夹,分别收集Class1和Class2的图像路径与标签,生成DataFrame
- 为Class1配置更强的增强策略(比如增加旋转、缩放范围),Class2用弱增强或仅归一化
- 用
flow_from_dataframe分别创建两类的生成器,再自定义合并生成器,让Class1的输出频次匹配Class2的数量规模
示例代码:
import pandas as pd import os from tensorflow.keras.preprocessing.image import ImageDataGenerator # 生成单类别数据的DataFrame def get_class_df(class_folder, label): img_paths = [os.path.join(class_folder, fname) for fname in os.listdir(class_folder) if fname.lower().endswith(('.png', '.jpg', '.jpeg'))] return pd.DataFrame({'image_path': img_paths, 'label': [label]*len(img_paths)}) # 指向训练集下的两类子文件夹 training_dir = "你的训练集路径" class1_dir = os.path.join(training_dir, "class1") class2_dir = os.path.join(training_dir, "class2") # 生成两类的DataFrame df_class1 = get_class_df(class1_dir, 0) # Class1标记为0 df_class2 = get_class_df(class2_dir, 1) # Class2标记为1 # 为Class1配置高强度数据增强 aug_class1 = ImageDataGenerator( rotation_range=15, height_shift_range=40, width_shift_range=40, zoom_range=0.15, horizontal_flip=True, vertical_flip=True, fill_mode='reflect', rescale=1/255.0 ) # Class2仅做归一化(数据量足够,无需额外增强) aug_class2 = ImageDataGenerator(rescale=1/255.0) # 创建两类的生成器 gen_class1 = aug_class1.flow_from_dataframe( df_class1, x_col='image_path', y_col='label', target_size=(96, 96), color_mode='rgb', class_mode='categorical', batch_size=64, shuffle=True ) gen_class2 = aug_class2.flow_from_dataframe( df_class2, x_col='image_path', y_col='label', target_size=(96, 96), color_mode='rgb', class_mode='categorical', batch_size=64, shuffle=True ) # 自定义合并生成器,让Class1的输出频次是原来的6倍(抵消数量差) def balanced_generator(gen_minority, gen_majority, minority_mult=6): while True: # 每次输出6个Class1的batch,再输出1个Class2的batch for _ in range(minority_mult): x_min, y_min = next(gen_minority) yield x_min, y_min x_maj, y_maj = next(gen_majority) yield x_maj, y_maj # 最终训练用的生成器 train_generator = balanced_generator(gen_class1, gen_class2) # 计算训练步数:(Class1数量*6 + Class2数量) // batch_size steps_per_epoch = (len(df_class1)*6 + len(df_class2)) // 64
方法2:结合类别权重与全局增强(快速补充方案)
如果不想拆分数据集,可以继续使用你原来的flow_from_directory,但在训练时加入类别权重,让模型对Class1的分类错误施加更重的惩罚,以此抵消数据量差异的影响。
示例代码:
# 计算类别权重:权重 = 总样本数 / (类别数 * 该类样本数) total_samples = len(df_class1) + len(df_class2) class_weight = { 0: total_samples / (2 * len(df_class1)), # Class1的权重 1: total_samples / (2 * len(df_class2)) # Class2的权重 } # 训练模型时传入class_weight参数 model.fit( aug_train_gen, steps_per_epoch=len(aug_train_gen), epochs=15, class_weight=class_weight )
方法3:物理复制Class1图像(简易但不推荐)
最直接的方式是手动复制Class1的图像文件,让其数量接近Class2,再用原来的增强器训练。但这种方式容易导致模型过拟合(因为只是重复相同样本),仅适合快速验证思路,不推荐用于正式训练。
关键注意事项
- 增强策略要适度,避免过度变换导致Class1图像失真,反而降低模型识别能力
- 训练时务必开启
shuffle=True,防止模型学习到样本顺序的无效信息 - 优先结合针对性增强+类别权重的方案,能同时平衡数据分布和模型损失权重,效果更稳定
内容的提问来源于stack exchange,提问作者Lorenzo Cutrupi
相关产品推荐
相关产品推荐

