用于分类的特征离散化

Beginner

This tutorial is from open-source community. Access the source code

简介

在机器学习中,特征离散化是一种通过创建区间或桶来表示数据集中连续变量,从而减少连续变量数量的方法。在连续变量数量众多且需要简化算法以便于分析的情况下,此方法可能会很有用。在本实验中,我们将在合成分类数据集上演示特征离散化。

虚拟机使用提示

虚拟机启动完成后,点击左上角切换到“笔记本”标签页,以访问 Jupyter Notebook 进行练习。

有时,你可能需要等待几秒钟让 Jupyter Notebook 完成加载。由于 Jupyter Notebook 的限制,操作验证无法自动化。

如果你在学习过程中遇到问题,随时向 Labby 提问。课程结束后提供反馈,我们会及时为你解决问题。

导入库

在这一步中,我们将导入本实验所需的库。我们将使用 scikit-learn 库进行机器学习任务,使用 numpy 进行数学运算,使用 matplotlib 进行数据可视化。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import make_moons, make_circles, make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import KBinsDiscretizer
from sklearn.svm import SVC, LinearSVC
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.utils._testing import ignore_warnings
from sklearn.exceptions import ConvergenceWarning

准备数据

在这一步中,我们将为特征离散化准备合成分类数据集。我们将使用 scikit-learn 库生成三个不同的数据集:月牙形、同心圆和线性可分数据。

h = 0.02  ## 网格中的步长

n_samples = 100
datasets = [
    make_moons(n_samples=n_samples, noise=0.2, random_state=0),
    make_circles(n_samples=n_samples, noise=0.2, factor=0.5, random_state=1),
    make_classification(
        n_samples=n_samples,
        n_features=2,
        n_redundant=0,
        n_informative=2,
        random_state=2,
        n_clusters_per_class=1,
    ),
]

定义分类器和参数

在这一步中,我们将定义在特征离散化过程中要使用的分类器和参数。我们将创建一个分类器列表,其中包括逻辑回归、线性支持向量机(SVM)、梯度提升分类器以及带有径向基函数核的 SVM。我们还将为每个分类器定义一组参数,以便在 GridSearchCV 算法中使用。

## (估计器,参数网格) 列表,其中参数网格用于 GridSearchCV
## 此示例中的参数空间限制在一个较窄的范围内,以减少运行时间。在实际应用中,
## 应该为算法使用更广泛的搜索空间。
classifiers = [
    (
        make_pipeline(StandardScaler(), LogisticRegression(random_state=0)),
        {"logisticregression__C": np.logspace(-1, 1, 3)},
    ),
    (
        make_pipeline(StandardScaler(), LinearSVC(random_state=0, dual="auto")),
        {"linearsvc__C": np.logspace(-1, 1, 3)},
    ),
    (
        make_pipeline(
            StandardScaler(),
            KBinsDiscretizer(encode="onehot"),
            LogisticRegression(random_state=0),
        ),
        {
            "kbinsdiscretizer__n_bins": np.arange(5, 8),
            "logisticregression__C": np.logspace(-1, 1, 3),
        },
    ),
    (
        make_pipeline(
            StandardScaler(),
            KBinsDiscretizer(encode="onehot"),
            LinearSVC(random_state=0, dual="auto"),
        ),
        {
            "kbinsdiscretizer__n_bins": np.arange(5, 8),
            "linearsvc__C": np.logspace(-1, 1, 3),
        },
    ),
    (
        make_pipeline(
            StandardScaler(), GradientBoostingClassifier(n_estimators=5, random_state=0)
        ),
        {"gradientboostingclassifier__learning_rate": np.logspace(-2, 0, 5)},
    ),
    (
        make_pipeline(StandardScaler(), SVC(random_state=0)),
        {"svc__C": np.logspace(-1, 1, 3)},
    ),
]

names = [get_name(e).replace("StandardScaler + ", "") for e, _ in classifiers]

可视化数据

在这一步中,我们将在进行特征离散化之前可视化合成分类数据集。我们将绘制每个数据集的训练和测试点。

fig, axes = plt.subplots(
    nrows=len(datasets), ncols=len(classifiers) + 1, figsize=(21, 9)
)

cm_piyg = plt.cm.PiYG
cm_bright = ListedColormap(["#b30065", "#178000"])

## 遍历数据集
for ds_cnt, (X, y) in enumerate(datasets):
    print(f"\ndataset {ds_cnt}\n---------")

    ## 拆分为训练和测试部分
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.5, random_state=42
    )

    ## 创建用于背景颜色的网格
    x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
    y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))

    ## 首先绘制数据集
    ax = axes[ds_cnt, 0]
    if ds_cnt == 0:
        ax.set_title("Input data")
    ## 绘制训练点
    ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap=cm_bright, edgecolors="k")
    ## 以及测试点
    ax.scatter(
        X_test[:, 0], X_test[:, 1], c=y_test, cmap=cm_bright, alpha=0.6, edgecolors="k"
    )
    ax.set_xlim(xx.min(), xx.max())
    ax.set_ylim(yy.min(), yy.max())
    ax.set_xticks(())
    ax.set_yticks(())

实现特征离散化

在这一步中,我们将使用 scikit-learn 中的 KBinsDiscretizer 类对数据集实现特征离散化。这将通过创建一组箱(bin)并对离散值进行独热编码来离散化特征。然后,我们将数据拟合到线性分类器并评估性能。

## 遍历分类器
for est_idx, (name, (estimator, param_grid)) in enumerate(zip(names, classifiers)):
    ax = axes[ds_cnt, est_idx + 1]

    clf = GridSearchCV(estimator=estimator, param_grid=param_grid)
    with ignore_warnings(category=ConvergenceWarning):
        clf.fit(X_train, y_train)
    score = clf.score(X_test, y_test)
    print(f"{name}: {score:.2f}")

    ## 绘制决策边界。为此,我们将为网格 [x_min, x_max]*[y_min, y_max] 中的每个点分配一种颜色。
    if hasattr(clf, "decision_function"):
        Z = clf.decision_function(np.column_stack([xx.ravel(), yy.ravel()]))
    else:
        Z = clf.predict_proba(np.column_stack([xx.ravel(), yy.ravel()]))[:, 1]

    ## 将结果放入颜色图中
    Z = Z.reshape(xx.shape)
    ax.contourf(xx, yy, Z, cmap=cm_piyg, alpha=0.8)

    ## 绘制训练点
    ax.scatter(
        X_train[:, 0], X_train[:, 1], c=y_train, cmap=cm_bright, edgecolors="k"
    )
    ## 以及测试点
    ax.scatter(
        X_test[:, 0],
        X_test[:, 1],
        c=y_test,
        cmap=cm_bright,
        edgecolors="k",
        alpha=0.6,
    )
    ax.set_xlim(xx.min(), xx.max())
    ax.set_ylim(yy.min(), yy.max())
    ax.set_xticks(())
    ax.set_yticks(())

    if ds_cnt == 0:
        ax.set_title(name.replace(" + ", "\n"))
    ax.text(
        0.95,
        0.06,
        (f"{score:.2f}").lstrip("0"),
        size=15,
        bbox=dict(boxstyle="round", alpha=0.8, facecolor="white"),
        transform=ax.transAxes,
        horizontalalignment="right",
    )

可视化结果

在这一步中,我们将可视化特征离散化过程的结果。我们将绘制每个分类器和数据集在测试集上的分类准确率。

plt.tight_layout()

## 在图形上方添加总标题
plt.subplots_adjust(top=0.90)
总标题 = [
    "线性分类器",
    "特征离散化与线性分类器",
    "非线性分类器",
]
for i, 总标题 in zip([1, 3, 5], 总标题):
    ax = axes[0, i]
    ax.text(
        1.05,
        1.25,
        总标题,
        transform=ax.transAxes,
        horizontalalignment="center",
        size="x-large",
    )
plt.show()

总结

在本实验中,我们展示了如何使用 scikit-learn 对合成分类数据集进行特征离散化。我们准备了数据,定义了分类器和参数,实现了特征离散化,并可视化了结果。这种预处理技术有助于降低数据集的复杂性并提高线性分类器的性能。然而,使用时应谨慎,并与其他技术结合使用,以避免过拟合。