Явная аппроксимация карты признаков для ядер RBF

Beginner

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

Введение

В этом практическом занятии показано, как приближать карту признаков ядра RBF с использованием RBFSampler и Nystroem для классификации с использованием SVM на наборе данных digits. Сравниваются результаты, полученные с использованием линейного SVM в исходном пространстве, линейного SVM с использованием приближенных отображений и ядра SVM. Показаны времена выполнения и точность для различных количеств Монте-Карло-выборок (в случае RBFSampler, который использует случайные Фурье-признаки) и различных размеров подмножеств обучающего набора (для Nystroem) для приближенного отображения.

Советы по использованию ВМ

После запуска ВМ перейдите в левый верхний угол и переключитесь на вкладку Notebook, чтобы приступить к практике в 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
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)
## вторая ось y для времен выполнения
timescale = plt.subplot(122)

accuracy.plot(sample_sizes, nystroem_scores, label="Nystroem approx. kernel")
timescale.plot(sample_sizes, nystroem_times, "--", label="Nystroem approx. kernel")

accuracy.plot(sample_sizes, fourier_scores, label="Fourier approx. kernel")
timescale.plot(sample_sizes, fourier_times, "--", label="Fourier approx. kernel")

## горизонтальные линии для точного rbf и линейных ядер:
accuracy.plot([sample_sizes[0], sample_sizes[-1]], [linear_svm_score, linear_svm_score], label="linear svm")
timescale.plot([sample_sizes[0], sample_sizes[-1]], [linear_svm_time, linear_svm_time], "--", label="linear 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("Classification accuracy")
timescale.set_title("Training times")
accuracy.set_xlim(sample_sizes[0], sample_sizes[-1])
accuracy.set_xticks(())
accuracy.set_ylim(np.min(fourier_scores), 1)
timescale.set_xlabel("Sampling steps = transformed feature dimension")
accuracy.set_ylabel("Classification accuracy")
timescale.set_ylabel("Training time in seconds")
accuracy.legend(loc="best")
timescale.legend(loc="best")
plt.tight_layout()
plt.show()

Решательные поверхности ядра SVM с RBF и линейного SVM

## визуализируем решательную поверхность, проектированную на первые
## два главных компонента датасета
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 = [
    "SVC с rbf ядром",
    "SVC (линейное ядро)\n с Fourier rbf картой признаков\nn_components=100",
    "SVC (линейное ядро)\n с 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()

Резюме

В этом практическом занятии показано, как приближать карту признаков ядра RBF с использованием RBFSampler и Nystroem для классификации с использованием SVM на наборе данных digits. Сравниваются результаты, полученные с использованием линейного SVM в исходном пространстве, линейного SVM с использованием приближенных отображений и ядра SVM. Показаны времена выполнения и точность для различных количеств Монте-Карло-выборок (в случае RBFSampler, который использует случайные Фурье-признаки) и различных размеров подмножеств обучающего набора (для Nystroem) для приближенного отображения. Наконец, визуализируются решательные поверхности классификаторов, проектированные на первые два главных компонента данных.