Wearable sensor systems for Human Activity Recognition (HAR) are typically trained once on a fixed sensor configuration and deployed. When a new sensor becomes available β whether due to hardware upgrades, changed deployment context, or expanded monitoring needs β the system must be updated. Naively, this requires collecting new labeled data for all activities with the expanded sensor set, which is expensive and disruptive.
We address the sensor increment problem: given a trained
The key observation is that the new sensor is not equally useful for all activities. Adding a thigh sensor to a wrist+ankle system provides redundant information for distinguishing treadmill walking variants (leg motion is identical across them) but may be critical for separating lying positions or distinguishing standing from sitting. Requesting re-annotation for all
We make two distinct contributions that together explain why the proposed approach consistently outperforms the oracle (which retrains all classes with full new labels):
We introduce a benefit score that identifies which activities will actually improve from adding the new sensor, without requiring any new labeled data. The oracle retrains everything β including classes where the new sensor hurts or adds noise. By selectively retraining only beneficial classes, the proposed approach avoids these degradations.
Given the selected classes, we propose a training strategy that exploits the large unlabeled paired dataset as a source of diverse negatives. The oracle retrains with fully labeled data using standard supervised training. We instead use the unlabeled
These contributions are complementary but independently valuable, as shown by the ablation:
| Condition | Selection | Training | Result |
|---|---|---|---|
| Baseline | β |
|
lowest |
| Oracle | all |
|
good but noisy |
| Ablation A | elbow only | standard (no PU) | tests Contribution 1 |
| Ablation B | all classes | PU + FL negatives | tests Contribution 2 |
| Proposed | elbow | PU + FL negatives | best |
The benefit score combines two signals:
The additive formulation is deliberate β discriminability boosts classes with new sensor evidence rather than suppressing classes with only one signal.
Even if an activity is confused under
where
Direct discriminability measures how much the new sensor alone separates class
Opposition discriminability handles the case where
If the new sensor separates
The benefit score induces a ranking of all classes. Rather than using a fixed threshold, we use elbow detection on the sorted benefit score curve: the point of maximum curvature identifies the natural cutoff where marginal benefit drops sharply. This is self-adaptive β it selects fewer classes when the score distribution drops off quickly and more when there is a long tail of genuinely beneficial classes.
Once new labels are obtained for the selected classes, we retrain each targeted classifier using the large unlabeled paired dataset as a negative source.
For each targeted class
-
Certain positives: new labeled windows of class
$A$ ($n+1$ sensors) -
Certain negatives: new labeled windows of other targeted classes unrelated to
$A$ in the activity hierarchy -
Uncertain negatives: pseudo-negatives sampled from a shared negative pool built from the unlabeled
$n+1$ -sensor data, weighted by$w = 1 - P(A \mid x)$
The uncertain negatives use a weighted focal loss where FL windows that might belong to class
This handles the unknown label problem: if the base classifier is uncertain whether a FL window belongs to class
Rather than blind K-means clustering on the embedding space, we use the base classifiers to pseudo-label each FL window by predicted activity, then build a shared negative pool by sampling proportionally from each pseudo-labeled group.
This has two key advantages:
- Semantic diversity: the pool covers all activities present in the FL data (Walking, Standing, Treadmill variants, etc.), ensuring the retrained classifier sees realistic negative examples from across the activity distribution.
-
Consistent negative distribution: all targeted classifiers sample from the same shared pool, differing only in which groups are excluded. For class
$A$ , windows pseudo-labeled as$A$ or any hierarchically related class (parents and children) are excluded before sampling, ensuring negatives are always semantically unrelated to$A$ .
The PU weight
Non-targeted classifiers are completely frozen β their weights, thresholds, and input dimensions are unchanged. Evaluation uses independent per-class binary F1 with multi-label ground truth, so changing one classifier mathematically cannot affect another class's metric.
| Condition | Sensors | New labels | Description |
|---|---|---|---|
| Baseline | 0 | existing system | |
| Proposed |
|
our approach | |
| Oracle |
|
upper bound |
Metrics: independent per-class binary F1, macro F1, weighted F1.
The approach works best when:
- The new sensor covers a different body region from the base sensors
- The base model has room for improvement (macro F1 < 0.75)
- The unlabeled FL data covers the activity space
Performance is limited when:
- The base model is already strong and the new sensor is redundant
- The val set is small, giving noisy F1 estimates for the confusion signal
- Targeted classes are semantic siblings where the new sensor doesn't discriminate between them (e.g. treadmill variants at the same speed)
We ablate over all 75 possible combinations of base sensors and new sensors from {LeftWrist, RightWrist, RightThigh, RightWaist, RightAnkle}, grouped by number of base sensors (1β4). Each config is evaluated at annotation budgets
We empirically validate the benefit score by computing Spearman correlation between benefit score and actual
| Formula | Expression |
|---|---|
| Additive (ours) | |
| Product | |
| Confusion only | |
| Discriminability only | discriminability |
sensorIL/
βββ configs/
β βββ pipeline_config.json
βββ scripts/
β βββ simclr_encoder.py # Frozen SimCLR encoder wrapper
β βββ dataset.py # SensorDataset, UnlabeledFLDataset
β βββ cooccurrence.py # Activity hierarchy, multi-label encoding
β βββ train_base.py # ParallelBinaryClassifiers, train_base()
β βββ detect_confusion.py # Step 2: rank classes by F1, confusion pairs
β βββ estimate_benefit.py # Step 3: benefit score, elbow detection
β βββ incremental_ft.py # Step 5: PU learning, diverse FL negatives
β βββ calibrate_thresholds.py # Per-class threshold calibration [0.2, 0.8]
β βββ evaluate.py # Independent per-class binary F1
β βββ run_pipeline.py # Full pipeline (Steps 1-6)
β βββ ablation_budget.py # Annotation budget vs F1 curve
β βββ ablation_sensor.py # Sensor config ablation (75 combos Γ 4 budgets)
β βββ analyze_benefit_score.py # Benefit score correlation analysis
βββ checkpoints/ # Model checkpoints + JSON results
βββ logs/ # Full stdout logs per run
python scripts/run_pipeline.py --config configs/pipeline_config.json# Run one n_base at a time
python scripts/ablation_sensor.py --config configs/pipeline_config.json --n-base 1 --budgets 5,10,15,all
python scripts/ablation_sensor.py --config configs/pipeline_config.json --n-base 2 --budgets 5,10,15,all
python scripts/ablation_sensor.py --config configs/pipeline_config.json --n-base 3 --budgets 5,10,15,all
python scripts/ablation_sensor.py --config configs/pipeline_config.json --n-base 4 --budgets 5,10,15,allpython scripts/analyze_benefit_score.py --results-dir checkpoints/ --n-base 1 2 3 4{
"sensors": {"known_sensors": ["LeftWrist", "RightAnkle"], "new_sensor": ["RightThigh"]},
"data": {"labeled_dir": "/path/to/lab", "unlabeled_dir": "/path/to/fl"},
"model": {"encoder_path": "/path/to/simclr.pt"},
"finetune": {"few_shot_samples_per_class": 40, "val_split": 0.2,
"epochs": 100, "batch_size": 256, "lr": 1e-3, "weight_decay": 1e-4},
"active_learning": {"pseudo_label_threshold": 0.7, "n_clusters": 40},
"output": {"checkpoint_dir": "checkpoints/", "log_dir": "logs/"}
}