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

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

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

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

  • 我的任务应该使用哪个评分函数?

简而言之,如果评分函数是给定的,例如在 Kaggle 竞赛或商业环境中,就使用它。如果您可以自由选择,首先要考虑预测的最终目标和应用。区分两个步骤很有用:

  • 预测

  • 决策制定

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

一旦确定了这一点,就使用该(目标)函数值的一个 **严格一致** 的评分函数,参见 [Gneiting2009]。这意味着使用一个评分函数,该函数通过 \(Y\) 的观测值(即 y_true)与预测值 y_pred 和真实目标函数值之间的 **距离进行衡量**。对于分类问题,**严格Proper评分规则**(参见 维基百科“评分规则”词条[Gneiting2007])与严格一致的评分函数是一致的。下表提供了示例。可以说,一致的评分函数充当“真言药水”,因为它们保证“说真话 […] 在期望上是一种最优策略[Gneiting2014]

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

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

决策制定: 最常见的决策是在二元分类任务中完成的,其中 predict_proba 的结果被转换为单个输出,例如,根据预测的降雨概率,决定如何采取行动(是否采取缓解措施,如打伞)。对于分类器,这就是 predict 返回的内容。另请参见 为类别预测调整决策阈值。有许多评分函数可以衡量此类决策的各个方面,其中大多数都包含在 metrics.confusion_matrix 中或由此派生。

严格一致的评分函数列表: 在这里,我们列出了一些在实践中最重要的统计函数值及其对应的严格一致的评分函数。请注意,此列表不完整,还有更多。有关如何选择特定函数的进一步标准,请参见 [Fissler2022]

函数值

评分或损失函数

响应 y

预测

分类

mean

Brier 分数 1

多类别

predict_proba

mean

对数损失

多类别

predict_proba

众数

零一损失 2

多类别

predict,类别型

回归

mean

平方误差 3

所有实数

predict,所有实数

mean

泊松偏差

非负

predict,严格正

mean

Gamma 偏差

严格正

predict,严格正

mean

Tweedie 偏差

取决于 power

predict,取决于 power

中位数

绝对误差

所有实数

predict,所有实数

分位数

弹球损失

所有实数

predict,所有实数

众数

不存在一致的

实数

1 对于具有独热编码目标的分类,Brier 分数只是平方误差的另一种名称。

2 零一损失对于众数来说只是一致的,而不是严格一致的。零一损失等同于 1 减去准确率得分,这意味着它们具有不同的分数,但排名相同。

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))进行比较时。

References

[Gneiting2007]

T. Gneiting 和 A. E. Raftery. Strictly Proper Scoring Rules, Prediction, and Estimation. In: Journal of the American Statistical Association 102 (2007), pp. 359– 378. pdf链接

[Gneiting2009] (1,2)

T. Gneiting. Making and Evaluating Point Forecasts. Journal of the American Statistical Association 106 (2009): 746 - 762.

[Gneiting2014]

T. Gneiting 和 M. Katzfuss. Probabilistic Forecasting. In: Annual Review of Statistics and Its Application 1.1 (2014), pp. 125–151.

3.4.2. 评分 API 概览#

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

最后,虚拟估计器 有助于为随机预测获得基线指标值。

另请参阅

对于“成对”指标,即在 **样本** 之间而不是估计器或预测之间,请参阅 成对指标、亲和度和核 部分。

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

使用 交叉验证 的模型选择和评估工具(如 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

需要 predict_proba 支持

‘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

需要 predict_proba 支持

‘d2_brier_score’

metrics.d2_brier_score

需要 predict_proba 支持

聚类

‘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" 设置为 "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
在 n_jobs > 1 的函数中使用自定义评分器#

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

例如,要在下面的示例中使用大于 1 的 n_jobs,将 custom_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)。

confusion_matrix_at_thresholds(y_true, y_score)

计算每个分类阈值的二元混淆矩阵项。

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

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

计算平衡准确度。

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

计算 Cohen's kappa:一个衡量注释者间一致性的统计量。

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

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

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

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

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

计算 Matthews 相关系数 (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-measure。

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

计算 F-beta 分数。

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

计算平均 Hamming 损失。

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

Jaccard 相似系数分数。

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

Log loss,又名 logistic loss 或交叉熵损失。

multilabel_confusion_matrix(y_true, y_pred, *)

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

precision_recall_fscore_support(y_true, ...)

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

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\) 分数函数,log loss 解释的比例。

一些函数也适用于二元和多标签(但不适用于多类别)问题:

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] 的值为 1,表示样本 i 具有标签 j,否则值为 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_scoreaccuracy_score 的泛化。区别在于,只要真实标签与 k 个最高预测分数之一相关联,预测就被认为是正确的。accuracy_scorek = 1 的特例。

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

如果 \(\hat{f}_{i,j}\) 是第 \(i\) 个样本对应于第 \(j\) 个最高预测分数的预测类别,而 \(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's J 统计量,或“知情度”。

注意

这里定义的多类别似乎是二元分类中使用的度量的最合理扩展,尽管文献中没有确切的共识。

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

  • 根据 [Mosley2013] 描述的类别平衡准确率:计算每个类别的精确率和召回率之间的最小值。然后将这些值平均到总类别数,以获得平衡准确率。

  • 根据 [Urbanowicz2015] 描述的平衡准确率:计算每个类别的灵敏度和特异度的平均值,然后平均到总类别数。

References

[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, Design of the 2015 ChaLearn AutoML Challenge, IJCNN 2015.

[Mosley2013] (1,2)

L. Mosley, A balanced approach to the multi-class imbalance problem, IJCV 2010.

3.4.4.5. Cohen's kappa#

函数 cohen_kappa_score 计算 Cohen's 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)

使用 confusion_matrix_at_thresholds,我们可以获得不同阈值下的真阴性、假阳性、假阴性和真阳性。

>>> from sklearn.metrics import confusion_matrix_at_thresholds
>>> y_true = np.array([0., 0., 1., 1.])
>>> y_score = np.array([0.1, 0.4, 0.35, 0.8])
>>> tns, fps, fns, tps, thresholds = confusion_matrix_at_thresholds(y_true, y_score)
>>> tns
array([2., 1., 1., 0.])
>>> fps
array([0., 1., 1., 2.])
>>> fns
array([1., 1., 0., 0.])
>>> tps
array([1., 1., 2., 2.])
>>> thresholds
array([0.8, 0.4, 0.35, 0.1])

请注意,阈值由递减顺序的 y_score 的不同值组成。

示例

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-测量#

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

**F-测量**(\(F_\beta\)\(F_1\) 测量)可以解释为精确率和召回率的加权调和平均。\(F_\beta\) 测量达到最佳值 1,最差分数 0。当 \(\beta = 1\) 时,\(F_\beta\)\(F_1\) 是等效的,召回率和精确率同等重要。

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

函数 average_precision_score 从预测分数计算 **平均精确率** (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 中计算曲线下面积时使用此线性插值(使用梯形法则)。[Chen2024] 对不同的插值策略进行了基准测试,以展示其影响。

有几个函数允许您分析精确率、召回率和 F-测量得分:

average_precision_score(y_true, y_score, *)

从预测分数计算平均精度 (AP)。

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

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

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

计算 F-beta 分数。

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

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

precision_recall_fscore_support(y_true, ...)

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

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

计算精确度。

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

计算召回率。

请注意,函数 precision_recall_curve 仅限于二元情况。函数 average_precision_score 通过以一对多(OvR)的方式计算每个类别的分数并根据其 average 参数的值进行平均或不平均,来支持多类别和多标签格式。

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

../_images/sphx_glr_plot_precision_recall_001.png

示例

References

[Manning2008]

C.D. Manning, P. Raghavan, H. Schütze, Introduction to Information Retrieval, 2008.

[Everingham2010]

M. Everingham, L. Van Gool, C.K.I. Williams, J. Winn, A. Zisserman, The Pascal Visual Object Classes (VOC) Challenge, IJCV 2010.

[Chen2024]

W. Chen, C. Miao, Z. Zhang, C.S. Fung, R. Wang, Y. Chen, Y. Qian, L. Cheng, K.Y. Yip, S.K Tsui, Q. Cao, Commonly used software tools produce conflicting and overly-optimistic AUPRC values, Genome Biology 2024.

3.4.4.9.1. 二元分类#

在二元分类任务中,“”和“”指分类器的预测,而“”和“”指该预测是否与外部判断(有时称为“观测值”)相符。根据这些定义,我们可以构建以下表格:

实际类别(观测值)

预测类别(期望)

tp (真阳性) 正确结果

fp (假阳性) 意外结果

fn (假阴性) 缺失结果

tn (真阴性) 正确缺席结果

在此上下文中,我们可以定义精确率和召回率的概念:

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

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

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分数,该分数介于精确率和召回率之间。

  • “macro”平均F度量是通过对每个标签/类别的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}\) 的子集

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

  • \(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)\)

None

\(\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

References

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,这是一个只考虑预测错误的有侧度量。(Hinge loss用于最大间隔分类器,如支持向量机。)

如果二元分类任务的真实标签 \(y_i\) 对于每个样本 \(i\) 编码为 \(y_i=\left\{-1, +1\right\}\);且 \(w_i\) 是相应的预测决策(形状为(n_samples,)的数组,由decision_function方法输出),则hinge loss定义为:

\[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\}\) 是所有其他标签的预测决策的最大值,那么多类别hinge loss定义为:

\[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\}\]

这里有一个小例子,展示了在二元类别问题中使用svm分类器时hinge_loss函数的使用。

>>> 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

这是一个例子,展示了在多类别问题中使用svm分类器时hinge_loss函数的使用。

>>> 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,也称为 logistic regression loss 或 cross-entropy loss,是基于概率估计定义的。它常用于(多项式)logistic回归和神经网络,以及某些期望最大化算法的变体,并且可以用于评估分类器的概率输出(predict_proba),而不是其离散预测。

对于二元分类,其中真实标签 \(y \in \{0,1\}\),概率估计 \(\hat{p} \approx \operatorname{Pr}(y = 1)\),则每个样本的log loss是分类器给定真实标签的负对数似然:

\[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)\)。那么整个集合的log loss为:

\[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}\]

要理解这如何推广了上面给出的二元log loss,请注意,在二元情况下,\(\hat{p}_{i,0} = 1 - \hat{p}_{i,1}\) 并且 \(y_{i,0} = 1 - y_{i,1}\),因此对 \(y_{i,k} \in \{0,1\}\) 的内层求和展开就得到了二元log loss。

log_loss函数计算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%。log loss是非负的。

3.4.4.13. Matthews 相关系数#

matthews_corrcoef函数为二元类别计算Matthew相关系数(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\)个类别的混淆矩阵 \(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。更多信息请参见[Wikipedia MCC2021]

这里有一个小例子,说明了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

References

[Wikipedia MCC2021]

Wikipedia contributors. Phi coefficient. Wikipedia, The Free Encyclopedia. April 21, 2021, 12:21 CEST. Available at: https://en.wikipedia.org/wiki/Phi_coefficient Accessed April 21, 2021.

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 ])

与子集准确率、Hamming loss 或 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也可以用于多类别分类。目前支持两种平均策略:一对一算法计算成对 ROC AUC 分数的平均值,一对多算法计算每个类别与所有其他类别 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])

示例

References

[HT2001] (1,2)

Hand, D.J. and Till, R.J., (2001). A simple generalisation of the area under the ROC curve for multiple class classification problems. Machine learning, 45(2), pp. 171-186.

[FC2009]

Ferri, Cèsar & Hernandez-Orallo, Jose & Modroiu, R. (2009). An Experimental Comparison of Performance Measures for Classification. Pattern Recognition Letters. 30. 27-38.

[PD2000]

Provost, F., Domingos, P. (2000). Well-trained PETs: Improving probability estimation trees (Section 6.2), CeDER Working Paper #IS-00-04, Stern School of Business, New York University.

[F2006]

Fawcett, T., 2006. An introduction to ROC analysis. Pattern Recognition Letters, 27(8), pp. 861-874.

[F2001]

Fawcett, T., 2001. Using rule sets to maximize ROC performance In Data Mining, 2001. Proceedings IEEE International Conference, pp. 131-138.

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

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

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

DET曲线是接收者操作特征(ROC)曲线的一个变体,其中假阴性率(False Negative Rate)绘制在y轴上,而不是真阳性率(True Positive Rate)。DET曲线通常以正态偏差尺度绘制,通过\(\phi^{-1}\)(其中\(\phi\)是累积分布函数)进行变换。所得的性能曲线显式地可视化了给定分类算法的错误类型之间的权衡。示例和进一步的动机请参见[Martin1997]

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

../_images/sphx_glr_plot_det_001.png
特性#
  • 如果检测得分呈正态(或接近正态)分布,则 DET 曲线在正态偏差尺度上是线性的。由[Navratil2007]证明,反之并非总是如此,甚至更一般的分布也能产生线性的 DET 曲线。

  • 正态偏差尺度变换会分散点,使得图中的一个相对较大的空间被占据。因此,分类性能相似的曲线在 DET 图上可能更容易区分。

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

应用和局限性#

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

另一方面,DET 曲线不提供单一数字的度量。因此,对于自动评估或与其他分类任务的比较,像 ROC 曲线下面积这样的度量可能更合适。

示例

References

[WikipediaDET2017]

Wikipedia contributors. Detection error tradeoff. Wikipedia, The Free Encyclopedia. September 4, 2017, 23:33 UTC. Available at: https://en.wikipedia.org/w/index.php?title=Detection_error_tradeoff&oldid=798982054. Accessed February 19, 2018.

[Martin1997]

A. Martin, G. Doddington, T. Kamm, M. Ordowski, and M. Przybocki, The DET Curve in Assessment of Detection Task Performance, 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将一个子集评为1(如果其标签与预测严格匹配),否则评为0(如果有任何错误)。默认情况下,函数返回预测不完美的子集百分比。要获取此类子集的计数,请将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 score loss#

该函数brier_score_loss计算二元和多类别概率预测的Brier score,并且等同于均方误差。引用维基百科:

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

\(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 score给出为:

\[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 score位于区间\([0, 2]\)内,值越低,概率估计越好(均方差越小)。实际上,Brier score是一个严格的评分规则,这意味着只有当估计的概率等于真实概率时,它才能获得最佳分数。

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

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

该函数brier_score_loss计算Brier score,给定真实标签和预测概率,这些是由估计器的predict_proba方法返回的。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 score可用于评估分类器的校准程度。然而,较低的Brier score loss并不一定意味着更好的校准。这是因为,类比均方误差的偏差-方差分解,Brier score loss可以分解为校准损失和精炼损失的总和[Bella2012]。校准损失定义为来自ROC片段斜率的经验概率的均方偏差。精炼损失可以定义为最优成本曲线下面积所测量的最优损失的期望值。精炼损失可以独立于校准损失而变化,因此较低的Brier score loss不一定意味着更好的校准模型。“只有当精炼损失保持不变时,较低的Brier score loss才总是意味着更好的校准”[Bella2012][Flach2008]

示例

References

[Brier1950]

G. Brier, Verification of forecasts expressed in terms of probability, Monthly weather review 78.1 (1950)

[Bella2012] (1,2)

Bella, Ferri, Hernández-Orallo, and Ramírez-Quintana “Calibration of Machine Learning Models” in Khosrow-Pour, M. “Machine learning: concepts, methodologies, tools and applications.” Hershey, PA: Information Science Reference (2012).

[Flach2008]

Flach, Peter, and Edson Matsubara. “On classification, ranking, and probability estimation.” Dagstuhl Seminar Proceedings. Schloss Dagstuhl-Leibniz-Zentrum für Informatik (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})\)(例如,Log loss,Brier score,)。D²是一种*技能得分*。计算公式为:

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

其中\(y_{\text{null}}\)是仅包含截距的模型的最优预测(例如,在Log loss和Brier score的情况下,y_true的每个类别的比例)。

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

D2 log loss score#

该函数d2_log_loss_score实现了D²的特殊情况,其中使用log loss,请参阅Log loss,即:

\[\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
D2 Brier score#

该函数d2_brier_score实现了D²的特殊情况,其中使用Brier score,请参阅Brier score loss,即:

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

这也被称为Brier Skill Score(BSS)。

这里是一些使用d2_brier_score函数的用法示例。

>>> from sklearn.metrics import d2_brier_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_brier_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_brier_score(y_true, y_pred)
0.9991
>>> 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_brier_score(y_true, y_pred)
-0.370...

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\) 是输出的数量,以及诱导排名函数 \(f\) 的预测 \(\hat{y}\),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. 回归度量#

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,)的数组。

r2_scoreexplained_variance_score接受multioutput参数的附加值'variance_weighted'。此选项通过相应目标变量的方差对每个单独分数进行加权。此设置量化了全局捕获的未缩放方差。如果目标变量的尺度不同,则此分数将更重视解释具有较大方差的变量。

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

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

它表示模型中的自变量解释了多少方差(y的方差)。它提供了拟合优度的指示,因此通过已解释方差的比例来衡量模型对未见过样本的预测可能有多好。

由于方差是数据集相关的,\(R^2\) 可能无法在不同数据集之间进行有意义的比较。最好的分数是 1.0,它也可以是负数(因为模型可能任意地变差)。一个恒定的模型,它总是预测 y 的期望(平均)值,而忽略输入特征,将获得 0.0 的 \(R^2\) 分数。

注意:当预测残差的均值为零时,\(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 计算 平均 Tweedie 偏差误差,并带有一个 power 参数(\(p\))。这是一个用于获取回归目标预测期望值的度量。

以下是特殊情况:

如果 \(\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{对于 }p=0\text{ (正态)}\\ 2(y_i \log(y_i/\hat{y}_i) + \hat{y}_i - y_i), & \text{对于 }p=1\text{ (泊松)}\\ 2(\log(\hat{y}_i/y_i) + y_i/\hat{y}_i - 1), & \text{对于 }p=2\text{ (伽马)}\\ 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{否则} \end{cases}\end{split}\]

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

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

均方误差(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=0d2_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 的期望值时成立。这通常是最小化均方误差目标函数或更通用的平均 Tweedie 偏差(对于其“power”参数的任何值)的回归模型的典型情况。

在绘制预测 Xy 的分位数的估计器(例如 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 方法会完全忽略输入数据。