python imblearn 处理样本不平衡,svm验证

本文探讨了样本不均衡问题,介绍了过采样、欠采样、设置权重和集成思想四种解决方案,并通过Python代码示例展示了RandomOverSample、SMOTE、ADASYM和RandomUnderSampler等方法的应用,以及不平衡数据集对SVM性能的影响。

参考地址:https://blog.youkuaiyun.com/tonydz0523/article/details/84325823

1.样本分布不均的解决方法:

1.1 .过采样 通过增加分类中样本较少的类别的采样数量来实现平衡,最直接的方法是简单复制小样本数据时加入随机噪声、干扰数据等。
1.2 .欠采样(下采样) 通过减少分类中多数类样本的数量来实现样本均衡,最直接的方法是随机去掉一些多数类样本来减小多数类的规模,缺点是会丢失多数类中的一些重要信息。
1.3 .设置权重 对不同样本数量的类别赋予不同的权重(通常会设置为与样本量成反比 n samples / (n class * n bincount(y) )
1.4 .集成思想 每次生成训练集时,使用全量小样本,同时从大样本量中随机抽取等量数据量,反复多次会得到多个训练模型。最后在应用时,采用组合(例如投票、加权投票等)产生分类预测结果。

2.python处理

2.1 生成数据集
from collections import Counter
from sklearn.datasets import  make_classification
x,y = make_classification(n_samples=100000,n_features=10,n_informative=2,n_redundant=2, n_repeated=0, n_classes=3, n_clusters_per_class=1, weights=[0.89,0.1,0.01], class_sep=0.3,random_state=2020)
Counter(y)
Counter({0: 88486, 1: 10211, 2: 1303})
2.2 RandomOverSample(过采样)
使用RandomOverSampler从少数类的样本中进行随机采样来增加新的样本使各个分类均衡
from imblearn.over_sampling import RandomOverSampler
x_resampled,y_resampled = RandomOverSampler(random_state=0).fit_sample(x,y)
Counter(y_resampled)
Counter({0: 88486, 1: 88486, 2: 88486})
2.3 SMOTE(过采样)
对于少数类样本a, 随机选择一个最近邻的样本b, 然后从a与b的连线上随机选取一个点c作为新的少数类样本
from imblearn.over_sampling import SMOTE
x_resampled,y_resampled = SMOTE().fit_sample(x,y)
Counter(y_resampled)
Counter({0: 88486, 1: 88486, 2: 88486})
2.4 ADASYM(过采样)
ADASYN: 关注的是在那些基于K最近邻分类器被错误分类的原始样本附近生成新的少数类样本
from imblearn.over_sampling import ADASYN
x_resampled,y_resampled = ADASYN().fit_sample(x,y)
Counter(y_resampled)
Counter({0: 88486, 1: 88198, 2: 88138})
2.5 RandomUnderSampler(下采样)
andomUnderSampler函数是一种快速并十分简单的方式来平衡各个类别的数据: 随机选取数据的子集.
from imblearn.under_sampling import RandomUnderSampler
x_resampled,y_resampled = RandomUnderSampler().fit_sample(x,y)
Counter(y_resampled)
Counter({0: 1303, 1: 1303, 2: 1303})
2.6 设置权重(sklearn SVM举例)
class_weight : {dict, 'balanced'}, optional
    Set the parameter C of class i to class_weight[i]*C for
    SVC. If not given, all classes are supposed to have
    weight one.
    The "balanced" mode uses the values of y to automatically adjust
    weights inversely proportional to class frequencies in the input data
    as ``n_samples / (n_classes * np.bincount(y))``

    
def __init__(self, C=1.0, kernel='rbf', degree=3, gamma='scale',
             coef0=0.0, shrinking=True, probability=False,
             tol=1e-3, cache_size=200, class_weight=None,
             verbose=False, max_iter=-1, decision_function_shape='ovr',
             break_ties=False,
             random_state=None):
    
estimator = svm.SVC(max_iter=1,probability=True,class_weight='balanced',random_state=1234) ##此处还可以采用网格寻参
3. 测试样本不均衡对svm的影响
from sklearn import svm
clf = svm.SVC(class_weight='balanced',max_iter=10000)
from collections import Counter
from sklearn.datasets import  make_classification
x,y = make_classification(n_samples=10000,n_features=10,n_informative=2,n_redundant=2, n_repeated=0, n_classes=2, n_clusters_per_class=1, weights=[0.999,0.001], class_sep=0.3,random_state=2020)
Counter(y)
Counter({0: 9945, 1: 55})
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
x_stand = StandardScaler().fit_transform(x)
x_train,x_test,y_train,y_test = train_test_split(x_stand,y,test_size=0.2)
clf.fit(x_train,y_train)
pred = clf.predict(x_test)
pred
array([0, 1, 0, ..., 0, 0, 0])
y_test
array([0, 0, 0, ..., 0, 0, 0])
from sklearn.metrics import accuracy_score
accuracy_score(pred,y_test)
0.911
from imblearn.over_sampling import RandomOverSampler
x_resampled,y_resampled = RandomOverSampler(random_state=0).fit_sample(x,y)
Counter(y_resampled)
Counter({0: 9945, 1: 9945})
x_stand = StandardScaler().fit_transform(x_resampled)
x_train,x_test,y_train,y_test = train_test_split(x_stand,y_resampled,test_size=0.2)
clf.fit(x_train,y_train)
pred = clf.predict(x_test)
pred
array([0, 1, 0, ..., 1, 1, 0])
accuracy_score(pred,y_test)
0.9595274007038713
<think>我们面对的问题是处理极度平衡数据集的交叉验证划分方法。根据用户之前的需求,他们已经尝试了分层抽样和上采样,但遇到了稀有类别样本过少(最少仅1个)导致StratifiedKFold警告的问题。结合站内引用[^3][^4]的内容,我们将给出系统解决方案。 ### 问题分析 在分类任务中,当数据集的类别分布极度平衡时(某些类别的样本数远小于其他类别),直接使用标准交叉验证方法会导致: 1. 某些折可能包含稀有类别的样本(违反分层原则) 2. 模型评估偏差(稀有类别的性能被忽视) ### 解决方案:分层K折交叉验证的改进方法 我们采用**分层抽样+上采样**的组合策略(如引用[^4]所述),具体步骤如下: #### 1. 识别并处理稀有类别 定义:若某类样本数$N_c < K$(K为折数),则视为稀有类别。 处理方式: - 对上采样后的样本进行分层抽样,确保每折都包含所有类别 - 模型训练时仅使用原始样本(上采样样本仅用于划分验证集) 数学表示: 设原始数据集$D = \{(x_i,y_i)\}_{i=1}^N$,类别集合$C$,第$c$类样本数为$n_c$。 对每个稀有类别$c \in C_{\text{rare}} = \{c \mid n_c < K\}$,补充样本数: $$\Delta n_c = \max(K - n_c, 0)$$ 通过有放回抽样生成新增样本$D_c^{\text{new}} = \{(x_j,y_j)\}_{j=1}^{\Delta n_c}$,构成扩展数据集: $$D' = D \cup \bigcup_{c\in C_{\text{rare}}} D_c^{\text{new}}$$ #### 2. 分层抽样划分 使用`StratifiedKFold`对扩展数据集$D'$进行划分(引用[^3]): - 每折中各类别比例与扩展数据集$D'$一致 - 划分完成后移除扩展样本(仅保留原始样本索引) ```python from sklearn.model_selection import StratifiedKFold import numpy as np from sklearn.utils import resample def balanced_stratified_kfold(X, y, n_splits=5, random_state=None): """ 处理极度平衡数据的分层K折交叉验证生成器 :param X: 特征列表(此处为图像路径列表) :param y: 标签列表(整数类别标签) :param n_splits: 折数K :param random_state: 随机种子 :yield: 每折的(train_idx, val_idx)索引 """ y = np.array(y) orig_len = len(y) # 识别稀有类别(样本数<n_splits) unique, counts = np.unique(y, return_counts=True) rare_classes = unique[counts < n_splits] # 上采样稀有类别 sampled_X = list(X) sampled_y = list(y) for cls in rare_classes: cls_indices = np.where(y == cls)[0] if len(cls_indices) == 0: continue # 计算需要补充的样本数 needed = n_splits - len(cls_indices) if needed <= 0: continue # 有放回抽样生成新样本 new_indices = resample(cls_indices, replace=True, n_samples=needed, random_state=random_state) # 添加到采样列表 sampled_X.extend([X[i] for i in new_indices]) sampled_y.extend([y[i] for i in new_indices]) sampled_y = np.array(sampled_y) # 使用扩展后的数据集进行分层划分 skf = StratifiedKFold(n_splits=n_splits, shuffle=True, random_state=random_state) # 生成划分索引 for train_idx, val_idx in skf.split(sampled_X, sampled_y): # 仅保留原始样本的索引(移除上采样添加的样本) train_idx_orig = [idx for idx in train_idx if idx < orig_len] val_idx_orig = [idx for idx in val_idx if idx < orig_len] yield train_idx_orig, val_idx_orig ``` #### 3. 验证划分效果 对每折统计类别分布,确保稀有类别在各折中均匀出现: ```python # 示例:验证划分的平衡性 for fold, (train_idx, val_idx) in enumerate(balanced_stratified_kfold(X, y, n_splits=5), 1): print(f"\nFold {fold}:") print(f" Train classes: {np.unique(y[train_idx], return_counts=True)}") print(f" Val classes: {np.unique(y[val_idx], return_counts=True)}") ``` ### 替代方案对比 | 方法 | 优点 | 缺点 | |--------------------|-------------------------------|-------------------------------| | 上采样划分(本文) | 保证每折包含所有类别 | 可能引入重复样本 | | SMOTE[^4] | 生成多样化合成样本 | 对高维数据效果有限 | | 代价敏感学习[^4] | 直接修改损失函数,无需数据增广 | 需调整权重参数,增加调优难度 | ### 工程实践建议 1. **图像数据特殊处理**: 在目标检测任务中,可对稀有类别进行**目标级增强**(复制-粘贴增强) 2. **评估指标选择**: 使用宏平均F1-score(Macro-F1)而非准确率(Accuracy)[^4],公式如下: $$F1_{\text{macro}} = \frac{1}{|C|}\sum_{c\in C} F1_c$$ 其中$F1_c = 2 \times \frac{\text{precision}_c \times \text{recall}_c}{\text{precision}_c + \text{recall}_c}$ 3. **分阶段训练**: - 第一阶段:所有数据训练基础模型 - 第二阶段:对稀有类别微调(使用上采样或增强数据) ### 应用示例 将上述方法集成到YOLO数据准备中: ```python # 读取图像和标签路径(代码同前) image_paths = [...] # 图像路径列表 label_paths = [...] # 对应标签路径列表 # 提取每张图像的类别标签(处理多对象) def extract_dominant_class(label_path): # 实现略:返回出现最多的类别,若无标签返回-1 pass y = [extract_dominant_class(lp) for lp in label_paths] # 生成平衡划分 kfold_generator = balanced_stratified_kfold( X=image_paths, y=y, n_splits=5, random_state=42 ) # 导出各折数据 for fold, (train_idx, val_idx) in enumerate(kfold_generator, 1): # 创建fold目录并复制文件 export_fold_data(train_idx, val_idx, fold_dir=f"fold_{fold}") ``` §§相关问题§§ 1. 如何评估极度平衡数据集的模型性能?[^4] 2. 上采样与SMOTE方法在实际应用中有何差异?[^4] 3. 交叉验证中如何处理时间序列数据?[^3] </think>针对处理极度平衡数据集交叉验证划分的问题,我将结合站内引用为您提供系统解决方案,核心思路是**分层抽样、智能上采样和评估指标优化**的三步策略。 ### 一、理论基础:平衡数据集的核心挑战 在类别分布极度均的数据集中(如您遇到的类别-1仅1个样本),传统分层抽样会失效: $$N_{\min} < K \implies \text{StratifiedKFold警告}$$ 其中$N_{\min}$为最少样本类别的数量,$K$为折数(参考文献[^3])。 ### 二、完整解决方案 #### 步骤1:分层抽样升级(解决稀有类别问题) 采用**自适应分层抽样算法**,动态处理稀有类别: ```python import numpy as np from collections import Counter from sklearn.utils import resample def adaptive_stratified_split(y, n_splits=5, min_samples=5): """ y: 类别标签列表 n_splits: 交叉验证折数 min_samples: 最小样本阈值(每折至少保证的样本数) 返回:改进的分层抽样索引 """ # 识别稀有类别(样本数 < min_samples * n_splits) class_counts = Counter(y) rare_classes = [cls for cls, cnt in class_counts.items() if cnt < min_samples * n_splits] # 上采样稀有类别 indices = np.arange(len(y)) for cls in rare_classes: cls_indices = indices[y == cls] needed = min_samples * n_splits - len(cls_indices) if needed > 0: # 有放回抽样补足样本(参考文献[^4]) new_idx = resample(cls_indices, replace=True, n_samples=needed, random_state=42) y = np.concatenate([y, [cls]*needed]) indices = np.concatenate([indices, new_idx]) # 执行分层KFold skf = StratifiedKFold(n_splits=n_splits, shuffle=True) return [(train_idx, test_idx) for train_idx, test_idx in skf.split(indices, y)] ``` #### 步骤2:SMOTE智能上采样(解决样本足问题) 对稀有类别采用SMOTE合成样本(参考文献[^4]): ```python from imblearn.over_sampling import SMOTE def apply_smart_oversampling(X_train, y_train): """ X_train: 训练集特征 y_train: 训练集标签 返回:上采样后的平衡数据集 """ # 只对少数类上采样(样本量<总样本量/类别数) sampling_strategy = { cls: max(2*np.mean(list(Counter(y_train).values())), 100) for cls in np.unique(y_train) if Counter(y_train)[cls] < len(y_train)/len(np.unique(y_train)) } smote = SMOTE( sampling_strategy=sampling_strategy, k_neighbors=min(5, min(sampling_strategy.values()) - 1), # 动态k值 random_state=42 ) return smote.fit_resample(X_train, y_train) ``` #### 步骤3:代价敏感学习(优化模型训练) 在交叉验证中集成代价敏感权重(参考文献[^4]): ```python from sklearn.svm import SVC from sklearn.model_selection import cross_val_score # 计算类别权重(样本越少权重越高) class_weights = { cls: len(y)/(len(np.unique(y)) * count) for cls, count in Counter(y).items() } model = SVC(class_weight=class_weights) # 可替换为任意分类器 # 执行带权重的交叉验证 scores = cross_val_score( model, X, y, cv=adaptive_stratified_split(y), # 使用步骤1的分层方法 scoring='f1_macro' # 使用宏平均F1(参考文献[^4]) ) ``` ### 三、数学模型优化 为防止过拟合,引入**类别权重正则项**: $$\mathcal{L} = \underbrace{\sum_{i=1}^{N} w_{y_i} \ell(f(x_i), y_i)}_{\text{加权损失}} + \lambda \underbrace{\sum_{c=1}^{C} \frac{1}{N_c} \|\mathbf{w}_c\|^2}_{\text{类别正则化}}$$ 其中: - $w_{y_i} = \frac{N}{C \cdot N_{y_i}}$ 为样本权重 - $\lambda$ 控制正则化强度 - $C$ 为类别总数 - $N_c$ 为类别$c$的样本数 ### 四、实施效果验证 验证划分平衡性的方法: ```python def check_stratification(train_idx, val_idx, y): """验证各类别在训练集/验证集的分布""" train_dist = Counter(y[train_idx]) val_dist = Counter(y[val_idx]) # 计算最大相对偏差(应<20%) max_dev = max( abs(train_dist[cls]/len(train_idx) - val_dist[cls]/len(val_idx)) / (train_dist[cls]/len(train_idx) + 1e-6) for cls in set(y) ) return max_dev < 0.2 ``` ### 五、同场景推荐方案 | 场景 | 推荐方法 | 优势 | |------|----------|------| | 类别数少 | SMOTE + 代价敏感学习 | 避免简单样本重复 | | 类别数多 | 自适应分层抽样 | 计算效率高 | | 小样本类别 | 目标级复制增强 | 保持语义合理性 | | 大数据集 | 欠采样多数类 | 减少计算开销 | > **工程提示**:在计算机视觉任务中,优先使用**目标级复制增强**而非像素级上采样,直接复制稀有类别目标到新背景(如将少量电路板元件粘贴到同背景图上)。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值