Upload 3 files
Browse files- LICENSE.txt +21 -0
- README.md +213 -0
- text2roi_colab_demo.ipynb +293 -0
LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2026 Amphora NeuroText contributors
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
README.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
language:
|
| 4 |
+
- en
|
| 5 |
+
tags:
|
| 6 |
+
- neuroscience
|
| 7 |
+
- fmri
|
| 8 |
+
- brain-encoding
|
| 9 |
+
- text-embeddings
|
| 10 |
+
- roi-prediction
|
| 11 |
+
- pytorch
|
| 12 |
+
library_name: pytorch
|
| 13 |
+
pipeline_tag: feature-extraction
|
| 14 |
+
---
|
| 15 |
+
|
| 16 |
+
# Amphora NeuroText — Text to Brain ROI Activations
|
| 17 |
+
|
| 18 |
+
**Hugging Face model:** [`ffh92r32rm0/Amphora_NeuroText`](https://huggingface.co/ffh92r32rm0/Amphora_NeuroText)
|
| 19 |
+
|
| 20 |
+
Lightweight MLP that maps **text** → **56 cortical/subcortical ROI activation scores**.
|
| 21 |
+
|
| 22 |
+
```
|
| 23 |
+
text → Qwen3-Embedding-4B (2560d) → Text2ROI MLP (3.2M params) → 56 ROI values
|
| 24 |
+
```
|
| 25 |
+
|
| 26 |
+
This is a fast, text-native brain encoding model. It does **not** require the full V5 joint fMRI encoder or multimodal feature extraction — only Qwen3 text embeddings plus this 13 MB projector.
|
| 27 |
+
|
| 28 |
+
[](https://huggingface.co/ffh92r32rm0/Amphora_NeuroText/colab)
|
| 29 |
+
|
| 30 |
+
**Try it now:** [Open the Colab demo](https://huggingface.co/ffh92r32rm0/Amphora_NeuroText/colab) (GPU runtime → type text → click **Predict** → ROI bar chart + fsaverage5 brain maps).
|
| 31 |
+
|
| 32 |
+
Weights (`text2roi_projector.pt`) download automatically from this public HF repo — no token needed.
|
| 33 |
+
|
| 34 |
+
**Model page:** https://huggingface.co/ffh92r32rm0/Amphora_NeuroText
|
| 35 |
+
|
| 36 |
+
## Model details
|
| 37 |
+
|
| 38 |
+
| | |
|
| 39 |
+
|---|---|
|
| 40 |
+
| **Input** | Any English text string |
|
| 41 |
+
| **Text encoder** | [`Qwen/Qwen3-Embedding-4B`](https://huggingface.co/Qwen/Qwen3-Embedding-4B) (2560d, L2-normalized last token) |
|
| 42 |
+
| **Projector** | 3-layer MLP: 2560 → 1024 → 512 → 56 |
|
| 43 |
+
| **Output** | 56 ROI activation scores (universal schema, same order as NeuroDiffusion V5) |
|
| 44 |
+
| **Parameters** | 3,178,040 (projector only) |
|
| 45 |
+
| **Checkpoint size** | ~13 MB |
|
| 46 |
+
| **Val mean Pearson R** | **0.170** (held-out text–fMRI pairs) |
|
| 47 |
+
| **Training data** | ~300k text–ROI pairs from naturalistic fMRI corpora |
|
| 48 |
+
| **License** | MIT (this repo). Qwen3-Embedding-4B has its own [license](https://huggingface.co/Qwen/Qwen3-Embedding-4B). |
|
| 49 |
+
|
| 50 |
+
### ROI outputs (56 regions)
|
| 51 |
+
|
| 52 |
+
Visual (`V1`–`EBA`), parietal (`IPS2`–`hIP3`), frontal executive (`dlPFC`–`FP2`), language (`IFG`–`AG`), default mode (`PCC`, `mPFC_dmn`, `LP_L/R`), salience/affect (`AI`, `dACC`, `vmPFC`, hippocampus), subcortical (`Amygdala`, `Caudate`, `Putamen`, `Thalamus`). See `config.json` for full list.
|
| 53 |
+
|
| 54 |
+
## Intended use
|
| 55 |
+
|
| 56 |
+
- Research prototyping: estimate population-level brain ROI responses to text stimuli
|
| 57 |
+
- Creative tools: text-driven neuroemotion / brain-state visualization pipelines
|
| 58 |
+
- Fast baseline before running the full multimodal V5 encoder
|
| 59 |
+
|
| 60 |
+
## Limitations
|
| 61 |
+
|
| 62 |
+
- **Not mind reading.** Outputs are model predictions trained on group fMRI, not measurements of any individual person's thoughts or emotions.
|
| 63 |
+
- **Modest accuracy.** Val mean R ≈ 0.17 — useful for relative comparisons across texts, not clinical or diagnostic use.
|
| 64 |
+
- **Text only.** No image, audio, or video input. Multimodal (image/audio/video) encoding is out of scope for this model.
|
| 65 |
+
- **English-biased.** Trained primarily on English naturalistic narratives; other languages are untested.
|
| 66 |
+
- **Depends on Qwen3.** First run downloads ~8 GB of embedding weights from Hugging Face.
|
| 67 |
+
|
| 68 |
+
## Quick start
|
| 69 |
+
|
| 70 |
+
### Colab demo (recommended — no install)
|
| 71 |
+
|
| 72 |
+
1. Open the [**Colab demo**](https://huggingface.co/ffh92r32rm0/Amphora_NeuroText/colab) (or model page → **Use this model** → **Google Colab**)
|
| 73 |
+
2. Runtime → **Change runtime type** → **GPU** (T4)
|
| 74 |
+
3. Run all cells, type any sentence, click **Predict brain map**
|
| 75 |
+
|
| 76 |
+
Everything loads from this HF repo (`ffh92r32rm0/Amphora_NeuroText`) — notebook, weights, and helper scripts.
|
| 77 |
+
|
| 78 |
+
**Commercial-friendly brain viz:** The Colab demo uses **MNE + Nilearn** for fsaverage5 / HCP-MMP plotting (`text2roi_to_fsaverage.py`). It does **not** import tribev2 (CC BY-NC). See `requirements-colab.txt`.
|
| 79 |
+
|
| 80 |
+
### Local install
|
| 81 |
+
|
| 82 |
+
**1. Install**
|
| 83 |
+
|
| 84 |
+
```bash
|
| 85 |
+
pip install torch numpy transformers huggingface_hub
|
| 86 |
+
# or
|
| 87 |
+
pip install -r requirements.txt
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
**2. Download this model**
|
| 91 |
+
|
| 92 |
+
```bash
|
| 93 |
+
huggingface-cli download ffh92r32rm0/Amphora_NeuroText text2roi_projector.pt predict.py config.json
|
| 94 |
+
```
|
| 95 |
+
|
| 96 |
+
Or in Python:
|
| 97 |
+
|
| 98 |
+
```python
|
| 99 |
+
from huggingface_hub import hf_hub_download
|
| 100 |
+
|
| 101 |
+
ckpt = hf_hub_download("ffh92r32rm0/Amphora_NeuroText", "text2roi_projector.pt")
|
| 102 |
+
```
|
| 103 |
+
|
| 104 |
+
**3. Run inference**
|
| 105 |
+
|
| 106 |
+
CLI:
|
| 107 |
+
|
| 108 |
+
```bash
|
| 109 |
+
python predict.py --text "a dog running through a sunny park" --device cuda
|
| 110 |
+
```
|
| 111 |
+
|
| 112 |
+
Python API:
|
| 113 |
+
|
| 114 |
+
```python
|
| 115 |
+
from predict import predict_text
|
| 116 |
+
from huggingface_hub import hf_hub_download
|
| 117 |
+
|
| 118 |
+
ckpt = hf_hub_download("ffh92r32rm0/Amphora_NeuroText", "text2roi_projector.pt")
|
| 119 |
+
result = predict_text("a terrifying monster in a dark alley", checkpoint_path=ckpt, device="cuda")
|
| 120 |
+
|
| 121 |
+
for roi, score in sorted(result["roi_predictions"].items(), key=lambda x: abs(x[1]), reverse=True)[:8]:
|
| 122 |
+
print(f"{roi:16s} {score:+.4f}")
|
| 123 |
+
```
|
| 124 |
+
|
| 125 |
+
**Example output:**
|
| 126 |
+
|
| 127 |
+
```
|
| 128 |
+
LO2 +0.4797
|
| 129 |
+
FFA-2 +0.4387
|
| 130 |
+
V3A +0.4014
|
| 131 |
+
mPFC_dmn +0.3215
|
| 132 |
+
V2 +0.3037
|
| 133 |
+
IPS2 +0.2845
|
| 134 |
+
HPC_L -0.2610
|
| 135 |
+
MST +0.2586
|
| 136 |
+
```
|
| 137 |
+
|
| 138 |
+
**4. Load projector weights manually**
|
| 139 |
+
|
| 140 |
+
```python
|
| 141 |
+
import torch
|
| 142 |
+
from predict import Text2ROI
|
| 143 |
+
|
| 144 |
+
ckpt = torch.load("text2roi_projector.pt", map_location="cpu", weights_only=False)
|
| 145 |
+
model = Text2ROI(in_dim=ckpt["in_dim"], hidden=ckpt["hidden"], out_dim=ckpt["n_roi"])
|
| 146 |
+
model.load_state_dict(ckpt["state_dict"])
|
| 147 |
+
model.eval()
|
| 148 |
+
|
| 149 |
+
# embed: (1, 2560) float32 from Qwen3-Embedding-4B
|
| 150 |
+
with torch.no_grad():
|
| 151 |
+
rois = model(torch.from_numpy(embed)).numpy() # (1, 56)
|
| 152 |
+
```
|
| 153 |
+
|
| 154 |
+
## Training
|
| 155 |
+
|
| 156 |
+
Trained with Pearson-R correlation loss + 0.1× MSE on ~300k (text embedding, ROI target) pairs:
|
| 157 |
+
|
| 158 |
+
```bash
|
| 159 |
+
python train_text2roi.py \
|
| 160 |
+
--data /path/to/neurotext_pairs.npz \
|
| 161 |
+
--out text2roi_projector.pt \
|
| 162 |
+
--epochs 80 --batch 2048 --lr 3e-4
|
| 163 |
+
```
|
| 164 |
+
|
| 165 |
+
Train your own projector with Pearson-R loss + 0.1× MSE on `(text embedding, ROI target)` pairs in `.npz` format.
|
| 166 |
+
|
| 167 |
+
## Evaluation
|
| 168 |
+
|
| 169 |
+
| Metric | Value |
|
| 170 |
+
|--------|-------|
|
| 171 |
+
| Val mean Pearson R (56 ROIs) | 0.170 |
|
| 172 |
+
| Projector params | 3.18M |
|
| 173 |
+
| Training time | ~5 min (RTX 5080) |
|
| 174 |
+
|
| 175 |
+
Reproduce on your val split:
|
| 176 |
+
|
| 177 |
+
```bash
|
| 178 |
+
python infer_text2roi.py --checkpoint text2roi_projector.pt --data neurotext_pairs.npz
|
| 179 |
+
```
|
| 180 |
+
|
| 181 |
+
## Files in this repo
|
| 182 |
+
|
| 183 |
+
| File | Description |
|
| 184 |
+
|------|-------------|
|
| 185 |
+
| `text2roi_projector.pt` | Trained MLP weights + metadata |
|
| 186 |
+
| `notebook.ipynb` | **Interactive Colab demo** (opened via `/colab` on the model page) |
|
| 187 |
+
| `text2roi_colab_demo.ipynb` | Same Colab demo (alternate filename) |
|
| 188 |
+
| `text2roi_to_fsaverage.py` | ROI → fsaverage5 mapping + Nilearn plotting (no tribev2) |
|
| 189 |
+
| `predict.py` | Standalone inference script |
|
| 190 |
+
| `config.json` | Model hyperparameters and eval summary |
|
| 191 |
+
| `requirements.txt` | Local Python dependencies |
|
| 192 |
+
| `requirements-colab.txt` | Colab deps (MNE + Nilearn, no tribev2) |
|
| 193 |
+
| `LICENSE` | MIT |
|
| 194 |
+
|
| 195 |
+
## Citation
|
| 196 |
+
|
| 197 |
+
```bibtex
|
| 198 |
+
@misc{amphora_neurotext2026,
|
| 199 |
+
author = {ffh92r32rm0},
|
| 200 |
+
title = {Amphora NeuroText: Text-to-Brain ROI Activation Projector},
|
| 201 |
+
year = {2026},
|
| 202 |
+
publisher = {Hugging Face},
|
| 203 |
+
howpublished = {\url{https://huggingface.co/ffh92r32rm0/Amphora_NeuroText}},
|
| 204 |
+
license = {MIT}
|
| 205 |
+
}
|
| 206 |
+
```
|
| 207 |
+
|
| 208 |
+
## Links
|
| 209 |
+
|
| 210 |
+
- **Model:** https://huggingface.co/ffh92r32rm0/Amphora_NeuroText
|
| 211 |
+
- **Colab demo:** https://huggingface.co/ffh92r32rm0/Amphora_NeuroText/colab
|
| 212 |
+
- **Text encoder:** https://huggingface.co/Qwen/Qwen3-Embedding-4B
|
| 213 |
+
- **Brain visualization:** MNE + Nilearn (fsaverage5 / HCP-MMP1, commercial-friendly)
|
text2roi_colab_demo.ipynb
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"metadata": {},
|
| 6 |
+
"source": [
|
| 7 |
+
"# Amphora NeuroText — Colab Demo\n",
|
| 8 |
+
"\n",
|
| 9 |
+
"**Type text → predict 56 brain ROI activations → visualize on fsaverage5 cortical maps (MNE + Nilearn).**\n",
|
| 10 |
+
"\n",
|
| 11 |
+
"Open this notebook from the [Hugging Face model page](https://huggingface.co/ffh92r32rm0/Amphora_NeuroText) → **Use this model** → **Google Colab**, or go directly to:\n",
|
| 12 |
+
"\n",
|
| 13 |
+
"**https://huggingface.co/ffh92r32rm0/Amphora_NeuroText/colab**\n",
|
| 14 |
+
"\n",
|
| 15 |
+
"All assets (weights, helper scripts) download from `ffh92r32rm0/Amphora_NeuroText` — no GitHub, no login required.\n",
|
| 16 |
+
"\n",
|
| 17 |
+
"Pipeline:\n",
|
| 18 |
+
"```\n",
|
| 19 |
+
"text → Qwen3-Embedding-4B → Text2ROI MLP (3M params) → 56 ROI scores → HCP-MMP parcels → fsaverage5 surface\n",
|
| 20 |
+
"```\n",
|
| 21 |
+
"\n",
|
| 22 |
+
"Brain plotting uses **MNE + Nilearn only** (BSD/MIT-friendly) — no tribev2 import (CC BY-NC).\n",
|
| 23 |
+
"\n",
|
| 24 |
+
"**Setup:** Runtime → Change runtime type → **GPU** (T4 is enough), then run all cells.\n",
|
| 25 |
+
"\n",
|
| 26 |
+
"Model: [ffh92r32rm0/Amphora_NeuroText](https://huggingface.co/ffh92r32rm0/Amphora_NeuroText) · MIT license\n",
|
| 27 |
+
"\n",
|
| 28 |
+
"> ⚠️ Predictions are **model estimates** from group fMRI training — not measurements of anyone's private thoughts."
|
| 29 |
+
]
|
| 30 |
+
},
|
| 31 |
+
{
|
| 32 |
+
"cell_type": "code",
|
| 33 |
+
"metadata": {},
|
| 34 |
+
"source": [
|
| 35 |
+
"# Install dependencies (first run ~5–8 min)\n",
|
| 36 |
+
"# Commercial-friendly: MNE + Nilearn for brain viz — no tribev2 (CC BY-NC).\n",
|
| 37 |
+
"import sys\n",
|
| 38 |
+
"\n",
|
| 39 |
+
"!{sys.executable} -m pip install -q --upgrade pip\n",
|
| 40 |
+
"!{sys.executable} -m pip install -q \"torch>=2.3\" \"transformers>=4.44\" huggingface_hub ipywidgets\n",
|
| 41 |
+
"!{sys.executable} -m pip install -q nilearn mne nibabel scipy matplotlib numpy\n",
|
| 42 |
+
"\n",
|
| 43 |
+
"print(\"Deps installed (MNE + Nilearn for fsaverage5 brain maps).\")"
|
| 44 |
+
],
|
| 45 |
+
"execution_count": null,
|
| 46 |
+
"outputs": []
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
"cell_type": "code",
|
| 50 |
+
"metadata": {},
|
| 51 |
+
"source": [
|
| 52 |
+
"import json\n",
|
| 53 |
+
"from pathlib import Path\n",
|
| 54 |
+
"\n",
|
| 55 |
+
"import matplotlib.pyplot as plt\n",
|
| 56 |
+
"import numpy as np\n",
|
| 57 |
+
"import torch\n",
|
| 58 |
+
"import torch.nn as nn\n",
|
| 59 |
+
"import torch.nn.functional as F\n",
|
| 60 |
+
"from huggingface_hub import hf_hub_download\n",
|
| 61 |
+
"from IPython.display import display, clear_output\n",
|
| 62 |
+
"import ipywidgets as widgets\n",
|
| 63 |
+
"\n",
|
| 64 |
+
"HF_REPO = \"ffh92r32rm0/Amphora_NeuroText\"\n",
|
| 65 |
+
"\n",
|
| 66 |
+
"DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n",
|
| 67 |
+
"print(f\"Device: {DEVICE}\")\n",
|
| 68 |
+
"\n",
|
| 69 |
+
"CKPT_PATH = hf_hub_download(HF_REPO, \"text2roi_projector.pt\")\n",
|
| 70 |
+
"print(f\"Checkpoint: {CKPT_PATH}\")"
|
| 71 |
+
],
|
| 72 |
+
"execution_count": null,
|
| 73 |
+
"outputs": []
|
| 74 |
+
},
|
| 75 |
+
{
|
| 76 |
+
"cell_type": "code",
|
| 77 |
+
"metadata": {},
|
| 78 |
+
"source": [
|
| 79 |
+
"ROI_NAMES_56 = [\n",
|
| 80 |
+
" \"V1\", \"V2\", \"V3\", \"V4\", \"V3A\", \"V3B\", \"LO1\", \"LO2\",\n",
|
| 81 |
+
" \"MT\", \"MST\", \"V7\", \"IPS1\",\n",
|
| 82 |
+
" \"FFA-1\", \"FFA-2\", \"PPA\", \"RSC\", \"OFA\", \"EBA\",\n",
|
| 83 |
+
" \"IPS2\", \"IPS3\", \"IPS4\", \"IPS5\", \"SPL1\", \"hIP1\", \"hIP2\", \"hIP3\",\n",
|
| 84 |
+
" \"dlPFC\", \"vlPFC\", \"OFC\", \"ACC\", \"mPFC\", \"FP1\", \"FP2\",\n",
|
| 85 |
+
" \"IFG\", \"IFGorb\", \"STG\", \"STS\", \"MTG\", \"AG\",\n",
|
| 86 |
+
" \"PCC\", \"mPFC_dmn\", \"LP_L\", \"LP_R\", \"HPC_L\", \"HPC_R\",\n",
|
| 87 |
+
" \"AI\", \"dACC\", \"sgACC\", \"vmPFC\",\n",
|
| 88 |
+
" \"Amygdala_L\", \"Amygdala_R\", \"Caudate_L\", \"Caudate_R\",\n",
|
| 89 |
+
" \"Putamen_L\", \"Putamen_R\", \"Thalamus\",\n",
|
| 90 |
+
"]\n",
|
| 91 |
+
"\n",
|
| 92 |
+
"class Text2ROI(nn.Module):\n",
|
| 93 |
+
" def __init__(self, in_dim=2560, hidden=1024, out_dim=56, dropout=0.1):\n",
|
| 94 |
+
" super().__init__()\n",
|
| 95 |
+
" self.net = nn.Sequential(\n",
|
| 96 |
+
" nn.Linear(in_dim, hidden), nn.GELU(), nn.Dropout(dropout), nn.LayerNorm(hidden),\n",
|
| 97 |
+
" nn.Linear(hidden, hidden // 2), nn.GELU(), nn.Dropout(dropout),\n",
|
| 98 |
+
" nn.Linear(hidden // 2, out_dim),\n",
|
| 99 |
+
" )\n",
|
| 100 |
+
" def forward(self, x):\n",
|
| 101 |
+
" return self.net(x)\n",
|
| 102 |
+
"\n",
|
| 103 |
+
"def load_projector(path):\n",
|
| 104 |
+
" ckpt = torch.load(path, map_location=\"cpu\", weights_only=False)\n",
|
| 105 |
+
" model = Text2ROI(int(ckpt[\"in_dim\"]), int(ckpt[\"hidden\"]), int(ckpt[\"n_roi\"]))\n",
|
| 106 |
+
" model.load_state_dict(ckpt[\"state_dict\"])\n",
|
| 107 |
+
" model.to(DEVICE).eval()\n",
|
| 108 |
+
" names = [str(x) for x in ckpt.get(\"roi_names\", ROI_NAMES_56)]\n",
|
| 109 |
+
" return model, names, ckpt\n",
|
| 110 |
+
"\n",
|
| 111 |
+
"PROJECTOR, ROI_NAMES, CKPT_META = load_projector(CKPT_PATH)\n",
|
| 112 |
+
"print(f\"Loaded Text2ROI v1 — val R={CKPT_META.get('best_val_r', 0):+.3f}, {len(ROI_NAMES)} ROIs\")"
|
| 113 |
+
],
|
| 114 |
+
"execution_count": null,
|
| 115 |
+
"outputs": []
|
| 116 |
+
},
|
| 117 |
+
{
|
| 118 |
+
"cell_type": "code",
|
| 119 |
+
"metadata": {},
|
| 120 |
+
"source": [
|
| 121 |
+
"QWEN_ID = \"Qwen/Qwen3-Embedding-4B\"\n",
|
| 122 |
+
"from transformers import AutoModel, AutoTokenizer\n",
|
| 123 |
+
"\n",
|
| 124 |
+
"print(\"Loading Qwen3 embedder (first run downloads ~8 GB)...\")\n",
|
| 125 |
+
"_tok = AutoTokenizer.from_pretrained(QWEN_ID, padding_side=\"left\")\n",
|
| 126 |
+
"_qwen = AutoModel.from_pretrained(QWEN_ID, dtype=torch.bfloat16).to(DEVICE).eval()\n",
|
| 127 |
+
"print(\"Qwen3 ready.\")\n",
|
| 128 |
+
"\n",
|
| 129 |
+
"@torch.no_grad()\n",
|
| 130 |
+
"def embed_text(text: str) -> np.ndarray:\n",
|
| 131 |
+
" tok = _tok([text], return_tensors=\"pt\", padding=True, truncation=True, max_length=512).to(DEVICE)\n",
|
| 132 |
+
" out = _qwen(**tok)\n",
|
| 133 |
+
" emb = out.last_hidden_state[:, -1].float()\n",
|
| 134 |
+
" emb = F.normalize(emb, p=2, dim=1).cpu().numpy().astype(np.float32)\n",
|
| 135 |
+
" return emb\n",
|
| 136 |
+
"\n",
|
| 137 |
+
"@torch.no_grad()\n",
|
| 138 |
+
"def predict_rois(text: str) -> dict:\n",
|
| 139 |
+
" x = torch.from_numpy(embed_text(text)).to(DEVICE)\n",
|
| 140 |
+
" pred = PROJECTOR(x).cpu().numpy().reshape(-1)\n",
|
| 141 |
+
" return {name: float(pred[i]) for i, name in enumerate(ROI_NAMES)}"
|
| 142 |
+
],
|
| 143 |
+
"execution_count": null,
|
| 144 |
+
"outputs": []
|
| 145 |
+
},
|
| 146 |
+
{
|
| 147 |
+
"cell_type": "code",
|
| 148 |
+
"metadata": {},
|
| 149 |
+
"source": [
|
| 150 |
+
"# Map 56 ROI values → fsaverage5 vertices (MNE + Nilearn — no tribev2)\n",
|
| 151 |
+
"import sys\n",
|
| 152 |
+
"from pathlib import Path\n",
|
| 153 |
+
"\n",
|
| 154 |
+
"_helper = Path(hf_hub_download(HF_REPO, \"text2roi_to_fsaverage.py\"))\n",
|
| 155 |
+
"sys.path.insert(0, str(_helper.parent))\n",
|
| 156 |
+
"from text2roi_to_fsaverage import roi_dict_to_fsaverage5, plot_fsaverage5_brain\n",
|
| 157 |
+
"\n",
|
| 158 |
+
"print(\"fsaverage5 plotting helpers ready (MNE + Nilearn).\")"
|
| 159 |
+
],
|
| 160 |
+
"execution_count": null,
|
| 161 |
+
"outputs": []
|
| 162 |
+
},
|
| 163 |
+
{
|
| 164 |
+
"cell_type": "code",
|
| 165 |
+
"metadata": {},
|
| 166 |
+
"source": [
|
| 167 |
+
"def plot_results(text: str, roi_values: dict):\n",
|
| 168 |
+
" vertex_map, skipped = roi_dict_to_fsaverage5(roi_values)\n",
|
| 169 |
+
"\n",
|
| 170 |
+
" # --- ROI bar chart ---\n",
|
| 171 |
+
" top = sorted(roi_values.items(), key=lambda kv: abs(kv[1]), reverse=True)[:16]\n",
|
| 172 |
+
" names = [t[0] for t in top]\n",
|
| 173 |
+
" vals = [t[1] for t in top]\n",
|
| 174 |
+
" colors = [\"#c44e52\" if v < 0 else \"#4c72b0\" for v in vals]\n",
|
| 175 |
+
"\n",
|
| 176 |
+
" fig, ax = plt.subplots(figsize=(7, 5))\n",
|
| 177 |
+
" ax.barh(names[::-1], vals[::-1], color=colors[::-1])\n",
|
| 178 |
+
" ax.axvline(0, color=\"#333\", lw=0.8)\n",
|
| 179 |
+
" ax.set_title(\"Top ROI predictions\")\n",
|
| 180 |
+
" ax.set_xlabel(\"Predicted activation\")\n",
|
| 181 |
+
" plt.tight_layout()\n",
|
| 182 |
+
" plt.show()\n",
|
| 183 |
+
"\n",
|
| 184 |
+
" # --- fsaverage5 brain maps (Nilearn) ---\n",
|
| 185 |
+
" skip_note = f\" — subcortical skipped: {len(skipped)}\" if skipped else \"\"\n",
|
| 186 |
+
" plot_fsaverage5_brain(\n",
|
| 187 |
+
" vertex_map,\n",
|
| 188 |
+
" views=[\"left\", \"right\", \"dorsal\"],\n",
|
| 189 |
+
" cmap=\"RdBu_r\",\n",
|
| 190 |
+
" threshold=0.02,\n",
|
| 191 |
+
" title=f\"fsaverage5 cortical map (HCP-MMP ROIs){skip_note}\",\n",
|
| 192 |
+
" )\n",
|
| 193 |
+
" plt.show()\n",
|
| 194 |
+
"\n",
|
| 195 |
+
" print(\"\\nTop ROIs:\")\n",
|
| 196 |
+
" for n, v in top[:10]:\n",
|
| 197 |
+
" print(f\" {n:<16} {v:+.4f}\")\n",
|
| 198 |
+
" if skipped:\n",
|
| 199 |
+
" print(f\"\\n(Subcortical / unmapped ROIs not painted on cortex: {', '.join(skipped[:8])}{'…' if len(skipped)>8 else ''})\")"
|
| 200 |
+
],
|
| 201 |
+
"execution_count": null,
|
| 202 |
+
"outputs": []
|
| 203 |
+
},
|
| 204 |
+
{
|
| 205 |
+
"cell_type": "markdown",
|
| 206 |
+
"metadata": {},
|
| 207 |
+
"source": [
|
| 208 |
+
"## Try it — enter text and click **Predict**\n",
|
| 209 |
+
"\n",
|
| 210 |
+
"Or use a preset example below."
|
| 211 |
+
]
|
| 212 |
+
},
|
| 213 |
+
{
|
| 214 |
+
"cell_type": "code",
|
| 215 |
+
"metadata": {},
|
| 216 |
+
"source": [
|
| 217 |
+
"text_in = widgets.Textarea(\n",
|
| 218 |
+
" value=\"a dog running through a sunny park\",\n",
|
| 219 |
+
" description=\"Text:\",\n",
|
| 220 |
+
" layout=widgets.Layout(width=\"90%\", height=\"80px\"),\n",
|
| 221 |
+
")\n",
|
| 222 |
+
"btn = widgets.Button(description=\"Predict brain map\", button_style=\"primary\", icon=\"play\")\n",
|
| 223 |
+
"out = widgets.Output()\n",
|
| 224 |
+
"\n",
|
| 225 |
+
"PRESETS = {\n",
|
| 226 |
+
" \"Sunny park\": \"a dog running through a sunny park\",\n",
|
| 227 |
+
" \"Scary scene\": \"a terrifying monster jumps out in a dark alley\",\n",
|
| 228 |
+
" \"Romantic\": \"two people sharing a quiet candlelit dinner\",\n",
|
| 229 |
+
" \"Math puzzle\": \"solving a difficult calculus proof step by step\",\n",
|
| 230 |
+
"}\n",
|
| 231 |
+
"preset_btns = [widgets.Button(description=k, layout=widgets.Layout(width=\"140px\")) for k in PRESETS]\n",
|
| 232 |
+
"\n",
|
| 233 |
+
"def _on_preset(btn):\n",
|
| 234 |
+
" text_in.value = PRESETS[btn.description]\n",
|
| 235 |
+
"\n",
|
| 236 |
+
"for b in preset_btns:\n",
|
| 237 |
+
" b.on_click(_on_preset)\n",
|
| 238 |
+
"\n",
|
| 239 |
+
"def _on_predict(_):\n",
|
| 240 |
+
" with out:\n",
|
| 241 |
+
" clear_output(wait=True)\n",
|
| 242 |
+
" text = text_in.value.strip()\n",
|
| 243 |
+
" if not text:\n",
|
| 244 |
+
" print(\"Enter some text first.\")\n",
|
| 245 |
+
" return\n",
|
| 246 |
+
" print(f\"Predicting for: {text!r} …\")\n",
|
| 247 |
+
" rois = predict_rois(text)\n",
|
| 248 |
+
" plot_results(text, rois)\n",
|
| 249 |
+
"\n",
|
| 250 |
+
"btn.on_click(_on_predict)\n",
|
| 251 |
+
"\n",
|
| 252 |
+
"display(widgets.VBox([\n",
|
| 253 |
+
" text_in,\n",
|
| 254 |
+
" widgets.HBox([btn] + preset_btns),\n",
|
| 255 |
+
" out,\n",
|
| 256 |
+
"]))"
|
| 257 |
+
],
|
| 258 |
+
"execution_count": null,
|
| 259 |
+
"outputs": []
|
| 260 |
+
},
|
| 261 |
+
{
|
| 262 |
+
"cell_type": "markdown",
|
| 263 |
+
"metadata": {},
|
| 264 |
+
"source": [
|
| 265 |
+
"## What the output means\n",
|
| 266 |
+
"\n",
|
| 267 |
+
"| Output | Description |\n",
|
| 268 |
+
"|--------|-------------|\n",
|
| 269 |
+
"| **Bar chart** | Top 16 of 56 ROI activation scores (positive = predicted up-regulation) |\n",
|
| 270 |
+
"| **Brain surface** | ROI values painted onto **fsaverage5** via HCP-MMP1 parcels (MNE + Nilearn) |\n",
|
| 271 |
+
"| **Skipped ROIs** | Subcortical regions (amygdala, striatum, thalamus) are not on the cortical surface |\n",
|
| 272 |
+
"\n",
|
| 273 |
+
"**Model:** [ffh92r32rm0/Amphora_NeuroText](https://huggingface.co/ffh92r32rm0/Amphora_NeuroText)"
|
| 274 |
+
]
|
| 275 |
+
}
|
| 276 |
+
],
|
| 277 |
+
"metadata": {
|
| 278 |
+
"colab": {
|
| 279 |
+
"provenance": [],
|
| 280 |
+
"gpuType": "T4"
|
| 281 |
+
},
|
| 282 |
+
"kernelspec": {
|
| 283 |
+
"display_name": "Python 3",
|
| 284 |
+
"name": "python3"
|
| 285 |
+
},
|
| 286 |
+
"language_info": {
|
| 287 |
+
"name": "python"
|
| 288 |
+
},
|
| 289 |
+
"accelerator": "GPU"
|
| 290 |
+
},
|
| 291 |
+
"nbformat": 4,
|
| 292 |
+
"nbformat_minor": 5
|
| 293 |
+
}
|