ROI mask with scattered voxels

Dear TDT experts,

I’m interested in decoding the pattern in V1, V2, V3, and MT+. Some literature mentions that they chose certain numbers of voxels (for instances, 100 voxels in V1) which have strongest response in retinotopic maps (e.g., Kamitani and Tong, 2006). If these 100 voxels are not adjacent to each other (i.e, no way to make a single ROI to cover all of these 100 voxels without including other unwanted voxels), is it possible to carry out the analysis with TDT? Does it make sense to create a mask with one voxel over here, one voxel over there, and so on?

Thanks for your help!

Best regards,
Wen-Jing

Hi Wen-Jing,

Yes, you can do that and it’s not unusual. But while it’s not intuitive, sometimes including noisy voxels can actually help the classification process. The classifier can effectively use this to remove correlated noise from signal voxels. See this and this (Figure 3).

You can also do something similar to what you want in TDT by using the feature selection utilities. Pass your normal ROI mask and then set

cfg.feature_selection.method = 'filter';

cfg.feature_selection.filter = 'F0'; % this uses both responses above and below baseline

cfg.feature_selection.n_vox = 100;

You can also set

cfg.feature_selection.filter = 'external';

and pass an image that you want to use to base the feature selection contrast on (e.g. a volume using a T-contrast). Just make sure there is no double dipping involved.

There are many more options, e.g. picking voxels that discriminate univariately between conditions but using only training data, or using nested cross-validation to find the optimal number of voxels, or using recursive feature elimination. I often found that for small data sets nested cross-validation doesn’t help much, though.

Hope that helps!
Martin

Hi Martin,

Thank you, it really helps.

But I have some follow-up questions after I tried the ‘filter’ method.
I have a single subject’s data, and there are 8 runs. I used a normal ROI mask (a V1 mask created based on retinotopic mapping analysis) and did exactly what you mentioned:

cfg.feature_selection.method = ‘filter’;
cfg.feature_selection.filter = ‘F0’; % this uses both responses above and below baseline
cfg.feature_selection.n_vox = 100;

The output confuses me:
results.accuracy.output is 81x1 double;
results.feature_selection is 1x81 structure and it looks like this- image

I thought this method will pick up 100 voxels and use them for training and testing, but the result suggests it does something else. Because in that case I would expect only one accuracy. So I’m not sure what exactly this method does and it would be nice if you could give me some hint.

Thanks very much for your help!

Best regards,
Wen-Jing

Hi Wen-Jing,

That looks unusual. Essentially it says you passed 81 ROIs instead of one, and since 100 voxels could not be selected in each iteration, it selected the maximum, i.e. 27 in the first, 12 in the second, etc. Since I don’t know your cfg settings, I can only guess. I think maybe instead of passing a binary ROI mask you passed a mask with many different numbers. This would be treated by TDT as separate ROIs (the ROI mask would essentially be a multimask).

Look at the screen output and warnings, TDT should inform you if it is 81 ROIs and if you can’t select as many voxels as you want. Perhaps plot the different steps using

cfg.plot_selected_voxels = 1;

Also, rethinking my suggestion, I think I would actually recommend using an external T-map or F-map instead, it will be more sensitive because it uses the residuals of the runs for calculating those values rather than the beta values alone. If you use the F-contrast against baseline, you could set

cfg.feature_selection.estimation = 'all';

If not, you should keep it at 'across'.

If you want to use the actual contrast that classification is based on itself (probably the most sensitive approach), you would have to create one external map for each cross-validation iteration. The first map would use all but the first run for the contrast, the second all but the second, etc. Then you can pass all eight.

cfg.decoding.method = 'classification'; 
cfg.feature_selection.method = 'filter'; 
cfg.feature_selection.filter = 'external'; 

cfg.feature_selection.external_fname = {...}; % file names for your contrast maps

Cheers,
Martin

Hi Martin,

I guess the problem I met earlier is because I erroneously set the cfg.analysis as ‘searchlight’. After I use cfg.analysis = ‘roi’, the result.accuracy.output becomes one single value, which makes more sense.

If I create a roi mask which contain exact 100 voxels I want (based on a functional localiser task), do I still need to use feature selection? Or do I just analyse it as a simple ROI analysis?

The other way you mentioned:

" If you want to use the actual contrast that classification is based on itself (probably the most sensitive approach)…"

Is it using univariate differences for feature selection?

Thanks for your further help and TDT is an excellent toolbox!

Best,
Wen-Jing

Hi Wen-Jing,

Yes, you don’t need feature selection and can just analyze it as an ROI. If it’s a separate functional localizer, that is even better! You could use nested cross-validation to identify the optimal number of voxels based on the separate functional localizer using the 'external' setting, but with few data points that will likely overfit during training. I would stick to your approach of the top 100 voxels.

Yes, that’s right! Just make sure that the data are independent (using the 'across' setting. With the 'all' setting and non-independent data you would really need to know what you do to avoid double dipping, with 'across' you are on the safe side. For independent data (i.e. external, always use 'all'.

Cheers,
Martin

1 Like