1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
# Author: Alexandre Gramfort <alexandre.gramfort@inria.fr>
# Olivier Grisel <olivier.grisel@ensta.org>
#
# License: BSD Style.
"""Implementation of coordinate descent for the Elastic Net with sparse data.
"""
import warnings
import numpy as np
import scipy.sparse as sp
from ...utils.extmath import safe_sparse_dot
from ..base import LinearModel
from . import cd_fast_sparse
class ElasticNet(LinearModel):
"""Linear Model trained with L1 and L2 prior as regularizer
This implementation works on scipy.sparse X and dense `coef_`.
rho=1 is the lasso penalty. Currently, rho <= 0.01 is not
reliable, unless you supply your own sequence of alpha.
Parameters
----------
alpha : float
Constant that multiplies the L1 term. Defaults to 1.0
rho : float
The ElasticNet mixing parameter, with 0 < rho <= 1.
fit_intercept: bool
Whether the intercept should be estimated or not. If False, the
data is assumed to be already centered.
TODO: fit_intercept=True is not yet implemented
Notes
-----
The parameter rho corresponds to alpha in the glmnet R package
while alpha corresponds to the lambda parameter in glmnet.
"""
def __init__(self, alpha=1.0, rho=0.5, fit_intercept=False,
normalize=False, max_iter=1000, tol=1e-4):
if fit_intercept:
raise NotImplementedError("fit_intercept=True is not implemented")
self.alpha = alpha
self.rho = rho
self.fit_intercept = fit_intercept
self.normalize = normalize
self.intercept_ = 0.0
self.max_iter = max_iter
self.tol = tol
self._set_coef(None)
def _set_coef(self, coef_):
self.coef_ = coef_
if coef_ is None:
self.sparse_coef_ = None
else:
# sparse representation of the fitted coef for the predict method
self.sparse_coef_ = sp.csr_matrix(coef_)
def fit(self, X, y):
"""Fit current model with coordinate descent
X is expected to be a sparse matrix. For maximum efficiency, use a
sparse matrix in CSC format (scipy.sparse.csc_matrix)
"""
X = sp.csc_matrix(X)
y = np.asarray(y, dtype=np.float64)
if X.shape[0] != y.shape[0]:
raise ValueError("X and y have incompatible shapes.\n" +
"Note: Sparse matrices cannot be indexed w/" +
"boolean masks (use `indices=True` in CV).")
# NOTE: we are explicitly not centering the data the naive way to
# avoid breaking the sparsity of X
n_samples, n_features = X.shape[0], X.shape[1]
if self.coef_ is None:
self.coef_ = np.zeros(n_features, dtype=np.float64)
alpha = self.alpha * self.rho * n_samples
beta = self.alpha * (1.0 - self.rho) * n_samples
X_data = np.array(X.data, np.float64)
# TODO: add support for non centered data
coef_, self.dual_gap_, self.eps_ = \
cd_fast_sparse.enet_coordinate_descent(
self.coef_, alpha, beta, X_data, X.indices, X.indptr, y,
self.max_iter, self.tol)
# update self.coef_ and self.sparse_coef_ consistently
self._set_coef(coef_)
if self.dual_gap_ > self.eps_:
warnings.warn('Objective did not converge, you might want'
'to increase the number of iterations')
# XXX TODO: implement intercept_ fitting
# return self for chaining fit and predict calls
return self
def decision_function(self, X):
"""Decision function of the linear model
Parameters
----------
X : scipy.sparse matrix of shape [n_samples, n_features]
Returns
-------
array, shape = [n_samples] with the predicted real values
"""
return np.ravel(safe_sparse_dot(self.sparse_coef_, X.T,
dense_output=True) + self.intercept_)
class Lasso(ElasticNet):
"""Linear Model trained with L1 prior as regularizer
This implementation works on scipy.sparse X and dense `coef_`. Technically
this is the same as Elastic Net with the L2 penalty set to zero.
Parameters
----------
alpha : float
Constant that multiplies the L1 term. Defaults to 1.0
`coef_` : ndarray of shape n_features
The initial coeffients to warm-start the optimization
fit_intercept: bool
Whether the intercept should be estimated or not. If False, the
data is assumed to be already centered.
"""
def __init__(self, alpha=1.0, fit_intercept=False, normalize=False,
max_iter=1000, tol=1e-4):
super(Lasso, self).__init__(
alpha=alpha, rho=1.0, fit_intercept=fit_intercept,
normalize=normalize, max_iter=max_iter, tol=tol)
|