Skip to content

Training

The model is a BiLSTM-CNN with attention trained via Leave-One-Subject-Out (LOSO) cross-validation.

Quick start

1. Smoke test with synthetic data (no hardware needed)

cd wearable_har_v2
.venv/bin/python -m host.train_model --synthetic --epochs 2 --output /tmp/test_model.pt

This generates random data mimicking the 11 classes, trains for 2 epochs, exports a TorchScript model, and prints a validation accuracy for each subject.

2. Train on real data

.venv/bin/python -m host.train_model \
    --data_dir recordings/ \
    --output models/worker_har_best.pt \
    --loso \
    --epochs 50 \
    --batch_size 32

CLI flags

Flag Default Description
--data_dir (required) Directory of .h5 recordings
--output models/model.pt TorchScript export path
--synthetic False Generate random training data instead of loading .h5 files
--loso False Leave-One-Subject-Out CV (splits by subject_id HDF5 attr)
--epochs 30 Training epochs per fold
--batch_size 32 Mini-batch size
--lr 0.001 Adam learning rate
--hidden_dim 128 BiLSTM hidden dimension
--num_layers 2 BiLSTM layers

Model architecture

Input (328-dim feature)
  → LayerNorm
  → Conv1d (kernel=3, 64 filters) → ReLU → MaxPool1d
  → Conv1d (kernel=3, 128 filters) → ReLU → MaxPool1d
  → BiLSTM (2 layers, hidden=128) → attention pooling
  → Linear(256 → 64) → ReLU → Dropout(0.3)
  → Linear(64 → 11) → softmax

LOSO cross-validation

For N subjects, the script runs N folds. Each fold holds out one subject for validation and trains on the remaining N−1. Final reported metrics are the mean ± std across all folds. The exported model is trained on all subjects (full dataset) — this is the model used for live inference.

Outputs

File Contents
models/worker_har_best.pt Self-contained TorchScript module (normalization baked in)
models/worker_har_best.meta.pkl Labels + feature names (Python pickle)
models/training_log.json Per-epoch loss, per-fold accuracy

Export format

The model exports via torch.jit.script (NOT torch.jit.trace). Tracing baked CPU-initialized hidden tensors into the LSTM graph, breaking GPU inference. torch.jit.script preserves the LSTM's runtime hidden-state creation, so the model runs on any device: CPU, CUDA, or Jetson.

import torch
model = torch.jit.load("models/worker_har_best.pt")
feats = torch.randn(1, 328)  # batch × feature_dim
output = model(feats)
print(output.shape)  # torch.Size([1, 11])

Feature names & labels

import pickle
with open("models/worker_har_best.meta.pkl", "rb") as f:
    meta = pickle.load(f)
print(meta["labels"])         # ['standing', 'walking', ..., 'troweling']
print(meta["feature_names"])  # ['imu0_quat_w_mean', 'imu0_quat_w_std', ..., 'fsr_ratio_right']