MIRAGE: Robust multi-modal architectures translate fMRI-to-image models from vision to mental imagery.
The 6 matches
- [1] § 2. Results ↔ src/final_evaluations_mi_multi.ipynb, lines 500–559 · score 0.77 · AlexNet, EffNet, InceptionV3, PixCorr, SwAV, brain correlation
- [2] § 2. Results ↔ src/plots_across_methods.sh, lines 1–47 · score 0.75 · AlexNet, EffNet, InceptionV3, PixCorr, SwAV, brain correlation
- [3] § 4. Methods › 4.2. MIRAGE › 4.2.6 Reconstruction. ↔ src/Train.ipynb, lines 214–238 · score 0.52 · Stable Cascade, text embeddings, image embeddings, latent, train, CLIP
- [4] § 4. Methods › 4.2. MIRAGE › 4.2.2. Methodology. ↔ src/Train.ipynb, lines 214–238 · score 0.51 · Stable Cascade, text embedding, image embedding, VDVAE, retrieval, GiT
- [5] § 4. Methods › 4.2. MIRAGE › 4.2.2. Methodology. ↔ src/recon_inference_mi.ipynb, lines 192–223 · score 0.51 · Stable Cascade, text embedding, image embedding, VDVAE, retrieval, GiT
- [6] § 4. Methods › 4.2. MIRAGE › 4.2.1. Datasets. ↔ src/utils.py, lines 766–850 · score 0.51 · visual cortex, NSD subjects, voxel, mask, stimuli, trained
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Jupyter notebook · 439 lines · 17 KB · MIT · 2 matches
- # %% [markdown]
- # # Import packages & functions
- # %%
- import os
- import sys
- import json
- import argparse
- import numpy as np
- import math
- from einops import rearrange
- import time
- import random
- import string
- import h5py
- from tqdm import tqdm
- import webdataset as wds
- import gc
- from PIL import Image
- import matplotlib.pyplot as plt
- import torch
- import torch.nn as nn
- from torchvision import transforms
- from sklearn.linear_model import SGDRegressor
- from sklearn.preprocessing import StandardScaler
- from sklearn.utils import shuffle
- # tf32 data type is faster than standard float32
- torch.backends.cuda.matmul.allow_tf32 = True
- from sklearn.linear_model import Ridge
- import pickle
- # custom functions #
- import utils
- from sc_reconstructor import SC_Reconstructor
- from vdvae import VDVAE
- # %% [markdown]
- # # Configurations
- # %%
- # if running this interactively, can specify jupyter_args here for argparser to use
- if utils.is_interactive():
- model_name = "subj01_40sess_hypatia_ridge_scsubj01_40sess_hypatia_ridge_sc_medium_captions"
- print("model_name:", model_name)
- # global_batch_size and batch_size should already be defined in the 2nd cell block
- jupyter_args = f"--data_path=../dataset/ \
- --cache_dir=../cache/ \
- --model_name={model_name} \
- --batch_size=64 \
- --no-multi_subject --subj=1 --num_sessions=40 \
- --dual_guidance --prompt_recon --caption_type medium"
- print(jupyter_args)
- jupyter_args = jupyter_args.split()
- from IPython.display import clear_output # function to clear print outputs in cell
- %load_ext autoreload
- # this allows you to change functions in models.py or utils.py and have this notebook automatically update with your revisions
- %autoreload 2
- # %%
- parser = argparse.ArgumentParser(description="Model Training Configuration")
- parser.add_argument(
- "--model_name", type=str, default="testing",
- help="name of model, used for ckpt saving and wandb logging (if enabled)",
- )
- parser.add_argument(
- "--data_path", type=str, default=os.getcwd(),
- help="Path to where NSD data is stored / where to download it to",
- )
- parser.add_argument(
- "--cache_dir", type=str, default=os.getcwd(),
- help="Path to where misc. files downloaded from huggingface are stored. Defaults to current src directory.",
- )
- parser.add_argument(
- "--subj",type=int, default=1, choices=[1,2,3,4,5,6,7,8],
- help="Validate on which subject?",
- )
- parser.add_argument(
- "--num_sessions", type=float, default=40,
- help="Number of training sessions to include",
- )
- parser.add_argument(
- "--prompt_recon",action=argparse.BooleanOptionalAction, default=True,
- help="Use for prompt generating",
- )
- parser.add_argument(
- "--blurry_recon",action=argparse.BooleanOptionalAction,default=True,
- help="whether to output blurry reconstructions",
- )
- parser.add_argument(
- "--seed",type=int,default=42,
- )
- parser.add_argument(
- "--weight_decay",type=int,default=100000,
- )
- parser.add_argument(
- "--max_iter",type=int,default=50000,
- )
- parser.add_argument(
- "--dual_guidance",action=argparse.BooleanOptionalAction,default=True,
- help="Use the decoded captions for dual guidance",
- )
- parser.add_argument(
- "--caption_type",type=str,default='medium',choices=['coco','short', 'medium', 'schmedium'],
- )
- parser.add_argument(
- "--retrieval",action=argparse.BooleanOptionalAction,default=True,
- help="Use the decoded captions for dual guidance",
- )
- if utils.is_interactive():
- args = parser.parse_args(jupyter_args)
- else:
- args = parser.parse_args()
- print(f"args: {args}")
- # create global variables without the args prefix
- for attribute_name in vars(args).keys():
- globals()[attribute_name] = getattr(args, attribute_name)
- # seed all random functions
- utils.seed_everything(seed)
- outdir = os.path.abspath(f'../train_logs/{model_name}')
- os.makedirs(outdir,exist_ok=True)
- device = "cuda"
- # %% [markdown]
- # # Prep data, models, and dataloaders
- # %%
- x_train, valid_nsd_ids_train, x_test, test_nsd_ids = utils.load_nsd(subject=subj, num_sessions=num_sessions, data_path=data_path)
- print(x_train.shape, valid_nsd_ids_train.shape)
- print(f"Loaded subj {subj} betas!\n")
- # %% [markdown]
- # ## Prepare git feature
- # %%
- if not os.path.exists(f'{data_path}/git_image_features.hdf5'):
- print("Creating Git Feature...")
- from PIL import Image
- import requests
- from transformers import AutoProcessor, GitVisionModel, AutoModelForCausalLM, GitModel
- from modeling_git import GitForCausalLMClipEmb
- # Load 73k NSD images
- f = h5py.File(f'{data_path}/coco_images_224_float16.hdf5', 'r')
- beta_images = f['images']
- print("Loaded all 73k possible NSD images to cpu!", beta_images.shape)
- git_images = []
- processor = AutoProcessor.from_pretrained("microsoft/git-large-coco")
- git_text_model = GitForCausalLMClipEmb.from_pretrained("microsoft/git-large-coco")
- git_text_model.to(device)
- git_text_model.eval().requires_grad_(False)
- print("success load Git model")
- for i, image in enumerate(tqdm(beta_images)):
- pil_image = (image.transpose((1, 2, 0))*255).astype(np.uint8)
- inputs = processor(images=pil_image, return_tensors="pt").pixel_values.to(device)
- outputs = git_text_model.git.image_encoder(inputs).last_hidden_state
- # valid the captions
- if i <= 5:
- generated_ids = git_text_model.generate(pixel_values=outputs, max_length=50)
- generated_caption = processor.batch_decode(generated_ids, skip_special_tokens=True)
- print(generated_caption)
- git_images.append(outputs.detach().cpu().numpy())
- with h5py.File(f'{data_path}/git_image_features.hdf5', 'w') as f:
- f.create_dataset('features', data=np.array(git_images))
- print("Finished!")
- del beta_images, git_images
- else:
- print("git_image_features.hdf5 already exist!")
- # %%
- # Load 73k NSD images
- f = h5py.File(f'{data_path}/coco_images_224_float16.hdf5', 'r')
- images = f['images'] # if you go OOM you can remove the [:] so it isnt preloaded to cpu! (will require a few edits elsewhere tho)
- # images = torch.Tensor(images).to("cpu").to(data_type)
- print("Loaded all 73k possible NSD images to cpu!", images.shape)
- # Load 73k NSD captions
- if caption_type == "schmedium":
- captions_small = np.load(f'{data_path}/preprocessed_data/short_length_captions.npy')
- captions_medium = np.load(f'{data_path}/preprocessed_data/mid_length_captions_73K.npy')
- # Create a mask to randomly select elements from both arrays
- mask = np.random.rand(len(captions_small)) > 0.5
- # Mix the arrays based on the mask
- captions = np.where(mask, captions_small, captions_medium)
- else:
- if caption_type == "coco":
- caption_file = "annots_73k.npy"
- elif caption_type == "short":
- caption_file = "short_length_captions.npy"
- elif caption_type == "medium":
- caption_file = "mid_length_captions_73K.npy"
- else:
- raise ValueError("Invalid caption type")
- captions = np.load(f'{data_path}/preprocessed_data/{caption_file}')
- print("Loaded all 73k NSD captions to cpu!", captions.shape)
- train_images = torch.zeros((len(valid_nsd_ids_train), 3, 224, 224))
- train_captions = np.zeros((len(valid_nsd_ids_train),), dtype=object)
- # Load specific training data
- for i, idx in enumerate(valid_nsd_ids_train):
- train_images[i] = torch.from_numpy(images[idx])
- train_captions[i] = captions[idx]
- print(f"Filtered down to only the {len(valid_nsd_ids_train)} training images for subject {subj}!")
- # %% [markdown]
- # ## Load models
- # %% [markdown]
- # ### Feature extractor model
- # %%
- clip_extractor = SC_Reconstructor(compile_models=False, embedder_only=True, device=device, cache_dir=cache_dir)
- vdvae = VDVAE(device=device, cache_dir=cache_dir)
- image_embedding_variant = "stable_cascade"
- clip_emb_dim = 768
- clip_seq_dim = 1
- retrieval_embedding_variant = "stable_cascade_hidden"
- retrieval_emb_dim = 1024
- retrieval_seq_dim = 257
- text_embedding_variant = "stable_cascade"
- clip_text_seq_dim=77
- clip_text_emb_dim=1280
- latent_embedding_variant = "vdvae"
- latent_emb_dim = 91168
- prompt_embedding_variant = "git"
- git_seq_dim = 257
- git_emb_dim = 1024
- if caption_type != "coco":
- text_embedding_variant += f"_{caption_type}"
- # %% [markdown]
- # # Creating block of CLIP embeddings
- # %%
- file_path = f"{data_path}/preprocessed_data/subject{subj}/{image_embedding_variant}_image_embeddings_train_{num_sessions}sess.pt"
- emb_batch_size = 50
- if not os.path.exists(file_path):
- # Generate CLIP Image embeddings
- print("Generating Image embeddings!")
- clip_image_train = torch.zeros((len(train_images), clip_seq_dim, clip_emb_dim)).to("cpu")
- for i in tqdm(range(len(train_images) // emb_batch_size), desc="Encoding clip images..."):
- batch_list = []
- for img in train_images[i * emb_batch_size:i * emb_batch_size + emb_batch_size]:
- batch_list.append(transforms.ToPILImage()(img))
- clip_image_train[i * emb_batch_size:i * emb_batch_size + emb_batch_size] = clip_extractor.embed_image(batch_list).to("cpu")
- torch.save(clip_image_train, file_path)
- else:
- clip_image_train = torch.load(file_path)
- if dual_guidance:
- emb_batch_size = 50
- file_path_txt = f"{data_path}/preprocessed_data/subject{subj}/{text_embedding_variant}_text_embeddings_train_{num_sessions}sess.pt"
- if not os.path.exists(file_path_txt):
- # Generate CLIP Text embeddings
- print("Generating Text embeddings!")
- clip_text_train = torch.zeros((len(train_captions), clip_text_seq_dim, clip_text_emb_dim)).to("cpu")
- for i in tqdm(range(len(train_captions) // emb_batch_size), desc="Encoding captions..."):
- batch_captions = train_captions[i * emb_batch_size:i * emb_batch_size + emb_batch_size].tolist()
- clip_text_train[i * emb_batch_size:i * emb_batch_size + emb_batch_size] = clip_extractor.embed_text(batch_captions).to("cpu")
- torch.save(clip_text_train, file_path_txt)
- else:
- clip_text_train = torch.load(file_path_txt)
- if blurry_recon:
- emb_batch_size = 1
- file_path = f"{data_path}/preprocessed_data/subject{subj}/{latent_embedding_variant}_latent_embeddings_train_{num_sessions}sess.pt"
- if not os.path.exists(file_path):
- print("Generating Latent Image embeddings!")
- vae_image_train = torch.zeros((len(train_images), latent_emb_dim)).to("cpu")
- for i in tqdm(range(len(train_images)), desc="Encoding blurry images..."):
- img = transforms.ToPILImage()(train_images[i])
- vae_image_train[i * emb_batch_size:i * emb_batch_size + emb_batch_size] = vdvae.embed_latent(img).reshape(-1, latent_emb_dim).to("cpu")
- torch.save(vae_image_train, file_path)
- else:
- vae_image_train = torch.load(file_path)
- if retrieval:
- file_path = f"{data_path}/preprocessed_data/subject{subj}/{retrieval_embedding_variant}_retrieval_embeddings_train_{num_sessions}sess.pt"
- emb_batch_size = 50
- if not os.path.exists(file_path):
- # Generate CLIP Retrieval embeddings
- print("Generating Retrieval embeddings!")
- retrieval_image_train = torch.zeros((len(train_images), retrieval_seq_dim, retrieval_emb_dim)).to("cpu")
- for i in tqdm(range(len(train_images) // emb_batch_size), desc="Encoding images..."):
- batch_list = []
- for img in train_images[i * emb_batch_size:i * emb_batch_size + emb_batch_size]:
- batch_list.append(transforms.ToPILImage()(img))
- retrieval_image_train[i * emb_batch_size:i * emb_batch_size + emb_batch_size] = clip_extractor.embed_image(batch_list, hidden=True).to("cpu")
- # Normalize for optimal cosine similarity
- retrieval_image_train = torch.nn.functional.normalize(retrieval_image_train, p=2, dim=2)
- torch.save(retrieval_image_train, file_path)
- else:
- retrieval_image_train = torch.load(file_path)
- # Load 73k GiT NSD features
- if prompt_recon:
- file_path_git = f"{data_path}/preprocessed_data/subject{subj}/{prompt_embedding_variant}_prompt_embeddings_train_{num_sessions}sess.pt"
- if not os.path.exists(file_path_git):
- with h5py.File(f'{data_path}/git_image_features.hdf5', 'r') as f:
- git_features = f['features'][:]
- train_git_images = torch.zeros((len(valid_nsd_ids_train), 257,1024))
- for i, idx in enumerate(valid_nsd_ids_train):
- train_git_images[i] = torch.from_numpy(git_features[idx])
- torch.save(train_git_images, file_path_git)
- del git_features
- else:
- train_git_images = torch.load(file_path_git)
- print(f"Loaded vectors for subj{subj}!")
- # %% [markdown]
- # # Train Ridge regression models
- # %%
- start = time.time()
- ridge_weights = np.zeros((clip_seq_dim * clip_emb_dim, x_train.shape[-1])).astype(np.float32)
- ridge_biases = np.zeros((clip_seq_dim * clip_emb_dim)).astype(np.float32)
- print(f"Training Ridge Image model with alpha={weight_decay}")
- model = Ridge(
- alpha=weight_decay,
- max_iter=max_iter,
- random_state=42,
- )
- model.fit(x_train, clip_image_train.reshape(len(clip_image_train), -1))
- ridge_weights = model.coef_
- ridge_biases = model.intercept_
- datadict = {"coef" : ridge_weights, "intercept" : ridge_biases}
- # Save the regression weights
- with open(f'{outdir}/ridge_image_weights.pkl', 'wb') as f:
- pickle.dump(datadict, f)
- del clip_image_train
- del ridge_weights
- del ridge_biases
- del datadict
- if dual_guidance:
- ridge_weights_txt = np.zeros((clip_text_seq_dim * clip_text_emb_dim, x_train.shape[-1])).astype(np.float32)
- ridge_biases_txt = np.zeros((clip_text_seq_dim * clip_text_emb_dim)).astype(np.float32)
- print(f"Training Ridge Text model with alpha={weight_decay}")
- model = Ridge(
- alpha=weight_decay,
- max_iter=max_iter,
- random_state=42,
- )
- model.fit(x_train, clip_text_train.reshape(len(clip_text_train), -1))
- ridge_weights_txt = model.coef_
- ridge_biases_txt = model.intercept_
- datadict = {"coef" : ridge_weights_txt, "intercept" : ridge_biases_txt}
- # Save the regression weights
- with open(f'{outdir}/ridge_text_weights.pkl', 'wb') as f:
- pickle.dump(datadict, f)
- del clip_text_train
- del ridge_weights_txt
- del ridge_biases_txt
- del datadict
- if blurry_recon:
- ridge_weights_blurry = np.zeros((latent_emb_dim, x_train.shape[-1])).astype(np.float32)
- ridge_biases_blurry = np.zeros((latent_emb_dim,)).astype(np.float32)
- print(f"Training Ridge Blurry recon model with alpha={weight_decay}")
- model = Ridge(
- alpha=weight_decay,
- max_iter=max_iter,
- random_state=42,
- )
- model.fit(x_train, vae_image_train)
- ridge_weights_blurry = model.coef_
- ridge_biases_blurry = model.intercept_
- datadict = {"coef" : ridge_weights_blurry, "intercept" : ridge_biases_blurry}
- # Save the regression weights
- with open(f'{outdir}/ridge_blurry_weights.pkl', 'wb') as f:
- pickle.dump(datadict, f)
- del vae_image_train
- del ridge_weights_blurry
- del ridge_biases_blurry
- del datadict
- if retrieval:
- ridge_weights = np.zeros((retrieval_seq_dim * retrieval_emb_dim, x_train.shape[-1])).astype(np.float32)
- ridge_biases = np.zeros((retrieval_seq_dim * retrieval_emb_dim)).astype(np.float32)
- print(f"Training Ridge Retrieval model with alpha={weight_decay}")
- model = Ridge(
- alpha=weight_decay,
- max_iter=max_iter,
- random_state=42,
- )
- x_train_norm = torch.nn.functional.normalize(x_train, p=2, dim=1)
- model.fit(x_train_norm, retrieval_image_train.reshape(len(retrieval_image_train), -1))
- ridge_weights = model.coef_
- ridge_biases = model.intercept_
- datadict = {"coef" : ridge_weights, "intercept" : ridge_biases}
- # Save the regression weights
- with open(f'{outdir}/ridge_retrieval_weights.pkl', 'wb') as f:
- pickle.dump(datadict, f)
- del retrieval_image_train
- del ridge_weights
- del ridge_biases
- del datadict
- if prompt_recon:
- ridge_weights_prompt = np.zeros((git_seq_dim*git_emb_dim, x_train.shape[-1])).astype(np.float32)
- ridge_biases_prompt = np.zeros((git_seq_dim*git_emb_dim)).astype(np.float32)
- print(f"Training Ridge prompt recon model with alpha={weight_decay}")
- model = Ridge(
- alpha=weight_decay,
- max_iter=max_iter,
- random_state=42,
- )
- model.fit(x_train, train_git_images.reshape(len(train_git_images), -1))
- ridge_weights_prompt = model.coef_
- ridge_biases_prompt = model.intercept_
- datadict = {"coef" : ridge_weights_prompt, "intercept" : ridge_biases_prompt}
- # Save the regression weights
- with open(f'{outdir}/ridge_prompt_weights.pkl', 'wb') as f:
- pickle.dump(datadict, f)
- del train_git_images
- del ridge_weights_prompt
- del ridge_biases_prompt
- del datadict
- print(f"Elapsed training time for {model_name}: {time.strftime('%H:%M:%S', time.gmtime(time.time() - start))}")
Train.ipynb at commit 7dbc13f, under MIT · at the source
Overview
- University of Minnesota, Minneapolis, Minnesota, United States of America
- Medical AI Research Center (MedARC), San Francisco, California, United States of America
- University of Sydney, Sydney, Australia
- Stanford University, Palo Alto, California, United States of America
- Alljoined, San Francisco, California, Unites States of America
- Sophont, San Francisco, California, Unites States of America
- Princeton Neuroscience Institute, Princeton, New Jersey, United States of America
Abstract
To be useful for downstream applications, vision decoding models that are trained to reconstruct seen images from human brain activity must be able to generalize to internally generated visual representations, i.e., mental images. In an analysis of the recently released NSD-Imagery dataset, we demonstrated that while some modern vision decoders can perform quite well on mental image reconstruction, some fail, and that state-of-the-art (SOTA) performance on seen image reconstruction is no guarantee of SOTA performance on mental image reconstruction. Motivated by these findings, we developed MIRAGE, a method explicitly designed to train on vision datasets and cross-decode mental images from brain activity. MIRAGE employs a linear backbone and multi-modal text and image features as input to a diffusion model. Feature metrics and human raters establish MIRAGE as SOTA for mental image reconstruction on the NSD-Imagery benchmark. With ablation analysis we show that mental image reconstruction works best when decoders use image features with relatively few dimensions and include guidance from text-based and both high- and low-level image-based features. Our work indicates that–given the right architecture–existing large-scale datasets using external stimuli are viable training data for decoding mental images, and warrant optimism about the future success and utility of mental image reconstruction.
Reproduced under the paper's license (CC BY), from the paper cited above.
Repositories
Its files are read in the Code ↔ Paper reader above, with 6 matches between paragraphs and lines of code.
naturalscenesdataset.org
Availability: 1 check, the latest on 28 September 2026: the link answers (HTTP 200)
- 28 September 2026: the link answers (HTTP 200)
MedARC-AI/MIRAGE
7dbc13fb888df0845ea40c4a8c7375cf0bce626f, 16 February 2025Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
100 files
- src/
Train.ipynb — Jupyter, 439 lines, 2 matches - src/
autoencoder/ — Python, 239 linesconvnext.py - src/
automate_best.sh — Shell, 46 lines - src/
automate_best2.sh — Shell, 46 lines - src/
automate_best3.sh — Shell, 48 lines - src/
automate_best4.sh — Shell, 48 lines - src/
final_evaluations_mi_mul — Jupyter, 748 lines, 1 matchti.ipynb - src/
generative_models/ — Python, 943 linesmain.py - src/
generative_models/ — Python, 1 linescripts/ __init__.py - src/
generative_models/ — Python, 1 linescripts/ demo/ __init__.py - src/
generative_models/ — Python, 156 linesscripts/ demo/ detect.py - src/
generative_models/ — Python, 59 linesscripts/ demo/ discretization.py - src/
generative_models/ — Python, 364 linesscripts/ demo/ sampling.py - src/
generative_models/ — Python, 887 linesscripts/ demo/ streamlit_helpers.py - src/
generative_models/ — Python, 223 linesscripts/ demo/ turbo.py - src/
generative_models/ — Python, 200 linesscripts/ demo/ video_sampling.py - src/
generative_models/ — Python, 278 linesscripts/ sampling/ simple_video_sample.py - src/
generative_models/ — Python, 319 linesscripts/ tests/ attention.py - src/
generative_models/ — Python, 1 linescripts/ util/ __init__.py - src/
generative_models/ — Python, 1 linescripts/ util/ detection/ __init__.py - src/
generative_models/ — Python, 110 linesscripts/ util/ detection/ nsfw_and_watermark_decte ction.py - src/
generative_models/ — Python, 4 linessgm/ __init__.py - src/
generative_models/ — Python, 1 linesgm/ data/ __init__.py - src/
generative_models/ — Python, 67 linessgm/ data/ cifar10.py - src/
generative_models/ — Python, 80 linessgm/ data/ dataset.py - src/
generative_models/ — Python, 85 linessgm/ data/ mnist.py - src/
generative_models/ — Python, 385 linessgm/ inference/ api.py - src/
generative_models/ — Python, 305 linessgm/ inference/ helpers.py - src/
generative_models/ — Python, 135 linessgm/ lr_scheduler.py - src/
generative_models/ — Python, 2 linessgm/ models/ __init__.py - src/
generative_models/ — Python, 615 linessgm/ models/ autoencoder.py - src/
generative_models/ — Python, 341 linessgm/ models/ diffusion.py - src/
generative_models/ — Python, 6 linessgm/ modules/ __init__.py - src/
generative_models/ — Python, 759 linessgm/ modules/ attention.py - src/
generative_models/ — Python, 1 linesgm/ modules/ autoencoding/ __init__.py - src/
generative_models/ — Python, 7 linessgm/ modules/ autoencoding/ losses/ __init__.py - src/
generative_models/ — Python, 306 linessgm/ modules/ autoencoding/ losses/ discriminator_loss.py - src/
generative_models/ — Python, 73 linessgm/ modules/ autoencoding/ losses/ lpips.py - src/
generative_models/ — Python, 1 linesgm/ modules/ autoencoding/ lpips/ __init__.py - src/
generative_models/ — Python, 1 linesgm/ modules/ autoencoding/ lpips/ loss/ __init__.py - src/
generative_models/ — Python, 147 linessgm/ modules/ autoencoding/ lpips/ loss/ lpips.py - src/
generative_models/ — Python, 1 linesgm/ modules/ autoencoding/ lpips/ model/ __init__.py - src/
generative_models/ — Python, 88 linessgm/ modules/ autoencoding/ lpips/ model/ model.py - src/
generative_models/ — Python, 128 linessgm/ modules/ autoencoding/ lpips/ util.py - src/
generative_models/ — Python, 17 linessgm/ modules/ autoencoding/ lpips/ vqperceptual.py - src/
generative_models/ — Python, 31 linessgm/ modules/ autoencoding/ regularizers/ __init__.py - src/
generative_models/ — Python, 40 linessgm/ modules/ autoencoding/ regularizers/ base.py - src/
generative_models/ — Python, 487 linessgm/ modules/ autoencoding/ regularizers/ quantize.py - src/
generative_models/ — Python, 349 linessgm/ modules/ autoencoding/ temporal_ae.py - src/
generative_models/ — Python, 1 linesgm/ modules/ diffusionmodules/ __init__.py - src/
generative_models/ — Python, 75 linessgm/ modules/ diffusionmodules/ denoiser.py - src/
generative_models/ — Python, 59 linessgm/ modules/ diffusionmodules/ denoiser_scaling.py - src/
generative_models/ — Python, 24 linessgm/ modules/ diffusionmodules/ denoiser_weighting.py - src/
generative_models/ — Python, 69 linessgm/ modules/ diffusionmodules/ discretizer.py - src/
generative_models/ — Python, 99 linessgm/ modules/ diffusionmodules/ guiders.py - src/
generative_models/ — Python, 105 linessgm/ modules/ diffusionmodules/ loss.py - src/
generative_models/ — Python, 32 linessgm/ modules/ diffusionmodules/ loss_weighting.py - src/
generative_models/ — Python, 748 linessgm/ modules/ diffusionmodules/ model.py - src/
generative_models/ — Python, 853 linessgm/ modules/ diffusionmodules/ openaimodel.py - src/
generative_models/ — Python, 362 linessgm/ modules/ diffusionmodules/ sampling.py - src/
generative_models/ — Python, 43 linessgm/ modules/ diffusionmodules/ sampling_utils.py - src/
generative_models/ — Python, 31 linessgm/ modules/ diffusionmodules/ sigma_sampling.py - src/
generative_models/ — Python, 369 linessgm/ modules/ diffusionmodules/ util.py - src/
generative_models/ — Python, 493 linessgm/ modules/ diffusionmodules/ video_model.py - src/
generative_models/ — Python, 34 linessgm/ modules/ diffusionmodules/ wrappers.py - src/
generative_models/ — Python, 1 linesgm/ modules/ distributions/ __init__.py - src/
generative_models/ — Python, 102 linessgm/ modules/ distributions/ distributions.py - src/
generative_models/ — Python, 86 linessgm/ modules/ ema.py - src/
generative_models/ — Python, 1 linesgm/ modules/ encoders/ __init__.py - src/
generative_models/ — Python, 1,065 linessgm/ modules/ encoders/ modules.py - src/
generative_models/ — Python, 301 linessgm/ modules/ video_attention.py - src/
generative_models/ — Python, 275 linessgm/ util.py - src/
generative_models/ — Python, 111 linestests/ inference/ test_inference.py - src/
modeling_git.py — Python, 2,053 lines - src/
models.py — Python, 631 lines - src/
plots_across_methods.ipy — Jupyter, 240 linesnb - src/
plots_across_methods.sh — Shell, 1,082 lines, 1 match - src/
plots_across_subjects.ip — Jupyter, 416 linesynb - src/
recon_inference_mi.ipynb — Jupyter, 511 lines, 1 match - src/
retrieval_inference_mi.i — Jupyter, 650 linespynb - src/
sc_reconstructor.py — Python, 189 lines - src/
setup.sh — Shell, 17 lines - src/
utils.py — Python, 1,411 lines, 1 match - src/
vdvae.py — Python, 75 lines - src/
vdvae/ — Python, 163 linesdata.py - src/
vdvae/ — Python, 14 linesfiles_to_npy.py - src/
vdvae/ — Python, 159 lineshps.py - src/
vdvae/ — Python, 68 linesimage_utils.py - src/
vdvae/ — Python, 102 linesmodel_utils.py - src/
vdvae/ — Shell, 2 linessetup_cifar10.sh - src/
vdvae/ — Shell, 13 linessetup_ffhq1024.sh - src/
vdvae/ — Shell, 12 linessetup_ffhq256.sh - src/
vdvae/ — Shell, 28 linessetup_imagenet.sh - src/
vdvae/ — Python, 136 linestrain.py - src/
vdvae/ — Python, 190 linestrain_helpers.py - src/
vdvae/ — Python, 252 linesvae.py - src/
vdvae/ — Python, 165 linesvae_helpers.py - src/
vdvae/ — Python, 140 linesvdvae_utils.py - LICENSE — License, 21 lines
- README.md — Text, 54 lines
The paper's code and data availability statement is in the Data section.
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 2 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 98 scripts, each with its path and the digest of its content;
- 6 matches between paragraphs of the paper and lines of the code (method lexical-v1);
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
No dataset and no data link were found in the paper.
Data Availability
NSD-Imagery data has been released publically, and can be accessed at https://
Reproduced under the paper's license (CC BY), from the paper cited above.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 1, 28 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 8 authors, 8 MeSH terms, 1 funder, 44 references.
Cite
This paper
Kneeland, R., Villanueva, C. K. T., Chen, T., Ojeda, J., Khanna, S., Xu, J., Scotti, P. S., & Naselaris, T. (2026). MIRAGE: Robust multi-modal architectures translate fMRI-to-image models from vision to mental imagery. PLoS computational biology, 22(5), e1014263. https://
BibTeX
@article{kneeland2026mir
author = {Kneeland, Reese and Villanueva, Cesar Kadir Torrico and Chen, Tong and Ojeda, Jordyn and Khanna, Shubh and Xu, Jonathan and Scotti, Paul S. and Naselaris, Thomas},
title = {{MIRAGE: Robust multi-modal architectures translate fMRI-to-image models from vision to mental imagery}},
journal = {PLoS computational biology},
year = {2026},
month = may,
volume = {22},
number = {5},
pages = {e1014263},
publisher = {PLOS},
issn = {1553-734X},
doi = {10.1371/
url = {https://
pmid = {42172302},
pmcid = {PMC13218618}
}
RIS
TY - JOUR
AU - Kneeland, Reese
AU - Villanueva, Cesar Kadir Torrico
AU - Chen, Tong
AU - Ojeda, Jordyn
AU - Khanna, Shubh
AU - Xu, Jonathan
AU - Scotti, Paul S.
AU - Naselaris, Thomas
TI - MIRAGE: Robust multi-modal architectures translate fMRI-to-image models from vision to mental imagery
T2 - PLoS computational biology
J2 - PLoS Comput Biol
PY - 2026
DA - 2026/
VL - 22
IS - 5
SP - e1014263
SN - 1553-734X
PB - PLOS
DO - 10.1371/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1371/
"type": "article-journal",
"title": "MIRAGE: Robust multi-modal architectures translate fMRI-to-image models from vision to mental imagery",
"container-title": "PLoS computational biology",
"author": [
{
"family": "Kneeland",
"given": "Reese"
},
{
"family": "Villanueva",
"given": "Cesar Kadir Torrico"
},
{
"family": "Chen",
"given": "Tong"
},
{
"family": "Ojeda",
"given": "Jordyn"
},
{
"family": "Khanna",
"given": "Shubh"
},
{
"family": "Xu",
"given": "Jonathan"
},
{
"family": "Scotti",
"given": "Paul S."
},
{
"family": "Naselaris",
"given": "Thomas"
}
],
"container-title-short":
"volume": "22",
"issue": "5",
"page": "e1014263",
"DOI": "10.1371/
"PMID": "42172302",
"PMCID": "PMC13218618",
"ISSN": "1553-734X",
"publisher": "PLOS",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
5,
22
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.7554/elife.107933 [code]
- Modality-agnostic decoding of vision and language from fMRI.Journal: eLifeIn common: Hugging Face Transformers, OpenCV, scikit-image, 9 other tools, fMRI, 11 references
- [2] doi:10.1007/s12021-026-09803-3 [code]
- NeuroFusion: A Unified Framework for Generalized Visual Stimulus Decoding from fMRI Across Datasets and Subjects.Journal: NeuroinformaticsIn common: PyTorch Lightning, scikit-image, h5py, 8 other tools, fMRI, 6 references
- [3] doi:10.1162/imag.a.1299 [code]
- A modular semantic-structural pipeline for visual decoding from primate spiking data via selective temporal integration.Journal: Imaging neuroscience (Cambridge, Mass.)In common: PyTorch Lightning, Hugging Face Transformers, scikit-image, 8 other tools, 6 references
- [4] doi:10.1038/s41597-026-07248-6 [code]
- A large-scale fMRI dataset for vision-language semantic association.Journal: Scientific dataIn common: imageio, OpenCV, scikit-image, 9 other tools, fMRI, 3 references
- [5] doi:10.1162/imag.a.1326 [code]
- RAVEN: Robust, generalizable, multi-resolution structural MRI upsampling using autoencoders.Journal: Imaging neuroscience (Cambridge, Mass.)In common: PyTorch Lightning, imageio, Hugging Face Transformers, 10 other tools
- [6] doi:10.1038/s42003-026-10169-0 [code]
- Shared representations in brains and models reveal a two-route cortical organization during scene perception.Journal: Communications biologyIn common: Hugging Face Transformers, h5py, Pillow, 7 other tools, 3 references
- [7] doi:10.1038/s42003-026-10957-8 [code]
- Brain defence by the extracellular matrix protein Cochlin.Journal: Communications biologyIn common: PyTorch Lightning, Hugging Face Transformers, UMAP, 9 other tools
- [8] doi:10.1038/s41467-026-71267-5 [code]
- Human-like cognitive generalization for large models via mental representation-guided supervision.Journal: Nature communicationsIn common: Hugging Face Transformers, UMAP, scikit-image, 8 other tools, 1 reference
- [9] doi:10.1371/journal.pcbi.1013499 [code]
- VesiclePy: A machine learning vesicle analysis toolbox for volume electron microscopy.Journal: PLoS computational biologyIn common: imageio, UMAP, OpenCV, 9 other tools
- [10] doi:10.1371/journal.pcbi.1014656 [code]
- Contrastive learning to fine-tune feature extraction models for the visual cortex.Journal: PLoS computational biologyIn common: Pillow, NiBabel, PyTorch, 5 other tools, fMRI, 4 references
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 2 repositories of the authors' code, each at its verified commit and with its license, 98 scripts, and 6 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:d1bc7320e463bcdd…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
Request its removal
To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).
Discussion, reproductions, activity
Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.
Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
