Validation & Reporting
Every run is validated one way — a hold-out, cross-validation, or an Evaluation Protocol — and the app records which. Runs validated differently are never averaged, ranked or paired together, and every results page shows what a reviewer will ask for in that scenario.
One run, one validation design
A score means nothing until you know how it was tested. 0.977 from a random 20% of the rows and 0.634 from a model trained on the indoor session and tested on the outdoor session answer two different questions — the first is how well the model fits one recording, the second whether it survives a new session. Averaged together they answer neither.
So every run carries its validation design, written when the trainer runs. Every page reads it: the Results page summarises one design at a time, the Leaderboard ranks within a design, and Paired Statistics refuses to pair runs validated differently.
The three designs
The design is set by what is wired into the trainer's Val/Eval input (named Validation until October 2026; a graph saved then opens under the new name with its wire).
| Val/Eval input | Design | Headline number |
|---|---|---|
| nothing wired | Hold-out — the trainer's own split (split_strategy, test_size) | the test score on the held-out rows |
| Validation Scheme | Cross-validation (k-fold, leave-one-subject-out …) | mean ± SD over the folds |
| Evaluation Protocol | Protocol — train on some sessions or subjects, test on others | mean over the cross-session tests, with the within-session test beside it |
A protocol is also recorded with how its trainer used it: plain evaluation, adapted (DANN Trainer), fine-tuned (Fine-Tuning Trainer) or learned in sequence (the continual trainers). An adapted score is not a plain transfer score, so these never share a ranking either.
On the trainer card
The trainer's card on the canvas states its design in one line, and updates as soon as you wire or unwire Val/Eval:
The other two read, for example, validation: hold-out, stratified 20% and validation: 5-fold cross-validation, stratified (Validation Scheme).
What the Val/Eval input accepts
Only a Validation Scheme or an Evaluation Protocol. A scikit-learn splitter object or a bare number of folds is refused, because neither says on the canvas — or in the run record — how the run was validated:
BCILattice Trainer: The Val/Eval input received the number 5. It takes only a ‘Validation Scheme’ block (k-fold, grouped or leave-one-subject-out cross-validation) or an ‘Evaluation Protocol’ block (train on one session or subject, test on another). Wire one of those, or leave Val/Eval empty for the trainer's own hold-out (split_strategy, test_size).
What each scenario shows
One rule set decides what leads, what qualifies it and what a reader is warned of. It reads only what the run recorded — it never asks for a metric your graph did not compute.
Which number leads
| Scenario | Leads with | Shown beside it | Why |
|---|---|---|---|
| Classification, balanced classes (largest ÷ smallest ≤ 1.5) | accuracy | Cohen's κ; two classes: ROC AUC, balanced accuracy; more: macro F1, balanced accuracy | κ corrects for chance — the BCI Competition convention |
| Classification, unbalanced (P300, ErrP oddball …) | balanced accuracy (else ROC AUC, macro F1, MCC) | accuracy, ROC AUC, macro F1, κ, MCC | accuracy reads the majority rate |
| Continuous decoding (regression) | Pearson r (else R², RMSE, MAE) | R², RMSE, MAE | r ignores offset and scale |
| Multi-task | mean task accuracy | worst task accuracy | two tasks can average identically |
The chance bar at your n
At the trial counts a BCI study has, 1/k is not the bar. The app computes the accuracy that is above chance at p < 0.05 for your number of test trials (binomial, Combrisson & Jerbi 2015) and says whether the headline clears it: 40 two-class test trials need 0.65, not 0.50. For balanced classes it also reports the Wolpaw information transfer rate in bits per trial, with its assumptions stated.
What each design warns of
| Design | Reported as | The note beside it |
|---|---|---|
| Hold-out, random or stratified rows | the test score (mean ± SD over runs) | one split; trials recorded seconds apart sit on both sides, which inflates EEG/fNIRS scores — a within-recording estimate |
| Hold-out, unseen subjects or sessions | the test score | a generalisation estimate |
| Cross-validation | mean ± SD over folds | the fold SD is spread, not a confidence interval (folds share training rows); ungrouped folds are within-recording |
| Protocol | mean over the train → test pairs | the within-session test sits beside it; their difference is the cost of transfer |
| Protocol, adapted (DANN) | after adaptation, with before-adaptation and the gain | the model saw the test session's data, without labels |
| Protocol, fine-tuned | after fine-tuning, with before and the gain | the model saw a labelled slice of each test session |
| Fewer than 3 runs | — | the drop is described, not tested |
On the Execute page
When a run finishes, its design sits on the run bar beside the run id — Validation: Evaluation Protocol, across sessions — so the tiles below are never read without it.
Save knowledge of this run
Reading the results tables
Every results table in the app follows one rule: a row is an evaluation, a column is a metric. What was tested is kept as separate facts and never folded into a metric's name. Averaging (macro, weighted) is a band over the metrics it groups.
| Column | What it says | Example |
|---|---|---|
| Condition | the train → test pair the row belongs to. All rows of one pair come first, then the next pair | indoor → outdoor |
| Evaluation | what the model was tested against | Within-session, Cross-session, Hold-out 20%, Stratified 5-fold CV |
| Training condition | the data the tested model was trained on | indoor · indoor 20% + outdoor 20% (fine-tuned) |
| Testing condition | the data it was tested on | outdoor |
A run without an Evaluation Protocol has no pair, so it shows the Evaluation column only.
| Subject | Workflow / Pipeline | Condition | Evaluation | Training condition | Testing condition | n test | Accuracy | Cohen's kappa | Macro | ||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Precision | Recall | F1 | |||||||||
| sub10 | CNN | indoor → outdoor | Within-session | indoor | indoor | 60 | 0.910 | 0.820 | 0.912 | 0.910 | 0.910 |
| Cross-session | indoor | outdoor | 300 | 0.640 | 0.280 | 0.760 | 0.640 | 0.593 | |||
| indoor + outdoor 20% | outdoor | 240 | 0.742 | 0.484 | 0.781 | 0.742 | 0.731 | ||||
| outdoor → indoor | Within-session | outdoor | outdoor | 60 | 0.880 | 0.760 | 0.884 | 0.880 | 0.879 | ||
| Cross-session | outdoor | indoor | 300 | 0.628 | 0.256 | 0.742 | 0.628 | 0.581 | |||
| Tab | One row is | Columns |
|---|---|---|
| Per Pipeline | pipeline × train → test pair × evaluation | Subjects, then every metric as mean ± SD across subjects (each subject's own mean first); the best per evaluation in bold. Under it, Contrasts: Within − Cross, Fine-tuned − Cross, after − before adaptation, pipeline vs pipeline — metric, Δ mean, 95% CI, test, p, paired on subject |
| Per Subject | one train → test evaluation of one subject | n train, n test, every metric |
| Detailed | one train → test evaluation | for the metric picked above the table: train score, test score, gap, 95% CI, chance, permutation p |
| Per Class | one class of one evaluation | support, precision, recall, F1 |
An Evaluation filter above the long tables shows one evaluation at a time. n always names what it counts — Subjects, Folds, or n train / n test. For a fine-tuned row, n train is the rows of the fine-tuning itself. Bold “best” compares only rows with the same pair, evaluation and training condition: a cross-session score is never best against a within-session one. A summary of a run with more than six pairs (every subject in turn against every other) pools them into one row per evaluation and names roles instead of parts: source and target. The design's headline metric leads the metric columns, its header in the accent colour over a rule. Right-click any table to copy it or save it as CSV.
The tiles above the tables carry the headline metric only: the metric is the tile's title and the evaluation its sub-line. A protocol run shows at most four — Within-session, the headline evaluation (Cross-session, or Cross-session · DANN / Fine-tuned), the protocol's own Fine-tuned (…) row when present, and Δ Within − Cross; Unadapted is in the tables. The Results page uses the same tiles.
Reading the train → test matrix
The session on the left is the one the model trained on; the session below is the one it was tested on. The outlined diagonal is the same-session test, on trials of that session the model never trained on. The key under the figure says this on every copy:
sub10 — CNN — Cross-session accuracy
| indoor | 0.910 | 0.640 |
| outdoor | 0.600 | 0.880 |
| indoor | outdoor |
Tested on (session)
Fine-tuning for the other session
With the plain BCILattice Trainer a model trained on indoor is tested on outdoor as trained. To also measure how far a little labelled data from the new session takes it, set fine_tune on the Evaluation Protocol (default none):
fine_tune | The model is trained a second time on | Sizes to set |
|---|---|---|
none | nothing: it is tested as trained only | — |
target only | a labelled slice of the other session | target_size (default 0.2 = 20%) |
source + target | a slice of its own training session together with a labelled slice of the other session, so it does not forget its own session | source_size and target_size (both default 0.2) |
A network is trained further from its trained weights. A model that cannot train further (LDA, SVM and the other classical models start from zero on every fit) is trained again from zero instead: on its whole training session plus the slice for target only, on the two slices for source + target. The run log, the methods text and the saved model's name say which happened.
| Condition | Evaluation | Training condition | Testing condition | Tested on |
|---|---|---|---|---|
| indoor → outdoor | Within-session | indoor | indoor | the indoor trials held back from training |
| Cross-session | indoor | outdoor | all of outdoor | |
| Within-session | indoor 20% + outdoor 20% | indoor | the same held-back indoor trials as the first row; source + target only | |
| Cross-session | indoor 20% + outdoor 20% | outdoor | the 80% of outdoor it never saw |
The two fine-tuned rows have the same training condition: one model, tested on each session. The first and third rows are the same trials, so their difference is what fine-tuning cost the model on its own session; the Contrasts table tests it subject by subject. With target only the training condition reads indoor + outdoor 20% and the third row is not reported. That row needs within_check set to hold-out: without held-back trials there is nothing honest to test on, and the run says so. With both directions, the same four rows follow for outdoor → indoor.
Each fine-tuned model is kept for export as CNN · trained on indoor, fine-tuned on 20% of outdoor (or retrained with 20% of outdoor for a model that cannot train further). Each fine_tune choice, and each size, is its own validation design, so a run that fine-tunes is never ranked against one that did not, or one that fine-tuned differently. The DANN and Fine-Tuning trainers use the same columns: the training condition reads indoor + outdoor (no labels) for DANN and indoor + part of outdoor for the Fine-Tuning Trainer, with the row as trained (indoor) beside it.
Until October 2026 this setting was a tick box called adapt with one size, adapt_size. A graph saved then opens as target only with the same size, and its rows read Fine-tuned where they read Adapted.
| Trainer wired to the protocol | Transfer | Settings the protocol card shows |
|---|---|---|
| BCILattice Trainer (and Multi-Task, Distillation) | the fine_tune choice above | fine_tune; target_size while it is on; source_size for source + target |
| DANN Trainer | always: adapts to that session's data without its labels; also tested before adapting | check_before_transfer |
| Fine-Tuning Trainer | always: fine-tuned on target_size of that session with labels, tested on the rest | target_size, check_before_transfer |
On the Results page
An experiment that holds runs validated differently shows one design at a time — the latest run's by default — with a switch listing every design and its run count. The tiles, the reading notes, the matrix and the charts all describe the runs of that design only.
Under the design line the page has tabs. The first, Results, is the design's runs taken together. Each run of the design then has a tab of its own, holding that run's Per Pipeline, Detailed, Per Subject, Per Class, Statistics, Graphs and Training Curves — the same tabs Execute shows while the run is on screen. With one run that tab is called Run Results; with several, each is named by its run and when it ran. Running the experiment again adds a tab; it does not add a second Results tab, because the Results tab already averages every run of the design.
| Check | Value | Result |
|---|---|---|
| Chance at n = 60, 2 classes (binomial, p < 0.05) | 0.617 | Cleared (0.634) |
| Class balance (largest ÷ smallest) | 1.20 | Balanced |
| Smallest test n | 60 | ≥ 30 |
| Subjects | 4 | Testable |
| Workflow / Pipeline | Evaluation | Subjects | Accuracy | Cohen's kappa |
|---|---|---|---|---|
| CNN | Within-session | 4 | 0.895 ± 0.022 | 0.790 ± 0.044 |
| Cross-session | 4 | 0.634 ± 0.041 | 0.268 ± 0.082 | |
| EEGNet | Within-session | 4 | 0.871 ± 0.030 | 0.742 ± 0.060 |
| Cross-session | 4 | 0.602 ± 0.052 | 0.204 ± 0.104 |
- Results table: pipeline × evaluation for the design shown, mean ± SD across participants — each participant averaged over the design's runs first. The best per evaluation is bold. Under it, the same Contrasts table as on Execute.
- Reporting checks: each check is a value and a result — the chance bar at your n (binomial, p < 0.05), the class ratio (unbalanced above 1.5), the smallest test (⚠ under 30) and the subjects (⚠ under 3: the drop is described, not tested). The design's caveat — a row-level split, DANN having seen the test data — is the tooltip of the Validation line.
- Pair scores such as
indoor -> outdoor accuracyare never tiles — they are the train → test matrix under the table, each cell the mean over the design's runs. - Charts come from the latest run of the design shown, the run-level picture first, in the order that design is read. They are at the foot of the Results tab; a run with twenty participants stores some four hundred pictures, and each is drawn when you pick it from the list.
- A run's tab is filled the first time you open it, and stays filled while the experiment is on screen. Its Statistics tab tests that run's own differences; nothing in it is pooled with another run.
- Opening an experiment shows a progress bar that names the part being built — scores, reporting checks, results tables, train → test matrix, performance charts — and the page appears complete.
- The study overview shows counts, not scores — Experiments, Runs, Subjects, Validation designs — and then each experiment × pipeline × evaluation for its latest design. There is no study-wide “best”: a best is only meaningful within one design, and it is bold inside that table.
In the Experiment Hub
Every page that lists or compares runs reads each run's evaluations — never one number pooled over its pipelines and evaluations. A score always says which metric, which evaluation and which pipeline it is.
Run History
The run list shows, per run, its Validation, how many Pipelines and Subjects, and its headline as three cells — Metric, Evaluation and Score (the best pipeline's mean ± SD across subjects; the tooltip names the pipeline). Run ID is an optional column and the name's tooltip.
| Name | Validation | Pipelines | Subjects | Metric | Evaluation | Score | Grade |
|---|---|---|---|---|---|---|---|
| CNN, indoor ↔ outdoor | Evaluation Protocol, across sessions | 2 | 8 | Accuracy | Cross-session | 0.634 ± 0.041 | publication |
| CNN baseline | hold-out, stratified 20% | 1 | 8 | Accuracy | Hold-out 20% · stratified | 0.977 ± 0.012 | exploratory |
| Tab | One row is | What it shows |
|---|---|---|
| Comparison | run × pipeline × evaluation | every metric, the best per evaluation in bold; three charts drawn from the same rows — score by evaluation, per-subject spread, score against cohort size — so a chart and the table always agree |
| Diagnostics | run × pipeline × same-data evaluation (design, Within-session) | for the metric picked: Subjects · Train · Test · Gap (⚠ above 0.15) · Fit (one word: Good fit, Overfit, Underfit, At chance …). A cross-session gap is a transfer drop, not overfitting, and is in Paired Statistics |
| Changes | a setting | Section (Cohort, Data, Split, Model, Trainer, Environment) · Setting · one column per run; a value that differs from the first run's is bold, the raw key is the tooltip. The heading counts them: Changes · 3 settings differ |
Paired Statistics
Two tables and no notes. Scores is the Comparison table of the selected runs. Contrasts pairs each run with the first (the reference) on one metric and one correction, per pipeline + evaluation + method, paired on subject (× train → test pair for a protocol). Method is what the trainer did before the test (DANN, Fine-tuned …); this table pools every pair, so it has no Condition column:
| Run | Workflow / Pipeline | Evaluation | Method | Metric | Test | Subjects | Δ mean | 95% CI | p | p (FDR) |
|---|---|---|---|---|---|---|---|---|---|---|
| Run 5 | CNN | Cross-session | — | Accuracy | Wilcoxon signed-rank | 8 | +0.034 | [+0.008, +0.060] | 0.016 | 0.031 |
| Run 3 | CNN | Cross-session | — | Accuracy | — | — | — | — | — | — |
A contrast that cannot be made is a row of — with the reason in its tooltip — runs validated differently, a pipeline one run lacks, fewer than two shared subjects. A refused cell is never blank, because a blank significance cell reads as “not significant”.
Leaderboard
One board per question — dataset × headline metric × validation design — picked from a Board list. An adapted or DANN design is its own design, so it gets its own board. A row is run × pipeline, the benchmark board's grain: a run with three pipelines is three entries and is never pooled.
| Rank | Run | Workflow / Pipeline | Subjects | Accuracy | Cohen's kappa | Δ best | Δ Within | Grade | Fit |
|---|---|---|---|---|---|---|---|---|---|
| 1 | Run 5 | CNN | 8 | 0.634 ± 0.041 | 0.268 ± 0.082 | — | +0.261 | publication | Good fit |
| 2 | Run 4 | EEGNet | 8 | 0.612 ± 0.052 | 0.224 ± 0.090 | −0.022 | +0.278 | publication | Good fit |
| — | Run 3 | CNN | 8 | 0.650 ± 0.060 | 0.300 ± 0.110 | — | +0.240 | exploratory | Overfit |
- Ranked on the board's headline metric (first, emphasised); the reporting standard's companions follow it.
- Δ Within (protocol boards) is each subject's Within − Cross, averaged: the cost of transfer.
- An exploratory or leakage row keeps its scores but gets
—for Rank and Δ best and sorts after the ranked rows — only a publication-grade run may be ranked or become the best. - Two values, both shown. When the headline has a second value, the metric becomes two columns under its name, and Rank and Δ best split the same way: each column is ranked on its own values, so an adapted score is only compared with other adapted scores. Which pair depends on the run: Before / After adaptation for DANN and fine-tuning (cross-part; the before side is the unadapted score when the run measured it), Before / After fine-tuning for a model's own part after source + target fine-tuning, and Just learned / After the sequence for class-, domain-incremental and continual learning (the difference is forgetting). A side the run did not measure is
—, never estimated. The website's boards show the same columns and ranks.
The Experiments list shows each experiment's latest design as Validation · Metric · Evaluation · Score (the Results page's mean ± SD) · Before → after · Runs — no colour pill, and never a best of runs, which could quote a run validated another way. Run History has the same Before → after column for each run's best pipeline, and Results Validation adds a two-value table (level · n · before · after · Δ) beside the comparison when the picked evaluation is one side of a pair — Within-session included.
Exporting the models
A protocol trains one model per training session — each in turn trains its own model — so a subject with two sessions has two models. Each is kept under its own name, by what it trained on:
| Model | Subject | Kind |
|---|---|---|
| Indoor vs outdoor — CNN · trained on indoor | sub10 | Deep learning |
| Indoor vs outdoor — CNN · trained on outdoor | sub10 | Deep learning |
Adapted and fine-tuned runs name the pair too (trained on indoor, adapted to outdoor). The model block's own object is never exported for a protocol run: each session trained a copy of it, so the original never learned anything.
Runs recorded before this
Old runs are classified once from what they already stored — their split policy and their Evaluation Protocol report — and nothing is re-run. Their design line ends with (read from the stored split). A run that stored neither is shown as Validation: not recorded; it is never guessed.
The methods text
The exported methods paragraph states the design and the reporting choices:
Performance was estimated by 5-fold cross-validation, stratified; scores are the mean ± SD over folds, each fold scored by a model that did not train on it. Chance level was 0.500; at the number of test trials, accuracy of at least 0.535 is above chance at p < 0.05 (binomial test).
Related