RBF カーネルの明示的な特徴マップ近似

Beginner

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

はじめに

この実験では、RBFSampler と Nystroem を使って RBF カーネルの特徴マップを近似することを示します。これにより、手書き数字データセットで SVM を使った分類において、RBF カーネルの特徴マップを近似します。元の空間での線形 SVM、近似マッピングを使った線形 SVM、およびカーネル化 SVM の結果を比較します。近似マッピングについて、さまざまな数のモンテカルロサンプリング(ランダムフーリエ特徴を使う RBFSampler の場合)と訓練セットの異なるサイズのサブセット(Nystroem の場合)に対する計測時間と精度を示します。

VM のヒント

VM の起動が完了したら、左上隅をクリックしてノートブックタブに切り替え、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")

## カーネル近似と線形 SVM からパイプラインを作成します
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"))
])

## 線形およびカーネル SVM を使って学習と予測を行います:
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)
## 計測時間用の 2 番目の 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="Fourier 近似カーネル")
timescale.plot(sample_sizes, fourier_times, "--", label="Fourier 近似カーネル")

## 正確な RBF と線形カーネル用の水平線:
accuracy.plot([sample_sizes[0], sample_sizes[-1]], [linear_svm_score, linear_svm_score], label="線形 SVM")
timescale.plot([sample_sizes[0], sample_sizes[-1]], [linear_svm_time, linear_svm_time], "--", label="線形 SVM")

accuracy.plot([sample_sizes[0], sample_sizes[-1]], [kernel_svm_score, kernel_svm_score], label="RBF SVM")
timescale.plot([sample_sizes[0], sample_sizes[-1]], [kernel_svm_time, kernel_svm_time], "--", label="RBF SVM")

## データセット次元 = 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="best")
timescale.legend(loc="best")
plt.tight_layout()
plt.show()

RBF カーネル SVM と線形 SVM の決定面

## 決定面を可視化します。データセットの最初の 2 つの主成分に射影します
pca = PCA(n_components=8).fit(data_train)

X = pca.transform(data_train)

## 最初の 2 つの主成分に沿ってグリッドを生成します
multiples = np.arange(-2, 2, 0.1)
## 最初の成分に沿ったステップ
first = multiples[:, np.newaxis] * pca.components_[0, :]
## 2 番目の成分に沿ったステップ
second = multiples[:, np.newaxis] * pca.components_[1, :]
## 結合
grid = first[np.newaxis, :, :] + second[:, np.newaxis, :]
flat_grid = grid.reshape(-1, data.shape[1])

## プロットのタイトル
titles = [
    "rbf カーネルを持つ SVC",
    "Fourier rbf 特徴マップを持つ SVC(線形カーネル)\nn_components=100",
    "Nystroem rbf 特徴マップを持つ SVC(線形カーネル)\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 を使った分類において、RBF カーネルの特徴マップを近似します。元の空間での線形 SVM、近似マッピングを使った線形 SVM、およびカーネル化 SVM の結果を比較しました。近似マッピングについて、さまざまな数のモンテカルロサンプリング(ランダムフーリエ特徴を使う RBFSampler の場合)と訓練セットの異なるサイズのサブセット(Nystroem の場合)に対する計測時間と精度を示しました。最後に、分類器の決定面をデータの最初の 2 つの主成分に射影して可視化しました。