imbalanced-learn欠采样技术全解析
【免费下载链接】imbalanced-learn 项目地址: https://gitcode.com/gh_mirrors/imb/imbalanced-learn
本文全面解析了imbalanced-learn库中的多种欠采样技术,包括随机欠采样(RandomUnderSampler)、基于近邻的NearMiss系列算法、Tomek Links与Condensed Nearest Neighbour方法,以及编辑最近邻(ENN)和邻域清理规则(NCR)。文章详细介绍了每种算法的核心原理、参数配置、实现机制和适用场景,为处理类别不平衡问题提供了全面的技术指南和实践建议。
随机欠采样(RandomUnderSampler)策略
随机欠采样(RandomUnderSampler)是处理类别不平衡数据最简单直观的方法之一。该方法通过从多数类中随机删除样本来平衡数据集,使得各个类别的样本数量达到相对均衡的状态。作为imbalanced-learn库中最基础的欠采样技术,RandomUnderSampler以其简单高效的特点成为处理类别不平衡问题的首选方法。
核心原理与工作机制
RandomUnderSampler的工作原理基于概率抽样理论,其核心思想是通过随机选择的方式减少多数类样本的数量。该算法的工作流程可以通过以下流程图清晰地展示:
参数配置详解
RandomUnderSampler提供了灵活的配置选项,让用户可以根据具体需求调整采样行为:
| 参数名称 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| sampling_strategy | str/dict | 'auto' | 采样策略,控制各个类别的目标样本数量 |
| random_state | int/RandomState | None | 随机数生成器种子,确保结果可重现 |
| replacement | bool | False | 是否使用有放回抽样 |
sampling_strategy参数详解:
'auto':自动将多数类采样到与少数类相同数量'majority':仅对多数类进行欠采样'not minority':对所有非少数类进行欠采样'all':对所有类进行重采样- 字典:自定义每个类别的目标样本数量
代码实现示例
下面通过一个完整的代码示例展示RandomUnderSampler的使用方法:
import numpy as np
from collections import Counter
from sklearn.datasets import make_classification
from imblearn.under_sampling import RandomUnderSampler
# 创建不平衡数据集
X, y = make_classification(
n_classes=2,
class_sep=2,
weights=[0.1, 0.9],
n_informative=3,
n_redundant=1,
flip_y=0,
n_features=20,
n_clusters_per_class=1,
n_samples=1000,
random_state=42
)
print('原始数据集分布:', Counter(y))
# 初始化RandomUnderSampler
rus = RandomUnderSampler(
sampling_strategy='auto', # 自动平衡到少数类数量
random_state=42, # 确保结果可重现
replacement=False # 无放回抽样
)
# 执行欠采样
X_resampled, y_resampled = rus.fit_resample(X, y)
print('重采样后数据集分布:', Counter(y_resampled))
print('选择的样本索引:', rus.sample_indices_[:10]) # 显示前10个被选中的样本索引
采样策略对比分析
不同的sampling_strategy设置会产生不同的采样效果,下表展示了各种策略的对比:
| 策略类型 | 目标类别 | 采样效果 | 适用场景 |
|---|---|---|---|
| auto | 多数类 → 少数类 | 完全平衡 | 标准的二分类问题 |
| majority | 仅多数类 | 减少多数类 | 需要保留所有少数类样本 |
| not minority | 所有非少数类 | 部分平衡 | 多分类问题中的主要类别 |
| all | 所有类别 | 自定义平衡 | 需要精确控制每个类别的样本数量 |
算法优势与局限性
优势:
- 简单高效:算法实现简单,计算复杂度低
- 内存友好:减少数据集大小,降低内存需求
- 训练加速:较小的数据集意味着更快的训练速度
- 广泛适用:支持多分类问题和各种数据类型
局限性:
- 信息损失:随机删除可能丢失重要样本信息
- 过拟合风险:如果删除的样本包含重要特征,可能导致模型欠拟合
- 不稳定性:不同的随机种子可能产生不同的结果
实际应用场景
RandomUnderSampler在以下场景中特别有用:
- 大规模数据集:当原始数据集非常大时,欠采样可以显著减少计算资源需求
- 探索性分析:在数据探索阶段快速获得平衡的数据视图
- 基准测试:作为其他复杂采样方法的性能对比基准
- 实时系统:需要快速响应的在线学习系统
性能优化建议
为了获得最佳的采样效果,建议遵循以下最佳实践:
- 设置随机种子:使用random_state参数确保实验的可重现性
- 多次采样:进行多次随机采样并评估结果的稳定性
- 结合交叉验证:在交叉验证过程中应用采样,避免数据泄露
- 监控信息损失:通过特征重要性分析评估采样对模型的影响
与其他采样方法的对比
RandomUnderSampler作为最简单的欠采样方法,与其他方法相比具有独特的定位:
通过合理配置参数和结合业务场景,RandomUnderSampler能够为类别不平衡问题提供一个简单而有效的解决方案。虽然它可能不是所有情况下的最优选择,但其简洁性和高效性使其成为处理不平衡数据的重要工具之一。
基于近邻的欠采样方法:NearMiss系列
在处理类别不平衡数据集时,NearMiss系列算法提供了一种基于距离度量的智能欠采样方法。与简单的随机欠采样不同,NearMiss通过分析样本间的空间关系,选择性地保留那些与少数类样本具有特定距离关系的大多数类样本,从而在减少多数类样本数量的同时保持数据分布的完整性。
NearMiss算法原理
NearMiss算法基于k近邻思想,通过计算多数类样本与少数类样本之间的距离关系来进行样本选择。该系列包含三个不同版本,每个版本采用不同的选择策略:
NearMiss-1:最近邻平均距离最小化
NearMiss-1选择那些与少数类样本平均距离最小的多数类样本。具体来说,对于每个多数类样本,算法计算其到k个最近少数类样本的平均距离,然后选择平均距离最小的样本。
算法流程:
- 对每个多数类样本,找到k个最近的少数类样本
- 计算到这些少数类样本的平均距离
- 选择平均距离最小的样本进行保留
from imblearn.under_sampling import NearMiss
from collections import Counter
from sklearn.datasets import make_classification
# 创建不平衡数据集
X, y = make_classification(n_classes=2, weights=[0.1, 0.9],
n_samples=1000, random_state=42)
print("原始数据集分布:", Counter(y))
# 应用NearMiss-1
nm1 = NearMiss(version=1, n_neighbors=3)
X_resampled, y_resampled = nm1.fit_resample(X, y)
print("NearMiss-1处理后分布:", Counter(y_resampled))
NearMiss-2:最远邻平均距离最小化
NearMiss-2采用相反的思路,选择那些与少数类样本最远邻平均距离最小的多数类样本。这种方法关注的是样本在特征空间中的边界区域。
算法特点:
- 计算每个多数类样本到最远k个少数类样本的平均距离
- 选择这些平均距离最小的样本
- 更适合处理类别边界模糊的情况
# 应用NearMiss-2
nm2 = NearMiss(version=2, n_neighbors=3)
X_resampled2, y_resampled2 = nm2.fit_resample(X, y)
print("NearMiss-2处理后分布:", Counter(y_resampled2))
NearMiss-3:两阶段选择策略
NearMiss-3采用更加复杂的两阶段选择过程,首先预选与少数类样本相关的多数类样本,然后从中选择最具代表性的样本。
两阶段流程:
| 阶段 | 描述 | 目的 |
|---|---|---|
| 第一阶段 | 使用k近邻找到与少数类样本相关的多数类样本 | 缩小候选样本范围 |
| 第二阶段 | 从预选样本中选择与少数类样本平均距离最大的样本 | 保留边界样本 |
# 应用NearMiss-3
nm3 = NearMiss(version=3, n_neighbors=3, n_neighbors_ver3=3)
X_resampled3, y_resampled3 = nm3.fit_resample(X, y)
print("NearMiss-3处理后分布:", Counter(y_resampled3))
参数配置与调优
NearMiss算法提供了多个可配置参数,合理设置这些参数对算法效果至关重要:
主要参数说明:
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| version | int | 1 | NearMiss版本(1、2或3) |
| n_neighbors | int | 3 | 近邻数量,用于距离计算 |
| n_neighbors_ver3 | int | 3 | NearMiss-3专用的近邻参数 |
| sampling_strategy | str/dict | 'auto' | 采样策略配置 |
| n_jobs | int | None | 并行计算线程数 |
参数调优建议:
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier
from sklearn.pipeline import Pipeline
# 创建包含NearMiss的管道
pipeline = Pipeline([
('sampler', NearMiss()),
('classifier', RandomForestClassifier())
])
# 参数网格
param_grid = {
'sampler__version': [1, 2, 3],
'sampler__n_neighbors': [3, 5, 7],
'sampler__n_neighbors_ver3': [3, 5]
}
# 网格搜索优化
grid_search = GridSearchCV(pipeline, param_grid, cv=5, scoring='f1')
grid_search.fit(X, y)
print("最佳参数:", grid_search.best_params_)
实际应用场景
NearMiss系列算法在以下场景中表现优异:
- 高维数据:当特征维度较高时,基于距离的方法能更好地捕捉样本间的关系
- 边界样本重要:当分类边界附近的样本对模型性能影响较大时
- 数据质量要求高:需要保持数据分布特性的场景
与其他方法的对比:
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| NearMiss-1 | 保留接近少数类的样本 | 可能过度简化边界 | 类别分离明显的数据 |
| NearMiss-2 | 关注边界区域 | 计算复杂度较高 | 边界模糊的数据 |
| NearMiss-3 | 两阶段选择,更精确 | 参数调优复杂 | 高质量要求的应用 |
| 随机欠采样 | 简单快速 | 可能丢失重要信息 | 大数据集初步处理 |
性能考量与最佳实践
在使用NearMiss算法时,需要注意以下性能考量:
- 计算复杂度:NearMiss算法需要计算距离矩阵,时间复杂度为O(n²),在大数据集上可能较慢
- 参数敏感性:近邻数量k对结果影响较大,需要通过交叉验证确定最优值
- 数据标准化:距离计算对特征尺度敏感,建议先进行特征标准化
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
# 推荐的数据处理管道
pipeline = Pipeline([
('scaler', StandardScaler()),
('sampler', NearMiss(version=1, n_neighbors=5)),
('classifier', RandomForestClassifier())
])
# 训练和评估
pipeline.fit(X_train, y_train)
accuracy = pipeline.score(X_test, y_test)
print(f"模型准确率: {accuracy:.3f}")
NearMiss系列算法为处理类别不平衡问题提供了基于距离的智能解决方案。通过合理选择算法版本和参数配置,可以在减少多数类样本的同时保持数据集的重要特征,为后续机器学习模型训练提供高质量的数据基础。
Tomek Links与Condensed Nearest Neighbour
在解决类别不平衡问题的欠采样技术中,Tomek Links和Condensed Nearest Neighbour(CNN)是两种基于原型选择的重要方法。它们通过不同的策略来识别和移除对分类边界影响较小的样本,从而改善数据集的平衡性。
Tomek Links算法原理
Tomek Links算法基于一个简单而有效的概念:识别并移除边界上的噪声样本。具体来说,Tomek Link是指两个不同类别的样本点,它们互为最近邻。这种成对关系通常出现在类别的边界区域,其中至少有一个样本可能是噪声或对分类决策边界贡献不大的点。
算法核心机制
Tomek Links算法的核心在于is_tomek静态方法,该方法检测Tomek链接对:
@staticmethod
def is_tomek(y, nn_index, class_type):
links = np.zeros(len(y), dtype=bool)
class_excluded = [c for c in np.unique(y) if c not in class_type]
for index_sample, target_sample in enumerate(y):
if target_sample in class_excluded:
continue
if y[nn_index[index_sample]] != target_sample:
if nn_index[nn_index[index_sample]] == index_sample:
links[index_sample] = True
return links
该方法的执行流程如下:
参数配置与使用
TomekLinks类的主要参数包括:
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| sampling_strategy | str/dict | "auto" | 采样策略,控制哪些类别需要欠采样 |
| n_jobs | int/None | None | 并行计算的工作进程数 |
使用示例:
from imblearn.under_sampling import TomekLinks
from collections import Counter
from sklearn.datasets import make_classification
# 创建不平衡数据集
X, y = make_classification(n_classes=2, weights=[0.1, 0.9],
n_samples=1000, random_state=42)
print('原始数据集分布:', Counter(y))
# 应用Tomek Links
tl = TomekLinks()
X_res, y_res = tl.fit_resample(X, y)
print('处理后数据集分布:', Counter(y_res))
Condensed Nearest Neighbour算法
Condensed Nearest Neighbour(CNN)是一种更复杂的原型选择方法,旨在找到能够保持原始数据集分类性能的最小样本子集。该算法通过迭代过程构建一个"浓缩"的训练集。
算法执行流程
CNN算法的核心在于_fit_resample方法,其执行过程如下:
关键参数说明
CondensedNearestNeighbour类提供多个可配置参数:
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| sampling_strategy | str/dict | "auto" | 采样策略配置 |
| random_state | int/RandomState | None | 随机数生成器种子 |
| n_neighbors | int/estimator | None | 最近邻数量或分类器对象 |
| n_seeds_S | int | 1 | 初始多数类样本数量 |
| n_jobs | int/None | None | 并行计算设置 |
实际应用示例
from imblearn.under_sampling import CondensedNearestNeighbour
from sklearn.neighbors import KNeighborsClassifier
# 使用自定义KNN分类器
cnn = CondensedNearestNeighbour(
n_neighbors=KNeighborsClassifier(n_neighbors=3),
random_state=42,
n_seeds_S=2
)
X_res_cnn, y_res_cnn = cnn.fit_resample(X, y)
print('CNN处理后样本分布:', Counter(y_res_cnn))
print('使用的KNN分类器数量:', len(cnn.estimators_))
算法比较与选择指南
Tomek Links和CNN虽然都属于原型选择方法,但在适用场景和效果上存在显著差异:
| 特性 | Tomek Links | Condensed Nearest Neighbour |
|---|---|---|
| 计算复杂度 | 低 | 高 |
| 样本减少程度 | 温和 | 显著 |
| 主要目标 | 移除边界噪声 | 构建最小充分训练集 |
| 多类别支持 | 一对多策略 | 一对多策略 |
| 参数敏感性 | 低 | 中等 |
适用场景建议
选择Tomek Links当:
- 数据集相对较小,计算资源有限
- 只需要轻微调整样本分布
- 主要目标是清理边界噪声样本
- 希望保持尽可能多的原始数据信息
选择Condensed Nearest Neighbour当:
- 数据集较大,需要显著减少样本数量
- 追求最小化的训练集规模
- 计算资源充足,可以接受较高的时间复杂度
- 需要构建高度浓缩的代表性样本集
性能优化技巧
- 并行处理优化:对于大型数据集,设置
n_jobs参数可以显著加速计算过程 - KNN配置调优:在CNN中,通过调整
n_neighbors参数可以平衡浓缩效果和计算效率 - 随机种子设置:使用固定的
random_state确保结果可重现 - 采样策略定制:根据具体需求定制
sampling_strategy参数,精确控制各类别的采样比例
实际应用注意事项
在使用这两种算法时,需要注意以下问题:
- 数据预处理:确保数据已经过适当的标准化处理,因为距离计算对尺度敏感
- 类别标签处理:算法支持多类别问题,但采用一对多策略处理
- 稀疏数据支持:两种算法都支持稀疏矩阵输入
- 特征名称保留:从0.10版本开始支持特征名称的保留
通过合理选择和应用Tomek Links与Condensed Nearest Neighbour算法,可以有效地处理类别不平衡问题,提升机器学习模型的性能和泛化能力。
编辑最近邻与邻域清理规则
在机器学习处理类别不平衡数据集时,编辑最近邻(Edited Nearest Neighbours, ENN)和邻域清理规则(Neighbourhood Cleaning Rule, NCR)是两种基于原型选择的重要欠采样技术。这些方法通过智能地移除靠近决策边界的噪声样本,而不是简单地随机删除多数类样本,从而在保持数据集质量的同时实现类别平衡。
编辑最近邻(ENN)算法原理
编辑最近邻算法基于一个核心思想:如果一个样本的大多数最近邻样本属于不同的类别,那么这个样本很可能位于决策边界附近或者是噪声样本,应该被移除。
ENN算法的工作流程如下:
ENN算法在imbalanced-learn中的实现提供了两种选择策略:
| 策略类型 | 描述 | 保守程度 | 移除样本数量 |
|---|---|---|---|
kind_sel="all" | 所有最近邻都必须属于相同类别才保留样本 | 较不保守 | 较多 |
kind_sel="mode" | 多数最近邻属于相同类别即可保留样本 | 较保守 | 较少 |
ENN算法实现详解
在imbalanced-learn中,ENN算法的核心实现逻辑如下:
def _fit_resample(self, X, y):
self._validate_estimator()
idx_under = np.empty((0,), dtype=int)
self.nn_.fit(X)
for target_class in np.unique(y):
if target_class in self.sampling_strategy_.keys():
target_class_indices = np.flatnonzero(y == target_class)
X_class = _safe_indexing(X, target_class_indices)
y_class = _safe_indexing(y, target_class_indices)
nnhood_idx = self.nn_.kneighbors(X_class, return_distance=False)[:, 1:]
nnhood_label = y[nnhood_idx]
if self.kind_sel == "mode":
nnhood_label, _ = _mode(nnhood_label, axis=1)
nnhood_bool = np.ravel(nnhood_label) == y_class
elif self.kind_sel == "all":
nnhood_label = nnhood_label == target_class
nnhood_bool = np.all(nnhood_label, axis=1)
index_target_class = np.flatnonzero(nnhood_bool)
else:
index_target_class = slice(None)
idx_under = np.concatenate((idx_under,
np.flatnonzero(y == target_class)[index_target_class]),
axis=0)
self.sample_indices_ = idx_under
return _safe_indexing(X, idx_under), _safe_indexing(y, idx_under)
邻域清理规则(NCR)算法
邻域清理规则是对ENN算法的扩展,它结合了ENN和k-NN分类器来提供更全面的数据清理。NCR采用两阶段清理策略:
NCR算法的关键参数配置:
| 参数 | 默认值 | 描述 |
|---|---|---|
n_neighbors | 3 | 最近邻数量 |
threshold_cleaning | 0.5 | 清理阈值,控制哪些类别参与第二阶段清理 |
edited_nearest_neighbours | None | 自定义ENN实例,默认为kind_sel="mode" |
NCR算法实现机制
NCR算法的实现包含两个主要阶段:
第一阶段 - ENN预处理:
self.edited_nearest_neighbours_.fit_resample(X, y)
index_not_a1 = self.edited_nearest_neighbours_.sample_indices_
index_a1 = np.ones(y.shape, dtype=bool)
index_a1[index_not_a1] = False
index_a1 = np.flatnonzero(index_a1)
第二阶段 - 邻域清理:
# 确定需要清理的类别
self.classes_to_clean_ = [
c for c, n_samples in target_stats.items()
if (c in self.sampling_strategy_.keys() and
n_samples > target_stats[class_minority] * self.threshold_cleaning)
]
# 使用k-NN识别误分类样本的邻居
self.nn_.fit(X, y)
y_pred_minority = self.nn_.predict(X_minority)
neighbors_to_minority_indices = self.nn_.kneighbors(
X_minority, n_neighbors=self.nn_.n_neighbors + 1, return_distance=False
)[:, 1:]
mask_misclassified_minority = y_pred_minority != y_minority
index_a2 = np.ravel(neighbors_to_minority_indices[mask_misclassified_minority])
算法性能对比
为了帮助选择合适的算法,以下是ENN和NCR的性能特征对比:
| 特性 | ENN | NCR |
|---|---|---|
| 计算复杂度 | 中等 | 较高 |
| 噪声处理能力 | 良好 | 优秀 |
| 边界样本处理 | 保守 | 积极 |
| 多类别支持 | 是 | 是 |
| 参数敏感性 | 中等 | 较高 |
实际应用示例
下面展示如何使用这两种算法处理不平衡数据集:
from collections import Counter
from sklearn.datasets import make_classification
from imblearn.under_sampling import EditedNearestNeighbours, NeighbourhoodCleaningRule
# 创建不平衡数据集
X, y = make_classification(n_classes=2, class_sep=2,
weights=[0.1, 0.9], n_informative=3,
n_redundant=1, flip_y=0, n_features=20,
n_clusters_per_class=1, n_samples=1000,
random_state=10)
print('原始数据集分布:', Counter(y))
# 使用ENN算法
enn = EditedNearestNeighbours(n_neighbors=5, kind_sel='mode')
X_enn, y_enn = enn.fit_resample(X, y)
print('ENN处理后分布:', Counter(y_enn))
# 使用NCR算法
ncr = NeighbourhoodCleaningRule(n_neighbors=5, threshold_cleaning=0.6)
X_ncr, y_ncr = ncr.fit_resample(X, y)
print('NCR处理后分布:', Counter(y_ncr))
参数调优建议
-
n_neighbors选择:通常选择3-11之间的奇数,较大的值会产生更平滑的决策边界但可能欠拟合。
-
kind_sel策略:
- 对于噪声较多的数据集,使用
kind_sel="all"进行更激进的清理 - 对于相对干净的数据集,使用
kind_sel="mode"保留更多样本
- 对于噪声较多的数据集,使用
-
threshold_cleaning调整:
- 较低的值(0.3-0.5)会清理更多类别
- 较高的值(0.6-0.8)只清理明显多数的类别
-
交叉验证:建议使用交叉验证来评估不同参数组合对最终模型性能的影响。
算法适用场景
ENN适合场景:
- 数据集相对干净,噪声较少
- 需要快速初步清理
- 计算资源有限的情况
NCR适合场景:
- 数据集包含大量噪声和边界样本
- 对分类精度要求较高
- 有足够的计算资源处理两阶段清理
这两种算法都是基于最近邻的智能欠采样方法,能够有效识别和移除噪声样本,同时在保持数据集代表性的前提下实现类别平衡。在实际应用中,建议根据具体数据集特性和业务需求选择合适的算法和参数配置。
总结
imbalanced-learn库提供了丰富而强大的欠采样技术来应对类别不平衡问题。从简单的随机欠采样到基于智能距离度量的NearMiss系列,再到专注于边界清理的Tomek Links和原型选择的CNN算法,以及最后的高级清理技术ENN和NCR,每种方法都有其独特的优势和适用场景。在实际应用中,需要根据数据集特性、计算资源和业务需求选择合适的算法。随机欠采样适合大规模数据和初步处理,NearMiss系列适用于高维数据和边界样本重要的场景,Tomek Links和CNN专注于噪声清理和原型选择,而ENN和NCR则提供更精细的邻域清理能力。通过合理配置参数和结合交叉验证,这些欠采样技术能够显著提升不平衡数据集的建模效果。
【免费下载链接】imbalanced-learn 项目地址: https://gitcode.com/gh_mirrors/imb/imbalanced-learn
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



