Ridge EEG Interpretability Figure Plan#

Context: Amrith asked for visualizations and analysis of the existing benchmark, not additional benchmarks. The goal is to sanity-check why the ridge baseline performs well on the current EEG future-state forecasting task.

Current benchmark object#

From src/neurotwin/data/prepared_tasks.py, the EEG future-state task is built as:

  • split prepared EEG recordings into windows

  • for each adjacent pair of windows:

    • X = current EEG window

    • Y = next EEG window

  • ridge regression receives flattened X rows and predicts flattened Y rows

  • training uses train-only centering/scaling in NumpyRidgeBaseline

So the mentor-facing story should be:

Ridge is not decoding abstract neural state. It is learning a regularized linear map from recent EEG samples/channels to the immediately following EEG samples/channels. If neighboring windows are autocorrelated, ridge can perform surprisingly well.

Figure set to generate#

Figure 1. Raw EEG window to ridge input/target#

Purpose: show exactly what goes into ridge and what comes out.

Panels:

  1. Raw EEG traces for 4-8 channels across one example window pair.

  2. Shaded X/current window and Y/next window regions.

  3. Small matrix view showing X flattened from [time, channel] into a feature vector.

  4. Label: ridge predicts next-window EEG samples, not class labels or new benchmarks.

What it answers:

  • “What actually goes into ridge regression?”

  • “What is being predicted?”

Figure 2. Ridge prediction overlay#

Purpose: sanity-check whether ridge is copying smooth temporal structure or capturing meaningful dynamics.

Panels:

  1. Actual future EEG traces for selected channels.

  2. Ridge-predicted future traces overlaid.

  3. Residual trace below each channel.

  4. Per-channel Pearson r / MSE annotations.

What it answers:

  • “Where is ridge good?”

  • “Is it mostly tracking low-frequency/autocorrelated structure?”

Figure 3. Autocorrelation and lag structure#

Purpose: explain why a linear model can do well.

Panels:

  1. Autocorrelation curve for representative channels.

  2. Cross-window correlation heatmap: current-window channel/time summaries vs future-window channel/time summaries.

  3. Mark the forecast horizon/window boundary.

What it answers:

  • “Does the task contain short-horizon temporal continuity that favors ridge?”

Figure 4. Ridge coefficient map#

Purpose: inspect learned linear weights.

Panels:

  1. Heatmap of coefficient magnitude aggregated by input channel and output channel.

  2. Optional time-lag heatmap if coefficient tensor can be reshaped as [input_time, input_channel, output_time, output_channel].

  3. Highlight diagonal/channel-local structure if present.

What it answers:

  • “Is ridge using same-channel temporal persistence, cross-channel mixing, or broad global leakage-like signals?”

Figure 5. Split/leakage sanity diagram#

Purpose: reassure that this is analysis of the current result, not accidental leakage.

Panels:

  1. Train/val/test split schematic at subject/session/run level.

  2. Explicit note: normalization fit on train only.

  3. Boundary buffer note if used or recommended.

What it answers:

  • “Could ridge be winning because of leakage or duplicated adjacent windows across split boundaries?”

Suggested caption language#

Example diagnostic visualization for the existing MOABB EEG future-state benchmark. The ridge baseline receives a flattened current EEG window X_t and predicts the next EEG window X_{t+1}. Because the target is a near-future EEG segment, strong short-horizon autocorrelation can make a regularized linear model competitive. These figures are intended as sanity checks of the current benchmark result rather than new benchmark evidence.

Implementation notes#

Use scripts/analysis/plot_ridge_eeg_diagnostics.py.

Required real-data input should eventually be one .npz containing:

  • x_train: [n_train, time, channels] or [n_train, features]

  • y_train: [n_train, time, channels] or [n_train, features]

  • x_test: [n_test, time, channels] or [n_test, features]

  • y_test: [n_test, time, channels] or [n_test, features]

  • optional sfreq, channel_names

If only flattened features are available, pass --time-length and --n-channels so the script can reshape for waveform figures.