A modular semantic-structural pipeline for visual decoding from primate spiking data via selective temporal integration.
The 4 matches
- [1] § Material and Methods › Task 2: Image generation ↔ monkeys_gener.ipynb, lines 81–144 · score 0.85 · classifier free guidance, IP Adapter scale, Stable Diffusion, VAE, prompts, pipeline
- [2] § Material and Methods › Task 2: Image generation ↔ monkeys_gener.ipynb, lines 848–915 · score 0.64 · Gaussian blur, SSIM score, smoothed, resized, reconstruct
- [3] § Material and Methods › Task 1: Stimulus retrieval ↔ monkeys_gener.ipynb, lines 751–773 · score 0.53 · closest neighbor, nearest neighbor, predicted
- [4] § Material and Methods ↔ monkeys_gener.ipynb, lines 81–144 · score 0.52 · IP Adapter, Stable Diffusion, pipeline, weighting, embedding, model
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 · 922 lines · 30 KB · no license · 4 matches
- # %%
- import numpy as np
- from scipy.io import loadmat
- import os
- from os.path import join as opj
- from h5py import File
- import pandas as pd
- # load CLIP from huggingface, load the first N images and extract the features
- from transformers import CLIPProcessor, CLIPModel
- import torch
- import tqdm
- from PIL import Image
- from diffusers import StableDiffusionXLPipeline
- from diffusers import AutoPipelineForText2Image
- from copy import deepcopy
- from PIL import Image, ImageFilter
- from skimage.metrics import structural_similarity as ssim
- # %%
- storage_path = "..." # path to the monkey storage directory
- base_path = storage_path + "/THINGS_Monkey"
- thing_base_path = storage_path + "/THINGS_img/"
- data_path = "..." # path to the monkey THINGS data
- monkey = "F"
- os.listdir(base_path)
- # %%
- def resolve_reference(hdf5_file, ref):
- """
- Resolve an HDF5 dataset reference and convert it into a string.
- """
- data = hdf5_file[ref][:]
- return ''.join(chr(i) for i in data.flatten() if i > 0)
- things_imgs = File(opj(base_path,f'things_imgs_{monkey}.mat'))
- train_imgs = things_imgs['train_imgs'] # group object --> <HDF5 group "/train_imgs" (3 members)>
- test_imgs = things_imgs['test_imgs']
- train_classes = []
- train_local_paths = []
- train_things_paths = []
- test_classes = []
- test_local_paths = []
- test_things_paths = []
- with File(opj(base_path, f"things_imgs_{monkey}.mat")) as f:
- train_imgs = f['train_imgs']
- train_classes = [resolve_reference(things_imgs, ref[0]) for ref in things_imgs['train_imgs']['class']]
- train_local_paths = [resolve_reference(things_imgs, ref[0]) for ref in things_imgs['train_imgs']['local_path']]
- train_things_paths = [resolve_reference(things_imgs, ref[0]) for ref in things_imgs['train_imgs']['things_path']]
- test_classes = [resolve_reference(things_imgs, ref[0]) for ref in things_imgs['test_imgs']['class']]
- test_local_paths = [resolve_reference(things_imgs, ref[0]) for ref in things_imgs['test_imgs']['local_path']]
- test_things_paths = [resolve_reference(things_imgs, ref[0]) for ref in things_imgs['test_imgs']['things_path']]
- # %%
- trials = File(opj(base_path, f"THINGS_MUA_trials_{monkey}.mat"))
- df =pd.DataFrame(trials["ALLMAT"][:].T, columns=["#trial_idx", "#train_idx", "#test_idx", "#rep", "#count", "#correct"])
- df
- # %%
- data = np.load(data_path + '/monkeys/trials_allmua.npy')
- print(data.shape)
- # %%
- train_indices = df[df["#train_idx"]!=0]["#train_idx"].values.astype(int) - 1
- test_indices = df[df["#test_idx"]!=0]["#test_idx"].values.astype(int) - 1
- sorted_train_img_path = [train_things_paths[i] for i in train_indices]
- sorted_test_img_path = [test_things_paths[i] for i in test_indices]
- # %% [markdown]
- # ## Pipeline for IMG generation
- # %%
- device = "cuda:3" if torch.cuda.is_available() else "cpu"
- print("Device:", device)
- pipeline = AutoPipelineForText2Image.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", torch_dtype=torch.float16).to(device)
- pipeline.load_ip_adapter("h94/IP-Adapter", subfolder="sdxl_models", weight_name="ip-adapter_sdxl.bin")
- pipeline.set_ip_adapter_scale(0.6)
- vae = deepcopy(pipeline.vae).to(device).float()
- # Function to process image embeddings
- def process_image_embeds(batch_images):
- with torch.no_grad(): # Disable gradient computation
- imgs = [Image.open(img).convert("RGB") for img in batch_images]
- # Compute image embeddings on GPU
- image_embeds = pipeline.prepare_ip_adapter_image_embeds(
- ip_adapter_image=[imgs],
- ip_adapter_image_embeds=None,
- device=device,
- num_images_per_prompt=1,
- do_classifier_free_guidance=True,
- )
- return image_embeds[0].permute(0,1,2).cpu()
- # Function to process latents
- def process_latents(batch_images):
- with torch.no_grad(): # Disable gradient computation
- imgs = [Image.open(img).convert("RGB").resize((256,256)) for img in batch_images]
- # Convert images to tensors
- image_tensors = torch.stack([
- torch.from_numpy(
- np.array(img).astype(np.float32) / 255.0 * 2 - 1
- ).permute(2, 0, 1) for img in imgs
- ]).to(device)
- # Compute latents on GPU
- posterior = vae.encode(image_tensors).latent_dist
- latents = posterior.sample()
- # Move results to CPU and store
- return latents.cpu()
- def extract_latents(batch_images):
- with torch.no_grad(): # Disable gradient computation
- # Convert images to tensors
- image_tensors = torch.stack([
- torch.from_numpy(
- np.array(img).astype(np.float32) / 255.0 * 2 - 1
- ).permute(2, 0, 1) for img in batch_images
- ]).to(device)
- # Compute latents on GPU
- posterior = vae.encode(image_tensors).latent_dist
- latents = posterior.sample()
- # Move results to CPU and store
- return latents.cpu()
- # %%
- to_save_files = data_path + "/monkeys/generative/"
- train_images = [opj(thing_base_path,"THINGS","Images", img).replace("\\","/") for img in sorted_train_img_path]
- test_images = [opj(thing_base_path,"THINGS","Images", img).replace("\\","/") for img in sorted_test_img_path]
- batch_size = 128 # Set your desired batch size
- train = False
- if train:
- train_latents = []
- test_latents = []
- # Process images in batches
- for i in tqdm.trange(0, len(train_images), batch_size):
- batch_images = train_images[i : i + batch_size]
- latents = process_latents(batch_images) # fill test_features and test_latents
- torch.cuda.empty_cache()
- train_latents.append(latents)
- train_latents = torch.cat(train_latents,0)
- # Process images in batches
- for i in tqdm.trange(0, len(test_images), batch_size):
- batch_images = test_images[i : i + batch_size]
- latents = process_latents(batch_images) # fill test_features and test_latents
- torch.cuda.empty_cache()
- test_latents.append(latents)
- test_latents = torch.cat(test_latents,0)
- ### Save in things_base_path as pt files
- torch.save(train_latents, opj(to_save_files,f"train_latents_{monkey}.pt"))
- torch.save(test_latents, opj(to_save_files,f"test_latents_{monkey}.pt"))
- print("Latents saved in", to_save_files)
- else:
- train_latents = torch.load(opj(to_save_files,f"train_latents_{monkey}.pt")) # thing_base_path
- test_latents = torch.load(opj(to_save_files,f"test_latents_{monkey}.pt"))
- print("Latents loaded from", to_save_files)
- # %%
- ### Extract features
- if train:
- train_features = []
- test_features = []
- for i in tqdm.trange(0, len(train_images), batch_size):
- batch_images = train_images[i : i + batch_size]
- features = process_image_embeds(batch_images)
- train_features.append(features)
- torch.cuda.empty_cache()
- else:
- train_features = torch.load(opj(to_save_files,f"train_features_{monkey}.pt"))
- test_features = torch.load(opj(to_save_files,f"test_features_{monkey}.pt"))
- print("Features loaded from", to_save_files)
- # %%
- if train:
- for i in tqdm.trange(0, len(test_images), batch_size):
- batch_images = test_images[i : i + batch_size]
- features = process_image_embeds(batch_images) # fill test_features and test_latents
- test_features.append(features)
- torch.cuda.empty_cache()
- if train:
- train_features = torch.cat(train_features,1).permute(1,0,2)
- test_features = torch.cat(test_features,1).permute(1,0,2)
- ## save features and check shapes
- if train:
- torch.save(train_features, opj(to_save_files,f"train_features_{monkey}.pt"))
- torch.save(test_features, opj(to_save_files,f"test_features_{monkey}.pt"))
- print("Features saved in", to_save_files)
- print("Train features shape", train_features.shape)
- print("Test features shape", test_features.shape)
- print("Train latents shape", train_latents.shape)
- print("Test latents shape", test_latents.shape)
- # %% [markdown]
- # ## Neural Data
- # %%
- neural_train_trial_idx = df[df["#train_idx"]!=0]["#trial_idx"].values.astype(int) - 1
- neural_test_trial_idx = df[df["#test_idx"]!=0]["#trial_idx"].values.astype(int) - 1
- train_neural = data[:,neural_train_trial_idx] # prendo tutte le osservazioni --> no data[:,neural_train_trial_idx[:N]]
- test_neural = data[:,neural_test_trial_idx]
- print(train_neural.shape, test_neural.shape)
- # %%
- train_indices = df[df["#train_idx"]!=0]["#train_idx"].values.astype(int) - 1
- test_indices = df[df["#test_idx"]!=0]["#test_idx"].values.astype(int) - 1
- sorted_train_img_path = [train_things_paths[i] for i in train_indices]
- sorted_test_img_path = [test_things_paths[i] for i in test_indices]
- ## Avg activity over test repetitions
- test_neural_avg = []
- test_features_avg = []
- test_latents_avg = []
- selected_test_imgs = []
- for idx in tqdm.tqdm(np.unique(test_indices)):
- test_neural_avg.append(test_neural[:,test_indices==idx].mean(1))
- test_features_avg.append(np.array(test_features)[test_indices==idx].mean(0))
- test_latents_avg.append(np.array(test_latents)[test_indices==idx].mean(0))
- selected_test_imgs.append(np.array(sorted_test_img_path)[test_indices==idx][0])
- test_neural_avg = np.array(test_neural_avg).transpose(1,0,-1)
- test_features_avg = np.array(test_features_avg)
- test_latents_avg = np.array(test_latents_avg)
- selected_test_imgs = np.array(selected_test_imgs)
- print(test_neural_avg.shape, selected_test_imgs.shape, test_features_avg.shape, test_latents_avg.shape)
- # %%
- # test_neural_avg = np.load(data_path + '/monkeys/test_neural_avg.npy')
- # train_neural = np.load(data_path + '/monkeys/train_neural.npy')
- # %%
- train_neural = train_neural[100:] # (200, 15000, 1024)
- test_neural_avg = test_neural_avg[100:] # (200, 100, 1024)
- # %%
- print("Train features shape", train_features.shape)
- print("Test features shape", test_features_avg.shape)
- print("Train latents shape", train_latents.shape)
- print("Test latents shape", test_latents_avg.shape)
- print("Train neural shape", train_neural.shape)
- print("Test neural shape", test_neural_avg.shape)
- # %% [markdown]
- # ## Soft Mapping Model
- # %%
- from torch.utils.data import TensorDataset, DataLoader
- from torch.utils.data import random_split
- from sklearn.preprocessing import StandardScaler
- batch_size = 128
- seed = 42
- device = "cuda:3" if torch.cuda.is_available() else "cpu"
- X_train = train_neural.transpose(1, 0, 2)
- Y_train = train_features[:,-1]
- Y_train_lat = train_latents.reshape(len(train_latents),-1)
- X_test = test_neural_avg.transpose(1, 0, 2) # shape: (22248, 200, 1024)
- Y_test = torch.tensor(test_features_avg[:,-1])
- Y_test_lat = test_latents_avg.reshape(len(test_latents_avg),-1)
- scaler_X = StandardScaler()
- X_reshaped = X_train.reshape(-1, X_train.shape[-1])
- # X_reshaped = X_train.reshape(X_train.shape[0], -1)
- X_scaled = scaler_X.fit_transform(X_reshaped)
- X_train_tensor = torch.tensor(X_scaled.reshape(22248, 200, 1024), dtype=torch.float32, device=device)
- X_test_reshaped = X_test.reshape(-1, X_test.shape[-1])
- X_test_scaled = scaler_X.transform(X_test_reshaped)
- X_test_tensor = torch.tensor(X_test_scaled.reshape(100, 200, 1024), dtype=torch.float32, device=device)
- # %%
- dataset = TensorDataset(X_train_tensor, torch.tensor(Y_train, dtype=torch.float32, device=device))
- test_dataset = TensorDataset(X_test_tensor, torch.tensor(Y_test, dtype=torch.float32, device=device))
- val_size = int(0.2 * len(dataset))
- train_size = len(dataset) - val_size
- generator1 = torch.Generator().manual_seed(seed)
- train_dataset, val_dataset = random_split(dataset, [train_size, val_size], generator=generator1)
- train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
- val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)
- test_loader_feat = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
- # %%
- from pytorch_lightning import Trainer
- from pytorch_lightning.loggers import CSVLogger
- from pytorch_lightning.callbacks import EarlyStopping
- import pytorch_lightning as pl
- import torch
- import torch.nn as nn
- import torch.nn.functional as F
- import geomloss
- class SoftMapping(pl.LightningModule):
- def __init__(self, input_dim=1024, output_dim=1280, lr=1e-4, tau=0.05):
- super().__init__()
- self.mlp = nn.Sequential(
- nn.Linear(input_dim, 768),
- nn.GELU(),
- nn.Dropout(0.5),
- nn.Linear(768, output_dim)
- )
- self.attn_linear = nn.Sequential(
- nn.Linear(input_dim, 1),
- nn.Dropout(0.5)
- )
- self.lin = nn.Sequential(
- nn.Linear(input_dim, output_dim)
- )
- self.loss_mse = nn.MSELoss()
- self.lr = lr
- self.tau = tau
- self.log_tau = nn.Parameter(torch.tensor(np.log(tau), dtype=torch.float32))
- def forward(self, x): # shape: (batch, 200, 1024)
- # attn_weights = torch.softmax(self.attn_linear(x), dim=1)
- attn_weights = torch.sigmoid(self.attn_linear(x))
- attn_out = torch.mean(attn_weights * x, dim=1)
- output = self.mlp(attn_out)
- return output, attn_weights
- def cosine_similarity_matrix(self, A, B):
- A_norm = F.normalize(A, dim=1)
- B_norm = F.normalize(B, dim=1)
- return torch.mm(A_norm, B_norm.T)
- def contrastive_loss_nt(self, S, tau):
- tau = torch.exp(self.log_tau)
- S_exp = torch.exp(S / tau)
- loss = -torch.log(torch.diag(S_exp) / S_exp.sum(dim=1))
- return loss.mean()
- def training_step(self, batch, batch_idx):
- x, y = batch
- # loss = self.loss_fn(self(x), y)
- output, attn_weights = self(x)
- cos_matrix = self.cosine_similarity_matrix(output, y)
- loss = self.contrastive_loss_nt(cos_matrix, self.tau)
- self.log("train_loss", loss, on_epoch=True, prog_bar=True)
- self.log("tau", torch.exp(self.log_tau).item(), prog_bar=True)
- return loss
- def validation_step(self, batch, batch_idx):
- x, y = batch
- # loss = self.loss_cl(self(x), y)
- output, attn_weights = self(x)
- cos_matrix = self.cosine_similarity_matrix(output, y)
- loss = self.contrastive_loss_nt(cos_matrix, self.tau)
- self.log("val_loss", loss, on_epoch=True, prog_bar=True)
- return loss
- def configure_optimizers(self):
- return torch.optim.Adam(self.parameters(), lr=self.lr, weight_decay=1e-4)
- # %%
- from models_nnlinear import SimpleTCN
- from models_linear import MlpAvgTime
- import pytorch_lightning as pl
- from pytorch_lightning.loggers import CSVLogger
- from pytorch_lightning.callbacks import EarlyStopping
- from pytorch_lightning import Trainer
- pl.seed_everything(seed, workers=True)
- model_feat = SoftMapping(input_dim=1024, output_dim=1280, lr=1e-4, tau=0.05)
- logger = CSVLogger("/home/repo/nlinear-monkeys/logs/", name="my_model")
- early_stop_callback = EarlyStopping(monitor="val_loss", min_delta=0.09, patience=10, verbose=True, mode="min")
- trainer = Trainer(max_epochs=50, devices=[1], logger=logger, callbacks=[early_stop_callback])
- trainer.fit(model_feat, train_loader, val_loader)
- # %%
- import pandas as pd
- import matplotlib.pyplot as plt
- log_path = logger.log_dir + "/metrics.csv"
- df = pd.read_csv(log_path)
- val_loss_df = df[~df["val_loss"].isna()]
- train_loss_df = df[~df["train_loss_epoch"].isna()]
- plt.figure(figsize=(8, 5))
- plt.plot(np.array(train_loss_df["epoch"]), np.array(train_loss_df["train_loss_epoch"]), label="Train Loss")
- plt.plot(np.array(val_loss_df["epoch"]), np.array(val_loss_df["val_loss"]), label="Val Loss")
- plt.xlabel("Epoch")
- plt.ylabel("MSE Loss")
- plt.title("Training vs Validation Loss")
- plt.legend()
- plt.grid(True)
- plt.tight_layout()
- plt.show()
- # %%
- x,y = next(iter(test_loader_feat))
- y_pred_feat=[]
- y_true_feat=[]
- attn_weights_feat = []
- model_feat.eval()
- model_feat.to(device)
- with torch.no_grad():
- for x,y in tqdm.tqdm(test_loader_feat):
- y_hat, attn_weights = model_feat(x)
- # y_hat = model_feat(x)
- y_true_feat.append(y)
- y_pred_feat.append(y_hat)
- attn_weights_feat.append(attn_weights.cpu())
- y_pred_feat=torch.cat(y_pred_feat,0)
- y_true_feat=torch.cat(y_true_feat,0)
- attn_weights_feat = torch.cat(attn_weights_feat, dim=0).squeeze(-1)
- # %%
- import seaborn as sns
- sns.heatmap(attn_weights_feat)
- # %%
- from sklearn.neighbors import NearestNeighbors
- n_neighbors = 5
- y_true_np = y_true_feat.cpu().numpy()
- y_pred_np = y_pred_feat.cpu().numpy()
- nbrs = NearestNeighbors(n_neighbors=n_neighbors, metric='cosine').fit(y_true_np)
- distances, top_indices = nbrs.kneighbors(y_pred_np)
- true_indices = torch.arange(len(y_true_np)).cpu().numpy()
- top1_count = (top_indices[:, 0] == true_indices).sum()
- top3_count = sum(true_idx in top_indices[i] for i, true_idx in enumerate(true_indices))
- print(f"Top-1 accuracy: {top1_count}/{len(y_true_np)} ({top1_count / len(y_true_np) * 100:.2f}%)")
- print(f"Top-5 accuracy: {top3_count}/{len(y_true_np)} ({top3_count / len(y_true_np) * 100:.2f}%)")
- # %%
- idx = 0
- fig, axs = plt.subplots(5, n_neighbors+1, figsize=(10, 10))
- for j in range(5):
- axs[j, 0].imshow(Image.open(opj(thing_base_path,"THINGS","Images", test_things_paths[idx+j]).replace("\\","/")))
- axs[j, 0].axis("off")
- axs[j, 0].set_title("Original Image")
- for i, neighbor_idx in enumerate(top_indices[idx+j]):
- img = Image.open(opj(thing_base_path,"THINGS","Images", test_things_paths[neighbor_idx]).replace("\\","/"))
- axs[j, i+1].imshow(img)
- axs[j, i+1].axis("off")
- axs[j, i+1].set_title(f"Neighbor {i+1}")
- plt.tight_layout()
- # %%
- import random
- import matplotlib.pyplot as plt
- from PIL import Image
- from matplotlib.patches import Rectangle
- from os.path import join as opj
- random.seed(34) # 11
- num_rows = 5 # Numero di immagini da mostrare
- random_indices = random.sample(range(len(test_things_paths)), num_rows)
- fig, axs = plt.subplots(num_rows, n_neighbors + 1, figsize=(15, 10))
- for j, idx in enumerate(random_indices):
- original_path = test_things_paths[idx]
- original_full_path = opj(thing_base_path, "THINGS", "Images", original_path).replace("\\", "/")
- axs[j, 0].imshow(Image.open(original_full_path))
- axs[j, 0].axis("off")
- axs[j, 0].set_title("Original Image")
- highlight_original = False # Flag per sapere se devo evidenziare l'originale
- for i, neighbor_idx in enumerate(top_indices[idx]):
- if neighbor_idx >= len(test_things_paths):
- continue
- neighbor_path = test_things_paths[neighbor_idx]
- neighbor_full_path = opj(thing_base_path, "THINGS", "Images", neighbor_path).replace("\\", "/")
- img = Image.open(neighbor_full_path)
- axs[j, i + 1].imshow(img)
- axs[j, i + 1].axis("off")
- axs[j, i + 1].set_title(f"Neighbor {i + 1}")
- if neighbor_path == original_path:
- highlight_original = True
- rect = Rectangle(
- (0, 0), 1, 1,
- transform=axs[j, i + 1].transAxes,
- fill=False, color='lime', linewidth=8
- )
- axs[j, i + 1].add_patch(rect)
- if highlight_original:
- rect = Rectangle(
- (0, 0), 1, 1,
- transform=axs[j, 0].transAxes,
- fill=False, color='lime', linewidth=8
- )
- axs[j, 0].add_patch(rect)
- plt.tight_layout()
- plt.subplots_adjust(wspace=-0.6)
- plt.show()
- # %% [markdown]
- # ## Soft Mapping Latens
- # %%
- dataset = TensorDataset(X_train_tensor, torch.tensor(Y_train_lat, dtype=torch.float32, device=device))
- test_dataset = TensorDataset(X_test_tensor, torch.tensor(Y_test_lat, dtype=torch.float32, device=device))
- val_size = int(0.2 * len(dataset))
- train_size = len(dataset) - val_size
- generator1 = torch.Generator().manual_seed(seed)
- train_dataset, val_dataset = random_split(dataset, [train_size, val_size], generator=generator1)
- train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
- val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)
- test_loader_lat = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
- # %%
- pl.seed_everything(seed, workers=True)
- model_latents = SoftMapping(input_dim=1024, output_dim=4*32*32, lr=1e-4, tau=0.05) # 1e-4 per generativo
- logger = CSVLogger("/home/repo/nlinear-monkeys/logs/", name="my_model")
- early_stop_callback = EarlyStopping(monitor="val_loss", min_delta=0.09, patience=10, verbose=True, mode="min")
- trainer = Trainer(max_epochs=50, devices=[1], logger=logger, callbacks=[early_stop_callback])
- trainer.fit(model_latents, train_loader, val_loader)
- # %%
- import pandas as pd
- import matplotlib.pyplot as plt
- log_path = logger.log_dir + "/metrics.csv"
- df = pd.read_csv(log_path)
- val_loss_df = df[~df["val_loss"].isna()]
- train_loss_df = df[~df["train_loss_epoch"].isna()]
- plt.figure(figsize=(8, 5))
- plt.plot(np.array(train_loss_df["epoch"]), np.array(train_loss_df["train_loss_epoch"]), label="Train Loss")
- plt.plot(np.array(val_loss_df["epoch"]), np.array(val_loss_df["val_loss"]), label="Val Loss")
- plt.xlabel("Epoch")
- plt.ylabel("MSE Loss")
- plt.title("Training vs Validation Loss")
- plt.legend()
- plt.grid(True)
- plt.tight_layout()
- plt.show()
- # %%
- x,y = next(iter(test_loader_lat))
- y_pred_lat=[]
- y_true_lat=[]
- attn_weights_lat = []
- model_latents.eval()
- model_latents.to(device)
- with torch.no_grad():
- for x,y in tqdm.tqdm(test_loader_lat):
- y_hat, attn_weights = model_latents(x)
- # y_hat = model_latents(x)
- y_true_lat.append(y.reshape(len(y),4,32,32))
- y_pred_lat.append(y_hat.reshape(len(y_hat),4,32,32))
- attn_weights_lat.append(attn_weights.cpu())
- y_pred_lat=torch.cat(y_pred_lat,0)
- y_true_lat=torch.cat(y_true_lat,0)
- attn_weights_lat = torch.cat(attn_weights_lat, dim=0).squeeze(-1)
- # %%
- y_pred_lat.shape, y_true_lat.shape, y_pred_lat.device
- # %%
- import seaborn as sns
- sns.heatmap(attn_weights_lat)
- # %% [markdown]
- # ## Structural Decoding
- # %%
- y_pred_lat_train=[]
- model_latents.eval()
- model_latents.to(device)
- with torch.no_grad():
- for x,y in tqdm.tqdm(train_loader):
- y_hat, attn_weights = model_latents(x)
- # y_hat = model_latents(x)
- y_pred_lat_train.append(y_hat.reshape(len(y_hat),4,32,32))
- y_pred_lat_train=torch.cat(y_pred_lat_train,0)
- # %%
- y_pred_lat_train.shape
- # %%
- expected_latents_mean = train_latents.mean(0).to(device)
- expected_latents_std = train_latents.std(0).to(device)
- predicted_latents_mean = y_pred_lat_train.mean(0)
- predicted_latents_std = y_pred_lat_train.std(0)
- # %%
- def adjust_latents(latents, expected_latents_mean=expected_latents_mean, expected_latents_std=expected_latents_std, predicted_latents_mean=predicted_latents_mean, predicted_latents_std=predicted_latents_std):
- return ((latents - predicted_latents_mean) / predicted_latents_std) * expected_latents_std + expected_latents_mean
- test_latents_pred = adjust_latents(y_pred_lat).float()
- # %%
- test_latents_pred.shape, test_latents_pred.mean(), test_latents_pred.std(), expected_latents_mean.mean(), expected_latents_std.mean()
- # %%
- def reconstruct_images(latents):
- with torch.no_grad():
- # Scale the latents (SDXL typically scales latents by a factor)
- latents = latents * vae.scaling_factor
- # Decode latents back to image
- decoded_image = vae.decode(latents / vae.scaling_factor).sample
- # Convert decoded tensor back to PIL Image
- decoded_image = decoded_image.squeeze(0).permute(1, 2, 0)
- decoded_image = (decoded_image.clamp(-1, 1) + 1) / 2
- decoded_image = (decoded_image.cpu().numpy() * 255).astype(np.uint8)
- decoded_pil = Image.fromarray(decoded_image)
- return decoded_pil
- # %%
- idx = 5
- fig, axs = plt.subplots(5, 2, figsize=(10, 10))
- images = []
- latents = test_latents_pred[idx:idx+5].to(device)
- for lat in latents:
- images.append(reconstruct_images(lat.unsqueeze(0)))
- for i, img in enumerate(images):
- axs[i,0].imshow(Image.open(opj(thing_base_path, "THINGS", "Images", test_things_paths[idx+i]).replace("\\", "/")))
- axs[i,0].axis("off")
- axs[i,0].set_title(f"Original {i+1}")
- axs[i,1].imshow(img)
- axs[i,1].axis("off")
- axs[i,1].set_title(f"Reconstructed {i+1}")
- plt.tight_layout()
- # %%
- y_pred_feat_train=[]
- model_feat.eval()
- model_feat.to(device)
- with torch.no_grad():
- for x,y in tqdm.tqdm(train_loader):
- y_hat, attn_weights = model_feat(x)
- # y_hat = model_feat(x)
- y_pred_feat_train.append(y_hat)
- y_pred_feat_train=torch.cat(y_pred_feat_train,0)
- # %%
- y_pred_feat_train.shape
- # %%
- expected_features_mean = train_features[:,-1].mean(0).to(device)
- expected_features_std = train_features[:,-1].std(0).to(device)
- predicted_features_mean = y_pred_feat_train.mean(0)
- predicted_features_std = y_pred_feat_train.std(0)
- # %%
- def adjust_features(features, expected_features_mean=expected_features_mean, expected_features_std=expected_features_std, predicted_features_mean=predicted_features_mean, predicted_features_std=predicted_features_std):
- print(features.shape)
- scaled_pred = ((features - predicted_features_mean) / predicted_features_std) * expected_features_std + expected_features_mean
- zeros = torch.tensor(np.zeros_like(features.cpu()), device=device)
- return torch.stack([zeros,scaled_pred], axis=1)
- # %%
- test_features_pred = adjust_features(y_pred_feat)
- # %%
- test_features_pred.shape
- # %% [markdown]
- # ## Retrieve Structural similar images
- # %%
- # Number of nearest neighbors to find
- n_neighbors = 5
- # Create and fit the NearestNeighbors model
- nn_struct_model = NearestNeighbors(n_neighbors=n_neighbors, metric='cosine')
- nn_struct_model.fit(train_latents.reshape(len(train_latents),-1))
- # Find the nearest neighbors for the adjusted embeddings
- distances_struct, nearest_neighbors_struct_indices = nn_struct_model.kneighbors(test_latents_pred.cpu().reshape(len(test_latents_pred),-1))
- # %%
- ## Show original test image idx, reconstructed image idx from pred latents and 5 closest neighbors in train images
- idx = 30
- fig, axs = plt.subplots(5, n_neighbors+2, figsize=(10, 10))
- for j in range(5):
- axs[j, 0].imshow(Image.open(opj(thing_base_path, "THINGS", "Images", test_things_paths[idx+j]).replace("\\", "/")))
- axs[j, 0].axis("off")
- axs[j, 0].set_title("Original Image")
- axs[j, 1].imshow(reconstruct_images(test_latents_pred[idx+j].unsqueeze(0).to(device)))
- axs[j, 1].axis("off")
- # axs[j, 1].set_title("Reconstructed Image")
- for i, neighbor_idx in enumerate(nearest_neighbors_struct_indices[idx+j]):
- img = Image.open(train_images[neighbor_idx])
- axs[j, i+2].imshow(img)
- axs[j, i+2].axis("off")
- axs[j, i+2].set_title(f"Neighbor {i+1}")
- plt.tight_layout()
- # %% [markdown]
- # ## Reconstruct Images
- # %%
- idx_rec = 77 # 9, 12, 22, 28, 40, 46, 48, 55, 58, 77
- # %%
- test_features_pred.shape, test_features.shape, test_features_pred[idx_rec].half().unsqueeze(0).shape, idx_rec
- # %%
- seed = 55 # 42, 55, 999
- generator = torch.Generator(device=device).manual_seed(seed)
- prepared_latents = pipeline.prepare_latents(batch_size=1,latents=test_latents_pred[idx_rec].unsqueeze(0).half(), num_channels_latents=4, height=32, width=32, dtype =torch.float16,device=device, generator=generator)
- recon = pipeline(
- prompt="",
- ip_adapter_image_embeds=[test_features_pred[idx_rec].half().unsqueeze(-2)],
- negative_prompt="deformed, ugly, wrong proportion, low res, bad anatomy, worst quality, low quality",
- num_inference_steps = 50,
- guide_strength = 0.9,
- guidance_scale = 10,
- num_images_per_prompt = 4,
- generator=generator,
- ).images
- # %%
- init_latents = test_latents_pred[idx_rec].unsqueeze(0).half().to(device)
- low_res = reconstruct_images(init_latents.float().to(device))
- low_res
- # %%
- from skimage.metrics import structural_similarity as ssim
- recon_small = [i.resize((256,256)) for i in recon]
- recon_smooth = [i.filter(ImageFilter.GaussianBlur(6)) for i in recon_small]
- ssim_scores = [ssim(np.array(recon_smooth[i]), np.array(low_res), win_size=3) for i in range(4)]
- recon_sorted = [recon[i] for i in np.argsort(ssim_scores)[::-1]]
- # %%
- fig, axs = plt.subplots(1, 3, figsize=(20, 20)) # 1 riga, 3 colonne
- # Mostra immagine originale
- axs[0].imshow(Image.open(opj(thing_base_path, "THINGS", "Images", test_things_paths[idx_rec]).replace("\\", "/")))
- axs[0].axis("off")
- axs[0].set_title("Original Image", fontsize=25)
- # Mostra immagine a bassa risoluzione
- axs[1].imshow(low_res)
- axs[1].axis("off")
- axs[1].set_title("Low Resolution", fontsize=25)
- # Seleziona solo la ricostruzione con SSIM più alto
- best_recon_idx = sorted(range(len(ssim_scores)), key=lambda i: ssim_scores[i], reverse=True)[0]
- best_recon_img = recon_sorted[best_recon_idx]
- best_ssim_score = ssim_scores[best_recon_idx]
- # Mostra la miglior ricostruzione
- axs[2].imshow(best_recon_img)
- axs[2].axis("off")
- axs[2].set_title(f"Reconstructed, SSIM: {best_ssim_score:.2f}", fontsize=25)
- # Migliora la spaziatura
- plt.tight_layout()
- plt.subplots_adjust(wspace=0.3)
- plt.show()
- # %% [markdown]
- # ## Save Reconstructed
- # %%
- import os
- import torch
- import numpy as np
- from skimage.metrics import structural_similarity as ssim
- from PIL import Image, ImageFilter
- import matplotlib.pyplot as plt
- from os.path import join as opj
- save_recon_dir = data_path + "/monkeys/generative/img_gen_attmlp"
- # os.makedirs(save_true_dir, exist_ok=True)
- os.makedirs(save_recon_dir, exist_ok=True)
- seed = 55
- generator = torch.Generator(device=device).manual_seed(seed)
- for idx_rec in tqdm.tqdm(range(100)):
- # Prepara i latents
- prepared_latents = pipeline.prepare_latents(
- batch_size=1,
- latents=test_latents_pred[idx_rec].unsqueeze(0).half(),
- num_channels_latents=4,
- height=32,
- width=32,
- dtype=torch.float16,
- device=device,
- generator=generator
- )
- # Genera 4 ricostruzioni
- recon = pipeline(
- prompt="",
- ip_adapter_image_embeds=[test_features_pred[idx_rec].half().unsqueeze(-2)],
- negative_prompt="deformed, ugly, wrong proportion, low res, bad anatomy, worst quality, low quality",
- num_inference_steps=50,
- guide_strength=0.9,
- guidance_scale=10,
- num_images_per_prompt=4,
- generator=generator,
- ).images
- # Prepara immagine low-res
- init_latents = test_latents_pred[idx_rec].unsqueeze(0).half()
- low_res = reconstruct_images(init_latents.float().to(device))
- # Prepara recon per SSIM
- recon_small = [i.resize((256, 256)) for i in recon]
- recon_smooth = [i.filter(ImageFilter.GaussianBlur(6)) for i in recon_small]
- # Calcola SSIM tra low_res e ogni ricostruzione
- ssim_scores = [ssim(np.array(recon_smooth[i]), np.array(low_res), win_size=3) for i in range(4)]
- # Seleziona la ricostruzione migliore
- best_recon_idx = np.argmax(ssim_scores)
- best_recon_img = recon[best_recon_idx]
- # Immagine vera
- # true_img = Image.open(opj(thing_base_path, "THINGS", "Images", test_things_paths[idx_rec]).replace("\\", "/"))
- # true_img.save(os.path.join(save_true_dir, f"true_{idx_rec:03d}.png"))
- # Salva immagine ricostruita
- best_recon_img.save(os.path.join(save_recon_dir, f"recon_{idx_rec:03d}.png"))
- print("✅ Tutte le immagini salvate!")
- # %%
- # %%
monkeys_gener.ipynb at commit 8ad7b9a, no license · at the source
Overview
- Department of Biomedicine and Prevention, University of Rome Tor Vergata, Rome, Italy
- Tether Evo, Edificio Centro Corporativo Presidente Plaza, San Salvador, El Salvador
- A.A. Martinos Center for Biomedical Imaging, Harvard Medical School/MGH, Boston, MA, United States
Abstract
Characterizing the information content of intracortical signals during visual processing is a central challenge in systems neuroscience. We address the problem of decoding visual information from high-density intracortical recordings in primates, using the THINGS Ventral Stream Spiking Dataset. We systematically evaluate the effects of model architecture, training objectives, and data scaling on decoding performance. Results show that decoding accuracy is jointly driven by non-linearity and selective temporal aggregation, rather than heavier sequence modelling in this data regime. A simple model combining temporal attention with a shallow MLP achieves up to 70% top-1 image retrieval accuracy, outperforming linear baselines as well as recurrent and convolutional approaches. Scaling analyses reveal predictable diminishing returns with increasing input dimensionality and dataset size. Building on these findings, we design a modular generative decoding pipeline that combines low-resolution latent reconstruction with semantically conditioned diffusion, generating plausible images from 200 ms of brain activity. This framework provides principles for brain-computer interfaces and semantic neural decoding.
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 4 matches between paragraphs and lines of code.
gin.g-node.org/paolo_papale/tvsd
f26bd45f86f5413432e42f30880418be6930ec19, 5 December 2025Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
186 files
- _code/
code_utils_v2/ , MATLAB, 48 linesNPMK/ @KTFigure/ KTFigure.m - _code/
code_utils_v2/ , MATLAB, 79 linesNPMK/ @KTFigureAxis/ KTFigureAxis.m - _code/
code_utils_v2/ , MATLAB, 131 linesNPMK/ @KTNEVComments/ KTNEVComments.m - _code/
code_utils_v2/ , MATLAB, 470 linesNPMK/ @KTNSPOnline/ KTNSPOnline.m - _code/
code_utils_v2/ , MATLAB, 249 linesNPMK/ @KTUEAImpedanceFile/ KTUEAImpedanceFile.m - _code/
code_utils_v2/ , MATLAB, 277 linesNPMK/ @KTUEAMapFile/ KTUEAMapFile.m - _code/
code_utils_v2/ , MATLAB, 85 linesNPMK/ Dependent Functions/ getFile.m - _code/
code_utils_v2/ , MATLAB, 39 linesNPMK/ Dependent Functions/ getFolder.m - _code/
code_utils_v2/ , MATLAB, 31 linesNPMK/ Dependent Functions/ getSettingFileFullPath.m - _code/
code_utils_v2/ , MATLAB, 56 linesNPMK/ Dependent Functions/ parseCommand.m - _code/
code_utils_v2/ , MATLAB, 64 linesNPMK/ Dependent Functions/ syncPatternDetectNEV.m - _code/
code_utils_v2/ , MATLAB, 67 linesNPMK/ Dependent Functions/ syncPatternDetectNSx.m - _code/
code_utils_v2/ , MATLAB, 73 linesNPMK/ Dependent Functions/ syncPatternFinderNSx.m - _code/
code_utils_v2/ , MATLAB, 193 linesNPMK/ LoadingEngines/ MClust/ BlackrockNEVLoadingEngin e.m - _code/
code_utils_v2/ , C, 700 linesNPMK/ LoadingEngines/ nsNEVLIbrary 3.05/ NevLIb-3-05/ ns/ ns.c - _code/
code_utils_v2/ , C/C++, 776 linesNPMK/ LoadingEngines/ nsNEVLIbrary 3.05/ NevLIb-3-05/ ns/ ns.h - _code/
code_utils_v2/ , MATLAB, 91 linesNPMK/ NEV Utilities/ findEventTimes.m - _code/
code_utils_v2/ , MATLAB, 76 linesNPMK/ NEV Utilities/ mergeNEV.m - _code/
code_utils_v2/ , MATLAB, 819 linesNPMK/ NEV Utilities/ openNEVTracking.m - _code/
code_utils_v2/ , MATLAB, 646 linesNPMK/ NEV Utilities/ saveNEV.m - _code/
code_utils_v2/ , MATLAB, 137 linesNPMK/ NEV Utilities/ saveNEVSpikes.m - _code/
code_utils_v2/ , MATLAB, 129 linesNPMK/ NEV Utilities/ saveNEVSubSpikes.m - _code/
code_utils_v2/ , MATLAB, 115 linesNPMK/ NEV Utilities/ sortNEV.m - _code/
code_utils_v2/ , MATLAB, 95 linesNPMK/ NEV Utilities/ splitNEVResets.m - _code/
code_utils_v2/ , MATLAB, 61 linesNPMK/ NPMKverChecker.m - _code/
code_utils_v2/ , MATLAB, 53 linesNPMK/ NSx Utilities/ NSxPowerSpectrum.m - _code/
code_utils_v2/ , MATLAB, 88 linesNPMK/ NSx Utilities/ NSxToHL.m - _code/
code_utils_v2/ , MATLAB, 38 linesNPMK/ NSx Utilities/ calcTimeDelay.m - _code/
code_utils_v2/ , MATLAB, 114 linesNPMK/ NSx Utilities/ combineNSxNEV.m - _code/
code_utils_v2/ , MATLAB, 168 linesNPMK/ NSx Utilities/ findSpikes.m - _code/
code_utils_v2/ , MATLAB, 148 linesNPMK/ NSx Utilities/ matrixToNSx.m - _code/
code_utils_v2/ , MATLAB, 233 linesNPMK/ NSx Utilities/ mergeNSxNEV.m - _code/
code_utils_v2/ , MATLAB, 118 linesNPMK/ NSx Utilities/ openNSxHL.m - _code/
code_utils_v2/ , MATLAB, 115 linesNPMK/ NSx Utilities/ plotAverageWaveforms.m - _code/
code_utils_v2/ , MATLAB, 93 linesNPMK/ NSx Utilities/ removeNSxData.m - _code/
code_utils_v2/ , MATLAB, 150 linesNPMK/ NSx Utilities/ rethresholdNSx.m - _code/
code_utils_v2/ , MATLAB, 223 linesNPMK/ NSx Utilities/ saveChNSx.m - _code/
code_utils_v2/ , MATLAB, 413 linesNPMK/ NSx Utilities/ saveNSx.m - _code/
code_utils_v2/ , MATLAB, 8 linesNPMK/ NSx Utilities/ separatePausedNSx.m - _code/
code_utils_v2/ , MATLAB, 89 linesNPMK/ NSx Utilities/ separatePausedNSx_old.m - _code/
code_utils_v2/ , MATLAB, 129 linesNPMK/ NSx Utilities/ splitNSx.m - _code/
code_utils_v2/ , MATLAB, 133 linesNPMK/ NSx Utilities/ splitNSxPauses.m - _code/
code_utils_v2/ , MATLAB, 28 linesNPMK/ NTrode Utilities/ ntrodeGroups.m - _code/
code_utils_v2/ , MATLAB, 80 linesNPMK/ NTrode Utilities/ saveNEVTetrodes.m - _code/
code_utils_v2/ , MATLAB, 75 linesNPMK/ NTrode Utilities/ splitNEVNTrode.m - _code/
code_utils_v2/ , MATLAB, 139 linesNPMK/ NTrode Utilities/ splitNSxNTrode.m - _code/
code_utils_v2/ , MATLAB, 84 linesNPMK/ Other tools/ edgeDetect.m - _code/
code_utils_v2/ , MATLAB, 21 linesNPMK/ Other tools/ kshuffle.m - _code/
code_utils_v2/ , MATLAB, 16 linesNPMK/ Other tools/ offline2Struct.m - _code/
code_utils_v2/ , MATLAB, 576 linesNPMK/ Other tools/ openCCF.m - _code/
code_utils_v2/ , MATLAB, 148 linesNPMK/ Other tools/ parseCCF.m - _code/
code_utils_v2/ , MATLAB, 37 linesNPMK/ Other tools/ periEventPlot.m - _code/
code_utils_v2/ , MATLAB, 94 linesNPMK/ Other tools/ playSound.m - _code/
code_utils_v2/ , MATLAB, 58 linesNPMK/ Other tools/ settingsManager.m - _code/
code_utils_v2/ , MATLAB, 43 linesNPMK/ installNPMK.m - _code/
code_utils_v2/ , MATLAB, 1,033 linesNPMK/ openNEV.m - _code/
code_utils_v2/ , MATLAB, 1,338 linesNPMK/ openNSx.m - _code/
code_utils_v2/ , MATLAB, 80 linesNPMK/ openNSxSync.m - _code/
code_utils_v2/ , MATLAB, 21 linescheck_log_rec_v2.m - _code/
code_utils_v2/ , MATLAB, 22 linesday_to_blocks_v2.m - _code/
code_utils_v2/ , MATLAB, 15 linesdisplayProgress.m - _code/
code_utils_v2/ , MATLAB, 147 linesdistinguishable_colors.m - _code/
code_utils_v2/ , MATLAB, 154 linesellipse.m - _code/
code_utils_v2/ , MATLAB, 52 linesez_spectrogram.m - _code/
code_utils_v2/ , MATLAB, 218 linesfast_filt/ FiltFiltM.m - _code/
code_utils_v2/ , MATLAB, 255 linesfast_filt/ FilterM.m - _code/
code_utils_v2/ , C, 912 linesfast_filt/ FilterX.c - _code/
code_utils_v2/ , MATLAB, 278 linesfast_filt/ uTest_FiltFiltM.m - _code/
code_utils_v2/ , MATLAB, 391 linesfast_filt/ uTest_FilterM.m - _code/
code_utils_v2/ , MATLAB, 24 linesgammacon.m - _code/
code_utils_v2/ , MATLAB, 9 linesimg_gammacorr.m - _code/
code_utils_v2/ , MATLAB, 155 lineslatencyfit4AM.m - _code/
code_utils_v2/ , MATLAB, 47 linesoutselect.m - _code/
code_utils_v2/ , MATLAB, 77 linesplot_val_arrays.m - _code/
code_utils_v2/ , MATLAB, 12 linesvassarp.m - _code/
collect_MUA_v2.m , MATLAB, 67 lines - _code/
export_MUA.m , MATLAB, 43 lines - _code/
extract_MUA_v2.m , MATLAB, 184 lines - _code/
extract_MUA_v2_N.m , MATLAB, 215 lines - _code/
lucent-things/ , Jupyter, 98 linesNPC_monkey.ipynb - _code/
lucent-things/ , Python, 67 linesextract_data.py - _code/
lucent-things/ , Python, 238 lineshelper.py - _code/
lucent-things/ , Python, 1 linelucent/ .ipynb_checkpoints/ __init__-checkpoint.py - _code/
lucent-things/ , Python, 29 lineslucent/ .ipynb_checkpoints/ util-checkpoint.py - _code/
lucent-things/ , Python, 1 linelucent/ __init__.py - _code/
lucent-things/ , Python, 1 linelucent/ misc/ __init__.py - _code/
lucent-things/ , Python, 125 lineslucent/ misc/ channel_reducer.py - _code/
lucent-things/ , Python, 18 lineslucent/ misc/ io/ __init__.py - _code/
lucent-things/ , Python, 75 lineslucent/ misc/ io/ collapse_channels.py - _code/
lucent-things/ , Python, 162 lineslucent/ misc/ io/ serialize_array.py - _code/
lucent-things/ , Python, 347 lineslucent/ misc/ io/ showing.py - _code/
lucent-things/ , Python, 3 lineslucent/ modelzoo/ .ipynb_checkpoints/ __init__-checkpoint.py - _code/
lucent-things/ , Python, 42 lineslucent/ modelzoo/ .ipynb_checkpoints/ util-checkpoint.py - _code/
lucent-things/ , Python, 3 lineslucent/ modelzoo/ __init__.py - _code/
lucent-things/ , Python, 374 lineslucent/ modelzoo/ inceptionv1/ .ipynb_checkpoints/ InceptionV1-checkpoint.p y - _code/
lucent-things/ , Python, 374 lineslucent/ modelzoo/ inceptionv1/ .ipynb_checkpoints/ InceptionV1_avgPool-chec kpoint.py - _code/
lucent-things/ , Python, 2 lineslucent/ modelzoo/ inceptionv1/ .ipynb_checkpoints/ __init__-checkpoint.py - _code/
lucent-things/ , Python, 118 lineslucent/ modelzoo/ inceptionv1/ .ipynb_checkpoints/ helper_layers-checkpoint .py - _code/
lucent-things/ , Python, 374 lineslucent/ modelzoo/ inceptionv1/ InceptionV1.py - _code/
lucent-things/ , Python, 374 lineslucent/ modelzoo/ inceptionv1/ InceptionV1_avgPool.py - _code/
lucent-things/ , Python, 2 lineslucent/ modelzoo/ inceptionv1/ __init__.py - _code/
lucent-things/ , Python, 118 lineslucent/ modelzoo/ inceptionv1/ helper_layers.py - _code/
lucent-things/ , Python, 42 lineslucent/ modelzoo/ util.py - _code/
lucent-things/ , Python, 1 linelucent/ optvis/ .ipynb_checkpoints/ __init__-checkpoint.py - _code/
lucent-things/ , Python, 369 lineslucent/ optvis/ .ipynb_checkpoints/ objectives-checkpoint.py - _code/
lucent-things/ , Python, 41 lineslucent/ optvis/ .ipynb_checkpoints/ objectives_util-checkpoi nt.py - _code/
lucent-things/ , Python, 206 lineslucent/ optvis/ .ipynb_checkpoints/ render-checkpoint.py - _code/
lucent-things/ , Python, 157 lineslucent/ optvis/ .ipynb_checkpoints/ transform-checkpoint.py - _code/
lucent-things/ , Python, 1 linelucent/ optvis/ __init__.py - _code/
lucent-things/ , Python, 369 lineslucent/ optvis/ objectives.py - _code/
lucent-things/ , Python, 41 lineslucent/ optvis/ objectives_util.py - _code/
lucent-things/ , Python, 7 lineslucent/ optvis/ param/ .ipynb_checkpoints/ __init__-checkpoint.py - _code/
lucent-things/ , Python, 47 lineslucent/ optvis/ param/ .ipynb_checkpoints/ color-checkpoint.py - _code/
lucent-things/ , Python, 71 lineslucent/ optvis/ param/ .ipynb_checkpoints/ cppn-checkpoint.py - _code/
lucent-things/ , Python, 164 lineslucent/ optvis/ param/ .ipynb_checkpoints/ gan-checkpoint.py - _code/
lucent-things/ , Python, 36 lineslucent/ optvis/ param/ .ipynb_checkpoints/ images-checkpoint.py - _code/
lucent-things/ , Python, 65 lineslucent/ optvis/ param/ .ipynb_checkpoints/ lowres-checkpoint.py - _code/
lucent-things/ , Python, 115 lineslucent/ optvis/ param/ .ipynb_checkpoints/ resize_bilinear_nd-check point.py - _code/
lucent-things/ , Python, 64 lineslucent/ optvis/ param/ .ipynb_checkpoints/ spatial-checkpoint.py - _code/
lucent-things/ , Python, 7 lineslucent/ optvis/ param/ __init__.py - _code/
lucent-things/ , Python, 47 lineslucent/ optvis/ param/ color.py - _code/
lucent-things/ , Python, 71 lineslucent/ optvis/ param/ cppn.py - _code/
lucent-things/ , Python, 164 lineslucent/ optvis/ param/ gan.py - _code/
lucent-things/ , Python, 36 lineslucent/ optvis/ param/ images.py - _code/
lucent-things/ , Python, 65 lineslucent/ optvis/ param/ lowres.py - _code/
lucent-things/ , Python, 115 lineslucent/ optvis/ param/ resize_bilinear_nd.py - _code/
lucent-things/ , Python, 64 lineslucent/ optvis/ param/ spatial.py - _code/
lucent-things/ , Python, 206 lineslucent/ optvis/ render.py - _code/
lucent-things/ , Python, 157 lineslucent/ optvis/ transform.py - _code/
lucent-things/ , Python, 29 lineslucent/ util.py - _code/
lucent-things/ , Python, 54 linesneural_model.py - _code/
lucent-things/ , Python, 627 linesnpc.py - _code/
lucent-things/ , Python, 64 linessetup.py - _code/
lucent-things/ , Python, 384 linestrain_neural_model.py - _code/
norm_MUA.m , MATLAB, 142 lines - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240109_ B1_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240109_ B2_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240109_ B3_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240109_ B4_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240112_ B1_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240112_ B2_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240112_ B3_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240112_ B4_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240115_ B1_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240115_ B2_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240115_ B3_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240115_ B4_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240115_ B5_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240116_ B1_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240116_ B2_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240116_ B3_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240116_ B4_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240116_ B5_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240118_ B1_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240118_ B2_runstim.m - monkeyF/
_logs/ , MATLAB, 367 linesTHINGS_monkeyF_20240118_ B3_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220111_ B10_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220111_ B1_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220111_ B2_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220111_ B3_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220111_ B4_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220111_ B5_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220111_ B6_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220111_ B7_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220111_ B8_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220111_ B9_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220112_ B10_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220112_ B11_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220112_ B1_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220112_ B2_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220112_ B3_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220112_ B4_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220112_ B5_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220112_ B6_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220112_ B7_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220112_ B8_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220112_ B9_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220113_ B1_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220113_ B2_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220113_ B3_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220113_ B4_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220114_ B1_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220114_ B2_runstim.m - monkeyN/
_logs/ , MATLAB, 367 linesTHINGS_monkeyN_20220114_ B3_runstim.m - LICENSE, License, 317 lines
- README.md, Text, 142 lines
fidelioc55/primates-mua-decode
8ad7b9a2a00f5a7a61b84599fde1eaafcd532e4a, 24 September 2025Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
6 files
- metrics.ipynb, Jupyter, 608 lines
- models_linear.py, Python, 117 lines
- models_nnlinear.py, Python, 274 lines
- monkeys_gener.ipynb, Jupyter, 922 lines, 4 matches
- monkeys_retrieve.ipynb, Jupyter, 1,264 lines
- README.md, Text, 62 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;
- 189 scripts, each with its path and the digest of its content;
- 4 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 and Code Availability
All data are from a publicly available dataset (THINGS Ventral Stream Spiking Dataset) 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, 27 September 2026: the first record
Recorded: type, language, journal, volume, pages, dates, 3 authors, 6 keywords, 34 references.
Cite
This paper
Ciferri, M., Ferrante, M., & Toschi, N. (2026). A modular semantic-structural pipeline for visual decoding from primate spiking data via selective temporal integration. Imaging neuroscience (Cambridge, Mass.), 4, IMAG.a.1299. https://
BibTeX
@article{ciferri2026modu
author = {Ciferri, Matteo and Ferrante, Matteo and Toschi, Nicola},
title = {{A modular semantic-structural pipeline for visual decoding from primate spiking data via selective temporal integration}},
journal = {Imaging neuroscience (Cambridge, Mass.)},
year = {2026},
month = jul,
volume = {4},
pages = {IMAG.a.1299},
publisher = {MIT Press},
issn = {2837-6056},
doi = {10.1162/
url = {https://
pmid = {42453642},
pmcid = {PMC13366611}
}
RIS
TY - JOUR
AU - Ciferri, Matteo
AU - Ferrante, Matteo
AU - Toschi, Nicola
TI - A modular semantic-structural pipeline for visual decoding from primate spiking data via selective temporal integration
T2 - Imaging neuroscience (Cambridge, Mass.)
J2 - Imaging Neurosci (Camb)
PY - 2026
DA - 2026/
VL - 4
SP - IMAG.a.1299
SN - 2837-6056
PB - MIT Press
DO - 10.1162/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1162/
"type": "article-journal",
"title": "A modular semantic-structural pipeline for visual decoding from primate spiking data via selective temporal integration",
"container-title": "Imaging neuroscience (Cambridge, Mass.)",
"author": [
{
"family": "Ciferri",
"given": "Matteo"
},
{
"family": "Ferrante",
"given": "Matteo"
},
{
"family": "Toschi",
"given": "Nicola"
}
],
"container-title-short":
"volume": "4",
"page": "IMAG.a.1299",
"DOI": "10.1162/
"PMID": "42453642",
"PMCID": "PMC13366611",
"ISSN": "2837-6056",
"publisher": "MIT Press",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
7,
13
]
]
}
}
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.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, 7 other tools, methods / tools, 8 references
- [2] doi:10.1371/journal.pcbi.1014263 [code]
- MIRAGE: Robust multi-modal architectures translate fMRI-to-image models from vision to mental imagery.Journal: PLoS computational biologyIn common: PyTorch Lightning, Hugging Face Transformers, scikit-image, 8 other tools, 6 references
- [3] doi:10.7554/elife.107933 [code]
- Modality-agnostic decoding of vision and language from fMRI.Journal: eLifeIn common: Hugging Face Transformers, scikit-image, h5py, 8 other tools, 5 references
- [4] 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, 4 references
- [5] 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, scikit-image, h5py, 7 other tools, 3 references
- [6] 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, scikit-image, 10 other tools
- [7] doi:10.1038/s41597-026-07248-6 [code]
- A large-scale fMRI dataset for vision-language semantic association.Journal: Scientific dataIn common: scikit-image, h5py, Pillow, 6 other tools, methods / tools, 4 references
- [8] doi:10.1038/s41467-026-76098-y [code]
- A single computational objective can produce specialization of streams in visual cortex.Journal: Nature communicationsIn common: Hugging Face Transformers, scikit-image, h5py, 9 other tools, 1 reference
- [9] doi:10.1038/s41593-026-02285-1 [code]
- Fixation duration on natural scenes is explained by memory encoding not processing demand.Journal: Nature neuroscienceIn common: scikit-image, h5py, Pillow, 7 other tools, 3 references
- [10] doi:10.1038/s41593-026-02232-0 [code]
- Entorhinal cortex represents task-relevant remote locations independently of CA1.Journal: Nature neuroscienceIn common: scikit-image, h5py, Image Processing Toolbox, 10 other tools
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, 189 scripts, and 4 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:65727181267dec10…
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.
