当前位置: 首页 > 后端技术 > Python

使用分类权重解决数据不平衡问题

时间:2023-03-26 13:24:16 Python

在分类任务中,不平衡数据集是指数据集中分类不均匀的情况,会出现一个或多个类比其他类多或少的情况。在我们的日常生活中,不平衡的数据非常普遍。例如,本文将以最常见的例子之一,信用卡欺诈检测来介绍,我们日常使用中的欺诈数量远远少于正常使用的数量。很多,对我们来说这是一个数据不平衡问题。我们使用kaggle上的信用卡交易数据集作为本文的数据集。数据的细节不是特别重要。因为为了脱敏,这个数据集的特征是PCA降维后输出的,所以讨论这些特征代表什么没有意义。除了PCA输出的特征之外,该数据集还包括与每笔交易相关的美元金额、以秒为单位的连续时间索引以及表示存在或不存在欺诈的二进制目标。对于时间索引,我们考虑了一些可能有用的特征工程,但这不是本文的重点。我们真实可见的数据,只有金额一项,非常重要!让我们再看看目标。284,807行数据中只有0.173%是欺诈案例。这绝对是不平衡数据的样本。这些数据的分布会使建模和预测欺诈变得非常棘手。性能指标几个有价值的性能指标可用于了解模型在数据不平衡时的性能。通常,指标的选择在很大程度上取决于应用程序和与之相关的结果,无论是正面的还是负面的。单独一种方法并不适用于所有人。在信用卡欺诈的背景下,我们对产生高准确度分数的模型不感兴趣。由于数据集非常不平衡,欺诈很少,如果我们将所有样本归类为无欺诈,准确率仍然很高。但我们对准确预测信用卡交易何时不存在欺诈不感兴趣。我们关心的是信用卡是否存在欺诈,即样本量小的分类是否可以判断。最简单的方法是使用召回分数作为模型性能的主要指标。召回率是衡量模型准确预测了多少阳性病例的指标。在我们的特定用例中,更高的召回分数意味着我们检测到更多的欺诈案例。在本文中,除了使用召回率之外,我们还将分类与最终财务指标相结合,还记得我们之前提到的包含美元交易金额的数据集吗?我们也将把它纳入绩效评估,称之为“财务召回”。我们将在下面详细介绍。数据准备首先,让我们读入数据并将其拆分为训练集和测试集:importpandasaspdfromsklearn.model_selectionimporttrain_test_splitdf=pd.read_csv('creditcard.csv')#enumeratethefeaturecolumnsfeats=[colforcolindf.columnsif'V'incol]#Splitdatax=df[feats+['Amount']]y=df['Class']X_train,X_test,y_train,y_test=train_test_split(x,y,test_size=.2,stratify=y,random_state=41)如果之前train_test_split中没有使用过stratify参数,在处理不平衡数据时应该使用,train_test_split后的欺诈案例比例会按照通过的列的比例进行分配(具体使用方法见sklearn文档),我们的目标是保证我们在训练集和测试集中保持相同比例的类分布。基础模型我们将创建和训练一个基本的逻辑回归模型作为基线。但在此之前,让我们创建一个小函数,将每笔交易的金额纳入绩效评估。因为我们关心的是可以正确分类的欺诈案件的数量,我们也关心(可能更关心)将欺诈造成的美元损失降到最低。这就是我们之前讨论的“财务召回”。让我们创造一个大胆的数字来计算正确知识的秘密:fromsklearn.metricsimportrecall_scoredefassess_test_set_performance(model):#createpredictionsyhat=model.predict(X_test[feats])#evaluateperformanceusingcountsmodel_recall=recall_score(y_test,yhat)*100print("ClassificationTestPerformance:")print(f"Recall:{model_recall:.2f}%")#使用与每笔交易相关的美元金额计算财务绩效performance_df=pd.DataFrame({'Amount':X_test['Amount'],'Actual':y_test,'Pred':yhat})performance_df['fraud_amount']=performance_df['Amount']*performance_df['Actual']performance_df['fraud_prevented']=performance_df['fraud_amount']*performance_df['Pred']performance_df['fraud_realized']=performance_df['fraud_amount']-performance_df['fraud_prevented']financial_recall=(performance_df['fraud_prevented'].sum()/(performance_df['fraud_prevented'].sum()+性能_df['fraud_realized'].sum()))*100print()print("FinancialTestPerformance:")print(f"FinancialRecall:{financial_recall:{financial_recall:.2f}%")现在我们已经确定了评估函数,让我们训练基线模型并评估其性能:fromsklearn.linear_modelimportLogisticRegressionbaseline_model=LogisticRegression()baseline_model.fit(X_train[feats],y_train)assess_test_set_performance(baseline_model)我们的基线模型实现了59%的召回率和61%的财务召回率,这实际上比预期的要好,但在实践中肯定不好,所以我们实际上可以做得更好。通过向基线模型添加类权重来改进模型,将两个类设置为同等重要,因为模型不知道我们更关心欺诈,所以我们需要重新定义损失函数。sklearnAPI提供了一个class_weight参数,让模型知道它对正确识别欺诈的偏好。使用class_weight时,模型会收到一个字典,其中每个类都有一个键,其值是该类的权重。我们有两个类,为了这个例子,我们假设欺诈案件的重要性是其10倍。在这种情况下,我们可以像这样传递class_weights的字典:fraud_class_weights={0:1,1:10}但sklearnAPI实际上使这个过程更容易。class_weight参数也可以取值'balanced'。我们需要做的是使用以下公式构建一个权重与数据中的类分布成正比的字典:len(X_train)/(2*numpy.bincount(y_train))将上述公式应用于我们的数据,我们估计阳性案例实际上比阴性案例重要575倍。当我们将这段新代码放入逻辑回归模型时,它会更专注于正确分类我们的欺诈交易。这正是我们想要的!让我们设置一下,然后研究性能:lr_class_weights=LogisticRegression(class_weight='balanced')lr_class_weights.fit(X_train[feats],y_train)assess_test_set_performance(lr_class_weights)这个简单的改变将把我们的召回率提高到90%和93%!因为它现在对欺诈更加敏感,所以它比使用基线模型更准确地识别出更多的欺诈案例。总之,在模型中使用class_weight肯定有助于从模型中获得更多的相对性能。而且它设置起来非常容易,至少值得一试。本文介绍的方法是解决类不平衡问题的过于简单化的方法,该领域还有许多其他方法可以讨论,但是为类设置权重是一个很好的开始。https://avoid.overfit.cn/post/13e8cb84f1e1480eb62d9f029647ed3a作者;格雷格·杰