Aditya369-WESAD-Binary โ wrist stress detector
by AGK FIRE INC
Small 1D-CNN (~225K params) on wrist Empatica E4 signals โ BINARY stress vs non-stress (baseline + amusement merged, the WESAD paper's binary setup), leave-one-subject-out evaluated.
Results (LOSO, 15 folds)
| metric | mean |
|---|---|
| accuracy | 0.871 |
| binary F1 | 0.781 |
| subject | acc | F1 | n_test |
|---|---|---|---|
| S10 | 1.000 | 1.000 | 75 |
| S11 | 0.737 | 0.474 | 76 |
| S13 | 0.987 | 0.978 | 75 |
| S14 | 0.533 | 0.222 | 75 |
| S15 | 0.853 | 0.744 | 75 |
| S16 | 0.973 | 0.952 | 73 |
| S17 | 0.547 | 0.292 | 75 |
| S2 | 0.930 | 0.865 | 71 |
| S3 | 0.658 | 0.419 | 73 |
| S4 | 1.000 | 1.000 | 72 |
| S5 | 0.973 | 0.950 | 73 |
| S6 | 1.000 | 1.000 | 73 |
| S7 | 0.945 | 0.913 | 73 |
| S8 | 0.932 | 0.902 | 74 |
| S9 | 1.000 | 1.000 | 72 |
Usage
import torch, json
from model import StressCNN
labels = json.load(open("label_map.json")) # {"0": "non-stress", "1": "stress"}
model = StressCNN(n_ch=6, n_cls=2)
model.load_state_dict(torch.load("best_model.pt", map_location="cpu"))
model.eval()
# x: (1, 6, 1920) float32 โ 60s window @32Hz, per-subject z-scored,
# channels [BVP, EDA, TEMP, ACCx, ACCy, ACCz]
with torch.no_grad():
pred = model(x).argmax(1).item() # 0=non-stress, 1=stress
Training
- Data: WESAD, wrist only (BVP 64Hz, EDA/TEMP 4Hz, ACC 32Hz), resampled to 32Hz
- 60s windows, 50% overlap, per-subject z-norm, majority-vote labels
- Binary labels: stress (WESAD 2) -> 1; baseline (1) + amusement (3) -> 0
- Leave-one-subject-out, early stopping on a val split of train subjects
best_model.pt= state_dict of the best fold
ยฉ 2026 AGK FIRE INC. Released under Apache 2.0.
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐ Ask for provider support