Skip to content

Conversation

@wmvanvliet
Copy link
Contributor

@wmvanvliet wmvanvliet commented Nov 5, 2019

Working on adding whitening support to the DICS beamformer, so we can mix different channel types.

Todo:

Copy link
Member

@larsoner larsoner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a great start, love to see a small diff bring a nice enhancement. (I know it's not done yet but it looks close!)


# Set picks, use a single sensor type
picks = mne.pick_types(raw.info, meg='grad', exclude='bads')

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hooray!

from ..cov import Covariance # to avoid circular import
return Covariance(data, self.ch_names, bads=[], projs=self.projs,
nfree=self.n_fft)
return None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debugging cruft?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably :)

Copy link
Member

@britta-wstnr britta-wstnr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hooray for combining sensors for DICS!
I fear I came to question leadfield whitening and its effect on DICS with multiple frequencies - see my reviewing comments ... What is your opinions on this @larsoner @wmvanvliet ?

computing the inverse.
noise_csd : instance of CrossSpectralDensity | None
Noise cross-spectral density (CSD) matrices. If provided, whitening
will be done. The noise CSDs need to have been computed for the same
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The noise CSD matrices need to be computed

Copy link
Contributor Author

@wmvanvliet wmvanvliet Nov 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My reasoning with the grammar was that as you are supplying the CSDs are a parameter to this function, they already have been computed.


_, _, proj, vertices, G, _, nn, orient_std = _prepare_beamformer_input(
info, forward, label, pick_ori, noise_cov=noise_csd_freq,
combine_xyz=combine_xyz, exp=exp)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this really need to be done per frequency now? It was outside the loop before if I see it right.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait ... we now whiten each lead field differently, is that it? Hummmmm, is that something we want (I do understand why we have it)? Different lead fields per frequency ... ? It strikes me as an odd thing to have. One step further: do we actually have to whiten the lead field or would it be sufficient to whiten the data covariance /CSD matrix.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We absolutely have to whiten per frequency. We have a different CSD at each frequency, and thus a different beamformer at each frequency. It's just a convenience feature that we pack multiple CSDs into one object and multiple beamformers in a single datastructure.

However, there is now a lot of redundant work happening within _prepare_beamformer_input. I plan to refactor that function to unpack the different things it does.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I do understand we get different weights at each frequency. The question is whether we want to have different lead fields, too. Probably not. And that raises the general question why we whiten lead fields - we do not invert the lead field in beamforming as compared to MNE solutions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what happens if the lead field is not whitened - if it mathematically would still pan out. Maybe worth a try! AFAIK there is no literature on lead field whitening with beamformers, only covariance matrices - but I might be missing that paper ;)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And that raises the general question why we whiten lead fields - we do not invert the lead field in beamforming as compared to MNE solutions.

At least for MNE any linear operator you apply to the data you should apply to the lead fields because otherwise the results will be biased. I assume the same would hold for beamformers, no?

For example if you apply a projector to the data, you apply it to the lead fields. If you know that this is the correct thing mathematically to do for beamformers when you apply a projection operation to the data, then it is probably mathematically also the correct thing to do in the case of whitening as well. (The whitener implicitly also applies the projection matrix FWIW.)

To me it's not so much about having "different lead fields" as it is about applying the same operators to data and the lead fields before doing computations on them. But maybe I'm thinking about it the wrong way...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it is def. the case that whatever is done on the original data needs to be done on the lead field as well. However - does the data need to be whitened to take care of scaling or is it enough to do the covariance matrix?
I quickly discussed things with @sarangnemo and he shares my intuition about this maybe not being ideal. I will try to run some data through differently whitened beamformers and report back!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the DICS code, there is no data, there is only the CSD matrix.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is a comparison between whitening both leadfield and the CSD (left) and only whitening the CSD (right):

fig

Co-Authored-By: Eric Larson <larson.eric.d@gmail.com>
@codecov
Copy link

codecov bot commented Nov 6, 2019

Codecov Report

❗ No coverage uploaded for pull request base (master@1ffebaf). Click here to learn what that means.
The diff coverage is n/a.

@@            Coverage Diff            @@
##             master    #7021   +/-   ##
=========================================
  Coverage          ?   89.76%           
=========================================
  Files             ?      444           
  Lines             ?    79430           
  Branches          ?    12699           
=========================================
  Hits              ?    71297           
  Misses            ?     5349           
  Partials          ?     2784

wmvanvliet and others added 2 commits November 6, 2019 15:50
Co-Authored-By: Eric Larson <larson.eric.d@gmail.com>
@wmvanvliet
Copy link
Contributor Author

Oops, completely forgot that _prepare_beamformer does not whiten the CSD. So we've been looking at results with only a whitened leadfield. Fixed now. Results look GOOD! :)

fig

@britta-wstnr
Copy link
Member

So there is no big difference in this case whether we whiten the lead field or not? I guess then the sparse thing to do, also in accordance with beamformer literature (Sekihara et al., 2008 https://ieeexplore.ieee.org/document/4454055 ), is to drop whitening of the lead field and data.
I will try to check this out with the LCMV as soon as I have time.

cc @sarangnemo

@wmvanvliet
Copy link
Contributor Author

I have tested the whitening also on some other data we have internally (cannot share) that is more noisy and with smaller effect sizes. For that case, whitening the leadfield "explodes" the result. Not whitening the leadfield gives sensible results. Evidence is accumulating against whitening the leadfield...

@wmvanvliet
Copy link
Contributor Author

wmvanvliet commented Nov 19, 2019

... I still don't completely understand why whitening the leadfield would be bad in beamforming but good in MNE. Could it be because MNE consideres the leadfield "as a whole", while a beamformer only operates on 3 columns at a time?

@agramfort
Copy link
Member

agramfort commented Nov 19, 2019 via email

@wmvanvliet
Copy link
Contributor Author

What do you mean by "simulation with some random coloring"? I agree that this implementation needs to be tested. I think I apply whitening constently to all objects, but we need to make sure.

@agramfort
Copy link
Member

agramfort commented Nov 19, 2019 via email

@wmvanvliet
Copy link
Contributor Author

ah, I see. Good idea!

@wmvanvliet
Copy link
Contributor Author

I think I finally know where things went wrong! I forgot to whiten the CSD in the apply_dics_csd function. (We also forgot to do it in the apply_lcmv_cov function). Now, the whitening gives much more sane results.

Check out this gist that multiplied the first 50 channels by 100 to make them artificially super loud. The whitening neatly undoes this:

https://gist.github.com/wmvanvliet/bf27cbf56a176651205789d1507870b6

@agramfort
Copy link
Member

agramfort commented Jan 6, 2020 via email

@britta-wstnr
Copy link
Member

So, @wmvanvliet, @ckiefer0, and me have been discussing this a few times over the last weeks off Github ... Our conclusion from today: we should take the same whitener (i.e., same noise cov matrix) over the different frequencies to prevent having different forward models per frequency (the motivation that kicked this whole discussion off).

@agramfort
Copy link
Member

agramfort commented Jan 6, 2020 via email

@larsoner
Copy link
Member

larsoner commented Jan 6, 2020

Agreed this seems reasonable.

So instead of having noise_csd as in this PR, you would just have noise_cov as an argument, right?

@wmvanvliet
Copy link
Contributor Author

Definitely not ready for merging yet. Lots of work still to be done to whip this into shape API wise. Of course, comments are always welcome!

Regarding the noise CSD, it is probably best if it were computed using exactly the same method as the data CSD. API-wise, I was thinking of putting in a check that the same frequencies are defined for both CSDs. Internally, we will average across the frequencies to obtain a single noise CSD.

Still some things to think about:

  • What happens when noise CSD is computed using less data than data CSD? I know COV matrices are scaled according to the number of averages, and CSD has n_fft field for this purpose.
  • Could it occur that one of the noise CSDs has a different rank than the data CSDs? Would this be cause for alarm?

@larsoner
Copy link
Member

larsoner commented Jun 1, 2020

We also forgot to do it in the apply_lcmv_cov function

This is now addressed in #7656 with a test that you can manually left- and right-apply_lcmv and obtain the same result

@larsoner larsoner mentioned this pull request Jun 3, 2020
13 tasks
@wmvanvliet
Copy link
Contributor Author

Closing in favor of #7656

@wmvanvliet wmvanvliet closed this Jul 9, 2020
@wmvanvliet wmvanvliet deleted the dics_whitening branch October 18, 2021 06:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants