annotate pyCSalgos/OMP/omp_sk_bugfix.py @ 68:cab8a215f9a1 tip

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