2011-07-17 22:22:23 +08:00
|
|
|
import numpy as np
|
2011-08-04 16:01:36 +08:00
|
|
|
import scipy.sparse as sp
|
2011-07-17 22:22:23 +08:00
|
|
|
|
2013-02-18 21:28:41 +08:00
|
|
|
from sklearn.utils.testing import (assert_array_almost_equal, assert_less,
|
|
|
|
|
assert_equal, assert_not_equal,
|
|
|
|
|
assert_raises)
|
2011-07-17 22:22:23 +08:00
|
|
|
|
2012-05-02 10:24:54 +08:00
|
|
|
from sklearn.decomposition import PCA, KernelPCA
|
|
|
|
|
from sklearn.datasets import make_circles
|
|
|
|
|
from sklearn.linear_model import Perceptron
|
2012-08-05 23:23:25 +08:00
|
|
|
from sklearn.pipeline import Pipeline
|
Main Commits - Major
--------------------
* ENH Reogranize classes/fn from grid_search into search.py
* ENH Reogranize classes/fn from cross_validation into split.py
* ENH Reogranize cls/fn from cross_validation/learning_curve into validate.py
* MAINT Merge _check_cv into check_cv inside the model_selection module
* MAINT Update all the imports to point to the model_selection module
* FIX use iter_cv to iterate throught the new style/old style cv objs
* TST Add tests for the new model_selection members
* ENH Wrap the old-style cv obj/iterables instead of using iter_cv
* ENH Use scipy's binomial coefficient function comb for calucation of nCk
* ENH Few enhancements to the split module
* ENH Improve check_cv input validation and docstring
* MAINT _get_test_folds(X, y, labels) --> _get_test_folds(labels)
* TST if 1d arrays for X introduce any errors
* ENH use 1d X arrays for all tests;
* ENH X_10 --> X (global var)
Minor
-----
* ENH _PartitionIterator --> _BaseCrossValidator;
* ENH CVIterator --> CVIterableWrapper
* TST Import the old SKF locally
* FIX/TST Clean up the split module's tests.
* DOC Improve documentation of the cv parameter
* COSMIT consistently hyphenate cross-validation/cross-validator
* TST Calculate n_samples from X
* COSMIT Use separate lines for each import.
* COSMIT cross_validation_generator --> cross_validator
Commits merged manually
-----------------------
* FIX Document the random_state attribute in RandomSearchCV
* MAINT Use check_cv instead of _check_cv
* ENH refactor OVO decision function, use it in SVC for sklearn-like
decision_function shape
* FIX avoid memory cost when sampling from large parameter grids
ENH Major to Minor incremental enhancements to the model_selection
Squashed commit messages - (For reference)
Major
-----
* ENH p --> n_labels
* FIX *ShuffleSplit: all float/invalid type errors at init and int error at split
* FIX make PredefinedSplit accept test_folds in constructor; Cleanup docstrings
* ENH+TST KFold: make rng to be generated at every split call for reproducibility
* FIX/MAINT KFold: make shuffle a public attr
* FIX Make CVIterableWrapper private.
* FIX reuse len_cv instead of recalculating it
* FIX Prevent adding *SearchCV estimators from the old grid_search module
* re-FIX In all_estimators: the sorting to use only the 1st item (name)
To avoid collision between the old and the new GridSearch classes.
* FIX test_validate.py: Use 2D X (1D X is being detected as a single sample)
* MAINT validate.py --> validation.py
* MAINT make the submodules private
* MAINT Support old cv/gs/lc until 0.19
* FIX/MAINT n_splits --> get_n_splits
* FIX/TST test_logistic.py/test_ovr_multinomial_iris:
pass predefined folds as an iterable
* MAINT expose BaseCrossValidator
* Update the model_selection module with changes from master
- From #5161
- - MAINT remove redundant p variable
- - Add check for sparse prediction in cross_val_predict
- From #5201 - DOC improve random_state param doc
- From #5190 - LabelKFold and test
- From #4583 - LabelShuffleSplit and tests
- From #5300 - shuffle the `labels` not the `indxs` in LabelKFold + tests
- From #5378 - Make the GridSearchCV docs more accurate.
- From #5458 - Remove shuffle from LabelKFold
- From #5466(#4270) - Gaussian Process by Jan Metzen
- From #4826 - Move custom error / warnings into sklearn.exception
Minor
-----
* ENH Make the KFold shuffling test stronger
* FIX/DOC Use the higher level model_selection module as ref
* DOC in check_cv "y : array-like, optional"
* DOC a supervised learning problem --> supervised learning problems
* DOC cross-validators --> cross-validation strategies
* DOC Correct Olivier Grisel's name ;)
* MINOR/FIX cv_indices --> kfold
* FIX/DOC Align the 'See also' section of the new KFold, LeaveOneOut
* TST/FIX imports on separate lines
* FIX use __class__ instead of classmethod
* TST/FIX import directly from model_selection
* COSMIT Relocate the random_state documentation
* COSMIT remove pass
* MAINT Remove deprecation warnings from old tests
* FIX correct import at test_split
* FIX/MAINT Move P_sparse, X, y defns to top; rm unused W_sparse, X_sparse
* FIX random state to avoid doctest failure
* TST n_splits and split wrapping of _CVIterableWrapper
* FIX/MAINT Use multilabel indicator matrix directly
* TST/DOC clarify why we conflate classes 0 and 1
* DOC add comment that this was taken from BaseEstimator
* FIX use of labels is not needed in stratified k fold
* Fix cross_validation reference
* Fix the labels param doc
FIX/DOC/MAINT Addressing the review comments by Arnaud and Andy
COSMIT Sort the members alphabetically
COSMIT len_cv --> n_splits
COSMIT Merge 2 if; FIX Use kwargs
DOC Add my name to the authors :D
DOC make labels parameter consistent
FIX Remove hack for boolean indices; + COSMIT idx --> indices; DOC Add Returns
COSMIT preds --> predictions
DOC Add Returns and neatly arrange X, y, labels
FIX idx(s)/ind(s)--> indice(s)
COSMIT Merge if and else to elif
COSMIT n --> n_samples
COSMIT Use bincount only once
COSMIT cls --> class_i / class_i (ith class indices) -->
perm_indices_class_i
FIX/ENH/TST Addressing the final reviews
COSMIT c --> count
FIX/TST make check_cv raise ValueError for string cv value
TST nested cv (gs inside cross_val_score) works for diff cvs
FIX/ENH Raise ValueError when labels is None for label based cvs;
TST if labels is being passed correctly to the cv and that the
ValueError is being propagated to the cross_val_score/predict and grid
search
FIX pass labels to cross_val_score
FIX use make_classification
DOC Add Returns; COSMIT Remove scaffolding
TST add a test to check the _build_repr helper
REVERT the old GS/RS should also be tested by the common tests.
ENH Add a tuple of all/label based CVS
FIX raise VE even at get_n_splits if labels is None
FIX Fabian's comments
PEP8
2015-06-05 03:45:10 +08:00
|
|
|
from sklearn.model_selection import GridSearchCV
|
2012-08-05 23:23:25 +08:00
|
|
|
from sklearn.metrics.pairwise import rbf_kernel
|
2011-07-17 22:22:23 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_kernel_pca():
|
2011-08-03 23:23:44 +08:00
|
|
|
rng = np.random.RandomState(0)
|
|
|
|
|
X_fit = rng.random_sample((5, 4))
|
|
|
|
|
X_pred = rng.random_sample((2, 4))
|
2011-07-17 22:22:23 +08:00
|
|
|
|
2013-03-19 04:48:12 +08:00
|
|
|
def histogram(x, y, **kwargs):
|
2015-03-21 13:22:08 +08:00
|
|
|
# Histogram kernel implemented as a callable.
|
2013-03-19 04:48:12 +08:00
|
|
|
assert_equal(kwargs, {}) # no kernel_params that we didn't ask for
|
|
|
|
|
return np.minimum(x, y).sum()
|
|
|
|
|
|
2011-08-03 13:15:48 +08:00
|
|
|
for eigen_solver in ("auto", "dense", "arpack"):
|
2013-03-19 04:48:12 +08:00
|
|
|
for kernel in ("linear", "rbf", "poly", histogram):
|
|
|
|
|
# histogram kernel produces singular matrix inside linalg.solve
|
|
|
|
|
# XXX use a least-squares approximation?
|
|
|
|
|
inv = not callable(kernel)
|
|
|
|
|
|
2011-08-03 13:15:48 +08:00
|
|
|
# transform fit data
|
|
|
|
|
kpca = KernelPCA(4, kernel=kernel, eigen_solver=eigen_solver,
|
2013-03-19 04:48:12 +08:00
|
|
|
fit_inverse_transform=inv)
|
2011-08-03 13:15:48 +08:00
|
|
|
X_fit_transformed = kpca.fit_transform(X_fit)
|
|
|
|
|
X_fit_transformed2 = kpca.fit(X_fit).transform(X_fit)
|
|
|
|
|
assert_array_almost_equal(np.abs(X_fit_transformed),
|
|
|
|
|
np.abs(X_fit_transformed2))
|
2011-07-17 22:22:23 +08:00
|
|
|
|
2013-02-18 21:28:41 +08:00
|
|
|
# non-regression test: previously, gamma would be 0 by default,
|
|
|
|
|
# forcing all eigenvalues to 0 under the poly kernel
|
2015-09-16 19:10:58 +08:00
|
|
|
assert_not_equal(X_fit_transformed.size, 0)
|
2013-02-18 21:28:41 +08:00
|
|
|
|
2011-08-03 13:15:48 +08:00
|
|
|
# transform new data
|
|
|
|
|
X_pred_transformed = kpca.transform(X_pred)
|
|
|
|
|
assert_equal(X_pred_transformed.shape[1],
|
|
|
|
|
X_fit_transformed.shape[1])
|
2011-07-17 22:22:23 +08:00
|
|
|
|
2011-08-03 13:15:48 +08:00
|
|
|
# inverse transform
|
2013-03-19 04:48:12 +08:00
|
|
|
if inv:
|
|
|
|
|
X_pred2 = kpca.inverse_transform(X_pred_transformed)
|
|
|
|
|
assert_equal(X_pred2.shape, X_pred.shape)
|
2011-07-17 22:22:23 +08:00
|
|
|
|
2011-12-16 00:08:39 +08:00
|
|
|
|
2015-12-15 11:05:00 +08:00
|
|
|
def test_kernel_pca_invalid_parameters():
|
2012-02-17 16:34:43 +08:00
|
|
|
assert_raises(ValueError, KernelPCA, 10, fit_inverse_transform=True,
|
|
|
|
|
kernel='precomputed')
|
|
|
|
|
|
|
|
|
|
|
2015-12-15 11:05:00 +08:00
|
|
|
def test_kernel_pca_consistent_transform():
|
|
|
|
|
# X_fit_ needs to retain the old, unmodified copy of X
|
|
|
|
|
state = np.random.RandomState(0)
|
|
|
|
|
X = state.rand(10, 10)
|
|
|
|
|
kpca = KernelPCA(random_state=state).fit(X)
|
|
|
|
|
transformed1 = kpca.transform(X)
|
|
|
|
|
|
|
|
|
|
X_copy = X.copy()
|
|
|
|
|
X[:, 0] = 666
|
|
|
|
|
transformed2 = kpca.transform(X_copy)
|
|
|
|
|
assert_array_almost_equal(transformed1, transformed2)
|
|
|
|
|
|
|
|
|
|
|
2011-08-04 16:01:36 +08:00
|
|
|
def test_kernel_pca_sparse():
|
|
|
|
|
rng = np.random.RandomState(0)
|
|
|
|
|
X_fit = sp.csr_matrix(rng.random_sample((5, 4)))
|
|
|
|
|
X_pred = sp.csr_matrix(rng.random_sample((2, 4)))
|
|
|
|
|
|
|
|
|
|
for eigen_solver in ("auto", "arpack"):
|
|
|
|
|
for kernel in ("linear", "rbf", "poly"):
|
|
|
|
|
# transform fit data
|
|
|
|
|
kpca = KernelPCA(4, kernel=kernel, eigen_solver=eigen_solver,
|
|
|
|
|
fit_inverse_transform=False)
|
|
|
|
|
X_fit_transformed = kpca.fit_transform(X_fit)
|
|
|
|
|
X_fit_transformed2 = kpca.fit(X_fit).transform(X_fit)
|
|
|
|
|
assert_array_almost_equal(np.abs(X_fit_transformed),
|
|
|
|
|
np.abs(X_fit_transformed2))
|
|
|
|
|
|
|
|
|
|
# transform new data
|
|
|
|
|
X_pred_transformed = kpca.transform(X_pred)
|
|
|
|
|
assert_equal(X_pred_transformed.shape[1],
|
2012-12-22 20:02:50 +08:00
|
|
|
X_fit_transformed.shape[1])
|
2011-08-04 16:01:36 +08:00
|
|
|
|
|
|
|
|
# inverse transform
|
2015-03-21 13:22:08 +08:00
|
|
|
# X_pred2 = kpca.inverse_transform(X_pred_transformed)
|
|
|
|
|
# assert_equal(X_pred2.shape, X_pred.shape)
|
2011-08-04 16:01:36 +08:00
|
|
|
|
2011-07-17 22:22:23 +08:00
|
|
|
|
|
|
|
|
def test_kernel_pca_linear_kernel():
|
2011-08-03 23:23:44 +08:00
|
|
|
rng = np.random.RandomState(0)
|
|
|
|
|
X_fit = rng.random_sample((5, 4))
|
|
|
|
|
X_pred = rng.random_sample((2, 4))
|
2011-07-17 22:22:23 +08:00
|
|
|
|
|
|
|
|
# for a linear kernel, kernel PCA should find the same projection as PCA
|
|
|
|
|
# modulo the sign (direction)
|
2011-08-03 13:15:48 +08:00
|
|
|
# fit only the first four components: fifth is near zero eigenvalue, so
|
|
|
|
|
# can be trimmed due to roundoff error
|
|
|
|
|
assert_array_almost_equal(
|
|
|
|
|
np.abs(KernelPCA(4).fit(X_fit).transform(X_pred)),
|
|
|
|
|
np.abs(PCA(4).fit(X_fit).transform(X_pred)))
|
2011-07-17 22:22:23 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_kernel_pca_n_components():
|
2011-08-03 23:23:44 +08:00
|
|
|
rng = np.random.RandomState(0)
|
|
|
|
|
X_fit = rng.random_sample((5, 4))
|
|
|
|
|
X_pred = rng.random_sample((2, 4))
|
2011-07-17 22:22:23 +08:00
|
|
|
|
2011-08-03 13:15:48 +08:00
|
|
|
for eigen_solver in ("dense", "arpack"):
|
|
|
|
|
for c in [1, 2, 4]:
|
|
|
|
|
kpca = KernelPCA(n_components=c, eigen_solver=eigen_solver)
|
|
|
|
|
shape = kpca.fit(X_fit).transform(X_pred).shape
|
2011-07-17 22:22:23 +08:00
|
|
|
|
2011-08-03 13:15:48 +08:00
|
|
|
assert_equal(shape, (2, c))
|
2011-07-17 22:22:23 +08:00
|
|
|
|
|
|
|
|
|
2013-03-11 00:34:19 +08:00
|
|
|
def test_remove_zero_eig():
|
|
|
|
|
X = np.array([[1 - 1e-30, 1], [1, 1], [1, 1 - 1e-20]])
|
|
|
|
|
|
|
|
|
|
# n_components=None (default) => remove_zero_eig is True
|
|
|
|
|
kpca = KernelPCA()
|
|
|
|
|
Xt = kpca.fit_transform(X)
|
|
|
|
|
assert_equal(Xt.shape, (3, 0))
|
|
|
|
|
|
|
|
|
|
kpca = KernelPCA(n_components=2)
|
|
|
|
|
Xt = kpca.fit_transform(X)
|
|
|
|
|
assert_equal(Xt.shape, (3, 2))
|
|
|
|
|
|
|
|
|
|
kpca = KernelPCA(n_components=2, remove_zero_eig=True)
|
|
|
|
|
Xt = kpca.fit_transform(X)
|
|
|
|
|
assert_equal(Xt.shape, (3, 0))
|
|
|
|
|
|
|
|
|
|
|
2011-07-17 22:22:23 +08:00
|
|
|
def test_kernel_pca_precomputed():
|
2011-08-03 23:23:44 +08:00
|
|
|
rng = np.random.RandomState(0)
|
|
|
|
|
X_fit = rng.random_sample((5, 4))
|
|
|
|
|
X_pred = rng.random_sample((2, 4))
|
2011-07-17 22:22:23 +08:00
|
|
|
|
2011-08-03 13:15:48 +08:00
|
|
|
for eigen_solver in ("dense", "arpack"):
|
|
|
|
|
X_kpca = KernelPCA(4, eigen_solver=eigen_solver).\
|
|
|
|
|
fit(X_fit).transform(X_pred)
|
2012-12-22 20:02:50 +08:00
|
|
|
X_kpca2 = KernelPCA(
|
|
|
|
|
4, eigen_solver=eigen_solver, kernel='precomputed').fit(
|
|
|
|
|
np.dot(X_fit, X_fit.T)).transform(np.dot(X_pred, X_fit.T))
|
|
|
|
|
|
|
|
|
|
X_kpca_train = KernelPCA(
|
|
|
|
|
4, eigen_solver=eigen_solver,
|
|
|
|
|
kernel='precomputed').fit_transform(np.dot(X_fit, X_fit.T))
|
|
|
|
|
X_kpca_train2 = KernelPCA(
|
|
|
|
|
4, eigen_solver=eigen_solver, kernel='precomputed').fit(
|
|
|
|
|
np.dot(X_fit, X_fit.T)).transform(np.dot(X_fit, X_fit.T))
|
2012-04-30 00:51:43 +08:00
|
|
|
|
2011-08-03 13:15:48 +08:00
|
|
|
assert_array_almost_equal(np.abs(X_kpca),
|
|
|
|
|
np.abs(X_kpca2))
|
2011-07-17 22:22:23 +08:00
|
|
|
|
2012-04-30 00:51:43 +08:00
|
|
|
assert_array_almost_equal(np.abs(X_kpca_train),
|
|
|
|
|
np.abs(X_kpca_train2))
|
|
|
|
|
|
2011-07-17 22:22:23 +08:00
|
|
|
|
|
|
|
|
def test_kernel_pca_invalid_kernel():
|
2011-08-03 23:23:44 +08:00
|
|
|
rng = np.random.RandomState(0)
|
|
|
|
|
X_fit = rng.random_sample((2, 4))
|
2011-07-17 22:22:23 +08:00
|
|
|
kpca = KernelPCA(kernel="tototiti")
|
2011-08-03 13:15:48 +08:00
|
|
|
assert_raises(ValueError, kpca.fit, X_fit)
|
|
|
|
|
|
|
|
|
|
|
2012-08-05 23:23:25 +08:00
|
|
|
def test_gridsearch_pipeline():
|
2012-08-05 23:38:31 +08:00
|
|
|
# Test if we can do a grid-search to find parameters to separate
|
|
|
|
|
# circles with a perceptron model.
|
2012-08-05 23:23:25 +08:00
|
|
|
X, y = make_circles(n_samples=400, factor=.3, noise=.05,
|
|
|
|
|
random_state=0)
|
|
|
|
|
kpca = KernelPCA(kernel="rbf", n_components=2)
|
|
|
|
|
pipeline = Pipeline([("kernel_pca", kpca), ("Perceptron", Perceptron())])
|
2012-10-29 04:40:21 +08:00
|
|
|
param_grid = dict(kernel_pca__gamma=2. ** np.arange(-2, 2))
|
2012-08-05 23:23:25 +08:00
|
|
|
grid_search = GridSearchCV(pipeline, cv=3, param_grid=param_grid)
|
|
|
|
|
grid_search.fit(X, y)
|
|
|
|
|
assert_equal(grid_search.best_score_, 1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_gridsearch_pipeline_precomputed():
|
2012-08-05 23:38:31 +08:00
|
|
|
# Test if we can do a grid-search to find parameters to separate
|
|
|
|
|
# circles with a perceptron model using a precomputed kernel.
|
2012-08-05 23:23:25 +08:00
|
|
|
X, y = make_circles(n_samples=400, factor=.3, noise=.05,
|
|
|
|
|
random_state=0)
|
|
|
|
|
kpca = KernelPCA(kernel="precomputed", n_components=2)
|
|
|
|
|
pipeline = Pipeline([("kernel_pca", kpca), ("Perceptron", Perceptron())])
|
2012-09-07 03:41:23 +08:00
|
|
|
param_grid = dict(Perceptron__n_iter=np.arange(1, 5))
|
2012-08-05 23:23:25 +08:00
|
|
|
grid_search = GridSearchCV(pipeline, cv=3, param_grid=param_grid)
|
2012-10-29 04:40:21 +08:00
|
|
|
X_kernel = rbf_kernel(X, gamma=2.)
|
2012-08-05 23:23:25 +08:00
|
|
|
grid_search.fit(X_kernel, y)
|
|
|
|
|
assert_equal(grid_search.best_score_, 1)
|
|
|
|
|
|
|
|
|
|
|
2012-05-02 10:24:54 +08:00
|
|
|
def test_nested_circles():
|
2015-03-21 13:22:08 +08:00
|
|
|
# Test the linear separability of the first 2D KPCA transform
|
2012-05-02 10:24:54 +08:00
|
|
|
X, y = make_circles(n_samples=400, factor=.3, noise=.05,
|
|
|
|
|
random_state=0)
|
|
|
|
|
|
|
|
|
|
# 2D nested circles are not linearly separable
|
|
|
|
|
train_score = Perceptron().fit(X, y).score(X, y)
|
2012-05-06 21:28:59 +08:00
|
|
|
assert_less(train_score, 0.8)
|
2012-05-02 10:24:54 +08:00
|
|
|
|
|
|
|
|
# Project the circles data into the first 2 components of a RBF Kernel
|
|
|
|
|
# PCA model.
|
2012-05-04 20:38:06 +08:00
|
|
|
# Note that the gamma value is data dependent. If this test breaks
|
2012-05-02 10:24:54 +08:00
|
|
|
# and the gamma value has to be updated, the Kernel PCA example will
|
|
|
|
|
# have to be updated too.
|
|
|
|
|
kpca = KernelPCA(kernel="rbf", n_components=2,
|
2012-10-29 04:40:21 +08:00
|
|
|
fit_inverse_transform=True, gamma=2.)
|
2012-05-02 10:24:54 +08:00
|
|
|
X_kpca = kpca.fit_transform(X)
|
|
|
|
|
|
|
|
|
|
# The data is perfectly linearly separable in that space
|
|
|
|
|
train_score = Perceptron().fit(X_kpca, y).score(X_kpca, y)
|
|
|
|
|
assert_equal(train_score, 1.0)
|