likes
comments
collection
share

机器学习基础-监督学习-标签平衡处理之 SMOTE(Synthetic Minority Over-sampling Technique)

作者站长头像
站长
· 阅读数 31

SMOTE(Synthetic Minority Over-sampling Technique)是一种基于 K 近邻的过采样方法,它可以根据少数类样本之间的距离,产生一些新的少数类样本,从而解决数据集不平衡的问题。SMOTE 方法可以有效地避免过拟合问题,同时不会改变数据集中的总体样本数。

SMOTE 方法的主要思想是对于每个少数类样本 x,从它的 K 个最近邻中随机选择一个样本 x',并根据以下公式产生新的合成样本:

xnew=x+λ(x′−x)x_{new} = x + \lambda(x^\prime-x)xnew=x+λ(xx)

其中,λ\lambdaλ 是一个 [0,1] 之间的随机数,用于控制新生成的样本在原有样本和其最近邻之间的位置。

具体地,SMOTE 方法的实现过程如下:

  1. 对于每个少数类样本 xix_ixi,计算它的 kkk 个最近邻样本。

  2. 对于每个最近邻样本 xi,nnx_{i,nn}xi,nn,根据上述公式产生一个新的合成样本 xnewx_{new}xnew

  3. 将所有合成样本添加到原始数据集中。

下面是一个使用 Python 实现 SMOTE 方法的示例代码:

from sklearn.neighbors import NearestNeighbors
import numpy as np

def SMOTE(X, y, k=5, ratio=1.0):
    """
    :param X: 原始数据集
    :param y: 标签
    :param k: KNN 参数
    :param ratio: 需要过采样的数量与少数类样本数量之间的比例
    :return: 过采样后的数据集和标签
    """
    # 找出所有少数类样本的索引
    minority_class = np.where(y == 1)[0]
    majority_class = np.where(y == 0)[0]

    # 计算需要生成的新样本数量
    n_minority_samples = len(minority_class)
    n_synthetic_samples = int(ratio * n_minority_samples)

    # 计算每个少数类样本的 k 个最近邻样本
    knn = NearestNeighbors(n_neighbors=k, n_jobs=-1)
    knn.fit(X[minority_class])
    neighbors = knn.kneighbors(return_distance=False)

    # 生成新样本
    synthetic_samples = np.zeros((n_synthetic_samples, X.shape[1]))
    for i, j in enumerate(np.random.choice(len(minority_class), n_synthetic_samples)):
        nn = np.random.choice(neighbors[j])
        dif = X[minority_class[nn]] - X[minority_class[j]]
        gap = np.random.rand() * dif
        synthetic_samples[i, :] = X[minority_class[j], :] + gap

    # 将合成样本添加到原始数据集中
    X_resampled = np.vstack((X, synthetic_samples))
    y_resampled = np.hstack((y, np.ones(n_synthetic_samples)))

    return X_resampled, y_resampled

需要注意的是,SMOTE 方法也有一些局限性。例如,当少数类样本分布极其不均匀时,SMOTE 方法可能会产生一些不太合理的合成样本。此外,当 k 值较小时,SMOTE 方法可能会产生一些与少数类样本过于相似的合成样本,从而导致过拟合问题。

为了解决这些问题,一些改进的 SMOTE 方法已经被提出,例如 Borderline-SMOTE 和 ADASYN 等。

下面是一个使用 Python 实现 Borderline-SMOTE 方法的示例代码:

from sklearn.neighbors import NearestNeighbors
import numpy as np

def BorderlineSMOTE(X, y, k=5, ratio=1.0, kind='borderline1'):
    """
    :param X: 原始数据集
    :param y: 标签
    :param k: KNN 参数
    :param ratio: 需要过采样的数量与少数类样本数量之间的比例
    :param kind: Borderline-SMOTE 类型,可选值为 'borderline1' 或 'borderline2'
    :return: 过采样后的数据集和标签
    """
    # 找出所有少数类样本的索引
    minority_class = np.where(y == 1)[0]
    majority_class = np.where(y == 0)[0]

    # 计算需要生成的新样本数量
    n_minority_samples = len(minority_class)
    n_synthetic_samples = int(ratio * n_minority_samples)

    # 计算每个少数类样本的 k 个最近邻样本
    knn = NearestNeighbors(n_neighbors=k, n_jobs=-1)
    knn.fit(X[minority_class])
    neighbors = knn.kneighbors(return_distance=False)

    # 计算每个少数类样本的半径
    distances, indices = knn.kneighbors(X[minority_class])
    radii = distances.sum(axis=1) / k

    # 生成新样本
    synthetic_samples = np.zeros((n_synthetic_samples, X.shape[1]))
    for i, j in enumerate(np.random.choice(len(minority_class), n_synthetic_samples)):
        nn = np.random.choice(neighbors[j])
        dif = X[minority_class[nn]] - X[minority_class[j]]
        gap = np.random.rand() * dif
        synthetic_samples[i, :] = X[minority_class[j], :] + gap

        # 如果生成的样本距离其 k 个最近邻样本的平均距离大于样本半径,则将其标记为噪声样本
        if kind == 'borderline1' and (distances[j, :] > radii[j]).sum() > 0:
            synthetic_samples[i, :] = X[minority_class[j], :]
        # 如果生成的样本距离其 k 个最近邻样本的平均距离小于样本半径,则将其标记为危险样本
        elif kind == 'borderline2' and ((distances[j, :] > radii[j]).sum() > 0 and (distances[j, :] <= radii[j]).sum() > 0):
            synthetic_samples[i, :] = X[minority_class[j], :]

    # 将生成的新样本添加到数据集中
    X = np.vstack((X, synthetic_samples))
    y = np.hstack((y, np.ones(n_synthetic_samples)))

    return X, y

在这个示例代码中,我们首先找出了所有少数类样本的索引,然后计算了需要生成的新样本数量。接着,我们使用 KNN 找出了每个少数类样本的 k 个最近邻样本,并计算了每个样本的半径。在生成新样本时,我们首先随机选择一个少数类样本作为基准样本,然后从它的 k 个最近邻样本中随机选择一个样本作为合成样本的参考样本。然后,我们计算了参考样本与基准样本之间的差异,并随机生成一个比例因子,将差异乘以这个比例因子,得到合成样本的特征值。最后,我们使用 Borderline-SMOTE 的规则判断合成样本是否为噪声样本或危险样本,并将其标记为相应的类型。

需要注意的是,Borderline-SMOTE 方法和 SMOTE 方法一样,也需要在训练集上使用。因此,在使用 Borderline-SMOTE 方法时,需要将原始数据集分成训练集和测试集,并只在训练集上使用 Borderline-SMOTE 方法进行过采样。

转载自:https://juejin.cn/post/7230728447206473786
评论
请登录