2012-03-12 17:19:01 +08:00
|
|
|
.. _model_selection_tut:
|
|
|
|
|
|
2011-12-18 19:39:53 +08:00
|
|
|
============================================================
|
|
|
|
|
Model selection: choosing estimators and their parameters
|
|
|
|
|
============================================================
|
|
|
|
|
|
|
|
|
|
Score, and cross-validated scores
|
|
|
|
|
==================================
|
|
|
|
|
|
2014-07-06 19:33:20 +08:00
|
|
|
As we have seen, every estimator exposes a ``score`` method that can judge
|
2011-12-18 19:39:53 +08:00
|
|
|
the quality of the fit (or the prediction) on new data. **Bigger is
|
|
|
|
|
better**.
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
2012-02-28 23:36:37 +08:00
|
|
|
>>> from sklearn import datasets, svm
|
2019-07-14 23:10:54 +08:00
|
|
|
>>> X_digits, y_digits = datasets.load_digits(return_X_y=True)
|
2012-03-01 00:21:42 +08:00
|
|
|
>>> svc = svm.SVC(C=1, kernel='linear')
|
2011-12-18 19:39:53 +08:00
|
|
|
>>> svc.fit(X_digits[:-100], y_digits[:-100]).score(X_digits[-100:], y_digits[-100:])
|
2018-03-27 13:44:40 +08:00
|
|
|
0.98
|
2011-12-18 19:39:53 +08:00
|
|
|
|
|
|
|
|
To get a better measure of prediction accuracy (which we can use as a
|
|
|
|
|
proxy for goodness of fit of the model), we can successively split the
|
|
|
|
|
data in *folds* that we use for training and testing::
|
|
|
|
|
|
|
|
|
|
>>> import numpy as np
|
|
|
|
|
>>> X_folds = np.array_split(X_digits, 3)
|
|
|
|
|
>>> y_folds = np.array_split(y_digits, 3)
|
|
|
|
|
>>> scores = list()
|
|
|
|
|
>>> for k in range(3):
|
|
|
|
|
... # We use 'list' to copy, in order to 'pop' later on
|
|
|
|
|
... X_train = list(X_folds)
|
2018-10-05 22:51:42 +08:00
|
|
|
... X_test = X_train.pop(k)
|
2011-12-18 19:39:53 +08:00
|
|
|
... X_train = np.concatenate(X_train)
|
|
|
|
|
... y_train = list(y_folds)
|
2018-10-05 22:51:42 +08:00
|
|
|
... y_test = y_train.pop(k)
|
2011-12-18 19:39:53 +08:00
|
|
|
... y_train = np.concatenate(y_train)
|
|
|
|
|
... scores.append(svc.fit(X_train, y_train).score(X_test, y_test))
|
2019-06-01 16:53:45 +08:00
|
|
|
>>> print(scores)
|
2018-03-27 13:44:40 +08:00
|
|
|
[0.934..., 0.956..., 0.939...]
|
2011-12-18 19:39:53 +08:00
|
|
|
|
2015-09-11 02:26:39 +08:00
|
|
|
.. currentmodule:: sklearn.model_selection
|
2011-12-18 19:39:53 +08:00
|
|
|
|
2015-09-11 02:26:39 +08:00
|
|
|
This is called a :class:`KFold` cross-validation.
|
2011-12-18 19:39:53 +08:00
|
|
|
|
2012-03-12 17:19:01 +08:00
|
|
|
.. _cv_generators_tut:
|
|
|
|
|
|
2011-12-18 19:39:53 +08:00
|
|
|
Cross-validation generators
|
|
|
|
|
=============================
|
|
|
|
|
|
2015-09-11 02:26:39 +08:00
|
|
|
Scikit-learn has a collection of classes which can be used to generate lists of
|
|
|
|
|
train/test indices for popular cross-validation strategies.
|
2012-03-08 22:21:53 +08:00
|
|
|
|
2015-09-11 02:26:39 +08:00
|
|
|
They expose a ``split`` method which accepts the input
|
|
|
|
|
dataset to be split and yields the train/test set indices for each iteration
|
|
|
|
|
of the chosen cross-validation strategy.
|
2012-03-08 22:21:53 +08:00
|
|
|
|
2015-09-11 02:26:39 +08:00
|
|
|
This example shows an example usage of the ``split`` method.
|
2011-12-18 19:39:53 +08:00
|
|
|
|
2015-09-11 02:26:39 +08:00
|
|
|
>>> from sklearn.model_selection import KFold, cross_val_score
|
2018-07-19 20:46:11 +08:00
|
|
|
>>> X = ["a", "a", "a", "b", "b", "c", "c", "c", "c", "c"]
|
|
|
|
|
>>> k_fold = KFold(n_splits=5)
|
2015-09-11 02:26:39 +08:00
|
|
|
>>> for train_indices, test_indices in k_fold.split(X):
|
2013-02-12 06:11:57 +08:00
|
|
|
... print('Train: %s | test: %s' % (train_indices, test_indices))
|
2018-07-19 20:46:11 +08:00
|
|
|
Train: [2 3 4 5 6 7 8 9] | test: [0 1]
|
|
|
|
|
Train: [0 1 4 5 6 7 8 9] | test: [2 3]
|
|
|
|
|
Train: [0 1 2 3 6 7 8 9] | test: [4 5]
|
|
|
|
|
Train: [0 1 2 3 4 5 8 9] | test: [6 7]
|
|
|
|
|
Train: [0 1 2 3 4 5 6 7] | test: [8 9]
|
2011-12-18 19:39:53 +08:00
|
|
|
|
2015-09-11 02:26:39 +08:00
|
|
|
The cross-validation can then be performed easily::
|
2011-12-18 19:39:53 +08:00
|
|
|
|
|
|
|
|
>>> [svc.fit(X_digits[train], y_digits[train]).score(X_digits[test], y_digits[test])
|
2019-06-01 16:53:45 +08:00
|
|
|
... for train, test in k_fold.split(X_digits)]
|
2018-07-19 20:46:11 +08:00
|
|
|
[0.963..., 0.922..., 0.963..., 0.963..., 0.930...]
|
2011-12-18 19:39:53 +08:00
|
|
|
|
2015-09-11 02:26:39 +08:00
|
|
|
The cross-validation score can be directly calculated using the
|
|
|
|
|
:func:`cross_val_score` helper. Given an estimator, the cross-validation object
|
|
|
|
|
and the input dataset, the :func:`cross_val_score` splits the data repeatedly into
|
|
|
|
|
a training and a testing set, trains the estimator using the training set and
|
|
|
|
|
computes the scores based on the testing set for each iteration of cross-validation.
|
2011-12-18 19:39:53 +08:00
|
|
|
|
2015-09-11 02:26:39 +08:00
|
|
|
By default the estimator's ``score`` method is used to compute the individual scores.
|
|
|
|
|
|
|
|
|
|
Refer the :ref:`metrics module <metrics>` to learn more on the available scoring
|
|
|
|
|
methods.
|
|
|
|
|
|
|
|
|
|
>>> cross_val_score(svc, X_digits, y_digits, cv=k_fold, n_jobs=-1)
|
2018-07-19 20:46:11 +08:00
|
|
|
array([0.96388889, 0.92222222, 0.9637883 , 0.9637883 , 0.93036212])
|
2011-12-18 19:39:53 +08:00
|
|
|
|
|
|
|
|
`n_jobs=-1` means that the computation will be dispatched on all the CPUs
|
|
|
|
|
of the computer.
|
|
|
|
|
|
2015-09-11 02:26:39 +08:00
|
|
|
Alternatively, the ``scoring`` argument can be provided to specify an alternative
|
|
|
|
|
scoring method.
|
|
|
|
|
|
|
|
|
|
>>> cross_val_score(svc, X_digits, y_digits, cv=k_fold,
|
|
|
|
|
... scoring='precision_macro')
|
2018-07-19 20:46:11 +08:00
|
|
|
array([0.96578289, 0.92708922, 0.96681476, 0.96362897, 0.93192644])
|
2015-09-11 02:26:39 +08:00
|
|
|
|
2011-12-18 19:39:53 +08:00
|
|
|
**Cross-validation generators**
|
|
|
|
|
|
2012-03-08 22:21:53 +08:00
|
|
|
|
2011-12-18 19:39:53 +08:00
|
|
|
.. list-table::
|
|
|
|
|
|
2012-03-31 20:11:43 +08:00
|
|
|
*
|
2011-12-18 19:39:53 +08:00
|
|
|
|
2016-08-17 04:56:55 +08:00
|
|
|
- :class:`KFold` **(n_splits, shuffle, random_state)**
|
2015-09-11 02:26:39 +08:00
|
|
|
|
2017-02-23 18:27:28 +08:00
|
|
|
- :class:`StratifiedKFold` **(n_splits, shuffle, random_state)**
|
2015-09-11 02:26:39 +08:00
|
|
|
|
2017-02-23 18:27:28 +08:00
|
|
|
- :class:`GroupKFold` **(n_splits)**
|
2015-09-11 02:26:39 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
|
|
- Splits it into K folds, trains on K-1 and then tests on the left-out.
|
|
|
|
|
|
|
|
|
|
- Same as K-Fold but preserves the class distribution within each fold.
|
|
|
|
|
|
2016-09-12 01:14:41 +08:00
|
|
|
- Ensures that the same group is not in both testing and training sets.
|
2015-09-11 02:26:39 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
.. list-table::
|
|
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
2017-02-23 18:27:28 +08:00
|
|
|
- :class:`ShuffleSplit` **(n_splits, test_size, train_size, random_state)**
|
2015-09-11 02:26:39 +08:00
|
|
|
|
|
|
|
|
- :class:`StratifiedShuffleSplit`
|
|
|
|
|
|
2016-09-12 01:14:41 +08:00
|
|
|
- :class:`GroupShuffleSplit`
|
2015-09-11 02:26:39 +08:00
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
|
|
- Generates train/test indices based on random permutation.
|
|
|
|
|
|
|
|
|
|
- Same as shuffle split but preserves the class distribution within each iteration.
|
|
|
|
|
|
2016-09-12 01:14:41 +08:00
|
|
|
- Ensures that the same group is not in both testing and training sets.
|
2015-09-11 02:26:39 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
.. list-table::
|
|
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
2016-09-12 01:14:41 +08:00
|
|
|
- :class:`LeaveOneGroupOut` **()**
|
2015-09-11 02:26:39 +08:00
|
|
|
|
2017-02-23 18:27:28 +08:00
|
|
|
- :class:`LeavePGroupsOut` **(n_groups)**
|
2015-09-11 02:26:39 +08:00
|
|
|
|
|
|
|
|
- :class:`LeaveOneOut` **()**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
2016-09-12 01:14:41 +08:00
|
|
|
- Takes a group array to group observations.
|
2015-09-11 02:26:39 +08:00
|
|
|
|
2016-09-12 01:14:41 +08:00
|
|
|
- Leave P groups out.
|
2011-12-18 19:39:53 +08:00
|
|
|
|
2015-09-11 02:26:39 +08:00
|
|
|
- Leave one observation out.
|
2011-12-18 19:39:53 +08:00
|
|
|
|
|
|
|
|
|
2015-09-11 02:26:39 +08:00
|
|
|
|
|
|
|
|
.. list-table::
|
2011-12-18 19:39:53 +08:00
|
|
|
|
2012-03-31 20:11:43 +08:00
|
|
|
*
|
2011-12-18 19:39:53 +08:00
|
|
|
|
2015-09-11 02:26:39 +08:00
|
|
|
- :class:`LeavePOut` **(p)**
|
|
|
|
|
|
|
|
|
|
- :class:`PredefinedSplit`
|
|
|
|
|
|
|
|
|
|
*
|
2011-12-18 19:39:53 +08:00
|
|
|
|
2015-09-11 02:26:39 +08:00
|
|
|
- Leave P observations out.
|
2011-12-18 19:39:53 +08:00
|
|
|
|
2015-09-11 02:26:39 +08:00
|
|
|
- Generates train/test indices based on predefined splits.
|
2011-12-18 19:39:53 +08:00
|
|
|
|
|
|
|
|
|
2012-03-26 07:20:53 +08:00
|
|
|
.. currentmodule:: sklearn.svm
|
|
|
|
|
|
2011-12-18 19:39:53 +08:00
|
|
|
.. topic:: **Exercise**
|
|
|
|
|
|
2020-08-29 22:32:33 +08:00
|
|
|
On the digits dataset, plot the cross-validation score of a :class:`SVC`
|
|
|
|
|
estimator with an linear kernel as a function of parameter ``C`` (use a
|
|
|
|
|
logarithmic grid of points, from 1 to 10).
|
2011-12-18 19:39:53 +08:00
|
|
|
|
2020-08-29 22:32:33 +08:00
|
|
|
.. literalinclude:: ../../auto_examples/exercises/plot_cv_digits.py
|
|
|
|
|
:lines: 13-23
|
|
|
|
|
|
|
|
|
|
.. image:: /auto_examples/exercises/images/sphx_glr_plot_cv_digits_001.png
|
2020-07-16 05:49:44 +08:00
|
|
|
:target: ../../auto_examples/exercises/plot_cv_digits.html
|
|
|
|
|
:align: center
|
|
|
|
|
:scale: 90
|
2012-03-26 07:20:53 +08:00
|
|
|
|
2020-08-29 22:32:33 +08:00
|
|
|
**Solution:** :ref:`sphx_glr_auto_examples_exercises_plot_cv_digits.py`
|
2011-12-18 19:39:53 +08:00
|
|
|
|
|
|
|
|
Grid-search and cross-validated estimators
|
|
|
|
|
============================================
|
|
|
|
|
|
|
|
|
|
Grid-search
|
|
|
|
|
-------------
|
|
|
|
|
|
2015-09-11 02:26:39 +08:00
|
|
|
.. currentmodule:: sklearn.model_selection
|
2012-03-08 22:21:53 +08:00
|
|
|
|
2016-08-29 06:20:13 +08:00
|
|
|
scikit-learn provides an object that, given data, computes the score
|
2011-12-18 19:39:53 +08:00
|
|
|
during the fit of an estimator on a parameter grid and chooses the
|
|
|
|
|
parameters to maximize the cross-validation score. This object takes an
|
|
|
|
|
estimator during the construction and exposes an estimator API::
|
|
|
|
|
|
2015-09-11 02:26:39 +08:00
|
|
|
>>> from sklearn.model_selection import GridSearchCV, cross_val_score
|
2015-01-28 07:15:36 +08:00
|
|
|
>>> Cs = np.logspace(-6, -1, 10)
|
|
|
|
|
>>> clf = GridSearchCV(estimator=svc, param_grid=dict(C=Cs),
|
2011-12-18 19:39:53 +08:00
|
|
|
... n_jobs=-1)
|
2018-07-18 07:08:11 +08:00
|
|
|
>>> clf.fit(X_digits[:1000], y_digits[:1000]) # doctest: +SKIP
|
2011-12-18 19:39:53 +08:00
|
|
|
GridSearchCV(cv=None,...
|
2018-07-18 07:08:11 +08:00
|
|
|
>>> clf.best_score_ # doctest: +SKIP
|
2015-01-28 07:15:36 +08:00
|
|
|
0.925...
|
2018-07-18 07:08:11 +08:00
|
|
|
>>> clf.best_estimator_.C # doctest: +SKIP
|
2015-01-28 07:15:36 +08:00
|
|
|
0.0077...
|
2011-12-18 19:39:53 +08:00
|
|
|
|
|
|
|
|
>>> # Prediction performance on test set is not as good as on train set
|
2018-07-18 07:08:11 +08:00
|
|
|
>>> clf.score(X_digits[1000:], y_digits[1000:]) # doctest: +SKIP
|
2015-01-28 07:15:36 +08:00
|
|
|
0.943...
|
2011-12-18 19:39:53 +08:00
|
|
|
|
|
|
|
|
|
2020-08-02 22:08:36 +08:00
|
|
|
By default, the :class:`GridSearchCV` uses a 5-fold cross-validation. However,
|
2012-12-06 09:20:28 +08:00
|
|
|
if it detects that a classifier is passed, rather than a regressor, it uses
|
2020-08-02 22:08:36 +08:00
|
|
|
a stratified 5-fold.
|
2011-12-18 19:39:53 +08:00
|
|
|
|
|
|
|
|
.. topic:: Nested cross-validation
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
2018-07-18 07:08:11 +08:00
|
|
|
>>> cross_val_score(clf, X_digits, y_digits) # doctest: +SKIP
|
2018-03-27 13:44:40 +08:00
|
|
|
array([0.938..., 0.963..., 0.944...])
|
2012-03-31 20:11:43 +08:00
|
|
|
|
2011-12-18 19:39:53 +08:00
|
|
|
Two cross-validation loops are performed in parallel: one by the
|
2014-07-06 19:33:20 +08:00
|
|
|
:class:`GridSearchCV` estimator to set ``gamma`` and the other one by
|
|
|
|
|
``cross_val_score`` to measure the prediction performance of the
|
2011-12-18 19:39:53 +08:00
|
|
|
estimator. The resulting scores are unbiased estimates of the
|
|
|
|
|
prediction score on new data.
|
|
|
|
|
|
|
|
|
|
.. warning::
|
|
|
|
|
|
2014-07-06 19:33:20 +08:00
|
|
|
You cannot nest objects with parallel computing (``n_jobs`` different
|
2011-12-18 19:39:53 +08:00
|
|
|
than 1).
|
|
|
|
|
|
2012-03-12 17:19:01 +08:00
|
|
|
.. _cv_estimators_tut:
|
|
|
|
|
|
2011-12-18 19:39:53 +08:00
|
|
|
Cross-validated estimators
|
|
|
|
|
----------------------------
|
|
|
|
|
|
|
|
|
|
Cross-validation to set a parameter can be done more efficiently on an
|
2016-08-29 06:20:13 +08:00
|
|
|
algorithm-by-algorithm basis. This is why, for certain estimators,
|
|
|
|
|
scikit-learn exposes :ref:`cross_validation` estimators that set their
|
|
|
|
|
parameter automatically by cross-validation::
|
2011-12-18 19:39:53 +08:00
|
|
|
|
2012-02-28 23:36:37 +08:00
|
|
|
>>> from sklearn import linear_model, datasets
|
2019-05-29 21:39:20 +08:00
|
|
|
>>> lasso = linear_model.LassoCV()
|
2019-08-25 11:17:01 +08:00
|
|
|
>>> X_diabetes, y_diabetes = datasets.load_diabetes(return_X_y=True)
|
2019-06-01 16:53:45 +08:00
|
|
|
>>> lasso.fit(X_diabetes, y_diabetes)
|
|
|
|
|
LassoCV()
|
2011-12-18 19:39:53 +08:00
|
|
|
>>> # The estimator chose automatically its lambda:
|
2019-06-01 16:53:45 +08:00
|
|
|
>>> lasso.alpha_
|
2019-05-29 21:39:20 +08:00
|
|
|
0.00375...
|
2011-12-18 19:39:53 +08:00
|
|
|
|
|
|
|
|
These estimators are called similarly to their counterparts, with 'CV'
|
|
|
|
|
appended to their name.
|
|
|
|
|
|
|
|
|
|
.. topic:: **Exercise**
|
|
|
|
|
|
|
|
|
|
On the diabetes dataset, find the optimal regularization parameter
|
|
|
|
|
alpha.
|
|
|
|
|
|
|
|
|
|
**Bonus**: How much can you trust the selection of alpha?
|
|
|
|
|
|
2012-03-26 07:20:53 +08:00
|
|
|
.. literalinclude:: ../../auto_examples/exercises/plot_cv_diabetes.py
|
2014-08-20 15:59:31 +08:00
|
|
|
:lines: 17-24
|
2012-03-26 07:20:53 +08:00
|
|
|
|
2016-01-25 07:18:26 +08:00
|
|
|
**Solution:** :ref:`sphx_glr_auto_examples_exercises_plot_cv_diabetes.py`
|