3.4. 指标与评分:量化预测质量#

3.4.1. 我应该使用哪种评分函数?#

在深入探讨众多评分和评估指标的细节之前,我们希望基于统计决策理论,就监督学习评分函数选择提供一些指导,详见[Gneiting2009]

  • 我应该使用哪种评分函数?

  • 哪种评分函数适合我的任务?

简而言之,如果评分函数已经给出,例如在 Kaggle 竞赛或商业场景中,就使用它。如果您可以自由选择,则应首先考虑预测的最终目标和应用。区分以下两个步骤会很有帮助:

  • 预测

  • 决策

预测: 通常,响应变量 \(Y\) 是一个随机变量,这意味着特征 \(X\) 之间没有确定性函数 \(Y = g(X)\)。相反,\(Y\) 存在一个概率分布 \(F\)。我们可以旨在预测整个分布,这称为概率预测,或者——scikit-learn 更侧重于——通过选择该分布 \(F\) 的一个属性或函数,发布一个点预测(或点预报)。典型的例子是响应变量 \(Y\)(在条件 \(X\) 下)的均值(期望值)、中位数或分位数。

一旦确定,就对该(目标)函数使用一个严格一致的评分函数,详见[Gneiting2009]。这意味着使用的评分函数应与测量预测值y_pred与使用\(Y\)观测值(即y_true)计算的真实目标函数之间的距离保持一致。对于分类,严格正确的评分规则(参见维基百科评分规则条目[Gneiting2007])与严格一致的评分函数相符。下表提供了示例。可以说,一致的评分函数就像吐真剂,它们保证“说真话 […] 是期望中的最优策略”[Gneiting2014]

一旦选择了严格一致的评分函数,最好将其用于模型训练的损失函数以及模型评估和模型比较中的指标/分数。

请注意,对于回归器,预测是通过predict完成的,而对于分类器,通常是predict_proba

决策: 最常见的决策是在二元分类任务中进行的,其中predict_proba的结果被转化为单一结果,例如,根据预测的降雨概率决定如何行动(是否采取携带雨伞等缓解措施)。对于分类器,这就是predict返回的结果。另请参阅调整类别预测的决策阈值。有许多评分函数衡量这种决策的不同方面,其中大多数都包含在metrics.confusion_matrix中或从中导出。

严格一致评分函数列表: 在此,我们列出了在实际任务中一些最相关的统计函数和对应的严格一致评分函数。请注意,此列表并非完整,还有更多此类函数。关于如何选择特定函数的更多标准,请参阅[Fissler2022]

函数

评分或损失函数

响应值 y

预测

分类

均值

布里尔分数 1

多类

predict_proba

均值

对数损失

多类

predict_proba

众数

零一损失 2

多类

predict, 分类

回归

均值

平方误差 3

所有实数

predict, 所有实数

均值

泊松偏差

非负

predict, 严格正

均值

伽马偏差

严格正

predict, 严格正

均值

Tweedie 偏差

取决于 power

predict, 取决于 power

中位数

绝对误差

所有实数

predict, 所有实数

分位数

弹珠损失

所有实数

predict, 所有实数

众数

不存在一致的

实数

1 布里尔分数在分类情况下只是平方误差的另一种称谓。

2 零一损失对于众数而言仅是一致的,但非严格一致。零一损失等同于一减去准确率分数,这意味着它给出不同的分数,但排名相同。

3 R² 给出与平方误差相同的排名。

虚构示例: 让我们将上述论点具体化。考虑一个网络可靠性工程场景,例如维护稳定的互联网或 Wi-Fi 连接。作为网络提供商,您可以访问包含随时间变化的负载和许多有趣特征的网络连接日志条目数据集。您的目标是提高连接的可靠性。事实上,您向客户承诺,在至少 99% 的天数中,不会出现超过 1 分钟的连接中断。因此,您对 99% 分位数(每日最长连接中断持续时间)的预测感兴趣,以便提前知道何时增加带宽,从而满足客户需求。所以目标函数是 99% 分位数。从上表中,您选择弹珠损失作为评分函数(这很公平,选择不多),用于模型训练(例如 HistGradientBoostingRegressor(loss="quantile", quantile=0.99))以及模型评估(mean_pinball_loss(..., alpha=0.99) —— 对于不同的参数名称 quantilealpha 我们深表歉意),无论是在网格搜索中寻找超参数还是与其他模型(如 QuantileRegressor(quantile=0.99))进行比较时。

参考文献

[Gneiting2007]

T. Gneiting 和 A. E. Raftery. 严格正确的评分规则、预测和估计 载于:Journal of the American Statistical Association 102 (2007),第 359–378 页。PDF 链接

[Gneiting2009] (1,2)

T. Gneiting. 点预测的制定与评估 Journal of the American Statistical Association 106 (2009): 746 - 762.

[Gneiting2014]

T. Gneiting 和 M. Katzfuss. 概率预测. 载于:Annual Review of Statistics and Its Application 1.1 (2014), 第 125–151 页。

3.4.2. 评分 API 概述#

有 3 种不同的 API 用于评估模型预测的质量

最后,Dummy 估计器有助于获取随机预测的这些指标的基准值。

另请参阅

有关样本之间而非估计器或预测之间的“成对”指标,请参阅成对指标、相似度与核函数一节。

3.4.3. scoring 参数:定义模型评估规则#

内部使用交叉验证的模型选择和评估工具(例如model_selection.GridSearchCVmodel_selection.validation_curvelinear_model.LogisticRegressionCV)接受一个scoring参数,该参数控制它们应用于被评估估计器的指标。

它们可以通过以下几种方式指定:

  • None:使用估计器的默认评估标准(即,估计器的score方法中使用的指标)。

  • 字符串名称:常用指标可以通过字符串名称传递。

  • 可调用对象:更复杂的指标可以通过自定义可调用指标(例如,函数)传递。

某些工具也支持多指标评估。详情请参阅使用多指标评估

3.4.3.1. 字符串名称评分器#

对于最常见的用例,您可以使用字符串名称通过 scoring 参数指定一个评分器对象;下表显示了所有可能的值。所有评分器对象都遵循返回值越高越好的约定。因此,衡量模型与数据之间距离的指标,例如 metrics.mean_squared_error,以 'neg_mean_squared_error' 的形式提供,返回指标的负值。

评分字符串名称

函数

备注

分类

‘accuracy’

metrics.accuracy_score

‘balanced_accuracy’

metrics.balanced_accuracy_score

‘top_k_accuracy’

metrics.top_k_accuracy_score

‘average_precision’

metrics.average_precision_score

‘neg_brier_score’

metrics.brier_score_loss

‘f1’

metrics.f1_score

用于二元目标

‘f1_micro’

metrics.f1_score

微平均

‘f1_macro’

metrics.f1_score

宏平均

‘f1_weighted’

metrics.f1_score

加权平均

‘f1_samples’

metrics.f1_score

按多标签样本

‘neg_log_loss’

metrics.log_loss

需要 predict_proba 支持

‘precision’ 等

metrics.precision_score

后缀与 ‘f1’ 相同

‘recall’ 等

metrics.recall_score

后缀与 ‘f1’ 相同

‘jaccard’ 等

metrics.jaccard_score

后缀与 ‘f1’ 相同

‘roc_auc’

metrics.roc_auc_score

‘roc_auc_ovr’

metrics.roc_auc_score

‘roc_auc_ovo’

metrics.roc_auc_score

‘roc_auc_ovr_weighted’

metrics.roc_auc_score

‘roc_auc_ovo_weighted’

metrics.roc_auc_score

‘d2_log_loss_score’

metrics.d2_log_loss_score

聚类

‘adjusted_mutual_info_score’

metrics.adjusted_mutual_info_score

‘adjusted_rand_score’

metrics.adjusted_rand_score

‘completeness_score’

metrics.completeness_score

‘fowlkes_mallows_score’

metrics.fowlkes_mallows_score

‘homogeneity_score’

metrics.homogeneity_score

‘mutual_info_score’

metrics.mutual_info_score

‘normalized_mutual_info_score’

metrics.normalized_mutual_info_score

‘rand_score’

metrics.rand_score

‘v_measure_score’

metrics.v_measure_score

回归

‘explained_variance’

metrics.explained_variance_score

‘neg_max_error’

metrics.max_error

‘neg_mean_absolute_error’

metrics.mean_absolute_error

‘neg_mean_squared_error’

metrics.mean_squared_error

‘neg_root_mean_squared_error’

metrics.root_mean_squared_error

‘neg_mean_squared_log_error’

metrics.mean_squared_log_error

‘neg_root_mean_squared_log_error’

metrics.root_mean_squared_log_error

‘neg_median_absolute_error’

metrics.median_absolute_error

‘r2’

metrics.r2_score

‘neg_mean_poisson_deviance’

metrics.mean_poisson_deviance

‘neg_mean_gamma_deviance’

metrics.mean_gamma_deviance

‘neg_mean_absolute_percentage_error’

metrics.mean_absolute_percentage_error

‘d2_absolute_error_score’

metrics.d2_absolute_error_score

使用示例

>>> from sklearn import svm, datasets
>>> from sklearn.model_selection import cross_val_score
>>> X, y = datasets.load_iris(return_X_y=True)
>>> clf = svm.SVC(random_state=0)
>>> cross_val_score(clf, X, y, cv=5, scoring='recall_macro')
array([0.96, 0.96, 0.96, 0.93, 1.        ])

注意

如果传入了错误的评分器名称,将引发 InvalidParameterError。您可以通过调用 get_scorer_names 来检索所有可用评分器的名称。

3.4.3.2. 可调用评分器#

对于更复杂的用例和更高的灵活性,您可以将一个可调用对象传递给 scoring 参数。这可以通过以下方式完成:

3.4.3.2.1. 通过 make_scorer 调整预定义指标#

以下指标函数未作为命名评分器实现,有时是因为它们需要额外的参数,例如 fbeta_score。它们不能直接传递给 scoring 参数;相反,它们的可调用对象需要连同用户可设置参数的值一起传递给 make_scorer

函数

参数

示例用法

分类

metrics.fbeta_score

beta

make_scorer(fbeta_score, beta=2)

回归

metrics.mean_tweedie_deviance

power

make_scorer(mean_tweedie_deviance, power=1.5)

metrics.mean_pinball_loss

alpha

make_scorer(mean_pinball_loss, alpha=0.95)

metrics.d2_tweedie_score

power

make_scorer(d2_tweedie_score, power=1.5)

metrics.d2_pinball_score

alpha

make_scorer(d2_pinball_score, alpha=0.95)

一个典型的用例是,使用非默认参数值(例如fbeta_score函数的beta参数)来包装库中现有的指标函数

>>> from sklearn.metrics import fbeta_score, make_scorer
>>> ftwo_scorer = make_scorer(fbeta_score, beta=2)
>>> from sklearn.model_selection import GridSearchCV
>>> from sklearn.svm import LinearSVC
>>> grid = GridSearchCV(LinearSVC(), param_grid={'C': [1, 10]},
...                     scoring=ftwo_scorer, cv=5)

模块 sklearn.metrics 还公开了一组简单的函数,用于衡量给定真实值和预测值时的预测误差。

  • _score 结尾的函数返回一个需要最大化的值,越高越好。

  • _error_loss_deviance 结尾的函数返回一个需要最小化的值,越低越好。当使用 make_scorer 转换为评分器对象时,将 greater_is_better 参数设置为 False(默认为 True;请参阅下面的参数说明)。

3.4.3.2.2. 创建自定义评分器对象#

您可以使用 make_scorer 创建自己的自定义评分器对象,或者为了获得最大灵活性,从头开始创建。详见下文。

使用 make_scorer 的自定义评分器对象#

您可以使用 make_scorer 从一个简单的 Python 函数构建一个完全自定义的评分器对象,该函数可以接受多个参数:

  • 您想要使用的 Python 函数(在下面的示例中是 my_custom_loss_func

  • Python 函数返回的是分数(greater_is_better=True,默认值)还是损失(greater_is_better=False)。如果是损失,评分器对象会将 Python 函数的输出取负,以符合交叉验证中评分器为更好的模型返回更高值的约定。

  • 仅适用于分类指标:您提供的 Python 函数是否需要连续的决策确定性。如果评分函数只接受概率估计(例如 metrics.log_loss),则需要设置参数 response_method="predict_proba"。有些评分函数不一定需要概率估计,而是非阈值的决策值(例如 metrics.roc_auc_score)。在这种情况下,可以提供一个列表(例如,response_method=["decision_function", "predict_proba"]),评分器将按照列表中给定的顺序使用第一个可用的方法来计算分数。

  • 评分函数的任何附加参数,例如 betalabels

以下是构建自定义评分器和使用 greater_is_better 参数的示例:

>>> import numpy as np
>>> def my_custom_loss_func(y_true, y_pred):
...     diff = np.abs(y_true - y_pred).max()
...     return float(np.log1p(diff))
...
>>> # score will negate the return value of my_custom_loss_func,
>>> # which will be np.log(2), 0.693, given the values for X
>>> # and y defined below.
>>> score = make_scorer(my_custom_loss_func, greater_is_better=False)
>>> X = [[1], [1]]
>>> y = [0, 1]
>>> from sklearn.dummy import DummyClassifier
>>> clf = DummyClassifier(strategy='most_frequent', random_state=0)
>>> clf = clf.fit(X, y)
>>> my_custom_loss_func(y, clf.predict(X))
0.69
>>> score(clf, X, y)
-0.69
从零开始创建自定义评分器对象#

您可以从头开始构建自己的评分对象,而无需使用 make_scorer 工厂,从而生成更灵活的模型评分器。

要使一个可调用对象成为评分器,它需要满足以下两条规则规定的协议:

  • 它可以通过参数 (estimator, X, y) 调用,其中 estimator 是要评估的模型,X 是验证数据,而 yX 的真实目标值(在监督学习情况下)或 None(在无监督学习情况下)。

  • 它返回一个浮点数,用于量化 estimatorX 上的预测质量,并参考 y。同样,按照惯例,数值越高越好,因此如果您的评分器返回的是损失值,则应将其取负。

  • 高级:如果它需要传递额外的元数据,它应该公开一个 get_metadata_routing 方法,返回请求的元数据。用户应该能够通过 set_score_request 方法设置请求的元数据。有关详细信息,请参阅用户指南开发者指南

在 n_jobs > 1 的函数中使用自定义评分器#

虽然在调用函数旁边定义自定义评分函数应该可以直接与默认的 joblib 后端(loky)一起工作,但从另一个模块导入它将是更稳健的方法,并且独立于 joblib 后端工作。

例如,要在下面的示例中使用大于 1 的 n_jobscustom_scoring_function 函数需要保存到用户创建的模块(custom_scorer_module.py)中并导入。

>>> from custom_scorer_module import custom_scoring_function 
>>> cross_val_score(model,
...  X_train,
...  y_train,
...  scoring=make_scorer(custom_scoring_function, greater_is_better=False),
...  cv=5,
...  n_jobs=-1) 

3.4.3.3. 使用多指标评估#

Scikit-learn 还允许在 GridSearchCVRandomizedSearchCVcross_validate 中评估多个指标。

有三种方法可以为 scoring 参数指定多个评分指标:

  • 作为字符串指标的可迭代对象

    >>> scoring = ['accuracy', 'precision']
    
  • 作为将评分器名称映射到评分函数的 dict

    >>> from sklearn.metrics import accuracy_score
    >>> from sklearn.metrics import make_scorer
    >>> scoring = {'accuracy': make_scorer(accuracy_score),
    ...            'prec': 'precision'}
    

    请注意,字典的值可以是评分函数或预定义的指标字符串之一。

  • 作为返回分数字典的可调用对象

    >>> from sklearn.model_selection import cross_validate
    >>> from sklearn.metrics import confusion_matrix
    >>> # A sample toy binary classification dataset
    >>> X, y = datasets.make_classification(n_classes=2, random_state=0)
    >>> svm = LinearSVC(random_state=0)
    >>> def confusion_matrix_scorer(clf, X, y):
    ...      y_pred = clf.predict(X)
    ...      cm = confusion_matrix(y, y_pred)
    ...      return {'tn': cm[0, 0], 'fp': cm[0, 1],
    ...              'fn': cm[1, 0], 'tp': cm[1, 1]}
    >>> cv_results = cross_validate(svm, X, y, cv=5,
    ...                             scoring=confusion_matrix_scorer)
    >>> # Getting the test set true positive scores
    >>> print(cv_results['test_tp'])
    [10  9  8  7  8]
    >>> # Getting the test set false negative scores
    >>> print(cv_results['test_fn'])
    [0 1 2 3 2]
    

3.4.4. 分类指标#

sklearn.metrics 模块实现了多个损失、评分和实用函数,用于衡量分类性能。某些指标可能需要正类的概率估计、置信度值或二元决策值。大多数实现允许每个样本通过 sample_weight 参数对总分提供加权贡献。

其中一些仅限于二元分类情况:

precision_recall_curve(y_true, y_score, *[, ...])

计算不同概率阈值下的精确率-召回率对。

roc_curve(y_true, y_score, *[, pos_label, ...])

计算接收者操作特征(ROC)。

class_likelihood_ratios(y_true, y_pred, *[, ...])

计算二元分类的正负似然比。

det_curve(y_true, y_score[, pos_label, ...])

计算不同概率阈值下的检测错误权衡(DET)。

其他函数也适用于多类情况:

balanced_accuracy_score(y_true, y_pred, *[, ...])

计算平衡准确率。

cohen_kappa_score(y1, y2, *[, labels, ...])

计算科恩 Kappa 系数:衡量标注者间一致性的统计量。

confusion_matrix(y_true, y_pred, *[, ...])

计算混淆矩阵以评估分类的准确性。

hinge_loss(y_true, pred_decision, *[, ...])

平均铰链损失(非正则化)。

matthews_corrcoef(y_true, y_pred, *[, ...])

计算马修斯相关系数(MCC)。

roc_auc_score(y_true, y_score, *[, average, ...])

根据预测分数计算接收者操作特征曲线下面积(ROC AUC)。

top_k_accuracy_score(y_true, y_score, *[, ...])

Top-k 准确率分类分数。

有些也适用于多标签情况:

accuracy_score(y_true, y_pred, *[, ...])

准确率分类分数。

classification_report(y_true, y_pred, *[, ...])

生成一个显示主要分类指标的文本报告。

f1_score(y_true, y_pred, *[, labels, ...])

计算 F1 分数,也称为平衡 F 分数或 F 值。

fbeta_score(y_true, y_pred, *, beta[, ...])

计算 F-beta 分数。

hamming_loss(y_true, y_pred, *[, sample_weight])

计算平均汉明损失。

jaccard_score(y_true, y_pred, *[, labels, ...])

Jaccard 相似系数分数。

log_loss(y_true, y_pred, *[, normalize, ...])

对数损失,又称逻辑损失或交叉熵损失。

multilabel_confusion_matrix(y_true, y_pred, *)

为每个类别或样本计算混淆矩阵。

precision_recall_fscore_support(y_true, ...)

计算每个类别的精确率、召回率、F 值和支持度。

precision_score(y_true, y_pred, *[, labels, ...])

计算精确率。

recall_score(y_true, y_pred, *[, labels, ...])

计算召回率。

roc_auc_score(y_true, y_score, *[, average, ...])

根据预测分数计算接收者操作特征曲线下面积(ROC AUC)。

zero_one_loss(y_true, y_pred, *[, ...])

零一分类损失。

d2_log_loss_score(y_true, y_pred, *[, ...])

\(D^2\) 评分函数,解释的对数损失分数。

有些也适用于二元和多标签(但不包括多类)问题:

average_precision_score(y_true, y_score, *)

根据预测分数计算平均精确率(AP)。

在以下子节中,我们将描述每个函数,并在之前附上一些关于通用 API 和指标定义的注释。

3.4.4.1. 从二元到多类和多标签#

一些指标主要为二元分类任务定义(例如f1_scoreroc_auc_score)。在这些情况下,默认只评估正标签,假定正类标签为 1(尽管这可以通过 pos_label 参数进行配置)。

在将二元指标扩展到多类或多标签问题时,数据被视为一组二元问题,每个类别对应一个。然后有多种方法可以对所有类别进行二元指标计算的平均,每种方法在某些场景中都可能有用。在可用时,您应使用 average 参数在这些方法中进行选择。

  • "macro"(宏平均)简单计算二元指标的平均值,为每个类别赋予相同的权重。在不常见类别却仍然重要的问题中,宏平均可能是一种突出其性能的方法。另一方面,所有类别同等重要的假设通常是不正确的,因此宏平均会过分强调不常见类别通常较低的性能。

  • "weighted"(加权平均)通过计算二元指标的平均值来考虑类别不平衡,其中每个类别的分数都根据其在真实数据样本中的出现频率进行加权。

  • "micro"(微平均)为每个样本-类别对提供对整体指标的同等贡献(样本权重导致的情况除外)。它不是按类别汇总指标,而是汇总构成每个类别指标的被除数和除数,以计算总商。微平均在多标签设置中可能更受青睐,包括当需要忽略多数类时的多类分类。

  • "samples"(样本平均)仅适用于多标签问题。它不计算每个类别的度量,而是计算评估数据中每个样本的真实类别和预测类别上的指标,并返回其(sample_weight 加权的)平均值。

  • 选择 average=None 将返回一个包含每个类别分数的数组。

多类数据像二元目标一样,以类别标签数组的形式提供给指标,而多标签数据则指定为指示矩阵,其中如果样本 i 具有标签 j,则单元格 [i, j] 的值为 1,否则为 0。

3.4.4.2. 准确率分数#

accuracy_score 函数计算准确率,即正确预测的比例(默认)或计数(normalize=False)。

在多标签分类中,该函数返回子集准确率。如果样本的整个预测标签集与真实标签集严格匹配,则子集准确率为 1.0;否则为 0.0。

如果 \(\hat{y}_i\) 是第 \(i\) 个样本的预测值,\(y_i\) 是对应的真实值,则 \(n_\text{samples}\) 中正确预测的比例定义为:

\[\texttt{accuracy}(y, \hat{y}) = \frac{1}{n_\text{samples}} \sum_{i=0}^{n_\text{samples}-1} 1(\hat{y}_i = y_i)\]

其中 \(1(x)\)指示函数

>>> import numpy as np
>>> from sklearn.metrics import accuracy_score
>>> y_pred = [0, 2, 1, 3]
>>> y_true = [0, 1, 2, 3]
>>> accuracy_score(y_true, y_pred)
0.5
>>> accuracy_score(y_true, y_pred, normalize=False)
2.0

在带有二元标签指示符的多标签情况下:

>>> accuracy_score(np.array([[0, 1], [1, 1]]), np.ones((2, 2)))
0.5

示例

3.4.4.3. Top-k 准确率分数#

top_k_accuracy_score 函数是 accuracy_score 的泛化。区别在于,只要真实标签与 k 个最高预测分数之一相关联,预测就被认为是正确的。accuracy_scorek = 1 的特例。

该函数涵盖二元和多类分类情况,但不涵盖多标签情况。

如果 \(\hat{f}_{i,j}\) 是对应于第 \(j\) 个最大预测分数的第 \(i\) 个样本的预测类别,并且 \(y_i\) 是对应的真实值,则在 \(n_\text{samples}\) 中正确预测的比例定义为:

\[\texttt{top-k accuracy}(y, \hat{f}) = \frac{1}{n_\text{samples}} \sum_{i=0}^{n_\text{samples}-1} \sum_{j=1}^{k} 1(\hat{f}_{i,j} = y_i)\]

其中 \(k\) 是允许的猜测次数,\(1(x)\)指示函数

>>> import numpy as np
>>> from sklearn.metrics import top_k_accuracy_score
>>> y_true = np.array([0, 1, 2, 2])
>>> y_score = np.array([[0.5, 0.2, 0.2],
...                     [0.3, 0.4, 0.2],
...                     [0.2, 0.4, 0.3],
...                     [0.7, 0.2, 0.1]])
>>> top_k_accuracy_score(y_true, y_score, k=2)
0.75
>>> # Not normalizing gives the number of "correctly" classified samples
>>> top_k_accuracy_score(y_true, y_score, k=2, normalize=False)
3.0

3.4.4.4. 平衡准确率分数#

balanced_accuracy_score 函数计算平衡准确率,这可以避免在不平衡数据集上出现过高的性能估计。它是每个类别的召回率的宏平均,或者等效地,是原始准确率,其中每个样本根据其真实类别的逆流行率进行加权。因此,对于平衡数据集,该分数等于准确率。

在二元情况下,平衡准确率等于灵敏度(真阳性率)和特异度(真阴性率)的算术平均值,或者说是二元预测而非分数下的 ROC 曲线下面积:

\[\texttt{balanced-accuracy} = \frac{1}{2}\left( \frac{TP}{TP + FN} + \frac{TN}{TN + FP}\right )\]

如果分类器在任一类别上表现同样出色,则此项将简化为传统的准确率(即,正确预测的数量除以总预测数量)。

相反,如果传统准确率仅因分类器利用了不平衡的测试集而高于偶然水平,那么平衡准确率将相应地降至 \(\frac{1}{n\_classes}\)

分数的范围是 0 到 1,或者当使用 adjusted=True 时,它被重新缩放到 \(\frac{1}{1 - n\_classes}\) 到 1(包含两端)的范围,其中随机表现得分0。

如果 \(y_i\) 是第 \(i\) 个样本的真实值,\(w_i\) 是对应的样本权重,那么我们将样本权重调整为:

\[\hat{w}_i = \frac{w_i}{\sum_j{1(y_j = y_i) w_j}}\]

其中 \(1(x)\)指示函数。给定样本 \(i\) 的预测值 \(\hat{y}_i\),平衡准确率定义为:

\[\texttt{balanced-accuracy}(y, \hat{y}, w) = \frac{1}{\sum{\hat{w}_i}} \sum_i 1(\hat{y}_i = y_i) \hat{w}_i\]

当使用 adjusted=True 时,平衡准确率报告的是相对于 \(\texttt{balanced-accuracy}(y, \mathbf{0}, w) = \frac{1}{n\_classes}\) 的相对增长。在二元情况下,这也称为*Youden 的 J 统计量*信息量

注意

这里提出的多类定义似乎是二元分类中使用指标最合理的扩展,尽管文献中尚无确切共识:

  • 我们的定义:[Mosley2013][Kelleher2015][Guyon2015],其中 [Guyon2015] 采用了调整后的版本,以确保随机预测的得分为 \(0\),完美预测的得分为 \(1\)

  • 类别平衡准确率,如[Mosley2013]所述:计算每个类别的精确率和召回率中的最小值。然后将这些值对总类别数进行平均,以获得平衡准确率。

  • 平衡准确率,如[Urbanowicz2015]所述:计算每个类别的灵敏度和特异度的平均值,然后对总类别数进行平均。

参考文献

[Guyon2015] (1,2)

I. Guyon, K. Bennett, G. Cawley, H.J. Escalante, S. Escalera, T.K. Ho, N. Macià, B. Ray, M. Saeed, A.R. Statnikov, E. Viegas, 2015 ChaLearn AutoML 挑战赛设计, IJCNN 2015.

[Mosley2013] (1,2)

L. Mosley, 多类不平衡问题的平衡方法, IJCV 2010.

[Kelleher2015]

John. D. Kelleher, Brian Mac Namee, Aoife D’Arcy, 预测数据分析的机器学习基础:算法、示例和案例研究, 2015。

[Urbanowicz2015]

Urbanowicz R.J., Moore, J.H. ExSTraCS 2.0: 可扩展学习分类器系统的描述与评估, Evol. Intel. (2015) 8: 89.

3.4.4.5. 科恩 Kappa 系数#

函数 cohen_kappa_score 计算科恩 Kappa 统计量。此度量旨在比较不同人工标注者的标注结果,而非分类器与真实值之间的比较。

Kappa 分数介于 -1 和 1 之间。高于 0.8 的分数通常被认为是良好的一致性;零或更低则表示不一致(实际上是随机标签)。

Kappa 分数可以为二元或多类问题计算,但不适用于多标签问题(除非手动计算每个标签的分数),也不适用于超过两名标注者的情况。

>>> from sklearn.metrics import cohen_kappa_score
>>> labeling1 = [2, 0, 2, 2, 0, 1]
>>> labeling2 = [0, 0, 2, 2, 0, 2]
>>> cohen_kappa_score(labeling1, labeling2)
0.4285714285714286

3.4.4.6. 混淆矩阵#

confusion_matrix 函数通过计算混淆矩阵来评估分类准确率,其中每行对应真实类别(维基百科和其他参考文献可能对轴使用不同的约定)。

根据定义,混淆矩阵中的条目 \(i, j\) 是实际属于组 \(i\) 但预测属于组 \(j\) 的观测数量。以下是一个示例:

>>> from sklearn.metrics import confusion_matrix
>>> y_true = [2, 0, 2, 2, 0, 1]
>>> y_pred = [0, 0, 2, 2, 0, 2]
>>> confusion_matrix(y_true, y_pred)
array([[2, 0, 0],
       [0, 0, 1],
       [1, 0, 2]])

ConfusionMatrixDisplay 可用于可视化混淆矩阵,如混淆矩阵示例所示,该示例创建了以下图形:

../_images/sphx_glr_plot_confusion_matrix_001.png

参数 normalize 允许报告比例而非计数。混淆矩阵可以通过 3 种不同方式进行归一化:'pred''true''all',它们将分别将计数除以每列、每行或整个矩阵的总和。

>>> y_true = [0, 0, 0, 1, 1, 1, 1, 1]
>>> y_pred = [0, 1, 0, 1, 0, 1, 0, 1]
>>> confusion_matrix(y_true, y_pred, normalize='all')
array([[0.25 , 0.125],
       [0.25 , 0.375]])

对于二元问题,我们可以按如下方式获取真阴性、假阳性、假阴性和真阳性的计数:

>>> y_true = [0, 0, 0, 1, 1, 1, 1, 1]
>>> y_pred = [0, 1, 0, 1, 0, 1, 0, 1]
>>> tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel().tolist()
>>> tn, fp, fn, tp
(2, 1, 2, 3)

示例

3.4.4.7. 分类报告#

classification_report 函数生成一个显示主要分类指标的文本报告。以下是一个带有自定义 target_names 和推断标签的小示例:

>>> from sklearn.metrics import classification_report
>>> y_true = [0, 1, 2, 2, 0]
>>> y_pred = [0, 0, 2, 1, 0]
>>> target_names = ['class 0', 'class 1', 'class 2']
>>> print(classification_report(y_true, y_pred, target_names=target_names))
              precision    recall  f1-score   support

     class 0       0.67      1.00      0.80         2
     class 1       0.00      0.00      0.00         1
     class 2       1.00      0.50      0.67         2

    accuracy                           0.60         5
   macro avg       0.56      0.50      0.49         5
weighted avg       0.67      0.60      0.59         5

示例

3.4.4.8. 汉明损失#

hamming_loss 计算两组样本之间的平均汉明损失或汉明距离

如果 \(\hat{y}_{i,j}\) 是给定样本 \(i\) 的第 \(j\) 个标签的预测值,\(y_{i,j}\) 是对应的真实值,\(n_\text{samples}\) 是样本数量,\(n_\text{labels}\) 是标签数量,则汉明损失 \(L_{Hamming}\) 定义为:

\[L_{Hamming}(y, \hat{y}) = \frac{1}{n_\text{samples} * n_\text{labels}} \sum_{i=0}^{n_\text{samples}-1} \sum_{j=0}^{n_\text{labels} - 1} 1(\hat{y}_{i,j} \not= y_{i,j})\]

其中 \(1(x)\)指示函数

上述公式在多类分类情况下不成立。有关更多信息,请参阅下面的注释。

>>> from sklearn.metrics import hamming_loss
>>> y_pred = [1, 2, 3, 4]
>>> y_true = [2, 2, 3, 4]
>>> hamming_loss(y_true, y_pred)
0.25

在带有二元标签指示符的多标签情况下:

>>> hamming_loss(np.array([[0, 1], [1, 1]]), np.zeros((2, 2)))
0.75

注意

在多类分类中,汉明损失对应于 y_truey_pred 之间的汉明距离,类似于零一损失函数。然而,零一损失惩罚的是与真实集不严格匹配的预测集,而汉明损失惩罚的是单个标签。因此,汉明损失被零一损失上限限制,始终介于零和一之间(含零和一);预测真实标签的真子集或真超集将导致汉明损失介于零和一之间(不含零和一)。

3.4.4.9. 精确率、召回率和 F 值#

直观地说,精确率(precision)是分类器不将负样本错误地标记为正样本的能力,而召回率(recall)是分类器找到所有正样本的能力。

F-度量(F-measure)\(F_\beta\)\(F_1\) 度量)可以解释为精确率和召回率的加权调和平均值。\(F_\beta\) 度量的最佳值为 1,最差值为 0。当 \(\beta = 1\) 时,\(F_\beta\)\(F_1\) 是等价的,并且召回率和精确率同等重要。

precision_recall_curve 函数通过改变决策阈值,从真实标签和分类器给出的分数计算出精确率-召回率曲线。

average_precision_score 函数根据预测分数计算平均精确率(average precision, AP)。该值介于 0 和 1 之间,越高越好。AP 定义为

\[\text{AP} = \sum_n (R_n - R_{n-1}) P_n\]

其中 \(P_n\)\(R_n\) 分别是第 n 个阈值下的精确率和召回率。对于随机预测,AP 是正样本的比例。

参考文献 [Manning2008][Everingham2010] 提出了 AP 的替代变体,它们通过插值精确率-召回率曲线来计算。目前,average_precision_score 没有实现任何插值变体。参考文献 [Davis2006][Flach2015] 解释了为什么精确率-召回率曲线上点的线性插值会提供对分类器性能过于乐观的度量。这种线性插值在 auc 中使用梯形法则计算曲线下面积时会被用到。

以下是一些函数,可用于分析精确率、召回率和 F-度量分数

average_precision_score(y_true, y_score, *)

根据预测分数计算平均精确率(AP)。

f1_score(y_true, y_pred, *[, labels, ...])

计算 F1 分数,也称为平衡 F 分数或 F 值。

fbeta_score(y_true, y_pred, *, beta[, ...])

计算 F-beta 分数。

precision_recall_curve(y_true, y_score, *[, ...])

计算不同概率阈值下的精确率-召回率对。

precision_recall_fscore_support(y_true, ...)

计算每个类别的精确率、召回率、F 值和支持度。

precision_score(y_true, y_pred, *[, labels, ...])

计算精确率。

recall_score(y_true, y_pred, *[, labels, ...])

计算召回率。

请注意,precision_recall_curve 函数仅限于二分类情况。average_precision_score 函数通过 One-vs-the-rest (OvR) 方式计算每个类别的分数,并根据其 average 参数值选择是否进行平均,从而支持多类别和多标签格式。

PrecisionRecallDisplay.from_estimatorPrecisionRecallDisplay.from_predictions 函数将如下绘制精确率-召回率曲线。

../_images/sphx_glr_plot_precision_recall_001.png

示例

参考文献

3.4.4.9.1. 二分类#

在二分类任务中,“正(positive)”和“负(negative)”指分类器的预测,而“真(true)”和“假(false)”指该预测是否与外部判断(有时称为“观察结果(observation)”)一致。根据这些定义,我们可以列出下表

实际类别(观察结果)

预测类别(预期结果)

tp (真阳性) 正确结果

fp (假阳性) 意外结果

fn (假阴性) 缺失结果

tn (真阴性) 正确无结果

在此背景下,我们可以定义精确率和召回率的概念

\[\text{precision} = \frac{\text{tp}}{\text{tp} + \text{fp}},\]
\[\text{recall} = \frac{\text{tp}}{\text{tp} + \text{fn}},\]

(有时召回率也称为“敏感度(sensitivity)”)

F-度量是精确率和召回率的加权调和平均值,其中精确率对平均值的贡献由参数 \(\beta\) 加权

\[F_\beta = (1 + \beta^2) \frac{\text{precision} \times \text{recall}}{\beta^2 \text{precision} + \text{recall}}\]

为了避免精确率和召回率为零时出现除以零的情况,Scikit-Learn 使用以下在其他情况下等效的公式计算 F-度量

\[F_\beta = \frac{(1 + \beta^2) \text{tp}}{(1 + \beta^2) \text{tp} + \text{fp} + \beta^2 \text{fn}}\]

请注意,当没有真阳性、假阳性或假阴性时,此公式仍然是未定义的。默认情况下,对于一组完全为真阴性的情况,F-1 的计算结果为 0,但是此行为可以通过 zero_division 参数进行更改。以下是二分类中的一些小例子

>>> from sklearn import metrics
>>> y_pred = [0, 1, 0, 0]
>>> y_true = [0, 1, 0, 1]
>>> metrics.precision_score(y_true, y_pred)
1.0
>>> metrics.recall_score(y_true, y_pred)
0.5
>>> metrics.f1_score(y_true, y_pred)
0.66
>>> metrics.fbeta_score(y_true, y_pred, beta=0.5)
0.83
>>> metrics.fbeta_score(y_true, y_pred, beta=1)
0.66
>>> metrics.fbeta_score(y_true, y_pred, beta=2)
0.55
>>> metrics.precision_recall_fscore_support(y_true, y_pred, beta=0.5)
(array([0.66, 1.        ]), array([1. , 0.5]), array([0.71, 0.83]), array([2, 2]))


>>> import numpy as np
>>> from sklearn.metrics import precision_recall_curve
>>> from sklearn.metrics import average_precision_score
>>> y_true = np.array([0, 0, 1, 1])
>>> y_scores = np.array([0.1, 0.4, 0.35, 0.8])
>>> precision, recall, threshold = precision_recall_curve(y_true, y_scores)
>>> precision
array([0.5       , 0.66, 0.5       , 1.        , 1.        ])
>>> recall
array([1. , 1. , 0.5, 0.5, 0. ])
>>> threshold
array([0.1 , 0.35, 0.4 , 0.8 ])
>>> average_precision_score(y_true, y_scores)
0.83

3.4.4.9.2. 多类别和多标签分类#

在多类别和多标签分类任务中,精确率、召回率和 F-度量的概念可以独立地应用于每个标签。有几种方法可以组合跨标签的结果,这些方法由 average_precision_scoref1_scorefbeta_scoreprecision_recall_fscore_supportprecision_scorerecall_score 函数的 average 参数指定,如上文所述。

请注意平均时的以下行为

  • 如果包含所有标签,多类别设置中的“微观(micro)”平均将产生与准确率完全相同的精确率、召回率和 \(F\) 值。

  • “加权(weighted)”平均可能会产生一个不在精确率和召回率之间的 F-分数。

  • F-度量的“宏观(macro)”平均是按每个标签/类别的 F-度量进行算术平均,而不是按精确率和召回率的算术平均值的调和平均。这两种计算方法都可以在文献中找到,但它们并不等价,详情请参阅 [OB2019]

为了更明确地说明这一点,请考虑以下符号

  • \(y\) *真实* \((样本, 标签)\) 对的集合

  • \(\hat{y}\) *预测* \((样本, 标签)\) 对的集合

  • \(L\) 标签的集合

  • \(S\) 样本的集合

  • \(y_s\)\(y\) 中包含样本 \(s\) 的子集,即 \(y_s := \left\{(s', l) \in y | s' = s\right\}\)

  • \(y_l\)\(y\) 中包含标签 \(l\) 的子集

  • 类似地,\(\hat{y}_s\)\(\hat{y}_l\)\(\hat{y}\) 的子集

  • 对于某些集合 \(A\)' 和 '\(B\)\(P(A, B) := \frac{\left| A \cap B \right|}{\left|B\right|}\)

  • \(R(A, B) := \frac{\left| A \cap B \right|}{\left|A\right|}\) (处理 \(A = \emptyset\) 的约定有所不同;此实现使用 \(R(A, B):=0\),对于 \(P\) 类似。)

  • \(F_\beta(A, B) := \left(1 + \beta^2\right) \frac{P(A, B) \times R(A, B)}{\beta^2 P(A, B) + R(A, B)}\)

那么这些指标定义为

平均方式

精确率

召回率

F_beta

"微观(micro)"

\(P(y, \hat{y})\)

\(R(y, \hat{y})\)

\(F_\beta(y, \hat{y})\)

"样本(samples)"

\(\frac{1}{\left|S\right|} \sum_{s \in S} P(y_s, \hat{y}_s)\)

\(\frac{1}{\left|S\right|} \sum_{s \in S} R(y_s, \hat{y}_s)\)

\(\frac{1}{\left|S\right|} \sum_{s \in S} F_\beta(y_s, \hat{y}_s)\)

"宏观(macro)"

\(\frac{1}{\left|L\right|} \sum_{l \in L} P(y_l, \hat{y}_l)\)

\(\frac{1}{\left|L\right|} \sum_{l \in L} R(y_l, \hat{y}_l)\)

\(\frac{1}{\left|L\right|} \sum_{l \in L} F_\beta(y_l, \hat{y}_l)\)

"加权(weighted)"

\(\frac{1}{\sum_{l \in L} \left|y_l\right|} \sum_{l \in L} \left|y_l\right| P(y_l, \hat{y}_l)\)

\(\frac{1}{\sum_{l \in L} \left|y_l\right|} \sum_{l \in L} \left|y_l\right| R(y_l, \hat{y}_l)\)

\(\frac{1}{\sum_{l \in L} \left|y_l\right|} \sum_{l \in L} \left|y_l\right| F_\beta(y_l, \hat{y}_l)\)

\(\langle P(y_l, \hat{y}_l) | l \in L \rangle\)

\(\langle R(y_l, \hat{y}_l) | l \in L \rangle\)

\(\langle F_\beta(y_l, \hat{y}_l) | l \in L \rangle\)

>>> from sklearn import metrics
>>> y_true = [0, 1, 2, 0, 1, 2]
>>> y_pred = [0, 2, 1, 0, 0, 1]
>>> metrics.precision_score(y_true, y_pred, average='macro')
0.22
>>> metrics.recall_score(y_true, y_pred, average='micro')
0.33
>>> metrics.f1_score(y_true, y_pred, average='weighted')
0.267
>>> metrics.fbeta_score(y_true, y_pred, average='macro', beta=0.5)
0.238
>>> metrics.precision_recall_fscore_support(y_true, y_pred, beta=0.5, average=None)
(array([0.667, 0., 0.]), array([1., 0., 0.]), array([0.714, 0., 0.]), array([2, 2, 2]))

对于带有“负类别”的多类别分类,可以排除某些标签

>>> metrics.recall_score(y_true, y_pred, labels=[1, 2], average='micro')
... # excluding 0, no labels were correctly recalled
0.0

同样,数据样本中不存在的标签也可以在宏观平均中考虑。

>>> metrics.precision_score(y_true, y_pred, labels=[0, 1, 2, 3], average='macro')
0.166

参考文献

3.4.4.10. Jaccard 相似系数分数#

jaccard_score 函数计算标签集对之间的 Jaccard 相似系数(也称为 Jaccard 指数)的平均值。

给定真实标签集 \(y\) 和预测标签集 \(\hat{y}\),Jaccard 相似系数定义为

\[J(y, \hat{y}) = \frac{|y \cap \hat{y}|}{|y \cup \hat{y}|}.\]

jaccard_score(类似于 precision_recall_fscore_support)原生适用于二元目标。通过逐集合计算,它可以通过使用 average 参数扩展到多标签和多类别(参见上文)。

在二分类情况下

>>> import numpy as np
>>> from sklearn.metrics import jaccard_score
>>> y_true = np.array([[0, 1, 1],
...                    [1, 1, 0]])
>>> y_pred = np.array([[1, 1, 1],
...                    [1, 0, 0]])
>>> jaccard_score(y_true[0], y_pred[0])
0.6666

在二维比较情况(例如图像相似度)下

>>> jaccard_score(y_true, y_pred, average="micro")
0.6

在带有二元标签指示符的多标签情况下:

>>> jaccard_score(y_true, y_pred, average='samples')
0.5833
>>> jaccard_score(y_true, y_pred, average='macro')
0.6666
>>> jaccard_score(y_true, y_pred, average=None)
array([0.5, 0.5, 1. ])

多类别问题被二值化并像相应的多标签问题一样处理

>>> y_pred = [0, 2, 1, 2]
>>> y_true = [0, 1, 2, 2]
>>> jaccard_score(y_true, y_pred, average=None)
array([1. , 0. , 0.33])
>>> jaccard_score(y_true, y_pred, average='macro')
0.44
>>> jaccard_score(y_true, y_pred, average='micro')
0.33

3.4.4.11. 合页损失(Hinge loss)#

hinge_loss 函数使用合页损失(hinge loss)计算模型和数据之间的平均距离,合页损失是一种只考虑预测错误的单侧度量。(合页损失用于支持向量机等最大间隔分类器。)

如果二分类任务中每个样本 \(i\) 的真实标签 \(y_i\) 被编码为 \(y_i=\left\{-1, +1\right\}\);并且 \(w_i\) 是相应的预测决策(由 decision_function 方法输出的形状为 (n_samples,) 的数组),那么合页损失定义为

\[L_\text{Hinge}(y, w) = \frac{1}{n_\text{samples}} \sum_{i=0}^{n_\text{samples}-1} \max\left\{1 - w_i y_i, 0\right\}\]

如果标签多于两个,hinge_loss 使用 Crammer & Singer 提出的多类别变体。这里是描述该变体的论文。

在这种情况下,预测决策是一个形状为 (n_samples, n_labels) 的数组。如果 \(w_{i, y_i}\) 是第 \(i\) 个样本真实标签 \(y_i\) 的预测决策;并且 \(\hat{w}_{i, y_i} = \max\left\{w_{i, y_j}~|~y_j \ne y_i \right\}\) 是所有其他标签的预测决策中的最大值,那么多类别合页损失定义为

\[L_\text{Hinge}(y, w) = \frac{1}{n_\text{samples}} \sum_{i=0}^{n_\text{samples}-1} \max\left\{1 + \hat{w}_{i, y_i} - w_{i, y_i}, 0\right\}\]

以下是一个小示例,演示了在二分类问题中将 hinge_loss 函数与 SVM 分类器一起使用的情况

>>> from sklearn import svm
>>> from sklearn.metrics import hinge_loss
>>> X = [[0], [1]]
>>> y = [-1, 1]
>>> est = svm.LinearSVC(random_state=0)
>>> est.fit(X, y)
LinearSVC(random_state=0)
>>> pred_decision = est.decision_function([[-2], [3], [0.5]])
>>> pred_decision
array([-2.18,  2.36,  0.09])
>>> hinge_loss([-1, 1, 1], pred_decision)
0.3

以下是一个示例,演示了在多类别问题中将 hinge_loss 函数与 SVM 分类器一起使用的情况

>>> X = np.array([[0], [1], [2], [3]])
>>> Y = np.array([0, 1, 2, 3])
>>> labels = np.array([0, 1, 2, 3])
>>> est = svm.LinearSVC()
>>> est.fit(X, Y)
LinearSVC()
>>> pred_decision = est.decision_function([[-1], [2], [3]])
>>> y_true = [0, 2, 3]
>>> hinge_loss(y_true, pred_decision, labels=labels)
0.56

3.4.4.12. 对数损失(Log loss)#

对数损失(Log loss),也称为逻辑回归损失或交叉熵损失,是基于概率估计定义的。它常用于(多项式)逻辑回归和神经网络,以及期望最大化的一些变体中,可用于评估分类器的概率输出(predict_proba),而非其离散预测。

对于真实标签 \(y \in \{0,1\}\) 和概率估计 \(\hat{p} \approx \operatorname{Pr}(y = 1)\) 的二分类问题,每个样本的对数损失是给定真实标签的分类器的负对数似然

\[L_{\log}(y, \hat{p}) = -\log \operatorname{Pr}(y|\hat{p}) = -(y \log (\hat{p}) + (1 - y) \log (1 - \hat{p}))\]

这可以扩展到多类别情况,如下所示。假设一组样本的真实标签编码为一个 1-of-K 二进制指示矩阵 \(Y\),即如果样本 \(i\) 具有从 \(K\) 个标签集中取出的标签 \(k\),则 \(y_{i,k} = 1\)。设 \(\hat{P}\) 是一个概率估计矩阵,其元素为 \(\hat{p}_{i,k} \approx \operatorname{Pr}(y_{i,k} = 1)\)。那么整个集合的对数损失是

\[L_{\log}(Y, \hat{P}) = -\log \operatorname{Pr}(Y|\hat{P}) = - \frac{1}{N} \sum_{i=0}^{N-1} \sum_{k=0}^{K-1} y_{i,k} \log \hat{p}_{i,k}\]

为了理解这如何泛化上述二元对数损失,请注意在二元情况下,\(\hat{p}_{i,0} = 1 - \hat{p}_{i,1}\)\(y_{i,0} = 1 - y_{i,1}\),因此将内部和对 \(y_{i,k} \in \{0,1\}\) 展开即可得到二元对数损失。

log_loss 函数根据真实标签列表和概率矩阵计算对数损失,该概率矩阵由估计器的 predict_proba 方法返回。

>>> from sklearn.metrics import log_loss
>>> y_true = [0, 0, 1, 1]
>>> y_pred = [[.9, .1], [.8, .2], [.3, .7], [.01, .99]]
>>> log_loss(y_true, y_pred)
0.1738

y_pred 中的第一个 [.9, .1] 表示第一个样本具有标签 0 的概率为 90%。对数损失是非负的。

3.4.4.13. Matthews 相关系数#

matthews_corrcoef 函数计算二分类的Matthews 相关系数(MCC)。引用维基百科的说法

“Matthews 相关系数在机器学习中用作衡量二元(两类)分类质量的指标。它考虑了真阳性、假阳性、真阴性和假阴性,通常被认为是一种平衡的度量,即使类别大小差异很大也可以使用。MCC 本质上是一个介于 -1 和 +1 之间的相关系数值。+1 的系数表示完美预测,0 表示平均随机预测,-1 表示反向预测。该统计量也称为 phi 系数。”

在二元(两类)情况下,\(tp\)\(tn\)\(fp\)\(fn\) 分别是真阳性、真阴性、假阳性和假阴性的数量,MCC 定义为

\[MCC = \frac{tp \times tn - fp \times fn}{\sqrt{(tp + fp)(tp + fn)(tn + fp)(tn + fn)}}.\]

在多类别情况下,Matthews 相关系数可以根据 \(K\) 个类别的 confusion_matrix \(C\)定义。为了简化定义,请考虑以下中间变量

  • \(t_k=\sum_{i}^{K} C_{ik}\) 是类别 \(k\) 真实出现的次数,

  • \(p_k=\sum_{i}^{K} C_{ki}\) 是类别 \(k\) 被预测的次数,

  • \(c=\sum_{k}^{K} C_{kk}\) 是正确预测的样本总数,

  • \(s=\sum_{i}^{K} \sum_{j}^{K} C_{ij}\) 是样本总数。

那么多类别 MCC 定义为

\[MCC = \frac{ c \times s - \sum_{k}^{K} p_k \times t_k }{\sqrt{ (s^2 - \sum_{k}^{K} p_k^2) \times (s^2 - \sum_{k}^{K} t_k^2) }}\]

当标签多于两个时,MCC 的值将不再介于 -1 和 +1 之间。相反,最小值将在 -1 和 0 之间,具体取决于真实标签的数量和分布。最大值始终为 +1。更多信息请参阅 [WikipediaMCC2021]

以下是一个小示例,说明了 matthews_corrcoef 函数的用法

>>> from sklearn.metrics import matthews_corrcoef
>>> y_true = [+1, +1, +1, -1]
>>> y_pred = [+1, -1, +1, +1]
>>> matthews_corrcoef(y_true, y_pred)
-0.33

参考文献

[WikipediaMCC2021]

维基百科贡献者。Phi 系数。维基百科,自由的百科全书。2021年4月21日,12:21 CEST。可访问:https://en.wikipedia.org/wiki/Phi_coefficient 访问日期:2021年4月21日。

3.4.4.14. 多标签混淆矩阵#

multilabel_confusion_matrix 函数计算类别维度(默认)或样本维度(samplewise=True)的多标签混淆矩阵,以评估分类的准确性。multilabel_confusion_matrix 也将多类别数据视为多标签数据进行处理,因为这是一种常用于使用二分类指标(如精确率、召回率等)评估多类别问题的转换。

当计算类别维度的多标签混淆矩阵 \(C\) 时,类别 \(i\) 的真阴性计数为 \(C_{i,0,0}\),假阴性为 \(C_{i,1,0}\),真阳性为 \(C_{i,1,1}\),假阳性为 \(C_{i,0,1}\)

以下是一个示例,演示了 multilabel_confusion_matrix 函数与多标签指示矩阵输入一起使用的情况

>>> import numpy as np
>>> from sklearn.metrics import multilabel_confusion_matrix
>>> y_true = np.array([[1, 0, 1],
...                    [0, 1, 0]])
>>> y_pred = np.array([[1, 0, 0],
...                    [0, 1, 1]])
>>> multilabel_confusion_matrix(y_true, y_pred)
array([[[1, 0],
        [0, 1]],

       [[1, 0],
        [0, 1]],

       [[0, 1],
        [1, 0]]])

或者可以为每个样本的标签构建一个混淆矩阵

>>> multilabel_confusion_matrix(y_true, y_pred, samplewise=True)
array([[[1, 0],
        [1, 1]],

       [[1, 1],
        [0, 1]]])

以下是一个示例,演示了 multilabel_confusion_matrix 函数与多类别输入一起使用的情况

>>> y_true = ["cat", "ant", "cat", "cat", "ant", "bird"]
>>> y_pred = ["ant", "ant", "cat", "cat", "ant", "cat"]
>>> multilabel_confusion_matrix(y_true, y_pred,
...                             labels=["ant", "bird", "cat"])
array([[[3, 1],
        [0, 2]],

       [[5, 0],
        [1, 0]],

       [[2, 1],
        [1, 2]]])

以下是一些示例,演示了如何使用 multilabel_confusion_matrix 函数,以多标签指示矩阵作为输入,计算每个类别的召回率(或敏感度)、特异性、误报率和漏报率。

计算每个类别的召回率(也称为真阳性率或敏感度)

>>> y_true = np.array([[0, 0, 1],
...                    [0, 1, 0],
...                    [1, 1, 0]])
>>> y_pred = np.array([[0, 1, 0],
...                    [0, 0, 1],
...                    [1, 1, 0]])
>>> mcm = multilabel_confusion_matrix(y_true, y_pred)
>>> tn = mcm[:, 0, 0]
>>> tp = mcm[:, 1, 1]
>>> fn = mcm[:, 1, 0]
>>> fp = mcm[:, 0, 1]
>>> tp / (tp + fn)
array([1. , 0.5, 0. ])

计算每个类别的特异性(也称为真阴性率)

>>> tn / (tn + fp)
array([1. , 0. , 0.5])

计算每个类别的误报率(也称为假阳性率)

>>> fp / (fp + tn)
array([0. , 1. , 0.5])

计算每个类别的漏报率(也称为假阴性率)

>>> fn / (fn + tp)
array([0. , 0.5, 1. ])

3.4.4.15. 受试者工作特征曲线(ROC)#

函数 roc_curve 计算受试者工作特征曲线,或称 ROC 曲线。引用维基百科的说法

“受试者工作特征(ROC),或简称 ROC 曲线,是一种图形化表示,它说明了二元分类系统在判别阈值变化时的性能。它通过绘制在不同阈值设置下,真阳性占阳性样本的比例(TPR = 真阳性率)与假阳性占阴性样本的比例(FPR = 假阳性率)来创建。TPR 也称为敏感度,FPR 是 1 减去特异性或真阴性率。”

此函数需要真实的二元值和目标分数,这些分数可以是正类别的概率估计、置信度值或二元决策。以下是如何使用 roc_curve 函数的一个小示例

>>> import numpy as np
>>> from sklearn.metrics import roc_curve
>>> y = np.array([1, 1, 2, 2])
>>> scores = np.array([0.1, 0.4, 0.35, 0.8])
>>> fpr, tpr, thresholds = roc_curve(y, scores, pos_label=2)
>>> fpr
array([0. , 0. , 0.5, 0.5, 1. ])
>>> tpr
array([0. , 0.5, 0.5, 1. , 1. ])
>>> thresholds
array([ inf, 0.8 , 0.4 , 0.35, 0.1 ])

与子集准确率、汉明损失或 F1 分数等指标相比,ROC 不需要为每个标签优化阈值。

roc_auc_score 函数(表示为 ROC-AUC 或 AUROC)计算 ROC 曲线下的面积。通过这种方式,曲线信息被概括为一个数字。

下图显示了分类器用于区分鸢尾花数据集中维吉尼亚鸢尾花与其他物种的 ROC 曲线和 ROC-AUC 分数

../_images/sphx_glr_plot_roc_001.png

有关更多信息,请参阅维基百科上关于 AUC 的文章

3.4.4.15.1. 二分类情况#

二分类情况中,您可以提供概率估计,使用 classifier.predict_proba() 方法,或者提供由 classifier.decision_function() 方法给出的未阈值化决策值。在提供概率估计的情况下,应提供“更大标签”类别的概率。“更大标签”对应于 classifier.classes_[1],因此是 classifier.predict_proba(X)[:, 1]。因此,y_score 参数的大小为 (n_samples, )。

>>> from sklearn.datasets import load_breast_cancer
>>> from sklearn.linear_model import LogisticRegression
>>> from sklearn.metrics import roc_auc_score
>>> X, y = load_breast_cancer(return_X_y=True)
>>> clf = LogisticRegression().fit(X, y)
>>> clf.classes_
array([0, 1])

我们可以使用对应于 clf.classes_[1] 的概率估计。

>>> y_score = clf.predict_proba(X)[:, 1]
>>> roc_auc_score(y, y_score)
0.99

或者,我们可以使用未阈值化的决策值

>>> roc_auc_score(y, clf.decision_function(X))
0.99

3.4.4.15.2. 多类别情况#

roc_auc_score 函数也可以用于多类别分类。目前支持两种平均策略:一对一(one-vs-one)算法计算成对 ROC AUC 分数的平均值,而一对多(one-vs-rest)算法计算每个类别与所有其他类别之间的 ROC AUC 分数的平均值。在这两种情况下,预测标签都以一个数组形式提供,其值从 0 到 n_classes,分数对应于样本属于特定类别的概率估计。OvO 和 OvR 算法支持均匀加权(average='macro')和按流行度加权(average='weighted')。

一对一算法#

计算所有可能的类别两两组合的平均 AUC。[HT2001] 定义了一个均匀加权的多类别 AUC 指标

\[\frac{1}{c(c-1)}\sum_{j=1}^{c}\sum_{k > j}^c (\text{AUC}(j | k) + \text{AUC}(k | j))\]

其中 \(c\) 是类别数量,\(\text{AUC}(j | k)\) 是将类别 \(j\) 作为正类别、类别 \(k\) 作为负类别时的 AUC。通常,在多类别情况下,\(\text{AUC}(j | k) \neq \text{AUC}(k | j))\)。通过将关键字参数 multiclass 设置为 'ovo' 并将 average 设置为 'macro' 来使用此算法。

[HT2001] 多类别 AUC 指标可以扩展为按流行度加权

\[\frac{1}{c(c-1)}\sum_{j=1}^{c}\sum_{k > j}^c p(j \cup k)( \text{AUC}(j | k) + \text{AUC}(k | j))\]

其中 \(c\) 是类别数量。通过将关键字参数 multiclass 设置为 'ovo' 并将 average 设置为 'weighted' 来使用此算法。'weighted' 选项返回按流行度加权的平均值,如 [FC2009] 中所述。

一对多算法#

计算每个类别与其余类别的 AUC [PD2000]。该算法在功能上与多标签情况相同。要启用此算法,请将关键字参数 multiclass 设置为 'ovr'。除了 'macro' [F2006]'weighted' [F2001] 平均之外,OvR 还支持 'micro' 平均。

在假阳性率不能容忍过高的应用中,可以使用 roc_auc_scoremax_fpr 参数来在给定限制内总结 ROC 曲线。

下图显示了分类器用于区分鸢尾花数据集中不同物种的微观平均 ROC 曲线及其对应的 ROC-AUC 分数

../_images/sphx_glr_plot_roc_002.png

3.4.4.15.3. 多标签情况#

多标签分类中,roc_auc_score 函数通过对标签进行平均来扩展,如上文所述。在这种情况下,您应该提供一个形状为 (n_samples, n_classes)y_score。因此,在使用概率估计时,需要为每个输出选择具有更大标签的类别的概率。

>>> from sklearn.datasets import make_multilabel_classification
>>> from sklearn.multioutput import MultiOutputClassifier
>>> X, y = make_multilabel_classification(random_state=0)
>>> inner_clf = LogisticRegression(random_state=0)
>>> clf = MultiOutputClassifier(inner_clf).fit(X, y)
>>> y_score = np.transpose([y_pred[:, 1] for y_pred in clf.predict_proba(X)])
>>> roc_auc_score(y, y_score, average=None)
array([0.828, 0.851, 0.94, 0.87, 0.95])

而决策值不需要这种处理。

>>> from sklearn.linear_model import RidgeClassifierCV
>>> clf = RidgeClassifierCV().fit(X, y)
>>> y_score = clf.decision_function(X)
>>> roc_auc_score(y, y_score, average=None)
array([0.82, 0.85, 0.93, 0.87, 0.94])

示例

参考文献

[HT2001] (1,2)

Hand, D.J. 和 Till, R.J. (2001)。《多类别分类问题中 ROC 曲线下面积的简单推广》。机器学习,45(2),第 171-186 页。

[FC2009]

Ferri, Cèsar & Hernandez-Orallo, Jose & Modroiu, R. (2009)。《分类性能度量的实验比较》。模式识别快报。30. 27-38页。

[PD2000]

Provost, F., Domingos, P. (2000)。《训练有素的PET:改进概率估计树》(第6.2节),CeDER工作论文 #IS-00-04,纽约大学斯特恩商学院。

[F2006]

Fawcett, T. (2006)。《ROC分析导论》。模式识别快报,27(8),第 861-874 页。

[F2001]

Fawcett, T. (2001)。《使用规则集最大化ROC性能》。载于《数据挖掘,2001年IEEE国际会议论文集》,第 131-138 页。

3.4.4.16. 检测错误权衡(DET)#

函数 det_curve 计算检测错误权衡曲线(DET)[WikipediaDET2017]。引用维基百科的说法

“检测错误权衡(DET)图是二元分类系统错误率的图形表示,绘制了虚假拒绝率与虚假接受率的关系。x 轴和 y 轴通过其标准正态偏差(或仅通过对数变换)进行非线性缩放,从而得到比 ROC 曲线更线性的权衡曲线,并利用大部分图像区域突出显示关键操作区域中的重要差异。”

DET 曲线是受试者工作特征(ROC)曲线的一种变体,其中 y 轴上绘制的是假阴性率而不是真阳性率。DET 曲线通常通过 \(\phi^{-1}\) 变换(其中 \(\phi\) 是累积分布函数)在正态偏差尺度上绘制。所得的性能曲线明确地可视化了给定分类算法的错误类型权衡。有关示例和进一步的动机,请参阅 [Martin1997]

该图比较了在相同分类任务中两个示例分类器的 ROC 曲线和 DET 曲线

../_images/sphx_glr_plot_det_001.png
性质#
  • 如果检测分数呈正态(或接近正态)分布,DET 曲线在正态偏差尺度上会形成一条线性曲线。[Navratil2007] 的研究表明,反之不一定成立,甚至更一般的分布也能产生线性 DET 曲线。

  • 正态偏差尺度变换会使点展开,从而占据相对更大的绘图空间。因此,在 DET 图上可能更容易区分具有相似分类性能的曲线。

  • 由于假阴性率与真阳性率“相反”,因此 DET 曲线的完美点是原点(与 ROC 曲线的左上角不同)。

应用和局限性#

DET 曲线易于理解,因此可以快速直观地评估分类器的性能。此外,DET 曲线可用于阈值分析和操作点选择。如果需要比较错误类型,这将特别有用。

另一方面,DET 曲线不将其度量作为单个数字提供。因此,对于自动化评估或与其他分类任务进行比较,ROC 曲线下的面积等衍生指标可能更适合。

示例

参考文献

[WikipediaDET2017]

维基百科贡献者。检测错误权衡。维基百科,自由的百科全书。2017年9月4日,23:33 UTC。可访问:https://en.wikipedia.org/w/index.php?title=Detection_error_tradeoff&oldid=798982054。访问日期:2018年2月19日。

[Martin1997]

A. Martin, G. Doddington, T. Kamm, M. Ordowski, 和 M. Przybocki,《DET 曲线在检测任务性能评估中的应用》,NIST 1997。

3.4.4.17. 零一损失#

zero_one_loss 函数计算 \(n_{\text{samples}}\) 上 0-1 分类损失(\(L_{0-1}\))的总和或平均值。默认情况下,该函数对样本进行归一化。要获取 \(L_{0-1}\) 的总和,请将 normalize 设置为 False

在多标签分类中,如果一个子集的标签与预测严格匹配,zero_one_loss 将其计为一,如果存在任何错误则计为零。默认情况下,该函数返回预测不完美的子集的百分比。要获取此类子集的计数,请将 normalize 设置为 False

如果 \(\hat{y}_i\) 是第 \(i\) 个样本的预测值,\(y_i\) 是对应的真实值,则 0-1 损失 \(L_{0-1}\) 定义为

\[L_{0-1}(y, \hat{y}) = \frac{1}{n_\text{samples}} \sum_{i=0}^{n_\text{samples}-1} 1(\hat{y}_i \not= y_i)\]

其中 \(1(x)\)指示函数。零一损失也可以计算为 \(\text{zero-one loss} = 1 - \text{accuracy}\)

>>> from sklearn.metrics import zero_one_loss
>>> y_pred = [1, 2, 3, 4]
>>> y_true = [2, 2, 3, 4]
>>> zero_one_loss(y_true, y_pred)
0.25
>>> zero_one_loss(y_true, y_pred, normalize=False)
1.0

在带有二进制标签指示符的多标签情况下,其中第一个标签集 [0,1] 存在错误

>>> zero_one_loss(np.array([[0, 1], [1, 1]]), np.ones((2, 2)))
0.5

>>> zero_one_loss(np.array([[0, 1], [1, 1]]), np.ones((2, 2)),  normalize=False)
1.0

示例

3.4.4.18. Brier 分数损失#

brier_score_loss 函数计算二分类和多类别概率预测的Brier 分数,它等价于均方误差。引用维基百科的说法

“Brier 分数是一种严格的适当评分规则,衡量概率预测的准确性。[...] [它]适用于预测必须将概率分配给一组互斥离散结果或类别的任务。”

设一组 \(N\) 个数据点的真实标签编码为一个 1-of-K 二进制指示矩阵 \(Y\),即如果样本 \(i\) 具有从 \(K\) 个标签集中取出的标签 \(k\),则 \(y_{i,k} = 1\)。设 \(\hat{P}\) 是一个概率估计矩阵,其元素为 \(\hat{p}_{i,k} \approx \operatorname{Pr}(y_{i,k} = 1)\)。根据 [Brier1950] 的原始定义,Brier 分数由下式给出

\[BS(Y, \hat{P}) = \frac{1}{N}\sum_{i=0}^{N-1}\sum_{k=0}^{K-1}(y_{i,k} - \hat{p}_{i,k})^{2}\]

Brier 分数位于 \([0, 2]\) 区间内,值越低表示概率估计越好(均方差越小)。实际上,Brier 分数是一种严格的适当评分规则,这意味着它只有在估计概率等于真实概率时才能达到最佳分数。

请注意,在二分类情况下,Brier 分数通常除以二,范围介于 \([0,1]\) 之间。对于二元目标 \(y_i \in {0, 1}\) 和正类别的概率估计 \(\hat{p}_i \approx \operatorname{Pr}(y_i = 1)\),Brier 分数等于

\[BS(y, \hat{p}) = \frac{1}{N} \sum_{i=0}^{N - 1}(y_i - \hat{p}_i)^2\]

brier_score_loss 函数根据真实标签和预测概率(由估计器的 predict_proba 方法返回)计算 Brier 分数。scale_by_half 参数控制遵循上述两种定义中的哪一种。

>>> import numpy as np
>>> from sklearn.metrics import brier_score_loss
>>> y_true = np.array([0, 1, 1, 0])
>>> y_true_categorical = np.array(["spam", "ham", "ham", "spam"])
>>> y_prob = np.array([0.1, 0.9, 0.8, 0.4])
>>> brier_score_loss(y_true, y_prob)
0.055
>>> brier_score_loss(y_true, 1 - y_prob, pos_label=0)
0.055
>>> brier_score_loss(y_true_categorical, y_prob, pos_label="ham")
0.055
>>> brier_score_loss(
...    ["eggs", "ham", "spam"],
...    [[0.8, 0.1, 0.1], [0.2, 0.7, 0.1], [0.2, 0.2, 0.6]],
...    labels=["eggs", "ham", "spam"],
... )
0.146

Brier 分数可用于评估分类器的校准程度。然而,较低的 Brier 分数损失并不总是意味着更好的校准。这是因为,类比于均方误差的偏差-方差分解,Brier 分数损失可以分解为校准损失和精炼损失之和 [Bella2012]。校准损失定义为从 ROC 分段斜率导出的经验概率的均方偏差。精炼损失可以定义为由最优成本曲线下面积衡量的预期最优损失。精炼损失可以独立于校准损失而变化,因此较低的 Brier 分数损失不一定意味着更好的校准模型。“只有当精炼损失保持不变时,较低的 Brier 分数损失才始终意味着更好的校准”[Bella2012][Flach2008]

示例

参考文献

[Brier1950]

G. Brier,《概率表示的预测验证》,月度天气评论 78.1 (1950)

[Bella2012] (1,2)

Bella, Ferri, Hernández-Orallo, and Ramírez-Quintana 《“机器学习模型的校准”》收录于 Khosrow-Pour, M.《机器学习:概念、方法、工具和应用》。赫尔希,宾夕法尼亚州:信息科学参考 (2012)。

[Flach2008]

Flach, Peter, 和 Edson Matsubara。《“关于分类、排序和概率估计。”》达格斯图尔研讨会论文集。达格斯图尔-莱布尼茨信息学中心 (2008)。

3.4.4.19. 类别似然比#

class_likelihood_ratios 函数计算二分类的正似然比和负似然比 \(LR_\pm\),这可以解释为检验后与检验前几率之比,如下文所述。因此,该指标对于类别流行度(正类别中的样本数除以总样本数)是不变的,并且无论是否存在类别不平衡,都可以在不同人群之间进行推断。

因此,\(LR_\pm\) 指标在以下情况下非常有用:用于学习和评估分类器的数据是类别近似平衡的研究人群(例如病例对照研究),而目标应用(即一般人群)的流行度非常低。

正似然比 \(LR_+\) 是分类器正确预测样本属于正类别的概率,除以预测负类别样本为正类别的概率

\[LR_+ = \frac{\text{PR}(P+|T+)}{\text{PR}(P+|T-)}.\]

这里的符号表示预测(\(P\))或真实(\(T\))标签,而符号 \(+\)\(-\) 分别指正类别和负类别,例如 \(P+\) 代表“预测为正”。

类似地,负似然比 \(LR_-\) 是正类别样本被分类为负类别的概率,除以负类别样本被正确分类的概率

\[LR_- = \frac{\text{PR}(P-|T+)}{\text{PR}(P-|T-)}.\]

对于高于偶然水平的分类器,\(LR_+\) 大于 1,值越高越好,而 \(LR_-\) 介于 0 到 1 之间,值越低越好。当 \(LR_\pm\approx 1\) 时,表示处于偶然水平。

请注意,概率与计数不同,例如 \(\operatorname{PR}(P+|T+)\) 不等于真阳性计数 tp(实际公式请参阅维基百科页面)。

示例

跨流行度变化的解释#

两种类别似然比都可以用几率比(检验前和检验后)来解释

\[\text{post-test odds} = \text{Likelihood ratio} \times \text{pre-test odds}.\]

几率通常与概率通过以下方式相关联

\[\text{odds} = \frac{\text{probability}}{1 - \text{probability}},\]

或者等效地

\[\text{probability} = \frac{\text{odds}}{1 + \text{odds}}.\]

在给定人群中,检验前概率由流行度给出。通过将几率转换为概率,似然比可以转换为分类器预测前后真正属于任一类别的概率

\[\text{post-test odds} = \text{Likelihood ratio} \times \frac{\text{pre-test probability}}{1 - \text{pre-test probability}},\]
\[\text{post-test probability} = \frac{\text{post-test odds}}{1 + \text{post-test odds}}.\]
数学发散性#

\(fp=0\) 时,正似然比(LR+)未定义,这意味着分类器没有将任何负标签错误分类为正。这种情况可能表示对所有负案例的完美识别,或者,如果也没有真阳性预测(\(tp=0\)),则表示分类器根本不预测正类别。在第一种情况下,LR+ 可以解释为 np.inf,在第二种情况(例如,对于高度不平衡的数据)下,可以解释为 np.nan

\(tn=0\) 时,负似然比(LR-)未定义。这种发散是无效的,因为 \(LR_- > 1.0\) 将表示样本在被分类为负后属于正类别的几率增加,就好像分类行为导致了正类别条件一样。这包括 DummyClassifier 始终预测正类别的情况(即当 \(tn=fn=0\) 时)。

\(tp=fn=0\) 时,两个类别似然比(LR+ LR-)均未定义,这意味着测试集中不存在任何正类别样本。这可能发生在对高度不平衡数据进行交叉验证时,也会导致除以零。

如果发生除以零的情况,并且 raise_warning 设置为 True(默认),class_likelihood_ratios 会引发 UndefinedMetricWarning,并默认返回 np.nan,以避免在交叉验证折叠上进行平均时产生污染。用户可以使用 replace_undefined_by 参数设置除以零情况下的返回值。

有关 class_likelihood_ratios 函数的详细演示,请参阅下面的示例。

参考文献#

3.4.4.20. 分类的 D² 分数#

D² 分数计算解释的偏差分数。它是 R² 的推广,其中平方误差被推广并替换为选择的分类偏差 \(\text{dev}(y, \hat{y})\)(例如,对数损失)。D² 是一种技能分数(skill score)。其计算公式为

\[D^2(y, \hat{y}) = 1 - \frac{\text{dev}(y, \hat{y})}{\text{dev}(y, y_{\text{null}})} \,.\]

其中 \(y_{\text{null}}\) 是一个仅包含截距的模型的最佳预测(例如,在对数损失的情况下,y_true 的每类别比例)。

与 R² 类似,最佳分数是 1.0,并且它也可以是负数(因为模型可能任意糟糕)。一个总是预测 \(y_{\text{null}}\) 而不考虑输入特征的常数模型,其 D² 分数将是 0.0。

D2 对数损失分数#

d2_log_loss_score 函数实现了带有对数损失的 D² 的特殊情况,参见 对数损失,即。

\[\text{dev}(y, \hat{y}) = \text{log_loss}(y, \hat{y}).\]

以下是 d2_log_loss_score 函数的一些使用示例

>>> from sklearn.metrics import d2_log_loss_score
>>> y_true = [1, 1, 2, 3]
>>> y_pred = [
...    [0.5, 0.25, 0.25],
...    [0.5, 0.25, 0.25],
...    [0.5, 0.25, 0.25],
...    [0.5, 0.25, 0.25],
... ]
>>> d2_log_loss_score(y_true, y_pred)
0.0
>>> y_true = [1, 2, 3]
>>> y_pred = [
...     [0.98, 0.01, 0.01],
...     [0.01, 0.98, 0.01],
...     [0.01, 0.01, 0.98],
... ]
>>> d2_log_loss_score(y_true, y_pred)
0.981
>>> y_true = [1, 2, 3]
>>> y_pred = [
...     [0.1, 0.6, 0.3],
...     [0.1, 0.6, 0.3],
...     [0.4, 0.5, 0.1],
... ]
>>> d2_log_loss_score(y_true, y_pred)
-0.552

3.4.5. 多标签排序指标#

在多标签学习中,每个样本可以关联任意数量的真实标签。目标是为真实标签提供高分数和更好的排名。

3.4.5.1. 覆盖误差#

coverage_error 函数计算最终预测中必须包含的标签的平均数量,以便预测所有真实标签。如果您想知道平均需要预测多少个得分最高的标签而不错过任何一个真实标签,这会很有用。因此,此指标的最佳值是真实标签的平均数量。

注意

我们实现的得分比 Tsoumakas 等人(2010)给出的得分高 1。这扩展了它以处理实例具有 0 个真实标签的退化情况。

形式上,给定真实标签的二元指示矩阵 \(y \in \left\{0, 1\right\}^{n_\text{samples} \times n_\text{labels}}\) 和与每个标签关联的分数 \(\hat{f} \in \mathbb{R}^{n_\text{samples} \times n_\text{labels}}\),覆盖率定义为

\[coverage(y, \hat{f}) = \frac{1}{n_{\text{samples}}} \sum_{i=0}^{n_{\text{samples}} - 1} \max_{j:y_{ij} = 1} \text{rank}_{ij}\]

其中 \(\text{rank}_{ij} = \left|\left\{k: \hat{f}_{ik} \geq \hat{f}_{ij} \right\}\right|\)。根据排名定义,y_scores 中的并列情况通过赋予所有并列值本应分配到的最大排名来打破。

以下是此函数的一个小使用示例

>>> import numpy as np
>>> from sklearn.metrics import coverage_error
>>> y_true = np.array([[1, 0, 0], [0, 0, 1]])
>>> y_score = np.array([[0.75, 0.5, 1], [1, 0.2, 0.1]])
>>> coverage_error(y_true, y_score)
2.5

3.4.5.2. 标签排序平均精度#

label_ranking_average_precision_score 函数实现了标签排序平均精度(LRAP)。此指标与 average_precision_score 函数相关联,但基于标签排序而非精确率和召回率的概念。

标签排序平均精度(LRAP)在样本上平均以下问题的答案:对于每个真实标签,有多少比例的更高排名的标签是真实标签?如果您能为每个样本关联的标签赋予更好的排名,此性能度量将更高。所得分数始终严格大于 0,最佳值为 1。如果每个样本只有一个相关标签,则标签排序平均精度等同于 平均倒数排名

形式上,给定真实标签的二元指示矩阵 \(y \in \left\{0, 1\right\}^{n_\text{samples} \times n_\text{labels}}\) 和与每个标签关联的分数 \(\hat{f} \in \mathbb{R}^{n_\text{samples} \times n_\text{labels}}\),平均精度定义为

\[LRAP(y, \hat{f}) = \frac{1}{n_{\text{samples}}} \sum_{i=0}^{n_{\text{samples}} - 1} \frac{1}{||y_i||_0} \sum_{j:y_{ij} = 1} \frac{|\mathcal{L}_{ij}|}{\text{rank}_{ij}}\]

其中 \(\mathcal{L}_{ij} = \left\{k: y_{ik} = 1, \hat{f}_{ik} \geq \hat{f}_{ij} \right\}\)\(\text{rank}_{ij} = \left|\left\{k: \hat{f}_{ik} \geq \hat{f}_{ij} \right\}\right|\)\(|\cdot|\) 计算集合的基数(即集合中元素的数量),而 \(||\cdot||_0\)\(\ell_0\) “范数”(它计算向量中非零元素的数量)。

以下是此函数的一个小使用示例

>>> import numpy as np
>>> from sklearn.metrics import label_ranking_average_precision_score
>>> y_true = np.array([[1, 0, 0], [0, 0, 1]])
>>> y_score = np.array([[0.75, 0.5, 1], [1, 0.2, 0.1]])
>>> label_ranking_average_precision_score(y_true, y_score)
0.416

3.4.5.3. 排序损失#

label_ranking_loss 函数计算排序损失,它在样本上平均错误排序的标签对数量,即真实标签的分数低于错误标签,并按错误标签和真实标签的有序对数量的倒数加权。可实现的最低排序损失为零。

形式上,给定真实标签的二元指示矩阵 \(y \in \left\{0, 1\right\}^{n_\text{samples} \times n_\text{labels}}\) 和与每个标签关联的分数 \(\hat{f} \in \mathbb{R}^{n_\text{samples} \times n_\text{labels}}\),排序损失定义为

\[ranking\_loss(y, \hat{f}) = \frac{1}{n_{\text{samples}}} \sum_{i=0}^{n_{\text{samples}} - 1} \frac{1}{||y_i||_0(n_\text{labels} - ||y_i||_0)} \left|\left\{(k, l): \hat{f}_{ik} \leq \hat{f}_{il}, y_{ik} = 1, y_{il} = 0 \right\}\right|\]

其中 \(|\cdot|\) 计算集合的基数(即集合中元素的数量),而 \(||\cdot||_0\)\(\ell_0\) “范数”(它计算向量中非零元素的数量)。

以下是此函数的一个小使用示例

>>> import numpy as np
>>> from sklearn.metrics import label_ranking_loss
>>> y_true = np.array([[1, 0, 0], [0, 0, 1]])
>>> y_score = np.array([[0.75, 0.5, 1], [1, 0.2, 0.1]])
>>> label_ranking_loss(y_true, y_score)
0.75
>>> # With the following prediction, we have perfect and minimal loss
>>> y_score = np.array([[1.0, 0.1, 0.2], [0.1, 0.2, 0.9]])
>>> label_ranking_loss(y_true, y_score)
0.0
参考文献#
  • Tsoumakas, G., Katakis, I., & Vlahavas, I. (2010). Mining multi-label data. In Data mining and knowledge discovery handbook (pp. 667-685). Springer US.

3.4.5.4. 归一化折损累积增益#

折损累积增益(DCG)和归一化折损累积增益(NDCG)是 dcg_scorendcg_score 中实现的排序指标;它们将预测顺序与真实分数(例如查询答案的相关性)进行比较。

摘自维基百科关于折损累积增益的页面

“折损累积增益(DCG)是衡量排序质量的一种指标。在信息检索中,它常用于衡量网络搜索引擎算法或相关应用的有效性。通过在搜索引擎结果集中使用文档的分级相关性度量,DCG 根据文档在结果列表中的位置来衡量其有用性或增益。增益从结果列表的顶部累积到底部,每个结果在较低排名处的增益都会打折。”

DCG 按照预测顺序对真实目标(例如查询答案的相关性)进行排序,然后将其乘以对数衰减并求和。该和可以在前 \(K\) 个结果之后截断,这种情况下我们称之为 DCG@K。NDCG,或 NDCG@K 是 DCG 除以完美预测获得的 DCG,因此它始终介于 0 和 1 之间。通常,NDCG 比 DCG 更受欢迎。

与排序损失相比,NDCG 可以考虑相关性分数,而不是真实排名。因此,如果真实数据仅包含一个排序,则应优先选择排序损失;如果真实数据包含实际的有用性分数(例如,0 表示不相关,1 表示相关,2 表示非常相关),则可以使用 NDCG。

对于一个样本,给定每个目标的连续真实值向量 \(y \in \mathbb{R}^{M}\)(其中 \(M\) 是输出数量)以及预测值 \(\hat{y}\)(其导出排序函数 \(f\)),DCG 分数是

\[\sum_{r=1}^{\min(K, M)}\frac{y_{f(r)}}{\log(1 + r)}\]

NDCG 分数是 DCG 分数除以 \(y\) 获得的 DCG 分数。

参考文献#
  • 折损累积增益的维基百科条目

  • Jarvelin, K., & Kekalainen, J. (2002). Cumulated gain-based evaluation of IR techniques. ACM Transactions on Information Systems (TOIS), 20(4), 422-446.

  • Wang, Y., Wang, L., Li, Y., He, D., Chen, W., & Liu, T. Y. (2013, May). A theoretical analysis of NDCG ranking measures. In Proceedings of the 26th Annual Conference on Learning Theory (COLT 2013)

  • McSherry, F., & Najork, M. (2008, March). Computing information retrieval performance measures efficiently in the presence of tied scores. In European conference on information retrieval (pp. 414-421). Springer, Berlin, Heidelberg.

3.4.6. 回归指标#

The sklearn.metrics 模块实现了多种损失、分数和效用函数来衡量回归性能。其中一些已增强以处理多输出情况,包括:mean_squared_errormean_absolute_errorr2_scoreexplained_variance_scoremean_pinball_lossd2_pinball_scored2_absolute_error_score

这些函数都有一个 multioutput 关键字参数,它指定了每个单独目标的得分或损失应该如何平均。默认值是 'uniform_average',它指定了输出的均匀加权平均。如果传入一个形状为 (n_outputs,)ndarray,则其条目被解释为权重,并返回相应的加权平均值。如果 multioutput'raw_values',则所有未更改的单个得分或损失将以形状为 (n_outputs,) 的数组形式返回。

The r2_scoreexplained_variance_score 对于 multioutput 参数接受额外的值 'variance_weighted'。此选项会导致每个独立得分按相应目标变量的方差进行加权。此设置量化了全局捕获的未缩放方差。如果目标变量的尺度不同,则此得分更重视解释方差较大的变量。

3.4.6.1. R² 分数,决定系数#

The r2_score 函数计算 决定系数,通常表示为 \(R^2\)

它表示模型中独立变量解释的方差(y 的)比例。它提供了拟合优度的一个指示,因此也衡量了模型通过解释方差的比例来预测未见样本的可能性。

由于方差是数据集依赖的,\(R^2\) 在不同数据集之间可能无法进行有意义的比较。最佳分数是 1.0,它也可以是负数(因为模型可能任意糟糕)。一个总是预测 y 的期望(平均)值而不考虑输入特征的常数模型,其 \(R^2\) 分数将是 0.0。

注意:当预测残差的均值为零时,\(R^2\) 分数与 解释方差分数 相同。

如果 \(\hat{y}_i\) 是第 \(i\) 个样本的预测值,\(y_i\) 是总共 \(n\) 个样本的对应真实值,则估计的 \(R^2\) 定义为

\[R^2(y, \hat{y}) = 1 - \frac{\sum_{i=1}^{n} (y_i - \hat{y}_i)^2}{\sum_{i=1}^{n} (y_i - \bar{y})^2}\]

其中 \(\bar{y} = \frac{1}{n} \sum_{i=1}^{n} y_i\)\(\sum_{i=1}^{n} (y_i - \hat{y}_i)^2 = \sum_{i=1}^{n} \epsilon_i^2\)

请注意,r2_score 计算的是未经调整的 \(R^2\),未对 y 的样本方差中的偏差进行校正。

在真实目标为常数这种特殊情况下,\(R^2\) 分数不是有限的:它要么是 NaN(完美预测),要么是 -Inf(不完美预测)。这种非有限分数可能会阻止正确执行模型优化,例如网格搜索交叉验证。因此,r2_score 的默认行为是将它们替换为 1.0(完美预测)或 0.0(不完美预测)。如果将 force_finite 设置为 False,则此分数将回退到原始 \(R^2\) 定义。

以下是 r2_score 函数的一个小使用示例

>>> from sklearn.metrics import r2_score
>>> y_true = [3, -0.5, 2, 7]
>>> y_pred = [2.5, 0.0, 2, 8]
>>> r2_score(y_true, y_pred)
0.948
>>> y_true = [[0.5, 1], [-1, 1], [7, -6]]
>>> y_pred = [[0, 2], [-1, 2], [8, -5]]
>>> r2_score(y_true, y_pred, multioutput='variance_weighted')
0.938
>>> y_true = [[0.5, 1], [-1, 1], [7, -6]]
>>> y_pred = [[0, 2], [-1, 2], [8, -5]]
>>> r2_score(y_true, y_pred, multioutput='uniform_average')
0.936
>>> r2_score(y_true, y_pred, multioutput='raw_values')
array([0.965, 0.908])
>>> r2_score(y_true, y_pred, multioutput=[0.3, 0.7])
0.925
>>> y_true = [-2, -2, -2]
>>> y_pred = [-2, -2, -2]
>>> r2_score(y_true, y_pred)
1.0
>>> r2_score(y_true, y_pred, force_finite=False)
nan
>>> y_true = [-2, -2, -2]
>>> y_pred = [-2, -2, -2 + 1e-8]
>>> r2_score(y_true, y_pred)
0.0
>>> r2_score(y_true, y_pred, force_finite=False)
-inf

示例

3.4.6.2. 平均绝对误差#

mean_absolute_error 函数计算 平均绝对误差,这是一种风险指标,对应于绝对误差损失或 \(l1\)-范数损失的期望值。

如果 \(\hat{y}_i\) 是第 \(i\) 个样本的预测值,\(y_i\) 是对应的真实值,则在 \(n_{\text{samples}}\) 个样本上估计的平均绝对误差 (MAE) 定义为

\[\text{MAE}(y, \hat{y}) = \frac{1}{n_{\text{samples}}} \sum_{i=0}^{n_{\text{samples}}-1} \left| y_i - \hat{y}_i \right|.\]

以下是 mean_absolute_error 函数的一个小使用示例

>>> from sklearn.metrics import mean_absolute_error
>>> y_true = [3, -0.5, 2, 7]
>>> y_pred = [2.5, 0.0, 2, 8]
>>> mean_absolute_error(y_true, y_pred)
0.5
>>> y_true = [[0.5, 1], [-1, 1], [7, -6]]
>>> y_pred = [[0, 2], [-1, 2], [8, -5]]
>>> mean_absolute_error(y_true, y_pred)
0.75
>>> mean_absolute_error(y_true, y_pred, multioutput='raw_values')
array([0.5, 1. ])
>>> mean_absolute_error(y_true, y_pred, multioutput=[0.3, 0.7])
0.85

3.4.6.3. 均方误差#

mean_squared_error 函数计算 均方误差,这是一种风险指标,对应于平方(二次)误差或损失的期望值。

如果 \(\hat{y}_i\) 是第 \(i\) 个样本的预测值,\(y_i\) 是对应的真实值,则在 \(n_{\text{samples}}\) 个样本上估计的均方误差(MSE)定义为

\[\text{MSE}(y, \hat{y}) = \frac{1}{n_\text{samples}} \sum_{i=0}^{n_\text{samples} - 1} (y_i - \hat{y}_i)^2.\]

这里是 mean_squared_error 函数的一个小例子

>>> from sklearn.metrics import mean_squared_error
>>> y_true = [3, -0.5, 2, 7]
>>> y_pred = [2.5, 0.0, 2, 8]
>>> mean_squared_error(y_true, y_pred)
0.375
>>> y_true = [[0.5, 1], [-1, 1], [7, -6]]
>>> y_pred = [[0, 2], [-1, 2], [8, -5]]
>>> mean_squared_error(y_true, y_pred)
0.7083

示例

  • 有关均方误差用法评估梯度提升回归的示例,请参阅 梯度提升回归

将 MSE 的平方根称为均方根误差(RMSE),这是另一种常用指标,其提供的度量单位与目标变量相同。RMSE 可通过 root_mean_squared_error 函数获得。

3.4.6.4. 均方对数误差#

mean_squared_log_error 函数计算一个风险指标,对应于平方对数(二次)误差或损失的期望值。

如果 \(\hat{y}_i\) 是第 \(i\) 个样本的预测值,\(y_i\) 是对应的真实值,则在 \(n_{\text{samples}}\) 个样本上估计的均方对数误差(MSLE)定义为

\[\text{MSLE}(y, \hat{y}) = \frac{1}{n_\text{samples}} \sum_{i=0}^{n_\text{samples} - 1} (\log_e (1 + y_i) - \log_e (1 + \hat{y}_i) )^2.\]

其中 \(\log_e (x)\) 表示 \(x\) 的自然对数。当目标具有指数增长时,例如人口数量、商品多年来的平均销售额等,最适合使用此指标。请注意,此指标对预测不足的估计的惩罚大于对预测过度的估计的惩罚。

这里是 mean_squared_log_error 函数的一个小例子

>>> from sklearn.metrics import mean_squared_log_error
>>> y_true = [3, 5, 2.5, 7]
>>> y_pred = [2.5, 5, 4, 8]
>>> mean_squared_log_error(y_true, y_pred)
0.0397
>>> y_true = [[0.5, 1], [1, 2], [7, 6]]
>>> y_pred = [[0.5, 2], [1, 2.5], [8, 8]]
>>> mean_squared_log_error(y_true, y_pred)
0.044

均方根对数误差(RMSLE)可通过 root_mean_squared_log_error 函数获得。

3.4.6.5. 平均绝对百分比误差#

mean_absolute_percentage_error (MAPE),也称为平均绝对百分比偏差(MAPD),是回归问题的评估指标。此指标的目的是对相对误差敏感。例如,它不会因目标变量的全局缩放而改变。

如果 \(\hat{y}_i\) 是第 \(i\) 个样本的预测值,\(y_i\) 是对应的真实值,则在 \(n_{\text{samples}}\) 个样本上估计的平均绝对百分比误差(MAPE)定义为

\[\text{MAPE}(y, \hat{y}) = \frac{1}{n_{\text{samples}}} \sum_{i=0}^{n_{\text{samples}}-1} \frac{{}\left| y_i - \hat{y}_i \right|}{\max(\epsilon, \left| y_i \right|)}\]

其中 \(\epsilon\) 是一个任意小但严格正的数,以避免当 y 为零时出现未定义的结果。

mean_absolute_percentage_error 函数支持多输出。

这里是 mean_absolute_percentage_error 函数的一个小例子

>>> from sklearn.metrics import mean_absolute_percentage_error
>>> y_true = [1, 10, 1e6]
>>> y_pred = [0.9, 15, 1.2e6]
>>> mean_absolute_percentage_error(y_true, y_pred)
0.2666

在上面的示例中,如果我们使用了 mean_absolute_error,它会忽略小幅值,只反映最大幅值预测中的误差。但对于 MAPE,这个问题得到了解决,因为它计算的是相对于实际输出的相对百分比误差。

注意

这里的 MAPE 公式不代表常见的“百分比”定义:范围 [0, 100] 中的百分比通过除以 100 转换为范围 [0, 1] 中的相对值。因此,200% 的误差对应于相对误差 2。这里的动机是使值范围与 scikit-learn 中的其他误差指标(例如 accuracy_score)更一致。

要获得维基百科公式中的平均绝对百分比误差,请将此处计算的 mean_absolute_percentage_error 乘以 100。

参考文献#

3.4.6.6. 中位数绝对误差#

median_absolute_error 特别有趣,因为它对异常值具有鲁棒性。损失是通过计算目标和预测之间所有绝对差值的中位数来计算的。

如果 \(\hat{y}_i\) 是第 \(i\) 个样本的预测值,\(y_i\) 是对应的真实值,则在 \(n_{\text{samples}}\) 个样本上估计的中位数绝对误差(MedAE)定义为

\[\text{MedAE}(y, \hat{y}) = \text{median}(\mid y_1 - \hat{y}_1 \mid, \ldots, \mid y_n - \hat{y}_n \mid).\]

median_absolute_error 不支持多输出。

这里是 median_absolute_error 函数的一个小例子

>>> from sklearn.metrics import median_absolute_error
>>> y_true = [3, -0.5, 2, 7]
>>> y_pred = [2.5, 0.0, 2, 8]
>>> median_absolute_error(y_true, y_pred)
0.5

3.4.6.7. 最大误差#

max_error 函数计算最大残差误差,这是一个捕捉预测值与真实值之间最坏情况误差的指标。在一个完美拟合的单输出回归模型中,max_error 在训练集上将是 0,尽管这在现实世界中极不可能发生,但此指标显示了模型在拟合时所产生的误差程度。

如果 \(\hat{y}_i\) 是第 \(i\) 个样本的预测值,\(y_i\) 是对应的真实值,则最大误差定义为

\[\text{Max Error}(y, \hat{y}) = \max(| y_i - \hat{y}_i |)\]

这里是 max_error 函数的一个小例子

>>> from sklearn.metrics import max_error
>>> y_true = [3, 2, 7, 1]
>>> y_pred = [9, 2, 7, 1]
>>> max_error(y_true, y_pred)
6.0

max_error 不支持多输出。

3.4.6.8. 解释方差分数#

explained_variance_score 计算解释方差回归分数

如果 \(\hat{y}\) 是估计的目标输出,\(y\) 是对应的(正确)目标输出,而 \(Var\)方差(标准差的平方),则解释方差估计如下

\[explained\_{}variance(y, \hat{y}) = 1 - \frac{Var\{ y - \hat{y}\}}{Var\{y\}}\]

可能获得的最佳分数为 1.0,值越低越差。

在真实目标为常数这种特殊情况下,解释方差分数不是有限的:它要么是 NaN(完美预测),要么是 -Inf(不完美预测)。这种非有限分数可能会阻碍正确的模型优化,例如网格搜索交叉验证的正确执行。因此,explained_variance_score 的默认行为是将其替换为 1.0(完美预测)或 0.0(不完美预测)。您可以将 force_finite 参数设置为 False 以防止此修正发生,并回退到原始的解释方差分数。

这里是 explained_variance_score 函数的一个小例子

>>> from sklearn.metrics import explained_variance_score
>>> y_true = [3, -0.5, 2, 7]
>>> y_pred = [2.5, 0.0, 2, 8]
>>> explained_variance_score(y_true, y_pred)
0.957
>>> y_true = [[0.5, 1], [-1, 1], [7, -6]]
>>> y_pred = [[0, 2], [-1, 2], [8, -5]]
>>> explained_variance_score(y_true, y_pred, multioutput='raw_values')
array([0.967, 1.        ])
>>> explained_variance_score(y_true, y_pred, multioutput=[0.3, 0.7])
0.990
>>> y_true = [-2, -2, -2]
>>> y_pred = [-2, -2, -2]
>>> explained_variance_score(y_true, y_pred)
1.0
>>> explained_variance_score(y_true, y_pred, force_finite=False)
nan
>>> y_true = [-2, -2, -2]
>>> y_pred = [-2, -2, -2 + 1e-8]
>>> explained_variance_score(y_true, y_pred)
0.0
>>> explained_variance_score(y_true, y_pred, force_finite=False)
-inf

3.4.6.9. 平均泊松、伽马和Tweedie偏差#

mean_tweedie_deviance 函数计算具有 power 参数 (\(p\)) 的平均 Tweedie 偏差误差。这是一个用来推断回归目标预测期望值的指标。

存在以下特殊情况:

如果 \(\hat{y}_i\) 是第 \(i\) 个样本的预测值,\(y_i\) 是对应的真实值,则在 \(n_{\text{samples}}\) 个样本上估计的功率 \(p\) 的平均 Tweedie 偏差误差 (D) 定义为

\[\begin{split}\text{D}(y, \hat{y}) = \frac{1}{n_\text{samples}} \sum_{i=0}^{n_\text{samples} - 1} \begin{cases} (y_i-\hat{y}_i)^2, & \text{for }p=0\text{ (Normal)}\\ 2(y_i \log(y_i/\hat{y}_i) + \hat{y}_i - y_i), & \text{for }p=1\text{ (Poisson)}\\ 2(\log(\hat{y}_i/y_i) + y_i/\hat{y}_i - 1), & \text{for }p=2\text{ (Gamma)}\\ 2\left(\frac{\max(y_i,0)^{2-p}}{(1-p)(2-p)}- \frac{y_i\,\hat{y}_i^{1-p}}{1-p}+\frac{\hat{y}_i^{2-p}}{2-p}\right), & \text{otherwise} \end{cases}\end{split}\]

Tweedie 偏差是一个 2-power 次的齐次函数。因此,power=2 的伽马分布意味着同时缩放 y_truey_pred 对偏差没有影响。对于泊松分布(power=1),偏差呈线性缩放;对于正态分布(power=0),偏差呈二次方缩放。通常,power 越高,真实目标和预测目标之间的极端偏差所占的权重就越小。

例如,让我们比较两个预测值 1.5 和 150,它们都比其对应的真实值大 50%。

均方误差(power=0)对第二个点的预测差异非常敏感,

>>> from sklearn.metrics import mean_tweedie_deviance
>>> mean_tweedie_deviance([1.0], [1.5], power=0)
0.25
>>> mean_tweedie_deviance([100.], [150.], power=0)
2500.0

如果我们将 power 增加到 1,

>>> mean_tweedie_deviance([1.0], [1.5], power=1)
0.189
>>> mean_tweedie_deviance([100.], [150.], power=1)
18.9

误差差异减小。最后,通过设置 power=2

>>> mean_tweedie_deviance([1.0], [1.5], power=2)
0.144
>>> mean_tweedie_deviance([100.], [150.], power=2)
0.144

我们将得到相同的误差。因此,当 power=2 时,偏差仅对相对误差敏感。

3.4.6.10. 弹子损失#

mean_pinball_loss 函数用于评估分位数回归模型的预测性能。

\[\text{pinball}(y, \hat{y}) = \frac{1}{n_{\text{samples}}} \sum_{i=0}^{n_{\text{samples}}-1} \alpha \max(y_i - \hat{y}_i, 0) + (1 - \alpha) \max(\hat{y}_i - y_i, 0)\]

当分位数参数 alpha 设置为 0.5 时,弹子损失的值相当于 mean_absolute_error 的一半。

这里是 mean_pinball_loss 函数的一个小例子

>>> from sklearn.metrics import mean_pinball_loss
>>> y_true = [1, 2, 3]
>>> mean_pinball_loss(y_true, [0, 2, 3], alpha=0.1)
0.033
>>> mean_pinball_loss(y_true, [1, 2, 4], alpha=0.1)
0.3
>>> mean_pinball_loss(y_true, [0, 2, 3], alpha=0.9)
0.3
>>> mean_pinball_loss(y_true, [1, 2, 4], alpha=0.9)
0.033
>>> mean_pinball_loss(y_true, y_true, alpha=0.1)
0.0
>>> mean_pinball_loss(y_true, y_true, alpha=0.9)
0.0

可以使用特定选择的 alpha 来构建评分器对象

>>> from sklearn.metrics import make_scorer
>>> mean_pinball_loss_95p = make_scorer(mean_pinball_loss, alpha=0.95)

这样的评分器可用于通过交叉验证来评估分位数回归器的泛化性能

>>> from sklearn.datasets import make_regression
>>> from sklearn.model_selection import cross_val_score
>>> from sklearn.ensemble import GradientBoostingRegressor
>>>
>>> X, y = make_regression(n_samples=100, random_state=0)
>>> estimator = GradientBoostingRegressor(
...     loss="quantile",
...     alpha=0.95,
...     random_state=0,
... )
>>> cross_val_score(estimator, X, y, cv=5, scoring=mean_pinball_loss_95p)
array([13.6, 9.7, 23.3, 9.5, 10.4])

也可以构建用于超参数调优的评分器对象。损失的符号必须切换,以确保“越大越好”,如下面链接的示例中所述。

示例

  • 有关使用弹子损失评估和调整具有非对称噪声和异常值数据的分位数回归模型超参数的示例,请参阅 梯度提升回归的预测区间

3.4.6.11. D² 分数#

D² 分数计算解释的偏差比例。它是 R² 的推广,其中平方误差被泛化并替换为所选的偏差 \(\text{dev}(y, \hat{y})\)(例如,Tweedie、弹子或平均绝对误差)。D² 是一种技能分数。其计算方法如下:

\[D^2(y, \hat{y}) = 1 - \frac{\text{dev}(y, \hat{y})}{\text{dev}(y, y_{\text{null}})} \,.\]

其中 \(y_{\text{null}}\) 是仅包含截距的模型的最佳预测(例如,对于 Tweedie 情况是 y_true 的均值,对于绝对误差是中位数,对于弹子损失是 alpha 分位数)。

与 R² 类似,最佳分数是 1.0,并且它也可以是负数(因为模型可能任意糟糕)。一个总是预测 \(y_{\text{null}}\) 而不考虑输入特征的常数模型,其 D² 分数将是 0.0。

D² Tweedie 分数#

d2_tweedie_score 函数实现了 D² 的特殊情况,其中 \(\text{dev}(y, \hat{y})\) 是 Tweedie 偏差,请参阅 平均泊松、伽马和 Tweedie 偏差。它也被称为 D² Tweedie,与 McFadden 似然比指数相关。

参数 power 定义了 Tweedie 幂,与 mean_tweedie_deviance 相同。请注意,当 power=0 时,d2_tweedie_score 等于 r2_score(对于单个目标)。

可以通过以下方式构建具有特定 power 选择的评分器对象

>>> from sklearn.metrics import d2_tweedie_score, make_scorer
>>> d2_tweedie_score_15 = make_scorer(d2_tweedie_score, power=1.5)
D² 弹子分数#

d2_pinball_score 函数实现了带有弹子损失的 D² 的特殊情况,请参阅 弹子损失,即

\[\text{dev}(y, \hat{y}) = \text{pinball}(y, \hat{y}).\]

参数 alpha 定义了弹子损失的斜率,与 mean_pinball_loss (弹子损失) 相同。它确定了弹子损失和 D² 达到最优的分位数水平 alpha。请注意,对于 alpha=0.5(默认值),d2_pinball_score 等于 d2_absolute_error_score

可以通过以下方式构建具有特定 alpha 选择的评分器对象

>>> from sklearn.metrics import d2_pinball_score, make_scorer
>>> d2_pinball_score_08 = make_scorer(d2_pinball_score, alpha=0.8)
D² 绝对误差分数#

d2_absolute_error_score 函数实现了 平均绝对误差 的特殊情况

\[\text{dev}(y, \hat{y}) = \text{MAE}(y, \hat{y}).\]

以下是 d2_absolute_error_score 函数的一些使用示例

>>> from sklearn.metrics import d2_absolute_error_score
>>> y_true = [3, -0.5, 2, 7]
>>> y_pred = [2.5, 0.0, 2, 8]
>>> d2_absolute_error_score(y_true, y_pred)
0.764
>>> y_true = [1, 2, 3]
>>> y_pred = [1, 2, 3]
>>> d2_absolute_error_score(y_true, y_pred)
1.0
>>> y_true = [1, 2, 3]
>>> y_pred = [2, 2, 2]
>>> d2_absolute_error_score(y_true, y_pred)
0.0

3.4.6.12. 回归模型的视觉评估#

在评估回归模型质量的方法中,scikit-learn 提供了 PredictionErrorDisplay 类。它允许以两种不同方式直观地检查模型的预测误差。

../_images/sphx_glr_plot_cv_predict_001.png

左侧的图表显示了实际值与预测值。对于旨在预测 y 的(条件)期望值的无噪声回归任务,完美的回归模型会在预测值等于实际值所定义的对角线上显示数据点。离这条最优线越远,模型的误差越大。在更现实的、具有不可约噪声的设置中,即当 y 的所有变化都无法通过 X 中的特征来解释时,最好的模型将导致数据点密集地排列在对角线周围。

请注意,上述情况仅在预测值是给定 Xy 的期望值时成立。对于最小化均方误差目标函数或更一般地最小化任何“power”参数值的 平均 Tweedie 偏差 的回归模型来说,情况通常如此。

当绘制给定 X 预测 y 的分位数估计器的预测时,例如 QuantileRegressor 或任何其他最小化 弹子损失 的模型,根据估计的分位数水平,一部分点预期位于对角线上方或下方。

总而言之,尽管这张图直观易懂,但它并没有真正告诉我们如何才能获得更好的模型。

右侧的图表显示了残差(即实际值与预测值之间的差值)与预测值的关系。

该图有助于直观地查看残差是否服从同方差或异方差分布。

特别是,如果 y|X 的真实分布是泊松分布或伽马分布,则最优模型的残差方差预期会随着 E[y|X] 的预测值而增长(对于泊松呈线性增长,对于伽马呈二次方增长)。

当拟合线性最小二乘回归模型时(请参阅 LinearRegressionRidge),我们可以使用此图来检查是否满足一些模型假设,特别是残差应该不相关、其期望值应该为零且其方差应该恒定(同方差性)。

如果情况并非如此,特别是如果残差图显示出某种香蕉形结构,则表明模型可能存在错误指定,非线性特征工程或切换到非线性回归模型可能是有益的。

请参阅下面的示例,了解利用此显示进行模型评估的用法。

示例

3.4.7. 聚类指标#

sklearn.metrics 模块实现了多个损失、分数和实用函数来衡量聚类性能。更多信息请参见实例聚类部分的 聚类性能评估,以及双聚类部分的 双聚类评估

3.4.8. 哑估计器#

在进行监督学习时,一个简单的健全性检查包括将自己的估计器与简单的经验法则进行比较。DummyClassifier 实现了几种这样的简单分类策略

  • stratified 通过尊重训练集类分布来生成随机预测。

  • most_frequent 始终预测训练集中最频繁的标签。

  • prior 始终预测使类别先验最大化的类别(类似于 most_frequent),并且 predict_proba 返回类别先验。

  • uniform 均匀随机生成预测。

  • constant 始终预测用户提供的常数标签。

    这种方法的一个主要动机是 F1 评分,当正类是少数时。

请注意,所有这些策略的 predict 方法完全忽略了输入数据!

为了说明 DummyClassifier,首先让我们创建一个不平衡数据集

>>> from sklearn.datasets import load_iris
>>> from sklearn.model_selection import train_test_split
>>> X, y = load_iris(return_X_y=True)
>>> y[y != 1] = -1
>>> X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

接下来,让我们比较 SVCmost_frequent 的准确性

>>> from sklearn.dummy import DummyClassifier
>>> from sklearn.svm import SVC
>>> clf = SVC(kernel='linear', C=1).fit(X_train, y_train)
>>> clf.score(X_test, y_test)
0.63
>>> clf = DummyClassifier(strategy='most_frequent', random_state=0)
>>> clf.fit(X_train, y_train)
DummyClassifier(random_state=0, strategy='most_frequent')
>>> clf.score(X_test, y_test)
0.579

我们看到 SVC 的表现并没有比哑分类器好多少。现在,让我们改变核函数

>>> clf = SVC(kernel='rbf', C=1).fit(X_train, y_train)
>>> clf.score(X_test, y_test)
0.94

我们看到准确性提升到了几乎 100%。如果 CPU 开销不是太大,建议采用交叉验证策略以更好地估计准确性。有关更多信息,请参阅 交叉验证:评估估计器性能 部分。此外,如果您想在参数空间中进行优化,强烈建议使用适当的方法;有关详细信息,请参阅 调整估计器的超参数 部分。

更一般地,当分类器的准确性与随机猜测过于接近时,很可能意味着出现了问题:特征没有帮助,超参数未正确调整,分类器受到类别不平衡的影响等等。

DummyRegressor 还为回归实现了四个简单的经验法则

  • mean 总是预测训练目标的均值。

  • median 总是预测训练目标的中位数。

  • quantile 总是预测用户提供的训练目标的分位数。

  • constant 总是预测用户提供的常数值。

在所有这些策略中,predict 方法完全忽略了输入数据。