Nilearn FirstLevelModel.fit() RAM memory explosion

Hi!
I’m having some issue when trying to fit() a nilearn.FirstLevelModel model, creating a GLM for each participant within 15-20. For each participant I’m calling the fit() for a new model and using the compute_contrast for calculating the beta coefficients that fit my fMRI data.
I’m trying to call explicitly del model and gc.collect() after fitting the model for each participant, but eventually get a rise of 8GB after calling each time fit() for a given participant, eventually RAM memory accumulating and getting an OutOfMemoryException.

I’d love to get some help, stuck on this issue for a long time…
Thanks!

Will try to have a look at this tomorrow.
I remember having this issue once but when I got back to it some times later I could not reproduce it.

In the meantime, can you tell us more?

How many runs per subject, how many time points per run, what image resolution and voxel size?

Also if you have a bit of code to show how you are setting and running your models.

Thanks Remi.
I have 1 run per subject, overall calling 15 times FirstLevelModel.fit() for each of my 15 participant’s fMRI BOLD data. I have 6804 timepoints per run, voxel size = 3X3X3 mm.

This is my relevant code section (after removing del model and gc.collect() because it didn’t help):

def run_for_participant(self, participant_id, data, stat_output_type, mask_strategy, mask_img=None):
        if mask_img is None:
            masker = NiftiMasker(mask_strategy=mask_strategy, lower_cutoff=0.9)
            masker.fit(data)
            mask_img = masker.mask_img_

        model = FirstLevelModel(mask_img=mask_img, memory=None, memory_level=0, n_jobs=1)
        model = model.fit(data, design_matrices=self.design_matrix)

        participant_dir = FileUtils.concat_file_paths(self.output_dir, participant_id)
        FileUtils.ensure_folder_exists(participant_dir)

        contrast_files = []
        for contrast_id, contrast_vector in self.contrasts.items():
            stats_map_img = model.compute_contrast(contrast_vector, output_type=stat_output_type)
            contrast_file = FileUtils.concat_file_paths(participant_dir, f"{participant_id}_{contrast_id}_map.nii.gz")
            stats_map_img.to_filename(contrast_file)
            contrast_files.append(contrast_file)

        merged_img = concat_imgs(contrast_files)
        merged_file = FileUtils.concat_file_paths(participant_dir, f"{participant_id}_contrast_map.nii.gz")
        merged_img.to_filename(merged_file)
        print(f"Saved participant {participant_id} contrast map of shape {merged_img.shape} in {merged_file}")

    def run_all(self, stat_output_type="z_score", mask_strategy="epi"):
        for participant_id, data in self.data_dict.items():
            print(f"Fitting GLM for participant {participant_id}...")
            self.run_for_participant(participant_id, data, stat_output_type, mask_strategy)
            print(f"Successfully fitted GLM for participant {participant_id}")

what’s the x, y, z dimension of your fmri data (how many voxel along each axis)?

There’s 6804 timepoints for each voxel (x,y,z, timepoints).

that does not answer my question: I need to know the number of voxels in your image.

when you run

def run_for_participant(self, participant_id, data, stat_output_type, mask_strategy, mask_img=None):
        if mask_img is None:
            masker = NiftiMasker(mask_strategy=mask_strategy, lower_cutoff=0.9)
            masker.fit(data)
            mask_img = masker.mask_img_

print the shape of the input image (assuming it is a nifti image object) with

print(data.shape)

or load it and print its shape

import nibabel as nib
print(nib.load(data).shape)

Oh, didn’t understand - it’s (64,76, 64,6804).

Hope it helps.

1 Like

I was not the clearest either.

OK with those dimensions, the following code eats through the 32GB of RAM of my laptop in no time and eventually crashes.

from nilearn._utils.data_gen import generate_fake_fmri_data_and_design
from nilearn.datasets import load_mni152_brain_mask
from nilearn.glm.first_level import FirstLevelModel

mni_brain_mask = load_mni152_brain_mask()

shape = [(64, 76, 64,6804)]
mask, fmri_data, design_matrices = generate_fake_fmri_data_and_design(shapes=shape, affine=mni_brain_mask.affine)

model = FirstLevelModel(t_r = 0.25, mask_img=mask)
model.fit(fmri_data, design_matrices=design_matrices)

Will open an issue nilearn to track this problem

OK I tried a couple of things (like using a slightly smaller mask) but that did not help.

There is an example in Nilearn that talks about dealing with large image (many time points) : have a look because it may help with what I want to suggest.

One thing you could do: restrict your GLM to only the regions you are interested in by using a mask of those regions only.

The other possibility is to do something similar to what SPM does under the hood. You run the GLM on subset of your data (chunk) and you run each chunk one after the other and reconstruct your total output by putting the chunks together.

Thanks @Remi-Gau for the thorough explanation.
But still not sure how to implement correctly using the batches approach (batch of participants or of batches of sub-pieces of the contrast map within a participant?).

The link you sent with dealing with big fMRI data mostly is about fitting a NiftiMasker and less useful for my case. In the near future I’m using a bigger Contrast Map (per participant) of the dims (64X76X64X120) - because of calculating 120 contrasts per voxel…

If you have any other ideas for a suitable implementation I’d be happy to hear.

Thanks again,

Roy.