机器学习之类别不平衡问题 (3) —— 采样方法

机器学习之类别不平衡问题 (1) —— 各种评估指标

机器学习之类别不平衡问题 (2) —— ROC和PR曲线

机器学习之类别不平衡问题 (3) —— 采样方法

前两篇主要谈类别不平衡问题的评估方法,重心放在各类评估指标以及ROC和PR曲线上,只有在明确了这些后,我们才能据此选择具体的处理类别不平衡问题的方法。本篇介绍的采样方法是其中比较常用的方法,其主要目的是通过改变原有的不平衡样本集,以期获得一个平衡的样本分布,进而学习出合适的模型。

采样方法大致可分为过采样 (oversampling) 和欠采样 (undersampling) ,虽然过采样和降采样主题思想简单,但这些年来研究出了很多变种,本篇挑一些来具体阐述。见下思维导图:


\(\scriptsize{\spadesuit}\) 过采样

1. 随机过采样

随机过采样顾名思义就是从样本少的类别中随机抽样,再将抽样得来的样本添加到数据集中。然而这种方法如今已经不大使用了,因为重复采样往往会导致严重的过拟合,因而现在的主流过采样方法是通过某种方式人工合成一些少数类样本,从而达到类别平衡的目的,而这其中的鼻祖就是SMOTE。

2. SMOTE

SMOTE (synthetic minority oversampling technique) 的思想概括起来就是在少数类样本之间进行插值来产生额外的样本。具体地,对于一个少数类样本\(\mathbf{x}_i\)使用K近邻法(k值需要提前指定),求出离\(\mathbf{x}_i\)距离最近的k个少数类样本,其中距离定义为样本之间n维特征空间的欧氏距离。然后从k个近邻点中随机选取一个,使用下列公式生成新样本:

\[
\mathbf{x}_{new}=\mathbf{x}_{i}+(\mathbf{\hat{x}}_{i}-\mathbf{x}_{i}) \times \delta \tag{1.1}
\]

其中\(\mathbf{\hat{x}}\)为选出的k近邻点,\(\delta\in[0,1]\)是一个随机数。下图就是一个SMOTE生成样本的例子,使用的是3-近邻,可以看出SMOTE生成的样本一般就在\(\mathbf{x}_{i}\)和\(\mathbf{\hat{x}}_{i}\)相连的直线上:

SMOTE会随机选取少数类样本用以合成新样本,而不考虑周边样本的情况,这样容易带来两个问题:

  1. 如果选取的少数类样本周围也都是少数类样本,则新合成的样本不会提供太多有用信息。这就像支持向量机中远离margin的点对决策边界影响不大。
  2. 如果选取的少数类样本周围都是多数类样本,这类的样本可能是噪音,则新合成的样本会与周围的多数类样本产生大部分重叠,致使分类困难。

总的来说我们希望新合成的少数类样本能处于两个类别的边界附近,这样往往能提供足够的信息用以分类。而这就是下面的 Border-line SMOTE 算法要做的事情。

3. Border-line SMOTE

这个算法会先将所有的少数类样本分成三类,如下图所示:

  • "noise" : 所有的k近邻个样本都属于多数类
  • "danger" : 超过一半的k近邻样本属于多数类
  • "safe": 超过一半的k近邻样本属于少数类

Border-line SMOTE算法只会从处于”danger“状态的样本中随机选择,然后用SMOTE算法产生新的样本。处于”danger“状态的样本代表靠近”边界“附近的少数类样本,而处于边界附近的样本往往更容易被误分类。因而 Border-line SMOTE 只对那些靠近”边界“的少数类样本进行人工合成样本,而 SMOTE 则对所有少数类样本一视同仁。

Border-line SMOTE 分为两种: Borderline-1 SMOTEBorderline-2 SMOTEBorderline-1 SMOTE 在合成样本时\((1.1)\)式中的\(\mathbf{\hat{x}}\)是一个少数类样本,而 Borderline-2 SMOTE 中的\(\mathbf{\hat{x}}\)则是k近邻中的任意一个样本。

4. ADASYN

ADASYN名为自适应合成抽样(adaptive synthetic sampling),其最大的特点是采用某种机制自动决定每个少数类样本需要产生多少合成样本,而不是像SMOTE那样对每个少数类样本合成同数量的样本。具体流程如下:

  1. 首先计算需要合成的样本总量:

    \[
    G = (S_{maj} - S_{min}) \times \beta
    \]

    其中\(S_{maj}\)为多数类样本数量,\(S_{min}\)为少数类样本数量,\(\beta \in [0,1]\)为系数。G即为总共想要合成的少数类样本数量,如果\(\beta=1\)则是合成后各类别数目相等。

    ?

  2. 对于每个少类别样本\(\mathbf{x}_i\),找出其K近邻个点,并计算:

    \[
    \Gamma_i = \frac{\Delta_i\,/\,K}{Z}
    \]

    其中\(\Delta_i\)为K近邻个点中多数类样本的数量,Z为规范化因子以确保 \(\Gamma\) 构成一个分布。这样若一个少数类样本\(\mathbf{x}_i\)的周围多数类样本越多,则其 \(\Gamma_i\) 也就越高。

    ?

  3. 最后对每个少类别样本\(\mathbf{x}_i\)计算需要合成的样本数量\(g_i\),再用SMOTE算法合成新样本:

    \[
    g_i = \Gamma_i \times G
    \]

可以看到ADASYN利用分布\(\Gamma\)来自动决定每个少数类样本所需要合成的样本数量,这等于是给每个少数类样本施加了一个权重,周围的多数类样本越多则权重越高。ADASYN的缺点是易受离群点的影响,如果一个少数类样本的K近邻都是多数类样本,则其权重会变得相当大,进而会在其周围生成较多的样本。

下面利用sklearn中的 make_classification 构造了一个不平衡数据集,各类别比例为{0:54, 1:946}。原始数据,SMOTEBorderline-1 SMOTEBorderline-2 SMOTEADASYN的比较见下图,左侧为过采样后的决策边界,右侧为过采样后的样本分布情况,可以看到过采样后原来少数类的决策边界都扩大了,导致更多的多数类样本被划为少数类了

从上图我们也可以比较几种过采样方法各自的特点。用 SMOTE 合成的样本分布比较平均,而Border-line SMOTE合成的样本则集中在类别边界处。ADASYN的特性是一个少数类样本周围多数类样本越多,则算法会为其生成越多的样本,从图中也可以看到生成的样本大都来自于原来与多数类比较靠近的那些少数类样本。


\(\scriptsize{\blacklozenge}\) 欠采样

1. 随机欠采样

随机欠采样的思想同样比较简单,就是从多数类样本中随机选取一些剔除掉。这种方法的缺点是被剔除的样本可能包含着一些重要信息,致使学习出来的模型效果不好。

2. EasyEnsemble 和 BalanceCascade

EasyEnsemble和BalanceCascade采用集成学习机制来处理传统随机欠采样中的信息丢失问题。

  • EasyEnsemble将多数类样本随机划分成n个子集,每个子集的数量等于少数类样本的数量,这相当于欠采样。接着将每个子集与少数类样本结合起来分别训练一个模型,最后将n个模型集成,这样虽然每个子集的样本少于总体样本,但集成后总信息量并不减少。

    ?

  • 如果说EasyEnsemble是基于无监督的方式从多数类样本中生成子集进行欠采样,那么BalanceCascade则是采用了有监督结合Boosting的方式。在第n轮训练中,将从多数类样本中抽样得来的子集与少数类样本结合起来训练一个基学习器H,训练完后多数类中能被H正确分类的样本会被剔除。在接下来的第n+1轮中,从被剔除后的多数类样本中产生子集用于与少数类样本结合起来训练,最后将不同的基学习器集成起来。BalanceCascade的有监督表现在每一轮的基学习器起到了在多数类中选择样本的作用,而其Boosting特点则体现在每一轮丢弃被正确分类的样本,进而后续基学习器会更注重那些之前分类错误的样本。

3. NearMiss

NearMiss本质上是一种原型选择(prototype selection)方法,即从多数类样本中选取最具代表性的样本用于训练,主要是为了缓解随机欠采样中的信息丢失问题。NearMiss采用一些启发式的规则来选择样本,根据规则的不同可分为3类:

  • NearMiss-1:选择到最近的K个少数类样本平均距离最近的多数类样本
  • NearMiss-2:选择到最远的K个少数类样本平均距离最近的多数类样本
  • NearMiss-3:对于每个少数类样本选择K个最近的多数类样本,目的是保证每个少数类样本都被多数类样本包围

NearMiss-1和NearMiss-2的计算开销很大,因为需要计算每个多类别样本的K近邻点。另外,NearMiss-1易受离群点的影响,如下面第二幅图中合理的情况是处于边界附近的多数类样本会被选中,然而由于右下方一些少数类离群点的存在,其附近的多数类样本就被选择了。相比之下NearMiss-2和NearMiss-3不易产生这方面的问题。

4. 数据清洗方法 (data cleaning tichniques)

这类方法主要通过某种规则来清洗重叠的数据,从而达到欠采样的目的,而这些规则往往也是启发性的,下面进行简要阐述:

  • Tomek Link:Tomek Link表示不同类别之间距离最近的一对样本,即这两个样本互为最近邻且分属不同类别。这样如果两个样本形成了一个Tomek Link,则要么其中一个是噪音,要么两个样本都在边界附近。这样通过移除Tomek Link就能“清洗掉”类间重叠样本,使得互为最近邻的样本皆属于同一类别,从而能更好地进行分类。

    下图左上为原始数据,右上为SMOTE后的数据,左下虚线标识出Tomek Link,右下为移除Tomek Link后的数据集,可以看到不同类别之间样本重叠减少了很多。

  • Edited Nearest Neighbours(ENN):对于属于多数类的一个样本,如果其K个近邻点有超过一半都不属于多数类,则这个样本会被剔除。这个方法的另一个变种是所有的K个近邻点都不属于多数类,则这个样本会被剔除。

最后,数据清洗技术最大的缺点是无法控制欠采样的数量。由于都在某种程度上采用了K近邻法,而事实上大部分多数类样本周围也都是多数类,因而能剔除的多数类样本比较有限。


\(\scriptsize{\clubsuit}\) 过采样和欠采样结合

上文中提到SMOTE算法的缺点是生成的少数类样本容易与周围的多数类样本产生重叠难以分类,而数据清洗技术恰好可以处理掉重叠样本,所以可以将二者结合起来形成一个pipeline,先过采样再进行数据清洗。主要的方法是 SMOTE + ENNSMOTE + Tomek ,其中 SMOTE + ENN 通常能清除更多的重叠样本,如下图:

综上,本文简要介绍了几种过采样和欠采样的方法,其实还有更多的变种,可参阅imbalanced-learn最后列出的的References


\(\scriptsize{\bigstar}\) 采样方法的效果

最后当然还有一个最重要的问题,本文列举了多种不同的采样方法,那么哪种方法效果好呢? 下面用两个数据集对各类方法进行比较,同样结论也是基于这两个数据集。本文代码主要使用了imbalanced-learn这个库,算是scikit-learn的姐妹项目。

第一个数据集为 us_crime,多数类样本和少数类样本的比例为12:1。

us_crime = fetch_datasets()[‘us_crime‘]
X_train, X_test, y_train, y_test = train_test_split(us_crime.data, us_crime.target,
                                                    test_size=0.5, random_state=42, stratify=us_crime.target)
                                                    # 分为训练集和测试集

总共用9个模型,1个 base_model (即不进行采样的模型) 加上8个过采样和欠采样方法。imbalanced-learn 中大部分采样方法都可以使用 make_pipeline 将采样方法和分类模型连接起来,但两种集成方法,EasyEnsembleBalanceCascade 无法使用 make_pipeline (因为本质上是集成了好几个分类模型),所以需要自定义方法。

sampling_methods = [Original(),
                    SMOTE(random_state=42),
                    SMOTE(random_state=42, kind=‘borderline1‘),
                    ADASYN(random_state=42),
                    EasyEnsemble(random_state=42),
                    BalanceCascade(random_state=42),
                    NearMiss(version=3, random_state=42),
                    SMOTEENN(random_state=42),
                    SMOTETomek(random_state=42)]

names = [‘Base model‘,
         ‘SMOTE‘,
         ‘Borderline SMOTE‘,
         ‘ADASYN‘,
         ‘EasyEnsemble‘,
         ‘BalanceCascade‘,
         ‘NearMiss‘,
         ‘SMOTE+ENN‘,
         ‘SMOTE+Tomek‘]

def ensemble_method(method):  # EasyEnsemble和BalanceCascade的方法
    count = 0
    xx, yy = method.fit_sample(X_train, y_train)
    y_pred, y_prob = np.zeros(len(X_test)), np.zeros(len(X_test))
    for X_ensemble, y_ensemble in zip(xx, yy):
        model = LogisticRegression()
        model.fit(X_ensemble, y_ensemble)
        y_pred += model.predict(X_test)
        y_prob += model.predict_proba(X_test)[:, 1]
        count += 1
    return np.where(y_pred >= 0, 1, -1), y_prob/count

# 画ROC曲线
plt.figure(figsize=(15,8))
for (name, method) in zip(names, sampling_methods):
    t0 = time.time()
    if name == ‘EasyEnsemble‘ or name == ‘BalanceCascade‘:
        y_pred, y_prob = ensemble_method(method)
    else:
        model = make_pipeline(method, LogisticRegression())
        model.fit(X_train, y_train)
        y_pred = model.predict(X_test)
        y_prob = model.predict_proba(X_test)[:, 1]
    fpr, tpr, thresholds = roc_curve(y_test, y_prob, pos_label=1)
    plt.plot(fpr, tpr, lw=3, label=‘{} (AUC={:.2f}, time={:.2f}s)‘.
             format(name, auc(fpr, tpr), time.time() - t0))
    plt.xlabel("FPR", fontsize=17)
    plt.ylabel("TPR", fontsize=17)
    plt.legend(fontsize=14)

# 画PR曲线
plt.figure(figsize=(15,8))
for (name, method) in zip(names, sampling_methods):
    t0 = time.time()
    if name == ‘EasyEnsemble‘ or name == ‘BalanceCascade‘:
        y_pred, y_prob = ensemble_method(method)
    else:
        model = make_pipeline(method, LogisticRegression())
        model.fit(X_train, y_train)
        y_pred = model.predict(X_test)
        y_prob = model.predict_proba(X_test)[:, 1]
    precision, recall, thresholds = precision_recall_curve(y_test, y_prob, pos_label=1)
    plt.plot(recall, precision, lw=3, label=‘{} (AUC={:.2f}, time={:.2f}s)‘.
             format(name, auc(recall, precision), time.time() - t0))
    plt.xlabel("Recall", fontsize=17)
    plt.ylabel("Precision", fontsize=17)
    plt.legend(fontsize=14, loc="upper right")



第二个数据集是 abalone,多数类样本和少数类样本的比例为130:1,非常悬殊。

abalone_19 = fetch_datasets()[‘abalone_19‘]
X_train, X_test, y_train, y_test = train_test_split(abalone_19.data, abalone_19.target, test_size=0.5,
                                                    random_state=42, stratify=abalone_19.target)

# 画ROC曲线和PR曲线
plt.figure(figsize=(15,8))
for (name, method) in zip(names, sampling_methods):
    t0 = time.time()
    if name == ‘EasyEnsemble‘ or name == ‘BalanceCascade‘:
        y_pred, y_prob = ensemble_method(method)
    else:
        model = make_pipeline(method, LogisticRegression())
        model.fit(X_train, y_train)
        y_pred = model.predict(X_test)
        y_prob = model.predict_proba(X_test)[:, 1]
    fpr, tpr, thresholds = roc_curve(y_test, y_prob, pos_label=1)
    plt.plot(fpr, tpr, lw=3, label=‘{} (AUC={:.2f}, time={:.2f}s)‘.
             format(name, auc(fpr, tpr), time.time() - t0))
    plt.xlabel("FPR", fontsize=17)
    plt.ylabel("TPR", fontsize=17)
    plt.legend(fontsize=14)

plt.figure(figsize=(15,8))
for (name, method) in zip(names, sampling_methods):
    t0 = time.time()
    if name == ‘EasyEnsemble‘ or name == ‘BalanceCascade‘:
        y_pred, y_prob = ensemble_method(method)
    else:
        model = make_pipeline(method, LogisticRegression())
        model.fit(X_train, y_train)
        y_pred = model.predict(X_test)
        y_prob = model.predict_proba(X_test)[:, 1]
    precision, recall, thresholds = precision_recall_curve(y_test, y_prob, pos_label=1)
    plt.plot(recall, precision, lw=3, label=‘{} (AUC={:.2f}, time={:.2f}s)‘.
             format(name, auc(recall, precision), time.time() - t0))
    plt.xlabel("Recall", fontsize=17)
    plt.ylabel("Precision", fontsize=17)
    plt.legend(fontsize=14, loc="best")

从以上几张图中我们可以得出一些推论

  1. 就时间开销而言,BalanceCascade以及两种过采样欠采样结合的方法( SMOTE + ENNSMOTE + Tomek )耗时最高,如果追求速度的话这几个可能并非很好的选择。
  2. 第一个数据集 us_crime 的多数类和少数类样本比例为 12:1,相差不是很悬殊,综合ROC曲线和PR曲线的AUC来看,两种集成方法EasyEnsembleBalanceCascade表现较好。

    对于第二个数据集 abalone_19 来说,多数类和少数类样本比例为 130:1,而且少数类样本非常少,因而从结果来看几种过采样方法如Borderline SMOTE, SMOTE+Tomek等效果较好。可见在类别差异很大的情况下,过采样能一定程度上弥补少数类样本的极端不足。然而从PR曲线上来看,其实结果都不尽如人意,对于这种极端不平衡的数据可能比较适合异常检测的方法,以后有机会详述。

  3. 上篇文章中提到 “ROC曲线通常会呈现一个过分乐观的效果估计”,这里再一次得到体现。第一个数据集中大部分ROC曲线的AUC都在0.9左右,而PR曲线都在0.5左右。第二个数据集则更夸张,从PR曲线来看其实模型对于少数类的预测准确率是无限接近于0了,但在ROC曲线上却很难看出这一点。
  4. 如果单纯从ROC曲线和PR曲线上来看,表面上各种采样方法和base model差别不大,但实际上这其中却是暗流涌动。下面来看一下 us_crime 数据集中各方法的 classification report。
def ensemble_method_2(method):  # 定义一个简化版集成方法
    xx, yy = method.fit_sample(X_train, y_train)
    y_pred, y_prob = np.zeros(len(X_test)), np.zeros(len(X_test))
    for X_ensemble, y_ensemble in zip(xx, yy):
        model = LogisticRegression()
        model.fit(X_ensemble, y_ensemble)
        y_pred += model.predict(X_test)
    return np.where(y_pred >= 0, 1, -1)

us_crime = fetch_datasets()[‘us_crime‘]
X_train, X_test, y_train, y_test = train_test_split(us_crime.data, us_crime.target,
                                                    test_size=0.5, random_state=42, stratify=us_crime.target)

class_names = [‘majority class‘, ‘minority class‘]

model = LogisticRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print("------------------------Base Model---------------------- \n",
      classification_report(y_test, y_pred, target_names=class_names), ‘\n‘)

model = make_pipeline(SMOTE(random_state=42), LogisticRegression())
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print("--------------------------SMOTE------------------------ \n",
      classification_report(y_test, y_pred, target_names=class_names), ‘\n‘)

model = make_pipeline(NearMiss(version=2, random_state=42), LogisticRegression())
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print("------------------------NearMiss------------------------ \n",
      classification_report(y_test, y_pred, target_names=class_names), ‘\n‘)

y_pred = ensemble_method_2(EasyEnsemble(random_state=42))
class_names = [‘majority class‘, ‘minority class‘]
print("-----------------------EasyEnsemble--------------------- \n",
      classification_report(y_test, y_pred, target_names=class_names), ‘\n‘)

输出:

------------------------Base Model----------------------
                 precision    recall  f1-score   support

majority class       0.95      0.98      0.97       922
minority class       0.62      0.35      0.44        75

   avg / total       0.92      0.93      0.93       997

--------------------------SMOTE------------------------
                 precision    recall  f1-score   support

majority class       0.98      0.90      0.94       922
minority class       0.38      0.75      0.51        75

   avg / total       0.93      0.89      0.91       997

------------------------NearMiss------------------------
                 precision    recall  f1-score   support

majority class       0.97      0.81      0.88       922
minority class       0.24      0.73      0.36        75

   avg / total       0.92      0.80      0.84       997

-----------------------EasyEnsemble---------------------
                 precision    recall  f1-score   support

majority class       0.98      0.85      0.91       922
minority class       0.31      0.84      0.45        75

   avg / total       0.93      0.85      0.88       997

这里我们主要关注少数类样本,可以看到 Base Model 的特点是precision高,recall低,而几种采样方法则相反,precision低,recall高。采样方法普遍扩大了少数类样本的决策边界(从上文中的决策边界图就能看出来),所以把很多多数类样本也划为少数类了,导致precision下降而recall提升。当然这些都是分类阈值为0.5的前提下得出的结论,如果进一步调整阈值的话能得到更好的模型。策略是base model的阈值往下调,采样方法的阈值往上调。

def ensemble_method_3(method):
    xx, yy = method.fit_sample(X_train, y_train)
    y_pred, y_prob = np.zeros(len(X_test)), np.zeros(len(X_test))
    for X_ensemble, y_ensemble in zip(xx, yy):
        model = LogisticRegression()
        model.fit(X_ensemble, y_ensemble)
        y_pred += np.where(model.predict_proba(X_test)[:, 1] >= 0.7, 1, -1)  # 阈值 > 0.7
    return np.where(y_pred >= 0, 1, -1)

us_crime = fetch_datasets()[‘us_crime‘]
X_train, X_test, y_train, y_test = train_test_split(us_crime.data, us_crime.target,
                                                    test_size=0.5, random_state=42, stratify=us_crime.target)

model = LogisticRegression()
model.fit(X_train, y_train)
y_pred = np.where(model.predict_proba(X_test)[:, 1] >= 0.3, 1, -1)
print("-----------------Base Model, threshold >= 0.3--------------- \n",
      classification_report(y_test, y_pred, target_names=class_names), ‘\n‘)

model = make_pipeline(SMOTE(random_state=42), LogisticRegression())
model.fit(X_train, y_train)
y_pred = np.where(model.predict_proba(X_test)[:, 1] >= 0.9, 1, -1)
print("-----------------SMOTE, threshold >= 0.9--------------- \n",
      classification_report(y_test, y_pred, target_names=class_names), ‘\n‘)

model = make_pipeline(NearMiss(version=2, random_state=42), LogisticRegression())
model.fit(X_train, y_train)
y_pred = np.where(model.predict_proba(X_test)[:, 1] >= 0.7, 1, -1)
print("-------------------NearMiss, threshold >= 0.7------------------- \n",
      classification_report(y_test, y_pred, target_names=class_names), ‘\n‘)

model = EasyEnsemble(random_state=42)
y_pred = ensemble_method_3(model)
class_names = [‘majority class‘, ‘minority class‘]
print("--------------EasyEnsemble, threshold >= 0.7-------------- \n",
      classification_report(y_test, y_pred, target_names=class_names), ‘\n‘)

输出:

-----------------Base Model, threshold >= 0.3------------
                 precision    recall  f1-score   support

majority class       0.96      0.96      0.96       922
minority class       0.53      0.53      0.53        75

   avg / total       0.93      0.93      0.93       997

-----------------SMOTE, threshold >= 0.9----------------
                 precision    recall  f1-score   support

majority class       0.96      0.97      0.97       922
minority class       0.60      0.49      0.54        75

   avg / total       0.93      0.94      0.93       997

-------------------NearMiss, threshold >= 0.7------------
                 precision    recall  f1-score   support

majority class       0.96      0.90      0.93       922
minority class       0.32      0.59      0.42        75

   avg / total       0.92      0.88      0.89       997

--------------EasyEnsemble, threshold >= 0.7-------------
                 precision    recall  f1-score   support

majority class       0.97      0.92      0.95       922
minority class       0.42      0.69      0.53        75

   avg / total       0.93      0.91      0.92       997

在经过阈值调整后,各方法的整体F1分数都有提高,可见很多单指标如 precision,recall 等都会受到不同阈值的影响。所以这也是为什么在类别不平衡问题中用ROC和PR曲线来评估非常流行,因为它们不受特定阈值变化的影响,反映的是模型的整体预测能力。

不过在这里我不得不得出一个比较悲观的结论:就这两个数据集的结果来看,如果本身数据偏斜不是很厉害,那么采样方法的提升效果很细微。如果本身数据偏斜很厉害,采样方法纵使比base model好很多,但由于base model本身的少数类预测能力很差,所以本质上也不尽如人意。这就像考试原来一直靠10分,采样了之后考了30分,绝对意义上提升很大,但其实还是差得远了。

原文地址:https://www.cnblogs.com/massquantity/p/9382710.html

时间: 2024-10-08 03:46:02

机器学习之类别不平衡问题 (3) —— 采样方法的相关文章

详解类别不平衡问题

本文详细介绍了类别不平衡问题,目录: 1 什么是类别不平衡问题? 2 类别不平衡导致分类困难的原因? 3 类别不平衡的解决方法? 4 如何选择类别不平衡中学习的评价指标? 5 关于解决方法选择的一些建议? 6 小结 1 什么是类别不平衡问题? 类别不平衡(class-imbalance),也叫数据倾斜,数据不平衡,就是指分类任务中不同类别的训练样例数目差别很大的情况.在现实的分类学习任务中,我们经常会遇到类别不平衡,例如交易欺诈.广告点击率预测.病毒脚本判断等:或者在通过拆分法解决多分类问题时,

[转]如何处理机器学习中的不平衡类别

如何处理机器学习中的不平衡类别 原文地址:How to Handle Imbalanced Classes in Machine Learning 原文作者:elitedatascience 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/gold-m- 译者:RichardLeeH 校对者:lsvih, lileizhenshuai 如何处理机器学习中的不平衡类别 不平衡类别使得"准确率"失去意义.这是机器学习 (特别是在分类)中一个令人惊讶的常见问题,出现于每

机器学习-类别不平衡问题

引言:我们假设有这种情况,训练数据有反例998个,正例2个,模型是一个永远将新样本预测为反例的学习器,就能达到99.8%的精度,这样显然是不合理的. 类别不平衡:分类任务中不同类别的训练样例数差别很大. 一般我们在训练模型时,基于样本分布均匀的假设.从线性分类器的角度讨论,使用 y=wTx+b 对新样本分类时,用预测的 y 与一个阈值进行比较,y>0.5 即判别为正例,否则判别为负例.这里的 y 实际表达了正例的可能性(1-y是反例的可能性),0.5表明分类器认为正反例可能性相同.即 ,则预测为

如何解决机器学习中数据不平衡问题

作者:无影随想 时间:2016年1月. 出处:http://www.zhaokv.com/2016/01/learning-from-imbalanced-data.html 声明:版权所有,转载请联系作者并注明出处 这几年来,机器学习和数据挖掘非常火热,它们逐渐为世界带来实际价值.与此同时,越来越多的机器学习算法从学术界走向工业界,而在这个过程中会有很多困难.数据不平衡问题虽然不是最难的,但绝对是最重要的问题之一. 一.数据不平衡 在学术研究与教学中,很多算法都有一个基本假设,那就是数据分布是

从重采样到数据合成:如何处理机器学习中的不平衡分类问题?

转载自[机器之心]http://www.jiqizhixin.com/article/2499本文作者为来自 KPMG 的数据分析顾问 Upasana Mukherjee 如果你研究过一点机器学习和数据科学,你肯定遇到过不平衡的类分布(imbalanced class distribution).这种情况是指:属于某一类别的观测样本的数量显著少于其它类别. 这个问题在异常检测是至关重要的的场景中很明显,例如电力盗窃.银行的欺诈交易.罕见疾病识别等.在这种情况下,利用传统机器学习算法开发出的预测模

机器不学习:如何处理数据中的「类别不平衡」?

机器不学习 jqbxx.com -机器学习好网站 机器学习中常常会遇到数据的类别不平衡(class imbalance),也叫数据偏斜(class skew).以常见的二分类问题为例,我们希望预测病人是否得了某种罕见疾病.但在历史数据中,阳性的比例可能很低(如百分之0.1).在这种情况下,学习出好的分类器是很难的,而且在这种情况下得到结论往往也是很具迷惑性的. 以上面提到的场景来说,如果我们的分类器总是预测一个人未患病,即预测为反例,那么我们依然有高达99.9%的预测准确率.然而这种结果是没有意

分类类别不平衡问题

一.什么是类不平衡 在分类中经常会遇到:某些类别数据特别多,某类或者几类数据特别少.如二分类中,一种类别(反例)数据特别多,另一种类别(正例)数据少的可怜.如银行欺诈问题,客户流失问题,电力盗窃以及罕见疾病识别等都存在着数据类别不均衡的情况. 二.为什么要对类不平衡进行特殊处理 传统的分类算法旨在最小化分类过程中产生的错误数量.它们假设假阳性(实际是反例,但是错分成正例)和假阴性(实际是正例,但是错分为反例)错误的成本是相等的,因此不适合于类不平衡的数据. 有研究表明,在某些应用下,1∶35的比

机器学习中样本不平衡的处理方法

在现实收集的样本中,正负类别不均衡是现实数据中很常见的问题.一个分类器往往 Accuracy 将近90%,但是对少数样本的判别的 Recall 却只有10%左右.这对于我们正确找出少数类样本非常不利. 举例来说:在一波新手推荐的活动中,预测用户是否会注册的背景下,不注册的用户往往是居多的,这个正负比例通常回事1:99甚至更大.一般而言,正负样本比例超过1:3,分类器就已经会倾向于负样本的判断(表现在负样本Recall过高,而正样本 Recall 低,而整体的 Accuracy依然会有很好的表现)

😆 机器学习采样方法大全

?? Index 数据采样的原因 常见的采样算法 失衡样本的采样 采样的Python实现 ?? 数据采样的原因 其实我们在训练模型的过程,都会经常进行数据采样,为了就是让我们的模型可以更好的去学习数据的特征,从而让效果更佳.但这是比较浅层的理解,更本质上,数据采样就是对随机现象的模拟,根据给定的概率分布从而模拟一个随机事件.另一说法就是用少量的样本点去近似一个总体分布,并刻画总体分布中的不确定性. 因为我们在现实生活中,大多数数据都是庞大的,所以总体分布可能就包含了无数多的样本点,模型是无法对这