A four-notebook tutorial series teaching the fundamentals of a classical machine learning pipeline for Human Activity Recognition (HAR), built on the SHL Challenge 2026 dataset.
The series walks through every stage of a HAR pipeline end-to-end — from raw sensor files to trained and evaluated classifiers — using smartphone inertial sensor data collected at four body positions.
| Notebook | Topic | Key output |
|---|---|---|
01_data_loading.ipynb |
Data loading & preprocessing | Preprocessed .npy arrays |
02_visualization.ipynb |
Data visualization | Signal plots, FFT spectra, PCA |
03_feature_extraction.ipynb |
Feature extraction | Feature matrices (N, F) |
04_model_training.ipynb |
Model training & evaluation | Trained models, results comparison |
The notebooks use the SHL Challenge 2026 dataset. You must download it separately from the official source:
The dataset is not included in this repository.
After downloading, place the data so the project root looks like this:
SHL-Tutorial/
├── Data/
│ ├── train/
│ │ └── Bag/
│ │ ├── Acc_x.txt
│ │ ├── Acc_y.txt
│ │ ├── Acc_z.txt
│ │ ├── Gyr_x.txt
│ │ ├── Gyr_y.txt
│ │ ├── Gyr_z.txt
│ │ ├── Mag_x.txt
│ │ ├── Mag_y.txt
│ │ ├── Mag_z.txt
│ │ └── Label.txt
│ ├── train 2/
│ │ └── Hand/ (same files)
│ ├── train 3/
│ │ └── Hips/ (same files)
│ ├── train 4/
│ │ └── Torso/ (same files)
│ └── validation/
│ ├── Bag/ (same files)
│ ├── Hand/ (same files)
│ ├── Hips/ (same files)
│ └── Torso/ (same files)
├── 01_data_loading.ipynb
├── 02_visualization.ipynb
├── 03_feature_extraction.ipynb
├── 04_model_training.ipynb
└── README.md
Each sensor file is a plain text matrix of shape (N_frames, 500):
- Rows — time windows (frames)
- Columns — 500 raw samples per frame (5 seconds × 100 Hz)
| Split | Users | Frames | Labels |
|---|---|---|---|
| Train | User 1 | 196,072 | ✓ |
| Validation | Users 2 & 3 | 28,789 | ✓ |
| Test | Users 2 & 3 | 92,726 | ✗ (challenge submission) |
Sensors: Accelerometer, Gyroscope, Magnetometer (3 axes each = 9 channels)
Positions: Bag, Hand, Hips, Torso
Activity classes:
| Code | Activity | Code | Activity |
|---|---|---|---|
| 1 | Still | 5 | Car |
| 2 | Walking | 6 | Bus |
| 3 | Run | 7 | Train |
| 4 | Bike | 8 | Subway |
Notebook 1 generates a preprocessed/ directory of .npy arrays. This folder is also not included in the repository — run 01_data_loading.ipynb first to generate it.
preprocessed/
├── X_train_Bag.npy X_val_Bag.npy
├── X_train_Hand.npy X_val_Hand.npy
├── X_train_Hips.npy X_val_Hips.npy
├── X_train_Torso.npy X_val_Torso.npy
├── y_train_{position}.npy y_val_{position}.npy
│
│ (generated by Notebook 3)
├── X_feat_train_Hips.npy X_feat_val_Hips.npy
├── y_feat_train_Hips.npy y_feat_val_Hips.npy
└── feature_names_Hips.txt
Array shapes and approximate sizes:
| File | Shape | Size |
|---|---|---|
X_train_{position}.npy |
(196072, 9, 500) | ~3.5 GB each |
X_val_{position}.npy |
(28789, 9, 500) | ~520 MB each |
X_feat_train_Hips.npy |
(196072, ~219) | ~170 MB |
X_feat_val_Hips.npy |
(28789, ~219) | ~25 MB |
Storage note: Full preprocessing of all 4 positions requires approximately 17 GB of free disk space.
- Python ≥ 3.10
- Conda or virtualenv recommended
# Clone the repository
git clone https://github.com/<your-username>/SHL-Tutorial.git
cd SHL-Tutorial
# Create and activate environment
conda create -n shl-tutorial python=3.11
conda activate shl-tutorial
# Install dependencies
pip install -r requirements.txt
# Launch Jupyter
jupyter notebooknumpy>=1.26
pandas>=2.0
matplotlib>=3.7
scipy>=1.11
scikit-learn>=1.4
tqdm
jupyter
Or install directly:
pip install numpy pandas matplotlib scipy scikit-learn tqdm jupyterEstimated runtime: 15–30 min (full dataset, all 4 positions)
Loads raw .txt sensor files, checks for NaN/Inf/zero-variance frames, applies per-sample z-score normalization, and saves stacked (N, 9, 500) arrays.
Key concepts taught:
- Sliding window representation of time-series
- Per-sample vs. global normalization
- Majority-vote label assignment for transition windows
Quick run: Set
MAX_FRAMES = 5000in the relevant cells to test the pipeline in seconds before committing to a full run.
Estimated runtime: 5–10 min (loads from preprocessed arrays)
Eight visualization sections building intuition about the sensor signals before any modeling. All plots are implemented as reusable parameterized functions.
| Section | What you see |
|---|---|
| Raw signal plots | Per-class waveforms for any sensor axis |
| Intra-class variability | Frame overlays showing within-class spread |
| Sensor comparison | Acc vs Gyr vs Mag for the same activity |
| Position comparison | Same activity across all 4 phone positions |
| Statistical boxplots | Mean, Std, RMS, Peak-to-peak per class |
| Correlation heatmap | Inter-axis Pearson correlation by class |
| FFT spectra | Dominant frequency content per class |
| PCA projection | 2D separability of classes from simple features |
Estimated runtime: 5–10 min (full dataset)
Transforms each (9, 500) raw frame into a flat feature vector using three feature groups:
| Group | Count | Examples |
|---|---|---|
| Time-domain | 13 × 9 = 117 | mean, std, RMS, skewness, kurtosis, ZCR |
| Frequency-domain | 10 × 9 = 90 | dominant frequency, spectral entropy, band energies |
| Cross-axis | 12 | SMA, vector magnitude, Acc–Gyr correlation |
| Total | ~219 | (after zero-variance removal) |
Also includes feature quality checks (NaN/Inf imputation, zero-variance removal, outlier clipping) and a feature importance ranking via a lightweight Random Forest.
Estimated runtime: 30–90 min (dominated by SVM; see note below)
Trains and evaluates four classifiers on the feature matrices from Notebook 3.
| Model | Train data | Notes |
|---|---|---|
| Random Forest | Unscaled | 200 trees, balanced class weight |
| KNN | Scaled | k=11, distance weighting |
| SVM | Scaled (subsample) | RBF kernel, C=10 — subsample to ~30K for tractability |
| HistGradientBoosting | Unscaled | 300 rounds, early stopping |
Results reported: Accuracy, Macro F1, Weighted F1, per-class F1, confusion matrices, train/inference time.
SVM warning:
sklearn.svm.SVCwith RBF kernel scales as O(n²)–O(n³). Training on the full 196K samples is not feasible on most laptops. The notebook includes a stratified subsample cell — use it.
The SHL Challenge 2026 test set has no labels — it is intended for official leaderboard submission only. Throughout this tutorial, the labeled validation set (Users 2 & 3) serves as a proxy test set.
This means:
- Reported validation metrics are slightly optimistic, as some modeling choices (hyperparameters, feature set) were guided by observing validation performance
- For a production pipeline, hyperparameter tuning should be performed on a held-out slice of the training data, with the validation set reserved for final evaluation
Indicative results — exact numbers will vary with hardware, NumPy/sklearn versions, and whether SVM is trained on a subsample.
| Model | Accuracy | Macro F1 | Train Time |
|---|---|---|---|
| Random Forest | ~75–80% | ~70–75% | ~3–5 min |
| KNN (k=11) | ~65–70% | ~60–65% | ~2 min (inference) |
| SVM (subsample) | ~65–70% | ~60–65% | ~5–10 min |
| HistGradientBoosting | ~78–83% | ~72–78% | ~3–6 min |
Vehicle transport modes (Car, Bus, Train, Subway) are the hardest to distinguish — they share similar posture and differ mainly in vibration frequency profile.