Enhancing a reproducible somatosensory response with DSS#

Can trial-average DSS learned on one set of somatosensory MEG trials enrich reproducible evoked activity in independent held-out trials while limiting distortion of the held-out evoked response?

DSS ranks components according to a chosen bias criterion. Here the bias is trial reproducibility, so the fitted spatial subspace emphasizes activity that survives averaging across repeated trials. A high score means strong according to that criterion; it does not identify a neural source by itself [1][2].

The before-versus-after comparison is a held-out preservation control, not a clean-neural ground truth.

References#

Load one somatosensory condition from the MNE Somato dataset#

import mne
import numpy as np
from mne.datasets import somato

from mne_denoise.dss import DSS, AverageBias
from mne_denoise.viz import plot_component_score_curve, plot_evoked_gfp_comparison

data_path = somato.data_path(update_path=False)
subject = "01"
task = "somato"
raw_fname = data_path / f"sub-{subject}" / "meg" / f"sub-{subject}_task-{task}_meg.fif"
raw = mne.io.read_raw_fif(raw_fname, preload=False, verbose="ERROR")
raw.crop(0.0, 360.0)
events = mne.find_events(raw, stim_channel="STI 014", verbose=False)
event_id = 1
somato_events = events[events[:, 2] == event_id]

raw.pick("grad", exclude="bads", verbose="ERROR").load_data(verbose="ERROR")
raw.filter(1.0, 40.0, verbose="ERROR")
epochs = mne.Epochs(
    raw,
    somato_events,
    event_id={"Somato": event_id},
    tmin=-0.2,
    tmax=0.5,
    baseline=(None, 0.0),
    preload=True,
    reject=None,
    verbose=False,
)

Split trials before fitting#

Alternating trials keep both the training and held-out sets broad in time. The two held-out reproducibility halves are alternating as well, so their comparison is not an early-versus-late drift comparison.

train_epochs = epochs[::2]
held_out_epochs = epochs[1::2]
held_out_a = held_out_epochs[::2]
held_out_b = held_out_epochs[1::2]
if min(len(held_out_a), len(held_out_b)) < 2:
    raise RuntimeError(
        "The Somato crop must provide at least two trials in each alternating "
        "held-out half."
    )

n_components = 6
n_select = 3
model = DSS(
    bias=AverageBias(axis="epochs"),
    n_components=n_components,
    n_select=n_select,
    component_action="retain",
    # Use the Somato recording's declared MNE rank for both covariances.
    cov_kws={"rank": "info"},
    verbose=False,
)
model.fit(train_epochs)
cleaned_held_out = model.transform(held_out_epochs)

Evaluate held-out reproducibility and evoked preservation#

before_evoked = held_out_epochs.average()
after_evoked = cleaned_held_out.average()
post_mask = before_evoked.times >= 0.0

# Two independent alternating halves of the held-out set provide the primary
# repeatability endpoint. Both halves use the same fitted spatial operator.
before_half_a = held_out_a.average()
before_half_b = held_out_b.average()
after_half_a = cleaned_held_out[::2].average()
after_half_b = cleaned_held_out[1::2].average()

split_half_before = float(
    np.corrcoef(
        before_half_a.get_data()[:, post_mask].ravel(),
        before_half_b.get_data()[:, post_mask].ravel(),
    )[0, 1]
)
split_half_after = float(
    np.corrcoef(
        after_half_a.get_data()[:, post_mask].ravel(),
        after_half_b.get_data()[:, post_mask].ravel(),
    )[0, 1]
)
held_out_waveform_correlation = float(
    np.corrcoef(
        before_evoked.get_data()[:, post_mask].ravel(),
        after_evoked.get_data()[:, post_mask].ravel(),
    )[0, 1]
)
before_sensor_rms = np.sqrt(np.mean(before_evoked.get_data()[:, post_mask] ** 2))
after_sensor_rms = np.sqrt(np.mean(after_evoked.get_data()[:, post_mask] ** 2))
held_out_sensor_rms_change = (after_sensor_rms - before_sensor_rms) / before_sensor_rms

print("Held-out somatosensory evoked DSS")
print(f"Training/held-out trial counts: {len(train_epochs)}/{len(held_out_epochs)}")
print(
    "Held-out split-half evoked correlation before / after: "
    f"{split_half_before:.4f} / {split_half_after:.4f}"
)
print(
    "Held-out input-vs-cleaned post-stimulus evoked waveform correlation: "
    f"{held_out_waveform_correlation:.4f}"
)
print(f"Held-out sensor-RMS normalized change: {held_out_sensor_rms_change:.4f}")
print("Preservation control: not clean-neural ground truth")
Held-out somatosensory evoked DSS
Training/held-out trial counts: 22/22
Held-out split-half evoked correlation before / after: 0.6646 / 0.9456
Held-out input-vs-cleaned post-stimulus evoked waveform correlation: 0.7182
Held-out sensor-RMS normalized change: -0.3016
Preservation control: not clean-neural ground truth

Inspect the held-out evoked result#

The main figure compares only the held-out evoked averages. The optional score curve shows the reproducibility-biased ordering used to retain the leading components.

plot_evoked_gfp_comparison(
    before_evoked,
    after_evoked,
    times=before_evoked.times,
    ci=None,
    labels=("Held-out input", "Held-out DSS retain"),
    x_label="Time (s)",
    y_label="Sensor RMS (T/m)",
    title="Held-out somatosensory evoked sensor RMS",
    show=False,
)

plot_component_score_curve(model, mode="ratio", show=False)
  • Held-out somatosensory evoked sensor RMS
  • Component Scores
<Figure size 1400x800 with 1 Axes>

Interpretation#

The component subspace was learned from the training trials, while both split-half reproducibility and the before-versus-after evoked comparison use held-out trials only. Increased repeatability is evidence that the selected subspace follows the specified bias on new trials; it is not proof that every retained component is neural. The before-versus-after comparison is a preservation control, not clean-neural ground truth. The retained count should be checked against the evoked endpoint and the signal of interest in an actual study.

Total running time of the script: (0 minutes 1.213 seconds)