Weiter zum Inhalt

TRIBE v2 Tutorial: Simulation menschlicher Gehirnaktivität aus Video, Audio und Text

Lerne, wie du Metas TRIBE v2 auf Google Colab ausführst, kortikale Aktivität aus natürlichen Reizen vorhersagst und die Ergebnisse als interaktive 3D-Gehirn-Heatmaps visualisierst.
Aktualisiert 18. Sept. 2026  · 15 Min. lesen

Entdecke KI

ChatGPTClaudePerplexity

Echte fMRT-Experimente kosten 1.000 bis 3.000 US-Dollar pro Stunde Scannerzeit, erfordern monatelange Planung und liefern dennoch verrauschte Aufnahmen, die durch Herzschlag und Bewegungsartefakte verzerrt sind. Was wäre, wenn du ein Neuroexperiment in Minuten durchführen könntest?

Metas KI-Modell TRIBE v2, ein trimodales Foundation Model, macht das möglich: Es sagt die gesamte fMRT-Gehirnaktivität aus Video-, Audio- und Texteingaben vorher. Es wurde auf über 1.100 Stunden fMRT-Aufnahmen von 720 Probanden trainiert und ist als Open Source unter CC-BY-NC verfügbar.

In diesem Tutorial wirst du:

  • Verstehen, was TRIBE v2 ist und wie die Architektur funktioniert
  • Inference auf Text-, Audio- und Videoeingaben ausführen
  • Vorhergesagte kortikale Aktivität als interaktive 3D-Gehirn-Heatmaps mit nilearn visualisieren
  • Ein In-silico-Vergleichsexperiment zwischen Sprachinhalt und visuell/räumlichem Inhalt durchführen
  • Eine Gradio-Demo starten 

Was ist TRIBE v2?

TRIBE v2 (TRImodal Brain Encoder) ist ein Deep-Learning-Modell, das natürliche Reize auf vorhergesagte fMRT-Gehirnantworten abbildet. Aus einem Videoclip, einer Audiodatei oder einem Textblock gibt das Modell ein vorhergesagtes BOLD-Signal für jeden der 20.484 Vertices auf der fsaverage5-kortikalen Oberfläche mit 1 Hz aus, also eine Vorhersage pro Sekunde.

Die Vorhersagen gelten für das durchschnittliche Subjekt (nicht für das Gehirn einer spezifischen Person), genauer: die kanonische Gruppenmittelwert-Antwort, die TRIBE v2 aus 720 Teilnehmenden über vier naturalistische Datensätze gelernt hat. Die Zero-shot-Vorhersagen des Modells übertreffen Einzelsubjekt-fMRT-Aufnahmen im Human Connectome Project 7T-Datensatz, der die höchste Signalqualität im Training aufweist.

Wichtigste Eigenschaften

Eigenschaft

Details

Ausgaberaum

20.484 kortikale Vertices auf der fsaverage5-Oberfläche und Ganzhirnvorhersagen über ungefähr 70.000 Voxel (Kortex + Subkortex)

Zeitliche Auflösung

1 Hz (entspricht der fMRT-TR-Frequenz)

Eingabemodalitäten

Video (V-JEPA2-Giant), Audio (Wav2Vec-BERT 2.0), Text (LLaMA 3.2-3B)

Encoder-Parameter

~1B lernbare Parameter in der Transformer-Integrationsschicht

Trainingsdaten

1.115 Stunden fMRT von 720 Probanden über 4 Datensätze

Generalisierung

Zero-shot auf neue Subjekte, Aufgaben und Sprachen

Lizenz

CC-BY-NC 4.0 (Forschung, nicht kommerziell)

Das Modell basiert auf dem Paper A foundation model of vision, audition, and language for in-silico neuroscience. Es zeigt, dass TRIBE v2 die Fusiform Face Area für Gesichter, die Parahippocampal Place Area für Szenen, Brocas Areal für komplexe Syntax und das linksseitig lokalisierte Sprachnetzwerk für Sprache wiederfindet – ganz ohne fMRT-Daten zur Inferenzzeit. 

Überblick über die TRIBE v2 Architektur

TRIBE v2 durchläuft bei jedem Inferenzaufruf drei aufeinanderfolgende Stufen:

TRIBE v2 brain activity prediction model

Abbildung: TRIBE v2 Modell zur Vorhersage von Gehirnaktivität (Mit KI generiert)

Stufe 1: Feature-Extraktion (eingefroren)

Zunächst verarbeiten drei separat vortrainierte Encoder jede Eingabemodalität unabhängig zu dichten, zeitlich ausgerichteten Embeddings. Keiner dieser Encoder wird im Training aktualisiert (eingefroren), daher übernimmt TRIBE v2 ihre Repräsentationen unverändert. Einige Kennzahlen zur Feature-Extraktion je Modalität:

  • Text: LLaMA 3.2-3B konvertiert Eingabetext in dichte Embeddings (D = 2048)

  • Audio: Wav2Vec-BERT 2.0 codiert Audiosignale mit ~2 Hz (D = 1024)

  • Video: V-JEPA2-Giant verarbeitet Bildfolgen zu zeitlichen Merkmalen (D = 1280)

Stufe 2: Universelle Integration (gelernt)

Die drei Embedding-Ströme werden zu einer gemeinsamen Repräsentation fusioniert und von einem Transformer über die Zeit verarbeitet. Hier liegen die gelernten Gewichte von TRIBE v2 und hier werden Cross-Modal-Interaktionen wie folgt erfasst:

  • Geteilte Repräsentation: Alle Modalitäts-Embeddings werden in einen einheitlichen Raum projiziert (D_model = 1152)

  • Transformer-Fusion: Ein 8-lagiger, 8-köpfiger Transformer integriert Signale über ein langes Kontextfenster (~100 s)

  • Modalitäts-Flexibilität: Modality Dropout (p = 0,3) ermöglicht Inferenz mit beliebigen Teilmengen (Text/Audio/Video)

Stufe 3: Gehirnabbildung (gelernt)

Die fusionierte latente Repräsentation wird auf die kortikale Oberfläche projiziert, um die finale fMRT-Vorhersage zu erzeugen. Diese Stufe übersetzt abstrakte Modellmerkmale in räumlich und zeitlich aufgelöste Schätzungen der Gehirnaktivität. 

  • Zeitliche Ausrichtung: Outputs werden auf 1 Hz ausgerichtet und abgetastet, passend zum fMRT-Timing
  • Kortikale Projektion: Eine subjektkonditionierte lineare Schicht mappt Features auf Gehirnoberflächen-Vertices
  • Endausgabe: Eine Matrix (T, 20484) repräsentiert vorhergesagte Gehirnaktivität über die Zeit

Da die drei Feature-Extractoren im Training eingefroren sind, lernt TRIBE v2 nur die Projektionsschichten und Transformer-Gewichte, die deren Ausgaben integrieren. Das ist wichtig, weil das Modell dadurch robuster gegenüber Out-of-Distribution-Reizen ist: Es erbt die Generalisierung dreier großskaliger, vortrainierter Modelle statt End-to-End nur auf fMRT-Daten zu trainieren.

Hinweis: Ein zentrales Trainingstrick ist Modalitäts-Dropout. Während des Trainings wird jede Modalität mit Wahrscheinlichkeit 0,3 unabhängig genullt. Das zwingt das Modell, aus jeder Teilmenge sinnvolle Vorhersagen zu machen. Zur Inferenzzeit kannst du also nur Audio oder nur Text übergeben und erhältst dennoch eine brauchbare kortikale Vorhersage.

Deep Learning in Python beherrschen

Erwerbe gefragte Deep Learning-Fähigkeiten mit Python.
Kostenloses Lernen Beginnen

TRIBE v2 Demo: Gehirnantworten vorhersagen

In diesem Abschnitt erstellen wir einen Schritt-für-Schritt-Workflow, der TRIBE v2 auf Text-, Audio- oder Videoeingaben ausführt und die vorhergesagte kortikale Aktivität als interaktive 3D-Gehirn-Heatmap visualisiert. Außerdem führen wir ein Vergleichsexperiment durch, das das In-silico-Paradigma aus dem Originalpaper repliziert. Abschließend entwickeln wir eine Gradio-App, mit der jede Person die Demo live erkunden kann.

Schritt 1: Voraussetzungen und Hardware

Bevor du beginnst, konfiguriere deine Colab-Runtime. Du kannst auch jeden anderen Dienst mit stabiler A100-GPU und viel RAM verwenden.

  • Öffne Runtime und wähle change runtime type
  • Wähle A100 GPU und aktiviere High RAM
  • Klicke auf Save

TRIBE v2 lädt gleichzeitig drei eingefrorene Encoder, darunter LLaMA 3.2-3B (~7 GB), V-JEPA2-Giant (~14 GB) und Wav2Vec-BERT 2.0 (~1 GB), zusammen mit den TRIBE-Transformer-Gewichten. Der gesamte VRAM-Bedarf liegt bei 28–32 GB. 

Hinweis: Eine T4 (16 GB) läuft beim Laden von LLaMA in model.predict() in einen Speichermangel. Nutze die A100 (40 GB) oder A100 mit High RAM (80 GB) für bessere Performance.

Überprüfe deine GPU, bevor du etwas installierst, mit folgendem Code:

import subprocess, sys
result = subprocess.run(
    ['nvidia-smi', '--query-gpu=name,memory.total',
     '--format=csv,noheader,nounits'],
    capture_output=True, text=True)
print(result.stdout.strip())
import torch
assert torch.cuda.is_available(), "No GPU detected"
props = torch.cuda.get_device_properties(0)
assert props.total_memory > 30e9, (
    f"Need ≥40 GB VRAM. Got {props.total_memory/1e9:.0f} GB. Switch to A100.")
print(f"GPU: {props.name} — {props.total_memory/1e9:.0f} GB")

Der Aufruf subprocess.run() startet nvidia-smi mit dem Flag --query-gpu, um GPU-Name und gesamten VRAM auszulesen. Die beiden Assert-Statements dienen als frühe Abbrüche: Das erste bestätigt CUDA-Verfügbarkeit, das zweite verifiziert, dass der VRAM 30 GB übersteigt. Ein lautes Scheitern hier ist besser als ein späterer, kryptischer CUDA-Out-of-Memory-Fehler in model.predict() nach 10 Minuten.

Schritt 2: NumPy-Versionskonflikt beheben

Überspringe diesen Schritt, wenn du nicht auf Google Colab arbeitest. Dies ist der erste Bug, auf den du stößt, da Colab standardmäßig mit NumPy 2.x ausgeliefert wird. Mehrere interne Abhängigkeiten von TRIBE v2, insbesondere neuralset, wurden gegen NumPy <2.1 kompiliert, wodurch das Symbol _center aus numpy._core.umath entfernt wurde. Das führt zu folgendem Fehler beim import tribev2:

ImportError
cannot import name '_center' from 'numpy._core.umath'
(/usr/local/lib/python3.12/dist-packages/numpy/_core/umath.py)

Die Lösung ist, NumPy vor der Installation von tribev2 oder dessen Abhängigkeiten auf <2.1 zu pinnen und dann die Runtime neu zu starten. Führe einfach diese Zelle aus. Sie deinstalliert das aktuelle NumPy und ersetzt es durch eine Version unter 2.1. 

import subprocess, sys
print("Pinning NumPy to <2.1 (required for neuralset compatibility)...")
subprocess.run([sys.executable, '-m', 'pip', 'uninstall', '-y', 'numpy'])
subprocess.run([sys.executable, '-m', 'pip', 'install', '-q',
                'numpy>=1.26.4,<2.1.0'])

Sobald Umgebung und Abhängigkeiten stehen, können wir TRIBE v2 installieren.

Schritt 3: TRIBE v2 installieren 

Mit frisch neu gestartetem Kernel und gepinntem NumPy installieren wir nun sicher das tribev2-Paket von GitHub sowie Visualisierungs- und UI-Bibliotheken. 

import numpy as np
from packaging.version import Version
assert Version(np.__version__) < Version('2.1.0'), (
    f"NumPy is {np.__version__}. Run Step 2a and restart first.")
print(f"NumPy {np.__version__} Checked")
# Install tribev2 from GitHub 
!pip install -q 'tribev2[plotting] @ git+https://github.com/facebookresearch/tribev2.git'
!pip install -q 'gradio>=4.19.0' 'nilearn>=0.10.3' 'plotly>=5.18.0'

Das Extra tribev2[plotting] installiert pyvista für 3D-Visualisierung und nilearn für Neuroimaging zusätzlich zum Kernpaket. Die direkte Installation aus der GitHub-URL stellt sicher, dass du den neuesten Commit bekommst, ohne das Repository lokal zu klonen. 

Die Pakete nilearn und gradio werden separat installiert, da ihre Versionsconstraints flexibler sind und unabhängig vom tribev2-Abhängigkeitsgraphen besser aufgelöst werden.

Schritt 4: HuggingFace-Authentifizierung

Der Text-Encoder nutzt LLaMA 3.2-3B, ein gated Modell auf HuggingFace. Du musst Metas Lizenz explizit akzeptieren, bevor die Gewichte heruntergeladen werden können. Einmalig durchführen:

  • Besuche HuggingFace und klicke auf Accept license
  • Erstelle unter Settings/Access Tokens ein Read-Token 
  • In Colab klicke links auf das Schlüssel-Icon, wähle Add secret. Nenne dein Token „HF_TOKEN“ und setze „value: dein Token“.

Sobald dein HF-Token gesetzt ist, melde dich mit folgendem Code an:

import os
# Load token from Colab Secrets
try:
    from google.colab import userdata
    os.environ['HF_TOKEN'] = userdata.get('HF_TOKEN')
    print("HF_TOKEN loaded from Colab Secrets")
except Exception:
    from huggingface_hub import login
    login()

Der bevorzugte Pfad nutzt google.colab.userdata.get() und liest aus Colabs verschlüsseltem Secrets-Store, der in geteilten Notebooks nicht versehentlich offengelegt werden kann.

Der Fallback ruft huggingface_hub.login() auf, fragt interaktiv nach dem Token und maskiert die Eingabe. Beide Wege schreiben das Token nach os.environ['HF_TOKEN'], wo die HuggingFace-Hub-Bibliothek es beim Download der gated Modellgewichte automatisch findet.

Schritt 5: Vortrainiertes Modell laden

Mit gepinntem NumPy, eingerichteter Authentifizierung und zwischengespeichertem LLaMA laden wir nun den TRIBE v2 Encoder-Checkpoint von HuggingFace. Beim ersten Lauf werden ca. 1 GB heruntergeladen, spätere Läufe kommen aus dem Cache.

from pathlib import Path
from tribev2.demo_utils import TribeModel
import torch
CACHE_DIR = Path('/content/tribe_cache')
CACHE_DIR.mkdir(exist_ok=True)
print('Loading TRIBE v2 (first run downloads ~1 GB)...')
model = TribeModel.from_pretrained(
    'facebook/tribev2',
    cache_folder=str(CACHE_DIR)
)
print('Model loaded')
if torch.cuda.is_available():
    used  = torch.cuda.memory_allocated() / 1e9
    total = torch.cuda.get_device_properties(0).total_memory / 1e9
    print(f'VRAM after load: {used:.1f} / {total:.1f} GB')

TribeModel.from_pretrained() lädt den TRIBE-Encoder-Checkpoint von facebook/tribev2 auf HuggingFace und speichert ihn in cache_folder. Dieser Checkpoint enthält die Transformer-Integrationsgewichte und den Subject-Block, nicht jedoch die drei Feature-Extractoren. Diese werden separat gezogen, wenn model.predict() eine Modalität erstmals nutzt.

Nach dem Laden nur des TRIBE-Encoders sind ungefähr 2–4 GB VRAM belegt, die übrigen 24–28 GB werden verbraucht, sobald model.predict() V-JEPA2-Giant und LLaMA 3.2-3B beim ersten Gebrauch lädt.

Schritt 6: Download-Timeout beheben

Nach dem Laden des TRIBE-Modells löst der erste Aufruf von model.predict() mit Texteingabe den Lazy-Download der LLaMA 3.2-3B-Gewichte (~6 GB) aus. Das Standard-Timeout des HuggingFace Hubs beträgt 10 Sekunden und führt zu folgendem Fehler mitten in der Inferenz:

ReadTimeout
The read operation timed out
Computing word embeddings:  0%|  | 0/9 [00:10<?, ?it/s]

Setze dafür höhere Timeout-Umgebungsvariablen und lade LLaMA anschließend explizit mit snapshot_download vorab herunter. So siehst du Fortschritt, hast automatisches Resume bei Unterbrechung und vermeidest stille Fehler in predict().

import os
os.environ['HF_HUB_DOWNLOAD_TIMEOUT'] = '300'   
os.environ['HF_HUB_HTTP_TIMEOUT']     = '300' 
from huggingface_hub import snapshot_download
print("Pre-downloading LLaMA 3.2-3B (~6 GB)...")
print("Runs once — subsequent calls load from cache.\n")
snapshot_download(
    repo_id        = "meta-llama/Llama-3.2-3B",
    cache_dir      = "/content/tribe_cache/llama",
    ignore_patterns= ["*.bin"], 
)
print("\n LLaMA 3.2-3B cached")

snapshot_download() lädt ein gesamtes Repository in den lokalen Cache per Range-Request-Protokoll, wodurch Downloads bei Verbindungsabbruch automatisch fortgesetzt werden. Mit ignore_patterns=["*.bin"] werden ältere PyTorch-Binärdateien übersprungen und nur Safetensors geladen – das reduziert die Gesamtgröße um rund 40 %.

Schritt 7: Helfer für Gehirnvisualisierung

Bevor wir echte Inferenz laufen lassen, richten wir die Visualisierungsebene ein. Diese Helferfunktionen konvertieren das rohe (T, 20484)-Vorhersagearray in interaktive 3D-Gehirn-Heatmaps mit nilearn. 

TRIBE v2 liefert Vorhersagen als NumPy-Array der Form (T, 20484), wobei T die Anzahl Sekunden der Eingabe ist. Die ersten 10.242 Vertices gehören zur linken Hemisphäre, die restlichen 10.242 zur rechten. 

Wir nutzen nilearn.plotting.view_surf, um jede Hemisphäre als interaktive WebGL-Oberfläche zu rendern. Das „inflated“ Mesh legt Sulci frei, die in den Falten verborgen wären, und die Sulcal-Tiefenkarte dient als anatomische Referenz unter der Heatmap.

Schritt 7.1: fsaverage5-Mesh herunterladen

Das fsaverage5-Mesh ist das Standard-FreeSurfer-Kortikaltemplate, das TRIBE v2 als Ausgaberaum nutzt. Wir laden es hier einmal herunter, damit spätere Visualisierungsaufrufe ohne erneute Netzwerkanfrage darauf zugreifen können.

import numpy as np
from nilearn import datasets as nl_datasets
from nilearn.plotting import view_surf
from IPython.display import display, HTML
N_PER_HEMI = 10242   # fsaverage5: 10242 vertices per hemisphere
print('Fetching fsaverage5 mesh...')
fsavg = nl_datasets.fetch_surf_fsaverage(mesh='fsaverage5')
print('Mesh ready')
print('Keys:', [k for k in fsavg.keys() if k != 'description'])

Der Aufruf fetch_surf_fsaverage(mesh='fsaverage5') lädt das FreeSurfer-Template fsaverage5 vom CDN von nilearn und cached es. Er liefert ein Bunch-Objekt (Dictionary) mit Schlüsseln wie infl_left, infl_right, sulc_left und sulc_right. 

Schritt 7.2: Hemisphären aufteilen und rendern

Dieser Teil definiert drei Kernfunktionen, auf denen die gesamte Visualisierung im Tutorial basiert. split_hemis() teilt den Vertex-Vektor, render_hemi() baut die interaktive WebGL-Ansicht für eine Hemisphäre und show_brain() setzt beides nebeneinander zusammen.

def split_hemis(v):
    n = v.shape[0]
    if n == 2 * N_PER_HEMI:
        return v[:N_PER_HEMI], v[N_PER_HEMI:]
    return v[:n//2], v[n//2:]  
def render_hemi(pred_vec, hemi='left', title=''):
    lh, rh = split_hemis(pred_vec)
    data = lh if hemi == 'left' else rh
    vmax = max(float(np.percentile(np.abs(data), 99)), 1e-6)
    return view_surf(
        surf_mesh = fsavg[f'infl_{hemi}'],   
        surf_map  = data,
        bg_map    = fsavg[f'sulc_{hemi}'],   
        hemi      = hemi,
        threshold = '20%',  
        cmap      = 'hot',    
        black_bg  = True,
        vmax      = vmax,
        bg_on_data= True,     
        colorbar  = True,
        title     = title,
    )
def show_brain(pred_vec, title='', t=None):
    sfx = f' — t={t}s' if t is not None else ''
    lv  = render_hemi(pred_vec, 'left',  f'{title} [Left]{sfx}')
    rv  = render_hemi(pred_vec, 'right', f'{title} [Right]{sfx}')
    html = (
        '<div style="display:flex;gap:10px;background:#000;'
        'border-radius:10px;">'
        f'<div style="flex:1">{lv.get_iframe(width="100%",height="460px")}</div>'
        f'<div style="flex:1">{rv.get_iframe(width="100%",height="460px")}</div>'
        '</div>'
    )
    display(HTML(html))

Das leisten die Helfer im Detail:

  • split_hemis() schneidet den Vorhersagevektor bei Index 10.242 – dem Standardtrennpunkt des fsaverage5-Mesh nach FreeSurfer-Konvention. Die linke Hemisphäre belegt Indizes 0–10241, die rechte 10242–20483. Der Fallback unten behandelt Sonderfälle mit abweichender Vertexzahl.

  • In render_hemi() wird vmax als 99. Perzentil der absoluten Aktivierungen statt als echtes Maximum berechnet. So verhindert man, dass ein Extremwert die gesamte Farbskala zusammendrückt, und macht das räumliche Muster sichtbar. 

  • view_surf() liefert ein SurfaceView-Objekt mit 2,4 MB selbstenthaltener WebGL-HTML. get_iframe() kapselt dies in einem <iframe> mit vorgegebener Größe. display(HTML(...)) mit zwei Iframes nebeneinander erzeugt das Split-Layout links/rechts.

Mit geladenem Modell und Visualisierungshilfen können wir die erste echte Inferenz ausführen. 

Schritt 8: Inferenz ausführen

Die Inferenz von TRIBE v2 erfolgt in zwei Schritten. Zuerst extrahiert model.get_events_dataframe() zeitlich ausgerichtete Events aus der Eingabe – inklusive Wortzeiten aus Text, Wav2Vec-Embeddings mit 2 Hz aus Audio oder V-JEPA2-Embeddings mit 2 Hz aus Videoframes. 

Das resultierende Events-DataFrame wird dann an model.predict() übergeben, das den Transformer und den Subject-Block ausführt, um die endgültigen kortikalen Vorhersagen zu erzeugen.

import tempfile, os
SAMPLE_TEXT = '''
The brain processes language through a distributed network in the left hemisphere.
Broca's area coordinates syntactic structure, while Wernicke's area handles semantics.
Together they form the language circuit activated when reading or hearing speech.
'''
tmp = tempfile.NamedTemporaryFile(delete=False, suffix='.txt', mode='w')
try:
    tmp.write(SAMPLE_TEXT.strip())
    tmp.flush()
    os.fsync(tmp.fileno())   
    tmp.close()
    events = model.get_events_dataframe(text_path=tmp.name)
finally:
    if os.path.exists(tmp.name):
        os.unlink(tmp.name) 
print(f'Events: {events.shape}')
print(events[['type', 'start', 'duration']].head(8))
print('\nRunning model.predict()...')
preds, segments = model.predict(events=events)
preds = np.asarray(preds)
print(f'Prediction shape: {preds.shape}')
print(f'  T = {preds.shape[0]}s   (1 Hz fMRI frequency)')
print(f'  V = {preds.shape[1]} vertices  (fsaverage5 cortical surface)')

Die Sequenz tmp.write(), tmp.flush(), os.fsync(tmp.fileno()), tmp.close() behebt einen subtilen Fehler: Wenn du get_events_dataframe() in einem with-Block aufrufst, bevor die Datei geschlossen ist, könnte Pythons interner Schreibpuffer noch nicht mit dem OS synchronisiert sein – tribev2 liest dann eine leere Datei und wirft ValueError. os.fsync() stellt sicher, dass der OS-Seiten-Cache vor dem Öffnen durch tribev2 auf die Platte geschrieben ist.

model.predict() gibt ein Tupel (preds, segments) zurück. preds hat die Form (T, 20484), also eine kortikale Vorhersage pro Sekunde Eingabe über alle 20.484 fsaverage5-Vertices. np.asarray() stellt sicher, dass es sich unabhängig vom internen Typ um ein NumPy-Array handelt. Mit preds kannst du die kortikale Antwort zu jedem Zeitpunkt visualisieren:

T = preds.shape[0]
print(f'Timesteps: 0 to {T-1}')
T_SHOW = min(5, T - 1)
show_brain(preds[T_SHOW], title='Language stimulus', t=T_SHOW)

Standardmäßig nutzen wir t=5, weil das BOLD-Signal (Blood-Oxygen-Level-Dependent) eine hämodynamische Verzögerung hat und die vaskuläre Antwort etwa 5–6 Sekunden nach Reizbeginn ihren Peak erreicht. Bei t=0 ist die Aktivierung nahezu null – unabhängig vom Reizinhalt. Die Schutzklausel min(5, T-1) verhindert Indexfehler bei weniger als 6 Zeitschritten.

TRIBE v2 Output for single text

Schritt 9: Vergleichsexperiment

Eine einzelne Aktivierungskarte zeigt, welche Areale aktiv sind, aber nicht, wodurch sich Reize unterscheiden. In diesem Schritt schicken wir zwei Eingaben durch das Modell und berechnen eine Kontrastkarte (A − B), um regionsspezifische Unterschiede zwischen Sprach- und visuell/räumlichem Inhalt zu isolieren.

Schritt 9.1: Wiederverwendbaren Inferenz-Helfer definieren

Anstatt das Muster Schreiben -> Flush -> Close -> Inferenz für jede Bedingung zu wiederholen, kapseln wir es in text_to_preds(). So gehen die kritischen Flush-Schritte nie verloren.

TEXT_A = '''
She spoke slowly and clearly, her voice filling the quiet room.
Every sentence carried meaning, and each word was chosen with care.
Language connects us, the professor said, bridging minds across time.
'''
TEXT_B = '''
The canyon walls rose steeply, layers of red and orange sandstone.
A hawk circled overhead, its wings barely moving in the thermal current.
Shadows shifted as the sun tracked its arc across the open desert sky.
'''
def text_to_preds(text):
    tmp = tempfile.NamedTemporaryFile(
        delete=False, suffix='.txt', mode='w', encoding='utf-8')
    try:
        tmp.write(text.strip())
        tmp.flush()
        os.fsync(tmp.fileno())
        tmp.close()
        evts = model.get_events_dataframe(text_path=tmp.name)
        p, _ = model.predict(events=evts)
        return np.asarray(p)
    finally:
        if os.path.exists(tmp.name):
            os.unlink(tmp.name)
print('Condition A: language content...')
preds_a = text_to_preds(TEXT_A)
print('Condition B: visual/spatial content...')
preds_b = text_to_preds(TEXT_B)

Wir nutzen zwei Textpassagen mit unterschiedlicher Semantik. Erwartungsgemäß treibt Sprachinhalt die linke Temporalkortex-Seite stärker, während visuell/räumlicher Inhalt den Okzipital- und posterioren Parietalkortex stärker rekrutiert.

Die Funktion text_to_preds() kapselt die gesamte Pipeline und verwendet das sichere Muster aus Schritt 8, damit die temporäre Datei immer vollständig geflusht ist, bevor tribev2 sie liest. encoding='utf-8' ist explizit gesetzt, um plattformspezifische Kodierungsprobleme zu vermeiden.

Schritt 9.2: Rohe Aktivierungen und Kontrast rendern

Nachdem beide Bedingungen vorhergesagt sind, visualisieren wir jede einzeln und subtrahieren sie anschließend Vertex für Vertex, um die Kontrastkarte zu erzeugen. 

T_shared = min(preds_a.shape[0], preds_b.shape[0])
t_show   = min(5, T_shared - 1)
print('\n[A] Language content:')
show_brain(preds_a[t_show], title='Condition A: Language', t=t_show)
print('\n[B] Visual/spatial content:')
show_brain(preds_b[t_show], title='Condition B: Visual', t=t_show)
print('\n[A − B] Contrast: Language > Visual')
show_brain(preds_a[t_show] - preds_b[t_show], title='Contrast A − B', t=t_show)

Die Kontrastkarte preds_a[t_show] - preds_b[t_show] ist eine direkte Vertex-weise Subtraktion: Positive Werte markieren Regionen, in denen Bedingung A stärker ist, negative, in denen Bedingung B stärker ist. 

Da beide Bedingungen denselben Textverarbeitungsweg teilen, ähneln sich die Rohkarten insgesamt. Der Kontrast hebt die domänenspezifischen Unterschiede zwischen Sprache und visuellen Inhalten hervor.

Schritt 9.3: Zeitlichen Unterschied plotten

Die Gehirn-Heatmaps zeigen räumliche Muster zu einem Zeitpunkt. Hier fügen wir die Zeitperspektive hinzu: Wie unterscheidet sich die Gesamtaktivierung über alle Zeitschritte, und wann divergieren die Bedingungen am stärksten? 

import matplotlib.pyplot as plt
diff_norms = [
    np.linalg.norm(preds_a[i] - preds_b[i])
    for i in range(T_shared)
]
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 3.5))
ax1.plot(np.abs(preds_a).mean(axis=1)[:T_shared],
         color='#e74c3c', linewidth=2, label='A: Language')
ax1.plot(np.abs(preds_b).mean(axis=1)[:T_shared],
         color='#3498db', linewidth=2, label='B: Visual')
ax1.set_title('Mean cortical activation over time')
ax1.set_xlabel('Time (s)'); ax1.legend(); ax1.grid(True, alpha=0.3)
ax2.plot(diff_norms, color='#f39c12', linewidth=2)
ax2.fill_between(range(T_shared), diff_norms, alpha=0.2, color='#f39c12')
ax2.set_title('||A − B|| difference over time')
ax2.set_xlabel('Time (s)'); ax2.grid(True, alpha=0.3)
plt.tight_layout(); plt.show()

TRIBE v2 Comparing two text inputs

Der linke Plot zeigt np.abs(preds).mean(axis=1), also die mittlere absolute Aktivierung über alle 20.484 Vertices je Sekunde. So siehst du, wie stark jede Bedingung den Kortex aktiviert und wann die Antwort ihren Höhepunkt erreicht. Der Betrag ist wichtig, weil vorhergesagte BOLD-Werte negativ sein können (Deaktivierung) – wir wollen die Stärke, nicht den Vorzeichenmittelwert.

Der rechte Plot zeigt die L2-Norm des Differenzvektors je Zeitschritt, np.linalg.norm(preds_a[i] - preds_b[i]). Ein Peak um t=5–7 s passt zur hämodynamischen Verzögerung: Beide Bedingungen brauchen Zeit, bis die BOLD-Antwort aufgebaut ist und divergieren. fill_between() macht Onset und Peak der Divergenz visuell klarer.

Schritt 10: Gradio-Demo starten

Zum Abschluss verpacken wir Inferenz und Visualisierung in eine Gradio-App mit aufgeräumter UI, Zeitregler und A/B-Vergleichs-Tab. 

import gradio as gr
_pred_cache = {}   
def _infer(mod, vid, aud, txt):
    """Run inference and cache the result. Subsequent calls return cached array."""
    key = (mod, vid, aud, hash(txt or ''))
    if key not in _pred_cache:
        if mod == 'video':
            evts = model.get_events_dataframe(video_path=vid)
        elif mod == 'audio':
            evts = model.get_events_dataframe(audio_path=aud)
        else:
            tmp = tempfile.NamedTemporaryFile(delete=False, suffix='.txt', mode='w')
            tmp.write((txt or '').strip()); tmp.flush()
            os.fsync(tmp.fileno()); tmp.close()
            evts = model.get_events_dataframe(text_path=tmp.name)
            os.unlink(tmp.name)
        p, _ = model.predict(events=evts)
        _pred_cache[key] = np.asarray(p)
    return _pred_cache[key]
demo.launch(
    share      = True,    
    debug      = False,
    server_name= "0.0.0.0",
)

So greifen Gradio-UI und Inferenz-Pipeline ineinander:

  • _infer() ist die zentrale Inferenzschicht für alle drei Modalitäten (Video, Audio, Text): Inputs vorbereiten, model.predict() aufrufen, vorhergesagte Gehirnaktivität zurückgeben.

  • Ein Prediction-Cache speichert Ergebnisse anhand eines Schlüssels aus Modalität, Pfaden und einem Hash des Texts. Identische Eingaben lösen so keine erneute Inferenz aus.

  • Das Caching ist entscheidend, weil UI-Elemente wie Slider häufig Callbacks triggern. Ohne Cache würde jede Interaktion die Inferenz neu starten (~60 Sekunden), mit Cache kommen Ergebnisse nach dem ersten Lauf sofort.

  • Die Oberfläche bietet zwei Tabs: einen Single-Input-Modus mit Zeitregler zur Erkundung der Aktivität über die Zeit und einen Vergleichsmodus, der zwei Eingaben ausführt und ihren Unterschied als Kontrast-Heatmap visualisiert.

Zum Schluss startet demo.launch() mit share=True eine öffentliche URL und mit server_name="0.0.0.0" externen Zugriff – ideal zum schnellen Teilen.

Beobachtungen und Praxiserkenntnisse zu TRIBE v2

Nach der Demo mit verschiedenen Eingaben (Video, Audio, Text) zeigen sich konsistente Muster, die helfen, die Ausgaben von TRIBE v2 zu interpretieren. Einige Erkenntnisse:

  • Zeitliche Dynamik: Die Gehirnaktivität verändert sich mit dem Input und bleibt nicht statisch. Besonders in den ersten Sekunden bauen sich Aktivierungen auf und verlagern sich – passend zur verzögerten Natur des Signals. Das zeigt, dass das Modell zeitabhängige Antworten erfasst.
  • Einfluss visueller Inputs auf posteriore Regionen: Bei Video-Beispielen treten die stärksten Aktivierungen am Hinterkopf auf – dort liegen visuelle Verarbeitungsareale. Das belegt passende Reaktionen auf visuelle Reize.
  • Kontrastkarten: Beim Vergleich zweier Inputs ist die Differenz-Heatmap oft aussagekräftiger als Einzelkarten. Statt breiter Aktivierungen zeigt der Kontrast, wo das Gehirn auf die jeweiligen Reize unterschiedlich reagiert – das erleichtert die Interpretation.

Häufige Fallstricke

Das Modell ist nicht unfehlbar und bringt typische Stolpersteine mit:

  • Rauschige Karten: Sehr kurze Inputs (wenige Sekunden) führen oft zu diffusen, schwachen Aktivierungen, die schwer zu deuten sind. Nutze 15–30 Sekunden, um genügend Kontext zu liefern.
  • Fehlende Modalitäten: Wenn du Audio oder Text ohne Video nutzt, können Warnungen erscheinen, dass bestimmte Extractor-Zweige entfernt wurden. Das ist normal – ungenutzte Zweige werden deaktiviert und mit den verfügbaren Inputs weitergerechnet.
  • Caching: Ohne Cache würde jede UI-Interaktion (z. B. der Slider) einen vollen Modelllauf auslösen – unbenutzbar. Mit Cache werden Vorhersagen einmal berechnet und wiederverwendet – flüssige, nahezu Echtzeit-Erkundung.
  • Umgebungsinkonsistenzen: Änderungen bei Abhängigkeiten (insbesondere NumPy) oder unsauberes Dateihandling (unflushed Textdateien) können zu stillen Fehlern führen.

Einschränkungen

TRIBE v2 ist ein leistungsfähiges Forschungstool, hat aber wichtige Grenzen, die beeinflussen, wie Ausgaben zu interpretieren sind. Diese zu kennen, ist entscheidend, bevor du wissenschaftliche oder klinische Schlüsse ziehst.

  • Durchschnittssubjekt: Vorhersagen repräsentieren Populationsmittelwerte. Individuelle Gehirne unterscheiden sich in Anatomie, Funktion und Rauschprofil. Fine-Tuning auf ~1 Stunde individueller fMRT-Daten wird unterstützt, liegt aber außerhalb dieses Tutorials.
  • fMRT-Auflösung: Das BOLD-Signal hat ~1 Hz zeitliche und ~4 mm räumliche Auflösung. TRIBE v2 erbt diese Grenzen und erfasst weder Millisekunden-Dynamik noch subgyrale Details.
  • Passiver Beobachter: Das Modell sagt Antworten auf Reize bei passiver Betrachtung vorher. Aufmerksamkeit, Motorik, soziale Interaktion oder aktive kognitive Zustände sind nicht repräsentiert.
  • Modalitätenspektrum: Nur Sehen, Hören und Sprache sind modelliert. Modalitäten wie Geruch, Tastsinn, Propriozeption oder Schmerz fehlen.
  • Kein klinisches Werkzeug: Vorhersagen sind nicht für Diagnose, Therapieplanung oder klinische Anwendungen gedacht.

Fazit

In diesem Tutorial haben wir eine funktionierende TRIBE v2 Pipeline in Google Colab A100 aufgebaut: vom Beheben zweier konkreter Bugs (NumPy-2.x-Konflikt und HuggingFace-Download-Timeout) über echte kortikale Vorhersagen bis zur Visualisierung als interaktive 3D-Gehirn-Heatmaps und einem Vergleichsexperiment, das das In-silico-Paradigma des Papers repliziert.

Die vier wichtigsten Engineering-Learnings sind: 

  1. NumPy auf <2.1 pinnen und die Runtime neu starten, bevor du tribev2 installierst

  2. HF_HUB_DOWNLOAD_TIMEOUT=300 setzen und LLaMA mit snapshot_download vorab ziehen, bevor model.predict() läuft

  3. Temporäre Dateien immer Schreiben → flush() → fsync() → close(), bevor du den Pfad ans Modell übergibst

  4. Vorhersagen im Dictionary cachen, damit UI-Slider keine erneute Inferenz auslösen.

Von hier aus bieten sich zwei naheliegende Erweiterungen an. Erstens reichhaltigere Reize: Echte Filmszenen oder Podcast-Ausschnitte über 30–60 Sekunden zeigen deutlich klarere zeitliche Dynamik und räumliche Muster als kurze Textpassagen. 

Zweitens individuelles Fine-Tuning: Laut Paper kann der Subject-Block von TRIBE v2 mit ~1 Stunde fMRT-Daten einer Person in einer Epoche feinjustiert werden und liefert dann 2–4x bessere personalisierte Vorhersagen als das Gruppenmittel.

Das vollständige Notebook findest du im TRIBE v2 GitHub-Repository. Das Paper lohnt sich besonders in Abschnitt 2.5 (In-silico-Visionsexperimente) und Abschnitt 2.8 (Erkenntnisse zur multimodalen Integration) – es zeigt, was diese Tools für die neurowissenschaftliche Forschung möglich machen.

TRIBE v2 Tutorial: Häufige Fragen

Welche GPU brauche ich wirklich für TRIBE v2?

Für die komplette trimodale Pipeline brauchst du mindestens 40 GB VRAM. Die A100 mit 40 GB auf Colab Pro ist die kleinste sinnvolle Option. Wenn du ausschließlich Audio nutzt und Text sowie Video weglässt, könnte es auf eine L4 (24 GB) passen – das musst du jedoch testen.

Kann ich den HuggingFace-Login-Schritt überspringen?

Ja, wenn du komplett auf Texteingaben verzichtest, denn LLaMA 3.2-3B wird nur heruntergeladen, wenn model.predict() mit Textevents aufgerufen wird. Bei ausschließlich Audio- oder Videoeingaben wird der Textextraktor nie initialisiert und es ist kein HuggingFace-Token nötig. Die TRIBE-Encodergewichte unter facebook/tribev2 sind nicht gated.

Warum zeigt das Gehirn kein Aktivierungsmuster, sondern nur gleichmäßig niedrige Farben?

Die drei häufigsten Ursachen sind:

  • Der Input ist zu kurz – nutze mindestens 15–30 Sekunden.

  • Der Schwellenwert unterdrückt echte Signale. Senke den Threshold in render_hemi() von '20%' auf '5%'.

  • Die text temp-Datei war leer wegen des Flush/Close-Bugs. Füge os.fsync() und tmp.close() ein, bevor du get_events_dataframe() aufrufst.

Wie verhält sich das im Vergleich zu Metas offizieller interaktiver Demo?

Das zugrunde liegende Modell und die Gewichte sind identisch. Metas Demo nutzt einen eigenen WebGL-Renderer mit Kopfsilhouette und videobasierten Wiedergabesteuerungen, die mit der Gehirnanimation synchronisiert sind. Unsere Gradio-Demo verwendet nilearn.plotting.view_surf und rendert dasselbe „inflated“ fsaverage5-Mesh mit derselben „hot“-Colormap über Plotlys WebGL-Engine.


Aashi Dutt's photo
Author
Aashi Dutt
LinkedIn
Twitter

Ich bin Google Developers Expertin für ML (Gen AI), dreifache Kaggle-Expertin und Women-Techmakers-Botschafterin mit über drei Jahren Erfahrung in der Tech-Branche. 2020 habe ich ein Health-Tech-Startup mitgegründet und absolviere derzeit einen Master in Informatik an der Georgia Tech mit Schwerpunkt Machine Learning.

Themen
Deep Learning
Große Sprachmodelle
Generative KI

Deep-Learning-Kurse

Lernpfad

Deep Learning in Python

18 Std.
Setze deine Reise zum maschinellen Lernen mit Deep Learning fort. Verwende die PyTorch-Bibliothek, um neuronale Netze zur Modellierung verschiedener Datentypen zu erstellen.
Details anzeigenRight Arrow
Kurs Starten
Mehr anzeigenRight Arrow
Ähnlich

Blog

Die 36 wichtigsten Fragen und Antworten zum Thema generative KI für 2026

Dieser Blog hat eine ganze Reihe von Fragen und Antworten zu generativer KI, von den Grundlagen bis hin zu fortgeschrittenen Themen.
Hesam Sheikh Hassani's photo

Hesam Sheikh Hassani

15 Min.

Blog

Arten von KI-Agenten: Ihre Rollen, Strukturen und Anwendungen verstehen

Lerne die wichtigsten Arten von KI-Agenten kennen, wie sie mit ihrer Umgebung interagieren und wie sie in verschiedenen Branchen eingesetzt werden. Verstehe einfache reflexive, modellbasierte, zielbasierte, nutzenbasierte, lernende Agenten und mehr.

Blog

Lehrer/innen und Schüler/innen erhalten das Premium DataCamp kostenlos für ihre gesamte akademische Laufbahn

Keine Hacks, keine Tricks. Schüler/innen und Lehrer/innen, lest weiter, um zu erfahren, wie ihr die Datenerziehung, die euch zusteht, kostenlos bekommen könnt.
Nathaniel Taylor-Leach's photo

Nathaniel Taylor-Leach

4 Min.

Blog

Q2 2023 DataCamp Donates Digest

DataCamp Donates hat im zweiten Quartal 2023 über 20.000 Stipendien an unsere gemeinnützigen Partner vergeben. Erfahre, wie fleißige benachteiligte Lernende diese Chancen in lebensverändernde berufliche Erfolge verwandelt haben.
Nathaniel Taylor-Leach's photo

Nathaniel Taylor-Leach

Tutorial

Fibonacci-Folge in Python: Lerne und entdecke Programmiertechniken

Finde raus, wie die Fibonacci-Folge funktioniert. Schau dir die mathematischen Eigenschaften und die Anwendungen in der echten Welt an.
Laiba Siddiqui's photo

Laiba Siddiqui

6 Min.

Tutorial

Python Datenstrukturen Tutorial

Mach dich mit Python-Datenstrukturen vertraut: Lerne mehr über Datentypen und primitive sowie nicht-primitive Datenstrukturen wie Strings, Listen, Stapel usw.
Sejal Jaiswal's photo

Sejal Jaiswal

24 Min.

Mehr AnzeigenMehr Anzeigen