1. 前言
处理数据不平衡是在机器学习中常见的问题之一。数据不平衡指的是在训练集中不同类别的样本数量差异较大,这可能导致模型对于数量较多的类别学习得更好,而对于数量较少的类别学习得不足。以下是一些处理数据不平衡的常见方法:
过采样(Over-sampling):
- 增加少数类别的样本数量,使其与多数类别的样本数量接近
- 常见的过采样方法包括随机复制样本、SMOTE等
欠采样(Under-sampling):
- 减少多数类别的样本数量,使其与少数类别的样本数量接近
- 可能会导致丢失一些信息,但可以减轻不平衡问题
2. Python详解
2.1 SMOTE
作用:SMOTE 是一种过采样方法,通过合成新的少数类样本来平衡数据集
原理:对于少数类样本,通过选择其近邻样本,随机生成合成样本,从而增加少数类的数量,这有助于提高模型对少数类的泛化性能
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from imblearn.over_sampling import SMOTE
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 生成模拟数据
X, y = make_classification(n_samples=1000, n_features=20, n_informative=2,
n_redundant=10, n_clusters_per_class=1, weights=[0.9], flip_y=0, random_state=42)
# 在应用SMOTE算法前打印数据维度
print("应用算法前数据维度:")
print("样本数量:", X.shape[0])
print("特征数量:", X.shape[1])
print("类别分布:", pd.Series(y).value_counts())
# 绘制原始数据分布
plt.figure(figsize=(10, 6))
plt.subplot(1, 2, 1)
plt.title("原始数据分布")
plt.scatter(X[:, 0], X[:, 1], c=y, marker='o', edgecolors='k')
plt.xlabel("特征 1")
plt.ylabel("特征 2")
# 应用SMOTE算法
smote = SMOTE(random_state=42)
X_resampled, y_resampled = smote.fit_resample(X, y)
# 在应用SMOTE算法后打印数据维度
print("\n应用算法后数据维度:")
print("样本数量:", X_resampled.shape[0])
print("特征数量:", X_resampled.shape[1])
print("类别分布:", pd.Series(y_resampled).value_counts())
# 绘制SMOTE后的数据分布
plt.subplot(1, 2, 2)
plt.title("SMOTE采样后数据分布")
plt.scatter(X_resampled[:, 0], X_resampled[:, 1], c=y_resampled, marker='o', edgecolors='k')
plt.xlabel("特征 1")
plt.ylabel("特征 2")
plt.tight_layout()
plt.show()
2.2 SMOTEENN
SMOTEENN 是一种结合了 SMOTE过采样和ENN欠采样的采样方法,用于处理类别不平衡的数据集。它的目标是在生成合成样本的同时去除一些噪声和冗余样本
SMOTE :
对于每个少数类样本,通过随机选择其 k 最近邻的样本之一,生成一个合成样本, 这样可以增加少数类样本,提高其在数据集中的比例
ENN:
移除一些样本,这些样本是由于噪声或冗余而产生的,从而改善模型的性能, 对于每个样本,通过计算其 k 最近邻的标签来检查它是否与多数类样本不同, 如果是,则将该样本从数据集中删除
通过结合 SMOTE 和 ENN,SMOTEENN 试图解决 SMOTE 生成的合成样本中可能存在的噪声问题,从而提高模型的性能,它的优点之一是在一次处理中执行两个步骤,减少了计算的复杂性
from imblearn.combine import SMOTEENN
# 在应用SMOTEENN前打印数据维度
print("应用算法前数据维度:")
print("样本数量:", X.shape[0])
print("特征数量:", X.shape[1])
print("类别分布:", pd.Series(y).value_counts())
# 绘制原始数据分布
plt.figure(figsize=(10, 6))
plt.subplot(1, 2, 1)
plt.title("原始数据分布")
plt.scatter(X[:, 0], X[:, 1], c=y, marker='o', edgecolors='k')
plt.xlabel("特征 1")
plt.ylabel("特征 2")
# 应用SMOTEENN算法
smoteenn = SMOTEENN(random_state=42)
X_resampled, y_resampled = smoteenn.fit_resample(X, y)
# 在应用SMOTEENN算法后打印数据维度
print("\n应用算法后数据维度:")
print("样本数量:", X_resampled.shape[0])
print("特征数量:", X_resampled.shape[1])
print("类别分布:", pd.Series(y_resampled).value_counts())
# 绘制SMOTEENN后的数据分布
plt.subplot(1, 2, 2)
plt.title("SMOTEENN 采样后数据分布")
plt.scatter(X_resampled[:, 0], X_resampled[:, 1], c=y_resampled, marker='o', edgecolors='k')
plt.xlabel("特征 1")
plt.ylabel("特征 2")
plt.tight_layout()
plt.show()
2.3 Tomek Links
Tomek Links 是一种用于处理不平衡数据集的方法,它的目标是 进行欠采样,以去除多数类别和少数类别之间的Tomek连结,从而减少多数类别和少数类别之间的重叠
from imblearn.under_sampling import TomekLinks
# 在应用Tomek Links算法前打印数据维度
print("应用算法前数据维度:")
print("样本数量:", X.shape[0])
print("特征数量:", X.shape[1])
print("类别分布:", pd.Series(y).value_counts())
# 绘制原始数据分布
plt.figure(figsize=(10, 6))
plt.subplot(1, 2, 1)
plt.title("原始数据分布")
plt.scatter(X[:, 0], X[:, 1], c=y, marker='o', edgecolors='k')
plt.xlabel("特征 1")
plt.ylabel("特征 2")
# 应用Tomek Links算法
tomek_links = TomekLinks()
X_resampled, y_resampled = tomek_links.fit_resample(X, y)
# 在应用Tomek Links算法后打印数据维度
print("\n应用算法后数据维度:")
print("样本数量:", X_resampled.shape[0])
print("特征数量:", X_resampled.shape[1])
print("类别分布:", pd.Series(y_resampled).value_counts())
# 绘制Tomek Links后的数据分布
plt.subplot(1, 2, 2)
plt.title("Tomek Links 采样后数据分布")
plt.scatter(X_resampled[:, 0], X_resampled[:, 1], c=y_resampled, marker='o', edgecolors='k')
plt.xlabel("特征 1")
plt.ylabel("特征 2")
plt.tight_layout()
plt.show()
2.4 其它采样方法
2.4.1 ADASYN
使用imbalanced-learn库中的ADASYN(Adaptive Synthetic Sampling)方法来处理不平衡的分类数据。ADASYN是一种自适应的合成数据过采样方法,它根据每个少数类别样本的密度来生成合成样本,以平衡类别分布
from imblearn.over_sampling import ADASYN
# 创建ADASYN对象
adasyn = ADASYN()
# 使用ADASYN进行过采样,以平衡类别
X_resampled, y_resampled = adasyn.fit_resample(X, y)
# X_resampled 和 y_resampled 现在包含了平衡后的数据,其中样本数较少的类别已经通过自适应合成样本进行过采样以达到与多数类别相等的样本数量。
2.4.2 RandomUnderSampler
将原始的X和y数据集进行欠采样,以解决类别不平衡的问题。欠采样会随机删除多数类别的一些样本,以使多数类别的样本数量与少数类别相等,以便在模型训练中更好地处理不平衡问题
from imblearn.under_sampling import RandomUnderSampler
# 创建RandomUnderSampler对象
rus = RandomUnderSampler()
# 使用RandomUnderSampler进行欠采样,以平衡类别
X_resampled, y_resampled = rus.fit_resample(X, y)
# X_resampled 和 y_resampled 现在包含了平衡后的数据,其中多数类别的样本数减少以与少数类别相等。
2.4.3 EasyEnsemble
将模型拟合到原始的X和y数据集。这一步骤会创建一个集成分类器,其中包含多个子分类器,每个子分类器通过欠采样来处理不平衡的类别,以提高整体模型性能。EasyEnsembleClassifier是一种有效的方法来应对不平衡数据问题,特别是当数据中的类别不平衡严重时。通过使用集成学习和欠采样的组合,它可以提高模型的鲁棒性,并减少错误分类
from imblearn.ensemble import EasyEnsembleClassifier
# 创建EasyEnsembleClassifier对象
ee = EasyEnsembleClassifier()
# 使用EasyEnsembleClassifier进行集成学习,以处理不平衡的分类数据
ee.fit(X, y)
# 在这一步,模型已经被训练,它是一个集成模型,包含多个子分类器,每个子分类器通过欠采样处理数据来提高模型性能。
2.4.4 RUSBoostClassifier
模型拟合到原始的X和y数据集。这一步骤会创建一个RUSBoost分类器,该分类器通过结合AdaBoost和随机欠采样来平衡类别分布,以提高整体模型性能,RUSBoostClassifier是一种有效的方法来处理不平衡数据问题,特别适用于处理多数类别和少数类别之间的不平衡。通过调整样本权重并结合多个基础分类器,它可以提高模型的性能,并产生可靠的预测
from imblearn.ensemble import RUSBoostClassifier
# 创建RUSBoostClassifier对象
rusboost = RUSBoostClassifier()
# 使用RUSBoostClassifier进行训练,以处理不平衡的分类数据
rusboost.fit(X, y)
# 在这一步,模型已经被训练,它是一个结合了AdaBoost和随机欠采样的分类器,通过多次训练基础分类器并调整样本权重来平衡类别分布,提高了模型性能。
2.4.5 BalancedRandomForestClassifier
模型拟合到原始的X和y数据集。这一步骤会创建一个随机森林分类器,其中包含多个决策树,每个决策树在每个节点上使用随机的欠采样来平衡类别分布,以提高整体模型性能
from imblearn.ensemble import BalancedRandomForestClassifier
# 创建BalancedRandomForestClassifier对象
brf = BalancedRandomForestClassifier()
# 使用BalancedRandomForestClassifier进行训练,以处理不平衡的分类数据
brf.fit(X, y)
# 在这一步,模型已经被训练,它是一个基于随机森林的分类器,通过在每个树的节点上使用随机的欠采样来平衡类别分布,提高了模型性能。
结论
这些算法提供了多样化的选择,可以根据实际情况选择合适的处理方式。在实践中,通常需要通过交叉验证等手段来评估模型性能,以选择最适合特定问题的处理方法
如果你对类似于这样的文章感兴趣。
欢迎关注、点赞、转发~