File size: 2,712 Bytes
dc9d5f6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
"""EEG Annotation Tool adapter for SenuaLab EEG-DINO Medium."""

from __future__ import annotations

try:
    from braindecode.models import EEGDINO
except ImportError as exc:
    raise ImportError("This model requires braindecode[hub]==1.6.1") from exc

from .preprocessing import INTERNATIONAL_10_20_CHANNELS, preprocess_batch as _preprocess


MODEL_CHANNELS = INTERNATIONAL_10_20_CHANNELS
MODEL_INPUT_SAMPLES = 800
MODEL_SAMPLING_RATE_HZ = 200.0
MODEL_WINDOW_SECONDS = 4.0
MODEL_ENTRY_CLASS = "SenuaEEGDINOMedium"
MODEL_NUM_CLASSES = 2
MODEL_CLASS_LABELS = ["Non-IED", "IED"]
MODEL_NON_IED_CLASS_INDEX = 0
MODEL_IED_CLASS_INDICES = [1]
MODEL_DESCRIPTION = "SenuaLab partially fine-tuned EEG-DINO Medium binary IED detector"
MODEL_BATCH_PREPROCESSOR = "preprocess_batch"
MODEL_REQUIRED_REFERENCE = "common average (applied by model adapter)"
MODEL_REQUIRED_FILTERS = ["1-45 Hz zero-phase Butterworth (applied by model adapter)"]
MODEL_REQUIRED_NORMALIZATION = "global four-second window z-score, clipped to [-8,8]"
MODEL_INPUT_UNIT = "scale-invariant after window z-score"
MODEL_SOURCE_SIGNAL_POLICY = "raw"
MODEL_REQUIRES_FULL_WINDOW = True
MODEL_REQUIRES_ALL_CHANNELS = True
MODEL_DEFAULT_THRESHOLD = 0.619140625
MODEL_DEFAULT_STEP_MS = 500.0
MODEL_DEFAULT_PAD_POLICY = "skip"
MODEL_DEFAULT_BATCH_SIZE = 8
MODEL_DEFAULT_BATCH_MEMORY_MB = 384.0
MODEL_VALIDATION_NOTE = "Threshold selected on vEpiSet validation subjects; requires Braindecode 1.6.1 and is not externally validated."
MODEL_PREPROCESSING_NOTE = "The adapter applies the released 1-45 Hz, common-average, 200 Hz resampling, and global-window z-score pipeline."


def preprocess_batch(batch, source_sfreq=None, channel_names=None):
    return _preprocess(
        batch,
        source_sfreq=source_sfreq,
        target_sfreq=200,
        target_samples=800,
        channel_names=channel_names,
    )


class SenuaEEGDINOMedium(EEGDINO):
    def __init__(self, num_channels: int = 19, num_classes: int = 2, input_length: int = 800):
        if num_channels != 19 or input_length != 800:
            raise ValueError("SenuaEEGDINOMedium requires 19 channels and 800 samples")
        super().__init__(
            n_outputs=num_classes,
            n_chans=19,
            n_times=800,
            sfreq=200,
            patch_size=200,
            n_layer=16,
            nhead=8,
            dim_feedforward=1024,
            channels_kernel_stride_padding_norm=(
                (64, 49, 25, 24, (8, 64)),
                (128, 3, 1, 1, (8, 128)),
                (64, 3, 1, 1, (8, 64)),
            ),
            n_channel_embeddings=19,
            n_global_tokens=1,
            global_token_layer=1,
            drop_prob=0.1,
        )