首先,谈谈不平衡数据集。不平衡数据集指的是训练数据中不同类别的样本数量差别较大的情况。在这种情况下,模型容易出现偏差,导致模型对数量较少的类别预测效果不佳。
为了解决这个问题,可以使用上采样和下采样等方法来调整数据集的平衡性,除此之外也有一些数据增强的方法。
上采样(Oversampling)和下采样(Undersampling)都是数据预处理技术,用于处理不平衡数据集的问题。
上采样:增加数量较少的类别的样本数量,使得数据集中各个类别的样本数量相等或接近。
常见:随机上采样、SMOTE(Synthetic Minority Over-sampling Technique)等。
优点:不会丢失信息,
缺点:可能会导致过拟合和噪声数据的引入。
下采样:减少数据集中数量较多的类别的样本数量,使得数据集中各个类别的样本数量相等或接近。
常见:随机下采样、聚类下采样等。
优点:可以快速处理大型不平衡数据集
缺点:可能会导致数据量减少,可能会损失一些重要的信息。
代码示例:
'''
随机上采样(Random Oversampling)
随机上采样是指对少数类样本进行复制,使得样本数量与多数类样本数量相等。
下面是使用Python的imbalanced-learn库进行随机上采样的示例代码:
X和y分别表示原始的特征矩阵和标签向量,fit_resample()方法将进行随机上采样操作。
'''
from imblearn.over_sampling import RandomOverSampler
ros = RandomOverSampler(random_state=42)
X_resampled, y_resampled = ros.fit_resample(X, y)
'''
SMOTE是一种通过插值的方式来合成新的少数类样本的方法。
它的基本思想是对每个少数类样本进行分析,找到它最近的k个少数类样本,然后在这些样本中随机选择一个样本,以该样本为基础生成新的少数类样本。
下面是使用Python的imbalanced-learn库进行SMOTE的示例代码:
X和y分别表示原始的特征矩阵和标签向量,fit_resample()方法将进行SMOTE操作。
'''
from imblearn.over_sampling import SMOTE
smote = SMOTE(random_state=42)
X_resampled, y_resampled = smote.fit_resample(X, y)
'''
随机下采样(Random Undersampling)
随机下采样是指从多数类样本中随机选择样本,使得样本数量与少数类样本数量相等。
下面是使用Python的imbalanced-learn库进行随机下采样的示例代码:
X和y分别表示原始的特征矩阵和标签向量,fit_resample()方法将进行随机下采样操作。
'''
from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler(random_state=42)
X_resampled, y_resampled = rus.fit_resample(X, y)
'''
聚类下采样(Cluster Centroids Undersampling)
聚类下采样是指对多数类样本进行聚类,然后选择每个聚类的中心点作为新的样本。
下面是使用Python的imbalanced-learn库进行聚类下采样的示例代码:
X和y分别表示原始的特征矩阵和标签向量,fit_resample()方法将进行聚类下采样操作。
'''
from imblearn.under_sampling import ClusterCentroids
cc = ClusterCentroids(random_state=42)
X_resampled, y_resampled = cc.fit_resample(X, y)
关于SMOTE(Synthetic Minority Over-sampling Technique),这种基于插值实现的上采样方法,很有意思,手动尝试实现一下:
方法思路:
- 对于每一个少数类样本,选择它最近的k个少数类样本,并计算它们之间的距离。
- 对于每一个选定的少数类样本,从它的k个最近的少数类样本中随机选择一个样本,以该样本为基础生成新的少数类样本。具体而言,对于第i个少数类样本,选择第j个最近的少数类样本作为基础样本,然后在i和j之间进行插值,生成一个新的样本。插值的具体方式可以是在i和j之间进行线性插值或多项式插值。
- 将新的样本添加到原始数据集中,形成新的数据集。
手动实现::
- 对于每一个少数类样本,计算它与所有少数类样本之间的距离,找到最近的k个少数类样本。
- 对于每一个选定的少数类样本,从它的k个最近的少数类样本中随机选择一个样本,以该样本为基础生成新的少数类样本。
- 将新的样本添加到原始数据集中,形成新的数据集。
import numpy as np
from sklearn.neighbors import NearestNeighbors
def SMOTE(X, y, k, ratio=1.0):
"""
X: shape例如[n_samples, n_features]
Training data
y: shape例如[n_samples]
Target values
k: int
最近邻居的数量
ratio: float, 可选,默认1.0
合成样本数与原始样本数之比
"""
n_samples, n_features = X.shape
n_syn = int(ratio * n_samples)
n_classes = len(np.unique(y))
if n_syn <= 0:
return X, y
X_syn = np.zeros((n_syn, n_features))
y_syn = np.zeros(n_syn, dtype=np.int)
# 对于每一个选定的少数类样本,从它的k个最近的少数类样本中随机选择一个样本,以该样本为基础生成新的少数类样本。
#knn 对象是使用 sklearn.neighbors 库中的 NearestNeighbors 类创建的,其中 n_neighbors=k+1 表示要找到每个样本的 k 个近邻样本
knn = NearestNeighbors(n_neighbors=k+1, algorithm='auto', n_jobs=-1)
knn.fit(X)
indices = np.arange(n_samples)
for i, x in enumerate(X):
# return_distance=False 表示只返回近邻样本的索引。使用 [:, 1:] 切片操作是为了去掉每个样本本身,只保留它的近邻样本的索引。
nn = knn.kneighbors([x], return_distance=False)[:, 1:]
for j in range(int(ratio)):
# 从 nn 数组中随机选择一个元素,也就是随机选择一个近邻样本的索引。这里的 nn 是一个形状为 (1, k) 的二维数组,表示 x 的 k 个近邻样本的索引。由于 choice() 方法只能对一维数组进行操作,因此需要使用 nn[0] 获取其中的一维数组。
# 选择一个随机的近邻样本索引是为了在原始样本和其近邻之间生成新的样本,从而增加训练数据的样本数量,同时减少训练数据的不平衡性。
# 假设 nn 的值为 np.array([[1, 3, 5]]),则 nn[0] 返回的是一个包含 1、3 和 5 的一维数组,即 [1, 3, 5]。然后,np.random.choice(nn[0]) 方法从中随机选择一个元素,比如选择了 3,就表示选择了 x 的第 3 个近邻样本。
nn_idx = np.random.choice(nn[0])
diff = X[nn_idx] - x
gap = np.random.random()
X_syn[i*int(ratio)+j] = x + gap * diff
y_syn[i*int(ratio)+j] = y