scikit-learn/sklearn/linear_model/omp.py

558 lines
20 KiB
Python
Raw Normal View History

2011-08-03 22:59:30 +08:00
"""Orthogonal matching pursuit algorithms
2011-07-25 18:57:42 +08:00
"""
# Author: Vlad Niculae
#
# License: BSD Style.
import warnings
2011-08-02 05:40:05 +08:00
2011-07-25 18:57:42 +08:00
import numpy as np
from scipy import linalg
from scipy.linalg.lapack import get_lapack_funcs
2011-07-25 18:57:42 +08:00
2011-07-29 07:54:23 +08:00
from .base import LinearModel
2012-06-13 12:26:02 +08:00
from ..base import RegressorMixin
from ..utils import array2d
from ..utils.arrayfuncs import solve_triangular
2011-07-25 18:57:42 +08:00
2011-08-02 05:40:05 +08:00
premature = """ Orthogonal matching pursuit ended prematurely due to linear
dependence in the dictionary. The requested precision might not have been met.
"""
def _cholesky_omp(X, y, n_nonzero_coefs, tol=None, copy_X=True):
"""Orthogonal Matching Pursuit step using the Cholesky decomposition.
2011-07-25 18:57:42 +08:00
Parameters:
-----------
2011-08-03 22:59:30 +08:00
X: array, shape = (n_samples, n_features)
2011-07-25 18:57:42 +08:00
Input dictionary. Columns are assumed to have unit norm.
2011-08-03 22:59:30 +08:00
y: array, shape = (n_samples,)
2011-07-25 18:57:42 +08:00
Input targets
2011-07-29 05:09:22 +08:00
n_nonzero_coefs: int
2011-07-25 18:57:42 +08:00
Targeted number of non-zero elements
2011-08-24 21:31:07 +08:00
tol: float
2011-07-29 05:09:22 +08:00
Targeted squared error, if not None overrides n_nonzero_coefs.
2011-07-25 18:57:42 +08:00
copy_X: bool, optional
Whether the design matrix X must be copied by the algorithm. A false
value is only helpful if X is already Fortran-ordered, otherwise a
copy is made anyway.
2011-08-03 21:18:16 +08:00
2011-07-25 18:57:42 +08:00
Returns:
--------
2011-08-03 22:59:30 +08:00
gamma: array, shape = (n_nonzero_coefs,)
2011-07-25 18:57:42 +08:00
Non-zero elements of the solution
2011-08-03 22:59:30 +08:00
idx: array, shape = (n_nonzero_coefs,)
2011-07-25 18:57:42 +08:00
Indices of the positions of the elements in gamma within the solution
vector
"""
if copy_X:
X = X.copy('F')
else: # even if we are allowed to overwrite, still copy it if bad order
X = np.asfortranarray(X)
2011-10-20 23:36:25 +08:00
2011-07-30 05:24:34 +08:00
min_float = np.finfo(X.dtype).eps
nrm2, swap = linalg.get_blas_funcs(('nrm2', 'swap'), (X,))
2011-07-30 06:41:47 +08:00
potrs, = get_lapack_funcs(('potrs',), (X,))
2011-07-30 05:24:34 +08:00
2011-07-25 18:57:42 +08:00
alpha = np.dot(X.T, y)
residual = y
gamma = np.empty(0)
2011-07-30 05:24:34 +08:00
n_active = 0
indices = range(X.shape[1]) # keeping track of swapping
2011-07-25 18:57:42 +08:00
2011-08-24 21:31:07 +08:00
max_features = X.shape[1] if tol is not None else n_nonzero_coefs
2011-07-30 05:24:34 +08:00
L = np.empty((max_features, max_features), dtype=X.dtype)
L[0, 0] = 1.
while True:
2011-08-02 23:00:20 +08:00
lam = np.argmax(np.abs(np.dot(X.T, residual)))
if lam < n_active or alpha[lam] ** 2 < min_float:
# atom already selected or inner product too small
warnings.warn(premature, RuntimeWarning, stacklevel=2)
break
2011-07-30 07:00:16 +08:00
if n_active > 0:
# Updates the Cholesky decomposition of X' X
L[n_active, :n_active] = np.dot(X[:, :n_active].T, X[:, lam])
solve_triangular(L[:n_active, :n_active], L[n_active, :n_active])
v = nrm2(L[n_active, :n_active]) ** 2
if 1 - v <= min_float: # selected atoms are dependent
warnings.warn(premature, RuntimeWarning, stacklevel=2)
break
L[n_active, n_active] = np.sqrt(1 - v)
X.T[n_active], X.T[lam] = swap(X.T[n_active], X.T[lam])
alpha[n_active], alpha[lam] = alpha[lam], alpha[n_active]
indices[n_active], indices[lam] = indices[lam], indices[n_active]
2011-07-30 05:24:34 +08:00
n_active += 1
2011-07-25 18:57:42 +08:00
# solves LL'x = y as a composition of two triangular systems
gamma, _ = potrs(L[:n_active, :n_active], alpha[:n_active], lower=True,
2011-07-30 07:00:16 +08:00
overwrite_b=False)
2011-07-30 06:41:47 +08:00
residual = y - np.dot(X[:, :n_active], gamma)
2011-08-24 21:31:07 +08:00
if tol is not None and nrm2(residual) ** 2 <= tol:
2011-07-25 18:57:42 +08:00
break
2011-07-30 05:28:02 +08:00
elif n_active == max_features:
2011-07-25 18:57:42 +08:00
break
return gamma, indices[:n_active]
2011-07-25 18:57:42 +08:00
2011-08-24 21:31:07 +08:00
def _gram_omp(Gram, Xy, n_nonzero_coefs, tol_0=None, tol=None,
copy_Gram=True, copy_Xy=True):
"""Orthogonal Matching Pursuit step on a precomputed Gram matrix.
This function uses the the Cholesky decomposition method.
2011-07-25 18:57:42 +08:00
Parameters:
-----------
2011-08-03 22:59:30 +08:00
Gram: array, shape = (n_features, n_features)
2011-07-25 18:57:42 +08:00
Gram matrix of the input data matrix
2011-08-03 22:59:30 +08:00
Xy: array, shape = (n_features,)
2011-07-25 18:57:42 +08:00
Input targets
2011-07-29 05:09:22 +08:00
n_nonzero_coefs: int
2011-07-25 18:57:42 +08:00
Targeted number of non-zero elements
2011-08-24 21:31:07 +08:00
tol_0: float
Squared norm of y, required if tol is not None.
2011-07-25 18:57:42 +08:00
2011-08-24 21:31:07 +08:00
tol: float
2011-07-29 05:09:22 +08:00
Targeted squared error, if not None overrides n_nonzero_coefs.
2011-07-25 18:57:42 +08:00
copy_Gram: bool, optional
Whether the gram matrix must be copied by the algorithm. A false
value is only helpful if it is already Fortran-ordered, otherwise a
copy is made anyway.
2011-08-03 21:18:16 +08:00
copy_Xy: bool, optional
Whether the covariance vector Xy must be copied by the algorithm.
If False, it may be overwritten.
2011-08-03 21:18:16 +08:00
2011-07-25 18:57:42 +08:00
Returns:
--------
2011-08-03 22:59:30 +08:00
gamma: array, shape = (n_nonzero_coefs,)
2011-07-25 18:57:42 +08:00
Non-zero elements of the solution
2011-08-03 22:59:30 +08:00
idx: array, shape = (n_nonzero_coefs,)
2011-07-25 18:57:42 +08:00
Indices of the positions of the elements in gamma within the solution
vector
"""
Gram = Gram.copy('F') if copy_Gram else np.asfortranarray(Gram)
2011-08-03 21:18:16 +08:00
if copy_Xy:
2011-08-03 21:18:16 +08:00
Xy = Xy.copy()
2011-07-25 18:57:42 +08:00
2011-08-03 21:18:16 +08:00
min_float = np.finfo(Gram.dtype).eps
nrm2, swap = linalg.get_blas_funcs(('nrm2', 'swap'), (Gram,))
potrs, = get_lapack_funcs(('potrs',), (Gram,))
2011-07-30 05:24:34 +08:00
indices = range(len(Gram)) # keeping track of swapping
2011-07-25 18:57:42 +08:00
alpha = Xy
2011-08-24 21:31:07 +08:00
tol_curr = tol_0
2011-07-25 18:57:42 +08:00
delta = 0
gamma = np.empty(0)
2011-07-30 05:24:34 +08:00
n_active = 0
2011-07-25 18:57:42 +08:00
2011-08-24 21:31:07 +08:00
max_features = len(Gram) if tol is not None else n_nonzero_coefs
2011-08-03 21:18:16 +08:00
L = np.empty((max_features, max_features), dtype=Gram.dtype)
2011-07-30 05:24:34 +08:00
L[0, 0] = 1.
while True:
2011-08-02 23:00:20 +08:00
lam = np.argmax(np.abs(alpha))
2011-08-03 21:18:16 +08:00
if lam < n_active or alpha[lam] ** 2 < min_float:
# selected same atom twice, or inner product too small
warnings.warn(premature, RuntimeWarning, stacklevel=2)
break
2011-07-30 07:00:16 +08:00
if n_active > 0:
2011-08-03 21:18:16 +08:00
L[n_active, :n_active] = Gram[lam, :n_active]
solve_triangular(L[:n_active, :n_active], L[n_active, :n_active])
v = nrm2(L[n_active, :n_active]) ** 2
if 1 - v <= min_float: # selected atoms are dependent
warnings.warn(premature, RuntimeWarning, stacklevel=2)
break
L[n_active, n_active] = np.sqrt(1 - v)
2011-08-03 21:18:16 +08:00
Gram[n_active], Gram[lam] = swap(Gram[n_active], Gram[lam])
Gram.T[n_active], Gram.T[lam] = swap(Gram.T[n_active], Gram.T[lam])
indices[n_active], indices[lam] = indices[lam], indices[n_active]
2011-08-03 21:18:16 +08:00
Xy[n_active], Xy[lam] = Xy[lam], Xy[n_active]
2011-07-30 05:24:34 +08:00
n_active += 1
# solves LL'x = y as a composition of two triangular systems
2011-08-03 21:18:16 +08:00
gamma, _ = potrs(L[:n_active, :n_active], Xy[:n_active], lower=True,
2011-07-30 07:00:16 +08:00
overwrite_b=False)
2011-07-30 06:41:47 +08:00
2011-08-03 21:18:16 +08:00
beta = np.dot(Gram[:, :n_active], gamma)
2011-07-25 18:57:42 +08:00
alpha = Xy - beta
2011-08-24 21:31:07 +08:00
if tol is not None:
tol_curr += delta
2011-08-03 21:18:16 +08:00
delta = np.inner(gamma, beta[:n_active])
2011-08-24 21:31:07 +08:00
tol_curr -= delta
if tol_curr <= tol:
2011-07-25 18:57:42 +08:00
break
2011-07-30 05:24:34 +08:00
elif n_active == max_features:
2011-07-25 18:57:42 +08:00
break
2011-07-30 05:24:34 +08:00
return gamma, indices[:n_active]
2011-07-25 18:57:42 +08:00
2011-08-24 21:31:07 +08:00
def orthogonal_mp(X, y, n_nonzero_coefs=None, tol=None, precompute_gram=False,
copy_X=True):
2011-07-25 18:57:42 +08:00
"""Orthogonal Matching Pursuit (OMP)
Solves n_targets Orthogonal Matching Pursuit problems.
An instance of the problem has the form:
2011-07-29 05:09:22 +08:00
When parametrized by the number of non-zero coefficients using
`n_nonzero_coefs`:
argmin ||y - X\gamma||^2 subject to ||\gamma||_0 <= n_{nonzero coefs}
2011-07-25 18:57:42 +08:00
2011-08-24 21:31:07 +08:00
When parametrized by error using the parameter `tol`:
argmin ||\gamma||_0 subject to ||y - X\gamma||^2 <= tol
2011-07-25 18:57:42 +08:00
Parameters
----------
2011-08-03 22:59:30 +08:00
X: array, shape = (n_samples, n_features)
Input data. Columns are assumed to have unit norm.
2011-07-25 18:57:42 +08:00
2011-08-03 22:59:30 +08:00
y: array, shape = (n_samples,) or (n_samples, n_targets)
2011-07-25 18:57:42 +08:00
Input targets
2011-07-29 05:09:22 +08:00
n_nonzero_coefs: int
Desired number of non-zero entries in the solution. If None (by
default) this value is set to 10% of n_features.
2011-07-25 18:57:42 +08:00
2011-08-24 21:31:07 +08:00
tol: float
2011-07-29 05:09:22 +08:00
Maximum norm of the residual. If not None, overrides n_nonzero_coefs.
2011-07-25 18:57:42 +08:00
2011-08-03 22:59:30 +08:00
precompute_gram: {True, False, 'auto'},
2011-07-25 18:57:42 +08:00
Whether to perform precomputations. Improves performance when n_targets
or n_samples is very large.
copy_X: bool, optional
Whether the design matrix X must be copied by the algorithm. A false
value is only helpful if X is already Fortran-ordered, otherwise a
copy is made anyway.
2011-08-03 21:18:16 +08:00
2011-07-25 18:57:42 +08:00
Returns
-------
2011-08-03 22:59:30 +08:00
coef: array, shape = (n_features,) or (n_features, n_targets)
2011-07-25 18:57:42 +08:00
Coefficients of the OMP solution
See also
--------
2011-08-03 22:59:30 +08:00
OrthogonalMatchingPursuit
2011-07-25 18:57:42 +08:00
orthogonal_mp_gram
lars_path
decomposition.sparse_encode
2011-07-25 18:57:42 +08:00
2011-08-03 22:59:30 +08:00
Notes
-----
Orthogonal matching pursuit was introduced in G. Mallat, Z. Zhang,
Matching pursuits with time-frequency dictionaries, IEEE Transactions on
Signal Processing, Vol. 41, No. 12. (December 1993), pp. 3397-3415.
(http://blanche.polytechnique.fr/~mallat/papiers/MallatPursuit93.pdf)
This implementation is based on Rubinstein, R., Zibulevsky, M. and Elad,
M., Efficient Implementation of the K-SVD Algorithm using Batch Orthogonal
Matching Pursuit Technical Report - CS Technion, April 2008.
http://www.cs.technion.ac.il/~ronrubin/Publications/KSVD-OMP-v2.pdf
2011-08-03 22:59:30 +08:00
2011-07-25 18:57:42 +08:00
"""
X = array2d(X, order='F', copy=copy_X)
copy_X = False
y = np.asarray(y)
2011-07-25 18:57:42 +08:00
if y.ndim == 1:
y = y[:, np.newaxis]
2011-08-04 00:27:34 +08:00
if y.shape[1] > 1: # subsequent targets will be affected
copy_X = True
2011-08-24 21:31:07 +08:00
if n_nonzero_coefs == None and tol == None:
n_nonzero_coefs = int(0.1 * X.shape[1])
2011-08-24 21:31:07 +08:00
if tol is not None and tol < 0:
2011-07-25 18:57:42 +08:00
raise ValueError("Epsilon cannot be negative")
2011-08-24 21:31:07 +08:00
if tol is None and n_nonzero_coefs <= 0:
2011-07-25 18:57:42 +08:00
raise ValueError("The number of atoms must be positive")
2011-08-24 21:31:07 +08:00
if tol is None and n_nonzero_coefs > X.shape[1]:
2012-08-26 03:54:30 +08:00
raise ValueError("The number of atoms cannot be more than the number "
"of features")
2011-08-03 22:59:30 +08:00
if precompute_gram == 'auto':
precompute_gram = X.shape[0] > X.shape[1]
2011-08-03 21:18:16 +08:00
if precompute_gram:
2011-07-25 18:57:42 +08:00
G = np.dot(X.T, X)
G = np.asfortranarray(G)
2011-07-25 18:57:42 +08:00
Xy = np.dot(X.T, y)
2011-08-24 21:31:07 +08:00
if tol is not None:
2011-07-25 18:57:42 +08:00
norms_squared = np.sum((y ** 2), axis=0)
else:
norms_squared = None
2011-08-24 21:31:07 +08:00
return orthogonal_mp_gram(G, Xy, n_nonzero_coefs, tol, norms_squared,
copy_Gram=copy_X, copy_Xy=False)
2011-07-25 18:57:42 +08:00
coef = np.zeros((X.shape[1], y.shape[1]))
for k in xrange(y.shape[1]):
2011-08-24 21:31:07 +08:00
x, idx = _cholesky_omp(X, y[:, k], n_nonzero_coefs, tol,
copy_X=copy_X)
2011-07-25 18:57:42 +08:00
coef[idx, k] = x
return np.squeeze(coef)
2011-08-24 21:31:07 +08:00
def orthogonal_mp_gram(Gram, Xy, n_nonzero_coefs=None, tol=None,
norms_squared=None, copy_Gram=True,
copy_Xy=True):
2011-07-25 18:57:42 +08:00
"""Gram Orthogonal Matching Pursuit (OMP)
Solves n_targets Orthogonal Matching Pursuit problems using only
2011-08-03 22:01:53 +08:00
the Gram matrix X.T * X and the product X.T * y.
2011-07-25 18:57:42 +08:00
Parameters
----------
2011-08-03 22:59:30 +08:00
Gram: array, shape = (n_features, n_features)
2011-07-25 18:57:42 +08:00
Gram matrix of the input data: X.T * X
2011-08-03 22:59:30 +08:00
Xy: array, shape = (n_features,) or (n_features, n_targets)
2011-08-03 22:01:53 +08:00
Input targets multiplied by X: X.T * y
2011-07-25 18:57:42 +08:00
2011-07-29 05:09:22 +08:00
n_nonzero_coefs: int
Desired number of non-zero entries in the solution. If None (by
default) this value is set to 10% of n_features.
2011-07-25 18:57:42 +08:00
2011-08-24 21:31:07 +08:00
tol: float
2011-07-29 05:09:22 +08:00
Maximum norm of the residual. If not None, overrides n_nonzero_coefs.
2011-07-25 18:57:42 +08:00
2011-08-03 22:59:30 +08:00
norms_squared: array-like, shape = (n_targets,)
2011-08-24 21:31:07 +08:00
Squared L2 norms of the lines of y. Required if tol is not None.
2011-07-25 18:57:42 +08:00
copy_Gram: bool, optional
Whether the gram matrix must be copied by the algorithm. A false
value is only helpful if it is already Fortran-ordered, otherwise a
copy is made anyway.
2011-08-03 21:18:16 +08:00
copy_Xy: bool, optional
Whether the covariance vector Xy must be copied by the algorithm.
If False, it may be overwritten.
2011-08-03 21:18:16 +08:00
2011-07-25 18:57:42 +08:00
Returns
-------
2011-08-03 22:59:30 +08:00
coef: array, shape = (n_features,) or (n_features, n_targets)
2011-07-25 18:57:42 +08:00
Coefficients of the OMP solution
See also
--------
2011-08-03 22:59:30 +08:00
OrthogonalMatchingPursuit
2011-07-25 18:57:42 +08:00
orthogonal_mp
lars_path
decomposition.sparse_encode
2011-07-25 18:57:42 +08:00
2011-08-03 22:59:30 +08:00
Notes
-----
Orthogonal matching pursuit was introduced in G. Mallat, Z. Zhang,
Matching pursuits with time-frequency dictionaries, IEEE Transactions on
Signal Processing, Vol. 41, No. 12. (December 1993), pp. 3397-3415.
(http://blanche.polytechnique.fr/~mallat/papiers/MallatPursuit93.pdf)
This implementation is based on Rubinstein, R., Zibulevsky, M. and Elad,
M., Efficient Implementation of the K-SVD Algorithm using Batch Orthogonal
Matching Pursuit Technical Report - CS Technion, April 2008.
http://www.cs.technion.ac.il/~ronrubin/Publications/KSVD-OMP-v2.pdf
2011-08-03 22:59:30 +08:00
2011-07-25 18:57:42 +08:00
"""
Gram = array2d(Gram, order='F', copy=copy_Gram)
Xy = np.asarray(Xy)
2012-10-10 15:38:10 +08:00
if Xy.ndim > 1 and Xy.shape[1] > 1:
# or subsequent target will be affected
copy_Gram = True
2011-07-25 18:57:42 +08:00
if Xy.ndim == 1:
Xy = Xy[:, np.newaxis]
2011-08-24 21:31:07 +08:00
if tol is not None:
norms_squared = [norms_squared]
2011-07-25 18:57:42 +08:00
2011-08-24 21:31:07 +08:00
if n_nonzero_coefs == None and tol is None:
2011-08-03 21:18:16 +08:00
n_nonzero_coefs = int(0.1 * len(Gram))
2011-08-24 21:31:07 +08:00
if tol is not None and norms_squared == None:
2012-08-26 03:54:30 +08:00
raise ValueError('Gram OMP needs the precomputed norms in order '
'to evaluate the error sum of squares.')
2011-08-24 21:31:07 +08:00
if tol is not None and tol < 0:
2012-08-26 03:54:30 +08:00
raise ValueError("Epsilon cannot be negative")
2011-08-24 21:31:07 +08:00
if tol is None and n_nonzero_coefs <= 0:
2011-07-25 18:57:42 +08:00
raise ValueError("The number of atoms must be positive")
2011-08-24 21:31:07 +08:00
if tol is None and n_nonzero_coefs > len(Gram):
2012-08-26 03:54:30 +08:00
raise ValueError("The number of atoms cannot be more than the number "
"of features")
2011-08-03 21:18:16 +08:00
coef = np.zeros((len(Gram), Xy.shape[1]))
2011-07-25 18:57:42 +08:00
for k in range(Xy.shape[1]):
2011-08-03 21:18:16 +08:00
x, idx = _gram_omp(Gram, Xy[:, k], n_nonzero_coefs,
2011-08-24 21:31:07 +08:00
norms_squared[k] if tol is not None else None, tol,
copy_Gram=copy_Gram, copy_Xy=copy_Xy)
2011-07-25 18:57:42 +08:00
coef[idx, k] = x
return np.squeeze(coef)
2011-07-29 07:54:23 +08:00
2012-06-13 12:26:02 +08:00
class OrthogonalMatchingPursuit(LinearModel, RegressorMixin):
2011-07-29 07:54:23 +08:00
"""Orthogonal Mathching Pursuit model (OMP)
Parameters
----------
2011-12-23 04:05:27 +08:00
n_nonzero_coefs : int, optional
Desired number of non-zero entries in the solution. If None (by
default) this value is set to 10% of n_features.
2011-07-29 07:54:23 +08:00
2011-12-23 04:05:27 +08:00
tol : float, optional
2011-07-29 07:54:23 +08:00
Maximum norm of the residual. If not None, overrides n_nonzero_coefs.
2011-12-23 04:05:27 +08:00
fit_intercept : boolean, optional
2011-07-29 07:54:23 +08:00
whether to calculate the intercept for this model. If set
to false, no intercept will be used in calculations
(e.g. data is expected to be already centered).
2011-07-29 07:55:48 +08:00
2011-12-23 04:05:27 +08:00
normalize : boolean, optional
2011-07-29 07:54:23 +08:00
If False, the regressors X are assumed to be already normalized.
2011-12-23 04:05:27 +08:00
precompute_gram : {True, False, 'auto'},
2011-08-03 21:18:16 +08:00
Whether to use a precomputed Gram and Xy matrix to speed up
2011-08-03 22:59:30 +08:00
calculations. Improves performance when `n_targets` or `n_samples` is
very large. Note that if you already have such matrices, you can pass
them directly to the fit method.
2011-07-29 07:54:23 +08:00
2011-12-23 04:05:27 +08:00
copy_X : bool, optional
Whether the design matrix X must be copied by the algorithm. A false
value is only helpful if X is already Fortran-ordered, otherwise a
copy is made anyway.
2011-12-23 04:05:27 +08:00
copy_Gram : bool, optional
Whether the gram matrix must be copied by the algorithm. A false
value is only helpful if X is already Fortran-ordered, otherwise a
copy is made anyway.
2011-12-23 04:05:27 +08:00
copy_Xy : bool, optional
Whether the covariance vector Xy must be copied by the algorithm.
If False, it may be overwritten.
2011-07-29 07:54:23 +08:00
Attributes
----------
2011-12-23 04:05:27 +08:00
`coef_` : array, shape = (n_features,) or (n_features, n_targets)
2011-07-29 07:54:23 +08:00
parameter vector (w in the fomulation formula)
2011-12-23 04:05:27 +08:00
`intercept_` : float or array, shape =(n_targets,)
2011-07-29 07:54:23 +08:00
independent term in decision function.
2011-08-03 22:59:30 +08:00
Notes
-----
Orthogonal matching pursuit was introduced in G. Mallat, Z. Zhang,
Matching pursuits with time-frequency dictionaries, IEEE Transactions on
Signal Processing, Vol. 41, No. 12. (December 1993), pp. 3397-3415.
(http://blanche.polytechnique.fr/~mallat/papiers/MallatPursuit93.pdf)
This implementation is based on Rubinstein, R., Zibulevsky, M. and Elad,
M., Efficient Implementation of the K-SVD Algorithm using Batch Orthogonal
Matching Pursuit Technical Report - CS Technion, April 2008.
http://www.cs.technion.ac.il/~ronrubin/Publications/KSVD-OMP-v2.pdf
2011-08-03 22:59:30 +08:00
See also
--------
orthogonal_mp
orthogonal_mp_gram
lars_path
Lars
LassoLars
decomposition.sparse_encode
2011-08-03 22:59:30 +08:00
2011-07-29 07:54:23 +08:00
"""
def __init__(self, copy_X=True, copy_Gram=True,
copy_Xy=True, n_nonzero_coefs=None, tol=None,
fit_intercept=True, normalize=True, precompute_gram=False):
2011-07-29 07:54:23 +08:00
self.n_nonzero_coefs = n_nonzero_coefs
2011-08-24 21:31:07 +08:00
self.tol = tol
2011-07-29 07:54:23 +08:00
self.fit_intercept = fit_intercept
self.normalize = normalize
2011-08-03 21:18:16 +08:00
self.precompute_gram = precompute_gram
self.copy_Gram = copy_Gram
self.copy_Xy = copy_Xy
self.copy_X = copy_X
2011-07-29 07:54:23 +08:00
def fit(self, X, y, Gram=None, Xy=None):
2011-07-29 07:54:23 +08:00
"""Fit the model using X, y as training data.
Parameters
----------
2011-08-03 22:01:53 +08:00
X: array-like, shape = (n_samples, n_features)
2011-07-29 07:54:23 +08:00
Training data.
2011-08-03 22:01:53 +08:00
y: array-like, shape = (n_samples,) or (n_samples, n_targets)
2011-07-29 07:54:23 +08:00
Target values.
2011-08-03 22:04:26 +08:00
2011-08-03 22:59:30 +08:00
Gram: array-like, shape = (n_features, n_features) (optional)
2011-08-03 22:01:53 +08:00
Gram matrix of the input data: X.T * X
2011-08-03 22:59:30 +08:00
Xy: array-like, shape = (n_features,) or (n_features, n_targets)
(optional)
2011-08-03 22:01:53 +08:00
Input targets multiplied by X: X.T * y
2011-07-29 07:54:23 +08:00
Returns
-------
2011-08-03 22:01:53 +08:00
self: object
2011-07-29 07:54:23 +08:00
returns an instance of self.
"""
X = array2d(X)
y = np.asarray(y)
n_features = X.shape[1]
2011-07-29 07:54:23 +08:00
X, y, X_mean, y_mean, X_std = self._center_data(X, y,
self.fit_intercept,
self.normalize,
self.copy_X)
2012-03-19 18:35:58 +08:00
if y.ndim == 1:
y = y[:, np.newaxis]
2011-08-24 21:31:07 +08:00
if self.n_nonzero_coefs == None and self.tol is None:
self.n_nonzero_coefs = int(0.1 * n_features)
2012-05-06 01:32:12 +08:00
if (Gram is not None or Xy is not None) and (self.fit_intercept is True
or self.normalize is True):
warnings.warn('Mean subtraction (fit_intercept) and '
'normalization cannot be applied on precomputed Gram '
'and Xy matrices. Your precomputed values are ignored '
2012-05-06 03:33:19 +08:00
'and recomputed. To avoid this, do the scaling yourself '
'and call with fit_intercept and normalize set to False.',
RuntimeWarning, stacklevel=2)
2012-05-06 01:32:12 +08:00
Gram, Xy = None, None
2011-08-03 21:18:16 +08:00
if Gram is not None:
if Xy is None:
Xy = np.dot(X.T, y)
else:
if self.copy_Xy:
Xy = Xy.copy()
if self.normalize:
if len(Xy.shape) == 1:
Xy /= X_std
else:
Xy /= X_std[:, np.newaxis]
if self.normalize:
Gram /= X_std
Gram /= X_std[:, np.newaxis]
2011-08-24 21:31:07 +08:00
norms_sq = np.sum(y ** 2, axis=0) if self.tol is not None else None
2011-08-03 22:04:26 +08:00
self.coef_ = orthogonal_mp_gram(Gram, Xy, self.n_nonzero_coefs,
2011-08-24 21:31:07 +08:00
self.tol, norms_sq,
self.copy_Gram, True).T
else:
2011-08-03 21:56:42 +08:00
precompute_gram = self.precompute_gram
if precompute_gram == 'auto':
precompute_gram = X.shape[0] > X.shape[1]
2011-08-24 21:31:07 +08:00
self.coef_ = orthogonal_mp(X, y, self.n_nonzero_coefs, self.tol,
precompute_gram=self.precompute_gram,
copy_X=self.copy_X).T
2011-07-29 07:54:23 +08:00
self._set_intercept(X_mean, y_mean, X_std)
2011-07-29 07:54:23 +08:00
return self