Repository: mne-tools/mne-python

Issue:
Bug mne.cov.compute_covariance + multiprocessing
Hi,
I just want to mention that I used the mne.cov.compute_covariance function and it works with n_jobs =1 (as in the example) but if I put more jobs, I have the following error message:

File "/usr/lib/python2.7/multiprocessing/pool.py", line 558, in get
raise self._value

PicklingError: Can't pickle <class 'mne.cov._ShrunkCovariance'>: it's not found as mne.cov._ShrunkCovariance



Discussion:
Ah that's annoying. We did not have that before. Cannot take a look right
now. We should bisect and see when this came up. Laetita btw. you can use
MKL + Anaconda. In my experience it will be faster than using joblib here.

On Tue, Sep 29, 2015 at 11:59 AM, LaetitiaG notifications@github.com
wrote:

> Hi,
> I just want to mention that I used the mne.cov.compute_covariance function
> and it works with n_jobs =1 (as in the example) but if I put more jobs, I
> have the following error message:
> 
> File "/usr/lib/python2.7/multiprocessing/pool.py", line 558, in get
> raise self._value
> 
> PicklingError: Can't pickle : it's not found as mne.cov._ShrunkCovariance
> 
> —
> Reply to this email directly or view it on GitHub
> https://github.com/mne-tools/mne-python/issues/2497.

denis can you reproduce?

I'm going to go ahead and close this as an isolated upstream bug (probably `multiprocessing`), but if it comes back up or is still happening, let's reopen

Resolving patch:
diff --git a/mne/cov.py b/mne/cov.py
--- a/mne/cov.py
+++ b/mne/cov.py
@@ -38,6 +38,7 @@
 
 from .externals.six.moves import zip
 from .externals.six import string_types
+from .fixes import BaseEstimator
 
 
 def _check_covs_algebra(cov1, cov2):
@@ -881,7 +882,6 @@ def _compute_covariance_auto(data, method, info, method_params, cv,
     _apply_scaling_array(data.T, picks_list=picks_list, scalings=scalings)
     estimator_cov_info = list()
     msg = 'Estimating covariance using %s'
-    _RegCovariance, _ShrunkCovariance = _get_covariance_classes()
     for this_method in method:
         data_ = data.copy()
         name = this_method.__name__ if callable(this_method) else this_method
@@ -1071,117 +1071,119 @@ def _auto_low_rank_model(data, mode, n_jobs, method_params, cv,
     return est, runtime_info
 
 
-def _get_covariance_classes():
-    """Prepare special cov estimators."""
-    from sklearn.covariance import (EmpiricalCovariance, shrunk_covariance,
-                                    ShrunkCovariance)
-
-    class _RegCovariance(EmpiricalCovariance):
-        """Aux class."""
-
-        def __init__(self, info, grad=0.01, mag=0.01, eeg=0.0,
-                     store_precision=False, assume_centered=False):
-            self.info = info
-            self.grad = grad
-            self.mag = mag
-            self.eeg = eeg
-            self.store_precision = store_precision
-            self.assume_centered = assume_centered
-
-        def fit(self, X):
-            EmpiricalCovariance.fit(self, X)
-            self.covariance_ = 0.5 * (self.covariance_ + self.covariance_.T)
-            cov_ = Covariance(
-                data=self.covariance_, names=self.info['ch_names'],
-                bads=self.info['bads'], projs=self.info['projs'],
-                nfree=len(self.covariance_))
-            cov_ = regularize(cov_, self.info, grad=self.grad, mag=self.mag,
-                              eeg=self.eeg, proj=False,
-                              exclude='bads')  # ~proj == important!!
-            self.covariance_ = cov_.data
-            return self
+###############################################################################
+# Sklearn Estimators
+
+class _RegCovariance(BaseEstimator):
+    """Aux class."""
+
+    def __init__(self, info, grad=0.01, mag=0.01, eeg=0.0,
+                 store_precision=False, assume_centered=False):
+        self.info = info
+        self.grad = grad
+        self.mag = mag
+        self.eeg = eeg
+        self.store_precision = store_precision
+        self.assume_centered = assume_centered
+
+    def fit(self, X):
+        """Fit covariance model with classical diagonal regularization."""
+        from sklearn.covariance import EmpiricalCovariance
+        self.estimator_ = EmpiricalCovariance(
+            store_precision=self.store_precision,
+            assume_centered=self.assume_centered)
+
+        self.covariance_ = self.estimator_.fit(X).covariance_
+        self.covariance_ = 0.5 * (self.covariance_ + self.covariance_.T)
+        cov_ = Covariance(
+            data=self.covariance_, names=self.info['ch_names'],
+            bads=self.info['bads'], projs=self.info['projs'],
+            nfree=len(self.covariance_))
+        cov_ = regularize(cov_, self.info, grad=self.grad, mag=self.mag,
+                          eeg=self.eeg, proj=False,
+                          exclude='bads')  # ~proj == important!!
+        self.estimator_.covariance_ = self.covariance_ = cov_.data
+        return self
 
-    class _ShrunkCovariance(ShrunkCovariance):
-        """Aux class."""
+    def score(self, X_test, y=None):
+        """Delegate call to modified EmpiricalCovariance instance."""
+        return self.estimator_.score(X_test, y=y)
 
-        def __init__(self, store_precision, assume_centered,
-                     shrinkage=0.1):
-            self.store_precision = store_precision
-            self.assume_centered = assume_centered
-            self.shrinkage = shrinkage
+    def get_precision(self):
+        """Delegate call to modified EmpiricalCovariance instance."""
+        return self.estimator_.get_precision()
 
-        def fit(self, X):
-            EmpiricalCovariance.fit(self, X)
-            cov = self.covariance_
 
-            if not isinstance(self.shrinkage, (list, tuple)):
-                shrinkage = [('all', self.shrinkage, np.arange(len(cov)))]
-            else:
-                shrinkage = self.shrinkage
-
-            zero_cross_cov = np.zeros_like(cov, dtype=bool)
-            for a, b in itt.combinations(shrinkage, 2):
-                picks_i, picks_j = a[2], b[2]
-                ch_ = a[0], b[0]
-                if 'eeg' in ch_:
-                    zero_cross_cov[np.ix_(picks_i, picks_j)] = True
-                    zero_cross_cov[np.ix_(picks_j, picks_i)] = True
-
-            self.zero_cross_cov_ = zero_cross_cov
-
-            # Apply shrinkage to blocks
-            for ch_type, c, picks in shrinkage:
-                sub_cov = cov[np.ix_(picks, picks)]
-                cov[np.ix_(picks, picks)] = shrunk_covariance(sub_cov,
-                                                              shrinkage=c)
-
-            # Apply shrinkage to cross-cov
-            for a, b in itt.combinations(shrinkage, 2):
-                shrinkage_i, shrinkage_j = a[1], b[1]
-                picks_i, picks_j = a[2], b[2]
-                c_ij = np.sqrt((1. - shrinkage_i) * (1. - shrinkage_j))
-                cov[np.ix_(picks_i, picks_j)] *= c_ij
-                cov[np.ix_(picks_j, picks_i)] *= c_ij
-
-            # Set to zero the necessary cross-cov
-            if np.any(zero_cross_cov):
-                cov[zero_cross_cov] = 0.0
-
-            self.covariance_ = cov
-            return self
+class _ShrunkCovariance(BaseEstimator):
+    """Aux class."""
+
+    def __init__(self, store_precision, assume_centered,
+                 shrinkage=0.1):
+
+        self.store_precision = store_precision
+        self.assume_centered = assume_centered
+        self.shrinkage = shrinkage
+
+    def fit(self, X):
+        """Fit covariance model with oracle shrinkage regularization."""
+        from sklearn.covariance import shrunk_covariance
+        from sklearn.covariance import EmpiricalCovariance
+        self.estimator_ = EmpiricalCovariance(
+            store_precision=self.store_precision,
+            assume_centered=self.assume_centered)
+
+        cov = self.estimator_.fit(X).covariance_
+
+        if not isinstance(self.shrinkage, (list, tuple)):
+            shrinkage = [('all', self.shrinkage, np.arange(len(cov)))]
+        else:
+            shrinkage = self.shrinkage
+
+        zero_cross_cov = np.zeros_like(cov, dtype=bool)
+        for a, b in itt.combinations(shrinkage, 2):
+            picks_i, picks_j = a[2], b[2]
+            ch_ = a[0], b[0]
+            if 'eeg' in ch_:
+                zero_cross_cov[np.ix_(picks_i, picks_j)] = True
+                zero_cross_cov[np.ix_(picks_j, picks_i)] = True
+
+        self.zero_cross_cov_ = zero_cross_cov
+
+        # Apply shrinkage to blocks
+        for ch_type, c, picks in shrinkage:
+            sub_cov = cov[np.ix_(picks, picks)]
+            cov[np.ix_(picks, picks)] = shrunk_covariance(sub_cov,
+                                                          shrinkage=c)
+
+        # Apply shrinkage to cross-cov
+        for a, b in itt.combinations(shrinkage, 2):
+            shrinkage_i, shrinkage_j = a[1], b[1]
+            picks_i, picks_j = a[2], b[2]
+            c_ij = np.sqrt((1. - shrinkage_i) * (1. - shrinkage_j))
+            cov[np.ix_(picks_i, picks_j)] *= c_ij
+            cov[np.ix_(picks_j, picks_i)] *= c_ij
+
+        # Set to zero the necessary cross-cov
+        if np.any(zero_cross_cov):
+            cov[zero_cross_cov] = 0.0
+
+        self.estimator_.covariance_ = self.covariance_ = cov
+        return self
 
-        def score(self, X_test, y=None):
-            """Compute the log-likelihood of a Gaussian data set.
-
-            This uses `self.covariance_` as an estimator of the data's
-            covariance matrix.
-
-            Parameters
-            ----------
-            X_test : array-like, shape = [n_samples, n_features]
-                Test data of which we compute the likelihood, where n_samples
-                is the number of samples and n_features is the number of
-                features. X_test is assumed to be drawn from the same
-                distribution as the data used in fit (including centering).
-
-            y : not used, present for API consistence purpose.
-
-            Returns
-            -------
-            res : float
-                The likelihood of the data set with `self.covariance_` as an
-                estimator of its covariance matrix.
-            """
-            from sklearn.covariance import empirical_covariance, log_likelihood
-            # compute empirical covariance of the test set
-            test_cov = empirical_covariance(X_test - self.location_,
-                                            assume_centered=True)
-            if np.any(self.zero_cross_cov_):
-                test_cov[self.zero_cross_cov_] = 0.
-            res = log_likelihood(test_cov, self.get_precision())
-            return res
-
-    return _RegCovariance, _ShrunkCovariance
+    def score(self, X_test, y=None):
+        """Delegate to modified EmpiricalCovariance instance."""
+        from sklearn.covariance import empirical_covariance, log_likelihood
+        # compute empirical covariance of the test set
+        test_cov = empirical_covariance(X_test - self.estimator_.location_,
+                                        assume_centered=True)
+        if np.any(self.zero_cross_cov_):
+            test_cov[self.zero_cross_cov_] = 0.
+        res = log_likelihood(test_cov, self.estimator_.get_precision())
+        return res
+
+    def get_precision(self):
+        """Delegate to modified EmpiricalCovariance instance."""
+        return self.estimator_.get_precision()
 
 
 ###############################################################################


Test patch:
diff --git a/mne/tests/test_cov.py b/mne/tests/test_cov.py
--- a/mne/tests/test_cov.py
+++ b/mne/tests/test_cov.py
@@ -575,6 +575,32 @@ def test_compute_covariance_auto_reg():
     covs = compute_covariance(epochs, method='auto',
                               method_params=method_params,
                               return_estimators=True)
+    # make sure regularization produces structured differencess
+    diag_mask = np.eye(len(epochs.ch_names)).astype(bool)
+    off_diag_mask = np.invert(diag_mask)
+    for cov_a, cov_b in itt.combinations(covs, 2):
+        if (cov_a['method'] == 'diagonal_fixed' and
+                # here we have diagnoal or no regularization.
+                cov_b['method'] == 'empirical'):
+
+            assert_true(not np.any(
+                        cov_a['data'][diag_mask] ==
+                        cov_b['data'][diag_mask]))
+
+            # but the rest is the same
+            assert_array_equal(
+                cov_a['data'][off_diag_mask],
+                cov_b['data'][off_diag_mask])
+
+        else:
+            # and here we have shrinkage everywhere.
+            assert_true(not np.any(
+                        cov_a['data'][diag_mask] ==
+                        cov_b['data'][diag_mask]))
+
+            assert_true(not np.any(
+                        cov_a['data'][diag_mask] ==
+                        cov_b['data'][diag_mask]))
 
     logliks = [c['loglik'] for c in covs]
     assert_true(np.diff(logliks).max() <= 0)  # descending order
@@ -597,4 +623,5 @@ def test_compute_covariance_auto_reg():
     assert_raises(ValueError, compute_covariance, epochs, method='shrunk',
                   scalings=dict(misc=123))
 
+
 run_tests_if_main()