当前位置: 首页 > news >正文

机器学习类别不平衡问题:欠采样方法详解与实践

1. 不平衡分类问题概述

在机器学习实践中,我们经常会遇到类别分布严重不均衡的数据集。比如在信用卡欺诈检测中,正常交易可能占99.9%,而欺诈交易仅占0.1%。这种极端不平衡的数据分布会给模型训练带来显著挑战。

传统分类算法在这种场景下往往会偏向多数类,导致对少数类的识别率极低。举个例子,在一个99:1的数据集上,即使模型简单地将所有样本预测为多数类,也能获得99%的准确率,但这种"模型"对少数类的识别完全失败。

实际经验表明,当类别不平衡比例超过3:1时,就需要考虑采用专门的应对策略。而在医疗诊断、工业缺陷检测等领域,10:1甚至100:1的不平衡比例都很常见。

2. 重采样技术基础

2.1 过采样与欠采样对比

解决类别不平衡的常用方法分为两大类:

  • 过采样(Oversampling):增加少数类样本,如SMOTE算法
  • 欠采样(Undersampling):减少多数类样本,如本文介绍的多种方法

关键区别

  • 过采样可能引发过拟合风险,特别是简单复制样本时
  • 欠采样可能丢失重要信息,特别是随机删除样本时
  • 实际应用中常组合使用,如SMOTEENN算法

2.2 欠采样的核心挑战

欠采样不是简单地随机删除多数类样本,而是需要智能选择:

  • 哪些样本对分类边界影响最小?
  • 哪些样本可能是噪声或异常值?
  • 如何保留最具代表性的多数类样本?

3. 保留样本的欠采样方法

3.1 NearMiss系列算法

NearMiss通过距离度量选择保留的多数类样本,包含三个变种:

NearMiss-1

from imblearn.under_sampling import NearMiss undersampler = NearMiss(version=1, n_neighbors=3) X_res, y_res = undersampler.fit_resample(X, y)
  • 保留距离最近的3个少数类样本平均距离最小的多数类样本
  • 适合:希望强化分类边界的情况

NearMiss-2

undersampler = NearMiss(version=2, n_neighbors=3)
  • 保留距离最远的3个少数类样本平均距离最小的多数类样本
  • 适合:多数类内部存在多个子簇的情况

NearMiss-3

undersampler = NearMiss(version=3, n_neighbors_ver3=3)
  • 为每个少数类样本保留最近的N个多数类样本
  • 适合:少数类样本分布稀疏的情况

实测建议:NearMiss-3通常表现最好,但计算成本较高。在小数据集上(<10万样本)可以优先尝试。

3.2 压缩最近邻规则(CNN)

from imblearn.under_sampling import CondensedNearestNeighbour undersampler = CondensedNearestNeighbour(n_neighbors=1) X_res, y_res = undersampler.fit_resample(X, y)

算法原理:

  1. 将所有少数类样本放入"store"
  2. 逐个检查多数类样本:
    • 用当前store中的样本训练1-NN分类器
    • 若该样本被错误分类,则加入store
  3. 最终store中的样本即为保留集合

注意事项

  • 计算复杂度O(n²),只适合小型数据集
  • 可能保留部分噪声样本
  • 实际应用中常设置n_neighbors=3或5而非1

4. 删除样本的欠采样方法

4.1 Tomek Links

from imblearn.under_sampling import TomekLinks undersampler = TomekLinks() X_res, y_res = undersampler.fit_resample(X, y)

识别互为最近邻且类别相反的样本对(Tomek Link),然后:

  • 删除多数类样本
  • 或删除两个样本(更激进)

应用场景

  • 清理边界模糊的样本
  • 常作为后处理步骤与其他方法联用

4.2 编辑最近邻规则(ENN)

from imblearn.under_sampling import EditedNearestNeighbours undersampler = EditedNearestNeighbours(n_neighbors=3) X_res, y_res = undersampler.fit_resample(X, y)

算法步骤:

  1. 对每个样本,找到其3个最近邻
  2. 如果多数类样本被3个最近邻误分类,则删除
  3. 如果少数类样本被误分类,则删除其多数类最近邻

优势

  • 有效去除噪声点和边界模糊点
  • 保留清晰的分类结构

5. 组合策略的实际应用

5.1 单边选择(OSS)

from imblearn.under_sampling import OneSidedSelection undersampler = OneSidedSelection(n_neighbors=1, n_seeds_S=200) X_res, y_res = undersampler.fit_resample(X, y)

组合了:

  1. CNN算法选择初始种子点
  2. 移除Tomek Links进一步净化

参数建议

  • n_seeds_S控制初始样本量,通常设为≈少数类样本数
  • 适合中等规模数据集(1万-10万样本)

5.2 邻域清洁规则(NCR)

from imblearn.under_sampling import NeighbourhoodCleaningRule undersampler = NeighbourhoodCleaningRule( n_neighbors=3, threshold_cleaning=0.5) X_res, y_res = undersampler.fit_resample(X, y)

三阶段处理:

  1. 使用ENN找出可疑样本
  2. 计算每个可疑样本的类别纯度
  3. 删除纯度低于阈值的邻域样本

调优建议

  • threshold_cleaning通常设为0.5-0.7
  • 对高维数据表现良好

6. 实战经验与避坑指南

6.1 方法选择决策树

根据数据特征选择合适方法:

if 样本量<1万: if 边界清晰: 使用Tomek Links+CNN else: 使用NearMiss-3 elif 1万≤样本量≤10万: if 计算资源充足: 使用NCR else: 使用OSS else: # >10万样本 先随机欠采样到10万再应用上述方法

6.2 参数调优技巧

  1. k值选择

    • 高维数据:增大k(5-10)
    • 低维数据:小k(3-5)足够
    • 可通过交叉验证确定最优k
  2. 采样比例

    • 初始设为1:1
    • 逐步调整到验证集F1最高
    • 极端不平衡时可分层设置(如1:2,1:3)
  3. 与过采样结合

from imblearn.pipeline import Pipeline from imblearn.over_sampling import SMOTE pipeline = Pipeline([ ('oversample', SMOTE()), ('undersample', NeighbourhoodCleaningRule()) ])

6.3 常见问题排查

问题1:欠采样后模型过拟合

  • 检查是否删除了太多多数类样本
  • 尝试增加k值或调整采样比例
  • 考虑添加正则化项

问题2:计算时间过长

  • 对大数据集先做随机欠采样
  • 使用近似最近邻算法(如Annoy)
  • 降低n_neighbors参数

问题3:少数类识别率下降

  • 检查是否过度清理了边界样本
  • 尝试组合过采样方法
  • 调整分类决策阈值

7. 评估指标选择

不平衡分类需用专用评估指标:

指标公式适用场景
F1-Score2*(P*R)/(P+R)平衡考量精确率与召回率
G-Mean√(TPR*TNR)要求两类表现均衡
MCC(TPTN-FPFN)/√[(TP+FP)(TP+FN)(TN+FP)(TN+FN)]综合评估指标

实施建议

from sklearn.metrics import classification_report print(classification_report(y_test, y_pred, target_names=['多数类','少数类']))

8. 进阶技巧与扩展

8.1 聚类辅助欠采样

  1. 对多数类进行聚类
  2. 从每个簇中选取代表样本
  3. 结合少数类样本形成新数据集
from sklearn.cluster import KMeans kmeans = KMeans(n_clusters=100) majority_samples = X[y==0] kmeans.fit(majority_samples) centroids = kmeans.cluster_centers_

8.2 集成学习方法

from imblearn.ensemble import BalancedRandomForestClassifier model = BalancedRandomForestClassifier( sampling_strategy='auto', replacement=False) model.fit(X_train, y_train)

优势:

  • 自动处理类别不平衡
  • 保持原始数据分布
  • 通常比单次采样效果更好

8.3 代价敏感学习

通过class_weight参数调整误分类代价:

from sklearn.svm import SVC model = SVC(class_weight={0:1, 1:10}) model.fit(X_train, y_train)

设置原则:

  • 少数类误分类代价 = 不平衡比例倒数
  • 可通过网格搜索优化

9. 行业应用案例

9.1 金融风控场景

挑战

  • 欺诈交易占比<0.1%
  • 误判成本不对称

解决方案

  1. 使用NearMiss-3初步欠采样
  2. 应用NCR清理噪声
  3. 训练XGBoost模型
  4. 设置动态决策阈值

9.2 医疗诊断系统

特殊要求

  • 假阴性代价极高
  • 特征维度通常较高

实施方案

pipeline = Pipeline([ ('undersample', TomekLinks()), # 温和清理 ('classifier', LogisticRegression( class_weight='balanced', penalty='l1')) ])

9.3 工业质检应用

特点

  • 缺陷样本获取成本高
  • 生产环境变化快

策略

  • 在线增量学习
  • 动态调整采样比例
  • 定期更新清洁规则

10. 工具与资源推荐

10.1 Python库比较

库名称特点适用场景
imbalanced-learn算法全面通用场景
SMOTE-variants专注过采样需要最新SMOTE变体
unbalanced-dataset专注集成方法大数据集

10.2 可视化工具

from imblearn.visualization import plot_2d_space plot_2d_space(X_res, y_res, 'After Undersampling')

功能

  • 2D/3D数据分布展示
  • 采样前后对比
  • 决策边界可视化

10.3 基准数据集

常用测试数据集:

  1. Credit Card Fraud (Kaggle)
  2. NASA软件缺陷数据集
  3. KDD Cup 2008乳腺癌数据

获取方式:

from imblearn.datasets import fetch_datasets dataset = fetch_datasets()['credit_card'] X, y = dataset.data, dataset.target

11. 性能优化策略

11.1 并行计算加速

from joblib import parallel_backend with parallel_backend('loky', n_jobs=4): undersampler.fit_resample(X, y)

配置建议

  • 大数据集:n_jobs=-1(使用所有核心)
  • 小数据集:n_jobs=2-4

11.2 内存优化

对于超大矩阵:

undersampler = NearMiss(version=3, n_jobs=4, ratio='majority') # 仅处理多数类

11.3 近似算法

from sklearn.neighbors import NearestNeighbors # 使用LSH近似最近邻 nn = NearestNeighbors(algorithm='lsh', n_neighbors=3) undersampler.set_params(n_neighbors=nn)

12. 持续学习与更新

12.1 学术前沿跟踪

近年创新方向:

  • 深度学习与欠采样结合
  • 自适应采样比例
  • 在线流数据欠采样

12.2 实践社区推荐

  1. Kaggle不平衡分类比赛
  2. Imbalanced Learning Symposium
  3. 各大学机器学习课程专题

12.3 版本更新注意

imbalanced-learn主要版本变化:

  • v0.6+: 新增ClusterCentroids
  • v0.7+: 改进NCR效率
  • v0.8+: 添加GPU支持

升级检查:

import imblearn print(imblearn.__version__)
http://www.jsqmd.com/news/701354/

相关文章:

  • Open-AutoGLM:基于视觉大模型的手机端智能体部署与开发实战
  • Java方法级性能监控利器MyPerf4J:低侵入、高精度的性能剖析实战
  • PHP作用域的庖丁解牛
  • 打卡信奥刷题(3166)用C++实现信奥题 P7865 「EVOI-RD1」无人机航拍
  • 2026Q2单相调压器技术解析:三相隔离变压器/交流稳压器/交流调压器/医用隔离变压器/医疗变压器/医疗设备UPS/选择指南 - 优质品牌商家
  • 海外玩家伪装来源? 怎么用IP归属地识别
  • 5分钟搭建原神私服:KCN-GenshinServer图形化一键启动终极指南
  • 抑郁症 = 焦虑症?
  • 2026西南地区尼龙皮PVC皮带厂家名录及选购参考指南:成都托辊生产厂家、成都输送带厂家、沙石料厂皮带、液压输送机选择指南 - 优质品牌商家
  • Java JVM 垃圾回收调优指南
  • 如何确保多个 goroutine 的执行结果按启动顺序收集
  • 基于MCP协议与NotebookLM构建零幻觉AI编程助手知识库
  • TV 2.0技术解析:家庭娱乐与PC功能的融合方案
  • 2026年热门的验厂咨询/QS工业生产许可证验厂咨询行业公司推荐 - 行业平台推荐
  • 为什么你学 AI 总是学不会?因为你踩了这 3 个坑
  • smol developer:基于LLM的智能代码生成工具,实现从需求到原型的快速开发
  • AI Agent Harness Engineering 做测试:用例生成、回归与缺陷定位
  • 【限时开源】工业级C++ MCP网关核心模块(含动态路由热加载+熔断降级SDK):GitHub Star破3k后首次完整解析
  • 现在不学C++26合约架构,半年后将无法维护下一代嵌入式/金融核心系统?4步构建可审计、可降级、可形式化验证的合约架构
  • Cursor Free VIP:3步解锁AI编程助手Pro功能的终极解决方案
  • Spyder 6.0:科学Python开发的7大效率革命
  • 可控硅(晶闸管)基础知识及应用电路Multisim电路仿真
  • Windows Media Audio技术解析与应用实践
  • 从零构建操作系统内核:引导、内存管理与多任务实现
  • 告别手动字幕:OpenLRC如何用AI解放你的创作时间
  • 解决 Leaflet 地图在移动端溢出导致导航栏不可见的问题
  • NVIDIA DGX Spark:本地化AI开发的高性能解决方案
  • Kubernetes日志调试进入“所见即所得”时代——VSCode 2026容器日志实时查看技术白皮书(内部泄露版)
  • 检测三位随机数中重复数字的Python实现方法
  • Agent 一接 Webhook 回调就开始状态穿越:从 Outbox 事务到事件去重窗口的工程实战