多类别训练元估计器的概述#

在此示例中,我们讨论目标变量包含两个以上类别时的分类问题。这被称为多类别分类。

在scikit-learn中,所有估计器都开箱即用地支持多类别分类:已为终端用户实现了最合理的策略。sklearn.multiclass 模块实现了可用于实验或开发仅支持二元分类的第三方估计器的各种策略。

sklearn.multiclass 包含OvO/OvR策略,用于通过拟合一组二元分类器(OneVsOneClassifierOneVsRestClassifier 元估计器)来训练多类别分类器。本示例将对其进行回顾。

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

Yeast UCI 数据集#

在此示例中,我们使用一个UCI数据集 [1],通常称为Yeast数据集。我们使用 sklearn.datasets.fetch_openml 函数从OpenML加载数据集。

from sklearn.datasets import fetch_openml

X, y = fetch_openml(data_id=181, as_frame=True, return_X_y=True)

为了了解我们正在处理的数据科学问题类型,我们可以检查我们想要构建预测模型的目标变量。

y.value_counts().sort_index()
class_protein_localization
CYT    463
ERL      5
EXC     35
ME1     44
ME2     51
ME3    163
MIT    244
NUC    429
POX     20
VAC     30
Name: count, dtype: int64

我们看到目标变量是离散的,由10个类别组成。因此,我们处理的是一个多类别分类问题。

策略比较#

在以下实验中,我们使用一个 DecisionTreeClassifier 和一个带3次分割和5次重复的 RepeatedStratifiedKFold 交叉验证。

我们比较以下策略

  • DecisionTreeClassifier 可以处理多类别分类,无需任何特殊调整。它通过将训练数据分解为较小的子集并关注每个子集中最常见的类别来工作。通过重复此过程,模型可以准确地将输入数据分类到多个不同类别。

  • OneVsOneClassifier 训练一组二元分类器,其中每个分类器都训练用于区分两个类别。

  • OneVsRestClassifier:训练一组二元分类器,其中每个分类器都训练用于区分一个类别和其余类别。

  • OutputCodeClassifier:训练一组二元分类器,其中每个分类器都训练用于区分一组类别和其余类别。类别集由码本定义,在scikit-learn中是随机生成的。此方法公开一个参数 code_size 来控制码本的大小。我们将其设置为大于1,因为我们不感兴趣压缩类别表示。

import pandas as pd

from sklearn.model_selection import RepeatedStratifiedKFold, cross_validate
from sklearn.multiclass import (
    OneVsOneClassifier,
    OneVsRestClassifier,
    OutputCodeClassifier,
)
from sklearn.tree import DecisionTreeClassifier

cv = RepeatedStratifiedKFold(n_splits=3, n_repeats=5, random_state=0)

tree = DecisionTreeClassifier(random_state=0)
ovo_tree = OneVsOneClassifier(tree)
ovr_tree = OneVsRestClassifier(tree)
ecoc = OutputCodeClassifier(tree, code_size=2)

cv_results_tree = cross_validate(tree, X, y, cv=cv, n_jobs=2)
cv_results_ovo = cross_validate(ovo_tree, X, y, cv=cv, n_jobs=2)
cv_results_ovr = cross_validate(ovr_tree, X, y, cv=cv, n_jobs=2)
cv_results_ecoc = cross_validate(ecoc, X, y, cv=cv, n_jobs=2)

我们现在可以比较不同策略的统计性能。我们绘制了不同策略的分数分布。

from matplotlib import pyplot as plt

scores = pd.DataFrame(
    {
        "DecisionTreeClassifier": cv_results_tree["test_score"],
        "OneVsOneClassifier": cv_results_ovo["test_score"],
        "OneVsRestClassifier": cv_results_ovr["test_score"],
        "OutputCodeClassifier": cv_results_ecoc["test_score"],
    }
)
ax = scores.plot.kde(legend=True)
ax.set_xlabel("Accuracy score")
ax.set_xlim([0, 0.7])
_ = ax.set_title(
    "Density of the accuracy scores for the different multiclass strategies"
)
Density of the accuracy scores for the different multiclass strategies

乍一看,我们可以看到决策树分类器的内置策略工作得相当好。一对一(One-vs-one)和纠错输出码策略甚至更好。然而,一对多(One-vs-rest)策略不如其他策略。

事实上,这些结果重现了文献中报告的一些内容,如 [2] 中所述。然而,情况并非看起来那么简单。

结论#

我们可以对这些结果获得一些直观理解。

首先,在未优化超参数时,一对一(One-vs-one)和纠错输出码(error-correcting output code)优于树的原因在于它们集成了更多的分类器。集成提高了泛化性能。这有点类似于为什么在不进行超参数优化的情况下,Bagging分类器通常比单个决策树表现更好。

然后,我们看到优化超参数的重要性。事实上,在开发预测模型时应该定期探索这一点,即使集成等技术有助于减少这种影响。

最后,重要的是要记住scikit-learn中的估计器是使用特定的策略开发的,可以开箱即用地处理多类别分类。因此,对于这些估计器,不需要使用不同的策略。这些策略主要适用于只支持二元分类的第三方估计器。在所有情况下,我们还表明应该优化超参数。

参考文献#

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

相关示例

嵌套与非嵌套交叉验证

嵌套与非嵌套交叉验证

使用分类器链进行多标签分类

使用分类器链进行多标签分类

决策函数截止点的后验调优

决策函数截止点的后验调优

多项式和One-vs-Rest逻辑回归的决策边界

多项式和One-vs-Rest逻辑回归的决策边界

由Sphinx-Gallery生成