This repository adapts the sentence-decoding code from d'Ascoli et al., "Towards decoding individual words from non-invasive brain recordings" (Nature Neuroscience, 2025), for the 2026 PNPL LibriBrain100 competition.
The default experiment is intentionally simple: train a brain-to-text-embedding model on the first ten Sherlock sessions, validate on the next Sherlock session, and test on a held-out Sherlock session.
- Loads LibriBrain100 through the
pnplpackage. - Converts PNPL LibriBrain100 records into
neuralsettimelines. - Trains the existing sentence-decoding pipeline with the ConvNet plus Transformer.
- Evaluates word retrieval from predicted text embeddings.
- Reports the PNPL-style metric: top-10 balanced accuracy on the 50-word LibriBrain100 evaluation vocabulary.
sentence_decoding/ Training loop, metrics, grids
sentence_decoding/grids/ Runnable experiment configs
neuralset/ Dataset loading and feature extraction
neuraltrain/ Model, optimizer, loss, and metric utilities
runs/ Local caches and results, if SAVEPATH points here
The main LibriBrain100 entry point is:
sentence_decoding/grids/libribrain100.py
Create an environment:
conda create -n neuralset python=3.11
conda activate neuralset
pip install -U torch==2.3.1 torchvision==0.18.1Install the local packages:
cd neuralset
pip install --config-settings editable_mode=strict -e '.[dev]'
cd ..
cd neuraltrain
pip install --config-settings editable_mode=strict -e .
cd ..Install the experiment dependencies:
pip install pnpl lightning wandb x_transformers kenlm transformers sentencepieceSet paths for data, caches, and results:
export SAVEPATH=/Users/hans/dascoli/runs
export DATAPATH=/Users/hans/dascoli/data
export LIBRIBRAIN100_PATH=/Users/hans/dascoli/libribrain100
mkdir -p "$SAVEPATH/cache" "$SAVEPATH/results" "$DATAPATH" "$LIBRIBRAIN100_PATH"Run the LibriBrain100 baseline:
python -m sentence_decoding.grids.libribrain100The first run may download LibriBrain100 files and compute text embeddings, so it can spend a while preparing data before training begins.
By default, sentence_decoding.grids.libribrain100 runs:
| Split | Subject | Task | Session | Run |
|---|---|---|---|---|
| Train | sub-0 | Sherlock1 | 1-10 | 1 |
| Validation | sub-0 | Sherlock1 | 11 | 2 |
| Test | sub-0 | Sherlock1 | 12 | 2 |
The model uses:
- neural input: MEG windows around each word
- target:
t5-largetext embeddings - loss: SigLIP-style contrastive loss
- architecture: SimpleConvTimeAgg plus Transformer
- metric: embedding retrieval
For LibriBrain100, the key metric is:
val_retrieval_acc10_vocab=libribrain50_macro_0
test_retrieval_acc10_vocab=libribrain50_macro_0
This is top-10 balanced accuracy, macro-averaged over the observed words from the 50-word LibriBrain100 evaluation vocabulary:
is the a to it i not was we be he that have this they of there and are in but
will so all my for she were any really at out our am its had him an very has do
can time think good always new people as on
The older 250-most-frequent-word retrieval metric is not used by the LibriBrain100 grid.
The default split is deliberately focused. To change it, edit:
sentence_decoding/grids/libribrain100.py
The important knob is data.query. For example:
"data.query": "subject == 'LibriBrain100/0' and corpus == 'sherlock'"That uses all deep-subject Sherlock records selected by the query, while keeping the train/validation/test split annotations provided by PNPL.
For all available sub-0 LibriBrain100 records:
"data.query": "subject == 'LibriBrain100/0'"Disable Weights & Biases:
"use_wandb": FalseChange the LibriBrain100 data directory:
export LIBRIBRAIN100_PATH=/path/to/libribrain100Change where caches and results are written:
export SAVEPATH=/path/to/runs
export DATAPATH=/path/to/dataResults are written under:
$SAVEPATH/results/sentence_decoding/libribrain100/
Useful artifacts include:
config.yaml: the exact config used for the runbest.ckptandlast.ckpt: model checkpointsretrieval_outputs/: saved retrieval predictions and targetsdecoded_sentences/: decoded text snapshots from validation/test
- This is a research baseline, not a polished competition submission script.
- The grid runs locally by default for LibriBrain100.
- PNPL handles LibriBrain100 file resolution and download.
- If a local run prints
Done.too quickly, inspect the saved job metadata in the corresponding results folder; the local task runner can cache failed jobs.
The original README from the sentence-decoding codebase is preserved as
README_ORIGINAL.md.