Coverage for nilearn/decoding/space_net.py: 12%
365 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-20 10:58 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-20 10:58 +0200
1"""sklearn-compatible implementation of spatially structured learners.
3For example: TV-L1, Graph-Net, etc
4"""
6import collections
7import time
8import warnings
9from functools import partial
10from typing import ClassVar
12import numpy as np
13from joblib import Memory, Parallel, delayed
14from scipy import stats
15from scipy.ndimage import binary_dilation, binary_erosion, gaussian_filter
16from sklearn.feature_selection import SelectPercentile, f_classif, f_regression
17from sklearn.linear_model import LinearRegression
18from sklearn.linear_model._base import _preprocess_data as center_data
19from sklearn.metrics import accuracy_score
20from sklearn.model_selection import check_cv
21from sklearn.preprocessing import LabelBinarizer
22from sklearn.utils import check_array, check_X_y
23from sklearn.utils.estimator_checks import check_is_fitted
24from sklearn.utils.extmath import safe_sparse_dot
26from nilearn._utils import fill_doc, logger
27from nilearn._utils.cache_mixin import CacheMixin
28from nilearn._utils.logger import find_stack_level
29from nilearn._utils.masker_validation import check_embedded_masker
30from nilearn._utils.param_validation import (
31 adjust_screening_percentile,
32 check_params,
33)
34from nilearn.image import get_data
35from nilearn.maskers import SurfaceMasker
36from nilearn.masking import unmask_from_to_3d_array
37from nilearn.surface import SurfaceImage
39from .space_net_solvers import (
40 graph_net_logistic,
41 graph_net_squared_loss,
42 tvl1_solver,
43)
46def _crop_mask(mask):
47 """Crops input mask to produce tighter (i.e smaller) bounding box \
48 with the same support (active voxels).
49 """
50 idx = np.where(mask)
51 if idx[0].size == 0:
52 raise ValueError(
53 "Empty mask: if you have given a mask, it is "
54 "empty, and if you have not given a mask, the "
55 "mask-extraction routines have failed. Please "
56 "provide an appropriate mask."
57 )
58 i_min = max(idx[0].min() - 1, 0)
59 i_max = idx[0].max()
60 j_min = max(idx[1].min() - 1, 0)
61 j_max = idx[1].max()
62 k_min = max(idx[2].min() - 1, 0)
63 k_max = idx[2].max()
64 return mask[i_min : i_max + 1, j_min : j_max + 1, k_min : k_max + 1]
67@fill_doc
68def _univariate_feature_screening(
69 X, y, mask, is_classif, screening_percentile, smoothing_fwhm=2.0
70):
71 """Select the most import features, via a univariate test.
73 Parameters
74 ----------
75 X : ndarray, shape (n_samples, n_features)
76 Design matrix.
78 y : ndarray, shape (n_samples,)
79 Response Vector.
81 mask : ndarray or booleans, shape (nx, ny, nz)
82 Mask defining brain Rois.
84 is_classif : bool
85 Flag telling whether the learning task is classification or regression.
87 screening_percentile : float in the closed interval [0., 100.]
88 Only the `screening_percentile * 100" percent most import voxels will
89 be retained.
90 %(smoothing_fwhm)s
91 Default=2.
93 Returns
94 -------
95 X_ : ndarray, shape (n_samples, n_features_)
96 Reduced design matrix with only columns corresponding to the voxels
97 retained after screening.
99 mask_ : ndarray of booleans, shape (nx, ny, nz)
100 Mask with support reduced to only contain voxels retained after
101 screening.
103 support : ndarray of ints, shape (n_features_,)
104 Support of the screened mask, as a subset of the support of the
105 original mask.
106 """
107 # smooth the data (with isotropic Gaussian kernel) before screening
108 if smoothing_fwhm > 0.0:
109 sX = np.empty(X.shape)
110 for sample in range(sX.shape[0]):
111 sX[sample] = gaussian_filter(
112 unmask_from_to_3d_array(
113 X[sample].copy(), # avoid modifying X
114 mask,
115 ),
116 (smoothing_fwhm, smoothing_fwhm, smoothing_fwhm),
117 )[mask]
118 else:
119 sX = X
121 # do feature screening proper
122 selector = SelectPercentile(
123 f_classif if is_classif else f_regression,
124 percentile=screening_percentile,
125 ).fit(sX, y)
126 support = selector.get_support()
128 # erode and then dilate mask, thus obtaining a "cleaner" version of
129 # the mask on which a spatial prior actually makes sense
130 mask_ = mask.copy()
131 mask_[mask] = support > 0
132 mask_ = binary_dilation(binary_erosion(mask_)).astype(bool)
133 mask_[np.logical_not(mask)] = 0
134 support = mask_[mask]
135 X = X[:, support]
137 return X, mask_, support
140def _space_net_alpha_grid(
141 X, y, eps=1e-3, n_alphas=10, l1_ratio=1.0, logistic=False
142):
143 """Compute the grid of alpha values for TV-L1 and Graph-Net.
145 Parameters
146 ----------
147 X : ndarray, shape (n_samples, n_features)
148 Training data (design matrix).
150 y : ndarray, shape (n_samples,)
151 Target / response vector.
153 l1_ratio : float, default=1
154 The ElasticNet mixing parameter, with ``0 <= l1_ratio <= 1``.
155 For ``l1_ratio = 0`` the penalty is purely a spatial prior
156 (Graph-Net, TV, etc.). ``For l1_ratio = 1`` it is an L1 penalty.
157 For ``0 < l1_ratio < 1``, the penalty is a combination of L1
158 and a spatial prior.
160 eps : float, default=1e-3
161 Length of the path. ``eps=1e-3`` means that
162 ``alpha_min / alpha_max = 1e-3``.
164 n_alphas : int, default=10
165 Number of alphas along the regularization path.
167 logistic : bool, default=False
168 Indicates where the underlying loss function is logistic.
170 """
171 if logistic:
172 # Computes the theoretical upper bound for the overall
173 # regularization, as derived in "An Interior-Point Method for
174 # Large-Scale l1-Regularized Logistic Regression", by Koh, Kim,
175 # Boyd, in Journal of Machine Learning Research, 8:1519-1555,
176 # July 2007.
177 # url: https://web.stanford.edu/~boyd/papers/pdf/l1_logistic_reg.pdf
178 m = float(y.size)
179 m_plus = float(y[y == 1].size)
180 m_minus = float(y[y == -1].size)
181 b = np.zeros_like(y)
182 b[y == 1] = m_minus / m
183 b[y == -1] = -m_plus / m
184 alpha_max = np.max(np.abs(X.T.dot(b)))
186 # tt may happen that b is in the kernel of X.T!
187 if alpha_max == 0.0:
188 alpha_max = np.abs(np.dot(X.T, y)).max()
189 else:
190 alpha_max = np.abs(np.dot(X.T, y)).max()
192 # prevent alpha_max from exploding when l1_ratio = 0
193 if l1_ratio == 0.0:
194 l1_ratio = 1e-3
195 alpha_max /= l1_ratio
197 if n_alphas == 1:
198 return np.array([alpha_max])
200 alpha_min = alpha_max * eps
201 return np.logspace(np.log10(alpha_min), np.log10(alpha_max), num=n_alphas)[
202 ::-1
203 ]
206class _EarlyStoppingCallback:
207 """Out-of-bag early stopping.
209 A callable that returns True when the test error starts
210 rising. We use a Spearman correlation (between X_test.w and y_test)
211 for scoring.
212 """
214 def __init__(self, X_test, y_test, is_classif, debias=False, verbose=0):
215 self.X_test = X_test
216 self.y_test = y_test
217 self.is_classif = is_classif
218 self.debias = debias
219 self.verbose = verbose
220 self.tol = -1e-4 if self.is_classif else -1e-2
221 self.test_scores = []
222 self.counter = 0.0
224 def __call__(self, variables):
225 """Perform callback."""
226 # misc
227 if not isinstance(variables, dict):
228 variables = {"w": variables}
229 self.counter += 1
230 w = variables["w"]
232 # use Spearman score as stopping criterion
233 score = self.test_score(w)[0]
235 self.test_scores.append(score)
236 if self.counter <= 20 or self.counter % 10 != 2:
237 return
239 # check whether score increased on average over last 5 iterations
240 if (
241 len(self.test_scores) > 4
242 and np.mean(np.diff(self.test_scores[-5:][::-1])) >= self.tol
243 ):
244 message = "."
245 if self.verbose > 1:
246 message = (
247 f"Early stopping.\nTest score: {score:.8f} {40 * '-'}"
248 )
249 logger.log(
250 message,
251 verbose=self.verbose,
252 )
253 return True
255 logger.log(
256 f"Test score: {score:.8f}", verbose=self.verbose, msg_level=1
257 )
258 return False
260 def _debias(self, w):
261 """Debias w by rescaling the coefficients by a fixed factor.
263 Precisely, the scaling factor is: <y_pred, y_test> / ||y_test||^2.
264 """
265 y_pred = np.dot(self.X_test, w)
266 scaling = np.dot(y_pred, y_pred)
267 if scaling > 0.0:
268 scaling = np.dot(y_pred, self.y_test) / scaling
269 w *= scaling
270 return w
272 def test_score(self, w):
273 """Compute test score for model, given weights map `w`.
275 We use correlations between linear prediction and
276 ground truth (y_test).
278 We return 2 scores for model selection: one is the Spearman
279 correlation, which captures ordering between input and
280 output, but tends to have 'flat' regions. The other
281 is the Pearson correlation, that we can use to disambiguate
282 between regions with equivalent Spearman correlation.
284 """
285 if self.is_classif:
286 w = w[:-1]
287 if np.ptp(w) == 0:
288 # constant map, there is nothing
289 return (-np.inf, -np.inf)
290 y_pred = np.dot(self.X_test, w)
291 spearman_score = stats.spearmanr(y_pred, self.y_test)[0]
292 pearson_score = np.corrcoef(y_pred, self.y_test)[1, 0]
293 if self.is_classif:
294 return spearman_score, pearson_score
295 else:
296 return pearson_score, spearman_score
299@fill_doc
300def path_scores(
301 solver,
302 X,
303 y,
304 mask,
305 alphas,
306 l1_ratios,
307 train,
308 test,
309 solver_params,
310 is_classif=False,
311 n_alphas=10,
312 eps=1e-3,
313 key=None,
314 debias=False,
315 screening_percentile=20.0,
316 verbose=1,
317):
318 """Compute scores of different alphas in regression \
319 and classification used by CV objects.
321 Parameters
322 ----------
323 X : 2D array of shape (n_samples, n_features)
324 Design matrix, one row per sample point.
326 y : 1D array of length n_samples
327 Response vector; one value per sample.
329 mask : 3D arrays of :obj:`bool`
330 Mask defining brain regions that we work on.
332 alphas : :obj:`list` of :obj:`float`
333 List of regularization parameters being considered.
335 train : array or :obj:`list` of :obj:`int`:
336 List of indices for the train samples.
338 test : array or :obj:`list` of :obj:`int`
339 List of indices for the test samples.
341 l1_ratios : :obj:`float` or :obj:`list` of floats in the interval [0, 1]
342 Constant that mixes L1 and TV (resp. Graph-Net) penalization.
343 l1_ratios == 0: just smooth. l1_ratios == 1: just lasso.
345 eps : :obj:`float`, default=1e-3
346 Length of the path. For example, ``eps=1e-3`` means that
347 ``alpha_min / alpha_max = 1e-3``.
349 n_alphas : :obj:`int`, default=10
350 Generate this number of alphas per regularization path.
351 This parameter is mutually exclusive with the `alphas` parameter.
353 solver : function handle
354 See for example tv.TVl1Classifier documentation.
356 solver_params : :obj:`dict`
357 Dictionary of param-value pairs to be passed to solver.
359 is_classif : :obj:`bool`, default=False
360 Indicates whether the loss is a classification loss or a
361 regression loss.
363 key: ??? TODO: Add description.
365 debias : :obj:`bool`, default=False
366 If set, then the estimated weights maps will be debiased.
368 screening_percentile : :obj:`float` in the interval [0, 100], \
369 default=20.0
370 Percentile value for :term:`ANOVA` univariate feature selection.
371 A value of 100 means 'keep all features'.
372 This percentile is expressed
373 w.r.t the volume of a standard (MNI152) brain, and so is corrected
374 at runtime to correspond to the volume of the user-supplied mask
375 (which is typically smaller). If '100' is given, all the features
376 are used, regardless of the number of voxels.
377 %(verbose)s
379 """
380 if l1_ratios is None:
381 raise ValueError("l1_ratios must be specified!")
383 # misc
384 _, n_features = X.shape
385 verbose = int(verbose if verbose is not None else 0)
387 # Univariate feature screening. Note that if we have only as few as 100
388 # features in the mask's support, then we should use all of them to
389 # learn the model i.e disable this screening)
390 do_screening = (n_features > 100) and screening_percentile < 100.0
391 if do_screening:
392 X, mask, support = _univariate_feature_screening(
393 X, y, mask, is_classif, screening_percentile
394 )
396 # crop the mask to have a tighter bounding box
397 mask = _crop_mask(mask)
399 # get train and test data
400 X_train, y_train = X[train].copy(), y[train].copy()
401 X_test, y_test = X[test].copy(), y[test].copy()
403 # it is essential to center the data in regression
404 X_train, y_train, _, y_train_mean, _ = center_data(
405 X_train, y_train, fit_intercept=True, copy=False
406 )
408 # misc
409 if not isinstance(l1_ratios, collections.abc.Iterable):
410 l1_ratios = [l1_ratios]
411 l1_ratios = sorted(l1_ratios)[::-1] # from large to small l1_ratios
412 best_score = -np.inf
413 best_secondary_score = -np.inf
414 best_l1_ratio = l1_ratios[0]
415 best_alpha = None
416 best_init = None
417 all_test_scores = []
418 if len(test) > 0.0:
419 # do l1_ratio path
420 for l1_ratio in l1_ratios:
421 this_test_scores = []
423 # make alpha grid
424 if alphas is None:
425 alphas_ = _space_net_alpha_grid(
426 X_train,
427 y_train,
428 l1_ratio=l1_ratio,
429 eps=eps,
430 n_alphas=n_alphas,
431 logistic=is_classif,
432 )
433 else:
434 alphas_ = alphas
435 alphas_ = sorted(alphas_)[::-1] # from large to small l1_ratios
437 # do alpha path
438 if best_alpha is None:
439 best_alpha = alphas_[0]
440 init = None
441 path_solver_params = solver_params.copy()
442 # Use a lighter tol during the path
443 path_solver_params["tol"] = 2 * path_solver_params.get("tol", 1e-4)
444 for alpha in alphas_:
445 # setup callback mechanism for early stopping
446 early_stopper = _EarlyStoppingCallback(
447 X_test,
448 y_test,
449 is_classif=is_classif,
450 debias=debias,
451 verbose=verbose,
452 )
453 w, _, init = solver(
454 X_train,
455 y_train,
456 alpha,
457 l1_ratio,
458 mask=mask,
459 init=init,
460 callback=early_stopper,
461 verbose=max(verbose - 1, 0.0),
462 **path_solver_params,
463 )
465 # We use 2 scores for model selection: the second one is to
466 # disambiguate between regions of equivalent Spearman
467 # correlations
468 score, secondary_score = early_stopper.test_score(w)
469 this_test_scores.append(score)
470 if np.isfinite(score) and (
471 score > best_score
472 or (
473 score == best_score
474 and secondary_score > best_secondary_score
475 )
476 ):
477 best_secondary_score = secondary_score
478 best_score = score
479 best_l1_ratio = l1_ratio
480 best_alpha = alpha
481 best_init = init.copy()
482 all_test_scores.append(this_test_scores)
483 else:
484 if alphas is None:
485 alphas_ = _space_net_alpha_grid(
486 X_train,
487 y_train,
488 l1_ratio=best_l1_ratio,
489 eps=eps,
490 n_alphas=n_alphas,
491 logistic=is_classif,
492 )
493 else:
494 alphas_ = alphas
495 best_alpha = alphas_[0]
497 # re-fit best model to high precision (i.e without early stopping, etc.)
498 best_w, _, init = solver(
499 X_train,
500 y_train,
501 best_alpha,
502 best_l1_ratio,
503 mask=mask,
504 init=best_init,
505 verbose=max(verbose - 1, 0),
506 **solver_params,
507 )
508 if debias:
509 best_w = _EarlyStoppingCallback(
510 X_test,
511 y_test,
512 is_classif=is_classif,
513 debias=debias,
514 verbose=verbose,
515 )._debias(best_w)
517 if len(test) == 0.0:
518 all_test_scores.append(np.nan)
520 # unmask univariate screening
521 if do_screening:
522 w_ = np.zeros(len(support))
523 if is_classif:
524 w_ = np.append(w_, best_w[-1])
525 w_[:-1][support] = best_w[:-1]
526 else:
527 w_[support] = best_w
528 best_w = w_
530 if len(best_w) == n_features:
531 # TODO: implement with Xmean
532 best_w = np.append(best_w, 0.0)
534 all_test_scores = np.array(all_test_scores)
535 return (
536 all_test_scores,
537 best_w,
538 best_alpha,
539 best_l1_ratio,
540 alphas_,
541 y_train_mean,
542 key,
543 )
546@fill_doc
547class BaseSpaceNet(CacheMixin, LinearRegression):
548 """Regression and classification learners with sparsity and spatial priors.
550 `SpaceNet` implements Graph-Net and TV-L1 priors /
551 penalties. Thus, the penalty is a sum of an L1 term and a spatial term. The
552 aim of such a hybrid prior is to obtain weights maps which are structured
553 (due to the spatial prior) and sparse (enforced by L1 norm).
555 Parameters
556 ----------
557 penalty : :obj:`str`, default='graph-net'
558 Penalty to used in the model. Can be 'graph-net' or 'tv-l1'.
560 loss : :obj:`str`, default=None
561 Loss to be used in the model. Must be an one of "mse", or "logistic".
563 is_classif : :obj:`bool`, default=False
564 Flag telling whether the learning task is classification or regression.
566 l1_ratios : :obj:`float` or :obj:`list` of floats in the interval [0, 1]; \
567 default=0.5
568 Constant that mixes L1 and spatial prior terms in penalization.
569 l1_ratios == 1 corresponds to pure LASSO. The larger the value of this
570 parameter, the sparser the estimated weights map. If list is provided,
571 then the best value will be selected by cross-validation.
573 alphas : :obj:`float` or :obj:`list` of floats, default=None
574 Choices for the constant that scales the overall regularization term.
575 This parameter is mutually exclusive with the `n_alphas` parameter.
576 If None or list of floats is provided, then the best value will be
577 selected by cross-validation.
579 n_alphas : :obj:`int`, default=10
580 Generate this number of alphas per regularization path.
581 This parameter is mutually exclusive with the `alphas` parameter.
583 eps : :obj:`float`, default=1e-3
584 Length of the path. For example, ``eps=1e-3`` means that
585 ``alpha_min / alpha_max = 1e-3``
587 mask : filename, niimg, NiftiMasker instance, default=None
588 Mask to be used on data. If an instance of masker is passed,
589 then its mask will be used. If no mask is it will be computed
590 automatically by a NiftiMasker.
591 %(target_affine)s
592 An important use-case of this parameter is for downsampling the
593 input data to a coarser resolution (to speed of the model fit).
594 %(target_shape)s
595 %(low_pass)s
596 %(high_pass)s
597 %(t_r)s
598 screening_percentile : :obj:`float` in the interval [0, 100]; Optional (\
599 default 20)
600 Percentile value for ANOVA univariate feature selection. A value of
601 100 means 'keep all features'. This percentile is expressed
602 w.r.t the volume of a standard (MNI152) brain, and so is corrected
603 at runtime to correspond to the volume of the user-supplied mask
604 (which is typically smaller). If '100' is given, all the features
605 are used, regardless of the number of voxels.
607 standardize : :obj:`bool`, default=True
608 If set, then the data (X, y) are centered to have mean zero along
609 axis 0. This is here because nearly all linear models will want
610 their data to be centered.
612 fit_intercept : :obj:`bool`, default=True
613 Fit or not an intercept.
615 max_iter : :obj:`int`, default=200
616 Defines the iterations for the solver.
618 tol : :obj:`float`, default=5e-4
619 Defines the tolerance for convergence for the backend FISTA solver.
620 %(verbose)s
621 %(n_jobs)s
622 %(memory)s
623 %(memory_level1)s
624 cv : :obj:`int`, a cv generator instance, or None, default=8
625 The input specifying which cross-validation generator to use.
626 It can be an integer, in which case it is the number of folds in a
627 KFold, None, in which case 3 fold is used, or another object, that
628 will then be used as a cv generator.
630 debias : :obj:`bool`, default=False
631 If set, then the estimated weights maps will be debiased.
633 positive : bool, default=False
634 When set to ``True``, forces the coefficients to be positive.
635 This option is only supported for dense arrays.
637 .. versionadded:: 0.11.2dev
639 Attributes
640 ----------
641 all_coef_ : ndarray, shape (n_l1_ratios, n_folds, n_features)
642 Coefficients for all folds and features.
644 alpha_grids_ : ndarray, shape (n_folds, n_alphas)
645 Alpha values considered for selection of the best ones
646 (saved in `best_model_params_`)
648 best_model_params_ : ndarray, shape (n_folds, n_parameter)
649 Best model parameters (alpha, l1_ratio) saved for the different
650 cross-validation folds.
652 classes_ : ndarray of labels (`n_classes_`)
653 Labels of the classes (for classification problems)
655 n_classes_ : int
656 Number of classes (for classification problems)
658 coef_ : ndarray, shape\
659 (1, n_features) for 2 class classification problems\
660 (i.e n_classes = 2)\
661 (n_classes, n_features) for n_classes > 2
662 Coefficient of the features in the decision function.
664 coef_img_ : nifti image
665 Masked model coefficients
667 mask_ : ndarray 3D
668 An array contains values of the mask image.
670 masker_ : instance of NiftiMasker
671 The nifti masker used to mask the data.
673 mask_img_ : Nifti like image
674 The mask of the data. If no mask was supplied by the user,
675 this attribute is the mask image computed automatically from the
676 data `X`.
678 memory_ : joblib memory cache
680 intercept_ : narray, shape
681 (1,) for 2 class classification problems (i.e n_classes = 2)
682 (n_classes,) for n_classes > 2
683 Intercept (a.k.a. bias) added to the decision function.
684 It is available only when parameter intercept is set to True.
686 cv_ : list of pairs of lists
687 Each pair is the list of indices for the train and test samples
688 for the corresponding fold.
690 cv_scores_ : ndarray, shape (n_folds, n_alphas)\
691 or (n_l1_ratios, n_folds, n_alphas)
692 Scores (misclassification) for each alpha, and on each fold
694 screening_percentile_ : float
695 Screening percentile corrected according to volume of mask,
696 relative to the volume of standard brain.
698 w_ : ndarray, shape
699 (1, n_features + 1) for 2 class classification problems
700 (i.e n_classes = 2)
701 (n_classes, n_features + 1) for n_classes > 2, and (n_features,)
702 for regression
703 Model weights
705 ymean_ : array, shape (n_samples,)
706 Mean of prediction targets
708 Xmean_ : array, shape (n_features,)
709 Mean of X across samples
711 Xstd_ : array, shape (n_features,)
712 Standard deviation of X across samples
713 """
715 SUPPORTED_PENALTIES: ClassVar[tuple[str, ...]] = ("graph-net", "tv-l1")
716 SUPPORTED_LOSSES: ClassVar[tuple[str, ...]] = ("mse", "logistic")
718 def __init__(
719 self,
720 penalty="graph-net",
721 is_classif=False,
722 loss=None,
723 l1_ratios=0.5,
724 alphas=None,
725 n_alphas=10,
726 mask=None,
727 target_affine=None,
728 target_shape=None,
729 low_pass=None,
730 high_pass=None,
731 t_r=None,
732 max_iter=200,
733 tol=5e-4,
734 memory=None,
735 memory_level=1,
736 standardize=True,
737 verbose=1,
738 mask_args=None,
739 n_jobs=1,
740 eps=1e-3,
741 cv=8,
742 fit_intercept=True,
743 screening_percentile=20.0,
744 debias=False,
745 positive=False,
746 ):
747 self.penalty = penalty
748 self.is_classif = is_classif
749 self.loss = loss
750 self.n_alphas = n_alphas
751 self.eps = eps
752 self.l1_ratios = l1_ratios
753 self.alphas = alphas
754 self.mask = mask
755 self.fit_intercept = fit_intercept
756 self.memory = memory
757 self.memory_level = memory_level
758 self.max_iter = max_iter
759 self.tol = tol
760 self.verbose = verbose
761 self.standardize = standardize
762 self.n_jobs = n_jobs
763 self.cv = cv
764 self.screening_percentile = screening_percentile
765 self.debias = debias
766 self.low_pass = low_pass
767 self.high_pass = high_pass
768 self.t_r = t_r
769 self.target_affine = target_affine
770 self.target_shape = target_shape
771 self.mask_args = mask_args
772 self.positive = positive
774 def _check_params(self):
775 """Make sure parameters are sane."""
776 if self.l1_ratios is not None:
777 l1_ratios = self.l1_ratios
778 if not isinstance(l1_ratios, collections.abc.Iterable):
779 l1_ratios = [l1_ratios]
780 for l1_ratio in l1_ratios:
781 if not 0 <= l1_ratio <= 1.0:
782 raise ValueError(
783 "l1_ratio must be in the interval [0, 1]; "
784 f" got {l1_ratio:g}"
785 )
786 elif l1_ratio in (0.0, 1.0):
787 warnings.warn(
788 f"Specified l1_ratio = {l1_ratio:g}. "
789 "It's advised to only specify values of l1_ratio "
790 "strictly between 0 and 1.",
791 stacklevel=find_stack_level(),
792 )
793 if not (0.0 <= self.screening_percentile <= 100.0):
794 raise ValueError(
795 "screening_percentile should be in the interval [0, 100]. "
796 f"Got {self.screening_percentile:g}."
797 )
798 if self.penalty not in self.SUPPORTED_PENALTIES:
799 raise ValueError(
800 "'penalty' parameter must be one of "
801 f"{self.SUPPORTED_PENALTIES}. "
802 f"Got {self.penalty}."
803 )
804 if self.loss is not None and self.loss not in self.SUPPORTED_LOSSES:
805 raise ValueError(
806 f"'loss' parameter must be one of {self.SUPPORTED_LOSSES}. "
807 f"Got {self.loss}."
808 )
809 if (
810 self.loss is not None
811 and not self.is_classif
812 and (self.loss == "logistic")
813 ):
814 raise ValueError(
815 "'logistic' loss is only available for classification "
816 "problems."
817 )
819 def _set_coef_and_intercept(self, w):
820 """Set the loadings vector (coef) and the intercept of the fitted \
821 model.
822 """
823 self.w_ = np.array(w)
824 if self.w_.ndim == 1:
825 self.w_ = self.w_[np.newaxis, :]
826 self.coef_ = self.w_[:, :-1]
827 if self.is_classif:
828 self.intercept_ = self.w_[:, -1]
829 else:
830 self._set_intercept(self.Xmean_, self.ymean_, self.Xstd_)
832 def fit(self, X, y):
833 """Fit the learner.
835 Parameters
836 ----------
837 X : :obj:`list` of Niimg-like objects
838 See :ref:`extracting_data`.
839 Data on which model is to be fitted. If this is a list,
840 the affine is considered the same for all.
842 y : array or :obj:`list` of length n_samples
843 The dependent variable (age, sex, QI, etc.).
845 Notes
846 -----
847 self : `SpaceNet` object
848 Model selection is via cross-validation with bagging.
849 """
850 check_params(self.__dict__)
851 # sanity check on params
852 self._check_params()
853 if isinstance(X, SurfaceImage) or isinstance(self.mask, SurfaceMasker):
854 raise NotImplementedError(
855 "Running space net on surface objects is not supported."
856 )
858 # misc
859 self._check_params()
860 if self.memory is None or isinstance(self.memory, str):
861 self.memory_ = Memory(
862 self.memory, verbose=max(0, self.verbose - 1)
863 )
864 else:
865 self.memory_ = self.memory
867 tic = time.time()
869 self.masker_ = check_embedded_masker(self, masker_type="nii")
870 X = self.masker_.fit_transform(X)
872 X, y = check_X_y(
873 X,
874 y,
875 ["csr", "csc", "coo"],
876 dtype=float,
877 multi_output=True,
878 y_numeric=not self.is_classif,
879 )
881 if not self.is_classif and np.all(np.diff(y) == 0.0):
882 raise ValueError(
883 "The given input y must have at least 2 targets"
884 " to do regression analysis. You provided only"
885 f" one target {np.unique(y)}"
886 )
888 # misc
889 self.Xmean_ = X.mean(axis=0)
890 self.Xstd_ = X.std(axis=0)
891 self.Xstd_[self.Xstd_ < 1e-8] = 1
892 self.mask_img_ = self.masker_.mask_img_
893 self.mask_ = get_data(self.mask_img_).astype(bool)
894 n_samples, _ = X.shape
895 y = np.array(y).copy()
896 l1_ratios = self.l1_ratios
897 if not isinstance(l1_ratios, collections.abc.Iterable):
898 l1_ratios = [l1_ratios]
899 alphas = self.alphas
900 if alphas is not None and not isinstance(
901 alphas, collections.abc.Iterable
902 ):
903 alphas = [alphas]
904 if self.loss is not None:
905 loss = self.loss
906 elif self.is_classif:
907 loss = "logistic"
908 else:
909 loss = "mse"
911 # set backend solver
912 if self.penalty.lower() == "graph-net":
913 if not self.is_classif or loss == "mse":
914 solver = graph_net_squared_loss
915 else:
916 solver = graph_net_logistic
917 elif not self.is_classif or loss == "mse":
918 solver = partial(tvl1_solver, loss="mse")
919 else:
920 solver = partial(tvl1_solver, loss="logistic")
922 # generate fold indices
923 case1 = (None in [alphas, l1_ratios]) and self.n_alphas > 1
924 case2 = (alphas is not None) and min(len(l1_ratios), len(alphas)) > 1
925 if case1 or case2:
926 self.cv_ = list(
927 check_cv(self.cv, y=y, classifier=self.is_classif).split(X, y)
928 )
929 else:
930 # no cross-validation needed, user supplied all params
931 self.cv_ = [(np.arange(n_samples), [])]
932 n_folds = len(self.cv_)
934 # number of problems to solve
935 y = self._binarize_y(y) if self.is_classif else y[:, np.newaxis]
937 n_problems = (
938 self.n_classes_ if self.is_classif and self.n_classes_ > 2 else 1
939 )
941 # standardize y
942 self.ymean_ = np.zeros(y.shape[0])
943 if n_problems == 1:
944 y = y[:, 0]
946 # scores & mean weights map over all folds
947 self.cv_scores_ = [[] for _ in range(n_problems)]
948 w = np.zeros((n_problems, X.shape[1] + 1))
949 self.all_coef_ = np.ndarray((n_problems, n_folds, X.shape[1]))
951 self.screening_percentile_ = adjust_screening_percentile(
952 self.screening_percentile, self.mask_img_, verbose=self.verbose
953 )
955 # main loop: loop on classes and folds
956 solver_params = {"tol": self.tol, "max_iter": self.max_iter}
957 self.best_model_params_ = []
958 self.alpha_grids_ = []
959 for (
960 test_scores,
961 best_w,
962 best_alpha,
963 best_l1_ratio,
964 alphas,
965 y_train_mean,
966 (cls, fold),
967 ) in Parallel(n_jobs=self.n_jobs, verbose=2 * self.verbose)(
968 delayed(self._cache(path_scores, func_memory_level=2))(
969 solver,
970 X,
971 y[:, cls] if n_problems > 1 else y,
972 self.mask_,
973 alphas,
974 l1_ratios,
975 self.cv_[fold][0],
976 self.cv_[fold][1],
977 solver_params,
978 n_alphas=self.n_alphas,
979 eps=self.eps,
980 is_classif=self.loss == "logistic",
981 key=(cls, fold),
982 debias=self.debias,
983 verbose=self.verbose,
984 screening_percentile=self.screening_percentile_,
985 )
986 for cls in range(n_problems)
987 for fold in range(n_folds)
988 ):
989 self.best_model_params_.append((best_alpha, best_l1_ratio))
990 self.alpha_grids_.append(alphas)
991 self.ymean_[cls] += y_train_mean
992 self.all_coef_[cls, fold] = best_w[:-1]
993 if len(np.atleast_1d(l1_ratios)) == 1:
994 test_scores = test_scores[0]
995 self.cv_scores_[cls].append(test_scores)
996 w[cls] += best_w
998 # misc
999 self.cv_scores_ = np.array(self.cv_scores_)
1000 self.best_model_params_ = np.array(self.best_model_params_)
1001 self.alpha_grids_ = np.array(self.alpha_grids_)
1002 self.ymean_ /= n_folds
1003 if not self.is_classif:
1004 self.all_coef_ = np.array(self.all_coef_)
1005 w = w[0]
1006 self.ymean_ = self.ymean_[0]
1008 # bagging: average best weights maps over folds
1009 w /= n_folds
1011 # set coefs and intercepts
1012 self._set_coef_and_intercept(w)
1014 # unmask weights map as a niimg
1015 self.coef_img_ = self.masker_.inverse_transform(self.coef_)
1017 # report time elapsed
1018 duration = time.time() - tic
1019 logger.log(
1020 f"Time Elapsed: {duration} seconds, {duration / 60.0} minutes.",
1021 self.verbose,
1022 )
1024 return self
1026 def __sklearn_is_fitted__(self):
1027 return hasattr(self, "masker_")
1029 def decision_function(self, X):
1030 """Predict confidence scores for samples.
1032 The confidence score for a sample is the signed distance of that
1033 sample to the hyperplane.
1035 Parameters
1036 ----------
1037 X : {array-like, sparse matrix}, shape = (n_samples, n_features)
1038 Samples.
1040 Returns
1041 -------
1042 array, shape=(n_samples,) if n_classes == 2 else (n_samples, n_classes)
1043 Confidence scores per (sample, class) combination. In the binary
1044 case, confidence score for `self.classes_[1]` where >0 means this
1045 class would be predicted.
1046 """
1047 # handle regression (least-squared loss)
1048 if not self.is_classif:
1049 raise ValueError("There is no decision_function in classification")
1051 X = check_array(X)
1052 n_features = self.coef_.shape[1]
1053 if X.shape[1] != n_features:
1054 raise ValueError(
1055 f"X has {X.shape[1]} features per sample; "
1056 f"expecting {n_features}."
1057 )
1059 scores = (
1060 safe_sparse_dot(X, self.coef_.T, dense_output=True)
1061 + self.intercept_
1062 )
1063 return scores.ravel() if scores.shape[1] == 1 else scores
1065 def predict(self, X):
1066 """Predict class labels for samples in X.
1068 Parameters
1069 ----------
1070 X : :obj:`list` of Niimg-like objects
1071 See :ref:`extracting_data`.
1072 Data on prediction is to be made. If this is a list,
1073 the affine is considered the same for all.
1075 Returns
1076 -------
1077 y_pred : ndarray, shape (n_samples,)
1078 Predicted class label per sample.
1079 """
1080 # cast X into usual 2D array
1081 check_is_fitted(self)
1083 X = self.masker_.transform(X)
1085 # handle regression (least-squared loss)
1086 if not self.is_classif:
1087 return LinearRegression.predict(self, X)
1089 # prediction proper
1090 scores = self.decision_function(X)
1091 if len(scores.shape) == 1:
1092 indices = (scores > 0).astype(int)
1093 else:
1094 indices = scores.argmax(axis=1)
1095 return self.classes_[indices]
1098@fill_doc
1099class SpaceNetClassifier(BaseSpaceNet):
1100 """Classification learners with sparsity and spatial priors.
1102 `SpaceNetClassifier` implements Graph-Net and TV-L1
1103 priors / penalties for classification problems. Thus, the penalty
1104 is a sum an L1 term and a spatial term. The aim of such a hybrid prior
1105 is to obtain weights maps which are structured (due to the spatial
1106 prior) and sparse (enforced by L1 norm).
1108 Parameters
1109 ----------
1110 penalty : :obj:`str`, default='graph-net'
1111 Penalty to used in the model. Can be 'graph-net' or 'tv-l1'.
1113 loss : :obj:`str`, default="logistic"
1114 Loss to be used in the classifier. Must be one of "mse", or "logistic".
1116 l1_ratios : :obj:`float` or :obj:`list` of floats in the interval [0, 1]; \
1117 default=0.5
1118 Constant that mixes L1 and spatial prior terms in penalization.
1119 l1_ratios == 1 corresponds to pure LASSO. The larger the value of this
1120 parameter, the sparser the estimated weights map. If list is provided,
1121 then the best value will be selected by cross-validation.
1123 alphas : :obj:`float` or :obj:`list` of floats, default=None
1124 Choices for the constant that scales the overall regularization term.
1125 This parameter is mutually exclusive with the `n_alphas` parameter.
1126 If None or list of floats is provided, then the best value will be
1127 selected by cross-validation.
1129 n_alphas : :obj:`int`, default=10
1130 Generate this number of alphas per regularization path.
1131 This parameter is mutually exclusive with the `alphas` parameter.
1133 eps : :obj:`float`, default=1e-3
1134 Length of the path. For example, ``eps=1e-3`` means that
1135 ``alpha_min / alpha_max = 1e-3``.
1137 mask : filename, niimg, NiftiMasker instance, default=None
1138 Mask to be used on data. If an instance of masker is passed,
1139 then its mask will be used. If no mask is it will be computed
1140 automatically by a MultiNiftiMasker with default parameters.
1141 %(target_affine)s
1142 %(target_shape)s
1143 %(low_pass)s
1144 %(high_pass)s
1145 %(t_r)s
1146 screening_percentile : :obj:`float` in the interval [0, 100]; \
1147 default=20
1148 Percentile value for ANOVA univariate feature selection.
1149 A value of 100 means 'keep all features'.
1150 This percentile is expressed w.r.t the volume
1151 of a standard (MNI152) brain, and so is corrected
1152 at runtime by premultiplying it with the ratio of the volume
1153 of the mask of the data and volume of a standard brain.
1154 If '100' is given, all the features are used,
1155 regardless of the number of voxels.
1157 standardize : :obj:`bool`, default=True
1158 If set, then we'll center the data (X, y) have mean zero along axis 0.
1159 This is here because nearly all linear models will want their data
1160 to be centered.
1162 fit_intercept : :obj:`bool`, default=True
1163 Fit or not an intercept.
1165 max_iter : :obj:`int`, default=200
1166 Defines the iterations for the solver.
1168 tol : :obj:`float`, default=1e-4.
1169 Defines the tolerance for convergence.
1170 %(verbose)s
1171 %(n_jobs)s
1172 %(memory)s
1173 %(memory_level1)s
1174 cv : :obj:`int`, a cv generator instance, or None, default=8
1175 The input specifying which cross-validation generator to use.
1176 It can be an integer, in which case it is the number of folds in a
1177 KFold, None, in which case 3 fold is used, or another object, that
1178 will then be used as a cv generator.
1180 debias : :obj:`bool`, default=False
1181 If set, then the estimated weights maps will be debiased.
1183 Attributes
1184 ----------
1185 all_coef_ : ndarray, shape (n_l1_ratios, n_folds, n_features)
1186 Coefficients for all folds and features.
1188 alpha_grids_ : ndarray, shape (n_folds, n_alphas)
1189 Alpha values considered for selection of the best ones
1190 (saved in `best_model_params_`)
1192 best_model_params_ : ndarray, shape (n_folds, n_parameter)
1193 Best model parameters (alpha, l1_ratio) saved for the different
1194 cross-validation folds.
1196 classes_ : ndarray of labels (`n_classes_`)
1197 Labels of the classes
1199 n_classes_ : int
1200 Number of classes
1202 coef_ : ndarray, shape
1203 (1, n_features) for 2 class classification problems (i.e n_classes = 2)
1204 (n_classes, n_features) for n_classes > 2
1205 Coefficient of the features in the decision function.
1207 coef_img_ : nifti image
1208 Masked model coefficients
1210 mask_ : ndarray 3D
1211 An array contains values of the mask image.
1213 masker_ : instance of NiftiMasker
1214 The nifti masker used to mask the data.
1216 mask_img_ : Nifti like image
1217 The mask of the data. If no mask was supplied by the user,
1218 this attribute is the mask image computed automatically from the
1219 data `X`.
1221 memory_ : joblib memory cache
1223 intercept_ : narray, shape
1224 (1, ) for 2 class classification problems (i.e n_classes = 2)
1225 (n_classes, ) for n_classes > 2
1226 Intercept (a.k.a. bias) added to the decision function.
1227 It is available only when parameter intercept is set to True.
1229 cv_ : list of pairs of lists
1230 Each pair is the list of indices for the train and test
1231 samples for the corresponding fold.
1233 cv_scores_ : ndarray, shape (n_folds, n_alphas)\
1234 or (n_l1_ratios, n_folds, n_alphas)
1235 Scores (misclassification) for each alpha, and on each fold
1237 screening_percentile_ : float
1238 Screening percentile corrected according to volume of mask,
1239 relative to the volume of standard brain.
1241 w_ : ndarray, shape
1242 (1, n_features + 1) for 2 class classification problems
1243 (i.e n_classes = 2)
1244 (n_classes, n_features + 1) for n_classes > 2
1245 Model weights
1247 ymean_ : array, shape (n_samples,)
1248 Mean of prediction targets
1250 Xmean_ : array, shape (n_features,)
1251 Mean of X across samples
1253 Xstd_ : array, shape (n_features,)
1254 Standard deviation of X across samples
1256 See Also
1257 --------
1258 nilearn.decoding.SpaceNetRegressor: Graph-Net and TV-L1 priors/penalties
1260 """
1262 def __init__(
1263 self,
1264 penalty="graph-net",
1265 loss="logistic",
1266 l1_ratios=0.5,
1267 alphas=None,
1268 n_alphas=10,
1269 mask=None,
1270 target_affine=None,
1271 target_shape=None,
1272 low_pass=None,
1273 high_pass=None,
1274 t_r=None,
1275 max_iter=200,
1276 tol=1e-4,
1277 memory=None,
1278 memory_level=1,
1279 standardize=True,
1280 verbose=1,
1281 n_jobs=1,
1282 eps=1e-3,
1283 cv=8,
1284 fit_intercept=True,
1285 screening_percentile=20.0,
1286 debias=False,
1287 ):
1288 if memory is None:
1289 memory = Memory(location=None)
1290 super().__init__(
1291 penalty=penalty,
1292 is_classif=True,
1293 l1_ratios=l1_ratios,
1294 alphas=alphas,
1295 n_alphas=n_alphas,
1296 target_shape=target_shape,
1297 low_pass=low_pass,
1298 high_pass=high_pass,
1299 mask=mask,
1300 t_r=t_r,
1301 max_iter=max_iter,
1302 tol=tol,
1303 memory=memory,
1304 memory_level=memory_level,
1305 n_jobs=n_jobs,
1306 eps=eps,
1307 cv=cv,
1308 debias=debias,
1309 fit_intercept=fit_intercept,
1310 standardize=standardize,
1311 screening_percentile=screening_percentile,
1312 loss=loss,
1313 target_affine=target_affine,
1314 verbose=verbose,
1315 )
1317 def _binarize_y(self, y):
1318 """Encode target classes as -1 and 1.
1320 Helper function invoked just before fitting a classifier.
1321 """
1322 y = np.array(y)
1324 # encode target classes as -1 and 1
1325 self._enc = LabelBinarizer(pos_label=1, neg_label=-1)
1326 y = self._enc.fit_transform(y)
1327 self.classes_ = self._enc.classes_
1328 self.n_classes_ = len(self.classes_)
1329 return y
1331 def score(self, X, y):
1332 """Return the mean accuracy on the given test data and labels.
1334 Parameters
1335 ----------
1336 X : :obj:`list` of Niimg-like objects
1337 See :ref:`extracting_data`.
1338 Data on which model is to be fitted. If this is a list,
1339 the affine is considered the same for all.
1341 y : array or :obj:`list` of length n_samples.
1342 Labels.
1344 Returns
1345 -------
1346 score : float
1347 Mean accuracy of self.predict(X) w.r.t y.
1348 """
1349 return accuracy_score(y, self.predict(X))
1352@fill_doc
1353class SpaceNetRegressor(BaseSpaceNet):
1354 """Regression learners with sparsity and spatial priors.
1356 `SpaceNetRegressor` implements Graph-Net and TV-L1 priors / penalties
1357 for regression problems. Thus, the penalty is a sum an L1 term and a
1358 spatial term. The aim of such a hybrid prior is to obtain weights maps
1359 which are structured (due to the spatial prior) and sparse (enforced
1360 by L1 norm).
1362 Parameters
1363 ----------
1364 penalty : :obj:`str`, default='graph-net'
1365 Penalty to used in the model. Can be 'graph-net' or 'tv-l1'.
1367 l1_ratios : :obj:`float` or :obj:`list` of floats in the interval [0, 1]; \
1368 default=0.5
1369 Constant that mixes L1 and spatial prior terms in penalization.
1370 l1_ratios == 1 corresponds to pure LASSO. The larger the value of this
1371 parameter, the sparser the estimated weights map. If list is provided,
1372 then the best value will be selected by cross-validation.
1374 alphas : :obj:`float` or :obj:`list` of floats or None, default=None
1375 Choices for the constant that scales the overall regularization term.
1376 This parameter is mutually exclusive with the `n_alphas` parameter.
1377 If None or list of floats is provided, then the best value will be
1378 selected by cross-validation.
1380 n_alphas : :obj:`int`, default=10
1381 Generate this number of alphas per regularization path.
1382 This parameter is mutually exclusive with the `alphas` parameter.
1384 eps : :obj:`float`, default=1e-3
1385 Length of the path. For example, ``eps=1e-3`` means that
1386 ``alpha_min / alpha_max = 1e-3``
1388 mask : filename, niimg, NiftiMasker instance, default=None
1389 Mask to be used on data. If an instance of masker is passed,
1390 then its mask will be used. If no mask is it will be computed
1391 automatically by a MultiNiftiMasker with default parameters.
1392 %(target_affine)s
1393 %(target_shape)s
1394 %(low_pass)s
1395 %(high_pass)s
1396 %(t_r)s
1397 screening_percentile : :obj:`float` in the interval [0, 100]; \
1398 default=20
1399 Percentile value for ANOVA univariate feature selection.
1400 A value of 100 means 'keep all features'.
1401 This percentile is expressed w.r.t the volume
1402 of a standard (MNI152) brain, and so is corrected
1403 at runtime to correspond to the volume of the user-supplied mask
1404 (which is typically smaller).
1406 standardize : :obj:`bool`, default=True
1407 If set, then we'll center the data (X, y) have mean zero along axis 0.
1408 This is here because nearly all linear models will want their data
1409 to be centered.
1411 fit_intercept : :obj:`bool`, default=True
1412 Fit or not an intercept.
1414 max_iter : :obj:`int`, default=200
1415 Defines the iterations for the solver.
1417 tol : :obj:`float`, default=1e-4
1418 Defines the tolerance for convergence.
1419 %(verbose)s
1420 %(n_jobs)s
1421 %(memory)s
1422 %(memory_level1)s
1423 cv : :obj:`int`, a cv generator instance, or None, default=8
1424 The input specifying which cross-validation generator to use.
1425 It can be an integer, in which case it is the number of folds in a
1426 KFold, None, in which case 3 fold is used, or another object, that
1427 will then be used as a cv generator.
1429 debias : :obj:`bool`, default=False
1430 If set, then the estimated weights maps will be debiased.
1432 Attributes
1433 ----------
1434 all_coef_ : ndarray, shape (n_l1_ratios, n_folds, n_features)
1435 Coefficients for all folds and features.
1437 alpha_grids_ : ndarray, shape (n_folds, n_alphas)
1438 Alpha values considered for selection of the best ones
1439 (saved in `best_model_params_`)
1441 best_model_params_ : ndarray, shape (n_folds, n_parameter)
1442 Best model parameters (alpha, l1_ratio) saved for the different
1443 cross-validation folds.
1445 coef_ : ndarray, shape (n_features,)
1446 Coefficient of the features in the decision function.
1448 coef_img_ : nifti image
1449 Masked model coefficients
1451 mask_ : ndarray 3D
1452 An array contains values of the mask image.
1454 masker_ : instance of NiftiMasker
1455 The nifti masker used to mask the data.
1457 mask_img_ : Nifti like image
1458 The mask of the data. If no mask was supplied by the user, this
1459 attribute is the mask image computed automatically from the data `X`.
1461 memory_ : joblib memory cache
1463 intercept_ : narray, shape (1)
1464 Intercept (a.k.a. bias) added to the decision function.
1465 It is available only when parameter intercept is set to True.
1467 cv_ : list of pairs of lists
1468 Each pair is the list of indices for the train and test
1469 samples for the corresponding fold.
1471 cv_scores_ : ndarray, shape (n_folds, n_alphas)\
1472 or (n_l1_ratios, n_folds, n_alphas)
1473 Scores (misclassification) for each alpha, and on each fold
1475 screening_percentile_ : :obj:`float`
1476 Screening percentile corrected according to volume of mask,
1477 relative to the volume of standard brain.
1479 w_ : ndarray, shape (n_features,)
1480 Model weights
1482 ymean_ : array, shape (n_samples,)
1483 Mean of prediction targets
1485 Xmean_ : array, shape (n_features,)
1486 Mean of X across samples
1488 Xstd_ : array, shape (n_features,)
1489 Standard deviation of X across samples
1491 See Also
1492 --------
1493 nilearn.decoding.SpaceNetClassifier: Graph-Net and TV-L1 priors/penalties
1495 """
1497 def __init__(
1498 self,
1499 penalty="graph-net",
1500 l1_ratios=0.5,
1501 alphas=None,
1502 n_alphas=10,
1503 mask=None,
1504 target_affine=None,
1505 target_shape=None,
1506 low_pass=None,
1507 high_pass=None,
1508 t_r=None,
1509 max_iter=200,
1510 tol=1e-4,
1511 memory=None,
1512 memory_level=1,
1513 standardize=True,
1514 verbose=1,
1515 n_jobs=1,
1516 eps=1e-3,
1517 cv=8,
1518 fit_intercept=True,
1519 screening_percentile=20.0,
1520 debias=False,
1521 ):
1522 if memory is None:
1523 memory = Memory(location=None)
1524 super().__init__(
1525 penalty=penalty,
1526 is_classif=False,
1527 l1_ratios=l1_ratios,
1528 alphas=alphas,
1529 n_alphas=n_alphas,
1530 target_shape=target_shape,
1531 low_pass=low_pass,
1532 high_pass=high_pass,
1533 mask=mask,
1534 t_r=t_r,
1535 max_iter=max_iter,
1536 tol=tol,
1537 memory=memory,
1538 memory_level=memory_level,
1539 n_jobs=n_jobs,
1540 eps=eps,
1541 cv=cv,
1542 debias=debias,
1543 fit_intercept=fit_intercept,
1544 standardize=standardize,
1545 screening_percentile=screening_percentile,
1546 target_affine=target_affine,
1547 verbose=verbose,
1548 )