使用CalibratedClassifierCV对多标签分类进行校准。

3
我已经建立了一些sklearn分类器模型来执行多标签分类,并希望校准它们的predict_proba输出,以便获得置信度分数。我还想使用诸如sklearn.metrics.recall_score之类的指标来评估它们。
我有4个标签要预测,真实标签是多热编码的(例如[0, 1, 1, 1])。因此,CalibratedClassifierCV不直接接受我的数据:
clf = tree.DecisionTreeClassifier(max_depth=15)
clf = clf.fit(train_X, train_Y)
calibrated_clf = CalibratedClassifierCV(clf, cv="prefit", method="sigmoid")
calibrated_clf.fit(dev_X, dev_Y)

这将返回一个错误:
ValueError: classes [[0 1]
 [0 1]
 [0 1]
 [0 1]] mismatch with the labels [0 1 2 3] found in the data

因此,我尝试将其包装在OneVsRestClassifier中:
clf = OneVsRestClassifier(tree.DecisionTreeClassifier(max_depth=15), n_jobs=4)
clf = clf.fit(train_X, train_Y)
calibrated_clf = CalibratedClassifierCV(clf, cv="prefit", method="sigmoid")
calibrated_clf.fit(dev_X, dev_Y)

请注意,即使它们可能更适合我的问题,MultiOutputClassifierClassifierChain也不起作用。
它能够工作,但由于其实现方式,校准分类器的predict输出是多类别而不是多标签。有四个类别([0 1 2 3]),但如果没有必要放置标签,它仍然会预测0
通过校准曲线的进一步检查,发现包装在校准分类器内部的基础估计器根本没有被校准。也就是说,(calibrated_clf.calibrated_classifiers_)[0].base_estimator返回与校准之前相同的clf
我希望观察我的(校准过的)模型在进行确定性(predict)和概率性(predict_proba)预测时的表现。我应该如何设计我的模型/包装其他容器,以获得每个标签的校准概率和易于理解的标签预测?
1个回答

4
在你的示例中,你使用了一个默认情况下支持维度为(n, m)且m > 1的目标的DecisionTreeClassifier
但是,如果你想要每个类别的边际概率作为结果,则使用OneVsRestClassifier。 注意,CalibratedClassifierCV期望目标为1d,因此“技巧”是将其扩展为支持MultiOutputClassifier的多标签分类。 完整示例
from sklearn.datasets import make_multilabel_classification
from sklearn.tree import DecisionTreeClassifier
from sklearn.multioutput import MultiOutputClassifier
from sklearn.model_selection import train_test_split, StratifiedKFold

import numpy as np

# Generate a sample multilabel target
X, y = make_multilabel_classification(n_classes=4, random_state=0)

y
>>> 
array([[1, 0, 1, 0],
       [0, 0, 0, 0],
       [1, 0, 1, 0],
       ...
       [0, 0, 0, 0],
       [0, 1, 1, 1],
       [1, 1, 0, 1]])

# Split in train/test
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.9, random_state=42
)

# Splits Stratify target variable
cv = StratifiedKFold(n_splits=2)

# Decision tree support by default multiclass target or use OneVsRest if marginal probabilities
clf = OneVsRestClassifier(DecisionTreeClassifier(max_depth=10))

# Calibrate estimator probabilities
calibrated_clf = CalibratedClassifierCV(base_estimator=clf, cv=cv)

# calibrated_clf target is one dimensional, extend classifier to multi-target classification.
multioutput_clf = MultiOutputClassifier(calibrated_clf).fit(X_train, y_train)

# Check predict 
multioutput_clf.predict(X_test[-5:])
>>> 
array([[0, 0, 1, 1],
       [0, 0, 0, 1],
       [0, 0, 0, 1],
       [0, 0, 0, 1],
       [0, 0, 0, 1]])

# Check predict_proba
multioutput_clf.predict_proba(X_test[-5:])
>>>
[array([[0.78333315, 0.21666685],
        [0.78333315, 0.21666685],
        [0.78333315, 0.21666685],
        [0.78333315, 0.21666685],
        [0.78333315, 0.21666685]]),
 array([[0.59166537, 0.40833463],
        [0.59166537, 0.40833463],
        [0.40833361, 0.59166639],
        [0.59166537, 0.40833463],
        [0.59166537, 0.40833463]]),
 array([[0.61666922, 0.38333078],
        [0.61666427, 0.38333573],
        [0.80000098, 0.19999902],
        [0.61666427, 0.38333573],
        [0.61666427, 0.38333573]]),
 array([[0.26874774, 0.73125226],
        [0.26874774, 0.73125226],
        [0.45208444, 0.54791556],
        [0.26874774, 0.73125226],
        [0.26874774, 0.73125226]])]

注意,predict_proba 的结果是一个包含4个数组的列表,每个数组都是属于类别i的概率。例如,在第一个数组的第一个样本中,包含了第一个样本属于类别1的概率等等。
关于校准曲线,scikit-learn 提供了绘制二维 链接1 和三维 链接2 目标概率路径的示例。

谢谢,校准后的模型现在以所需格式输出。经过校准的模型曲线比未进行校准的更加不稳定,并且性能在校准后变得更差。这是正常的行为吗? - ivsiquadrus
1
@ivsiquadrus,你使用基于决策树的模型吗?如果是这种情况,由于算法的特性,曲线可能会有点“不稳定”,预测往往集中在中心而不是极端。 - Miguel Trejo
1
还有,你是如何评估校准的?这里有一个使用Brier分数的好例子:https://scikit-learn.org/stable/auto_examples/calibration/plot_calibration.html - Miguel Trejo
我明白了。我试图重复绘制校准曲线并在其他模型上评估F1分数的过程,例如XGBoost的梯度提升分类器。看起来校准对概率的改善很小,但确实会对F1分数产生负面影响(例如下降了0.05-0.10)。我已经使用XGB模型计算了所有四个标签的Brier分数,并且校准模型在除一个标签外的所有标签中都具有更高的Brier分数(高出0.001),其中校准似乎有效。 - ivsiquadrus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接