likes
comments
collection
share

机器学习基础-监督学习-标签平衡处理之 ADASYN(Adaptive Synthetic Sampling)

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

ADASYN(Adaptive Synthetic Sampling)是一种基于 SMOTE 的过采样方法,它通过计算每个少数类样本周围的多数类样本比例,决定需要产生的新样本数量。ADASYN 可以更好地适应不同的数据集,因为它会为那些与多数类更接近的少数类样本产生更多的新样本,而为那些与多数类更远的少数类样本产生较少的新样本。这种方法可以帮助避免产生噪声样本,并提高分类器的性能。

下面是 ADASYN 方法的详细流程:

  1. 对于每个少数类样本,计算它周围多数类样本的比例 p;
  2. 对于每个少数类样本,计算需要产生的新样本数量 n = p * k,其中 k 是一个可调参数,用于控制新样本的数量;
  3. 对于每个少数类样本,从它的 k 个最近邻的多数类样本中随机选择 n 个样本,并将它们插入到少数类样本和其 k 个最近邻之间。

下面是 ADASYN 的 Python 代码实现:

import numpy as np
from sklearn.neighbors import NearestNeighbors

def ADASYN(X, y, k=5, ratio=0.5):
    """
    ADASYN: Adaptive Synthetic Sampling
    """
    # 计算每个少数类样本周围多数类样本的数量
    neigh = NearestNeighbors(n_neighbors=k+1)
    neigh.fit(X[y == 1])
    distances, indices = neigh.kneighbors()
    n_samples, n_features = X.shape
    synthetic_X = []
    synthetic_y = []
    for i in range(len(indices)):
        # 计算需要产生的新样本数量
        n = int(round(ratio * indices[i].shape[0]))
        if n == 0:
            continue
        # 对于每个少数类样本,随机选择 n 个样本,并插入到样本之间
        for j in range(n):
            nn = np.random.randint(1, indices[i].shape[0])
            dif = X[indices[i][nn]] - X[indices[i][0]]
            gap = np.random.random()
            synthetic = X[indices[i][0]] + gap * dif
            synthetic_X.append(synthetic)
            synthetic_y.append(1)
    synthetic_X = np.array(synthetic_X)
    synthetic_y = np.array(synthetic_y)
    # 将生成的新样本和原始样本合并成新的训练集
    X = np.vstack((X, synthetic_X))
    y = np.hstack((y, synthetic_y))
    return X, y

上面的代码中,输入 X 是训练数据的特征矩阵,y 是训练数据的标签,k 是指定的 k 值,ratio 是新样本占比的参数。函数首先计算每个少数类样本周围多数类样本的数量,然后随机从多数类样本中选择一定数量的样本,插入到少数类样本和其 k 个最近邻之间,生成新的合成样本。最后,将生成的新样本和原始样本合并成新的训练集返回。

需要注意的是,ADASYN 方法在生成新样本时可能会产生重叠的样本,因此需要在生成后去除重复的样本。

下面是一个简单的示例:

from collections import Counter
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report

# 生成一个二分类样本不平衡的数据集
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('Original dataset shape %s' % Counter(y))

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=10)

# 使用 ADASYN 过采样方法平衡训练集
X_resampled, y_resampled = ADASYN(X_train, y_train)

# 平衡后的数据集中少数类样本的数量
print('Resampled dataset shape %s' % Counter(y_resampled))

# 训练模型
clf = LogisticRegression(random_state=10)
clf.fit(X_resampled, y_resampled)

# 在测试集上进行预测并评估性能
y_pred = clf.predict(X_test)
print(classification_report(y_test, y_pred))

在这个示例中,我们使用了 make_classification() 函数生成一个二分类样本不平衡的数据集,并划分出训练集和测试集。然后,我们使用 ADASYN 过采样方法平衡训练集,并训练逻辑回归模型进行分类。最后,我们在测试集上进行预测并评估模型性能。

上面的代码中使用的 ADASYN() 函数并不是 sklearn 中提供的函数,这里我们自己实现一下 ADASYN 过采样算法。

import numpy as np
from sklearn.neighbors import NearestNeighbors

def ADASYN(X, y, k=5, ratio=1):
    """
    ADASYN 过采样算法实现

    参数:
    X: 原始特征矩阵
    y: 原始标签向量
    k: 选择 k 个最近邻样本
    ratio: 生成新样本与原样本比例

    返回值:
    new_X: 合成新的特征矩阵
    new_y: 合成新的标签向量
    """
    # 统计少数类样本的数量和多数类样本的数量
    minority_num = sum(y == 1)
    majority_num = sum(y == 0)

    # 计算需要生成的新样本数量
    new_sample_num = int((minority_num * ratio) - minority_num)

    # 如果新样本数量为 0,则返回原始数据集
    if new_sample_num == 0:
        return X, y

    # 计算每个少数类样本需要生成的新样本数量
    num_neighbors = np.zeros(minority_num)
    for i in range(minority_num):
        # 计算少数类样本 i 的 k 个最近邻样本
        nn = NearestNeighbors(n_neighbors=k+1)
        nn.fit(X[y == 1])
        distances, indices = nn.kneighbors(X[y == 1][i].reshape(1, -1))
        # 计算少数类样本 i 和其 k 个最近邻样本中属于多数类的样本数量
        num_neighbors[i] = sum(y[y == 1][indices[0, 1:]] == 0)

    # 计算少数类样本 i 生成新样本的比例,即生成 num_synthetic 样本
    # 需要从其 k 个最近邻样本中选择 num_synthetic * (num_neighbors[i] / sum(num_neighbors)) 个样本
    synthetic_ratios = num_neighbors / sum(num_neighbors)
    num_synthetic = np.round(synthetic_ratios * new_sample_num).astype(int)

    # 对每个少数类样本 i,根据其 k 个最近邻样本生成 num_synthetic 个新样本
    new_X = []
    new_y = []
    for i in range(minority_num):
        nn = NearestNeighbors(n_neighbors=k+1)
        nn.fit(X[y == 1])
        distances, indices = nn.kneighbors(X[y == 1][i].reshape(1, -1))
        # 生成 num_synthetic[i] 个新样本
        for j in range(num_synthetic[i]):
            # 随机选择一个少数类样本的 k 个最近邻样本
            nn_index = np.random.randint(1, k+1)
            # 计算合成样本的特征值
            dif = X[y == 1][indices[0, nn_index]] - X[y == 1][i]
            gap = np.random.rand(1, X.shape[1])
            new_X.append(X[y == 1][i] + gap * dif)
            new_y.append(1)

    # 将新生成的新样本和原始数据集合并,返回合成的新特征矩阵和新标签向量
    new_X = np.vstack([X, np.array(new_X).reshape(-1, X.shape[1])])
    new_y = np.hstack([y, np.array(new_y)])

    return new_X, new_y

以上是 Python 代码实现 ADASYN 过采样算法的过程。通过统计少数类样本的数量和多数类样本的数量,计算需要生成的新样本数量,并根据每个少数类样本的 k 个最近邻样本生成新样本,最后将新生成的样本和原始数据集合并,即可得到合成的新特征矩阵和新标签向量。