Rules:
- Freeze the trunk before personalizing
- Feed embeddings, never class logits
Personalizer.load/from_profilerequire re-attaching the encoder- Optional
embedding_dim=validates embedding width - Prefer factories (
for_deployment,for_features, …) over raw constructors
Canonical recipe
pip install nimbus-bci[riemann]):
Online head updates default to cumulative (
λ=1, byte-identical to 0.5). Optional forgetting on LDA/QDA uses λ\<1 (recency factor). Prefer session calibration and short stream budgets unless you intentionally enable forgetting.(n, d) so LDA/QDA heads can consume them. Spatial/temporal structure is collapsed at the encoder boundary, not inside the Bayesian head.
Ladder levels under one API
Shipped Personalizer path (same trunk, no architecture change):
Opt-in / priced (not deployment defaults):
Linear probe (LP) in Evidence is a matched sklearn control on the same frozen
Z, not a ladder level. Plain-language legend: Overview. Evidence for when L1 helps and how L3 is priced: Evidence.
Heads: product default is LDA (head="lda"). QDA is a documented severe-shift option behind the same API; Softmax stays optional (nimbus-bci[softmax]). The multi-trunk map in Paper 1 locks LDA for comparability — numbers for LDA vs QDA vs Softmax (EEGNet): Evidence → Which head?.
Optional shortcuts
Named helpers only reduce boilerplate. Architectures stay outside core:Personalizer / BrainState contract — not a confirmatory accuracy bake-off.
Active learning → personalization
Prefer Personalizer wrappers (encode for you), orCalibrationSession on the head: