Skip to content

hypline encoding

Fit and score voxelwise ridge encoding models, the step that joins the two sides the rest of the pipeline prepares. train maps stimulus features (X) onto denoised BOLD (Y) per subject; analyze scores one subject's model against another subject's brain, across production and comprehension turns. encoding is a group of subcommands.

hypline encoding <command> <dataset-root> [OPTIONS]
Subcommand Does
train Fit a per-subject ridge model from features (+ optional confounds)
analyze Score a model's cross-subject predictions against a target's BOLD

Both read from the dataset root and write to a new results/ area (see Outputs). Results load back as live Python objects rather than flat tables; read them for downstream analysis with the encoding results API.

The banded ridge model

Each feature becomes its own ridge band (its own regularization strength); all confounds share one trailing band, partialled out in the same fit so a feature cannot claim variance a confound explains. In a dyadic conversation a subject alternates between producing speech and comprehending the partner's, so by default train duplicates every feature and confound regressor into a production copy and a comprehension copy and learns separate weights for each (--no-split keeps one shared copy). Either way, two intercept-like production/comprehension task boxcars always ride a reserved band that absorbs the mean BOLD offset between the two states — --no-split drops the per-regressor duplication, not this band.


hypline encoding train

Fit a voxelwise ridge encoding model per subject, writing one model artifact per subject to results/.

hypline encoding train <dataset-root> \
  --tasks  --features  --desc  --fold-by  [OPTIONS]

Inputs

  • Features under features/ — the predictors, generated by featuregen. Named by --features as <kind>[-<desc>] refs; each ref becomes its own ridge band.
  • Confounds under confounds/ (optional) — stimulus-derived nuisance regressors, generated by confoundgen. Named by --confounds; all share one band. Must be saved at TR level.
  • Denoised BOLD under derivatives/hypline/ — the target, produced by denoise. Selected by --bold-space and --bold-desc (default denoised).

Features are dyad-keyed and BOLD is subject-keyed, so train bridges them through participants.tsv — see Subject vs. dyad.

Options

Option Description Default
--tasks Comma-separated task labels to train on (e.g. neutral,opinion) — required
--features Comma-separated feature refs as <kind>[-<desc>] (e.g. semantic-gpt3,phonemic-noArt); each becomes its own ridge band — required
--desc Variant label for this model (alphanumeric); output lands under results/sub-XX/encodingModel-<desc>/required
--fold-by Cross-validation grouping axis: a BIDS entity to fold on (e.g. run), or none for a single unfolded model — required
--n-folds Number of folds: an integer (>=2) or loo. Required with --fold-by <entity>; omit with --fold-by none none
--confounds Comma-separated confound refs as <kind>[-<desc>] (e.g. phonemic-onset,phonemic-rate); all share one band none
--bold-space BOLD space to train on: fsaverage5, fsaverage6, MNI152NLin6Asym, MNI152NLin2009cAsym MNI152NLin2009cAsym
--bold-desc BOLD desc entity selecting the input runs denoised
--downsample Feature-to-TR downsampling: mean or sum mean
--delays Comma-separated FIR delays in TRs 0,1,2,3,4,5
--alphas Comma-separated ridge alphas to search 11e12 log grid (13 points)
--device Compute device for the fit: cpu or cuda cpu
--no-split Fit one model over all screens instead of separate production/comprehension models off
--sub-ids Comma-separated subject IDs to train (e.g. 031,032); omit for all all
--data-filters Comma-separated BIDS entity filters bounding the training corpus — see Segments and metadata none
--force Overwrite existing outputs (default skips them) off

--fold-by and --n-folds are paired

Give --n-folds when --fold-by names an entity, and omit it when --fold-by none. Supplying one without the other raises. --fold-by none fits a single model over the whole corpus; --fold-by run --n-folds 5 fits five cross-validated models grouped by run.

Example

Fit a single model per subject from semantic features plus phonemic-timing confounds, over the conv task:

hypline encoding train data/ \
  --tasks conv \
  --features semantic-gpt3 \
  --confounds phonemic-onset,phonemic-rate \
  --desc v1 \
  --fold-by none

Cross-validate by run, five folds, on a GPU:

hypline encoding train data/ \
  --tasks conv \
  --features semantic-gpt3 \
  --desc cv5 \
  --fold-by run \
  --n-folds 5 \
  --device cuda

hypline encoding analyze

Score a trained model's predictions against a target subject's actual BOLD, per role (production / comprehension / both). Three subject roles are independent:

  • model (--model-sub) — whose trained weights are loaded.
  • source (--source-sub) — whose features build the prediction inputs (X).
  • target (--target-sub) — whose actual BOLD is the comparison (Y).

Any combination is valid within one study: source == target is a within-subject fit; partners in one dyad share a conversation across different brains; different dyads are a scramble/null control (analyze warns). The values self and partner are accepted for --model-sub / --source-sub, resolved relative to --target-sub via participants.tsv.

hypline encoding analyze <dataset-root> \
  --target-sub  --model-sub  --model-desc  --desc  [OPTIONS]

Options

Option Description Default
--target-sub Subject whose actual BOLD and production/comprehension turns are scored against — required
--model-sub Subject whose trained model is loaded: an ID, or self/partner (relative to --target-sub) — required
--model-desc The --desc passed to encoding train (its encodingModel-<desc> tag) — required
--desc Variant label for this eval (alphanumeric); output lands under results/sub-<target>/encodingEval-<desc>/required
--source-sub Subject whose regressors drive the prediction: an ID, or self/partner self
--test-on Comma-separated BIDS entity filters naming which cells to score (e.g. run-6, or run-6,run-8); omit to score each model's out-of-sample cells out-of-sample cells
--force Overwrite existing outputs (default skips them) off

The output is per-fold, per-band, per-role, per-voxel scores. These are himalaya split scores — each band's own contribution to the joint prediction — so a band's value is not a plain Pearson correlation and need not fall in [-1, 1]. Roles are derived from the target's turns: prod and comp are the exclusive pure-production / pure-comprehension rows, and both is the union of any speech-active row. A role with no rows scores NaN rather than zero.

Example

Score subject 031's own model against its own BOLD (a within-subject fit). With default --test-on, analyze scores each model's out-of-sample cells, so the model must be folded. A --fold-by none model trained on every cell has no held-out cells and raises here; name cells with --test-on, or use a folded model as below:

hypline encoding analyze data/ \
  --target-sub 031 \
  --model-sub self \
  --model-desc cv5 \
  --desc selfeval

Score subject 031's BOLD using the partner's model and features (a cross-brain, shared-conversation eval) on run 6:

hypline encoding analyze data/ \
  --target-sub 031 \
  --model-sub partner \
  --source-sub partner \
  --model-desc v1 \
  --desc crosseval \
  --test-on run-6

Outputs

Both commands write to a new top-level results/ area, keyed by subject (one analysis output consumes many runs across sessions, so results carry no source-run or session entity):

<dataset-root>/results/
├── sub-031/encodingModel-v1/
│   ├── sub-031_result-encodingModel_desc-v1.joblib   # train: fitted model artifact
│   └── sub-031_result-encodingModel_desc-v1.json     # provenance sidecar (greppable)
└── sub-031/encodingEval-selfeval/
    └── sub-031_result-encodingEval_desc-selfeval.nc # analyze: per-voxel correlations (netCDF-4)
  • encodingModel-<desc>/ (train) — the fitted model as a .joblib blob, plus a JSON sidecar mirroring the recipe and provenance without unpickling.
  • encodingEval-<desc>/ (analyze) — the eval as a self-describing netCDF-4 file any tool can read, carrying provenance (model_sub, source_sub, target_sub, delays, bold_space, test_on, …) in its attributes.

Common errors

What you see Cause Fix
give --n-folds with --fold-by <entity>, and omit it with --fold-by none --fold-by and --n-folds were not passed both-or-neither. Pass --n-folds when --fold-by names an entity; omit it with --fold-by none.
n_folds='loo' needs >= 2 groups to fold; got 1 --fold-by names an entity with only one value in the training corpus, so there is nothing to leave out. Widen the corpus (task/filters), or fit unfolded with --fold-by none.
n_folds=N exceeds the M group(s) found for the fold_by entity (train) --n-folds is larger than the number of distinct --fold-by values available. Lower --n-folds to at most the group count, or use loo.
No regressor files match the given filters / No BOLD files match the given filters (train) --tasks, --features, --data-filters, --bold-space, or --bold-desc selected nothing. Confirm the features and desc-denoised BOLD exist and that the filters are not too narrow.
empty out-of-sample set — pass test_on to name cells (analyze) The model was trained unfolded (--fold-by none), so it has no held-out cells to score. Score a folded model, or name cells explicitly with --test-on.
test_on matched no available cells: … (analyze) --test-on names cells the source subject does not have. Check the --test-on filter against the runs/conditions that exist.
test_on entities […] not found on any available cell … check for a typo (analyze) A --test-on filter uses an entity that no cell carries. Fix the entity name (e.g. run-6, not ses-1).
Log warns source (…) and target (…) are different dyads (analyze) The model/source and target belong to different dyads — a scramble/null control. Expected for a null control; otherwise fix --source-sub / --model-sub so they share the target's dyad.
No subjects found No subjects under derivatives/fmriprep/, or --sub-ids excluded them all. Confirm denoised BOLD exists and that filters are not too narrow.

Loading results in Python

Both commands write Python objects, not tables. Load them back for downstream analysis with the encoding results API: load_eval for an eval, load_artifact for a fitted model.