You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用Stratified sampling优化不平衡图像数据集的训练效果?

针对类别不平衡图像数据集的分层抽样优化方案

针对你遇到的7万张标注图像、20类分布不均导致模型准确率仅68%的问题,分层抽样确实是解决类别不平衡的核心方向之一。结合图像任务的特性,我整理了几个实操性强的抽样策略,帮你优化训练过程:

1. 基础分层抽样:先锁定类别比例一致性

这是最直接的入门方案,核心是保证训练集中各类别的样本占比与原始数据集完全一致,避免模型被样本量多的大类“带偏”。

用Python可以借助sklearnStratifiedShuffleSplit快速实现,比如按7:3划分训练/测试集:

from sklearn.model_selection import StratifiedShuffleSplit
import pandas as pd

# 假设你有存储图像路径和标签的DataFrame
df = pd.read_csv("image_labels.csv")
X = df["image_path"].values
y = df["label"].values

# 初始化分层抽样器,固定随机种子保证可复现
sss = StratifiedShuffleSplit(n_splits=1, test_size=0.3, random_state=42)
for train_idx, test_idx in sss.split(X, y):
    train_df = df.iloc[train_idx]
    test_df = df.iloc[test_idx]

# 验证抽样后类别分布是否匹配原数据集
print("原数据集类别占比:\n", df["label"].value_counts(normalize=True))
print("训练集类别占比:\n", train_df["label"].value_counts(normalize=True))

如果小类的样本量本身还能接受(比如超过100张),这个方法就能快速改善模型的偏向性,但如果部分小类样本极少,就得结合下面的进阶策略。

2. 小类过抽样+大类欠抽样的组合策略

如果某些小类样本量不足(比如少于500张),单纯分层抽可能还是让模型学不到足够特征;而大类样本过多又会带来冗余、拖慢训练。这时候可以针对性处理:

  • 小类过抽样:不要简单重复样本(容易过拟合),建议用图像增强生成新样本——比如对小类图像做随机翻转、裁剪、亮度调整、高斯模糊等,人工扩充小类的有效样本量
  • 大类欠抽样:不是随机删除,而是选最具代表性的样本——比如用聚类算法把大类图像分组,每个簇只保留1-2张核心样本,减少冗余

举个代码示例(假设样本数<500为小类,>5000为大类):

import numpy as np
from sklearn.cluster import KMeans
from skimage.io import imread
from skimage.transform import resize

# 统计各类别样本数
class_counts = df["label"].value_counts()
small_classes = class_counts[class_counts < 500].index.tolist()
large_classes = class_counts[class_counts > 5000].index.tolist()

# 处理小类:用图像增强生成新样本(这里简化为重复+随机采样,实际替换为增强逻辑)
small_class_dfs = []
for cls in small_classes:
    cls_df = df[df["label"] == cls]
    # 扩充到500张样本
    expanded_df = pd.concat([cls_df]*(500 // len(cls_df) + 1)).sample(n=500, random_state=42)
    small_class_dfs.append(expanded_df)

# 处理大类:聚类选代表性样本
large_class_dfs = []
for cls in large_classes:
    cls_df = df[df["label"] == cls]
    # 提取图像特征(实际可用预训练CNN提取更精准的特征)
    features = []
    for path in cls_df["image_path"]:
        img = imread(path)
        img_resized = resize(img, (64, 64))
        features.append(img_resized.flatten())
    features = np.array(features)
    # 聚类后每个簇选1个样本,最终保留2000张
    kmeans = KMeans(n_clusters=2000, random_state=42)
    kmeans.fit(features)
    cluster_indices = [np.where(kmeans.labels_ == c)[0][0] for c in range(2000)]
    sampled_large_df = cls_df.iloc[cluster_indices]
    large_class_dfs.append(sampled_large_df)

# 中等类别直接分层抽样
medium_classes = [c for c in class_counts.index if c not in small_classes + large_classes]
medium_class_dfs = [df[df["label"] == c].sample(frac=0.7, random_state=42) for c in medium_classes]

# 合并最终训练集并打乱
final_train_df = pd.concat(small_class_dfs + large_class_dfs + medium_class_dfs)
final_train_df = final_train_df.sample(frac=1, random_state=42)

3. 基于模型反馈的主动抽样:优先学难分类样本

如果基础抽样后准确率提升有限,可以试试主动学习的思路:让模型自己“选”需要学的样本。

  1. 先用基础分层抽样的数据集训练一个初始模型(比如ResNet50)
  2. 用模型预测整个数据集,找出那些模型预测置信度低的样本(比如top1和top2类别概率差小于0.2)
  3. 把这些难分类样本加入训练集(注意保持类别比例),重新训练模型
  4. 重复上述过程,直到准确率达到预期

这种方法能让模型优先攻克容易混淆的样本,尤其是小类中容易被误判为大类的图像,针对性提升整体准确率。

关键注意事项

  • 验证/测试集也要分层:测试集必须严格匹配原始数据的类别分布,否则评估的准确率毫无参考价值
  • 避免小类简单重复采样:重复样本会导致模型过拟合,一定要结合图像增强生成新的有效样本
  • 全程监控类别分布:每次抽样后都要检查训练集的类别占比,确保没有偏离预期

建议你先从基础分层抽样开始,观察小类的召回率变化,如果小类表现依然很差,再逐步加入过抽样+欠抽样的组合策略,应该能有效提升模型的准确率。

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

火山引擎 最新活动