
Tribe V2 Neuroscience
- 1 installs
- 3 repo stars
- Updated August 5, 2026
- broomva/skills
tribe-v2-neuroscience is a Claude skill that uses Meta FAIR's TRIBE v2 to predict fMRI cortical responses to video, audio, and text for in-silico neuroscience experiments.
About
tribe-v2-neuroscience is a Claude skill for in-silico neuroscience using Meta FAIR's TRIBE v2. It predicts fMRI cortical responses to video, audio, or text with a single pretrained transformer, so experiments run on any hardware without a scanner. A developer uses it to design virtual experiments, map stimulus-to-region activation, and test hypotheses before expensive fMRI studies. It also generates synthetic fMRI data for research.
- Predicts fMRI cortical responses to video, audio, and text with one model
- Runs virtual neuroscience experiments across stimulus sets
- Maps which stimuli activate specific cortical regions
Tribe V2 Neuroscience by the numbers
- 1 all-time installs (skills.sh)
- Ranked #1,803 of 2,064 Data Science & ML skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
tribe-v2-neuroscience capabilities & compatibility
- Capabilities
- fmri prediction · cortical mapping · neuro experiment
- Use cases
- research · data analysis
- Runs
- Runs locally
- Pricing
- Free
What tribe-v2-neuroscience says it does
In-silico neuroscience using Meta FAIR's TRIBE v2 — predict fMRI cortical responses to video, audio, or text using a single pretrained transformer.
It is **not** a language model — it predicts fMRI BOLD responses on the fsaverage5 cortical surface
70x** resolution improvement over TRIBE v1
npx skills add https://github.com/broomva/skills --skill tribe-v2-neuroscienceAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 3 |
| Last updated | August 5, 2026 |
| Repository | broomva/skills ↗ |
What it does
Run in-silico neuroscience experiments predicting fMRI cortical responses to stimuli without a scanner.
Who is it for?
Designing virtual neuroscience experiments and mapping stimulus-to-cortex activation.
Skip if: Commercial use; TRIBE v2 is CC BY-NC 4.0, non-commercial research only.
When should I use this skill?
Running virtual experiments, predicting brain responses, or mapping which stimuli activate cortical regions.
What you get
Predicted cortical responses and regional activation maps for any stimulus, on any hardware.
- Predicted cortical response arrays
- Regional activation tables
- Virtual experiment results
By the numbers
- Predicts ~20,000 fsaverage5 vertices per hemisphere
- 70x resolution over TRIBE v1
- First load downloads ~7GB of weights
Files
TRIBE v2 Neuroscience
In-silico neuroscience using Meta FAIR's TRIBE v2 — predict fMRI cortical responses to video, audio, or text using a single pretrained transformer. Run experiments on any hardware, without a scanner.
What TRIBE v2 Is
TRansformer for In-silico Brain Experiments (v2) is a brain encoding model released by Meta FAIR on March 26, 2026. It is not a language model — it predicts fMRI BOLD responses on the fsaverage5 cortical surface (~20,000 vertices per hemisphere) given multimodal sensory input.
Architecture:
Video → V-JEPA2 (video encoder)
Audio → Wav2Vec-BERT 2.0 (audio encoder)
Text → LLaMA 3.2-3B (text encoder)
↓
Unified Transformer
↓
fsaverage5 mesh (~20k vertices)
(n_timesteps × n_vertices)Key properties:
- 70x resolution improvement over TRIBE v1
- 2-3x accuracy improvement, zero-shot generalization to new subjects
- 5-second temporal offset built in — accounts for hemodynamic lag
- Log-linear scaling with fMRI training data (like LLMs with tokens)
- License: CC BY-NC 4.0 (non-commercial research only)
- HuggingFace:
facebook/tribev2 - Demo: https://aidemos.atmeta.com/tribev2
---
Quick Start
1. Install TRIBE v2
# Requires Python 3.11+
git clone https://github.com/facebookresearch/tribev2
cd tribev2
pip install -e .2. Load the Model
from tribev2 import TribeModel
model = TribeModel.from_pretrained("facebook/tribev2", cache_folder="./cache")The first load downloads model weights (~7GB). Subsequent loads use the cache.
3. Run Your First Prediction
# Video input (returns DataFrame of events)
df = model.get_events_dataframe(video_path="stimulus.mp4")
# Text input
df = model.get_events_dataframe(text_path="transcript.txt")
# Audio input
df = model.get_events_dataframe(audio_path="audio.wav")
# Predict cortical responses
preds, segments = model.predict(events=df)
# Output shape: (n_timesteps, n_vertices)
# n_vertices ≈ 20,484 on fsaverage5
print(preds.shape) # e.g., (142, 20484)
print(segments) # list of segment boundaries in seconds---
Workflow A: Single Stimulus Prediction
Trigger: You have one video, audio clip, or transcript and want to know which brain regions activate.
from tribev2 import TribeModel
import numpy as np
import pandas as pd
# Load model
model = TribeModel.from_pretrained("facebook/tribev2", cache_folder="./cache")
# Load stimulus (choose one modality)
df = model.get_events_dataframe(video_path="face_stimulus.mp4")
# df = model.get_events_dataframe(audio_path="speech.wav")
# df = model.get_events_dataframe(text_path="story.txt")
# Predict
preds, segments = model.predict(events=df)
# preds: numpy array (n_timesteps, n_vertices)
# Find peak activation timestep
peak_t = np.argmax(preds.mean(axis=1))
peak_activations = preds[peak_t, :]
# Top-10 most activated vertices at peak
top_verts = np.argsort(peak_activations)[-10:][::-1]
print(f"Peak timestep: {peak_t} (~{peak_t * 1.5:.1f}s)")
print(f"Top vertices: {top_verts}")
print(f"Peak activation values: {peak_activations[top_verts]}")
# Regional mean activations (using known fsaverage5 ranges)
REGIONS = {
"V1_left": (0, 1500),
"FFA_right": (9900, 10400),
"A1_left": (3500, 4200),
"Broca_left": (6200, 6800),
"DMN_mPFC": (14000, 15000),
}
for region, (v_start, v_end) in REGIONS.items():
region_mean = preds[:, v_start:v_end].mean()
print(f" {region}: mean activation = {region_mean:.4f}")Use the Cortical Atlas to interpret which vertices correspond to which regions.
---
Workflow B: Virtual Experiment
Trigger: You want to compare brain responses across multiple stimuli — e.g., faces vs. objects vs. scenes.
from tribev2 import TribeModel
import numpy as np
import pandas as pd
from pathlib import Path
model = TribeModel.from_pretrained("facebook/tribev2", cache_folder="./cache")
# Define your stimulus set
stimuli = {
"faces": "stimuli/faces.mp4",
"scenes": "stimuli/scenes.mp4",
"objects": "stimuli/objects.mp4",
"baseline": "stimuli/scrambled.mp4",
}
# Define regions of interest (vertex ranges on fsaverage5)
ROIS = {
"FFA_right": (9900, 10400), # Fusiform Face Area
"PPA_right": (10400, 11000), # Parahippocampal Place Area
"LOC_right": (8800, 9500), # Lateral Occipital Complex
"EBA_right": (9500, 9900), # Extrastriate Body Area
}
results = []
for condition, path in stimuli.items():
df = model.get_events_dataframe(video_path=path)
preds, _ = model.predict(events=df)
for roi_name, (v_start, v_end) in ROIS.items():
roi_activation = preds[:, v_start:v_end].mean()
results.append({
"condition": condition,
"roi": roi_name,
"mean_activation": roi_activation,
"peak_activation": preds[:, v_start:v_end].max(),
})
# Analyze
df_results = pd.DataFrame(results)
pivot = df_results.pivot(index="condition", columns="roi", values="mean_activation")
print(pivot)
# Expected result for face selectivity:
# FFA_right should be highest for "faces" condition
# PPA_right should be highest for "scenes" conditionUse scripts/run_experiment.py to automate this over a directory of stimuli.
---
Workflow C: Paradigm Replication
Trigger: You want to replicate a classic neuroscience finding in-silico before running a real fMRI study.
Example: Replicating face selectivity in the Fusiform Face Area (Kanwisher 1997).
from tribev2 import TribeModel
import numpy as np
model = TribeModel.from_pretrained("facebook/tribev2", cache_folder="./cache")
# Kanwisher 1997: faces >> objects in FFA
# FFA is right-lateralized, ~vertices 9900-10400 (fsaverage5)
FFA_RIGHT = (9900, 10400)
FFA_LEFT = (1100, 1600) # smaller response expected
face_df = model.get_events_dataframe(video_path="faces_stimulus.mp4")
object_df = model.get_events_dataframe(video_path="objects_stimulus.mp4")
face_preds, _ = model.predict(events=face_df)
object_preds, _ = model.predict(events=object_df)
# Compute selectivity index
def roi_mean(preds, vertex_range):
v_start, v_end = vertex_range
return preds[:, v_start:v_end].mean()
face_ffa_r = roi_mean(face_preds, FFA_RIGHT)
object_ffa_r = roi_mean(object_preds, FFA_RIGHT)
face_ffa_l = roi_mean(face_preds, FFA_LEFT)
selectivity_index = (face_ffa_r - object_ffa_r) / (face_ffa_r + object_ffa_r + 1e-8)
print(f"FFA-right (faces): {face_ffa_r:.4f}")
print(f"FFA-right (objects): {object_ffa_r:.4f}")
print(f"FFA-left (faces): {face_ffa_l:.4f}")
print(f"Selectivity index: {selectivity_index:.4f}")
# Positive selectivity_index = FFA prefers faces
# Right > Left = expected right lateralization
# This replicates Kanwisher 1997 in-silicoSee paradigm-library.md for 8 fully documented paradigms with expected results and vertex ranges.
---
Workflow D: Hypothesis Testing
Trigger: You have a hypothesis like "visual cortex responds to motion but not to static images" and want to test it computationally.
Step 1: Define your hypothesis formally
H0: mean_activation(MT_right, motion_video) == mean_activation(MT_right, static_image_video)
H1: mean_activation(MT_right, motion_video) > mean_activation(MT_right, static_image_video)
Region: MT/V5 right hemisphere, vertices 7800-8200 (approx fsaverage5)Step 2: Generate contrasting stimuli
The stimuli must differ only on the dimension you're testing. For motion vs. static:
- Motion: videos with global optic flow (dot fields, moving gratings)
- Static: same scene photographed repeatedly (no temporal change)
Step 3: Run predictions and compute the contrast
from tribev2 import TribeModel
import numpy as np
from scipy import stats
model = TribeModel.from_pretrained("facebook/tribev2", cache_folder="./cache")
MT_RIGHT = (7800, 8200)
# Multiple clips per condition for effect size estimation
motion_clips = ["motion_01.mp4", "motion_02.mp4", "motion_03.mp4"]
static_clips = ["static_01.mp4", "static_02.mp4", "static_03.mp4"]
def get_roi_activation(clips, roi):
activations = []
for clip in clips:
df = model.get_events_dataframe(video_path=clip)
preds, _ = model.predict(events=df)
v_start, v_end = roi
activations.append(preds[:, v_start:v_end].mean())
return np.array(activations)
motion_acts = get_roi_activation(motion_clips, MT_RIGHT)
static_acts = get_roi_activation(static_clips, MT_RIGHT)
# One-tailed t-test
t_stat, p_val = stats.ttest_ind(motion_acts, static_acts, alternative='greater')
effect_size = (motion_acts.mean() - static_acts.mean()) / np.std(np.concatenate([motion_acts, static_acts]))
print(f"Motion MT activation: {motion_acts.mean():.4f} ± {motion_acts.std():.4f}")
print(f"Static MT activation: {static_acts.mean():.4f} ± {static_acts.std():.4f}")
print(f"t = {t_stat:.3f}, p = {p_val:.4f}")
print(f"Cohen's d = {effect_size:.3f}")
print(f"H1 supported: {p_val < 0.05 and t_stat > 0}")Step 4: Interpret and decide whether to proceed to real fMRI
If the in-silico result supports H1 with d > 0.5, the effect size is large enough to power a real study. Use TRIBE v2 output to:
- Estimate required sample size (TRIBE v2 predictions correlate with real fMRI at r~0.6-0.8)
- Identify best ROIs to measure in-scanner
- Pre-register your analysis plan
---
Output Interpretation
Shape: (n_timesteps, n_vertices)
preds, segments = model.predict(events=df)
# preds.shape[0] = number of TRs (fMRI volumes)
# Each TR ≈ 1.5 seconds (typical fMRI repetition time)
# Total duration covered = n_timesteps × 1.5s
# preds.shape[1] = 20,484 vertices (fsaverage5 surface)
# Vertices 0–10,241 = Left hemisphere
# Vertices 10,242–20,483 = Right hemisphereWhat the values mean
TRIBE v2 outputs z-scored BOLD signal predictions in arbitrary units:
0.0= mean response for this brain region (no activation above baseline)> 0= above-average activation< 0= below-average (suppression or deactivation)- Typical range:
[-3.0, 3.0]
The 5-second temporal offset
TRIBE v2 automatically applies a +5s hemodynamic lag. The prediction at timestep t reflects neural processing that occurred at t - 5s in the stimulus. You do not need to manually shift; the model handles this.
Vertex-to-region mapping
fsaverage5 has 10,242 vertices per hemisphere. Key landmarks:
Left hemisphere (vertices 0–10,241):
V1/V2 primary visual: 0–1,500
V3/V4 ventral stream: 1,500–3,000
MT/V5 motion: 2,800–3,200
A1 primary auditory: 3,500–4,200
Broca's area (44/45): 6,200–6,800
VWFA (word form): 7,100–7,500
Right hemisphere (vertices 10,242–20,483):
V1/V2 primary visual: 10,242–11,742
FFA (face area): 9,900–10,400 ← note: RH vertex numbers
PPA (place area): 10,400–11,000
A1 primary auditory: 13,742–14,442See references/brain-regions.md for the full atlas with all regions, hemispheres, and vertex ranges.
Segments
preds, segments = model.predict(events=df)
# segments: list of (start_sec, end_sec) tuples
# Corresponds to natural scene/speech boundaries the model detected
# Useful for aligning predictions to stimulus timing---
Using the Companion Scripts
Single Prediction
python scripts/predict_brain.py \
--input stimulus.mp4 \
--modality video \
--output results/predictions.csv \
--cache-dir ./model-cacheOutput CSV columns: timestep, vertex_id, predicted_activation
Also prints top-5 vertices at peak timestep to stdout.
Batch Experiment
python scripts/run_experiment.py \
--stimuli-dir stimuli/faces/ \
--modality video \
--output-dir results/face_experiment/ \
--region FFA_rightOutput CSV: stimulus_file, region, mean_activation, peak_timestep
---
Common Pitfalls
| Issue | Cause | Fix |
|---|---|---|
preds.shape[1] != 20484 | Wrong surface resolution | Verify facebook/tribev2 loaded, not v1 |
| All activations near 0 | Stimulus too short | Use clips > 10 seconds; TRIBE v2 needs sufficient temporal context |
| Right-hemisphere FFA vertex range seems off | Vertex indexing | RH vertices start at 10,242; FFA_right is still ~9,900-10,400 in the combined array |
| Memory error on GPU | Long video, full batch | Pass --chunk-duration 30 to process in 30s windows |
get_events_dataframe fails on audio | Wrong sample rate | Convert to 16kHz mono WAV first: ffmpeg -i input.mp4 -ar 16000 -ac 1 audio.wav |
---
Detailed References
- [references/brain-regions.md](references/brain-regions.md) — Full cortical atlas: every region, hemisphere, fsaverage5 vertex range, and what activates it
- [references/paradigm-library.md](references/paradigm-library.md) — 8 classic paradigms with TRIBE v2 replication protocols and expected results
- [scripts/predict_brain.py](scripts/predict_brain.py) — CLI for single-stimulus prediction with CSV output
- [scripts/run_experiment.py](scripts/run_experiment.py) — Batch multi-stimulus experiment runner with region-averaged output
Cortical Atlas — fsaverage5 Brain Regions for TRIBE v2
TRIBE v2 outputs predictions on the fsaverage5 surface mesh, which has 10,242 vertices per hemisphere (20,484 total). This reference maps named brain regions to approximate vertex ranges in that combined array.
Important caveats:
- Vertex boundaries are approximate. Individual functional regions vary across subjects.
- The ranges below reflect typical functional boundaries from FreeSurfer parcellations and published fMRI atlases.
- Left hemisphere: vertices 0–10,241. Right hemisphere: vertices 10,242–20,483.
- TRIBE v2 is trained on naturalistic stimuli; very short (< 5s) or highly artificial stimuli may produce weaker signals.
---
Visual Cortex
| Region | Full Name | Hemisphere | Approx vertex range (fsaverage5) | Primary inputs | Activated by |
|---|---|---|---|---|---|
| V1 | Primary Visual Cortex | Left | 0–1,500 | Retino-geniculo-calcarine | Any visual stimulus; retinotopic |
| V1 | Primary Visual Cortex | Right | 10,242–11,742 | Retino-geniculo-calcarine | Any visual stimulus; retinotopic |
| V2/V3 | Secondary / Tertiary Visual | Left | 1,500–2,500 | V1 outputs | Oriented edges, contours |
| V2/V3 | Secondary / Tertiary Visual | Right | 11,742–12,742 | V1 outputs | Oriented edges, contours |
| V4 | Ventral Color Area | Left | 2,500–3,000 | V2/V3 | Color, curved contours, faces |
| V4 | Ventral Color Area | Right | 12,742–13,242 | V2/V3 | Color, curved contours, faces |
| MT/V5 | Middle Temporal / V5 | Left | 2,800–3,200 | V1, V2, dorsal stream | Visual motion, optic flow, direction selectivity |
| MT/V5 | Middle Temporal / V5 | Right | 13,042–13,442 | V1, V2, dorsal stream | Visual motion, optic flow, direction selectivity |
| LOC | Lateral Occipital Complex | Left | 4,200–5,200 | V2/V3, ventral stream | Intact object shapes, viewpoint-invariant recognition |
| LOC | Lateral Occipital Complex | Right | 14,442–15,442 | V2/V3, ventral stream | Intact object shapes, viewpoint-invariant recognition |
| FFA | Fusiform Face Area | Left | 1,100–1,600 | Ventral visual stream | Faces (weaker than right); upright > inverted |
| FFA | Fusiform Face Area | Right | 9,900–10,400 | Ventral visual stream | Faces (dominant); upright > inverted; own race > other race |
| PPA | Parahippocampal Place Area | Left | 1,600–2,100 | Ventral stream | Scenes, spatial layout, indoor/outdoor environments |
| PPA | Parahippocampal Place Area | Right | 10,400–11,000 | Ventral stream | Scenes, spatial layout, indoor/outdoor environments |
| EBA | Extrastriate Body Area | Left | 5,200–5,700 | Lateral occipital | Body parts, silhouettes; not faces |
| EBA | Extrastriate Body Area | Right | 15,442–15,942 | Lateral occipital | Body parts, silhouettes; not faces |
Lateralization notes (visual):
- FFA is strongly right-lateralized for faces.
- PPA is bilateral with slight right dominance for scenes.
- MT is bilateral; both hemispheres required for full motion processing.
- LOC is bilateral for object recognition.
---
Auditory Cortex
| Region | Full Name | Hemisphere | Approx vertex range (fsaverage5) | Primary inputs | Activated by |
|---|---|---|---|---|---|
| A1 | Primary Auditory Cortex (Heschl's Gyrus) | Left | 3,500–4,200 | Medial geniculate nucleus (MGN) | Any sound; tonotopic (low-to-high frequency mapped) |
| A1 | Primary Auditory Cortex (Heschl's Gyrus) | Right | 13,742–14,442 | MGN | Any sound; right A1 biased toward pitch/music |
| Belt auditory | Auditory Belt / Lateral HG | Left | 4,200–4,800 | A1 | Complex sounds, pitch contours, voice identity |
| Belt auditory | Auditory Belt / Lateral HG | Right | 14,442–15,000 | A1 | Music, prosody, environmental sounds |
| STS | Superior Temporal Sulcus | Left | 4,800–5,300 | Belt, prefrontal | Audiovisual integration, voice, biological motion |
| STS | Superior Temporal Sulcus | Right | 15,000–15,500 | Belt, prefrontal | Social signals, facial expressions with sound |
Lateralization notes (auditory):
- Speech: left-lateralized (left A1 + Wernicke's area stronger for phonemes and words).
- Music and prosody: right-lateralized (right belt auditory + right STS).
- STS is a multimodal convergence zone — activates for combined audio-visual speech.
---
Language Network
| Region | Full Name | Hemisphere | Approx vertex range (fsaverage5) | Primary inputs | Activated by |
|---|---|---|---|---|---|
| Broca's area | IFG pars triangularis + opercularis (BA44/45) | Left | 6,200–6,800 | STS, DLPFC, premotor | Sentence comprehension, syntax, verbal working memory, speech production |
| Broca's area | IFG pars triangularis + opercularis (BA44/45) | Right | 16,442–17,042 | — | Prosodic processing; much weaker than left |
| Wernicke's area | Posterior STG / MTG (BA22) | Left | 5,700–6,200 | A1, STS | Word meaning, speech comprehension, phonological processing |
| Wernicke's area | Posterior STG / MTG (BA22) | Right | 15,942–16,442 | A1, STS | Prosody, emotional speech content |
| VWFA | Visual Word Form Area | Left | 7,100–7,500 | LOC, ventral visual stream | Written words, letter strings; font/case invariant |
Lateralization notes (language):
- Language is strongly left-lateralized in ~95% of right-handers and ~70% of left-handers.
- Lateralization index (LI) =
(left - right) / (left + right); expect LI > 0.2 for any language stimulus. - VWFA is exclusively left hemisphere; no right homolog shows significant activation for text.
---
Motor and Somatosensory Cortex
| Region | Full Name | Hemisphere | Approx vertex range (fsaverage5) | Primary inputs | Activated by |
|---|---|---|---|---|---|
| M1 | Primary Motor Cortex (BA4) | Left | 8,600–9,000 | Premotor, SMA | Observed or imagined movement (mirror neuron overlap); strongest for contralateral movement |
| M1 | Primary Motor Cortex (BA4) | Right | 18,800–19,200 | Premotor, SMA | Contralateral (left side) observed movement |
| S1 | Primary Somatosensory Cortex (BA1/2/3) | Left | 9,100–9,600 | Thalamus (VPL) | Observed touch, pain imagery, body-contact scenes |
| S1 | Primary Somatosensory Cortex (BA3/1/2) | Right | 19,200–19,600 | Thalamus (VPL) | Contralateral body touch (left side of body) |
| SMA | Supplementary Motor Area | Left | 9,600–10,000 | M1, DLPFC | Action sequences, tool use videos, rhythm tracking |
Notes:
- M1 and S1 can activate during observation of actions (action observation network).
- For TRIBE v2 with video input, expect M1/S1 activation when stimuli show people interacting physically (sports, dance, handcraft).
- Somatotopy: face representation at inferior end, leg at superior-medial end of both M1 and S1.
---
Default Mode Network (DMN)
| Region | Full Name | Hemisphere | Approx vertex range (fsaverage5) | Primary inputs | Activated by |
|---|---|---|---|---|---|
| mPFC | Medial Prefrontal Cortex | Left | 8,000–8,600 | PCC, angular gyrus, hippocampus | Self-referential thought, resting state, narrative; deactivates during demanding tasks |
| mPFC | Medial Prefrontal Cortex | Right | 18,200–18,800 | PCC, angular gyrus, hippocampus | Self-referential thought, resting state |
| PCC | Posterior Cingulate Cortex | Left | 9,000–9,500 | mPFC, hippocampus | Autobiographical memory retrieval, mind-wandering |
| PCC | Posterior Cingulate Cortex | Right | 19,200–19,700 | mPFC, hippocampus | Autobiographical memory retrieval, mind-wandering |
| Angular gyrus | Inferior parietal lobule | Left | 7,500–8,000 | STS, TPJ, parietal | Semantic integration, social cognition, story comprehension |
| Angular gyrus | Inferior parietal lobule | Right | 17,700–18,200 | STS, TPJ, parietal | Theory of mind, causal inference |
| Hippocampus | Parahippocampal / entorhinal | Left | 2,100–2,500 | Entorhinal cortex | Memory encoding, spatial context, episodic recall |
Deactivation pattern:
- The key DMN signature is negative BOLD (deactivation below resting baseline) during externally directed, cognitively demanding tasks.
- In TRIBE v2, this appears as negative predicted activation values in DMN regions for demanding stimuli.
- Conversely, slow narrative content or rest-like stimuli should produce positive activation in DMN.
---
Prefrontal Cortex
| Region | Full Name | Hemisphere | Approx vertex range (fsaverage5) | Primary inputs | Activated by |
|---|---|---|---|---|---|
| DLPFC | Dorsolateral Prefrontal Cortex (BA9/46) | Left | 6,800–7,100 | Parietal, premotor, thalamus | Working memory, cognitive control, rule following, verbal rehearsal |
| DLPFC | Dorsolateral Prefrontal Cortex (BA9/46) | Right | 17,042–17,342 | Parietal, premotor | Spatial working memory, monitoring |
| OFC | Orbitofrontal Cortex (BA11/13) | Left | 9,600–10,000 | Amygdala, reward circuits | Reward prediction, emotional valence, hedonic value of stimuli |
| OFC | Orbitofrontal Cortex (BA11/13) | Right | 19,600–20,000 | Amygdala, reward circuits | Punishment, social emotion, facial attractiveness |
| vmPFC | Ventromedial PFC (BA10/11) | Left | 8,600–9,000 | OFC, mPFC, amygdala | Value-based decision, moral judgments, social reward |
Notes:
- DLPFC activates during narrative content with high cognitive load (complex grammar, working memory demands in the story).
- OFC is harder to drive with purely sensory stimuli; best activated by emotionally valenced or reward-predictive content.
---
Quick Lookup: Region by Stimulus Type
| Stimulus type | Primary regions activated | Primary regions deactivated |
|---|---|---|
| Human faces (frontal, neutral) | FFA_right, FFA_left | — |
| Human faces (expressive) | FFA_right, STS_right, OFC | — |
| Outdoor scenes, landscapes | PPA_right, PPA_left | — |
| Moving objects / optic flow | MT_left, MT_right, V1 | — |
| Intact objects | LOC_left, LOC_right | — |
| Body parts, silhouettes | EBA_left, EBA_right | — |
| Spoken language (sentences) | Broca_left, Wernicke_left, A1_left | — |
| Written words / text | VWFA_left, Broca_left | — |
| Music / tonal sequences | A1_right, belt_auditory_right | — |
| Audiovisual speech | STS_left, Wernicke_left, A1 | — |
| Demanding cognitive task | DLPFC, Broca_left | mPFC, PCC, angular_gyrus (DMN) |
| Slow naturalistic narrative | mPFC, PCC, angular_gyrus (DMN) | — |
| Physical action / sports | M1, S1, STS | — |
| Tool use | Broca_left, premotor, M1 | — |
---
Notes on Precision
These vertex ranges are best estimates derived from:
- FreeSurfer's Desikan-Killiany and Destrieux cortical parcellations mapped to fsaverage5
- Published fMRI coordinates converted to surface vertices (MNI → fsaverage5 via spherical registration)
- The TRIBE v2 paper's reported region-of-interest analyses
Exact activation peaks will vary with stimulus content and quality. For high-precision region localization, use a full parcellation atlas (e.g., mne.datasets.fetch_fsaverage, then map Glasser HCP 360-region parcellation to fsaverage5 vertices).
For exploratory work, the ranges above are sufficient to detect the canonical activations described in the paradigm library.
Paradigm Library — Classic Neuroscience Findings Replicable In-Silico with TRIBE v2
Each entry covers: the original finding, the stimulus type required, the expected TRIBE v2 vertex range to check, and what a positive replication looks like.
---
1. Face Selectivity in the Fusiform Face Area (FFA)
Original finding: Kanwisher, McDermott & Chun (1997, J. Neuroscience) A region in the right fusiform gyrus (FFA) responds significantly more to upright faces than to objects, houses, or scrambled images. This was the founding paper for the "face patch" system.
Stimulus type needed
- Faces condition: video or images of frontal/profile human faces (no bodies visible, neutral expression acceptable)
- Control condition: matched images/video of common objects (chairs, tools) at same visual complexity
Expected TRIBE v2 vertex range to check
| Region | Hemisphere | Vertex range (fsaverage5) |
|---|---|---|
| FFA (primary) | Right | 9,900–10,400 |
| FFA (secondary) | Left | 1,100–1,600 |
How to interpret a positive replication
mean_activation(FFA_right, faces) > mean_activation(FFA_right, objects)with a selectivity index > 0.15- Right FFA should show at least 1.5x higher response than left FFA for faces
- Objects and scrambled stimuli should produce activation near 0 in FFA
selectivity_index = (face_ffa - object_ffa) / (face_ffa + object_ffa + 1e-8)
# Positive replication: selectivity_index > 0.15---
2. Scene Selectivity in the Parahippocampal Place Area (PPA)
Original finding: Epstein & Kanwisher (1998, Nature) A region in the parahippocampal cortex (PPA) responds maximally to images of places and scenes (indoor rooms, outdoor landscapes) compared to faces or objects.
Stimulus type needed
- Scenes condition: video of outdoor landscapes, cityscapes, indoor environments (no people in foreground)
- Control condition: close-up object or face videos without spatial context
Expected TRIBE v2 vertex range to check
| Region | Hemisphere | Vertex range (fsaverage5) |
|---|---|---|
| PPA (primary) | Right | 10,400–11,000 |
| PPA | Left | 1,600–2,100 |
How to interpret a positive replication
- PPA shows higher activation for scenes than for faces or isolated objects
- PPA and FFA should show a double dissociation: scenes > faces in PPA, faces > scenes in FFA
- The double dissociation is the strongest replication signal
---
3. Object Selectivity in the Lateral Occipital Complex (LOC)
Original finding: Malach et al. (1995, PNAS) A lateral occipital region (LOC) responds to intact objects regardless of exact size, viewpoint, or illumination — tuned to object shape rather than low-level features.
Stimulus type needed
- Intact objects condition: video or images of everyday objects in various viewpoints
- Scrambled control: pixel-scrambled versions of the same images (same spatial frequency, no object structure)
Expected TRIBE v2 vertex range to check
| Region | Hemisphere | Vertex range (fsaverage5) |
|---|---|---|
| LOC | Right | 14,442–15,442 |
| LOC | Left | 4,200–5,200 |
How to interpret a positive replication
mean_activation(LOC, intact_objects) > mean_activation(LOC, scrambled)by at least 0.2 units- LOC activation should be bilateral (both hemispheres), unlike FFA which is right-dominant
- Should be robust across object category (tools, animals, vehicles)
---
4. Body Selectivity in the Extrastriate Body Area (EBA)
Original finding: Downing et al. (2001, Science) A region in lateral occipitotemporal cortex (EBA) responds selectively to images of human bodies and body parts compared to objects, scrambled bodies, or faces.
Stimulus type needed
- Body condition: video of people walking, silhouettes, body parts (hands, arms) — faces cropped out
- Control condition: matched objects or scrambled body images
Expected TRIBE v2 vertex range to check
| Region | Hemisphere | Vertex range (fsaverage5) |
|---|---|---|
| EBA | Right | 15,442–15,942 |
| EBA | Left | 5,200–5,700 |
How to interpret a positive replication
- EBA activation > baseline for body stimuli
- Face stimuli should not drive EBA (helps dissociate EBA from FFA)
- Both hemispheres should activate; slight right lateralization expected
---
5. Language Lateralization in Broca's Area
Original finding: Broca (1861) (clinical); modern fMRI confirmations by Binder et al. (1997, JCMS) Language production and comprehension are strongly left-lateralized in most right-handed individuals. Broca's area (IFG pars triangularis, BA44/45) activates during sentence comprehension, verbal working memory, and syntactic processing.
Stimulus type needed
- Language condition: spoken or written sentences — narrative, syntactically complex preferred
- Control condition: non-linguistic auditory tone sequences or visual patterns of the same duration
Expected TRIBE v2 vertex range to check
| Region | Hemisphere | Vertex range (fsaverage5) |
|---|---|---|
| Broca's area | Left | 6,200–6,800 |
| Broca's area | Right (control) | 16,442–17,042 |
| Wernicke's area | Left | 5,700–6,200 |
How to interpret a positive replication
- Left Broca's shows higher activation for sentences than for matched non-linguistic stimuli
- Compute lateralization index (LI):
LI = (left_broca - right_broca) / (left_broca + right_broca + 1e-8)
# LI > 0 → left-lateralized (expected)
# LI > 0.2 → strong lateralization (replication)---
6. Visual Word Form Area (VWFA) Selectivity
Original finding: Cohen et al. (2000, Science) A region in the left fusiform gyrus (VWFA) responds selectively to written words and letter strings compared to non-orthographic visual stimuli such as faces, objects, or symbol strings.
Stimulus type needed
- Words condition: video or images of printed words, sentences, or letter strings in various fonts and cases
- Control condition: false fonts (letter-like symbols with no linguistic value), objects, or faces
Expected TRIBE v2 vertex range to check
| Region | Hemisphere | Vertex range (fsaverage5) |
|---|---|---|
| VWFA | Left | 7,100–7,500 |
How to interpret a positive replication
- VWFA activation significantly higher for real words vs. false-font strings
- Left-lateralized: right hemisphere homolog (17,300–17,700) should show minimal response
- Selectivity should be invariant to font and case (uppercase vs. lowercase both drive VWFA)
---
7. Motion Selectivity in MT/V5
Original finding: Zeki (1974, Brain); Zeki et al. (1991, fMRI) Area MT (middle temporal) / V5 responds selectively to visual motion — coherent moving dot fields, optic flow, moving gratings — compared to static images.
Stimulus type needed
- Motion condition: video with coherent optic flow (moving dot fields, walking humans, flowing water, camera dolly shots)
- Static condition: same content photographed with no camera or object motion (freeze frames replayed)
Expected TRIBE v2 vertex range to check
| Region | Hemisphere | Vertex range (fsaverage5) |
|---|---|---|
| MT/V5 | Left | 2,800–3,200 |
| MT/V5 | Right | 13,042–13,442 |
How to interpret a positive replication
- MT activation significantly higher for motion than static stimuli
- Bilateral: both hemispheres should show the effect
- Effect should be robust across motion types (translational, radial, rotational)
- V1 activation should also increase but less specifically (distinguishes MT from V1 response)
---
8. Default Mode Network Deactivation During Task
Original finding: Raichle et al. (2001, PNAS); Buckner et al. (2008, review) The Default Mode Network (DMN) — comprising mPFC, PCC, angular gyrus, and hippocampus — deactivates during externally-directed cognitive tasks but is active during rest, mind-wandering, and self-referential thought.
Stimulus type needed
- Task condition: cognitively demanding content — rapid arithmetic narration, spatial navigation instructions, fast-changing unfamiliar stimuli that demand attention
- Rest/narrative condition: slow, familiar naturalistic narrative (a familiar story told at relaxed pace)
Expected TRIBE v2 vertex range to check
| Region | Hemisphere | Vertex range (fsaverage5) |
|---|---|---|
| mPFC | Left | 8,000–8,600 |
| PCC | Left | 9,000–9,500 |
| Angular gyrus | Left | 7,500–8,000 |
| mPFC | Right | 18,200–18,800 |
| PCC | Right | 19,200–19,700 |
How to interpret a positive replication
- DMN regions show negative activation (below-baseline suppression) during demanding-task stimuli
- DMN regions show positive activation during naturalistic narrative
- Task-negative contrast =
mean_activation(DMN, demanding) - mean_activation(DMN, narrative)should be negative
dmn_contrast = (demanding_mPFC + demanding_PCC) / 2 - (narrative_mPFC + narrative_PCC) / 2
# Replication: dmn_contrast < -0.1This is the most counterintuitive paradigm to replicate: showing that brain regions deactivate with demanding stimuli.
---
Using These Paradigms in TRIBE v2
All paradigms above require the same basic workflow:
from tribev2 import TribeModel
import numpy as np
model = TribeModel.from_pretrained("facebook/tribev2", cache_folder="./cache")
# Load condition stimuli
df_a = model.get_events_dataframe(video_path="condition_A.mp4")
df_b = model.get_events_dataframe(video_path="condition_B.mp4")
preds_a, _ = model.predict(events=df_a)
preds_b, _ = model.predict(events=df_b)
# Extract ROI
v_start, v_end = 9900, 10400 # e.g., FFA_right
roi_a = preds_a[:, v_start:v_end].mean()
roi_b = preds_b[:, v_start:v_end].mean()
contrast = roi_a - roi_b
print(f"Contrast A - B = {contrast:.4f}")See scripts/run_experiment.py for automated multi-condition batch processing.
#!/usr/bin/env python3
"""
predict_brain.py — CLI wrapper for TRIBE v2 single-stimulus brain response prediction.
Usage:
python predict_brain.py --input stimulus.mp4 --modality video --output results.csv
python predict_brain.py --input speech.wav --modality audio --output results.csv
python predict_brain.py --input transcript.txt --modality text --output results.csv
Output:
CSV with columns: timestep, vertex_id, predicted_activation
Prints top-5 most activated vertices at peak timestep to stdout.
"""
import argparse
import sys
from pathlib import Path
import numpy as np
import pandas as pd
# fsaverage5 approximate vertex ranges for key regions
# Format: region_name -> (vertex_start, vertex_end, hemisphere)
BRAIN_REGIONS = {
# Visual cortex — left hemisphere
"V1_left": (0, 1500, "left"),
"V2V3_left": (1500, 2500, "left"),
"V4_ventral_left": (2500, 3000, "left"),
"MT_left": (2800, 3200, "left"),
"LOC_left": (4200, 5200, "left"),
"FFA_left": (1100, 1600, "left"),
"PPA_left": (1600, 2100, "left"),
"EBA_left": (5200, 5700, "left"),
# Auditory cortex — left hemisphere
"A1_left": (3500, 4200, "left"),
"STS_left": (4800, 5300, "left"),
# Language — left hemisphere
"Broca_left": (6200, 6800, "left"),
"Wernicke_left": (5700, 6200, "left"),
"VWFA_left": (7100, 7500, "left"),
# Default Mode Network — left hemisphere
"mPFC_left": (8000, 8600, "left"),
"PCC_left": (9000, 9500, "left"),
# Motor/somatosensory — left hemisphere
"M1_left": (8600, 9000, "left"),
"S1_left": (9100, 9600, "left"),
# Visual cortex — right hemisphere (offset by 10242)
"V1_right": (10242, 11742, "right"),
"MT_right": (13042, 13442, "right"),
"LOC_right": (14442, 15442, "right"),
"FFA_right": (9900, 10400, "right"), # fusiform, measured in combined array
"PPA_right": (10400, 11000, "right"),
"EBA_right": (15442, 15942, "right"),
# Auditory cortex — right hemisphere
"A1_right": (13742, 14442, "right"),
"STS_right": (15000, 15500, "right"),
# Language — right hemisphere (typically weaker)
"Broca_right": (16442, 17042, "right"),
"Wernicke_right": (15942, 16442, "right"),
# Default Mode Network — right hemisphere
"mPFC_right": (18200, 18800, "right"),
"PCC_right": (19200, 19700, "right"),
}
def load_model(cache_dir: str):
"""Load TRIBE v2 model from HuggingFace."""
try:
from tribev2 import TribeModel # type: ignore[import-untyped]
except ImportError:
print("ERROR: tribev2 not installed.", file=sys.stderr)
print("Install with:", file=sys.stderr)
print(" git clone https://github.com/facebookresearch/tribev2", file=sys.stderr)
print(" cd tribev2 && pip install -e .", file=sys.stderr)
sys.exit(1)
print(f"Loading TRIBE v2 from HuggingFace (cache: {cache_dir}) ...")
model = TribeModel.from_pretrained("facebook/tribev2", cache_folder=cache_dir)
print("Model loaded.")
return model
def build_events_dataframe(model, input_path: str, modality: str):
"""Build the events DataFrame from a stimulus file."""
p = Path(input_path)
if not p.exists():
print(f"ERROR: Input file not found: {input_path}", file=sys.stderr)
sys.exit(1)
modality = modality.lower()
if modality == "video":
return model.get_events_dataframe(video_path=str(p))
elif modality == "audio":
return model.get_events_dataframe(audio_path=str(p))
elif modality == "text":
return model.get_events_dataframe(text_path=str(p))
else:
print(f"ERROR: Unknown modality '{modality}'. Use: video, audio, text", file=sys.stderr)
sys.exit(1)
def predictions_to_long_df(preds: np.ndarray) -> pd.DataFrame:
"""
Convert (n_timesteps, n_vertices) prediction array to long-format DataFrame.
Returns DataFrame with columns: timestep, vertex_id, predicted_activation
This is memory-intensive for long stimuli. For very long predictions,
consider saving only peak timestep or region summaries instead.
"""
n_timesteps, n_vertices = preds.shape
# Build arrays directly to avoid cartesian product memory issues
timestep_ids = np.repeat(np.arange(n_timesteps), n_vertices)
vertex_ids = np.tile(np.arange(n_vertices), n_timesteps)
activations = preds.ravel()
df = pd.DataFrame({
"timestep": timestep_ids,
"vertex_id": vertex_ids,
"predicted_activation": activations,
})
return df
def print_top_vertices(preds: np.ndarray, n_top: int = 5):
"""Print summary of top activated vertices at peak timestep."""
# Find peak timestep (highest mean activation across cortex)
mean_per_timestep = preds.mean(axis=1)
peak_t = int(np.argmax(mean_per_timestep))
peak_activations = preds[peak_t, :]
print(f"\n--- Peak Timestep: {peak_t} (~{peak_t * 1.5:.1f}s into stimulus) ---")
print(f"Mean cortical activation at peak: {mean_per_timestep[peak_t]:.4f}")
print(f"\nTop {n_top} most activated vertices at peak:")
print(f" {'Rank':<5} {'Vertex':>8} {'Activation':>12} {'Region'}")
print(f" {'-'*5} {'-'*8} {'-'*12} {'-'*20}")
top_verts = np.argsort(peak_activations)[-n_top:][::-1]
for rank, vert in enumerate(top_verts, start=1):
activation = peak_activations[vert]
# Find the region this vertex falls in
region_label = "unknown"
for region_name, (v_start, v_end, _) in BRAIN_REGIONS.items():
if v_start <= vert < v_end:
region_label = region_name
break
print(f" {rank:<5} {vert:>8} {activation:>12.4f} {region_label}")
print(f"\nRegion summary at peak timestep:")
print(f" {'Region':<25} {'Mean Activation':>16}")
print(f" {'-'*25} {'-'*16}")
for region_name, (v_start, v_end, _) in sorted(BRAIN_REGIONS.items()):
region_mean = peak_activations[v_start:v_end].mean()
if abs(region_mean) > 0.1: # only print non-trivial activations
print(f" {region_name:<25} {region_mean:>16.4f}")
def save_predictions(preds: np.ndarray, output_path: str):
"""Save predictions to CSV. Warns if file will be large."""
n_timesteps, n_vertices = preds.shape
n_rows = n_timesteps * n_vertices
estimated_mb = n_rows * 3 * 8 / 1e6 # rough estimate: 3 cols × 8 bytes
print(f"\nPrediction shape: {preds.shape}")
print(f"Output rows: {n_rows:,} (~{estimated_mb:.0f} MB estimated)")
if n_rows > 5_000_000:
print("WARNING: Large output. Consider using --region to filter, or reducing stimulus length.")
print(f"Saving to {output_path} ...")
df = predictions_to_long_df(preds)
output_path_obj = Path(output_path)
output_path_obj.parent.mkdir(parents=True, exist_ok=True)
df.to_csv(output_path_obj, index=False)
print(f"Saved {len(df):,} rows to {output_path_obj}")
def main():
parser = argparse.ArgumentParser(
description="TRIBE v2 brain response prediction for a single stimulus.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python predict_brain.py --input clip.mp4 --modality video --output out.csv
python predict_brain.py --input speech.wav --modality audio --output out.csv
python predict_brain.py --input story.txt --modality text --output out.csv --cache-dir /tmp/tribe2
""",
)
parser.add_argument(
"--input", required=True,
help="Path to input stimulus file (video .mp4, audio .wav, or text .txt)",
)
parser.add_argument(
"--modality", required=True, choices=["video", "audio", "text"],
help="Modality of the input file",
)
parser.add_argument(
"--output", required=True,
help="Path to output CSV file (columns: timestep, vertex_id, predicted_activation)",
)
parser.add_argument(
"--cache-dir", default="./tribe2-cache",
help="Directory to cache downloaded model weights (default: ./tribe2-cache)",
)
parser.add_argument(
"--top-n", type=int, default=5,
help="Number of top vertices to print at peak timestep (default: 5)",
)
args = parser.parse_args()
# Load model
model = load_model(args.cache_dir)
# Build events DataFrame
print(f"Processing {args.modality} input: {args.input}")
events_df = build_events_dataframe(model, args.input, args.modality)
print(f"Events DataFrame shape: {events_df.shape}")
# Predict
print("Running prediction ...")
preds, segments = model.predict(events=events_df)
print(f"Prediction complete. Shape: {preds.shape}")
if segments:
print(f"Detected segments: {len(segments)}")
for i, seg in enumerate(segments[:5]):
print(f" Segment {i}: {seg}")
if len(segments) > 5:
print(f" ... and {len(segments) - 5} more")
# Print top vertices
print_top_vertices(preds, n_top=args.top_n)
# Save output
save_predictions(preds, args.output)
print("\nDone.")
return 0
if __name__ == "__main__":
sys.exit(main())
#!/usr/bin/env python3
"""
run_experiment.py — Batch stimulus experiment runner using TRIBE v2.
Runs TRIBE v2 on all stimuli in a directory, computes per-stimulus region-averaged
activations, and outputs a summary CSV for downstream analysis.
Usage:
python run_experiment.py --stimuli-dir stimuli/faces/ --modality video --output-dir results/
python run_experiment.py --stimuli-dir stimuli/ --modality video --output-dir results/ --region FFA_right
python run_experiment.py --stimuli-dir stimuli/ --modality audio --output-dir results/ --list-regions
Output:
<output-dir>/experiment_summary.csv
Columns: stimulus_file, region, mean_activation, peak_activation,
peak_timestep, n_timesteps, n_vertices_in_region
<output-dir>/activations/<stimulus_name>.npy (optional, raw preds)
"""
import argparse
import sys
import json
from pathlib import Path
import numpy as np
import pandas as pd
# fsaverage5 cortical region definitions
# Format: region_name -> (vertex_start, vertex_end)
# All indices are into the combined (left+right hemisphere) array of 20,484 vertices
# Left hemisphere: 0–10,241 | Right hemisphere: 10,242–20,483
BRAIN_REGIONS = {
# ── Visual cortex ──────────────────────────────────────────────────────────
"V1_left": (0, 1500),
"V2V3_left": (1500, 2500),
"V4_ventral_left": (2500, 3000),
"MT_left": (2800, 3200), # motion-selective
"LOC_left": (4200, 5200), # lateral occipital complex
"FFA_left": (1100, 1600), # fusiform face area (smaller, left)
"PPA_left": (1600, 2100), # parahippocampal place area
"EBA_left": (5200, 5700), # extrastriate body area
"V1_right": (10242, 11742),
"MT_right": (13042, 13442),
"LOC_right": (14442, 15442),
"FFA_right": (9900, 10400), # dominant face region
"PPA_right": (10400, 11000),
"EBA_right": (15442, 15942),
# ── Auditory cortex ────────────────────────────────────────────────────────
"A1_left": (3500, 4200), # primary auditory cortex
"belt_auditory_left": (4200, 4800), # auditory belt (pitch, timbre)
"STS_left": (4800, 5300), # superior temporal sulcus
"A1_right": (13742, 14442),
"belt_auditory_right": (14442, 15000),
"STS_right": (15000, 15500),
# ── Language network ───────────────────────────────────────────────────────
"Broca_left": (6200, 6800), # IFG pars triangularis / opercularis
"Wernicke_left": (5700, 6200), # posterior STG / MTG
"VWFA_left": (7100, 7500), # visual word form area
"Broca_right": (16442, 17042), # typically weaker
"Wernicke_right": (15942, 16442),
# ── Default Mode Network ───────────────────────────────────────────────────
"mPFC_left": (8000, 8600), # medial prefrontal cortex
"PCC_left": (9000, 9500), # posterior cingulate cortex
"angular_gyrus_left": (7500, 8000),
"mPFC_right": (18200, 18800),
"PCC_right": (19200, 19700),
"angular_gyrus_right": (17700, 18200),
# ── Motor / Somatosensory ──────────────────────────────────────────────────
"M1_left": (8600, 9000), # primary motor cortex
"S1_left": (9100, 9600), # primary somatosensory cortex
"M1_right": (18800, 19200),
"S1_right": (19200, 19600),
# ── Prefrontal cortex ──────────────────────────────────────────────────────
"DLPFC_left": (6800, 7100), # dorsolateral PFC
"OFC_left": (9600, 10000), # orbitofrontal cortex
"DLPFC_right": (17042, 17342),
"OFC_right": (19600, 20000),
}
# File extensions that TRIBE v2 accepts per modality
MODALITY_EXTENSIONS = {
"video": {".mp4", ".avi", ".mov", ".mkv", ".webm"},
"audio": {".wav", ".flac", ".mp3", ".ogg"},
"text": {".txt", ".md", ".json"},
}
def load_model(cache_dir: str):
"""Load TRIBE v2 model from HuggingFace."""
try:
from tribev2 import TribeModel # type: ignore[import-untyped]
except ImportError:
print("ERROR: tribev2 not installed.", file=sys.stderr)
print("Install with:", file=sys.stderr)
print(" git clone https://github.com/facebookresearch/tribev2", file=sys.stderr)
print(" cd tribev2 && pip install -e .", file=sys.stderr)
sys.exit(1)
print(f"Loading TRIBE v2 (cache: {cache_dir}) ...")
model = TribeModel.from_pretrained("facebook/tribev2", cache_folder=cache_dir)
print("Model loaded.\n")
return model
def get_stimuli_files(stimuli_dir: str, modality: str) -> list:
"""Return sorted list of stimulus files matching the modality."""
d = Path(stimuli_dir)
if not d.exists():
print(f"ERROR: Stimuli directory not found: {stimuli_dir}", file=sys.stderr)
sys.exit(1)
valid_exts = MODALITY_EXTENSIONS.get(modality, set())
files = sorted([f for f in d.iterdir() if f.is_file() and f.suffix.lower() in valid_exts])
if not files:
print(f"ERROR: No {modality} files found in {stimuli_dir}", file=sys.stderr)
print(f"Expected extensions: {', '.join(sorted(valid_exts))}", file=sys.stderr)
sys.exit(1)
return files
def predict_stimulus(model, stimulus_path: Path, modality: str) -> tuple:
"""Run TRIBE v2 prediction on one stimulus. Returns (preds, segments)."""
modality = modality.lower()
if modality == "video":
df = model.get_events_dataframe(video_path=str(stimulus_path))
elif modality == "audio":
df = model.get_events_dataframe(audio_path=str(stimulus_path))
elif modality == "text":
df = model.get_events_dataframe(text_path=str(stimulus_path))
else:
raise ValueError(f"Unknown modality: {modality}")
preds, segments = model.predict(events=df)
return preds, segments
def compute_region_stats(preds: np.ndarray, regions: dict) -> list:
"""
Compute per-region activation statistics for one stimulus.
Returns list of dicts with keys:
region, mean_activation, peak_activation, peak_timestep, n_vertices_in_region
"""
records = []
for region_name, (v_start, v_end) in regions.items():
# Guard against out-of-bounds
actual_end = min(v_end, preds.shape[1])
actual_start = min(v_start, preds.shape[1])
if actual_start >= actual_end:
continue
roi_preds = preds[:, actual_start:actual_end] # (n_timesteps, n_roi_vertices)
roi_mean_over_time = roi_preds.mean(axis=1) # (n_timesteps,)
peak_t = int(np.argmax(roi_mean_over_time))
mean_activation = float(roi_mean_over_time.mean())
peak_activation = float(roi_mean_over_time[peak_t])
records.append({
"region": region_name,
"mean_activation": mean_activation,
"peak_activation": peak_activation,
"peak_timestep": peak_t,
"n_vertices_in_region": actual_end - actual_start,
})
return records
def save_raw_predictions(preds: np.ndarray, stimulus_path: Path, output_dir: Path):
"""Save raw (n_timesteps, n_vertices) predictions as .npy."""
raw_dir = output_dir / "activations"
raw_dir.mkdir(parents=True, exist_ok=True)
out_file = raw_dir / f"{stimulus_path.stem}.npy"
np.save(out_file, preds)
return out_file
def main():
parser = argparse.ArgumentParser(
description="TRIBE v2 batch experiment: run predictions on a directory of stimuli.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Run all videos, output summary for all regions
python run_experiment.py --stimuli-dir stimuli/ --modality video --output-dir results/
# Filter to a specific region
python run_experiment.py --stimuli-dir stimuli/ --modality video --output-dir results/ --region FFA_right
# Run on audio, save raw predictions
python run_experiment.py --stimuli-dir stimuli/ --modality audio --output-dir results/ --save-raw
# List available regions
python run_experiment.py --list-regions
""",
)
parser.add_argument(
"--stimuli-dir",
help="Directory containing stimulus files (all files matching the modality are processed)",
)
parser.add_argument(
"--modality", choices=["video", "audio", "text"],
help="Stimulus modality",
)
parser.add_argument(
"--output-dir",
help="Directory for output files",
)
parser.add_argument(
"--region", default=None,
help=(
"Optional: restrict output to a specific region name "
"(e.g., FFA_right, Broca_left). Run --list-regions to see options."
),
)
parser.add_argument(
"--cache-dir", default="./tribe2-cache",
help="Directory to cache model weights (default: ./tribe2-cache)",
)
parser.add_argument(
"--save-raw", action="store_true",
help="Save raw (n_timesteps, n_vertices) .npy arrays for each stimulus",
)
parser.add_argument(
"--list-regions", action="store_true",
help="List all available region names and exit",
)
args = parser.parse_args()
# Handle --list-regions
if args.list_regions:
print("Available regions (fsaverage5 vertex ranges):")
print(f"\n {'Region':<30} {'Start':>8} {'End':>8} {'N vertices':>12}")
print(f" {'-'*30} {'-'*8} {'-'*8} {'-'*12}")
for name, (v_start, v_end) in sorted(BRAIN_REGIONS.items()):
print(f" {name:<30} {v_start:>8} {v_end:>8} {v_end - v_start:>12}")
return 0
# Validate required args
if not args.stimuli_dir or not args.modality or not args.output_dir:
parser.error("--stimuli-dir, --modality, and --output-dir are required unless --list-regions is used")
# Validate region filter
if args.region and args.region not in BRAIN_REGIONS:
print(f"ERROR: Unknown region '{args.region}'", file=sys.stderr)
print(f"Run with --list-regions to see available regions.", file=sys.stderr)
sys.exit(1)
# Determine which regions to report
if args.region:
active_regions = {args.region: BRAIN_REGIONS[args.region]}
else:
active_regions = BRAIN_REGIONS
# Discover stimuli
stimulus_files = get_stimuli_files(args.stimuli_dir, args.modality)
print(f"Found {len(stimulus_files)} {args.modality} file(s) in {args.stimuli_dir}")
print(f"Reporting on {len(active_regions)} brain region(s)\n")
# Prepare output directory
output_dir = Path(args.output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
# Load model once
model = load_model(args.cache_dir)
# Run experiment
all_results = []
errors = []
for i, stim_path in enumerate(stimulus_files, start=1):
print(f"[{i}/{len(stimulus_files)}] Processing: {stim_path.name}")
try:
preds, segments = predict_stimulus(model, stim_path, args.modality)
print(f" Prediction shape: {preds.shape}")
region_stats = compute_region_stats(preds, active_regions)
for stat in region_stats:
stat["stimulus_file"] = stim_path.name
all_results.extend(region_stats)
if args.save_raw:
raw_path = save_raw_predictions(preds, stim_path, output_dir)
print(f" Raw saved: {raw_path}")
except Exception as exc:
print(f" ERROR processing {stim_path.name}: {exc}", file=sys.stderr)
errors.append({"stimulus_file": stim_path.name, "error": str(exc)})
# Build summary DataFrame
if not all_results:
print("ERROR: No predictions succeeded.", file=sys.stderr)
sys.exit(1)
df = pd.DataFrame(all_results)
# Reorder columns for clarity
cols = ["stimulus_file", "region", "mean_activation", "peak_activation",
"peak_timestep", "n_vertices_in_region"]
df = df[cols]
df = df.sort_values(["stimulus_file", "region"]).reset_index(drop=True)
# Save summary CSV
summary_path = output_dir / "experiment_summary.csv"
df.to_csv(summary_path, index=False)
print(f"\nSummary saved: {summary_path} ({len(df):,} rows)")
# Save errors if any
if errors:
error_path = output_dir / "errors.json"
with open(error_path, "w") as f:
json.dump(errors, f, indent=2)
print(f"Errors ({len(errors)}): {error_path}")
# Print pivot table for quick inspection
print("\n--- Mean Activation by Stimulus × Region (top 10 regions by variance) ---")
try:
pivot = df.pivot_table(
index="stimulus_file", columns="region",
values="mean_activation", aggfunc="mean"
)
# Show only regions with highest variance across stimuli (most informative)
region_variance = pivot.var(axis=0).nlargest(min(10, len(pivot.columns))) # type: ignore[union-attr]
pivot_display = pivot[region_variance.index]
print(pivot_display.to_string(float_format=lambda x: f"{x:.4f}")) # type: ignore[arg-type]
except Exception:
# Pivot may fail with single stimulus or single region; just skip
print("(pivot table unavailable for this result shape)")
print("\nExperiment complete.")
return 0
if __name__ == "__main__":
sys.exit(main())
Related skills
FAQ
What is TRIBE v2?
A brain encoding model from Meta FAIR that predicts fMRI BOLD responses on the fsaverage5 cortical surface from multimodal input; it is not a language model.
Do I need a scanner?
No. It predicts cortical responses on any hardware without an fMRI scanner.