用于不平衡分类的 Bagging 和随机森林
用于不平衡分类的 Bagging 和随机森林
Bagging 是一种集成算法,它在训练数据集的不同子集上拟合多个模型,然后结合所有模型的预测。
[随机森林]是 bagging 的扩展,它也会随机选择每个数据样本中使用的特征子集。bagging 和随机森林都已被证明能有效解决各种不同的预测建模问题。
虽然这些算法很有效,但它们并不适合处理类别分布不均的分类问题。不过,人们已经提出了许多算法修改方案,以调整算法的行为,使其更适合严重的类别不平衡问题。
在本教程中,您将了解如何使用装袋和随机森林进行不平衡分类。
完成本教程后,您将了解:
- 如何使用随机欠采样的 Bagging 来解决不平衡分类问题。
- 如何使用具有类别加权和随机欠采样的随机森林来解决不平衡分类问题。
- 如何使用结合 bagging 和 boosting 的 Easy Ensemble 来解决不平衡分类问题。
教程概述
本教程分为三个部分:
- 不平衡分类的 Bagging
- 标准装袋
- 随机欠采样的 Bagging
- 不平衡分类的随机森林
- 标准随机森林
- 带类别权重的随机森林
- 带引导类权重的随机森林
- 随机欠采样的随机森林
- 不平衡分类的简单集成
- 简易合奏
不平衡分类的 Bagging
[Bootstrap Aggregation(简称 Bagging)是一种集成机器学习算法。
它首先涉及选择训练数据集的随机样本,并进行替换,这意味着给定的样本可能包含训练数据集中零个、一个或多个示例副本。这称为引导样本。然后对每个数据样本拟合一个弱学习器模型。通常,不使用剪枝的决策树模型(例如,可能略微过度拟合其训练集)被用作弱学习器。最后,将所有拟合弱学习器的预测组合起来以做出单个预测(例如,聚合)。
然后使用集成中的每个模型来生成新样本的预测,并对这些 m 个预测取平均值以得出 bagged 模型的预测。
— 第 192 页,《应用预测模型》,2013 年。
创建新的引导样本以及拟合和向样本添加树的过程可以持续进行,直到在验证数据集上集合的性能不再得到改善为止。
这个简单的过程通常比单个配置良好的决策树算法产生更好的性能。
原样装袋将创建[引导样本],不会考虑不平衡分类数据集的倾斜类别分布。因此,尽管该技术总体上表现良好,但如果存在严重的类别不平衡,则可能表现不佳。
标准Bagging
在我们深入探索 bagging 的扩展之前,让我们先评估一个标准的 bagged 决策树集成,并将其用作比较点。
我们可以使用BaggingClassifier scikit-sklearn 类来创建具有大致相同配置的袋装决策树模型。
首先,让我们定义一个合成的不平衡二元分类问题,其中包含 10,000 个示例,其中 99% 属于多数类,1% 属于少数类。
...
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4)
然后,我们可以定义标准的袋装决策树集成模型以供评估。
...
# define model
model = BaggingClassifier()
然后,我们可以使用重复分层[k 折交叉验证来]评估该模型,重复 3 次,折数 10 次。
我们将使用所有折叠和重复的平均[ROC AUC 分数来评估模型的性能。
...
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
综合以上几点,下面列出了在不平衡分类数据集上评估标准袋装集成的完整示例。
# bagged decision trees on an imbalanced classification problem
from numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.ensemble import BaggingClassifier
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4)
# define model
model = BaggingClassifier()
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
# summarize performance
print('Mean ROC AUC: %.3f' % mean(scores))
运行示例评估模型并报告平均 ROC AUC 分数。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。
在这种情况下,我们可以看到该模型的得分约为 0.87。
Mean ROC AUC: 0.871
随机欠采样的 Bagging
有很多方法可以使 bagging 适应不平衡分类的情况。
也许最直接的方法是在拟合弱学习器模型之前对引导样本应用数据重采样。这可能涉及对少数类进行过采样或对多数类进行欠采样。
在 bagging 的重采样阶段,克服类别不平衡问题的一个简单方法是,在从原始数据集中随机抽取实例时考虑实例的类别。
— 第 175 页,从不平衡数据集学习,2018 年。
在引导法中对少数类进行过度采样称为 OverBagging;同样,在引导法中对多数类进行欠采样称为 UnderBagging,将两种方法结合起来称为 OverUnderBagging。
不平衡学习库提供了 UnderBagging 的实现。
具体来说,它提供了一种 bagging 版本,该版本对引导样本中的多数类使用随机欠采样策略来平衡两个类。这在BalancedBaggingClassifier 类中提供。
...
# define model
model = BalancedBaggingClassifier()
接下来,我们可以评估袋装决策树集成的修改版本,该版本在拟合每个决策树之前对多数类进行随机欠采样。
我们期望使用随机欠采样能够提高集成的性能。
此模型和前一个模型的默认树数(n_estimators)均为 10。实际上,测试此超参数的较大值(例如 100 或 1,000)是个好主意。
完整的示例如下。
# bagged decision trees with random undersampling for imbalanced classification
from numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from imblearn.ensemble import BalancedBaggingClassifier
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4)
# define model
model = BalancedBaggingClassifier()
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
# summarize performance
print('Mean ROC AUC: %.3f' % mean(scores))
运行示例评估模型并报告平均 ROC AUC 分数。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。
在这种情况下,我们可以看到平均 ROC AUC 从没有任何数据重采样时的约 0.87 提升到多数类随机欠采样时的约 0.96。
这不是一个真正的同类比较,因为我们使用的是来自两个不同库的相同算法实现,但它表明,当类分布不均匀时,在拟合弱学习器之前平衡引导程序会带来一些好处。
Mean ROC AUC: 0.962
尽管BalancedBaggingClassifier类使用决策树,但您可以测试不同的模型,例如 k-最近邻等。您可以在定义类时设置base_estimator参数以使用不同的较弱学习器分类器模型。
不平衡分类的随机森林
随机森林是另一组决策树模型,可以被认为是 bagging 的改进。
与 bagging 类似,随机森林涉及从训练数据集中选择引导样本,并在每个样本上拟合决策树。主要区别在于,不使用所有特征(变量或列);而是为每个引导样本选择一小部分随机选择的特征(列)。这可以降低决策树的相关性(使它们更加独立),从而改善集成预测。
然后,集合中的每个模型都用于生成新样本的预测,并将这 m 个预测取平均值以得出森林的预测。由于算法在每次分割时随机选择预测因子,因此树相关性必然会降低。
— 第 199 页,《应用预测模型》,2013 年。
再次,随机森林在广泛的问题上非常有效,但与装袋一样,标准算法在不平衡分类问题上的性能并不好。
在学习极度不平衡的数据时,引导样本很有可能包含很少甚至不包含少数类,从而导致树在预测少数类方面性能较差。
—使用随机森林学习不平衡数据,2004 年。
标准随机森林
在我们深入研究随机森林集成算法的扩展以使其更适合不平衡分类之前,让我们在我们的合成数据集上拟合和评估随机森林算法。
我们可以使用scikit-learn 中的RandomForestClassifier类并使用少量的树,在本例中为 10 棵。
...
# define model
model = RandomForestClassifier(n_estimators=10)
下面列出了在不平衡数据集上拟合标准随机森林集成的完整示例。
# random forest for imbalanced classification
from numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.ensemble import RandomForestClassifier
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4)
# define model
model = RandomForestClassifier(n_estimators=10)
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
# summarize performance
print('Mean ROC AUC: %.3f' % mean(scores))
运行示例评估模型并报告平均 ROC AUC 分数。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。
在这种情况下,我们可以看到该模型实现了约 0.86 的平均 ROC AUC。
Mean ROC AUC: 0.869
带类别权重的随机森林
修改决策树以解决分类不平衡问题的一个简单技巧是,在计算所选分割点的“不纯度”分数时改变每个类别的权重。
杂质衡量的是训练数据集中给定分割的样本组的混合程度,通常用基尼系数或熵来衡量。计算可能会出现偏差,导致有利于少数类的混合受到青睐,从而允许多数类出现一些假阳性。
这种随机森林的修改被称为加权随机森林。
另一种使随机森林更适合从极度不平衡的数据中学习的方法遵循成本敏感学习的思想。由于 RF 分类器倾向于偏向多数类,因此我们将对错误分类少数类施加更重的惩罚。
—使用随机森林学习不平衡数据,2004 年。
这可以通过在RandomForestClassifier类上设置class_weight参数来实现。
此参数采用一个字典,其中包含每个类值(例如 0 和 1)到权重的映射。可以提供“ balanced ”参数值以自动使用训练数据集中的逆权重,重点关注少数类。
...
# define model
model = RandomForestClassifier(n_estimators=10, class_weight='balanced')
我们可以在测试问题上测试随机森林的这种修改。虽然不是针对随机森林的,但我们预计会有一些适度的改进。
完整的示例如下。
# class balanced random forest for imbalanced classification
from numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.ensemble import RandomForestClassifier
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4)
# define model
model = RandomForestClassifier(n_estimators=10, class_weight='balanced')
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
# summarize performance
print('Mean ROC AUC: %.3f' % mean(scores))
运行示例评估模型并报告平均 ROC AUC 分数。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。
在这种情况下,我们可以看到该模型的平均 ROC AUC 从 0.86 适度提升至约 0.87。
Mean ROC AUC: 0.871
带引导类权重的随机森林
鉴于每棵决策树都是由引导样本(例如,有放回的随机选择)构建的,因此数据样本中的类分布对于每棵树来说都会有所不同。
因此,根据每个引导样本而不是整个训练数据集中的类分布来改变类权重可能会很有趣。
这可以通过将class_weight参数设置为值“ balanced_subsample ”来实现。
我们可以测试这个修改并将结果与上面的“平衡”情况进行比较;完整的示例如下所示。
# bootstrap class balanced random forest for imbalanced classification
from numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.ensemble import RandomForestClassifier
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4)
# define model
model = RandomForestClassifier(n_estimators=10, class_weight='balanced_subsample')
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
# summarize performance
print('Mean ROC AUC: %.3f' % mean(scores))
运行示例评估模型并报告平均 ROC AUC 分数。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。
在这种情况下,我们可以看到该模型的平均 ROC AUC 从 0.87 适度提升至约 0.88。
Mean ROC AUC: 0.884
随机欠采样的随机森林
随机森林的另一个有用的修改是对引导样本执行数据重采样,以便明确改变类分布。
不平衡学习库中的BalancedRandomForestClassifier类实现了这一点,并在达到引导样本时对多数类进行随机欠采样。这通常被称为平衡随机森林。
...
# define model
model = BalancedRandomForestClassifier(n_estimators=10)
鉴于数据重采样技术的广泛成功,我们预计这将对模型性能产生更显著的影响。
我们可以在我们的合成数据集上测试随机森林的这种修改并比较结果。完整的示例如下所示。
# random forest with random undersampling for imbalanced classification
from numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from imblearn.ensemble import BalancedRandomForestClassifier
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4)
# define model
model = BalancedRandomForestClassifier(n_estimators=10)
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
# summarize performance
print('Mean ROC AUC: %.3f' % mean(scores))
运行示例评估模型并报告平均 ROC AUC 分数。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。
在这种情况下,我们可以看到该模型的平均 ROC AUC 从 0.89 适度提升至约 0.97。
Mean ROC AUC: 0.970
不平衡分类的简单集成
当考虑使用袋装集成进行不平衡分类时,一个自然的想法可能是对多数类进行随机重采样,以创建具有平衡类分布的多个数据集。
具体来说,可以从少数类中的所有示例和从多数类中随机选择的样本创建一个数据集。然后可以在这个数据集上拟合一个模型或弱学习器。这个过程可以重复多次,并且可以使用整个模型集合的平均预测来进行预测。
这正是 Xu-Ying Liu 等人在 2008 年的论文“类别不平衡学习的探索性欠采样”中提出的方法。
子样本的选择性构建被视为多数类的欠采样的一种。生成多个子样本允许集成克服欠采样的缺点,因为在欠采样中有价值的信息会被从训练过程中丢弃。
…欠采样是解决类别不平衡的有效策略。然而,欠采样的缺点是它会丢弃许多可能有用的数据。
—用于类别不平衡学习的探索性欠采样,2008 年。
作者提出了该方法的变体,例如 Easy Ensemble 和 Balance Cascade。
让我们仔细看看 Easy Ensemble。
Easy Ensemble
Easy Ensemble 涉及通过从少数类中选择所有示例并从多数类中选择子集来创建训练数据集的平衡样本。
不是使用剪枝决策树,而是在每个子集上使用增强决策树,具体来说是 AdaBoost 算法。
AdaBoost 的工作原理是,首先在数据集上拟合一个决策树,然后确定决策树的错误,并根据这些错误对数据集中的示例进行加权,以便更多地关注错误分类的示例,而较少关注正确分类的示例。然后在加权数据集上拟合后续树,旨在纠正错误。然后对给定数量的决策树重复该过程。
这意味着难以分类的样本会获得越来越大的权重,直到算法找到一个可以正确分类这些样本的模型。因此,算法的每次迭代都需要学习数据的不同方面,重点关注包含难以分类样本的区域。
— 第 389 页,《应用预测模型》,2013 年。
不平衡学习库中的EasyEnsembleClassifier 类提供了简单集成技术的实现。
...
# define model
model = EasyEnsembleClassifier(n_estimators=10)
我们可以在我们的综合不平衡分类问题上评估该技术。
鉴于使用某种类型的随机欠采样,我们期望该技术总体上表现良好。
完整的示例如下。
# easy ensemble for imbalanced classification
from numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from imblearn.ensemble import EasyEnsembleClassifier
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4)
# define model
model = EasyEnsembleClassifier(n_estimators=10)
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
# summarize performance
print('Mean ROC AUC: %.3f' % mean(scores))
运行示例评估模型并报告平均 ROC AUC 分数。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。
在这种情况下,我们可以看到集成在数据集上表现良好,实现了约 0.96 的平均 ROC AUC,接近使用随机欠采样的随机森林在该数据集上实现的 AUC(0.97)。
Mean ROC AUC: 0.968
尽管每个子样本都使用了 AdaBoost 分类器,但可以通过为模型设置base_estimator参数来使用替代分类器模型。