annotate OMP/omp_sk_bugfix.py @ 1:2a2abf5092f8

Organized into test file and lib files Changed sklearn cholesky function to behave as the others: tol does not override number of atoms, but the two conditions work together
author nikcleju
date Thu, 20 Oct 2011 21:06:06 +0000
parents
children
rev   line source
nikcleju@1 1 """Orthogonal matching pursuit algorithms
nikcleju@1 2 """
nikcleju@1 3
nikcleju@1 4 # Author: Vlad Niculae
nikcleju@1 5 #
nikcleju@1 6 # License: BSD Style.
nikcleju@1 7
nikcleju@1 8 from warnings import warn
nikcleju@1 9
nikcleju@1 10 import numpy as np
nikcleju@1 11 from scipy import linalg
nikcleju@1 12 from scipy.linalg.lapack import get_lapack_funcs
nikcleju@1 13
nikcleju@1 14 #from .base import LinearModel
nikcleju@1 15 #from ..utils.arrayfuncs import solve_triangular
nikcleju@1 16 from sklearn.linear_model.base import LinearModel
nikcleju@1 17 from sklearn.utils.arrayfuncs import solve_triangular
nikcleju@1 18
nikcleju@1 19 premature = """ Orthogonal matching pursuit ended prematurely due to linear
nikcleju@1 20 dependence in the dictionary. The requested precision might not have been met.
nikcleju@1 21 """
nikcleju@1 22
nikcleju@1 23
nikcleju@1 24 def _cholesky_omp(X, y, n_nonzero_coefs, tol=None, copy_X=True):
nikcleju@1 25 """Orthogonal Matching Pursuit step using the Cholesky decomposition.
nikcleju@1 26
nikcleju@1 27 Parameters:
nikcleju@1 28 -----------
nikcleju@1 29 X: array, shape = (n_samples, n_features)
nikcleju@1 30 Input dictionary. Columns are assumed to have unit norm.
nikcleju@1 31
nikcleju@1 32 y: array, shape = (n_samples,)
nikcleju@1 33 Input targets
nikcleju@1 34
nikcleju@1 35 n_nonzero_coefs: int
nikcleju@1 36 Targeted number of non-zero elements
nikcleju@1 37
nikcleju@1 38 tol: float
nikcleju@1 39 Targeted squared error, if not None overrides n_nonzero_coefs.
nikcleju@1 40
nikcleju@1 41 copy_X: bool, optional
nikcleju@1 42 Whether the design matrix X must be copied by the algorithm. A false
nikcleju@1 43 value is only helpful if X is already Fortran-ordered, otherwise a
nikcleju@1 44 copy is made anyway.
nikcleju@1 45
nikcleju@1 46 Returns:
nikcleju@1 47 --------
nikcleju@1 48 gamma: array, shape = (n_nonzero_coefs,)
nikcleju@1 49 Non-zero elements of the solution
nikcleju@1 50
nikcleju@1 51 idx: array, shape = (n_nonzero_coefs,)
nikcleju@1 52 Indices of the positions of the elements in gamma within the solution
nikcleju@1 53 vector
nikcleju@1 54
nikcleju@1 55 """
nikcleju@1 56 if copy_X:
nikcleju@1 57 X = X.copy('F')
nikcleju@1 58 else: # even if we are allowed to overwrite, still copy it if bad order
nikcleju@1 59 X = np.asfortranarray(X)
nikcleju@1 60
nikcleju@1 61 min_float = np.finfo(X.dtype).eps
nikcleju@1 62 nrm2, swap = linalg.get_blas_funcs(('nrm2', 'swap'), (X,))
nikcleju@1 63 potrs, = get_lapack_funcs(('potrs',), (X,))
nikcleju@1 64
nikcleju@1 65 alpha = np.dot(X.T, y)
nikcleju@1 66 residual = y
nikcleju@1 67 n_active = 0
nikcleju@1 68 indices = range(X.shape[1]) # keeping track of swapping
nikcleju@1 69
nikcleju@1 70 #max_features = X.shape[1] if tol is not None else n_nonzero_coefs
nikcleju@1 71 # Nic: tol not None should not overide n_nonzero_coefs, but act together
nikcleju@1 72 max_features = n_nonzero_coefs
nikcleju@1 73
nikcleju@1 74 L = np.empty((max_features, max_features), dtype=X.dtype)
nikcleju@1 75 L[0, 0] = 1.
nikcleju@1 76
nikcleju@1 77 while True:
nikcleju@1 78 lam = np.argmax(np.abs(np.dot(X.T, residual)))
nikcleju@1 79 if lam < n_active or alpha[lam] ** 2 < min_float:
nikcleju@1 80 # atom already selected or inner product too small
nikcleju@1 81 warn(premature)
nikcleju@1 82 break
nikcleju@1 83 if n_active > 0:
nikcleju@1 84 # Updates the Cholesky decomposition of X' X
nikcleju@1 85 L[n_active, :n_active] = np.dot(X[:, :n_active].T, X[:, lam])
nikcleju@1 86 solve_triangular(L[:n_active, :n_active], L[n_active, :n_active])
nikcleju@1 87 v = nrm2(L[n_active, :n_active]) ** 2
nikcleju@1 88 if 1 - v <= min_float: # selected atoms are dependent
nikcleju@1 89 warn(premature)
nikcleju@1 90 break
nikcleju@1 91 L[n_active, n_active] = np.sqrt(1 - v)
nikcleju@1 92 X.T[n_active], X.T[lam] = swap(X.T[n_active], X.T[lam])
nikcleju@1 93 alpha[n_active], alpha[lam] = alpha[lam], alpha[n_active]
nikcleju@1 94 indices[n_active], indices[lam] = indices[lam], indices[n_active]
nikcleju@1 95 n_active += 1
nikcleju@1 96 # solves LL'x = y as a composition of two triangular systems
nikcleju@1 97 gamma, _ = potrs(L[:n_active, :n_active], alpha[:n_active], lower=True,
nikcleju@1 98 overwrite_b=False)
nikcleju@1 99
nikcleju@1 100 residual = y - np.dot(X[:, :n_active], gamma)
nikcleju@1 101 if tol is not None and nrm2(residual) ** 2 <= tol:
nikcleju@1 102 break
nikcleju@1 103 #elif n_active == max_features:
nikcleju@1 104 # Nic: tol not None should not overide n_nonzero_coefs, but act together
nikcleju@1 105 if n_active == max_features:
nikcleju@1 106 break
nikcleju@1 107
nikcleju@1 108 return gamma, indices[:n_active]
nikcleju@1 109
nikcleju@1 110
nikcleju@1 111 def _gram_omp(Gram, Xy, n_nonzero_coefs, tol_0=None, tol=None,
nikcleju@1 112 copy_Gram=True, copy_Xy=True):
nikcleju@1 113 """Orthogonal Matching Pursuit step on a precomputed Gram matrix.
nikcleju@1 114
nikcleju@1 115 This function uses the the Cholesky decomposition method.
nikcleju@1 116
nikcleju@1 117 Parameters:
nikcleju@1 118 -----------
nikcleju@1 119 Gram: array, shape = (n_features, n_features)
nikcleju@1 120 Gram matrix of the input data matrix
nikcleju@1 121
nikcleju@1 122 Xy: array, shape = (n_features,)
nikcleju@1 123 Input targets
nikcleju@1 124
nikcleju@1 125 n_nonzero_coefs: int
nikcleju@1 126 Targeted number of non-zero elements
nikcleju@1 127
nikcleju@1 128 tol_0: float
nikcleju@1 129 Squared norm of y, required if tol is not None.
nikcleju@1 130
nikcleju@1 131 tol: float
nikcleju@1 132 Targeted squared error, if not None overrides n_nonzero_coefs.
nikcleju@1 133
nikcleju@1 134 copy_Gram: bool, optional
nikcleju@1 135 Whether the gram matrix must be copied by the algorithm. A false
nikcleju@1 136 value is only helpful if it is already Fortran-ordered, otherwise a
nikcleju@1 137 copy is made anyway.
nikcleju@1 138
nikcleju@1 139 copy_Xy: bool, optional
nikcleju@1 140 Whether the covariance vector Xy must be copied by the algorithm.
nikcleju@1 141 If False, it may be overwritten.
nikcleju@1 142
nikcleju@1 143 Returns:
nikcleju@1 144 --------
nikcleju@1 145 gamma: array, shape = (n_nonzero_coefs,)
nikcleju@1 146 Non-zero elements of the solution
nikcleju@1 147
nikcleju@1 148 idx: array, shape = (n_nonzero_coefs,)
nikcleju@1 149 Indices of the positions of the elements in gamma within the solution
nikcleju@1 150 vector
nikcleju@1 151
nikcleju@1 152 """
nikcleju@1 153 Gram = Gram.copy('F') if copy_Gram else np.asfortranarray(Gram)
nikcleju@1 154
nikcleju@1 155 if copy_Xy:
nikcleju@1 156 Xy = Xy.copy()
nikcleju@1 157
nikcleju@1 158 min_float = np.finfo(Gram.dtype).eps
nikcleju@1 159 nrm2, swap = linalg.get_blas_funcs(('nrm2', 'swap'), (Gram,))
nikcleju@1 160 potrs, = get_lapack_funcs(('potrs',), (Gram,))
nikcleju@1 161
nikcleju@1 162 indices = range(len(Gram)) # keeping track of swapping
nikcleju@1 163 alpha = Xy
nikcleju@1 164 tol_curr = tol_0
nikcleju@1 165 delta = 0
nikcleju@1 166 n_active = 0
nikcleju@1 167
nikcleju@1 168 max_features = len(Gram) if tol is not None else n_nonzero_coefs
nikcleju@1 169 L = np.empty((max_features, max_features), dtype=Gram.dtype)
nikcleju@1 170 L[0, 0] = 1.
nikcleju@1 171
nikcleju@1 172 while True:
nikcleju@1 173 lam = np.argmax(np.abs(alpha))
nikcleju@1 174 if lam < n_active or alpha[lam] ** 2 < min_float:
nikcleju@1 175 # selected same atom twice, or inner product too small
nikcleju@1 176 warn(premature)
nikcleju@1 177 break
nikcleju@1 178 if n_active > 0:
nikcleju@1 179 L[n_active, :n_active] = Gram[lam, :n_active]
nikcleju@1 180 solve_triangular(L[:n_active, :n_active], L[n_active, :n_active])
nikcleju@1 181 v = nrm2(L[n_active, :n_active]) ** 2
nikcleju@1 182 if 1 - v <= min_float: # selected atoms are dependent
nikcleju@1 183 warn(premature)
nikcleju@1 184 break
nikcleju@1 185 L[n_active, n_active] = np.sqrt(1 - v)
nikcleju@1 186 Gram[n_active], Gram[lam] = swap(Gram[n_active], Gram[lam])
nikcleju@1 187 Gram.T[n_active], Gram.T[lam] = swap(Gram.T[n_active], Gram.T[lam])
nikcleju@1 188 indices[n_active], indices[lam] = indices[lam], indices[n_active]
nikcleju@1 189 Xy[n_active], Xy[lam] = Xy[lam], Xy[n_active]
nikcleju@1 190 n_active += 1
nikcleju@1 191 # solves LL'x = y as a composition of two triangular systems
nikcleju@1 192 gamma, _ = potrs(L[:n_active, :n_active], Xy[:n_active], lower=True,
nikcleju@1 193 overwrite_b=False)
nikcleju@1 194
nikcleju@1 195 beta = np.dot(Gram[:, :n_active], gamma)
nikcleju@1 196 alpha = Xy - beta
nikcleju@1 197 if tol is not None:
nikcleju@1 198 tol_curr += delta
nikcleju@1 199 delta = np.inner(gamma, beta[:n_active])
nikcleju@1 200 tol_curr -= delta
nikcleju@1 201 if tol_curr <= tol:
nikcleju@1 202 break
nikcleju@1 203 elif n_active == max_features:
nikcleju@1 204 break
nikcleju@1 205
nikcleju@1 206 return gamma, indices[:n_active]
nikcleju@1 207
nikcleju@1 208
nikcleju@1 209 def orthogonal_mp(X, y, n_nonzero_coefs=None, tol=None, precompute_gram=False,
nikcleju@1 210 copy_X=True):
nikcleju@1 211 """Orthogonal Matching Pursuit (OMP)
nikcleju@1 212
nikcleju@1 213 Solves n_targets Orthogonal Matching Pursuit problems.
nikcleju@1 214 An instance of the problem has the form:
nikcleju@1 215
nikcleju@1 216 When parametrized by the number of non-zero coefficients using
nikcleju@1 217 `n_nonzero_coefs`:
nikcleju@1 218 argmin ||y - X\gamma||^2 subject to ||\gamma||_0 <= n_{nonzero coefs}
nikcleju@1 219
nikcleju@1 220 When parametrized by error using the parameter `tol`:
nikcleju@1 221 argmin ||\gamma||_0 subject to ||y - X\gamma||^2 <= tol
nikcleju@1 222
nikcleju@1 223 Parameters
nikcleju@1 224 ----------
nikcleju@1 225 X: array, shape = (n_samples, n_features)
nikcleju@1 226 Input data. Columns are assumed to have unit norm.
nikcleju@1 227
nikcleju@1 228 y: array, shape = (n_samples,) or (n_samples, n_targets)
nikcleju@1 229 Input targets
nikcleju@1 230
nikcleju@1 231 n_nonzero_coefs: int
nikcleju@1 232 Desired number of non-zero entries in the solution. If None (by
nikcleju@1 233 default) this value is set to 10% of n_features.
nikcleju@1 234
nikcleju@1 235 tol: float
nikcleju@1 236 Maximum norm of the residual. If not None, overrides n_nonzero_coefs.
nikcleju@1 237
nikcleju@1 238 Nic: tol does not oveeride n_nonzero_coefs, the wto conditions act jointly!
nikcleju@1 239
nikcleju@1 240 precompute_gram: {True, False, 'auto'},
nikcleju@1 241 Whether to perform precomputations. Improves performance when n_targets
nikcleju@1 242 or n_samples is very large.
nikcleju@1 243
nikcleju@1 244 copy_X: bool, optional
nikcleju@1 245 Whether the design matrix X must be copied by the algorithm. A false
nikcleju@1 246 value is only helpful if X is already Fortran-ordered, otherwise a
nikcleju@1 247 copy is made anyway.
nikcleju@1 248
nikcleju@1 249 Returns
nikcleju@1 250 -------
nikcleju@1 251 coef: array, shape = (n_features,) or (n_features, n_targets)
nikcleju@1 252 Coefficients of the OMP solution
nikcleju@1 253
nikcleju@1 254 See also
nikcleju@1 255 --------
nikcleju@1 256 OrthogonalMatchingPursuit
nikcleju@1 257 orthogonal_mp_gram
nikcleju@1 258 lars_path
nikcleju@1 259 decomposition.sparse_encode
nikcleju@1 260 decomposition.sparse_encode_parallel
nikcleju@1 261
nikcleju@1 262 Notes
nikcleju@1 263 -----
nikcleju@1 264 Orthogonal matching pursuit was introduced in G. Mallat, Z. Zhang,
nikcleju@1 265 Matching pursuits with time-frequency dictionaries, IEEE Transactions on
nikcleju@1 266 Signal Processing, Vol. 41, No. 12. (December 1993), pp. 3397-3415.
nikcleju@1 267 (http://blanche.polytechnique.fr/~mallat/papiers/MallatPursuit93.pdf)
nikcleju@1 268
nikcleju@1 269 This implementation is based on Rubinstein, R., Zibulevsky, M. and Elad,
nikcleju@1 270 M., Efficient Implementation of the K-SVD Algorithm using Batch Orthogonal
nikcleju@1 271 Matching Pursuit Technical Report - CS Technion, April 2008.
nikcleju@1 272 http://www.cs.technion.ac.il/~ronrubin/Publications/KSVX-OMP-v2.pdf
nikcleju@1 273
nikcleju@1 274 """
nikcleju@1 275 X, y = map(np.asanyarray, (X, y))
nikcleju@1 276 if y.ndim == 1:
nikcleju@1 277 y = y[:, np.newaxis]
nikcleju@1 278 if copy_X:
nikcleju@1 279 X = X.copy('F')
nikcleju@1 280 copy_X = False
nikcleju@1 281 else:
nikcleju@1 282 X = np.asfortranarray(X)
nikcleju@1 283 if y.shape[1] > 1: # subsequent targets will be affected
nikcleju@1 284 copy_X = True
nikcleju@1 285 if n_nonzero_coefs == None and tol == None:
nikcleju@1 286 n_nonzero_coefs = int(0.1 * X.shape[1])
nikcleju@1 287 if tol is not None and tol < 0:
nikcleju@1 288 raise ValueError("Epsilon cannot be negative")
nikcleju@1 289 if tol is None and n_nonzero_coefs <= 0:
nikcleju@1 290 raise ValueError("The number of atoms must be positive")
nikcleju@1 291 if tol is None and n_nonzero_coefs > X.shape[1]:
nikcleju@1 292 raise ValueError("The number of atoms cannot be more than the number \
nikcleju@1 293 of features")
nikcleju@1 294 if precompute_gram == 'auto':
nikcleju@1 295 precompute_gram = X.shape[0] > X.shape[1]
nikcleju@1 296 if precompute_gram:
nikcleju@1 297 G = np.dot(X.T, X)
nikcleju@1 298 G = np.asfortranarray(G)
nikcleju@1 299 Xy = np.dot(X.T, y)
nikcleju@1 300 if tol is not None:
nikcleju@1 301 norms_squared = np.sum((y ** 2), axis=0)
nikcleju@1 302 else:
nikcleju@1 303 norms_squared = None
nikcleju@1 304 return orthogonal_mp_gram(G, Xy, n_nonzero_coefs, tol, norms_squared,
nikcleju@1 305 copy_Gram=copy_X, copy_Xy=False)
nikcleju@1 306
nikcleju@1 307 coef = np.zeros((X.shape[1], y.shape[1]))
nikcleju@1 308 for k in xrange(y.shape[1]):
nikcleju@1 309 x, idx = _cholesky_omp(X, y[:, k], n_nonzero_coefs, tol,
nikcleju@1 310 copy_X=copy_X)
nikcleju@1 311 coef[idx, k] = x
nikcleju@1 312 return np.squeeze(coef)
nikcleju@1 313
nikcleju@1 314
nikcleju@1 315 def orthogonal_mp_gram(Gram, Xy, n_nonzero_coefs=None, tol=None,
nikcleju@1 316 norms_squared=None, copy_Gram=True,
nikcleju@1 317 copy_Xy=True):
nikcleju@1 318 """Gram Orthogonal Matching Pursuit (OMP)
nikcleju@1 319
nikcleju@1 320 Solves n_targets Orthogonal Matching Pursuit problems using only
nikcleju@1 321 the Gram matrix X.T * X and the product X.T * y.
nikcleju@1 322
nikcleju@1 323 Parameters
nikcleju@1 324 ----------
nikcleju@1 325 Gram: array, shape = (n_features, n_features)
nikcleju@1 326 Gram matrix of the input data: X.T * X
nikcleju@1 327
nikcleju@1 328 Xy: array, shape = (n_features,) or (n_features, n_targets)
nikcleju@1 329 Input targets multiplied by X: X.T * y
nikcleju@1 330
nikcleju@1 331 n_nonzero_coefs: int
nikcleju@1 332 Desired number of non-zero entries in the solution. If None (by
nikcleju@1 333 default) this value is set to 10% of n_features.
nikcleju@1 334
nikcleju@1 335 tol: float
nikcleju@1 336 Maximum norm of the residual. If not None, overrides n_nonzero_coefs.
nikcleju@1 337
nikcleju@1 338 norms_squared: array-like, shape = (n_targets,)
nikcleju@1 339 Squared L2 norms of the lines of y. Required if tol is not None.
nikcleju@1 340
nikcleju@1 341 copy_Gram: bool, optional
nikcleju@1 342 Whether the gram matrix must be copied by the algorithm. A false
nikcleju@1 343 value is only helpful if it is already Fortran-ordered, otherwise a
nikcleju@1 344 copy is made anyway.
nikcleju@1 345
nikcleju@1 346 copy_Xy: bool, optional
nikcleju@1 347 Whether the covariance vector Xy must be copied by the algorithm.
nikcleju@1 348 If False, it may be overwritten.
nikcleju@1 349
nikcleju@1 350 Returns
nikcleju@1 351 -------
nikcleju@1 352 coef: array, shape = (n_features,) or (n_features, n_targets)
nikcleju@1 353 Coefficients of the OMP solution
nikcleju@1 354
nikcleju@1 355 See also
nikcleju@1 356 --------
nikcleju@1 357 OrthogonalMatchingPursuit
nikcleju@1 358 orthogonal_mp
nikcleju@1 359 lars_path
nikcleju@1 360 decomposition.sparse_encode
nikcleju@1 361 decomposition.sparse_encode_parallel
nikcleju@1 362
nikcleju@1 363 Notes
nikcleju@1 364 -----
nikcleju@1 365 Orthogonal matching pursuit was introduced in G. Mallat, Z. Zhang,
nikcleju@1 366 Matching pursuits with time-frequency dictionaries, IEEE Transactions on
nikcleju@1 367 Signal Processing, Vol. 41, No. 12. (December 1993), pp. 3397-3415.
nikcleju@1 368 (http://blanche.polytechnique.fr/~mallat/papiers/MallatPursuit93.pdf)
nikcleju@1 369
nikcleju@1 370 This implementation is based on Rubinstein, R., Zibulevsky, M. and Elad,
nikcleju@1 371 M., Efficient Implementation of the K-SVD Algorithm using Batch Orthogonal
nikcleju@1 372 Matching Pursuit Technical Report - CS Technion, April 2008.
nikcleju@1 373 http://www.cs.technion.ac.il/~ronrubin/Publications/KSVX-OMP-v2.pdf
nikcleju@1 374
nikcleju@1 375 """
nikcleju@1 376 Gram, Xy = map(np.asanyarray, (Gram, Xy))
nikcleju@1 377 if Xy.ndim == 1:
nikcleju@1 378 Xy = Xy[:, np.newaxis]
nikcleju@1 379 if tol is not None:
nikcleju@1 380 norms_squared = [norms_squared]
nikcleju@1 381
nikcleju@1 382 if n_nonzero_coefs == None and tol is None:
nikcleju@1 383 n_nonzero_coefs = int(0.1 * len(Gram))
nikcleju@1 384 if tol is not None and norms_squared == None:
nikcleju@1 385 raise ValueError('Gram OMP needs the precomputed norms in order \
nikcleju@1 386 to evaluate the error sum of squares.')
nikcleju@1 387 if tol is not None and tol < 0:
nikcleju@1 388 raise ValueError("Epsilon cennot be negative")
nikcleju@1 389 if tol is None and n_nonzero_coefs <= 0:
nikcleju@1 390 raise ValueError("The number of atoms must be positive")
nikcleju@1 391 if tol is None and n_nonzero_coefs > len(Gram):
nikcleju@1 392 raise ValueError("The number of atoms cannot be more than the number \
nikcleju@1 393 of features")
nikcleju@1 394 coef = np.zeros((len(Gram), Xy.shape[1]))
nikcleju@1 395 for k in range(Xy.shape[1]):
nikcleju@1 396 x, idx = _gram_omp(Gram, Xy[:, k], n_nonzero_coefs,
nikcleju@1 397 norms_squared[k] if tol is not None else None, tol,
nikcleju@1 398 copy_Gram=copy_Gram, copy_Xy=copy_Xy)
nikcleju@1 399 coef[idx, k] = x
nikcleju@1 400 return np.squeeze(coef)
nikcleju@1 401
nikcleju@1 402
nikcleju@1 403 class OrthogonalMatchingPursuit(LinearModel):
nikcleju@1 404 """Orthogonal Mathching Pursuit model (OMP)
nikcleju@1 405
nikcleju@1 406 Parameters
nikcleju@1 407 ----------
nikcleju@1 408 n_nonzero_coefs: int, optional
nikcleju@1 409 Desired number of non-zero entries in the solution. If None (by
nikcleju@1 410 default) this value is set to 10% of n_features.
nikcleju@1 411
nikcleju@1 412 tol: float, optional
nikcleju@1 413 Maximum norm of the residual. If not None, overrides n_nonzero_coefs.
nikcleju@1 414
nikcleju@1 415 fit_intercept: boolean, optional
nikcleju@1 416 whether to calculate the intercept for this model. If set
nikcleju@1 417 to false, no intercept will be used in calculations
nikcleju@1 418 (e.g. data is expected to be already centered).
nikcleju@1 419
nikcleju@1 420 normalize: boolean, optional
nikcleju@1 421 If False, the regressors X are assumed to be already normalized.
nikcleju@1 422
nikcleju@1 423 precompute_gram: {True, False, 'auto'},
nikcleju@1 424 Whether to use a precomputed Gram and Xy matrix to speed up
nikcleju@1 425 calculations. Improves performance when `n_targets` or `n_samples` is
nikcleju@1 426 very large. Note that if you already have such matrices, you can pass
nikcleju@1 427 them directly to the fit method.
nikcleju@1 428
nikcleju@1 429 copy_X: bool, optional
nikcleju@1 430 Whether the design matrix X must be copied by the algorithm. A false
nikcleju@1 431 value is only helpful if X is already Fortran-ordered, otherwise a
nikcleju@1 432 copy is made anyway.
nikcleju@1 433
nikcleju@1 434 copy_Gram: bool, optional
nikcleju@1 435 Whether the gram matrix must be copied by the algorithm. A false
nikcleju@1 436 value is only helpful if X is already Fortran-ordered, otherwise a
nikcleju@1 437 copy is made anyway.
nikcleju@1 438
nikcleju@1 439 copy_Xy: bool, optional
nikcleju@1 440 Whether the covariance vector Xy must be copied by the algorithm.
nikcleju@1 441 If False, it may be overwritten.
nikcleju@1 442
nikcleju@1 443
nikcleju@1 444 Attributes
nikcleju@1 445 ----------
nikcleju@1 446 coef_: array, shape = (n_features,) or (n_features, n_targets)
nikcleju@1 447 parameter vector (w in the fomulation formula)
nikcleju@1 448
nikcleju@1 449 intercept_: float or array, shape =(n_targets,)
nikcleju@1 450 independent term in decision function.
nikcleju@1 451
nikcleju@1 452 Notes
nikcleju@1 453 -----
nikcleju@1 454 Orthogonal matching pursuit was introduced in G. Mallat, Z. Zhang,
nikcleju@1 455 Matching pursuits with time-frequency dictionaries, IEEE Transactions on
nikcleju@1 456 Signal Processing, Vol. 41, No. 12. (December 1993), pp. 3397-3415.
nikcleju@1 457 (http://blanche.polytechnique.fr/~mallat/papiers/MallatPursuit93.pdf)
nikcleju@1 458
nikcleju@1 459 This implementation is based on Rubinstein, R., Zibulevsky, M. and Elad,
nikcleju@1 460 M., Efficient Implementation of the K-SVD Algorithm using Batch Orthogonal
nikcleju@1 461 Matching Pursuit Technical Report - CS Technion, April 2008.
nikcleju@1 462 http://www.cs.technion.ac.il/~ronrubin/Publications/KSVX-OMP-v2.pdf
nikcleju@1 463
nikcleju@1 464 See also
nikcleju@1 465 --------
nikcleju@1 466 orthogonal_mp
nikcleju@1 467 orthogonal_mp_gram
nikcleju@1 468 lars_path
nikcleju@1 469 Lars
nikcleju@1 470 LassoLars
nikcleju@1 471 decomposition.sparse_encode
nikcleju@1 472 decomposition.sparse_encode_parallel
nikcleju@1 473
nikcleju@1 474 """
nikcleju@1 475 def __init__(self, copy_X=True, copy_Gram=True,
nikcleju@1 476 copy_Xy=True, n_nonzero_coefs=None, tol=None,
nikcleju@1 477 fit_intercept=True, normalize=True, precompute_gram=False):
nikcleju@1 478 self.n_nonzero_coefs = n_nonzero_coefs
nikcleju@1 479 self.tol = tol
nikcleju@1 480 self.fit_intercept = fit_intercept
nikcleju@1 481 self.normalize = normalize
nikcleju@1 482 self.precompute_gram = precompute_gram
nikcleju@1 483 self.copy_Gram = copy_Gram
nikcleju@1 484 self.copy_Xy = copy_Xy
nikcleju@1 485 self.copy_X = copy_X
nikcleju@1 486
nikcleju@1 487 def fit(self, X, y, Gram=None, Xy=None):
nikcleju@1 488 """Fit the model using X, y as training data.
nikcleju@1 489
nikcleju@1 490 Parameters
nikcleju@1 491 ----------
nikcleju@1 492 X: array-like, shape = (n_samples, n_features)
nikcleju@1 493 Training data.
nikcleju@1 494
nikcleju@1 495 y: array-like, shape = (n_samples,) or (n_samples, n_targets)
nikcleju@1 496 Target values.
nikcleju@1 497
nikcleju@1 498 Gram: array-like, shape = (n_features, n_features) (optional)
nikcleju@1 499 Gram matrix of the input data: X.T * X
nikcleju@1 500
nikcleju@1 501 Xy: array-like, shape = (n_features,) or (n_features, n_targets)
nikcleju@1 502 (optional)
nikcleju@1 503 Input targets multiplied by X: X.T * y
nikcleju@1 504
nikcleju@1 505
nikcleju@1 506 Returns
nikcleju@1 507 -------
nikcleju@1 508 self: object
nikcleju@1 509 returns an instance of self.
nikcleju@1 510 """
nikcleju@1 511 X = np.atleast_2d(X)
nikcleju@1 512 y = np.atleast_1d(y)
nikcleju@1 513 n_features = X.shape[1]
nikcleju@1 514
nikcleju@1 515 X, y, X_mean, y_mean, X_std = self._center_data(X, y,
nikcleju@1 516 self.fit_intercept,
nikcleju@1 517 self.normalize,
nikcleju@1 518 self.copy_X)
nikcleju@1 519
nikcleju@1 520 if self.n_nonzero_coefs == None and self.tol is None:
nikcleju@1 521 self.n_nonzero_coefs = int(0.1 * n_features)
nikcleju@1 522
nikcleju@1 523 if Gram is not None:
nikcleju@1 524 Gram = np.atleast_2d(Gram)
nikcleju@1 525
nikcleju@1 526 if self.copy_Gram:
nikcleju@1 527 copy_Gram = False
nikcleju@1 528 Gram = Gram.copy('F')
nikcleju@1 529 else:
nikcleju@1 530 Gram = np.asfortranarray(Gram)
nikcleju@1 531
nikcleju@1 532 copy_Gram = self.copy_Gram
nikcleju@1 533 if y.shape[1] > 1: # subsequent targets will be affected
nikcleju@1 534 copy_Gram = True
nikcleju@1 535
nikcleju@1 536 if Xy is None:
nikcleju@1 537 Xy = np.dot(X.T, y)
nikcleju@1 538 else:
nikcleju@1 539 if self.copy_Xy:
nikcleju@1 540 Xy = Xy.copy()
nikcleju@1 541 if self.normalize:
nikcleju@1 542 if len(Xy.shape) == 1:
nikcleju@1 543 Xy /= X_std
nikcleju@1 544 else:
nikcleju@1 545 Xy /= X_std[:, np.newaxis]
nikcleju@1 546
nikcleju@1 547 if self.normalize:
nikcleju@1 548 Gram /= X_std
nikcleju@1 549 Gram /= X_std[:, np.newaxis]
nikcleju@1 550
nikcleju@1 551 norms_sq = np.sum(y ** 2, axis=0) if self.tol is not None else None
nikcleju@1 552 self.coef_ = orthogonal_mp_gram(Gram, Xy, self.n_nonzero_coefs,
nikcleju@1 553 self.tol, norms_sq,
nikcleju@1 554 copy_Gram, True).T
nikcleju@1 555 else:
nikcleju@1 556 precompute_gram = self.precompute_gram
nikcleju@1 557 if precompute_gram == 'auto':
nikcleju@1 558 precompute_gram = X.shape[0] > X.shape[1]
nikcleju@1 559 self.coef_ = orthogonal_mp(X, y, self.n_nonzero_coefs, self.tol,
nikcleju@1 560 precompute_gram=self.precompute_gram,
nikcleju@1 561 copy_X=self.copy_X).T
nikcleju@1 562
nikcleju@1 563 self._set_intercept(X_mean, y_mean, X_std)
nikcleju@1 564 return self