RBF 核的显式特征图近似

Beginner

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

简介

本实验展示了如何使用 RBFSampler 和 Nystroem 来近似 RBF 核的特征图,以便在数字数据集上使用支持向量机(SVM)进行分类。我们比较了在原始空间中使用线性 SVM、使用近似映射的线性 SVM 以及使用核化 SVM 的结果。展示了在近似映射中,不同数量的蒙特卡罗采样(对于使用随机傅里叶特征的 RBFSampler)和训练集的不同大小子集(对于 Nystroem)的时间和准确性。

虚拟机使用提示

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

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

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

Python 包和数据集导入、加载数据集

## 标准的科学 Python 导入
import matplotlib.pyplot as plt
import numpy as np
from time import time

## 导入数据集、分类器和性能指标
from sklearn import datasets, svm, pipeline
from sklearn.kernel_approximation import RBFSampler, Nystroem
from sklearn.decomposition import PCA

## 数字数据集
digits = datasets.load_digits(n_class=9)

时间和准确率绘图

## 要在此数据上应用分类器,我们需要将图像展平,
## 把数据转换为(样本数,特征数)矩阵:
n_samples = len(digits.data)
data = digits.data / 16.0
data -= data.mean(axis=0)

## 我们在前半部分数字上学习数字特征
data_train, targets_train = (data[: n_samples // 2], digits.target[: n_samples // 2])

## 现在预测后半部分数字的值:
data_test, targets_test = (data[n_samples // 2 :], digits.target[n_samples // 2 :])

## 创建一个分类器:一个支持向量分类器
kernel_svm = svm.SVC(gamma=0.2)
linear_svm = svm.LinearSVC(dual="auto")

## 从核近似和线性支持向量机创建管道
feature_map_fourier = RBFSampler(gamma=0.2, random_state=1)
feature_map_nystroem = Nystroem(gamma=0.2, random_state=1)

fourier_approx_svm = pipeline.Pipeline([
  ("feature_map", feature_map_fourier),
  ("svm", svm.LinearSVC(dual="auto"))
])

nystroem_approx_svm = pipeline.Pipeline([
  ("feature_map", feature_map_nystroem),
  ("svm", svm.LinearSVC(dual="auto"))
])

## 使用线性和核支持向量机进行拟合和预测:
kernel_svm_time = time()
kernel_svm.fit(data_train, targets_train)
kernel_svm_score = kernel_svm.score(data_test, targets_test)
kernel_svm_time = time() - kernel_svm_time

linear_svm_time = time()
linear_svm.fit(data_train, targets_train)
linear_svm_score = linear_svm.score(data_test, targets_test)
linear_svm_time = time() - linear_svm_time

sample_sizes = 30 * np.arange(1, 10)
fourier_scores = []
nystroem_scores = []
fourier_times = []
nystroem_times = []

for D in sample_sizes:
  fourier_approx_svm.set_params(feature_map__n_components=D)
  nystroem_approx_svm.set_params(feature_map__n_components=D)

  start = time()
  nystroem_approx_svm.fit(data_train, targets_train)
  nystroem_times.append(time() - start)

  start = time()
  fourier_approx_svm.fit(data_train, targets_train)
  fourier_times.append(time() - start)

  fourier_score = fourier_approx_svm.score(data_test, targets_test)
  nystroem_score = nystroem_approx_svm.score(data_test, targets_test)
  nystroem_scores.append(nystroem_score)
  fourier_scores.append(fourier_score)

## 绘制结果:
plt.figure(figsize=(16, 4))
accuracy = plt.subplot(121)
## 第二个 y 轴用于时间
timescale = plt.subplot(122)

accuracy.plot(sample_sizes, nystroem_scores, label="Nystroem 近似核")
timescale.plot(sample_sizes, nystroem_times, "--", label="Nystroem 近似核")

accuracy.plot(sample_sizes, fourier_scores, label="傅里叶近似核")
timescale.plot(sample_sizes, fourier_times, "--", label="傅里叶近似核")

## 精确的 rbf 和线性核的水平线:
accuracy.plot([sample_sizes[0], sample_sizes[-1]], [linear_svm_score, linear_svm_score], label="线性支持向量机")
timescale.plot([sample_sizes[0], sample_sizes[-1]], [linear_svm_time, linear_svm_time], "--", label="线性支持向量机")

accuracy.plot([sample_sizes[0], sample_sizes[-1]], [kernel_svm_score, kernel_svm_score], label="rbf 支持向量机")
timescale.plot([sample_sizes[0], sample_sizes[-1]], [kernel_svm_time, kernel_svm_time], "--", label="rbf 支持向量机")

## 数据集维度 = 64 的垂直线
accuracy.plot([64, 64], [0.7, 1], label="n_features")

## 图例和标签
accuracy.set_title("分类准确率")
timescale.set_title("训练时间")
accuracy.set_xlim(sample_sizes[0], sample_sizes[-1])
accuracy.set_xticks(())
accuracy.set_ylim(np.min(fourier_scores), 1)
timescale.set_xlabel("采样步数 = 变换后的特征维度")
accuracy.set_ylabel("分类准确率")
timescale.set_ylabel("训练时间(秒)")
accuracy.legend(loc="最佳")
timescale.legend(loc="最佳")
plt.tight_layout()
plt.show()

RBF 核支持向量机和线性支持向量机的决策面

## 可视化决策面,投影到数据集的前两个主成分上
pca = PCA(n_components=8).fit(data_train)

X = pca.transform(data_train)

## 沿着前两个主成分生成网格
multiples = np.arange(-2, 2, 0.1)
## 沿着第一个成分的步长
first = multiples[:, np.newaxis] * pca.components_[0, :]
## 沿着第二个成分的步长
second = multiples[:, np.newaxis] * pca.components_[1, :]
## 合并
grid = first[np.newaxis, :, :] + second[:, np.newaxis, :]
flat_grid = grid.reshape(-1, data.shape[1])

## 绘图标题
titles = [
    "带 rbf 核的支持向量分类器",
    "带傅里叶 rbf 特征映射的支持向量分类器(线性核)\nn_components=100",
    "带 Nystroem rbf 特征映射的支持向量分类器(线性核)\nn_components=100",
]

plt.figure(figsize=(18, 7.5))
plt.rcParams.update({"font.size": 14})
## 预测并绘图
for i, clf in enumerate((kernel_svm, nystroem_approx_svm, fourier_approx_svm)):
    ## 绘制决策边界。为此,我们将为网格 [x_min, x_max]x[y_min, y_max] 中的每个点分配一种颜色。
    plt.subplot(1, 3, i + 1)
    Z = clf.predict(flat_grid)

    ## 将结果放入颜色图中
    Z = Z.reshape(grid.shape[:-1])
    levels = np.arange(10)
    lv_eps = 0.01  ## 调整从计算出的等高线级别到颜色的映射。
    plt.contourf(
        multiples,
        multiples,
        Z,
        levels=levels - lv_eps,
        cmap=plt.cm.tab10,
        vmin=0,
        vmax=10,
        alpha=0.7,
    )
    plt.axis("off")

    ## 也绘制训练点
    plt.scatter(
        X[:, 0],
        X[:, 1],
        c=targets_train,
        cmap=plt.cm.tab10,
        edgecolors=(0, 0, 0),
        vmin=0,
        vmax=10,
    )

    plt.title(titles[i])
plt.tight_layout()
plt.show()

总结

本实验展示了如何使用 RBFSampler 和 Nystroem 来近似 RBF 核的特征图,以便在数字数据集上使用支持向量机(SVM)进行分类。我们比较了在原始空间中使用线性 SVM、使用近似映射的线性 SVM 以及使用核化 SVM 的结果。展示了在近似映射中,不同数量的蒙特卡罗采样(对于使用随机傅里叶特征的 RBFSampler)和训练集的不同大小子集(对于 Nystroem)的时间和准确性。最后,将分类器的决策面投影到数据的前两个主成分上进行了可视化。