分类器校准的比较#

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

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

作者:scikit-learn 开发者 SPDX-License-Identifier: BSD-3-Clause

#
# Dataset
# -------
#
# We will use a synthetic binary classification dataset with 100,000 samples
# and 20 features. Of the 20 features, only 2 are informative, 2 are
# redundant (random combinations of the informative features) and the
# remaining 16 are uninformative (random numbers).
#
# Of the 100,000 samples, 100 will be used for model fitting and the remaining
# for testing. Note that this split is quite unusual: the goal is to obtain
# stable calibration curve estimates for models that are potentially prone to
# overfitting. In practice, one should rather use cross-validation with more
# balanced splits but this would make the code of this example more complicated
# to follow.

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, scoring="neg_log_loss", max_iter=1_000
)
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 返回相当良好校准的预测,尽管训练集大小很小:它的可靠性曲线在四个模型中与对角线最接近。

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

在这个例子中,训练集故意保持得很小。在这种情况下,由于过拟合,优化对数损失仍然可能导致校准不良的模型。为了减轻这种情况,LogisticRegressionCV 类被配置为调整C正则化参数,以便通过内部交叉验证最小化对数损失,从而在这个小型训练集设置中找到该模型的最佳折衷方案。

由于有限的训练集大小和缺乏对良好规范的保证,我们观察到逻辑回归模型的校准曲线接近但并不完全位于对角线上。该模型校准曲线的形状可以解释为略微缺乏自信:与真实正样本比例相比,预测概率过于接近0.5。

其他方法都输出校准较差的概率。

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

  • LinearSVC并非天然的概率分类器。为了将其预测解释为概率,我们在上面定义的NaivelyCalibratedLinearSVC包装类中,通过应用最小-最大缩放,将决策函数的输出简单地缩放至[0, 1]区间。该估计器在此数据集上显示了典型的S形校准曲线:大于0.5的预测对应于具有更大有效正类分数的样本(位于对角线上方),而小于0.5的预测对应于更低的正类分数(位于对角线下方)。这种过于保守的预测是最大间隔方法的典型特征[1]

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

随意使用不同的随机种子和其他数据集生成参数重新运行此示例,以查看校准图的差异。通常,逻辑回归和随机森林往往是校准最好的分类器,而SVC通常会显示典型的过于保守的失校准。朴素贝叶斯模型也经常校准不良,但其校准曲线的总体形状会根据数据集而有很大差异。

最后,请注意,对于某些数据集种子,即使调整上述正则化参数,所有模型的校准效果都很差。当训练样本量太小或模型严重错误指定时,这种情况必然会发生。

参考文献#

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

相关示例

概率校准曲线

概率校准曲线

分类器的概率校准

分类器的概率校准

三类分类的概率校准

三类分类的概率校准

绘制 VotingClassifier 计算的类概率

绘制 VotingClassifier 计算的类概率

由Sphinx-Gallery生成的图库