爱笑的姑娘|应对机器学习中类不平衡的10种技巧
当一个类的观察值高于其他类的观察值时 , 则存在类失衡 。
示例:检测信用卡欺诈交易 。 如下图所示 , 欺诈交易大约为400 , 而非欺诈交易大约为90000 。
本文插图
类不平衡是机器学习中的常见问题 , 尤其是在分类问题中 。 不平衡数据可能会长时间妨碍我们的模型准确性 。
类不平衡出现在许多领域 , 包括:
- 欺诈识别
- 垃圾邮件过滤
- 疾病筛查
- SaaS订阅流失
- 广告点击
然而 , 如果数据集不平衡 , 那么在这种情况下 , 仅仅通过预测多数类就可以获得相当高的准确率 , 但是无法捕捉少数类 , 这通常是创建模型的首要目的 。
假设我们有一个信用卡公司的数据集 , 我们必须找出信用卡交易是否是欺诈性的 。
但是这里有个陷阱……欺诈交易相对罕见 , 只有6%的交易是欺诈行为 。
现在 , 在你还没有开始之前 , 你是否能想到问题应该如何解决?想象一下 , 如果你根本不花时间训练模型 。 相反 , 如果你只编写了一行总是预测“没有欺诈性交易”的代码 , 该怎么办?
def transaction(transaction_data):return 'No fradulent transaction'好吧 , 你猜怎么着?你的“解决方案”将具有94%的准确性! 不幸的是 , 这种准确性令人误解 。
- 所有这些非欺诈性的交易 , 你都将拥有100%的准确性 。
- 那些欺诈性的交易 , 你的准确性为0% 。
- 仅仅因为大多数交易不是欺诈性的(不是因为你的模型很好) , 你的总体准确性就很高 。
我们将在本文中使用信用卡欺诈检测数据集 , 你可以从此处找到该数据集 。
本文插图
# check the target variable that is fraudulet and not fradulent transactiondata['Class'].value_counts()# 0 -> non fraudulent# 1 -> fraudulent本文插图
# visualize the target variableg = sns.countplot(data['Class'])g.set_xticklabels(['Not Fraud','Fraud'])plt.show()本文插图
你可以清楚地看到数据集之间存在巨大差异 。 9000次非欺诈性交易和492次欺诈性交易 。
新开发人员用户在处理不平衡数据集时遇到的一个主要问题与用于评估其模型的指标有关 。 使用更简单的指标 , 比如准确度得分 , 可能会产生误导 。 在具有高度不平衡类的数据集中 , 分类器总是在不进行特征分析的情况下“预测”最常见的类 , 并且它的准确率很高 , 显然不是正确的 。
让我们做这个实验 , 使用简单的XGBClassifier和无特征工程:
# import linraryfrom xgboost import XGBClassifierxgb_model = XGBClassifier().fit(x_train, y_train)# predictxgb_y_predict = xgb_model.predict(x_test)# accuracy scorexgb_score = accuracy_score(xgb_y_predict, y_test)print('Accuracy score is:', xbg_score)OUTPUTAccuracy score is: 0.992我们可以看到99%的准确度 , 我们得到的是非常高的准确度 , 因为它预测大多数类别为0(非欺诈性) 。一种处理高度不平衡数据集的广泛采用的技术称为重采样 。 它包括从多数类中删除样本(欠采样)和/或从少数类中添加更多示例(过采样) 。
本文插图
尽管平衡类有很多好处 , 但是这些技巧也有缺点 。
过采样最简单的实现是复制少数群体类别的随机记录 , 这可能会导致过度捕捞 。
欠采样最简单的实现涉及从多数类中删除随机记录 , 这可能会导致信息丢失 。
让我们用信用卡欺诈检测示例来实现它 。
我们将首先将类0和类1分开 。
# class countclass_count_0, class_count_1 = data['Class'].value_counts()# Separate classclass_0 = data[data['Class'] == 0]class_1 = data[data['Class'] == 1]# print the shape of the classprint('class 0:', class_0.shape)print('class 1:', class_1.shape 本文插图
欠采样可以定义为删除多数类的观察值 。 这是在多数类和少数类被平衡之前进行的 。
当你拥有大量数据时 , 欠采样可能是一个不错的选择 , 比如数百万行 。 但是欠采样的一个缺点是我们可能删除了有价值的信息 。
本文插图
【爱笑的姑娘|应对机器学习中类不平衡的10种技巧】
class_0_under = class_0.sample(class_count_1)test_under = pd.concat([class_0_under, class_1], axis=0)print("total class of 1 and0:",test_under['Class'].value_counts())# plot the count after under-sampelingtest_under['Class'].value_counts().plot(kind='bar', title='count (target)')过采样可以定义为向少数类添加更多副本 。 当你没有大量数据要处理时 , 过采样可能是一个不错的选择 。
欠采样时要考虑的一个弊端是 , 它可能导致过拟合并导致测试集泛化不佳 。
class_1_over = class_1.sample(class_count_0, replace=True)test_over = pd.concat([class_1_over, class_0], axis=0)print("total class of 1 and 0:",test_under['Class'].value_counts())# plot the count after under-sampelingtest_over['Class'].value_counts().plot(kind='bar', title='count (target)') 本文插图
在科学文献中已经提出了许多更复杂的重采样技术 。
例如 , 我们可以将多数类的记录聚类 , 并通过从每个聚类中删除记录来进行欠采样 , 从而寻求保留信息 。 在过采样中 , 我们可以为这些副本引入较小的变化 , 从而创建更多样的合成样本 , 而不是创建少数群体记录的精确副本 。
让我们使用Python库 imbalanced-learn应用其中一些重采样技术 。 它与scikit-learn兼容 , 并且是scikit-learn-contrib项目的一部分 。
import imblearnRandomUnderSampler通过为目标类别随机选择数据子集来平衡数据的快速简便方法 。 通过随机选择有或没有替代品的样本对多数类别进行欠采样 。# import libraryfrom imblearn.under_sampling import RandomUnderSamplerrus = RandomUnderSampler(random_state=42, replacement=True)# fit predictor and target variablex_rus, y_rus = rus.fit_resample(x, y)print('original dataset shape:', Counter(y))print('Resample dataset shape', Counter(y_rus)) 本文插图
解决不平衡数据的一种方法是在少数群体中生成新样本 。 最幼稚的策略是通过随机采样替换当前可用的采样来生成新的采样 。 随机过采样提供了这样一个方案 。
# import libraryfrom imblearn.over_sampling import RandomOverSamplerros = RandomOverSampler(random_state=42)# fit predictor and target variablex_ros, y_ros = ros.fit_resample(x, y)print('Original dataset shape', Counter(y))print('Resample dataset shape', Counter(y_ros)) 本文插图
Tomek链接是一对非常接近的实例 , 但类别相反 。 删除每对多数类的实例会增加两个类之间的空间 , 从而有助于分类过程 。
如果两个样本是彼此的最近邻 , 则存在Tomek的链接
本文插图
在下面的代码中 , 我们将用于ratio='majority'对多数类进行重新采样 。
# import libraryfrom imblearn.under_sampling import TomekLinkstl = RandomOverSampler(sampling_strategy='majority')# fit predictor and target variablex_tl, y_tl = ros.fit_resample(x, y)print('Original dataset shape', Counter(y))print('Resample dataset shape', Counter(y_ros)) 本文插图
该技术为合成少数过采样技术 。
SMOTE(合成少数过采样技术)的工作原理是从少数类中随机选取一个点并计算该点的k近邻 。 合成点被添加到所选的点和它的相邻点之间 。
本文插图
SMOTE算法通过以下四个简单步骤工作:
- 选择少数类作为输入向量
- 查找其k个最近邻(在SMOTE()函数中将k_neighbors指定为参数)
- 选择这些邻居中的一个 , 并将合成点放置在连接考虑中的点及其所选邻居的线上的任何位置
- 重复这些步骤 , 直到数据平衡
# import libraryfrom imblearn.over_sampling import SMOTEsmote = SMOTE()# fit predictor and target variablex_smote, y_smote = smote.fit_resample(x, y)print('Original dataset shape', Counter(y))print('Resample dataset shape', Counter(y_ros))本文插图
NearMiss是欠采样技术 。 与其使用距离重新采样少数类 , 不如将多数类等同于少数类 。
from imblearn.under_sampling import NearMissnm = NearMiss()x_nm, y_nm = nm.fit_resample(x, y)print('Original dataset shape:', Counter(y))print('Resample dataset shape:', Counter(y_nm)) 本文插图
评估不平衡数据集时 , 准确性不是最佳的度量标准 , 因为它可能会产生误导 。
可以提供更好洞察力的指标是:
- 混淆矩阵:显示正确预测和错误预测类型的表 。
- 精度:真实阳性的数目除以所有阳性预测 。 精度也称为正预测值 。 它是分类器准确性的度量 。 低精度表示大量误报 。
- 召回率:真实阳性的数量除以测试数据中的阳性值的数量 。 召回也称为敏感度或真实阳性率 。 它是分类器完整性的度量 。 较低的召回率表示大量假阴性 。
- F1:得分:准确性和召回率的加权平均值 。
- ROC曲线下面积(AUROC):AUROC表示模型将观测值与两个类区分开来的可能性 。
下一个策略是使用惩罚性学习算法 , 该算法会增加少数类分类错误的成本 。
这项技术的一种流行算法是Penalized-SVM 。
在训练过程中 , 我们可以使用参数class_weight='balanced'来惩罚少数类的错误 , 惩罚量与代表性不足的程度成正比 。
如果我们想为支持向量机算法启用概率估计 , 我们还希望包含参数probability=True 。
让我们在原始不平衡数据集上使用Penalized-SVM训练模型:
# load libraryfrom sklearn.svm import SVC# we can add class_weight='balanced' to add panalize mistakesvc_model = SVC(class_weight='balanced', probability=True)svc_model.fit(x_train, y_train)svc_predict = svc_model.predict(x_test)# check performanceprint('ROCAUC score:',roc_auc_score(y_test, svc_predict))print('Accuracy score:',accuracy_score(y_test, svc_predict))print('F1 score:',f1_score(y_test, svc_predict)) 本文插图
尽管在每个机器学习问题中 , 尝试各种算法都是一个很好的经验法则 , 但是对于不平衡的数据集而言 , 这尤其有利 。
决策树经常在不平衡的数据上表现良好 。 在现代机器学习中 , 树集成(随机森林 , 梯度增强树等)几乎总是胜过单个决策树 , 因此我们将直接跳到:
基于树的算法通过学习 if / else 问题的层次结构来工作 。 这可以强制解决两个类 。
# load libraryfrom sklearn.ensemble import RandomForestClassifierrfc = RandomForestClassifier()# fit the predictor and targetrfc.fit(x_train, y_train)# predictrfc_predict = rfc.predict(x_test)# check performanceprint('ROCAUC score:',roc_auc_score(y_test, rfc_predict))print('Accuracy score:',accuracy_score(y_test, rfc_predict))print('F1 score:',f1_score(y_test, rfc_predict))本文插图
优点
- 当训练数据集很大时 , 它可以通过减少训练数据样本的数量来帮助改善运行时间和存储问题 。
- 它可以丢弃可能有用的信息 , 这对于构建规则分类器可能很重要 。
- 通过随机欠采样选择的样本可能是有偏差的样本 。 可能导致实际测试数据集的结果不准确 。
- 与欠采样不同 , 此方法不会导致信息丢失 。
- 在抽样条件下表现更佳
- 由于它复制了少数群体事件 , 因此增加了过度拟合的可能性 。
推荐阅读
- 养老金|2021年上半年办理退休,养老金核算的这些知识要把握
- 量化|量化大师麦教授:美好的不确定性
- 浪胃仙|泡泡龙的离世给所有吃播提了醒,浪胃仙顺势决定“转行”,新职业认真的吗?
- 脑梗死|脑梗死和喝酒有没有关系呢?爱喝酒的朋友,应该看看
- 米歇尔·戴斯玛克特|海奥华预言的真相,地球人被带到九级文明,揭开神话背后的秘密
- 减肥也能吃的小零食,营养美味,低脂低热量,多吃也不怕!
- 1碗面粉,不加水,锅里蒸一蒸,做香甜可口的发糕,比蛋糕还香
- 扇贝最好吃的做法,适合冬日里吃,做法简单好吃不腻,家人超爱吃
- 七种颜色的布丁吃过没有?软糯爽口,Q弹软糯
- 爱吃南瓜饼的收藏,外酥里嫩,香甜软糯,饭桌上必备,做法超简单
