2011-11-13 18:58:43 +08:00
|
|
|
"""
|
2011-11-14 07:00:08 +08:00
|
|
|
==================================================
|
|
|
|
|
Explicit feature map approximation for RBF kernels
|
|
|
|
|
==================================================
|
2011-11-13 18:58:43 +08:00
|
|
|
|
2011-12-20 21:12:20 +08:00
|
|
|
.. currentmodule:: sklearn.kernel_approximation
|
|
|
|
|
|
2012-11-26 05:00:11 +08:00
|
|
|
An example shows how to use :class:`RBFSampler` and :class:`Nystrom` to
|
|
|
|
|
appoximate the feature map of an RBF kernel for classification with an SVM on
|
|
|
|
|
the digits dataset. Results using a linear SVM in the original space, a linear
|
|
|
|
|
SVM using the approximate mappings and using a kernelized SVM are compared.
|
|
|
|
|
Timings and accuracy for varying amounts of Monte Carlo samplings (in the case
|
|
|
|
|
of :class:`RBFSampler`, which uses random Fourier features) and different sized
|
|
|
|
|
subsets of the training set (for :class:`Nystroem)` for the approximate mapping
|
|
|
|
|
are shown.
|
2011-12-19 23:46:38 +08:00
|
|
|
|
|
|
|
|
Sampling more dimensions clearly leads to better classification results, but
|
|
|
|
|
comes at a greater cost. This means there is a tradeoff between runtime and
|
2012-11-26 05:00:11 +08:00
|
|
|
accuracy, given by the parameter n_components. Note that solving the Linear
|
2011-12-20 00:35:25 +08:00
|
|
|
SVM and also the approximate kernel SVM could be greatly accelerated by using
|
2011-12-20 21:14:44 +08:00
|
|
|
stochastic gradient descent via :class:`sklearn.linear_model.SGDClassifier`.
|
|
|
|
|
This is not easily possible for the case of the kernelized SVM.
|
2011-12-20 01:08:03 +08:00
|
|
|
|
2011-12-20 03:11:11 +08:00
|
|
|
The second plot visualized the decision surfaces of the RBF kernel SVM and
|
2012-11-26 05:00:11 +08:00
|
|
|
the linear SVM with approximate kernel maps.
|
2011-12-20 03:11:11 +08:00
|
|
|
The plot shows decision surfaces of the classifiers projected onto
|
|
|
|
|
the first two principal components of the data. This visualization should
|
|
|
|
|
be taken with a grain of salt since it is just an interesting slice through
|
|
|
|
|
the decision surface in 64 dimensions. In particular note that
|
|
|
|
|
a datapoint (represented as a dot) does not necessarily be classified
|
|
|
|
|
into the region it is lying in, since it will not lie on the plane
|
|
|
|
|
that the first two principal components span.
|
|
|
|
|
|
2012-11-26 05:00:11 +08:00
|
|
|
The usage of :class:`RBFSampler` and :class:`Nystroem` is described in detail
|
|
|
|
|
in :ref:`kernel_approximation`.
|
2011-12-20 01:08:03 +08:00
|
|
|
|
2011-11-13 18:58:43 +08:00
|
|
|
"""
|
|
|
|
|
print __doc__
|
|
|
|
|
|
|
|
|
|
# Author: Gael Varoquaux <gael dot varoquaux at normalesup dot org>
|
2012-11-26 05:00:11 +08:00
|
|
|
# Andreas Mueller <amueller@ais.uni-bonn.de>
|
2011-11-13 18:58:43 +08:00
|
|
|
# License: Simplified BSD
|
|
|
|
|
|
|
|
|
|
# Standard scientific Python imports
|
|
|
|
|
import pylab as pl
|
2011-11-14 07:00:08 +08:00
|
|
|
import numpy as np
|
2011-12-20 00:35:25 +08:00
|
|
|
from time import time
|
2011-11-13 18:58:43 +08:00
|
|
|
|
|
|
|
|
# Import datasets, classifiers and performance metrics
|
2011-11-14 18:59:15 +08:00
|
|
|
from sklearn import datasets, svm, pipeline
|
2012-11-26 04:48:41 +08:00
|
|
|
from sklearn.kernel_approximation import (RBFSampler,
|
2012-11-26 05:00:11 +08:00
|
|
|
Nystroem)
|
2011-12-20 03:11:11 +08:00
|
|
|
from sklearn.decomposition import PCA
|
2011-11-13 18:58:43 +08:00
|
|
|
|
|
|
|
|
# The digits dataset
|
2011-12-20 03:11:11 +08:00
|
|
|
digits = datasets.load_digits(n_class=9)
|
2011-11-13 18:58:43 +08:00
|
|
|
|
|
|
|
|
# To apply an classifier on this data, we need to flatten the image, to
|
|
|
|
|
# turn the data in a (samples, feature) matrix:
|
2011-12-20 00:35:25 +08:00
|
|
|
n_samples = len(digits.data)
|
|
|
|
|
data = digits.data / 16.
|
2011-12-20 03:11:11 +08:00
|
|
|
data -= data.mean(axis=0)
|
2011-11-13 18:58:43 +08:00
|
|
|
|
2011-11-14 07:00:08 +08:00
|
|
|
# We learn the digits on the first half of the digits
|
2011-12-19 23:46:38 +08:00
|
|
|
data_train, targets_train = data[:n_samples / 2], digits.target[:n_samples / 2]
|
2011-11-14 07:00:08 +08:00
|
|
|
|
2011-12-20 00:35:25 +08:00
|
|
|
|
2011-11-14 07:00:08 +08:00
|
|
|
# Now predict the value of the digit on the second half:
|
2011-12-19 23:46:38 +08:00
|
|
|
data_test, targets_test = data[n_samples / 2:], digits.target[n_samples / 2:]
|
2011-12-20 00:35:25 +08:00
|
|
|
#data_test = scaler.transform(data_test)
|
2011-11-14 07:00:08 +08:00
|
|
|
|
2011-11-13 18:58:43 +08:00
|
|
|
# Create a classifier: a support vector classifier
|
2011-12-20 00:35:25 +08:00
|
|
|
kernel_svm = svm.SVC(gamma=.2)
|
2011-11-13 18:58:43 +08:00
|
|
|
linear_svm = svm.LinearSVC()
|
|
|
|
|
|
|
|
|
|
# create pipeline from kernel approximation
|
|
|
|
|
# and linear svm
|
2012-11-26 04:48:41 +08:00
|
|
|
feature_map_fourier = RBFSampler(gamma=.2, random_state=1)
|
2012-11-26 05:00:11 +08:00
|
|
|
feature_map_nystroem = Nystroem(gamma=.2, random_state=1)
|
2012-11-26 04:48:41 +08:00
|
|
|
fourier_approx_svm = pipeline.Pipeline([("feature_map", feature_map_fourier),
|
|
|
|
|
("svm", svm.LinearSVC())])
|
|
|
|
|
|
|
|
|
|
nystroem_approx_svm = pipeline.Pipeline([("feature_map", feature_map_nystroem),
|
|
|
|
|
("svm", svm.LinearSVC())])
|
2011-11-13 18:58:43 +08:00
|
|
|
|
2011-11-14 07:00:08 +08:00
|
|
|
# fit and predict using linear and kernel svm:
|
2011-12-20 00:35:25 +08:00
|
|
|
|
|
|
|
|
kernel_svm_time = time()
|
2011-11-14 07:00:08 +08:00
|
|
|
kernel_svm.fit(data_train, targets_train)
|
|
|
|
|
kernel_svm_score = kernel_svm.score(data_test, targets_test)
|
2011-12-20 00:35:25 +08:00
|
|
|
kernel_svm_time = time() - kernel_svm_time
|
2011-11-14 07:00:08 +08:00
|
|
|
|
2011-12-20 00:35:25 +08:00
|
|
|
linear_svm_time = time()
|
2011-11-14 07:00:08 +08:00
|
|
|
linear_svm.fit(data_train, targets_train)
|
|
|
|
|
linear_svm_score = linear_svm.score(data_test, targets_test)
|
2011-12-20 00:35:25 +08:00
|
|
|
linear_svm_time = time() - linear_svm_time
|
2011-11-13 18:58:43 +08:00
|
|
|
|
2012-11-26 04:48:41 +08:00
|
|
|
sample_sizes = 30 * np.arange(1, 10)
|
|
|
|
|
fourier_scores = []
|
|
|
|
|
nystroem_scores = []
|
|
|
|
|
fourier_times = []
|
|
|
|
|
nystroem_times = []
|
2011-12-20 00:35:25 +08:00
|
|
|
|
2011-11-14 07:00:08 +08:00
|
|
|
for D in sample_sizes:
|
2012-11-26 04:48:41 +08:00
|
|
|
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)
|
2011-11-13 18:58:43 +08:00
|
|
|
|
2011-11-14 18:59:15 +08:00
|
|
|
# plot the results:
|
2012-11-26 04:48:41 +08:00
|
|
|
pl.figure(figsize=(8, 8))
|
2011-12-20 21:12:20 +08:00
|
|
|
accuracy = pl.subplot(211)
|
2011-12-20 00:35:25 +08:00
|
|
|
# second y axis for timeings
|
2011-12-20 21:12:20 +08:00
|
|
|
timescale = pl.subplot(212)
|
2011-12-20 00:35:25 +08:00
|
|
|
|
2012-11-26 05:00:11 +08:00
|
|
|
accuracy.plot(sample_sizes, nystroem_scores, label="Nystroem approx. kernel")
|
2012-11-26 04:48:41 +08:00
|
|
|
timescale.plot(sample_sizes, nystroem_times, '--',
|
2012-11-26 05:00:11 +08:00
|
|
|
label='Nystroem approx. kernel')
|
2012-11-26 04:48:41 +08:00
|
|
|
|
|
|
|
|
accuracy.plot(sample_sizes, fourier_scores, label="Fourier approx. kernel")
|
|
|
|
|
timescale.plot(sample_sizes, fourier_times, '--',
|
|
|
|
|
label='Fourier approx. kernel')
|
2011-11-14 18:59:15 +08:00
|
|
|
|
|
|
|
|
# horizontal lines for exact rbf and linear kernels:
|
2011-12-20 00:35:25 +08:00
|
|
|
accuracy.plot([sample_sizes[0], sample_sizes[-1]], [linear_svm_score,
|
2011-12-19 23:46:38 +08:00
|
|
|
linear_svm_score], label="linear svm")
|
2011-12-20 00:35:25 +08:00
|
|
|
timescale.plot([sample_sizes[0], sample_sizes[-1]], [linear_svm_time,
|
2011-12-20 21:12:20 +08:00
|
|
|
linear_svm_time], '--', label='linear svm')
|
2011-12-20 00:35:25 +08:00
|
|
|
|
|
|
|
|
accuracy.plot([sample_sizes[0], sample_sizes[-1]], [kernel_svm_score,
|
2011-12-19 23:46:38 +08:00
|
|
|
kernel_svm_score], label="rbf svm")
|
2011-12-20 00:35:25 +08:00
|
|
|
timescale.plot([sample_sizes[0], sample_sizes[-1]], [kernel_svm_time,
|
2011-12-20 21:12:20 +08:00
|
|
|
kernel_svm_time], '--', label='rbf svm')
|
2011-11-14 18:59:15 +08:00
|
|
|
|
|
|
|
|
# vertical line for dataset dimensionality = 64
|
2011-12-20 21:12:20 +08:00
|
|
|
accuracy.plot([64, 64], [0.7, 1], label="n_features")
|
2011-11-14 18:59:15 +08:00
|
|
|
|
|
|
|
|
# legends and labels
|
2011-12-20 21:12:20 +08:00
|
|
|
accuracy.set_title("Classification accuracy")
|
|
|
|
|
timescale.set_title("Training times")
|
2011-12-20 00:35:25 +08:00
|
|
|
accuracy.set_xlim(sample_sizes[0], sample_sizes[-1])
|
2011-12-20 21:12:20 +08:00
|
|
|
accuracy.set_xticks(())
|
2012-11-26 04:48:41 +08:00
|
|
|
accuracy.set_ylim(np.min(fourier_scores), 1)
|
2011-12-20 21:12:20 +08:00
|
|
|
timescale.set_xlabel("Sampling steps = transformed feature dimension")
|
2011-12-20 00:35:25 +08:00
|
|
|
accuracy.set_ylabel("Classification accuracy")
|
2011-12-20 21:12:20 +08:00
|
|
|
timescale.set_ylabel("Training time in seconds")
|
2011-12-20 00:35:25 +08:00
|
|
|
accuracy.legend(loc='best')
|
2011-12-20 21:12:20 +08:00
|
|
|
timescale.legend(loc='best')
|
2011-12-20 03:11:11 +08:00
|
|
|
|
|
|
|
|
# visualize the decision surface, projected down to the first
|
|
|
|
|
# two principal components of the dataset
|
|
|
|
|
pca = PCA(n_components=8).fit(data_train)
|
|
|
|
|
|
|
|
|
|
X = pca.transform(data_train)
|
|
|
|
|
|
|
|
|
|
# Gemerate grid along first two principal components
|
|
|
|
|
multiples = np.arange(-2, 2, 0.1)
|
|
|
|
|
# steps along first component
|
|
|
|
|
first = multiples[:, np.newaxis] * pca.components_[0, :]
|
|
|
|
|
# steps along second component
|
|
|
|
|
second = multiples[:, np.newaxis] * pca.components_[1, :]
|
|
|
|
|
# combine
|
|
|
|
|
grid = first[np.newaxis, :, :] + second[:, np.newaxis, :]
|
|
|
|
|
flat_grid = grid.reshape(-1, data.shape[1])
|
|
|
|
|
|
|
|
|
|
# title for the plots
|
|
|
|
|
titles = ['SVC with rbf kernel',
|
2012-11-26 04:48:41 +08:00
|
|
|
'SVC (linear kernel)\n with Fourier rbf feature map\n'
|
|
|
|
|
'n_components=100',
|
2012-11-26 05:00:11 +08:00
|
|
|
'SVC (linear kernel)\n with Nystroem rbf feature map\n'
|
2012-11-26 04:48:41 +08:00
|
|
|
'n_components=100']
|
2011-12-20 03:11:11 +08:00
|
|
|
|
2012-11-26 04:48:41 +08:00
|
|
|
pl.tight_layout()
|
2011-12-20 21:14:44 +08:00
|
|
|
pl.figure(figsize=(12, 5))
|
2011-12-20 03:11:11 +08:00
|
|
|
|
|
|
|
|
# predict and plot
|
2012-11-26 04:48:41 +08:00
|
|
|
for i, clf in enumerate((kernel_svm, nystroem_approx_svm,
|
|
|
|
|
fourier_approx_svm)):
|
2011-12-20 03:11:11 +08:00
|
|
|
# Plot the decision boundary. For that, we will asign a color to each
|
|
|
|
|
# point in the mesh [x_min, m_max]x[y_min, y_max].
|
2012-11-26 04:48:41 +08:00
|
|
|
pl.subplot(1, 3, i + 1)
|
2011-12-20 03:11:11 +08:00
|
|
|
Z = clf.predict(flat_grid)
|
|
|
|
|
|
|
|
|
|
# Put the result into a color plot
|
|
|
|
|
Z = Z.reshape(grid.shape[:-1])
|
2012-05-06 02:24:55 +08:00
|
|
|
pl.contourf(multiples, multiples, Z, cmap=pl.cm.Paired)
|
2011-12-20 03:11:11 +08:00
|
|
|
pl.axis('off')
|
|
|
|
|
|
|
|
|
|
# Plot also the training points
|
2012-05-06 02:24:55 +08:00
|
|
|
pl.scatter(X[:, 0], X[:, 1], c=targets_train, cmap=pl.cm.Paired)
|
2011-12-20 03:11:11 +08:00
|
|
|
|
|
|
|
|
pl.title(titles[i])
|
2012-11-26 04:48:41 +08:00
|
|
|
pl.tight_layout()
|
2011-11-14 07:00:08 +08:00
|
|
|
pl.show()
|