3.3. 调整分类预测的决策阈值#

分类问题最好分为两个部分:

  • 统计问题,学习一个模型来预测,理想情况下是类概率;

  • 决策问题,根据这些概率预测采取具体行动。

让我们以一个与天气预报相关的简单例子来说明:第一点涉及回答“明天降雨的概率是多少?”,而第二点涉及回答“我明天应该带伞吗?”。

在 scikit-learn API 中,第一点通过使用 predict_probadecision_function 提供分数来解决。前者返回每个类的条件概率估计 \(P(y|X)\) ,而后者返回每个类的决策分数。

与标签对应的决策是通过 predict 获得的。在二分类中,通过阈值化分数来定义决策规则或行动,从而为每个样本预测一个单一的类标签。在 scikit-learn 的二分类中,类标签预测是通过硬编码的截止规则获得的:当条件概率 \(P(y|X)\) 大于 0.5(通过 predict_proba 获得)或决策分数大于 0(通过 decision_function 获得)时,预测为正类。

在这里,我们展示了一个示例,说明了条件概率估计 \(P(y|X)\) 与类标签之间的关系:

>>> from sklearn.datasets import make_classification
>>> from sklearn.tree import DecisionTreeClassifier
>>> X, y = make_classification(random_state=0)
>>> classifier = DecisionTreeClassifier(max_depth=2, random_state=0).fit(X, y)
>>> classifier.predict_proba(X[:4])

array([[0.94     , 0.06     ],
       [0.94     , 0.06     ],
       [0.0416..., 0.9583...],
       [0.0416..., 0.9583...]])
>>> classifier.predict(X[:4])
array([0, 0, 1, 1])

虽然这些硬编码规则在一开始作为默认行为可能看起来合理,但它们绝对不是大多数用例的理想选择。让我们用一个例子来说明。

考虑一个场景,一个预测模型被部署来协助医生检测肿瘤。在这种情况下,医生最有可能对识别所有患有癌症的患者并不错过任何患有癌症的人感兴趣,以便他们可以为他们提供正确的治疗。换句话说,医生优先考虑实现高召回率。当然,这种对召回的强调伴随着可能更多的假阳性预测,从而降低了模型的精确度。这是一个医生愿意承担的风险,因为错过癌症的成本远高于进一步诊断测试的成本。因此,在决定是否将患者分类为患有癌症时,当条件概率估计远低于0.5时,将其分类为癌症阳性可能更有益。

3.3.1. 调整决策阈值后#

解决引言中提到的问题的一个解决方案是在模型训练后调整分类器的决策阈值。TunedThresholdClassifierCV 使用内部交叉验证来调整此阈值。选择最佳阈值以最大化给定指标。

下图说明了梯度提升分类器的决策阈值调整。虽然普通分类器和调整后的分类器提供了相同的 predict_proba 输出,因此具有相同的接收器操作特征(ROC)和精确度-召回曲线,但由于调整后的阈值,类标签预测不同。

决策阈值。传统的分类器在条件概率大于0.5时预测感兴趣的类别,而经过调优的分类器在非常低的概率(大约0.02)时预测感兴趣的类别。这个决策阈值优化了由业务(在本例中是一家保险公司)定义的效用指标。

../_images/sphx_glr_plot_cost_sensitive_learning_002.png

3.3.1.1. 调优决策阈值的选项#

决策阈值可以通过由参数 scoring 控制的多种策略进行调优。

一种调优阈值的方法是通过最大化预定义的scikit-learn指标。这些指标可以通过调用函数 get_scorer_names 找到。默认情况下,使用平衡准确率作为指标,但请注意应为您的用例选择有意义的指标。

Note

需要注意的是,这些指标带有默认参数,特别是感兴趣类别的标签(即 pos_label )。因此,如果这个标签不适用于您的应用,您需要定义一个评分器并传递正确的 pos_label (以及附加参数),使用 make_scorer 。请参阅 从指标函数定义您的评分策略 以获取定义自己的评分函数的信息。例如,我们展示了如何在最大化 f1_score 时传递感兴趣标签为 0 的信息:

>>> from sklearn.linear_model import LogisticRegression
>>> from sklearn.model_selection import TunedThresholdClassifierCV
>>> from sklearn.metrics import make_scorer, f1_score
>>> X, y = make_classification(
...   n_samples=1_000, weights=[0.1, 0.9], random_state=0)
>>> pos_label = 0
>>> scorer = make_scorer(f1_score, pos_label=pos_label)
>>> base_model = LogisticRegression()
>>> model = TunedThresholdClassifierCV(base_model, scoring=scorer)
>>> scorer(model.fit(X, y), X, y)
0.88...
>>> # 与通过交叉验证找到的内部评分进行比较
>>> model.best_score_
0.86...

3.3.1.2. 关于内部交叉验证的重要说明#

默认情况下,TunedThresholdClassifierCV 使用 5 折分层交叉验证来调整决策阈值。参数 cv 允许控制交叉验证策略。可以通过设置 cv="prefit" 并提供一个已拟合的分类器来绕过交叉验证。在这种情况下,决策阈值是根据提供给 fit 方法的数据进行调整的。

然而,在使用此选项时应极其小心。由于过拟合的风险,永远不应将相同的数据用于训练分类器和调整决策阈值。有关更多详细信息,请参阅以下示例部分(参见 TunedThresholdClassifierCV_no_cv )。如果资源有限,可以考虑为 cv 使用一个浮点数,以限制为内部单一训练-测试分割。

选项 cv="prefit" 仅应在提供的分类器已经训练过,并且您只想使用新的验证集找到最佳决策阈值时使用。

3.3.1.3. 手动设置决策阈值#

前几节讨论了寻找最优决策阈值的策略。也可以通过类 FixedThresholdClassifier 手动设置决策阈值。如果您不希望在调用 fit 时重新拟合模型,可以设置参数 prefit=True

3.3.1.4. 示例#