Scikit-learn中三种average_precision计算方式结果不一致的原因咨询
代码示例
from sklearn.metrics import make_scorer, average_precision_score, balanced_accuracy_score, f1_score from sklearn.metrics import accuracy_score, precision_score, recall_score from sklearn.model_selection import StratifiedKFold skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) scoring1 = ['average_precision'] scores1 = cross_validate(model, X_train[selection], y_train, scoring=scoring1, cv=skf)['test_average_precision'].mean() print("scores1 : ", scores1) scoring2 = {'average_precision': make_scorer(average_precision_score)} scores2 = cross_validate(model, X_train[selection], y_train, scoring=scoring2, cv=skf)['test_average_precision'].mean() print("scores2 : ", scores2) scoring3 = {'average_precision': 'average_precision'} scores3 = cross_validate(model, X_train[selection], y_train, scoring=scoring3, cv=skf)['test_average_precision'].mean() print("scores3 : ", scores3) # 输出结果 scores1 : 0.8458995430639721 scores2 : 0.7025850825147928 scores3 : 0.8458995430639721
问题描述
上述三种计算average_precision的方式理论上应得到相同结果,但实际scores1与scores3一致,scores2却存在明显差异。补充说明:当使用accuracy、precision、recall、f1、balanced_accuracy这些指标时,三种方式的结果完全一致,仅average_precision出现该问题。
差异的核心原因是内置评分器与手动构建的评分器在输入数据类型上的区别:
内置
'average_precision'评分器的行为
sklearn内置的'average_precision'评分器会自动适配模型输出,调用模型的predict_proba()或decision_function()方法,获取正类的概率估计/置信度得分,将其作为average_precision_score的y_score参数。这是计算平均精度(Average Precision)的标准方式,因为PR曲线需要基于连续的得分而非离散的类别标签。手动
make_scorer(average_precision_score)的默认行为
当直接用make_scorer包装average_precision_score时,默认情况下cross_validate会传入模型的预测类别标签(即model.predict()返回的0/1离散值)作为y_score。此时计算的PR曲线是基于硬分类标签的,和基于概率得分的PR曲线完全不同,最终得到的平均精度会显著偏低,这就是scores2结果异常的原因。让scoring2与其他两种方式结果一致的方法
只需在构建评分器时指定needs_proba=True,告诉cross_validate需要传入概率估计而非类别标签:scoring2 = {'average_precision': make_scorer(average_precision_score, needs_proba=True)}修改后,scores2的结果会和scores1、scores3完全一致。
为什么其他指标无此问题
accuracy、precision、recall等指标仅依赖预测类别标签,不管是内置评分器还是手动构建的评分器,传入的都是相同的类别数据,因此结果一致。而average_precision属于依赖连续得分的指标,对输入数据类型的要求更特殊。
内容的提问来源于stack exchange,提问作者user23448068

