分类器校准对比#

校准良好的分类器是指其 predict_proba 输出可以直接解释为置信度的概率分类器。例如,一个校准良好的(二元)分类器应该对样本进行分类,使得对于其给出 predict_proba 值接近 0.8 的样本,大约有 80% 确实属于正类。

在此示例中,我们将比较四种不同模型的校准情况:逻辑回归高斯朴素贝叶斯随机森林分类器线性 SVM

# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause

数据集#

我们将使用一个包含 100,000 个样本和 20 个特征的合成二分类数据集。在这 20 个特征中,只有 2 个是有信息量的,2 个是冗余的(信息特征的随机组合),其余 16 个是无信息量的(随机数)。

在 100,000 个样本中,100 个将用于模型拟合,其余用于测试。请注意,这种拆分非常不寻常:其目的是为那些容易过拟合的模型获得稳定的校准曲线估计。在实践中,人们应该使用交叉验证进行更均衡的拆分,但这会使本示例的代码变得难以理解。

from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split

X, y = make_classification(
    n_samples=100_000, n_features=20, n_informative=2, n_redundant=2, random_state=42
)

train_samples = 100  # Samples used for training the models
X_train, X_test, y_train, y_test = train_test_split(
    X,
    y,
    shuffle=False,
    test_size=100_000 - train_samples,
)

校准曲线#

下面,我们使用小型训练数据集训练四种模型中的每一种,然后使用测试数据集的预测概率绘制校准曲线(也称为可靠性图)。校准曲线是通过对预测概率进行分箱,然后绘制每个分箱中的平均预测概率与观察到的频率(“正类比例”)的关系来创建的。在校准曲线下方,我们绘制了一个直方图,显示预测概率的分布,更具体地说,是每个预测概率分箱中的样本数量。

import numpy as np

from sklearn.svm import LinearSVC


class NaivelyCalibratedLinearSVC(LinearSVC):
    """LinearSVC with `predict_proba` method that naively scales
    `decision_function` output."""

    def fit(self, X, y):
        super().fit(X, y)
        df = self.decision_function(X)
        self.df_min_ = df.min()
        self.df_max_ = df.max()

    def predict_proba(self, X):
        """Min-max scale output of `decision_function` to [0,1]."""
        df = self.decision_function(X)
        calibrated_df = (df - self.df_min_) / (self.df_max_ - self.df_min_)
        proba_pos_class = np.clip(calibrated_df, 0, 1)
        proba_neg_class = 1 - proba_pos_class
        proba = np.c_[proba_neg_class, proba_pos_class]
        return proba
from sklearn.calibration import CalibrationDisplay
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegressionCV
from sklearn.naive_bayes import GaussianNB

# Define the classifiers to be compared in the study.
#
# Note that we use a variant of the logistic regression model that can
# automatically tune its regularization parameter.
#
# For a fair comparison, we should run a hyper-parameter search for all the
# classifiers but we don't do it here for the sake of keeping the example code
# concise and fast to execute.
lr = LogisticRegressionCV(
    Cs=np.logspace(-6, 6, 101),
    cv=10,
    l1_ratios=(0,),
    scoring="neg_log_loss",
    max_iter=1_000,
    use_legacy_attributes=False,
)
gnb = GaussianNB()
svc = NaivelyCalibratedLinearSVC(C=1.0)
rfc = RandomForestClassifier(random_state=42)

clf_list = [
    (lr, "Logistic Regression"),
    (gnb, "Naive Bayes"),
    (svc, "SVC"),
    (rfc, "Random forest"),
]
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

fig = plt.figure(figsize=(10, 10))
gs = GridSpec(4, 2)
colors = plt.get_cmap("Dark2")

ax_calibration_curve = fig.add_subplot(gs[:2, :2])
calibration_displays = {}
markers = ["^", "v", "s", "o"]
for i, (clf, name) in enumerate(clf_list):
    clf.fit(X_train, y_train)
    display = CalibrationDisplay.from_estimator(
        clf,
        X_test,
        y_test,
        n_bins=10,
        name=name,
        ax=ax_calibration_curve,
        color=colors(i),
        marker=markers[i],
    )
    calibration_displays[name] = display

ax_calibration_curve.grid()
ax_calibration_curve.set_title("Calibration plots")

# Add histogram
grid_positions = [(2, 0), (2, 1), (3, 0), (3, 1)]
for i, (_, name) in enumerate(clf_list):
    row, col = grid_positions[i]
    ax = fig.add_subplot(gs[row, col])

    ax.hist(
        calibration_displays[name].y_prob,
        range=(0, 1),
        bins=10,
        label=name,
        color=colors(i),
    )
    ax.set(title=name, xlabel="Mean predicted probability", ylabel="Count")

plt.tight_layout()
plt.show()
Calibration plots, Logistic Regression, Naive Bayes, SVC, Random forest

结果分析#

LogisticRegressionCV 尽管训练集规模很小,但仍能返回校准相当良好的预测:在四个模型中,其可靠性曲线最接近对角线。

逻辑回归是通过最小化对数损失(log-loss)来训练的,这是一种严格恰当的评分规则:在训练数据趋于无穷大的极限下,严格恰当的评分规则由预测真实条件概率的模型最小化。因此,那个(假设的)模型将是完美校准的。然而,仅使用恰当的评分规则作为训练目标并不足以保证模型校准良好:即使有非常大的训练集,如果逻辑回归正则化过强,或者输入特征的选择和预处理导致模型错误指定(例如,如果数据集的真实决策边界是输入特征的高度非线性函数),逻辑回归仍然可能校准不佳。

在本例中,训练集被有意保持得非常小。在这种情况下,优化对数损失仍可能因为过拟合而导致模型校准不佳。为了缓解这种情况,LogisticRegressionCV 类被配置为通过内部交叉验证调整 C 正则化参数,以同时最小化对数损失,从而在小型训练集设置中为该模型找到最佳平衡点。

由于训练集规模有限且缺乏模型定义良好的保证,我们观察到逻辑回归模型的校准曲线靠近但并未完美落在对角线上。该模型校准曲线的形状可以解释为稍微欠自信:与真实的阳性样本比例相比,预测概率过于接近 0.5。

其他方法输出的概率校准效果都不如前者

  • GaussianNB 在此特定数据集上倾向于将概率推向 0 或 1(参见直方图)(过度自信)。这主要是因为朴素贝叶斯方程只有在特征条件独立的假设成立时,才能提供正确的概率估计 [2]。然而,特征之间可能存在相关性,本数据集就是这种情况,它包含 2 个由信息特征的随机线性组合生成的特征。这些相关特征被“重复计算”,导致预测概率被推向 0 和 1 [3]。但请注意,更改生成数据集所使用的种子可能会导致朴素贝叶斯估计器的结果发生巨大变化。

  • LinearSVC 不是天生的概率分类器。为了将其预测解释为概率,我们在上述定义的 NaivelyCalibratedLinearSVC 包装类中,通过应用最小-最大缩放(min-max scaling),天真地将 decision_function 的输出缩放到 [0, 1] 之间。该估计器在此数据上显示出典型的 S 型校准曲线:大于 0.5 的预测对应于具有更高有效正类比例(对角线上方)的样本,而小于 0.5 的预测则对应于更低的正类比例(对角线下方)。这种欠自信的预测对于最大间隔方法来说是很典型的 [1]

  • RandomForestClassifier 的预测直方图显示在约 0.2 和 0.9 概率处有峰值,而接近 0 或 1 的概率非常罕见。[1] 给出了一个解释:“诸如装袋法(bagging)和随机森林之类对基础模型集进行平均的方法,在进行接近 0 和 1 的预测时可能会遇到困难,因为底层基础模型中的方差会使原本应该接近 0 或 1 的预测偏离这些值。由于预测被限制在区间 [0, 1] 内,由方差引起的误差往往在 0 和 1 附近呈单侧分布。例如,如果模型应该为一个案例预测 p = 0,那么装袋法实现这一点的唯一途径是所有装袋树都预测为零。如果我们向装袋所平均的树中添加噪声,这种噪声会导致一些树为此案例预测出大于 0 的值,从而使装袋集成的平均预测远离 0。我们最强烈地观察到随机森林的这种效应,因为随机森林训练的基础树由于特征子集选择而具有相对较高的方差。”这种效应会使随机森林显得欠自信。尽管存在这种可能的偏差,但请注意,树本身是通过最小化基尼指数(Gini)或熵(Entropy)准则来拟合的,这两者都会导致最小化恰当评分规则(即 Brier 分数或对数损失)的分割。详情请参阅 用户指南。这可以解释为什么该模型在这个特定的示例数据集上显示出足够好的校准曲线。事实上,随机森林模型并不比逻辑回归模型显著更欠自信。

欢迎使用不同的随机种子和其他数据集生成参数重新运行此示例,以查看校准图可能看起来有多么不同。总的来说,逻辑回归和随机森林往往是校准得最好的分类器,而 SVC 通常会显示典型的欠自信校准不佳现象。朴素贝叶斯模型通常也校准不佳,但其校准曲线的一般形状会根据数据集的不同而产生巨大差异。

最后,请注意,对于某些数据集种子,所有模型校准效果都很差,即使像上面那样调整了正则化参数。当训练集过小或模型被严重错误指定时,这种情况不可避免。

参考文献#

脚本总运行时间: (0 分 3.875 秒)

相关示例

概率校准曲线

概率校准曲线

分类器的概率校准

分类器的概率校准

3分类问题的概率校准

3分类问题的概率校准

惩罚逻辑回归模型收敛性分析

惩罚逻辑回归模型收敛性分析

由 Sphinx-Gallery 生成的图库