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

设置机器学习模型的最佳阈值:0.5是二元分类的最佳阈值

时间:2023-03-25 23:40:59 Python

对于二元分类,分类器输出一个实值分数,然后对其进行阈值化以生成二元响应。例如,逻辑回归输出一个概率(0.0到1.0之间的值);分数等于或高于0.5的观察会产生正输出(许多其他模型默认使用0.5阈值)。但是使用默认的0.5阈值并不理想。在本文中,我将展示如何从二元分类器中选择最佳阈值。本文将使用Ploomber并行执行我们的实验,并使用sklearn-evaluation生成图表。这里我们以训练逻辑回归为例。假设我们正在开发一个内容审核系统,其中模型会标记包含有害内容的帖子(图像、视频等);然后人们会查看它并决定是否应该删除该内容。构建一个简单的二元分类器下面的代码片段训练我们的分类器:['figure.figsize']=(4,4)mpl.rcParams['figure.dpi']=150#创建样本数据集X,y=datasets.make_classification(1000,10,n_informative=5,class_sep=0.4)X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.3)#fitmodelclf=LogisticRegression()_=clf.fit(X_train,y_train)现在让我们对测试集进行预测,并通过混淆评估性能matrix:#predictonthetestsety_pred=clf.predict(X_test)#plotconfusionmatrixcm_dot_five=ConfusionMatrix(y_test,y_pred)cm_dot_five混淆矩阵总结了模型在四个区域的表现:我们想要左上角和Obta在右下象限中尽可能多的观察(来自测试集),因为这些是我们的模型得到正确的观察。其他象限是模型错误。改变模型的阈值会改变混淆矩阵中的值。在前面的示例中,使用clf.predict返回二进制响应(即使用0.5作为阈值);但是我们可以使用clf.predict_proba函数来获取原始概率并使用自定义阈值:y_score=clf.predict_proba(X_test)创建一个新的混淆矩阵:cm_dot_four=ConfusionMatrix(y_score[:,1]>=0.4,y_pred)sklearn-evaluation库可以轻松比较两个矩阵:cm_dot_five+cm_dot_four三角形的上部来自阈值0.5,阈值0.4中的较低者:两个模型都预测相同数量的观察结果为0(这是一个巧合)。0.5阈值:(90+56=146)。0.4阈值:(78+68=146)降低阈值会导致更多的假阴性(从56到68)降低阈值会大大增加真阳性(从92到154)小的阈值变化会极大地影响混淆矩阵。我们只分析了两个阈值。如果我们可以分析所有值的模型性能,那么我们就可以很好地理解阈值动态。但在此之前,需要定义新的模型评估指标。到目前为止,我们已经在绝对数量上评估了我们的模型。为了便于比较和评估,我们现在将定义两个归一化指标(它们的值介于0.0和1.0之间)。精度是被标记的观察到的事件的比例(例如,我们的模型认为有害的帖子,它们是有害的)。召回率是我们的模型检索到的实际事件的比例(即,在所有有害帖子中,我们能够检测到的比例)。上图来自维基百科,可以很好的说明这两个指标是如何计算的。precision和recall都是成正比的,所以都是0比1的比例。运行实验我们会根据几个threshold得到precision,recall等统计数据,以便更好的理解thresholds是如何影响它们的。我们还将多次重复此实验以测量可变性。本节中的命令是bash命令。它们需要在终端中执行,如果使用Jupyter,您可以使用%%sh魔术命令。在这里,我们使用PloomberCloud运行我们的实验。因为它允许我们并行运行实验并快速检索结果。创建一个适合模型的笔记本并计算多个阈值的统计数据,并行执行同一个笔记本20次。curl-Ohttps://raw.githubusercontent.com/ploomber/posts/master/threshold/fit.ipynb?utm_source=medium&utm_medium=blog&utm_campaign=threshold执行此笔记本(文件中的配置将告诉PloomberCloud运行它20timesinparallel):ploombercloudnbfit.ipynb几分钟后,我们将看到20个实验完成:ploombercloudstatus@latest--summarystatuscount---------------finished20流水线完成。检查输出:$ploombercloudproductsLet'sdownloadtheexperimentresultsstoredinthe.csvfile:ploomberclouddownload'threshold-selection/*.csv'--summaryVisualizeexperimentresults将加载所有实验的结果并一次绘制它们.fromglobimportglobimportpandasaspdimportnumpyasnppaths=glob('threshold-selection/**/*.csv')metrics=[pd.read_csv(path)forpathinpaths]foridx,dfinenumerate(指标):plt.plot(df.threshold,df.precision,color='blue',alpha=0.2,label='precision'ifidx==0elseNone)plt.plot(df.threshold,df.recall,color='green',alpha=0.2,label='recall'ifidx==0elseNone)plt.plot(df.threshold,df.f1,color='orange',alpha=0.2,label='f1'ifidx==0elseNone)plt.grid()plt.legend()plt.xlabel('Threshold')plt.ylabel('Metricvalue')forhandleinplt.legend().legendHandles:handle.set_alpha(1)ax=plt.twinx()foridx,dfinenumerate(metrics):ax.plot(df.threshold,df.n_flagged,label='flagged'ifidx==0elseNone,color='red',alpha=0.2)plt.ylabel('Flagged')ax.legend(loc=0)ax.legend().legendHandles[0].set_alpha(1)左侧的刻度(从0到1)是我们的三个指标:precision、recall和F1F1分为precision和recall的调和平均数,F1得分的最佳值为1.0,最差值为0.0;F1对精度和召回率一视同仁,所以你可以看到它在两者之间取得了平衡。如果您正在处理精度和召回率都很重要的用例,那么最大化F1是一种可以帮助您优化分类器阈值的方法。这里还包括一条红色曲线(右侧刻度),显示我们的模型标记为有害的案例数量。在这个内容审核示例中,可能有X名工作人员手动审查模型标记的有害帖子,但他们的数量是有限的,因此考虑标记帖子的总数可以帮助我们更好地选择阈值:例如,仅检查5,000个帖子,模型找到10,000个帖子不会有任何改善。如果我每天可以手动处理10,000个帖子,但模型只标记100个帖子,这显然是一种浪费。设置较低的阈值时,召回率较高(我们检索了大部分实际上有害的帖子),但精度较低(包括许多无害的帖子)。如果我们增加阈值,情况就会相反:召回率降低(许多有害帖子被遗漏),但准确率很高(大多数标记的帖子都是有害的)。因此,在为我们的二元分类器选择阈值时,我们必须在精度或召回率上做出妥协,因为没有分类器是完美的。让我们讨论如何推理选择合适的阈值。选择最佳阈值右侧的数据会产生噪声(较大的阈值)。为了稍微清理一下,我们将重新创建图表,而不是绘制所有值,我们将绘制2.5%、50%和97.5%的百分位数。shape=(df.shape[0],len(metrics))precision=np.zeros(shape)recall=np.zeros(shape)f1=np.zeros(shape)n_flagged=np.zeros(shape)对于我,dfinenumerate(metrics):precision[:,i]=df.precision.valuesrecall[:,i]=df.recall.valuesf1[:,i]=df.f1.valuesn_flagged[:,i]=df.n_flagged.valuesprecision_=np.quantile(precision,q=0.5,axis=1)recall_=np.quantile(recall,q=0.5,axis=1)f1_=np.quantile(f1,q=0.5,axis=1)n_flagged_=np.quantile(n_flagged,q=0.5,轴=1)plt.plot(df.threshold,precision_,color='blue',label='precision')plt.plot(df.threshold,recall_,color='green',label='recall')plt.plot(df.threshold,f1_,color='orange',label='f1')plt.fill_between(df.threshold,precision_interval[0],precision_interval[1]、color='blue'、alpha=0.2)plt.fill_between(df.threshold、recall_interval[0]、recall_interval[1]、color='green'、alpha=0.2)plt.fill_between(df.threshold,f1_interval[0],f1_interval[1],color='orange',alpha=0.2)plt.xlabel('阈值')plt.ylabel('指标值')plt.legend()ax=plt.twinx()ax.plot(df.threshold,n_flagged_,color='red',label='flagged')ax.fill_between(df.threshold,n_flagged_interval[0],n_flagged_interval[1],color='red',alpha=0.2)ax.legend(loc=3)plt.ylabel('Flagged')plt.grid()我们可以根据需要选择阈值,比如尽可能多的检索有害帖子(高召回率)更重要吗?或者为了有更高的确定性,我们标记的东西必须是有害的(高精度)?如果两者同等重要,那么在这些条件下优化的常用方法是最大化F-1分数:idx=np.argmax(f1_)prec_lower,prec_upper=precision_interval[0][idx],precision_interval[1][idx]]rec_lower,rec_upper=recall_interval[0][idx],recall_interval[1][idx]threshold=df.threshold[idx]print(f'MaxF1score:{f1_[idx]:.2f}')print('最大化F1分数时的指标:')print(f'-Threshold:{threshold:.2f}')print(f'-Precisionrange:({prec_lower:.2f},{prec_upper:.2f})')print(f'-召回范围:({rec_lower:.2f},{rec_upper:.2f})')#ResultMaxF1score:0.71MetricswhenmaximizingF1score:-Threshold:0.26-Precisionrange:(0.58,0.61)-召回范围:(0.86,0.90)在很多情况下很难决定这种权衡,因此添加一些约束会有所帮助。假设我们有10个人在审查有害帖子,他们可以一起检查5000个。那么让我们看看指标,如果我们修改阈值,使其标记大约5000个帖子:idx=np.argmax(n_flagged_<=5000)prec_lower,prec_upper=precision_interval[0][idx],precision_interval[1][idx]rec_lower,rec_upper=recall_interval[0][idx],recall_interval[1][idx]threshold=df.threshold[idx]print('Metricswhenlimitedtothemaximumof5,000flaggedevents:')print(f'-阈值:{threshold:.2f}')print(f'-精度范围:({prec_lower:.2f},{prec_upper:.2f})')print(f'-召回范围:({rec_lower:.2f},{rec_upper:.2f})')#ResultsMetricswhenlimitedtothemaximumof5,000flaggedevents:-Threshold:0.82-Precisionrange:(0.77,0.81)-Recallrange:(0.25,0.36)如果需要报告,我们可以在展示结果时展示一些替代方案:比如当前约束条件下的模型性能(5000个帖子),以及如果我们增加团队(比如将规模扩大一倍)我们如何做得更好。总结一下,二元分类器的最佳阈值是针对业务结果进行优化并考虑流程约束的阈值。通过本文中描述的过程,您可以更好地决定您的用例的最佳阈值。如果您对本文有任何疑问,请随时发表评论。还有,PloomberCloud!提供一些免费的计算能力!如果您需要一些免费服务,请尝试一下。https://avoid.overfit.cn/post/951babc49b3e4b4ca66c03c47199708f作者:EduardoBlancas