{
  "nbformat": 4,
  "nbformat_minor": 5,
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Train your own EEG decoder\n",
        "\n",
        "This notebook produces a local experiment bundle for Daybreak Decoder Studio. You need labeled, consented, de-identified EEG; no data is uploaded. Read README.md for required channel order, filtering, epoch construction and limitations."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 1. Install the tested environment\n",
        "Use Python 3.12 and matching CPU PyTorch/torchaudio. Restart the kernel after installation if needed."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "%pip install torch==2.11.0 torchaudio==2.11.0 --index-url https://download.pytorch.org/whl/cpu\n",
        "%pip install -r requirements.txt"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 2. Prepare epochs from your own recordings\n",
        "Using MNE, filter each run separately (4\u201338 Hz), resample to 128 Hz, select C3/Cz/C4 in that order, then make three-second labeled epochs. Keep entire sessions as groups. Do not mix epochs from one recording across train and test.\n",
        "\n",
        "The following cell is a template to complete with your prepared arrays; it does not generate example EEG."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "# Replace these variables with your prepared arrays before running.\n",
        "# X_uv: float32, [trials, 3, 384], microvolts\n",
        "# labels: integer 0/1, [trials]\n",
        "# session_ids: strings, [trials], such as session_1, session_2, session_3\n",
        "import numpy as np\n",
        "# np.savez(\"epochs.npz\", X=X_uv, y=labels, groups=session_ids)"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 3. Train on earlier sessions; hold out one whole session\n",
        "The script fits scaling on training data only, trains an EEGNet for 60 fixed epochs, checks PyTorch/ONNX agreement, and exports the checkpoint plus held-out test data. Decide the test session before seeing performance. Adapt the labels and group ID below."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "import subprocess, sys\n",
        "subprocess.run([sys.executable, \"train_custom.py\", \"epochs.npz\", \"--test-group\", \"session_3\", \"--class-0\", \"Left\", \"--class-1\", \"Right\"], check=True)"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 4. Inspect on Daybreak\n",
        "Open https://daybreak-neural.org/decoder and select **Import a trained experiment**. Choose `experiment.json`, then **Run decoding**. Inspect the confusion matrix and export results.\n",
        "\n",
        "These measurements describe your test session only. Model selection based on this test set requires another untouched test set. Assess performance across multiple participants/sessions before any broader claims. Softmax scores are not calibrated confidence."
      ]
    }
  ]
}