OSCR

Modality-agnostic decoding of vision and language from fMRI.

Code ↔ Paper

17 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 17 matches
  1. [1] § Results › Modality-agnostic decoders ↔ notebooks/modality_agnostic_decoding.ipynb, lines 485–526 · score 0.80 · dual stream, late fusion, early fusion, single stream, modality agnostic, ANOVA
  2. [2] § Methods › Modality-agnostic decoders ↔ data.py, lines 182–248 · score 0.76 · vision features, Llama2, mistral, mixtral, ResNet, SigLip
  3. [3] § Results › Modality-agnostic decoders ↔ notebooks/notebook_utils.py, lines 204–262 · score 0.69 · Dino giant, GPT2 large, language models, vision features, imagebind, multimodal
  4. [4] § Methods › Modality-agnostic decoders ↔ analyses/decoding/ridge_regression_decoding.py, lines 45–187 · score 0.69 · ridge regression, predict latent, fMRI beta, decoder training, fitting, models
  5. [5] § Methods › fMRI preprocessing ↔ preprocessing/fmri_preprocessing.py, lines 83–124 · score 0.68 · anatomical scan, SPM, nipype, slice, coregistered, realignment
  6. [6] § Appendix 2 › Feature extraction details ↔ feature_extraction/extract_bridgetower_features.py, lines 45–51 · score 0.67 · bridgetower large itm, mlm itc, Model
  7. [7] § Appendix 1 › Dataset quality metrics › Head motion ↔ analyses/visualization/plot_dataset_quality_stats.py, lines 20–108 · score 0.64 · framewise displacement, realignment parameters, absolute, sum, head, motion
  8. [8] § Results › Modality-invariant regions ↔ analyses/decoding/searchlight/searchlight_cluster_manual_corrections.py, lines 13–38 · score 0.62 · inferior parietal, inferior temporal, middle temporal, cluster, searchlight
  9. [9] § Appendix 2 › Feature extraction details ↔ notebooks/modality_agnostic_decoding.ipynb, lines 485–526 · score 0.62 · dual stream, single stream, multimodal models, BLIP2, Paligemma2, Flava
  10. [10] § Appendix 1 › Intersession alignment ↔ notebooks/intersession_alignment.ipynb, lines 24–64 · score 0.62 · normalized mutual information, alignment, Intersession, coregistered, preprocessing
  11. [11] § Appendix 2 › Feature extraction details ↔ feature_extraction/extract_siglip_features.py, lines 37–42 · score 0.61 · siglip so400m patch14, Model
  12. [12] § Methods › fMRI preprocessing ↔ preprocessing/make_spm_design_job_mat.py, lines 178–310 · score 0.57 · gray matter masks, slice, coregistered, Preprocessing, realignment, scan
  13. [13] § Appendix 1 › Intersession alignment ↔ preprocessing/fmri_preprocessing.py, lines 83–124 · score 0.56 · anatomical scan, SPM, pipeline, coregistered, preprocessing
  14. [14] § Appendix 1 › Dataset quality metrics › Head motion ↔ analyses/visualization/plot_dataset_quality_stats.py, lines 20–108 · score 0.56 · realignment parameters, pitch, roll, yaw, displacement, head
  15. [15] § Methods › Modality-agnostic decoders ↔ notebooks/modality_agnostic_decoding.ipynb, lines 702–764 · score 0.55 · cross modal, pairwise accuracy, distance, cosine, latent, predictions
  16. [16] § Methods › fMRI preprocessing ↔ preprocessing/make_spm_design_job_mat.py, lines 22–55 · score 0.55 · train captions, train images, blank, fixations, preprocessing, imagery
  17. [17] § Methods › Modality-agnostic decoders ↔ notebooks/zero_shot_cross_modal_decoding.ipynb, lines 112–154 · score 0.54 · cross modal decoding, language model, pairwise, accuracy, captions, agnostic

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 · 819 lines · 29 KB · MIT · 3 matches

  1. # %%
  2. import os
  3. import numpy as np
  4. import matplotlib.pyplot as plt
  5. import pandas as pd
  6. import seaborn as sns
  7. from sklearn.preprocessing import StandardScaler
  8. from utils import RESULTS_DIR, SUBJECTS, COCO_IMAGES_DIR, STIM_INFO_PATH
  9. from data import MODALITY_AGNOSTIC, TRAINING_MODES, CAPTION, IMAGE, DEFAULT_VISION_FEATURES
  10. from eval import ACC_MODALITY_AGNOSTIC, ACC_CAPTIONS, ACC_IMAGES, ACC_IMAGERY, ACC_IMAGERY_WHOLE_TEST, get_distance_matrix, dist_mat_to_pairwise_acc
  11. from notebook_utils import create_result_graph, plot_metric_catplot, load_results_data, ACC_MEAN, METRICS_ERROR_ANALYSIS, get_data_default_feats, calc_model_feat_order, FEATS_MULTIMODAL, DEFAULT_FEAT_OPTIONS
  12. from matplotlib import patches
  13. sns.set_style("ticks", {'axes.grid': True})
  14. sns.set_theme(font_scale=1.6)
  15. from data import DEFAULT_FEATURES, DEFAULT_VISION_FEATURES, DEFAULT_LANG_FEATURES, TRAINING_MODES, MODALITY_AGNOSTIC, MODALITY_SPECIFIC_IMAGES, MODALITY_SPECIFIC_CAPTIONS
  16. # %%
  17. MODELS = [
  18. "random-imagebind", "vit-b-16", "vit-l-16", "vit-h-14", "resnet-18", "resnet-50", "resnet-152", "dino-base", "dino-large", "dino-giant",
  19. "bert-base-uncased", "bert-large-uncased", "llama2-7b", "llama2-13b", "mistral-7b", "mixtral-8x7b", "gpt2-small", "gpt2-medium", "gpt2-large", "gpt2-xl",
  20. "visualbert", "bridgetower", "vilt", "siglip", "paligemma2", "clip", "flava", "blip2", "imagebind"
  21. ]
  22. # for model in MODELS:
  23. # print(model, end=" ")
  24. # %%
  25. all_data = load_results_data(MODELS, recompute_acc_scores=False)
  26. all_data = all_data[all_data["mask"] == "whole_brain"]
  27. all_data_vol = all_data[all_data.surface == False].copy()
  28. all_data = all_data[all_data.surface == True].copy()
  29. multimodal_models = all_data[all_data.features.isin(FEATS_MULTIMODAL)].model.unique().tolist()
  30. vision_models = [m for m in all_data[all_data.features == "vision"].model.unique() if len(all_data[all_data.model == m].features.unique()) == 1]
  31. # %%
  32. data_default_feats = get_data_default_feats(all_data)
  33. # %%
  34. dd = data_default_feats.copy()
  35. dd = dd[dd.subject == 'sub-01']
  36. dd = dd[dd.model == 'dino-base']
  37. dd = dd[dd.metric.isin(['pairwise_acc_images', 'pairwise_acc_captions'])]
  38. dd = dd[dd.training_mode == 'agnostic']
  39. display(dd)
  40. # %%
  41. dd = data_default_feats.copy()
  42. dd = dd[dd.subject == 'sub-01']
  43. dd = dd[dd.model == 'gpt2-small']
  44. dd = dd[dd.metric.isin(['pairwise_acc_images', 'pairwise_acc_captions'])]
  45. dd = dd[dd.training_mode == 'agnostic']
  46. display(dd)
  47. # %% [markdown]
  48. # ## Feature comparison for multimodal models
  49. # %%
  50. data_mod_agnostic_train = all_data[(all_data.metric == ACC_MEAN) & (all_data.training_mode == MODALITY_AGNOSTIC)]
  51. with pd.option_context('display.max_rows', None, 'display.max_columns', None): # more options can be specified also
  52. # grouped = data_mod_agnostic_train.groupby(["model", "features", "vision_features", "lang_features"]).agg(count=('value', 'size'), pairwise_acc=('value', 'mean'), pairwise_acc_stddev=('value', 'std')).reset_index()
  53. grouped = data_mod_agnostic_train.groupby(["model", "features", "vision_features", "lang_features"]).agg(pairwise_acc=('value', 'mean')).reset_index()
  54. grouped = grouped[grouped.model.isin(multimodal_models)]
  55. grouped = grouped[~grouped.model.isin(["random-imagebind"])]
  56. display(grouped)
  57. # print(grouped.to_markdown())
  58. grouped = grouped.replace("n_a", "")
  59. # grouped = grouped[grouped.model.isin(multimodal_models)]
  60. # del grouped["count"]
  61. print(grouped.to_latex(index=False, escape=True, float_format="%.3f"))
  62. # %%
  63. # data_default_vision_feats = all_data.copy()
  64. # for model in all_data.model.unique():
  65. # default_vision_feats = DEFAULT_VISION_FEATURES[model]
  66. # data_default_vision_feats = data_default_vision_feats[((data_default_vision_feats.model == model) & (data_default_vision_feats.vision_features == default_vision_feats)) | (data_default_vision_feats.model != model)]
  67. #
  68. # feat_legend = {"avg": "average over tokens from vision and language streams", "fused_cls": "fused [CLS] token", "fused_mean": "average over fused tokens"}
  69. # feat_order = ["fused_cls", "fused_mean", "avg"]
  70. #
  71. # feat_order_long = [feat_legend[feat] for feat in feat_order]
  72. #
  73. # data_to_plot = data_default_vision_feats.copy()
  74. #
  75. # data_to_plot = data_to_plot[data_to_plot.model.isin(multimodal_models)]
  76. # models_excluded = ['random-imagebind', 'siglip', 'clip', 'imagebind']
  77. # data_to_plot = data_to_plot[~data_to_plot.model.isin(models_excluded)]
  78. #
  79. # data_to_plot["features"] = data_to_plot.features.replace(feat_legend)
  80. #
  81. # model_feat_order = calc_model_feat_order(data_to_plot, MODELS, feat_options=feat_order)
  82. #
  83. # metrics_order = [ACC_MEAN]
  84. # figure, lgd = create_result_graph(data_to_plot, order=model_feat_order, metrics=metrics_order, row_order=metrics_order, hue_order=feat_order_long, ylim=(0.5, 1),
  85. # legend_bbox=(0.06,1.01), height=5, legend_title="Modality-agnostic decoders projecting into multimodal model features based on ", verify_num_datapoints=False, plot_modality_specific=False)
  86. # plt.savefig(os.path.join(RESULTS_DIR, f"features_comparison_multimodal_models.png"), bbox_extra_artists=(lgd,), bbox_inches='tight', pad_inches=0, dpi=300)
  87. # %% [markdown]
  88. # ## Feature comparison for vision models
  89. # %%
  90. data_mod_agnostic_train = all_data[(all_data.metric == ACC_MEAN) & (all_data.training_mode == MODALITY_AGNOSTIC)]
  91. with pd.option_context('display.max_rows', None, 'display.max_columns', None): # more options can be specified also
  92. # grouped = data_mod_agnostic_train.groupby(["model", "features", "vision_features", "lang_features"]).agg(count=('value', 'size'), pairwise_acc=('value', 'mean'), pairwise_acc_stddev=('value', 'std')).reset_index()
  93. grouped = data_mod_agnostic_train.groupby(["model", "vision_features"]).agg(pairwise_acc=('value', 'mean')).reset_index()
  94. grouped = grouped[grouped.model.isin(vision_models)]
  95. models_excluded = ["resnet-18", "resnet-50", "resnet-152"]
  96. grouped = grouped[~grouped.model.isin(models_excluded)]
  97. display(grouped)
  98. # print(grouped.to_markdown())
  99. grouped = grouped.replace("n_a", "")
  100. # grouped = grouped[grouped.model.isin(multimodal_models)]
  101. # del grouped["count"]
  102. print(grouped.to_latex(index=False, escape=True, float_format="%.3f"))
  103. # %%
  104. # data_default_vision_feats = all_data.copy()
  105. #
  106. # feat_legend = {"vision_features_cls": "[CLS] token", "vision_features_mean": "average over patches"}
  107. # feat_order = ["vision_features_cls", "vision_features_mean"]
  108. #
  109. # feat_order_long = [feat_legend[feat] for feat in feat_order]
  110. #
  111. # data_to_plot = data_default_vision_feats.copy()
  112. #
  113. # data_to_plot = data_to_plot[data_to_plot.model.isin(vision_models)]
  114. # models_excluded = ["resnet-18", "resnet-50", "resnet-152"]
  115. # data_to_plot = data_to_plot[~data_to_plot.model.isin(models_excluded)]
  116. #
  117. # data_to_plot["vision_features"] = data_to_plot.vision_features.replace(feat_legend)
  118. #
  119. # model_feat_order = calc_model_feat_order(data_to_plot, MODELS)
  120. # metrics_order = [ACC_MEAN]
  121. # figure, lgd = create_result_graph(data_to_plot, order=model_feat_order, metrics=metrics_order, row_order=metrics_order, hue_variable='vision_features', hue_order=feat_order_long, ylim=(0.5, 1),
  122. # legend_bbox=(0.06,1.01), height=5, legend_title="Modality-agnostic decoders projecting into vision model features based on ", verify_num_datapoints=False, plot_modality_specific=False)
  123. # plt.savefig(os.path.join(RESULTS_DIR, f"features_comparison_vision_models.png"), bbox_extra_artists=(lgd,), bbox_inches='tight', pad_inches=0, dpi=300)
  124. # %% [markdown]
  125. # ## Modality-agnostic decoding vs. modality-specific decoding
  126. # %% [markdown]
  127. # ### Model features comparison
  128. # %%
  129. model_order = ['random-imagebind']
  130. model_feat_order = ['random-imagebind_avg']
  131. for features in DEFAULT_FEAT_OPTIONS:
  132. print('\nmodel feat type: ', features)
  133. dp = data_default_feats.copy()
  134. dp = dp[dp.features == features]
  135. dp = dp[dp.training_mode == MODALITY_AGNOSTIC]
  136. dp = dp[dp.metric == ACC_MEAN]
  137. for model in dp.model.unique():
  138. if len(dp[dp.model == model]) != len(SUBJECTS):
  139. print(f"unexpected number of datapoints for {model}: {len(dp[dp.model == model])}")
  140. # scores = dp.groupby("model").value.mean().sort_values()
  141. scores = dp.groupby("model_feat").value.mean().sort_values()
  142. if len(scores) > 0:
  143. print(scores)
  144. model_order.extend([mf.split('_')[0] for mf in scores.index.values])
  145. model_feat_order.extend(scores.index.values)
  146. model_order
  147. # model_feat_order
  148. # %%
  149. FEAT_ORDER = ["vision models", "language models", "multimodal models"]
  150. data_to_plot = data_default_feats.copy()
  151. data_to_plot["features"] = data_to_plot.features.replace({"vision": "vision models", "lang": "language models"})
  152. data_to_plot["features"] = data_to_plot.features.replace({f: "multimodal models" for f in FEATS_MULTIMODAL})
  153. # model_feat_order = calc_model_feat_order(data_to_plot)
  154. metrics_order = [ACC_MEAN]
  155. HUE_ORDER = [MODALITY_AGNOSTIC, MODALITY_SPECIFIC_IMAGES, MODALITY_SPECIFIC_CAPTIONS]
  156. figure, lgd = create_result_graph(data_to_plot, order=model_feat_order, metrics=metrics_order, row_order=metrics_order, hue_order=HUE_ORDER, ylim=(0.5, 1), row_title_height=0.85,
  157. legend_bbox=(0.68, 1.05), height=5, aspect=3.2, legend_title="", verify_num_datapoints=False)
  158. figure.axes[0][0].add_patch(
  159. patches.Rectangle(
  160. (-0.5, 0.1), # (x,y)
  161. 1, # width
  162. 0.9, # height
  163. linewidth=0,
  164. clip_on=False,
  165. alpha=0.05,
  166. color='0.2'
  167. )
  168. )
  169. figure.axes[0][0].add_patch(
  170. patches.Rectangle(
  171. (0.5, 0.1), # (x,y)
  172. 9, # width
  173. 0.9, # height
  174. linewidth=0,
  175. clip_on=False,
  176. alpha=0.15,
  177. color='0.2'
  178. )
  179. )
  180. figure.axes[0][0].add_patch(
  181. patches.Rectangle(
  182. (9.5, 0.1), # (x,y)
  183. 10, # width
  184. 0.9, # height
  185. linewidth=0,
  186. clip_on=False,
  187. alpha=0.05,
  188. color='0.2'
  189. )
  190. )
  191. figure.axes[0][0].add_patch(
  192. patches.Rectangle(
  193. (19.5, 0.1), # (x,y)
  194. 9, # width
  195. 0.9, # height
  196. linewidth=0,
  197. clip_on=False,
  198. alpha=0.15,
  199. color='0.2'
  200. )
  201. )
  202. figure.axes[0][0].text(4, 0.11, 'Vision Models', fontsize=12)
  203. figure.axes[0][0].text(14, 0.11, 'Language Models', fontsize=12)
  204. figure.axes[0][0].text(23, 0.11, 'Multimodal Models', fontsize=12)
  205. plt.savefig(os.path.join(RESULTS_DIR, f"features_comparison.png"), bbox_extra_artists=(lgd,), bbox_inches='tight', pad_inches=0.3, dpi=300)
  206. # %%
  207. FEAT_ORDER = ["vision models", "language models", "multimodal models"]
  208. data_to_plot = data_default_feats.copy()
  209. data_to_plot["features"] = data_to_plot.features.replace({"vision": "vision models", "lang": "language models"})
  210. data_to_plot["features"] = data_to_plot.features.replace({f: "multimodal models" for f in FEATS_MULTIMODAL})
  211. # model_feat_order = calc_model_feat_order(data_to_plot)
  212. metrics_order = [ACC_IMAGES, ACC_CAPTIONS]
  213. HUE_ORDER = [MODALITY_AGNOSTIC, MODALITY_SPECIFIC_IMAGES, MODALITY_SPECIFIC_CAPTIONS]
  214. figure, lgd = create_result_graph(data_to_plot, order=model_feat_order, metrics=metrics_order, row_order=metrics_order, hue_order=HUE_ORDER, ylim=(0.5, 1), row_title_height=1.07, row_title_loc='left',
  215. legend_bbox=(0.68, 1.03), height=5, aspect=3.2, legend_title="", verify_num_datapoints=False)
  216. height=0.5
  217. figure.axes[0][0].add_patch(
  218. patches.Rectangle(
  219. (-0.5, 0.5), # (x,y)
  220. 1, # width
  221. height, # height
  222. linewidth=0,
  223. clip_on=False,
  224. alpha=0.05,
  225. color='0.2'
  226. )
  227. )
  228. figure.axes[0][0].add_patch(
  229. patches.Rectangle(
  230. (0.5, 0.5), # (x,y)
  231. 9, # width
  232. height, # height
  233. linewidth=0,
  234. clip_on=False,
  235. alpha=0.15,
  236. color='0.2'
  237. )
  238. )
  239. figure.axes[0][0].add_patch(
  240. patches.Rectangle(
  241. (9.5, 0.5), # (x,y)
  242. 10, # width
  243. height, # height
  244. linewidth=0,
  245. clip_on=False,
  246. alpha=0.05,
  247. color='0.2'
  248. )
  249. )
  250. figure.axes[0][0].add_patch(
  251. patches.Rectangle(
  252. (19.5, 0.5), # (x,y)
  253. 9, # width
  254. height, # height
  255. linewidth=0,
  256. clip_on=False,
  257. alpha=0.15,
  258. color='0.2'
  259. )
  260. )
  261. height=0.8
  262. figure.axes[1][0].add_patch(
  263. patches.Rectangle(
  264. (-0.5, 0.2), # (x,y)
  265. 1, # width
  266. height, # height
  267. linewidth=0,
  268. clip_on=False,
  269. alpha=0.05,
  270. color='0.2'
  271. )
  272. )
  273. figure.axes[1][0].add_patch(
  274. patches.Rectangle(
  275. (0.5, 0.2), # (x,y)
  276. 9, # width
  277. height, # height
  278. linewidth=0,
  279. clip_on=False,
  280. alpha=0.15,
  281. color='0.2'
  282. )
  283. )
  284. figure.axes[1][0].add_patch(
  285. patches.Rectangle(
  286. (9.5, 0.2), # (x,y)
  287. 10, # width
  288. height, # height
  289. linewidth=0,
  290. clip_on=False,
  291. alpha=0.05,
  292. color='0.2'
  293. )
  294. )
  295. figure.axes[1][0].add_patch(
  296. patches.Rectangle(
  297. (19.5, 0.2), # (x,y)
  298. 9, # width
  299. height, # height
  300. linewidth=0,
  301. clip_on=False,
  302. alpha=0.15,
  303. color='0.2'
  304. )
  305. )
  306. figure.axes[1][0].text(4, 0.21, 'Vision Models', fontsize=12)
  307. figure.axes[1][0].text(14, 0.21, 'Language Models', fontsize=12)
  308. figure.axes[1][0].text(23, 0.21, 'Multimodal Models', fontsize=12)
  309. plt.subplots_adjust(hspace=0.3)
  310. plt.savefig(os.path.join(RESULTS_DIR, f"features_comparison_split_by_modality.png"), bbox_extra_artists=(lgd,), bbox_inches='tight', pad_inches=0.5, dpi=300)
  311. # %% [markdown]
  312. # # Statistical tests
  313. # %%
  314. from scipy.stats import ttest_ind
  315. import statsmodels.formula.api as smf
  316. # %% [markdown]
  317. # ## Mod agnostic vs. mod specific
  318. # %%
  319. data_default_feats_copy = data_default_feats.copy()
  320. for metric, comparison_training_mode in zip(['pairwise_acc_mean', 'pairwise_acc_mean'], ["images", "captions"]):
  321. print(f"{metric} | comparison with {comparison_training_mode}")
  322. data_filtered = data_default_feats_copy[data_default_feats_copy.metric == metric]
  323. data_mod_agno = data_filtered[data_filtered.training_mode == "agnostic"]
  324. assert len(data_mod_agno) == len(SUBJECTS) * len(data_mod_agno.model.unique())
  325. data_mod_spec = data_filtered[data_filtered.training_mode == comparison_training_mode]
  326. assert len(data_mod_spec) == len(SUBJECTS) * len(data_mod_spec.model.unique())
  327. print('mod agno: ', data_mod_agno.value.mean())
  328. print('mod spec: ', data_mod_spec.value.mean())
  329. # display(ttest_ind(data_mod_agno.value.values, data_mod_spec.value.values))
  330. # display(data_mod_agno)
  331. data_anova = pd.concat([data_mod_agno, data_mod_spec])
  332. data_anova = data_anova[['model', 'subject', 'training_mode', 'value']]
  333. mod = smf.mixedlm("value ~ training_mode", data_anova, groups=data_anova["subject"]).fit()
  334. print("=" * 50 + "\nANOVA\n" + "=" * 50)
  335. print(mod.summary())
  336. print('pvalues:\n', mod.pvalues)
  337. print('\n')
  338. # %% [markdown]
  339. # ## Mod agnostic vs. mod specific - imagery decoding
  340. # %%
  341. # from scipy.stats import ttest_ind
  342. # from pymer4.models import Lmer
  343. # data_ttest = data_default_feats.copy()
  344. # for metric in [ACC_IMAGERY_WHOLE_TEST, ACC_IMAGERY]:
  345. # for comparison_training_mode in ["images", "captions"]:
  346. # print(f"{metric} | comparison with {comparison_training_mode}")
  347. # data_filtered = data_ttest[data_ttest.metric == metric]
  348. # data_mod_agno = data_filtered[data_filtered.training_mode == "agnostic"]
  349. # # data_mod_agno = data_mod_agno[data_mod_agno.model == "imagebind"]
  350. # assert len(data_mod_agno) == len(SUBJECTS) * len(data_mod_agno.model.unique())
  351. # data_mod_spec = data_filtered[data_filtered.training_mode == comparison_training_mode]
  352. # assert len(data_mod_spec) == len(SUBJECTS) * len(data_mod_spec.model.unique())
  353. # print('mod agno: ', data_mod_agno.value.mean())
  354. # print('mod spec: ', data_mod_spec.value.mean())
  355. # print(len(data_mod_agno.value.values))
  356. # print(ttest_ind(data_mod_agno.value.values, data_mod_spec.value.values))
  357. # # display(data_mod_agno)
  358. # data_glm = pd.concat([data_mod_agno, data_mod_spec])
  359. # # display(data_glm)
  360. # mod = Lmer('value ~ training_mode + (1 | subject)', data=data_glm)
  361. # # mod = Lmer('value ~ training_mode + (1 | model) + (1 | subject)', data=data_glm)
  362. # print("=" * 50 + "\nGLM\n" + "=" * 50)
  363. # fitted = mod.fit()
  364. # print(fitted)
  365. # print(fitted[["Estimate", "SE", "Sig"]])
  366. # print('\n')
  367. # %%
  368. data_ttest = data_default_feats.copy()
  369. for metric in [ACC_IMAGERY_WHOLE_TEST]:
  370. for comparison_training_mode in ["images", "captions"]:
  371. print(f"{metric} | comparison with {comparison_training_mode}")
  372. data_filtered = data_ttest[data_ttest.metric == metric]
  373. data_mod_agno = data_filtered[data_filtered.training_mode == "agnostic"]
  374. assert len(data_mod_agno) == len(SUBJECTS) * len(data_mod_agno.model.unique())
  375. data_mod_spec = data_filtered[data_filtered.training_mode == comparison_training_mode]
  376. assert len(data_mod_spec) == len(SUBJECTS) * len(data_mod_spec.model.unique())
  377. print('mod agno: ', data_mod_agno.value.mean())
  378. print('mod spec: ', data_mod_spec.value.mean())
  379. print(ttest_ind(data_mod_agno.value.values, data_mod_spec.value.values))
  380. # display(data_mod_agno)
  381. data_anova = pd.concat([data_mod_agno, data_mod_spec])
  382. data_anova = data_anova[['model', 'subject', 'training_mode', 'value']]
  383. # print(data_anova)
  384. mod = smf.mixedlm("value ~ training_mode", data_anova, groups=data_anova["subject"]).fit()
  385. print("=" * 50 + "\nANOVA\n" + "=" * 50)
  386. print(mod.summary())
  387. print('pvalues:\n', mod.pvalues)
  388. print('\n')
  389. # %% [markdown]
  390. # ## Effect of multimodal model architecture
  391. # %%
  392. data_ttest = data_default_feats.copy()
  393. for metric in [ACC_MEAN]:
  394. print(f"{metric}")
  395. data_filtered = data_ttest[data_ttest.metric == metric]
  396. data_mod_agno = data_filtered[data_filtered.training_mode == "agnostic"]
  397. data_mod_agno = data_mod_agno[data_mod_agno.model != "random-imagebind"]
  398. assert len(data_mod_agno) == len(SUBJECTS) * len(data_mod_agno.model.unique())
  399. data_dual_stream = data_mod_agno[data_mod_agno.model.isin(['clip', 'imagebind', 'siglip'])].copy()
  400. data_dual_stream['multimodal_type'] = 'dual_stream'
  401. data_single_stream_early_fusion = data_mod_agno[data_mod_agno.model.isin(['visualbert', 'vilt', 'paligemma2'])].copy()
  402. data_single_stream_early_fusion['multimodal_type'] = 'single_stream_early_fusion'
  403. data_single_stream_late_fusion = data_mod_agno[data_mod_agno.model.isin(['bridgetower', 'flava', 'blip2'])].copy()
  404. data_single_stream_late_fusion['multimodal_type'] = 'single_stream_late_fusion'
  405. print('data_dual_stream: ', data_dual_stream.value.mean())
  406. print(len(data_dual_stream.value.values))
  407. print('data_single_stream_early_fusion: ', data_single_stream_early_fusion.value.mean())
  408. print(len(data_single_stream_early_fusion.value.values))
  409. print('data_single_stream_late_fusion: ', data_single_stream_late_fusion.value.mean())
  410. print(len(data_single_stream_late_fusion.value.values))
  411. # print(ttest_ind(data_mod_agno.value.values, data_mod_spec.value.values))
  412. data_anova = pd.concat([data_dual_stream, data_single_stream_early_fusion, data_single_stream_late_fusion])
  413. data_anova = data_anova[['subject', 'multimodal_type', 'value']]
  414. mod = smf.mixedlm("value ~ multimodal_type", data_anova, groups=data_anova["subject"]).fit()
  415. print("=" * 50 + "\nANOVA\n" + "=" * 50)
  416. # print(mod.random_effects)
  417. print(mod.summary())
  418. print('pvalues:\n', mod.pvalues)
  419. print('\n')
  420. # %% [markdown]
  421. # ## Comparing cross-decoding for images and captions
  422. # %%
  423. data_filtered = data_default_feats.copy()
  424. # for metric, comparison_training_mode in zip(['pairwise_acc_mean', 'pairwise_acc_mean'], ["images", "captions"]):
  425. # print(f"{metric} | comparison with {comparison_training_mode}")
  426. # data_filtered = data_default_feats_copy[data_default_feats_copy.metric == 'pairwise_acc_mean']
  427. data_cross_images = data_filtered[(data_filtered.training_mode == "captions") & (data_filtered.metric == 'pairwise_acc_images')]
  428. assert len(data_cross_images) == len(SUBJECTS) * len(data_cross_images.model.unique())
  429. data_cross_captions = data_filtered[(data_filtered.training_mode == "images") & (data_filtered.metric == 'pairwise_acc_captions')]
  430. assert len(data_cross_captions) == len(SUBJECTS) * len(data_cross_captions.model.unique())
  431. print('data_cross_images: ', data_cross_images.value.mean())
  432. print('data_cross_captions: ', data_cross_captions.value.mean())
  433. # display(ttest_ind(data_mod_agno.value.values, data_mod_spec.value.values))
  434. # # display(data_mod_agno)
  435. data_anova = pd.concat([data_cross_images, data_cross_captions])
  436. data_anova = data_anova[['model', 'subject', 'training_mode', 'value']]
  437. mod = smf.mixedlm("value ~ training_mode", data_anova, groups=data_anova["subject"]).fit()
  438. print("=" * 50 + "\nANOVA\n" + "=" * 50)
  439. print(mod.summary())
  440. print('pvalues:\n', mod.pvalues)
  441. print('\n')
  442. # %% [markdown]
  443. # # Imagery decoding feat comparison
  444. # %%
  445. FEAT_ORDER = ["vision models", "language models", "multimodal models"]
  446. data_to_plot = data_default_feats.copy()
  447. data_to_plot["features"] = data_to_plot.features.replace({"vision": "vision models", "lang": "language models"})
  448. data_to_plot["features"] = data_to_plot.features.replace({f: "multimodal models" for f in FEATS_MULTIMODAL})
  449. # model_feat_order = calc_model_feat_order(data_to_plot)
  450. metrics_order = [ACC_IMAGERY, ACC_IMAGERY_WHOLE_TEST]
  451. HUE_ORDER = [MODALITY_AGNOSTIC, MODALITY_SPECIFIC_IMAGES, MODALITY_SPECIFIC_CAPTIONS]
  452. figure, lgd = create_result_graph(data_to_plot, order=model_feat_order, metrics=metrics_order, row_order=metrics_order, hue_order=HUE_ORDER, ylim=(0.5, 1), row_title_height=0.85,
  453. legend_bbox=(0.68, 1.07), height=5, aspect=3.2, legend_title="", verify_num_datapoints=False)
  454. figure.axes[1][0].add_patch(
  455. patches.Rectangle(
  456. (-0.5, 0.2), # (x,y)
  457. 1, # width
  458. 1.4, # height
  459. linewidth=0,
  460. clip_on=False,
  461. alpha=0.05,
  462. color='0.2'
  463. )
  464. )
  465. figure.axes[1][0].add_patch(
  466. patches.Rectangle(
  467. (0.5, 0.2), # (x,y)
  468. 9, # width
  469. 1.4, # height
  470. linewidth=0,
  471. clip_on=False,
  472. alpha=0.15,
  473. color='0.2'
  474. )
  475. )
  476. figure.axes[1][0].add_patch(
  477. patches.Rectangle(
  478. (9.5, 0.2), # (x,y)
  479. 10, # width
  480. 1.4, # height
  481. linewidth=0,
  482. clip_on=False,
  483. alpha=0.05,
  484. color='0.2'
  485. )
  486. )
  487. figure.axes[1][0].add_patch(
  488. patches.Rectangle(
  489. (19.5, 0.2), # (x,y)
  490. 9, # width
  491. 1.4, # height
  492. linewidth=0,
  493. clip_on=False,
  494. alpha=0.15,
  495. color='0.2'
  496. )
  497. )
  498. figure.axes[1][0].text(4, 0.21, 'Vision Models', fontsize=12)
  499. figure.axes[1][0].text(14, 0.21, 'Language Models', fontsize=12)
  500. figure.axes[1][0].text(23, 0.21, 'Multimodal Models', fontsize=12)
  501. plt.savefig(os.path.join(RESULTS_DIR, f"features_comparison_imagery.png"), bbox_extra_artists=(lgd,), bbox_inches='tight', pad_inches=0.3, dpi=300)
  502. # %% [markdown]
  503. # ## Per-subject results
  504. # %%
  505. FEAT_ORDER = ["vision", "lang", "matched"]
  506. FEAT_PALETTE = sns.color_palette('Set2')[:3]
  507. def create_result_graph_all_subjs(data, order=model_feat_order, metrics=[ACC_CAPTIONS, ACC_IMAGES], hue_variable="training_mode", hue_order=FEAT_ORDER, ylim=None,
  508. legend_title="Modality-agnostic decoders based on features from", palette=FEAT_PALETTE, dodge=False, noise_ceilings=None, plot_modality_specific=True,
  509. row_variable="metric", col_variable=None, legend_bbox=None):
  510. for mode in TRAINING_MODES:
  511. data_mode = data[data.training_mode == mode]
  512. for x_variable_value in order:
  513. length = len(data_mode[(data_mode["model_feat"] == x_variable_value) & (data_mode.metric == metrics[0])])
  514. expected_num_datapoints = len(SUBJECTS)
  515. # if hue_variable != "features":
  516. # expected_num_datapoints *= len(data[hue_variable].unique())
  517. if (length > 0) and (length != expected_num_datapoints):
  518. message = f"unexpected number of datapoints: {length} (expected: {expected_num_datapoints}) (model_feat: {x_variable_value} {mode}"
  519. print(f"Warning: {message}")
  520. catplot_g, data_plotted, lgd = plot_metric_catplot(data, order=model_feat_order, metrics=metrics, x_variable="model_feat", legend_title=legend_title, aspect=2, legend_bbox=legend_bbox, rotation=90, cut_labels=False,
  521. hue_variable=hue_variable, row_variable=row_variable, col_variable=col_variable, hue_order=hue_order, palette=palette, ylim=ylim, noise_ceilings=noise_ceilings)
  522. for i in range(len(data.subject.unique())):
  523. catplot_g.axes[i,0].set_title(f"Subject {i+1} | Decoding of captions", fontsize=25)
  524. catplot_g.axes[i,1].set_title(f"Subject {i+1} | Decoding of images", fontsize=25)
  525. catplot_g.axes[i,0].set_ylabel('pairwise accuracy')
  526. return catplot_g, lgd
  527. data_to_plot = data_default_feats.copy()
  528. data_to_plot["features"] = data_to_plot.features.replace({"vision": "vision models", "lang": "language models"})
  529. data_to_plot["features"] = data_to_plot.features.replace({f: "multimodal models" for f in FEATS_MULTIMODAL})
  530. model_feat_order = calc_model_feat_order(data_to_plot, MODELS)
  531. HUE_ORDER = [MODALITY_AGNOSTIC, MODALITY_SPECIFIC_IMAGES, MODALITY_SPECIFIC_CAPTIONS]
  532. figure, lgd = create_result_graph_all_subjs(data_to_plot, model_feat_order, metrics=[ACC_CAPTIONS, ACC_IMAGES], hue_order=HUE_ORDER, ylim=(0.5, 1), row_variable="subject", col_variable="metric", legend_bbox=(0.58, 1.07))
  533. plt.savefig(os.path.join(RESULTS_DIR, f"features_comparison_per_subject.png"), dpi=300, bbox_extra_artists=(lgd,), bbox_inches='tight', pad_inches=0)
  534. # %%
  535. # %%
  536. # %% [markdown]
  537. # #
  538. # %% [markdown]
  539. # # Error analysis
  540. # %%
  541. models = ['imagebind']
  542. data = load_results_data(models, metrics=METRICS_ERROR_ANALYSIS)
  543. # metric = ACC_IMAGES
  544. all_data = data[data.model.isin(MODELS)]
  545. all_data = all_data[all_data["mask"] == "whole_brain"]
  546. all_data = all_data[all_data.surface == True]
  547. data_default_feats_err_analysis = get_data_default_feats(all_data)
  548. # %%
  549. def all_pairwise_accuracy_scores(latents, predictions, stim_types=None, metric="cosine", standardize=True):
  550. results = dict()
  551. results['comp_mats'] = dict()
  552. for modality, acc_metric_name in zip([CAPTION, IMAGE], [ACC_CAPTIONS, ACC_IMAGES]):
  553. preds_mod = predictions[stim_types == modality].copy()
  554. latents_mod = latents[stim_types == modality]
  555. if standardize:
  556. preds_mod = StandardScaler().fit_transform(preds_mod)
  557. dist_mat = get_distance_matrix(preds_mod, latents_mod, metric)
  558. diag = dist_mat.diagonal().reshape(-1, 1)
  559. comp_mat = diag < dist_mat
  560. score = dist_mat_to_pairwise_acc(dist_mat)
  561. results[acc_metric_name] = score
  562. results['comp_mats'][acc_metric_name] = (~comp_mat).astype(int)
  563. if standardize:
  564. predictions = StandardScaler().fit_transform(predictions)
  565. dist_mat = get_distance_matrix(predictions, latents, metric)
  566. mod_agnostic_accs = []
  567. for modality in [CAPTION, IMAGE]:
  568. dist_mat_within_mod = dist_mat[stim_types == modality][:, stim_types == modality]
  569. dist_mat_cross_modal = dist_mat[stim_types == modality][:, stim_types != modality]
  570. dist_mat_min = np.min((dist_mat_within_mod, dist_mat_cross_modal), axis=0)
  571. diag = dist_mat_min.diagonal().reshape(-1, 1)
  572. comp_mat = diag < dist_mat_min
  573. score = dist_mat_to_pairwise_acc(dist_mat_min)
  574. # scores = np.mean(comp_mat, axis=0)
  575. mod_agnostic_accs.append(score)
  576. results[f"pairwise_acc_mod_agnostic_{modality}s"] = score
  577. results['comp_mats'][f"pairwise_acc_mod_agnostic_{modality}s"] = (~comp_mat).astype(int)
  578. results[ACC_MODALITY_AGNOSTIC] = np.mean(mod_agnostic_accs)
  579. return results
  580. metric = ACC_IMAGES #TODO which metric?
  581. training_type = MODALITY_AGNOSTIC
  582. all_subj_mats = []
  583. for subject in SUBJECTS:
  584. df = data_default_feats_err_analysis.copy()
  585. df_subj = df[(df.training_mode == training_type) & (df.subject == subject)]
  586. predictions = df_subj[df_subj.metric == "predictions"].value.item()
  587. latents = df_subj[df_subj.metric == "latents"].value.item()
  588. stimulus_ids = df_subj[df_subj.metric == "stimulus_ids"].value.item()
  589. stimulus_types = df_subj[df_subj.metric == "stimulus_types"].value.item()
  590. results = all_pairwise_accuracy_scores(latents, predictions, stimulus_types)
  591. print(results[ACC_IMAGES])
  592. mat = results['comp_mats'][metric]
  593. all_subj_mats.append(mat)
  594. mat = np.sum(all_subj_mats, axis=0)
  595. mat = mat - np.diag(np.diag(mat))
  596. df_err = pd.DataFrame(mat, index=stimulus_ids[:70], columns=stimulus_ids[:70])
  597. # %%
  598. # df_err = df_err.map(lambda x: 0 if x < 3 else x)
  599. # with pd.option_context('display.max_rows', None, 'display.max_columns', None):
  600. # display(df_err)
  601. # %%
  602. stimuli_info = pd.read_csv(STIM_INFO_PATH, index_col=0)
  603. stimuli_info = stimuli_info[stimuli_info.used == True]
  604. captions_dict = stimuli_info.caption.to_dict()
  605. img_paths = stimuli_info.img_path.to_dict()
  606. img_paths = {idx: os.path.join(COCO_IMAGES_DIR, path) for idx, path in img_paths.items()}
  607. from PIL import Image
  608. def display_stimuli(coco_ids):
  609. imgs = [Image.open(img_paths[img_id]).convert('RGB') for img_id in coco_ids]
  610. min_height = np.min([np.array(im).shape[0] for im in imgs])
  611. stacked = np.hstack([np.array(im)[:min_height,:,:] for im in imgs])
  612. img = Image.fromarray(stacked)
  613. # print(min_height)
  614. captions = [captions_dict[coco_id] for coco_id in coco_ids]
  615. for cap in captions:
  616. print(cap)
  617. display(img)
  618. # display_stimuli([16764, 79642])
  619. # %%
  620. for id1, row in df_err.iterrows():
  621. confusions = []
  622. for id2, count in row.items():
  623. if count > 5:
  624. confusions.append(id2)
  625. if len(confusions) > 0:
  626. print(f'common confusions involving id {id1}:\n')
  627. confusions = [id1] + confusions
  628. display_stimuli(confusions)
  629. print('\n\n')
  630. # %%
  631. # %%
  632. # %%
  633. # %%

modality_agnostic_decoding.ipynb at commit 57f7a1c, under MIT · at the source

Overview

Authors: Mitja Nikolaus1, Milad Mozafari2, Isabelle Berry1, Nicholas Asher3, Leila Reddy1, Rufin VanRullen1
  1. Université de Toulouse, CNRS, CerCo, Toulouse, France
  2. Torus AI, Toulouse, France
  3. Université de Toulouse, IRIT, Toulouse, France
Journal: eLife, volume 14, article RP107933
Dates: published online 17 April 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.7554/elife.107933 · PMID 41995708 · PMCID PMC13090028 · OpenAlex W4413947960
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: fMRI (modality), human (organism), cognitive (subfield)
Methods: Connectivity, Statistics, fMRI & imaging, Machine learning
Keywords: Human
MeSH: Brain*, Language*, Magnetic Resonance Imaging*, Vision, Ocular*, Visual Perception*, Brain Mapping, Humans, Photic Stimulation (* major topic)
Topic: Face Recognition and Perception (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Funding: Agence Nationale de la Recherche (ANITI (ANR-19-PI3A-0004), AI-REPS (ANR-18-CE37-0007-01)); European Research Council (Advanced grant GLoW (101096017), 101096017)
Citations: cited by 3 papers (Europe PMC); 134 references in the paper

Abstract

Humans perform tasks involving the manipulation of inputs regardless of how these signals are perceived by the brain, thanks to representations that are invariant to the stimulus modality. In this paper, we present modality-agnostic decoders that leverage such modality-invariant representations to predict which stimulus a subject is seeing, irrespective of the modality in which the stimulus is presented. Training these modality-agnostic decoders is made possible thanks to our new large-scale fMRI dataset SemReps-8K, released publicly along with this paper. It comprises six subjects watching both images and short text descriptions of such images, as well as the conditions during which the subjects were imagining visual scenes. We find that modality-agnostic decoders can perform as well as modality-specific decoders and even outperform them when decoding captions and mental imagery. Furthermore, a searchlight analysis revealed that large areas of the brain contain modality-invariant representations. Such areas are also particularly suitable for decoding visual scenes from the mental imagery condition.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repository

Its files are read in the Code ↔ Paper reader above, with 17 matches between paragraphs and lines of code.

mitjanikolaus/multimodal_decoding

License: MIT
State: the link answers, verified on 29 September 2026
Evidence: files inventoried
Commit: 57f7a1c4a5da1c96887ed79f0507cbd286c727fd, 17 April 2026
Languages: Python (46), Jupyter (7), MATLAB (1)
Size: 77 files, 54 scripts
Software Heritage: archived
Found in: the references
Holds: README, license file, environment (setup.py, environment_files/environment.yml, environment_files/environment_gpu.yml), 7 notebooks
Not found: CITATION.cff, tests, continuous integration, documentation
Tools: NumPy (33 files), Matplotlib (19 files), Pillow (19 files), PyTorch (15 files), NiBabel (14 files), pandas (14 files), seaborn (14 files), SciPy (11 files), Hugging Face Transformers (10 files), Nilearn (9 files), scikit-learn (8 files), FreeSurfer (4 files), Nipype (3 files), SPM (3 files), h5py (1 file), OpenCV (1 file), scikit-image (1 file), statsmodels (1 file)
Availability: 1 check, the latest on 29 September 2026: the link answers
  • 29 September 2026: the link answers
56 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:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 54 scripts, each with its path and the digest of its content;
  • 17 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

Datasets cited

Data availability

All fMRI data is available at https://openneuro.org/datasets/ds007272. Code for preprocessing and analyses is available at https://github.com/mitjanikolaus/multimodal_decoding (copy archived at Nikolaus, 2026).

The following dataset was generated:

Nikolaus M, Mozafari M, Berry I, Asher N, Reddy L, VanRullen R. 2025. SemReps-8K. OpenNeuro.

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, 29 September 2026: the first record

Recorded: type, language, journal, volume, pages, dates, 6 authors, 1 keyword, 8 MeSH terms, 2 funders, 119 references.

Cite

This paper

Nikolaus, M., Mozafari, M., Berry, I., Asher, N., Reddy, L., & VanRullen, R. (2026). Modality-agnostic decoding of vision and language from fMRI. eLife, 14, RP107933. https://doi.org/10.7554/elife.107933

BibTeX

@article{nikolaus2026modality,
author = {Nikolaus, Mitja and Mozafari, Milad and Berry, Isabelle and Asher, Nicholas and Reddy, Leila and VanRullen, Rufin},
title = {{Modality-agnostic decoding of vision and language from fMRI}},
journal = {eLife},
year = {2026},
month = apr,
volume = {14},
pages = {RP107933},
publisher = {eLife Sciences Publications, Ltd},
issn = {2050-084X},
doi = {10.7554/elife.107933},
url = {https://doi.org/10.7554/elife.107933},
pmid = {41995708},
pmcid = {PMC13090028}
}

RIS

TY - JOUR
AU - Nikolaus, Mitja
AU - Mozafari, Milad
AU - Berry, Isabelle
AU - Asher, Nicholas
AU - Reddy, Leila
AU - VanRullen, Rufin
TI - Modality-agnostic decoding of vision and language from fMRI
T2 - eLife
J2 - eLife
PY - 2026
DA - 2026/04/17
VL - 14
SP - RP107933
SN - 2050-084X
PB - eLife Sciences Publications, Ltd
DO - 10.7554/elife.107933
UR - https://doi.org/10.7554/elife.107933
LA - en
ER -

CSL-JSON

{
"id": "10.7554/elife.107933",
"type": "article-journal",
"title": "Modality-agnostic decoding of vision and language from fMRI",
"container-title": "eLife",
"author": [
{
"family": "Nikolaus",
"given": "Mitja"
},
{
"family": "Mozafari",
"given": "Milad"
},
{
"family": "Berry",
"given": "Isabelle"
},
{
"family": "Asher",
"given": "Nicholas"
},
{
"family": "Reddy",
"given": "Leila"
},
{
"family": "VanRullen",
"given": "Rufin"
}
],
"container-title-short": "eLife",
"volume": "14",
"page": "RP107933",
"DOI": "10.7554/elife.107933",
"PMID": "41995708",
"PMCID": "PMC13090028",
"ISSN": "2050-084X",
"publisher": "eLife Sciences Publications, Ltd",
"URL": "https://doi.org/10.7554/elife.107933",
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
17
]
]
}
}

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.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: Hugging Face Transformers, OpenCV, scikit-image, 9 other tools, fMRI, 11 references
[2] doi:10.1038/s41597-026-07248-6 [code]
A large-scale fMRI dataset for vision-language semantic association.
Journal: Scientific data
In common: FreeSurfer, Nilearn, OpenCV, 10 other tools, fMRI, 10 references
[3] 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, 9 other tools, cognitive, 7 references
[4] 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, FreeSurfer, scikit-image, 11 other tools, 5 references
[5] 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: Nilearn, scikit-image, h5py, 8 other tools, fMRI, 7 references
[6] doi:10.1167/jov.26.5.7 [code]
Representations in vision and language converge in a shared, multidimensional space of perceived similarities.
Journal: Journal of vision
In common: Nilearn, OpenCV, h5py, 10 other tools, cognitive, 5 references
[7] doi:10.1162/nol.a.271 [code]
Compositional Complexity in Text and Images.
Journal: Neurobiology of language (Cambridge, Mass.)
In common: Nilearn, OpenCV, Pillow, 8 other tools, fMRI, 7 references
[8] doi:10.1038/s41467-026-71568-9 [code]
Convergent and selective representations of pain, appetitive processes, aversive processes, and cognitive control in the insula.
Journal: Nature communications
In common: Nipype, FreeSurfer, Nilearn, 9 other tools, cognitive, 3 references
[9] doi:10.1038/s41467-026-76452-0 [code]
Music evokes shared neural representations of imagined narratives across sensory modalities.
Journal: Nature communications
In common: FreeSurfer, Nilearn, SPM, 10 other tools, cognitive, 3 references
[10] doi:10.1162/imag.a.1299 [code]
A modular semantic-structural pipeline for visual decoding from primate spiking data via selective temporal integration.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: Hugging Face Transformers, scikit-image, h5py, 8 other tools, 5 references

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

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.