This project classifies EEG stress state into 3 classes:
- Low (0)
- Medium (1)
- High (2)
It implements an enhanced CBGG pipeline inspired by Roy et al. (2023), with additional hand-crafted EEG features and an ablation study to measure the contribution of each feature group.
The full data flow is:
- Load raw EEG and labels from MAT files.
- Align tensor shape into segment-major format.
- Expand subject-level labels to segment-level labels.
- Apply wavelet decomposition (DWT) per channel.
- Compute per-band features (CBGG + added feature set).
- Concatenate features into one vector per segment.
- Normalize features fold-wise (no leakage).
- Train CBGG deep model using stratified cross-validation.
- Aggregate fold predictions for final metrics and plots.
- Run ablation experiments.
- Save all artifacts to Kaggle working directory.
- Input data root: /kaggle/input
- Outputs: /kaggle/working/results
- Checkpoints: /kaggle/working/checkpoints
The notebook configures:
- MirroredStrategy for multi-GPU data parallel training when multiple GPUs are available.
- Mixed precision (mixed_float16) to accelerate Tensor Core operations on T4.
- XLA JIT for graph-level optimization.
- Batch size scaling by number of replicas.
- Learning-rate scaling with replica count.
This means on dual T4 runtime, global batch and optimizer step settings adapt automatically.
The loader searches recursively under /kaggle/input for:
- dataset.mat
- class_012.mat
If both are found in the same folder, that pair is preferred.
Raw EEG is transformed into:
- X shape: (n_segments, n_channels, n_samples)
This standardization is critical because all downstream signal processing assumes segment-major indexing.
The provided labels are subject-level, while training occurs at segment-level. The notebook computes how many segments belong to each subject and expands labels via repeat operations.
Result:
- One label per EEG segment.
EEG is non-stationary. DWT captures localized time-frequency behavior better than plain FFT-only summaries.
- Wavelet family: db4 (Daubechies-4)
Why db4:
- Commonly used in EEG literature.
- Good compact support and smoothness tradeoff.
- Effective for transient and oscillatory EEG components.
The notebook computes maximum safe level from signal length and uses a bounded level (typically up to 3) for stable feature extraction.
Depending on selected level, resulting coefficient sets represent coarse-to-fine frequency content (approximation + detail sub-bands).
Features are computed per band and per channel, then flattened and concatenated.
For each band/channel:
- Mean: central tendency of coefficients.
- Variance: signal dispersion/energy spread.
- Skewness: asymmetry of coefficient distribution.
- Kurtosis: tail/heaviness and peakedness.
- Power: mean squared magnitude (energy proxy).
Interpretation:
- Together they describe distribution shape + energy profile of each EEG band.
For each band/channel:
- Differential Entropy (DE): entropy-like complexity measure for near-Gaussian assumptions.
- Hjorth Activity: variance (signal power in time domain).
- Hjorth Mobility: mean frequency tendency via first derivative dynamics.
- Hjorth Complexity: waveform shape complexity relative to a pure sine.
- Katz Fractal Dimension (KFD): geometric complexity/irregularity of EEG trajectory.
Cross-band/channel ratios:
- High-band to low-band power ratio.
- Mid-band to low-band power ratio (when available).
Why these were added:
- They encode non-linear complexity and relative spectral balance not fully captured by simple moments.
Depending on flags:
- Baseline: CBGG-only features.
- Extended: CBGG + added features.
- Optional: added features only.
A MinMaxScaler is fitted inside each training fold and applied to validation fold using the train-fit scaler only.
This prevents data leakage from validation into training statistics and gives a realistic estimate of generalization.
Input tensor per sample:
- (n_features, 1)
Sequence of layers:
- Conv1D(128, kernel_size=1)
- Softmax activation
- MaxPooling1D(pool_size=1)
- Bidirectional LSTM(64, return_sequences=True)
- GRU(32, return_sequences=True)
- GRU(16, return_sequences=False)
- Dropout(0.2)
- Dense(n_classes, softmax)
- Conv1D(1x1): channel-wise projection/mixing over feature axis.
- BiLSTM: models forward and backward temporal dependencies in the transformed sequence.
- Stacked GRUs: compact recurrent refinement with lower parameter cost than pure LSTM stacks.
- Dropout: regularization against overfitting.
- Final softmax: class probability distribution.
Final Dense output is forced to float32 dtype for numerically stable softmax/loss when using mixed_float16 globally.
- Stratified K-fold (typically 10 folds) at segment level.
- Class balance preserved across folds.
- Adam optimizer.
- Categorical cross-entropy.
- One-hot encoded targets.
Per-fold class weights are computed from training labels and passed to fit().
- ReduceLROnPlateau lowers LR when val_loss stalls.
- EarlyStopping monitors val_loss with patience/min_delta.
- Best weights restored before evaluation.
This combination improves speed and avoids wasting epochs after convergence.
Per fold and overall:
- Accuracy
- Weighted F1
- Multi-class ROC-AUC (OvR)
Global outputs:
- Combined confusion matrix.
- Classification report.
- ROC curves per class.
- Convergence curves per fold.
Paper-style metrics table:
- Precision
- Sensitivity (Recall)
- Specificity
- F1
- Accuracy
- Positive likelihood ratio (+LR)
- Negative likelihood ratio (-LR)
Three configurations are compared:
- CBGG features only (baseline)
- Added features only
- CBGG + added features (enhanced)
Purpose:
- Quantify incremental value of the added feature family.
- Validate that accuracy gains are due to feature enrichment, not randomness.
All artifacts are written under /kaggle/working/results (or checkpoints folder), including:
- cv_results.json
- cv_results_partial.json
- metrics_table.csv
- ablation_results.json
- final_summary.json
- convergence_curves.png
- confusion_matrix.png
- roc_curve.png
- ablation_chart.png
Run notebook sequentially from top to bottom:
- Configuration and runtime setup
- Kaggle path checks
- Data loading
- Shape and label expansion
- DWT
- Feature extraction
- Main CV training
- Metrics and plots
- Ablation
- Final summary
- DWT stage: where discriminative time-frequency content exists.
- CBGG moment features: distributional shifts across stress levels.
- Entropy/Hjorth/KFD: complexity and dynamical irregularity changes with stress.
- Ratios: relative dominance of high vs low-frequency energy.
- Confusion matrix: which stress classes overlap behaviorally.
- ROC-AUC per class: separability quality per class boundary.
- Ablation: direct evidence for contribution of feature families.
- Global random seeds are fixed.
- Fold splitting is deterministic via RANDOM_SEED.
- Output paths are deterministic.
- Early stopping and LR scheduling reduce variance from over-training.
- Segment-level split can inflate estimates if subject leakage exists in ordering; subject-independent CV can be added for stricter generalization testing.
- Add confidence intervals via repeated CV seeds.
- Add calibration checks (ECE/Brier) for probability reliability.
- Add SHAP or permutation importance on engineered features for interpretability.
- If only one GPU is shown, runtime may not provide dual GPU; code still works.
- If MAT files are not found, verify dataset attachment under /kaggle/input.
- If memory issues occur, reduce BASE_BATCH_SIZE.
- If training is unstable, lower EFFECTIVE_LR or increase EarlyStopping patience slightly.