Aditya369-WESAD โ wrist stress/affect classifier
by AGK FIRE INC
Small 1D-CNN (~225K params) on wrist Empatica E4 signals โ 3-class baseline / stress / amusement, leave-one-subject-out evaluated.
Results (LOSO, 15 folds)
| metric | mean |
|---|---|
| accuracy | 0.669 |
| macro F1 | 0.572 |
| subject | acc | macro F1 | n_test |
|---|---|---|---|
| S10 | 0.747 | 0.590 | 75 |
| S11 | 0.895 | 0.894 | 76 |
| S13 | 0.653 | 0.549 | 75 |
| S14 | 0.400 | 0.217 | 75 |
| S15 | 0.707 | 0.637 | 75 |
| S16 | 0.616 | 0.633 | 73 |
| S17 | 0.333 | 0.254 | 75 |
| S2 | 0.718 | 0.545 | 71 |
| S3 | 0.575 | 0.514 | 73 |
| S4 | 0.556 | 0.578 | 72 |
| S5 | 0.767 | 0.563 | 73 |
| S6 | 0.890 | 0.876 | 73 |
| S7 | 0.616 | 0.529 | 73 |
| S8 | 0.730 | 0.585 | 74 |
| S9 | 0.833 | 0.622 | 72 |
Usage
import torch, json
from model import StressCNN
labels = json.load(open("label_map.json")) # {"1": "baseline", ...}
model = StressCNN(n_ch=6, n_cls=3)
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=baseline, 1=stress, 2=amusement
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
- 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