OSCR

A modular semantic-structural pipeline for visual decoding from primate spiking data via selective temporal integration.

Code ↔ Paper

4 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 4 matches
  1. [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. [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. [3] § Material and Methods › Task 1: Stimulus retrieval ↔ monkeys_gener.ipynb, lines 751–773 · score 0.53 · closest neighbor, nearest neighbor, predicted
  4. [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

  1. # %%
  2. import numpy as np
  3. from scipy.io import loadmat
  4. import os
  5. from os.path import join as opj
  6. from h5py import File
  7. import pandas as pd
  8. # load CLIP from huggingface, load the first N images and extract the features
  9. from transformers import CLIPProcessor, CLIPModel
  10. import torch
  11. import tqdm
  12. from PIL import Image
  13. from diffusers import StableDiffusionXLPipeline
  14. from diffusers import AutoPipelineForText2Image
  15. from copy import deepcopy
  16. from PIL import Image, ImageFilter
  17. from skimage.metrics import structural_similarity as ssim
  18. # %%
  19. storage_path = "..." # path to the monkey storage directory
  20. base_path = storage_path + "/THINGS_Monkey"
  21. thing_base_path = storage_path + "/THINGS_img/"
  22. data_path = "..." # path to the monkey THINGS data
  23. monkey = "F"
  24. os.listdir(base_path)
  25. # %%
  26. def resolve_reference(hdf5_file, ref):
  27. """
  28. Resolve an HDF5 dataset reference and convert it into a string.
  29. """
  30. data = hdf5_file[ref][:]
  31. return ''.join(chr(i) for i in data.flatten() if i > 0)
  32. things_imgs = File(opj(base_path,f'things_imgs_{monkey}.mat'))
  33. train_imgs = things_imgs['train_imgs'] # group object --> <HDF5 group "/train_imgs" (3 members)>
  34. test_imgs = things_imgs['test_imgs']
  35. train_classes = []
  36. train_local_paths = []
  37. train_things_paths = []
  38. test_classes = []
  39. test_local_paths = []
  40. test_things_paths = []
  41. with File(opj(base_path, f"things_imgs_{monkey}.mat")) as f:
  42. train_imgs = f['train_imgs']
  43. train_classes = [resolve_reference(things_imgs, ref[0]) for ref in things_imgs['train_imgs']['class']]
  44. train_local_paths = [resolve_reference(things_imgs, ref[0]) for ref in things_imgs['train_imgs']['local_path']]
  45. train_things_paths = [resolve_reference(things_imgs, ref[0]) for ref in things_imgs['train_imgs']['things_path']]
  46. test_classes = [resolve_reference(things_imgs, ref[0]) for ref in things_imgs['test_imgs']['class']]
  47. test_local_paths = [resolve_reference(things_imgs, ref[0]) for ref in things_imgs['test_imgs']['local_path']]
  48. test_things_paths = [resolve_reference(things_imgs, ref[0]) for ref in things_imgs['test_imgs']['things_path']]
  49. # %%
  50. trials = File(opj(base_path, f"THINGS_MUA_trials_{monkey}.mat"))
  51. df =pd.DataFrame(trials["ALLMAT"][:].T, columns=["#trial_idx", "#train_idx", "#test_idx", "#rep", "#count", "#correct"])
  52. df
  53. # %%
  54. data = np.load(data_path + '/monkeys/trials_allmua.npy')
  55. print(data.shape)
  56. # %%
  57. train_indices = df[df["#train_idx"]!=0]["#train_idx"].values.astype(int) - 1
  58. test_indices = df[df["#test_idx"]!=0]["#test_idx"].values.astype(int) - 1
  59. sorted_train_img_path = [train_things_paths[i] for i in train_indices]
  60. sorted_test_img_path = [test_things_paths[i] for i in test_indices]
  61. # %% [markdown]
  62. # ## Pipeline for IMG generation
  63. # %%
  64. device = "cuda:3" if torch.cuda.is_available() else "cpu"
  65. print("Device:", device)
  66. pipeline = AutoPipelineForText2Image.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", torch_dtype=torch.float16).to(device)
  67. pipeline.load_ip_adapter("h94/IP-Adapter", subfolder="sdxl_models", weight_name="ip-adapter_sdxl.bin")
  68. pipeline.set_ip_adapter_scale(0.6)
  69. vae = deepcopy(pipeline.vae).to(device).float()
  70. # Function to process image embeddings
  71. def process_image_embeds(batch_images):
  72. with torch.no_grad(): # Disable gradient computation
  73. imgs = [Image.open(img).convert("RGB") for img in batch_images]
  74. # Compute image embeddings on GPU
  75. image_embeds = pipeline.prepare_ip_adapter_image_embeds(
  76. ip_adapter_image=[imgs],
  77. ip_adapter_image_embeds=None,
  78. device=device,
  79. num_images_per_prompt=1,
  80. do_classifier_free_guidance=True,
  81. )
  82. return image_embeds[0].permute(0,1,2).cpu()
  83. # Function to process latents
  84. def process_latents(batch_images):
  85. with torch.no_grad(): # Disable gradient computation
  86. imgs = [Image.open(img).convert("RGB").resize((256,256)) for img in batch_images]
  87. # Convert images to tensors
  88. image_tensors = torch.stack([
  89. torch.from_numpy(
  90. np.array(img).astype(np.float32) / 255.0 * 2 - 1
  91. ).permute(2, 0, 1) for img in imgs
  92. ]).to(device)
  93. # Compute latents on GPU
  94. posterior = vae.encode(image_tensors).latent_dist
  95. latents = posterior.sample()
  96. # Move results to CPU and store
  97. return latents.cpu()
  98. def extract_latents(batch_images):
  99. with torch.no_grad(): # Disable gradient computation
  100. # Convert images to tensors
  101. image_tensors = torch.stack([
  102. torch.from_numpy(
  103. np.array(img).astype(np.float32) / 255.0 * 2 - 1
  104. ).permute(2, 0, 1) for img in batch_images
  105. ]).to(device)
  106. # Compute latents on GPU
  107. posterior = vae.encode(image_tensors).latent_dist
  108. latents = posterior.sample()
  109. # Move results to CPU and store
  110. return latents.cpu()
  111. # %%
  112. to_save_files = data_path + "/monkeys/generative/"
  113. train_images = [opj(thing_base_path,"THINGS","Images", img).replace("\\","/") for img in sorted_train_img_path]
  114. test_images = [opj(thing_base_path,"THINGS","Images", img).replace("\\","/") for img in sorted_test_img_path]
  115. batch_size = 128 # Set your desired batch size
  116. train = False
  117. if train:
  118. train_latents = []
  119. test_latents = []
  120. # Process images in batches
  121. for i in tqdm.trange(0, len(train_images), batch_size):
  122. batch_images = train_images[i : i + batch_size]
  123. latents = process_latents(batch_images) # fill test_features and test_latents
  124. torch.cuda.empty_cache()
  125. train_latents.append(latents)
  126. train_latents = torch.cat(train_latents,0)
  127. # Process images in batches
  128. for i in tqdm.trange(0, len(test_images), batch_size):
  129. batch_images = test_images[i : i + batch_size]
  130. latents = process_latents(batch_images) # fill test_features and test_latents
  131. torch.cuda.empty_cache()
  132. test_latents.append(latents)
  133. test_latents = torch.cat(test_latents,0)
  134. ### Save in things_base_path as pt files
  135. torch.save(train_latents, opj(to_save_files,f"train_latents_{monkey}.pt"))
  136. torch.save(test_latents, opj(to_save_files,f"test_latents_{monkey}.pt"))
  137. print("Latents saved in", to_save_files)
  138. else:
  139. train_latents = torch.load(opj(to_save_files,f"train_latents_{monkey}.pt")) # thing_base_path
  140. test_latents = torch.load(opj(to_save_files,f"test_latents_{monkey}.pt"))
  141. print("Latents loaded from", to_save_files)
  142. # %%
  143. ### Extract features
  144. if train:
  145. train_features = []
  146. test_features = []
  147. for i in tqdm.trange(0, len(train_images), batch_size):
  148. batch_images = train_images[i : i + batch_size]
  149. features = process_image_embeds(batch_images)
  150. train_features.append(features)
  151. torch.cuda.empty_cache()
  152. else:
  153. train_features = torch.load(opj(to_save_files,f"train_features_{monkey}.pt"))
  154. test_features = torch.load(opj(to_save_files,f"test_features_{monkey}.pt"))
  155. print("Features loaded from", to_save_files)
  156. # %%
  157. if train:
  158. for i in tqdm.trange(0, len(test_images), batch_size):
  159. batch_images = test_images[i : i + batch_size]
  160. features = process_image_embeds(batch_images) # fill test_features and test_latents
  161. test_features.append(features)
  162. torch.cuda.empty_cache()
  163. if train:
  164. train_features = torch.cat(train_features,1).permute(1,0,2)
  165. test_features = torch.cat(test_features,1).permute(1,0,2)
  166. ## save features and check shapes
  167. if train:
  168. torch.save(train_features, opj(to_save_files,f"train_features_{monkey}.pt"))
  169. torch.save(test_features, opj(to_save_files,f"test_features_{monkey}.pt"))
  170. print("Features saved in", to_save_files)
  171. print("Train features shape", train_features.shape)
  172. print("Test features shape", test_features.shape)
  173. print("Train latents shape", train_latents.shape)
  174. print("Test latents shape", test_latents.shape)
  175. # %% [markdown]
  176. # ## Neural Data
  177. # %%
  178. neural_train_trial_idx = df[df["#train_idx"]!=0]["#trial_idx"].values.astype(int) - 1
  179. neural_test_trial_idx = df[df["#test_idx"]!=0]["#trial_idx"].values.astype(int) - 1
  180. train_neural = data[:,neural_train_trial_idx] # prendo tutte le osservazioni --> no data[:,neural_train_trial_idx[:N]]
  181. test_neural = data[:,neural_test_trial_idx]
  182. print(train_neural.shape, test_neural.shape)
  183. # %%
  184. train_indices = df[df["#train_idx"]!=0]["#train_idx"].values.astype(int) - 1
  185. test_indices = df[df["#test_idx"]!=0]["#test_idx"].values.astype(int) - 1
  186. sorted_train_img_path = [train_things_paths[i] for i in train_indices]
  187. sorted_test_img_path = [test_things_paths[i] for i in test_indices]
  188. ## Avg activity over test repetitions
  189. test_neural_avg = []
  190. test_features_avg = []
  191. test_latents_avg = []
  192. selected_test_imgs = []
  193. for idx in tqdm.tqdm(np.unique(test_indices)):
  194. test_neural_avg.append(test_neural[:,test_indices==idx].mean(1))
  195. test_features_avg.append(np.array(test_features)[test_indices==idx].mean(0))
  196. test_latents_avg.append(np.array(test_latents)[test_indices==idx].mean(0))
  197. selected_test_imgs.append(np.array(sorted_test_img_path)[test_indices==idx][0])
  198. test_neural_avg = np.array(test_neural_avg).transpose(1,0,-1)
  199. test_features_avg = np.array(test_features_avg)
  200. test_latents_avg = np.array(test_latents_avg)
  201. selected_test_imgs = np.array(selected_test_imgs)
  202. print(test_neural_avg.shape, selected_test_imgs.shape, test_features_avg.shape, test_latents_avg.shape)
  203. # %%
  204. # test_neural_avg = np.load(data_path + '/monkeys/test_neural_avg.npy')
  205. # train_neural = np.load(data_path + '/monkeys/train_neural.npy')
  206. # %%
  207. train_neural = train_neural[100:] # (200, 15000, 1024)
  208. test_neural_avg = test_neural_avg[100:] # (200, 100, 1024)
  209. # %%
  210. print("Train features shape", train_features.shape)
  211. print("Test features shape", test_features_avg.shape)
  212. print("Train latents shape", train_latents.shape)
  213. print("Test latents shape", test_latents_avg.shape)
  214. print("Train neural shape", train_neural.shape)
  215. print("Test neural shape", test_neural_avg.shape)
  216. # %% [markdown]
  217. # ## Soft Mapping Model
  218. # %%
  219. from torch.utils.data import TensorDataset, DataLoader
  220. from torch.utils.data import random_split
  221. from sklearn.preprocessing import StandardScaler
  222. batch_size = 128
  223. seed = 42
  224. device = "cuda:3" if torch.cuda.is_available() else "cpu"
  225. X_train = train_neural.transpose(1, 0, 2)
  226. Y_train = train_features[:,-1]
  227. Y_train_lat = train_latents.reshape(len(train_latents),-1)
  228. X_test = test_neural_avg.transpose(1, 0, 2) # shape: (22248, 200, 1024)
  229. Y_test = torch.tensor(test_features_avg[:,-1])
  230. Y_test_lat = test_latents_avg.reshape(len(test_latents_avg),-1)
  231. scaler_X = StandardScaler()
  232. X_reshaped = X_train.reshape(-1, X_train.shape[-1])
  233. # X_reshaped = X_train.reshape(X_train.shape[0], -1)
  234. X_scaled = scaler_X.fit_transform(X_reshaped)
  235. X_train_tensor = torch.tensor(X_scaled.reshape(22248, 200, 1024), dtype=torch.float32, device=device)
  236. X_test_reshaped = X_test.reshape(-1, X_test.shape[-1])
  237. X_test_scaled = scaler_X.transform(X_test_reshaped)
  238. X_test_tensor = torch.tensor(X_test_scaled.reshape(100, 200, 1024), dtype=torch.float32, device=device)
  239. # %%
  240. dataset = TensorDataset(X_train_tensor, torch.tensor(Y_train, dtype=torch.float32, device=device))
  241. test_dataset = TensorDataset(X_test_tensor, torch.tensor(Y_test, dtype=torch.float32, device=device))
  242. val_size = int(0.2 * len(dataset))
  243. train_size = len(dataset) - val_size
  244. generator1 = torch.Generator().manual_seed(seed)
  245. train_dataset, val_dataset = random_split(dataset, [train_size, val_size], generator=generator1)
  246. train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
  247. val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)
  248. test_loader_feat = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
  249. # %%
  250. from pytorch_lightning import Trainer
  251. from pytorch_lightning.loggers import CSVLogger
  252. from pytorch_lightning.callbacks import EarlyStopping
  253. import pytorch_lightning as pl
  254. import torch
  255. import torch.nn as nn
  256. import torch.nn.functional as F
  257. import geomloss
  258. class SoftMapping(pl.LightningModule):
  259. def __init__(self, input_dim=1024, output_dim=1280, lr=1e-4, tau=0.05):
  260. super().__init__()
  261. self.mlp = nn.Sequential(
  262. nn.Linear(input_dim, 768),
  263. nn.GELU(),
  264. nn.Dropout(0.5),
  265. nn.Linear(768, output_dim)
  266. )
  267. self.attn_linear = nn.Sequential(
  268. nn.Linear(input_dim, 1),
  269. nn.Dropout(0.5)
  270. )
  271. self.lin = nn.Sequential(
  272. nn.Linear(input_dim, output_dim)
  273. )
  274. self.loss_mse = nn.MSELoss()
  275. self.lr = lr
  276. self.tau = tau
  277. self.log_tau = nn.Parameter(torch.tensor(np.log(tau), dtype=torch.float32))
  278. def forward(self, x): # shape: (batch, 200, 1024)
  279. # attn_weights = torch.softmax(self.attn_linear(x), dim=1)
  280. attn_weights = torch.sigmoid(self.attn_linear(x))
  281. attn_out = torch.mean(attn_weights * x, dim=1)
  282. output = self.mlp(attn_out)
  283. return output, attn_weights
  284. def cosine_similarity_matrix(self, A, B):
  285. A_norm = F.normalize(A, dim=1)
  286. B_norm = F.normalize(B, dim=1)
  287. return torch.mm(A_norm, B_norm.T)
  288. def contrastive_loss_nt(self, S, tau):
  289. tau = torch.exp(self.log_tau)
  290. S_exp = torch.exp(S / tau)
  291. loss = -torch.log(torch.diag(S_exp) / S_exp.sum(dim=1))
  292. return loss.mean()
  293. def training_step(self, batch, batch_idx):
  294. x, y = batch
  295. # loss = self.loss_fn(self(x), y)
  296. output, attn_weights = self(x)
  297. cos_matrix = self.cosine_similarity_matrix(output, y)
  298. loss = self.contrastive_loss_nt(cos_matrix, self.tau)
  299. self.log("train_loss", loss, on_epoch=True, prog_bar=True)
  300. self.log("tau", torch.exp(self.log_tau).item(), prog_bar=True)
  301. return loss
  302. def validation_step(self, batch, batch_idx):
  303. x, y = batch
  304. # loss = self.loss_cl(self(x), y)
  305. output, attn_weights = self(x)
  306. cos_matrix = self.cosine_similarity_matrix(output, y)
  307. loss = self.contrastive_loss_nt(cos_matrix, self.tau)
  308. self.log("val_loss", loss, on_epoch=True, prog_bar=True)
  309. return loss
  310. def configure_optimizers(self):
  311. return torch.optim.Adam(self.parameters(), lr=self.lr, weight_decay=1e-4)
  312. # %%
  313. from models_nnlinear import SimpleTCN
  314. from models_linear import MlpAvgTime
  315. import pytorch_lightning as pl
  316. from pytorch_lightning.loggers import CSVLogger
  317. from pytorch_lightning.callbacks import EarlyStopping
  318. from pytorch_lightning import Trainer
  319. pl.seed_everything(seed, workers=True)
  320. model_feat = SoftMapping(input_dim=1024, output_dim=1280, lr=1e-4, tau=0.05)
  321. logger = CSVLogger("/home/repo/nlinear-monkeys/logs/", name="my_model")
  322. early_stop_callback = EarlyStopping(monitor="val_loss", min_delta=0.09, patience=10, verbose=True, mode="min")
  323. trainer = Trainer(max_epochs=50, devices=[1], logger=logger, callbacks=[early_stop_callback])
  324. trainer.fit(model_feat, train_loader, val_loader)
  325. # %%
  326. import pandas as pd
  327. import matplotlib.pyplot as plt
  328. log_path = logger.log_dir + "/metrics.csv"
  329. df = pd.read_csv(log_path)
  330. val_loss_df = df[~df["val_loss"].isna()]
  331. train_loss_df = df[~df["train_loss_epoch"].isna()]
  332. plt.figure(figsize=(8, 5))
  333. plt.plot(np.array(train_loss_df["epoch"]), np.array(train_loss_df["train_loss_epoch"]), label="Train Loss")
  334. plt.plot(np.array(val_loss_df["epoch"]), np.array(val_loss_df["val_loss"]), label="Val Loss")
  335. plt.xlabel("Epoch")
  336. plt.ylabel("MSE Loss")
  337. plt.title("Training vs Validation Loss")
  338. plt.legend()
  339. plt.grid(True)
  340. plt.tight_layout()
  341. plt.show()
  342. # %%
  343. x,y = next(iter(test_loader_feat))
  344. y_pred_feat=[]
  345. y_true_feat=[]
  346. attn_weights_feat = []
  347. model_feat.eval()
  348. model_feat.to(device)
  349. with torch.no_grad():
  350. for x,y in tqdm.tqdm(test_loader_feat):
  351. y_hat, attn_weights = model_feat(x)
  352. # y_hat = model_feat(x)
  353. y_true_feat.append(y)
  354. y_pred_feat.append(y_hat)
  355. attn_weights_feat.append(attn_weights.cpu())
  356. y_pred_feat=torch.cat(y_pred_feat,0)
  357. y_true_feat=torch.cat(y_true_feat,0)
  358. attn_weights_feat = torch.cat(attn_weights_feat, dim=0).squeeze(-1)
  359. # %%
  360. import seaborn as sns
  361. sns.heatmap(attn_weights_feat)
  362. # %%
  363. from sklearn.neighbors import NearestNeighbors
  364. n_neighbors = 5
  365. y_true_np = y_true_feat.cpu().numpy()
  366. y_pred_np = y_pred_feat.cpu().numpy()
  367. nbrs = NearestNeighbors(n_neighbors=n_neighbors, metric='cosine').fit(y_true_np)
  368. distances, top_indices = nbrs.kneighbors(y_pred_np)
  369. true_indices = torch.arange(len(y_true_np)).cpu().numpy()
  370. top1_count = (top_indices[:, 0] == true_indices).sum()
  371. top3_count = sum(true_idx in top_indices[i] for i, true_idx in enumerate(true_indices))
  372. print(f"Top-1 accuracy: {top1_count}/{len(y_true_np)} ({top1_count / len(y_true_np) * 100:.2f}%)")
  373. print(f"Top-5 accuracy: {top3_count}/{len(y_true_np)} ({top3_count / len(y_true_np) * 100:.2f}%)")
  374. # %%
  375. idx = 0
  376. fig, axs = plt.subplots(5, n_neighbors+1, figsize=(10, 10))
  377. for j in range(5):
  378. axs[j, 0].imshow(Image.open(opj(thing_base_path,"THINGS","Images", test_things_paths[idx+j]).replace("\\","/")))
  379. axs[j, 0].axis("off")
  380. axs[j, 0].set_title("Original Image")
  381. for i, neighbor_idx in enumerate(top_indices[idx+j]):
  382. img = Image.open(opj(thing_base_path,"THINGS","Images", test_things_paths[neighbor_idx]).replace("\\","/"))
  383. axs[j, i+1].imshow(img)
  384. axs[j, i+1].axis("off")
  385. axs[j, i+1].set_title(f"Neighbor {i+1}")
  386. plt.tight_layout()
  387. # %%
  388. import random
  389. import matplotlib.pyplot as plt
  390. from PIL import Image
  391. from matplotlib.patches import Rectangle
  392. from os.path import join as opj
  393. random.seed(34) # 11
  394. num_rows = 5 # Numero di immagini da mostrare
  395. random_indices = random.sample(range(len(test_things_paths)), num_rows)
  396. fig, axs = plt.subplots(num_rows, n_neighbors + 1, figsize=(15, 10))
  397. for j, idx in enumerate(random_indices):
  398. original_path = test_things_paths[idx]
  399. original_full_path = opj(thing_base_path, "THINGS", "Images", original_path).replace("\\", "/")
  400. axs[j, 0].imshow(Image.open(original_full_path))
  401. axs[j, 0].axis("off")
  402. axs[j, 0].set_title("Original Image")
  403. highlight_original = False # Flag per sapere se devo evidenziare l'originale
  404. for i, neighbor_idx in enumerate(top_indices[idx]):
  405. if neighbor_idx >= len(test_things_paths):
  406. continue
  407. neighbor_path = test_things_paths[neighbor_idx]
  408. neighbor_full_path = opj(thing_base_path, "THINGS", "Images", neighbor_path).replace("\\", "/")
  409. img = Image.open(neighbor_full_path)
  410. axs[j, i + 1].imshow(img)
  411. axs[j, i + 1].axis("off")
  412. axs[j, i + 1].set_title(f"Neighbor {i + 1}")
  413. if neighbor_path == original_path:
  414. highlight_original = True
  415. rect = Rectangle(
  416. (0, 0), 1, 1,
  417. transform=axs[j, i + 1].transAxes,
  418. fill=False, color='lime', linewidth=8
  419. )
  420. axs[j, i + 1].add_patch(rect)
  421. if highlight_original:
  422. rect = Rectangle(
  423. (0, 0), 1, 1,
  424. transform=axs[j, 0].transAxes,
  425. fill=False, color='lime', linewidth=8
  426. )
  427. axs[j, 0].add_patch(rect)
  428. plt.tight_layout()
  429. plt.subplots_adjust(wspace=-0.6)
  430. plt.show()
  431. # %% [markdown]
  432. # ## Soft Mapping Latens
  433. # %%
  434. dataset = TensorDataset(X_train_tensor, torch.tensor(Y_train_lat, dtype=torch.float32, device=device))
  435. test_dataset = TensorDataset(X_test_tensor, torch.tensor(Y_test_lat, dtype=torch.float32, device=device))
  436. val_size = int(0.2 * len(dataset))
  437. train_size = len(dataset) - val_size
  438. generator1 = torch.Generator().manual_seed(seed)
  439. train_dataset, val_dataset = random_split(dataset, [train_size, val_size], generator=generator1)
  440. train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
  441. val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)
  442. test_loader_lat = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
  443. # %%
  444. pl.seed_everything(seed, workers=True)
  445. model_latents = SoftMapping(input_dim=1024, output_dim=4*32*32, lr=1e-4, tau=0.05) # 1e-4 per generativo
  446. logger = CSVLogger("/home/repo/nlinear-monkeys/logs/", name="my_model")
  447. early_stop_callback = EarlyStopping(monitor="val_loss", min_delta=0.09, patience=10, verbose=True, mode="min")
  448. trainer = Trainer(max_epochs=50, devices=[1], logger=logger, callbacks=[early_stop_callback])
  449. trainer.fit(model_latents, train_loader, val_loader)
  450. # %%
  451. import pandas as pd
  452. import matplotlib.pyplot as plt
  453. log_path = logger.log_dir + "/metrics.csv"
  454. df = pd.read_csv(log_path)
  455. val_loss_df = df[~df["val_loss"].isna()]
  456. train_loss_df = df[~df["train_loss_epoch"].isna()]
  457. plt.figure(figsize=(8, 5))
  458. plt.plot(np.array(train_loss_df["epoch"]), np.array(train_loss_df["train_loss_epoch"]), label="Train Loss")
  459. plt.plot(np.array(val_loss_df["epoch"]), np.array(val_loss_df["val_loss"]), label="Val Loss")
  460. plt.xlabel("Epoch")
  461. plt.ylabel("MSE Loss")
  462. plt.title("Training vs Validation Loss")
  463. plt.legend()
  464. plt.grid(True)
  465. plt.tight_layout()
  466. plt.show()
  467. # %%
  468. x,y = next(iter(test_loader_lat))
  469. y_pred_lat=[]
  470. y_true_lat=[]
  471. attn_weights_lat = []
  472. model_latents.eval()
  473. model_latents.to(device)
  474. with torch.no_grad():
  475. for x,y in tqdm.tqdm(test_loader_lat):
  476. y_hat, attn_weights = model_latents(x)
  477. # y_hat = model_latents(x)
  478. y_true_lat.append(y.reshape(len(y),4,32,32))
  479. y_pred_lat.append(y_hat.reshape(len(y_hat),4,32,32))
  480. attn_weights_lat.append(attn_weights.cpu())
  481. y_pred_lat=torch.cat(y_pred_lat,0)
  482. y_true_lat=torch.cat(y_true_lat,0)
  483. attn_weights_lat = torch.cat(attn_weights_lat, dim=0).squeeze(-1)
  484. # %%
  485. y_pred_lat.shape, y_true_lat.shape, y_pred_lat.device
  486. # %%
  487. import seaborn as sns
  488. sns.heatmap(attn_weights_lat)
  489. # %% [markdown]
  490. # ## Structural Decoding
  491. # %%
  492. y_pred_lat_train=[]
  493. model_latents.eval()
  494. model_latents.to(device)
  495. with torch.no_grad():
  496. for x,y in tqdm.tqdm(train_loader):
  497. y_hat, attn_weights = model_latents(x)
  498. # y_hat = model_latents(x)
  499. y_pred_lat_train.append(y_hat.reshape(len(y_hat),4,32,32))
  500. y_pred_lat_train=torch.cat(y_pred_lat_train,0)
  501. # %%
  502. y_pred_lat_train.shape
  503. # %%
  504. expected_latents_mean = train_latents.mean(0).to(device)
  505. expected_latents_std = train_latents.std(0).to(device)
  506. predicted_latents_mean = y_pred_lat_train.mean(0)
  507. predicted_latents_std = y_pred_lat_train.std(0)
  508. # %%
  509. 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):
  510. return ((latents - predicted_latents_mean) / predicted_latents_std) * expected_latents_std + expected_latents_mean
  511. test_latents_pred = adjust_latents(y_pred_lat).float()
  512. # %%
  513. test_latents_pred.shape, test_latents_pred.mean(), test_latents_pred.std(), expected_latents_mean.mean(), expected_latents_std.mean()
  514. # %%
  515. def reconstruct_images(latents):
  516. with torch.no_grad():
  517. # Scale the latents (SDXL typically scales latents by a factor)
  518. latents = latents * vae.scaling_factor
  519. # Decode latents back to image
  520. decoded_image = vae.decode(latents / vae.scaling_factor).sample
  521. # Convert decoded tensor back to PIL Image
  522. decoded_image = decoded_image.squeeze(0).permute(1, 2, 0)
  523. decoded_image = (decoded_image.clamp(-1, 1) + 1) / 2
  524. decoded_image = (decoded_image.cpu().numpy() * 255).astype(np.uint8)
  525. decoded_pil = Image.fromarray(decoded_image)
  526. return decoded_pil
  527. # %%
  528. idx = 5
  529. fig, axs = plt.subplots(5, 2, figsize=(10, 10))
  530. images = []
  531. latents = test_latents_pred[idx:idx+5].to(device)
  532. for lat in latents:
  533. images.append(reconstruct_images(lat.unsqueeze(0)))
  534. for i, img in enumerate(images):
  535. axs[i,0].imshow(Image.open(opj(thing_base_path, "THINGS", "Images", test_things_paths[idx+i]).replace("\\", "/")))
  536. axs[i,0].axis("off")
  537. axs[i,0].set_title(f"Original {i+1}")
  538. axs[i,1].imshow(img)
  539. axs[i,1].axis("off")
  540. axs[i,1].set_title(f"Reconstructed {i+1}")
  541. plt.tight_layout()
  542. # %%
  543. y_pred_feat_train=[]
  544. model_feat.eval()
  545. model_feat.to(device)
  546. with torch.no_grad():
  547. for x,y in tqdm.tqdm(train_loader):
  548. y_hat, attn_weights = model_feat(x)
  549. # y_hat = model_feat(x)
  550. y_pred_feat_train.append(y_hat)
  551. y_pred_feat_train=torch.cat(y_pred_feat_train,0)
  552. # %%
  553. y_pred_feat_train.shape
  554. # %%
  555. expected_features_mean = train_features[:,-1].mean(0).to(device)
  556. expected_features_std = train_features[:,-1].std(0).to(device)
  557. predicted_features_mean = y_pred_feat_train.mean(0)
  558. predicted_features_std = y_pred_feat_train.std(0)
  559. # %%
  560. 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):
  561. print(features.shape)
  562. scaled_pred = ((features - predicted_features_mean) / predicted_features_std) * expected_features_std + expected_features_mean
  563. zeros = torch.tensor(np.zeros_like(features.cpu()), device=device)
  564. return torch.stack([zeros,scaled_pred], axis=1)
  565. # %%
  566. test_features_pred = adjust_features(y_pred_feat)
  567. # %%
  568. test_features_pred.shape
  569. # %% [markdown]
  570. # ## Retrieve Structural similar images
  571. # %%
  572. # Number of nearest neighbors to find
  573. n_neighbors = 5
  574. # Create and fit the NearestNeighbors model
  575. nn_struct_model = NearestNeighbors(n_neighbors=n_neighbors, metric='cosine')
  576. nn_struct_model.fit(train_latents.reshape(len(train_latents),-1))
  577. # Find the nearest neighbors for the adjusted embeddings
  578. distances_struct, nearest_neighbors_struct_indices = nn_struct_model.kneighbors(test_latents_pred.cpu().reshape(len(test_latents_pred),-1))
  579. # %%
  580. ## Show original test image idx, reconstructed image idx from pred latents and 5 closest neighbors in train images
  581. idx = 30
  582. fig, axs = plt.subplots(5, n_neighbors+2, figsize=(10, 10))
  583. for j in range(5):
  584. axs[j, 0].imshow(Image.open(opj(thing_base_path, "THINGS", "Images", test_things_paths[idx+j]).replace("\\", "/")))
  585. axs[j, 0].axis("off")
  586. axs[j, 0].set_title("Original Image")
  587. axs[j, 1].imshow(reconstruct_images(test_latents_pred[idx+j].unsqueeze(0).to(device)))
  588. axs[j, 1].axis("off")
  589. # axs[j, 1].set_title("Reconstructed Image")
  590. for i, neighbor_idx in enumerate(nearest_neighbors_struct_indices[idx+j]):
  591. img = Image.open(train_images[neighbor_idx])
  592. axs[j, i+2].imshow(img)
  593. axs[j, i+2].axis("off")
  594. axs[j, i+2].set_title(f"Neighbor {i+1}")
  595. plt.tight_layout()
  596. # %% [markdown]
  597. # ## Reconstruct Images
  598. # %%
  599. idx_rec = 77 # 9, 12, 22, 28, 40, 46, 48, 55, 58, 77
  600. # %%
  601. test_features_pred.shape, test_features.shape, test_features_pred[idx_rec].half().unsqueeze(0).shape, idx_rec
  602. # %%
  603. seed = 55 # 42, 55, 999
  604. generator = torch.Generator(device=device).manual_seed(seed)
  605. 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)
  606. recon = pipeline(
  607. prompt="",
  608. ip_adapter_image_embeds=[test_features_pred[idx_rec].half().unsqueeze(-2)],
  609. negative_prompt="deformed, ugly, wrong proportion, low res, bad anatomy, worst quality, low quality",
  610. num_inference_steps = 50,
  611. guide_strength = 0.9,
  612. guidance_scale = 10,
  613. num_images_per_prompt = 4,
  614. generator=generator,
  615. ).images
  616. # %%
  617. init_latents = test_latents_pred[idx_rec].unsqueeze(0).half().to(device)
  618. low_res = reconstruct_images(init_latents.float().to(device))
  619. low_res
  620. # %%
  621. from skimage.metrics import structural_similarity as ssim
  622. recon_small = [i.resize((256,256)) for i in recon]
  623. recon_smooth = [i.filter(ImageFilter.GaussianBlur(6)) for i in recon_small]
  624. ssim_scores = [ssim(np.array(recon_smooth[i]), np.array(low_res), win_size=3) for i in range(4)]
  625. recon_sorted = [recon[i] for i in np.argsort(ssim_scores)[::-1]]
  626. # %%
  627. fig, axs = plt.subplots(1, 3, figsize=(20, 20)) # 1 riga, 3 colonne
  628. # Mostra immagine originale
  629. axs[0].imshow(Image.open(opj(thing_base_path, "THINGS", "Images", test_things_paths[idx_rec]).replace("\\", "/")))
  630. axs[0].axis("off")
  631. axs[0].set_title("Original Image", fontsize=25)
  632. # Mostra immagine a bassa risoluzione
  633. axs[1].imshow(low_res)
  634. axs[1].axis("off")
  635. axs[1].set_title("Low Resolution", fontsize=25)
  636. # Seleziona solo la ricostruzione con SSIM più alto
  637. best_recon_idx = sorted(range(len(ssim_scores)), key=lambda i: ssim_scores[i], reverse=True)[0]
  638. best_recon_img = recon_sorted[best_recon_idx]
  639. best_ssim_score = ssim_scores[best_recon_idx]
  640. # Mostra la miglior ricostruzione
  641. axs[2].imshow(best_recon_img)
  642. axs[2].axis("off")
  643. axs[2].set_title(f"Reconstructed, SSIM: {best_ssim_score:.2f}", fontsize=25)
  644. # Migliora la spaziatura
  645. plt.tight_layout()
  646. plt.subplots_adjust(wspace=0.3)
  647. plt.show()
  648. # %% [markdown]
  649. # ## Save Reconstructed
  650. # %%
  651. import os
  652. import torch
  653. import numpy as np
  654. from skimage.metrics import structural_similarity as ssim
  655. from PIL import Image, ImageFilter
  656. import matplotlib.pyplot as plt
  657. from os.path import join as opj
  658. save_recon_dir = data_path + "/monkeys/generative/img_gen_attmlp"
  659. # os.makedirs(save_true_dir, exist_ok=True)
  660. os.makedirs(save_recon_dir, exist_ok=True)
  661. seed = 55
  662. generator = torch.Generator(device=device).manual_seed(seed)
  663. for idx_rec in tqdm.tqdm(range(100)):
  664. # Prepara i latents
  665. prepared_latents = pipeline.prepare_latents(
  666. batch_size=1,
  667. latents=test_latents_pred[idx_rec].unsqueeze(0).half(),
  668. num_channels_latents=4,
  669. height=32,
  670. width=32,
  671. dtype=torch.float16,
  672. device=device,
  673. generator=generator
  674. )
  675. # Genera 4 ricostruzioni
  676. recon = pipeline(
  677. prompt="",
  678. ip_adapter_image_embeds=[test_features_pred[idx_rec].half().unsqueeze(-2)],
  679. negative_prompt="deformed, ugly, wrong proportion, low res, bad anatomy, worst quality, low quality",
  680. num_inference_steps=50,
  681. guide_strength=0.9,
  682. guidance_scale=10,
  683. num_images_per_prompt=4,
  684. generator=generator,
  685. ).images
  686. # Prepara immagine low-res
  687. init_latents = test_latents_pred[idx_rec].unsqueeze(0).half()
  688. low_res = reconstruct_images(init_latents.float().to(device))
  689. # Prepara recon per SSIM
  690. recon_small = [i.resize((256, 256)) for i in recon]
  691. recon_smooth = [i.filter(ImageFilter.GaussianBlur(6)) for i in recon_small]
  692. # Calcola SSIM tra low_res e ogni ricostruzione
  693. ssim_scores = [ssim(np.array(recon_smooth[i]), np.array(low_res), win_size=3) for i in range(4)]
  694. # Seleziona la ricostruzione migliore
  695. best_recon_idx = np.argmax(ssim_scores)
  696. best_recon_img = recon[best_recon_idx]
  697. # Immagine vera
  698. # true_img = Image.open(opj(thing_base_path, "THINGS", "Images", test_things_paths[idx_rec]).replace("\\", "/"))
  699. # true_img.save(os.path.join(save_true_dir, f"true_{idx_rec:03d}.png"))
  700. # Salva immagine ricostruita
  701. best_recon_img.save(os.path.join(save_recon_dir, f"recon_{idx_rec:03d}.png"))
  702. print("✅ Tutte le immagini salvate!")
  703. # %%
  704. # %%

monkeys_gener.ipynb at commit 8ad7b9a, no license · at the source

Overview

Authors: Matteo Ciferri1, Matteo Ferrante1,2, Nicola Toschi1,3
ORCID iDs: Matteo Ciferri
  1. Department of Biomedicine and Prevention, University of Rome Tor Vergata, Rome, Italy
  2. Tether Evo, Edificio Centro Corporativo Presidente Plaza, San Salvador, El Salvador
  3. A.A. Martinos Center for Biomedical Imaging, Harvard Medical School/MGH, Boston, MA, United States
Journal: Imaging neuroscience (Cambridge, Mass.), volume 4, article IMAG.a.1299
Dates: received 26 November 2025; accepted 19 June 2026; published online 13 July 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1162/imag.a.1299 · PMID 42453642 · PMCID PMC13366611 · OpenAlex W7124817775
Open access: green, a free copy (OpenAlex)
Status: code verified
Categories: methods / tools (subfield)
Methods: Smoothing, state filtering, decompositions, Statistics, Preprocessing, Machine learning, fMRI & imaging
Keywords: visual decoding, intracortical recordings, primate visual cortex, machine learning, generative reconstruction, brain–computer interfaces
Topic: Face Recognition and Perception (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Citations: not cited yet (Europe PMC); 35 references in the paper

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

License: CC-BY-4.0
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: f26bd45f86f5413432e42f30880418be6930ec19, 5 December 2025
Languages: MATLAB (126), Python (54), C (2), C/C++ (1), Jupyter (1)
Size: 1,345 files, 184 scripts
Software Heritage: not archived
Found in: “Data and Code Availability”
Holds: README, license file, environment (_code/lucent-things/setup.py), 1 notebook
Not found: CITATION.cff, tests, continuous integration, documentation
Tools: PyTorch (34 files), NumPy (25 files), Statistics and Machine Learning Toolbox (6 files), Pillow (6 files), Signal Processing Toolbox (5 files), h5py (2 files), Matplotlib (2 files), scikit-learn (2 files), SciPy (2 files), Curve Fitting Toolbox (1 file), Image Processing Toolbox (1 file), scikit-image (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
186 files

fidelioc55/primates-mua-decode

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 8ad7b9a2a00f5a7a61b84599fde1eaafcd532e4a, 24 September 2025
Languages: Jupyter (3), Python (2)
Size: 6 files, 5 scripts
Software Heritage: not archived
Found in: “Data and Code Availability”
Holds: README, 3 notebooks
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (5 files), PyTorch (5 files), PyTorch Lightning (4 files), pandas (3 files), Pillow (3 files), SciPy (3 files), Hugging Face Transformers (3 files), h5py (2 files), Matplotlib (2 files), scikit-image (2 files), scikit-learn (2 files), seaborn (2 files)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
6 files

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://gin.g-node.org/paolo_papale/TVSD. Implementation code for reproducibility is available at the repository: https://github.com/fidelioc55/primates-mua-decode.

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://doi.org/10.1162/imag.a.1299

BibTeX

@article{ciferri2026modular,
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/imag.a.1299},
url = {https://doi.org/10.1162/imag.a.1299},
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/07/13
VL - 4
SP - IMAG.a.1299
SN - 2837-6056
PB - MIT Press
DO - 10.1162/imag.a.1299
UR - https://doi.org/10.1162/imag.a.1299
LA - en
ER -

CSL-JSON

{
"id": "10.1162/imag.a.1299",
"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": "Imaging Neurosci (Camb)",
"volume": "4",
"page": "IMAG.a.1299",
"DOI": "10.1162/imag.a.1299",
"PMID": "42453642",
"PMCID": "PMC13366611",
"ISSN": "2837-6056",
"publisher": "MIT Press",
"URL": "https://doi.org/10.1162/imag.a.1299",
"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: Neuroinformatics
In 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 biology
In 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: eLife
In 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 biology
In 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 communications
In 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 biology
In 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 data
In 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 communications
In 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 neuroscience
In 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 neuroscience
In 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.

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.