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 来找到这些指标。默认情况下,使用的是平衡准确率,但请注意,应该为用例选择有意义的指标。

注意

重要的是要注意,这些指标带有默认参数,特别是目标类的标签(即 pos_label)。因此,如果此标签不适合您的应用程序,则需要定义一个评分器并使用 make_scorer 传递正确的 pos_label(以及其他参数)。请参考 从指标函数定义评分策略 以获取有关定义自己的评分函数的信息。例如,我们展示了如何在最大化 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...
>>> # compare it with the internal score found by cross-validation
>>> model.best_score_
0.86...

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

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

但是,在使用此选项时应格外小心。由于过度拟合的风险,您永远不应该使用相同的数据来训练分类器和调整决策阈值。有关更多详细信息,请参考以下示例部分(参见 关于模型重拟合和交叉验证的考虑)。如果您资源有限,请考虑为 cv 使用浮点数以限制为内部单次训练-测试拆分。

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

3.3.1.3. 手动设置决策阈值#

前面的部分讨论了查找最佳决策阈值的策略。也可以使用类 FixedThresholdClassifier 手动设置决策阈值。

3.3.1.4. 示例#