在机器学习领域,经常面临两个基本问题:一是建立一个模型来预测类别概率,理想情况下是准确的;二是根据这些概率预测来采取具体的行动。以天气预报为例,第一个问题与回答“明天下雨的可能性有多大?”有关,而第二个问题则与回答“明天是否应该带伞?”有关。
在scikit-learnAPI中,第一个问题通过提供预测概率或决策函数来解决。前者返回每个类别的条件概率估计\(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)
print(classifier.predict_proba(X[:4]))
print(classifier.predict(X[:4]))
尽管这些硬编码规则最初看起来作为默认行为似乎是合理的,但它们肯定不是大多数用例的理想选择。让用一个例子来说明。考虑一个预测模型被部署以协助医生检测肿瘤的场景。在这种情况下,医生很可能对识别所有癌症患者感兴趣,而不是错过任何癌症患者,以便他们能够为他们提供正确的治疗。换句话说,医生优先考虑实现高召回率。这种对召回率的强调当然会带来潜在的更多假阳性预测的风险,降低了模型的精确度。医生愿意承担这种风险,因为错过癌症的成本远高于进一步诊断测试的成本。因此,当决定是否将患者分类为癌症时,当条件概率估计远低于0.5时,可能更有利于将他们分类为癌症阳性。
3.3.1 后调优决策阈值
解决引言中提出的问题的一个解决方案是在模型训练完成后调整分类器的决策阈值。TunedThresholdClassifierCV
使用内部交叉验证来调整这个阈值。选择最优阈值以最大化给定的度量标准。
以下图像说明了为梯度提升分类器调整决策阈值。虽然原始和调整后的分类器提供了相同的predict_proba
输出,因此具有相同的接收者操作特征(ROC)和精确度-召回率曲线,但由于调整的决策阈值,类别标签预测不同。原始分类器预测当条件概率大于0.5时感兴趣的类别,而调整后的分类器预测当概率非常低(大约0.02)时感兴趣的类别。这个决策阈值优化了业务(在这种情况下是一家保险公司)定义的效用度量标准。
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)
print(scorer(model.fit(X, y), X, y))
print(model.best_score_)
3.3.1.2 关于内部交叉验证的重要说明
默认情况下,TunedThresholdClassifierCV
使用5折分层交叉验证来调整决策阈值。参数cv
允许控制交叉验证策略。可以通过设置cv="prefit"
并提供拟合的分类器来绕过交叉验证。在这种情况下,决策阈值在提供给fit
方法的数据上进行调整。
但是,使用此选项时应该非常小心。永远不应该使用相同的数据来训练分类器和调整决策阈值,因为存在过拟合的风险。有关更多详细信息,请参阅以下示例部分(参见“关于模型重新拟合和交叉验证的考虑”)。如果资源有限,可以考虑使用浮点数作为cv
以限制内部的单一训练-测试拆分。
cv="prefit"
选项仅应在提供的分类器已经训练并且只想使用新的验证集找到最佳决策阈值时使用。
3.3.1.3 手动设置决策阈值
前面的部分讨论了寻找最佳决策阈值的策略。也可以使用FixedThresholdClassifier
类手动设置决策阈值。
3.3.1.4 示例