Part VI: Motion, Location, and Inertial Intelligence
Chapter 26: Human Activity and Behavior Recognition

Segmentation and transition states

"I was ninety-eight percent sure the user was sitting and ninety-eight percent sure they were standing. The half second in between, when they were neither, is the only part anyone ever asks me about."

An Over-Confident AI Agent

Prerequisites

This section assumes you can turn an inertial stream into per-window features, developed earlier in this chapter and in Chapter 23, and that you know the activity label hierarchy from Section 26.1. The boundary-finding methods here are the change-detection tools of Chapter 12; sequence models that learn boundaries implicitly come from Chapter 14. The evaluation pitfalls draw on leakage-safe protocol design from Chapter 65.

The Big Picture

An activity classifier is trained on tidy, pre-cut clips: three seconds of pure walking, three seconds of pure sitting. The world does not arrive pre-cut; it arrives as an unbroken stream in which the user walks, slows, pauses at a door, fumbles for a key, and sits, with no markers for where one activity ends and the next begins. Segmentation is the act of drawing those boundaries, and transition states are the messy in-between spans that belong cleanly to no activity at all. Get this wrong and a model that scores 95% on a benchmark of clean clips will stumble in deployment, not because its clip-level judgement is bad, but because it is being asked the wrong question at the wrong instant. This section covers where to cut the stream, what to do with the moments that resist cutting, and how to score the result without fooling yourself.

Why the continuous stream resists cutting

The dominant approach is the sliding window: slice the stream into fixed-length frames of length \(W\) with hop \(H\), extract features from each, and classify each frame independently. It is simple, streams naturally, and dominates the literature. It also hides an unavoidable tension in the choice of \(W\). A window must be long enough to contain the periodic structure of the activity: a full gait cycle for walking is roughly one second, so a \(0.5\ \mathrm{s}\) window literally cannot see a stride and will confuse walking with fidgeting. But a long window is more likely to straddle a boundary and contain two activities at once, which no single label describes. Every window length trades one error for another: a short window sees the boundary but not the activity, a long window sees the activity but not the boundary, and no fixed \(W\) escapes both failures at once.

Formally, with hop \(H\) the fraction of windows that are "mixed" (straddle a true boundary) grows with \(W\): the expected number of boundary-straddling windows per segment is about \(W/H\), independent of the segment's own duration \(\bar{d}\), so short activities suffer most. A two-second bout of stair-climbing sampled with a three-second window may never produce a single pure window. Smaller \(H\) improves temporal resolution and yields more training examples, but also makes adjacent windows highly correlated, which quietly inflates any accuracy number computed by splitting windows randomly rather than by subject or by time.

Common Misconception

It is tempting to treat a shrinking hop \(H\) as a free upgrade: more overlap looks like more training data and sharper boundary resolution, so why not push \(H\) toward a single sample? It is not free. Each extra window is the same signal seen through a slightly shifted lens, so neighbor correlation rises faster than genuine information content, inference and storage load scale as \(1/H\) with no benefit past the sensor's noise floor, and a random split that ignores the correlation reports a resolution gain that is mostly duplicated signal, not new evidence.

Key Insight

Transitions are not noise to be filtered away; they are a legitimate class of their own. Real segmentation ground truth almost always needs three kinds of label, not one: the activities themselves, a NULL class for spans that are none of the target activities (standing idle, unclassifiable fumbling), and explicit transition spans (sit-to-stand, lie-to-sit) that are dynamically distinct and often the clinically interesting part. A model trained only on pure activity clips has never seen a transition and will confidently misclassify it as whichever activity it superficially resembles. The most common deployment failure in activity recognition is not a wrong activity call; it is a right call made half a second too late.

Two families of segmentation

Segmentation strategies split into two families. Explicit (bottom-up) segmentation first finds boundaries, then classifies the resulting variable-length segments. Boundaries come from change-point detection on the signal statistics: a sit-to-stand event is a sharp change in the mean and variance of vertical acceleration, exactly what the CUSUM and sliding-window detectors of Chapter 12 are built to catch. The appeal is that each classified segment is internally homogeneous, so the classifier never sees a mixed window. The risk is cascading error: a missed boundary merges two activities forever, and a detector run on raw acceleration will fire on every footstep unless smoothed or thresholded carefully.

The listing below applies an off-the-shelf change-point search to a synthetic accelerometer-magnitude trace with three regimes (still, walking, still) and recovers the two boundaries.

import numpy as np
import ruptures as rpt

fs = 50.0
t = np.arange(0, 12, 1 / fs)
sig = np.full(t.size, 9.81)                     # accel magnitude, resting
walk = (t > 4) & (t < 8)                        # a walking bout
sig[walk] += 2.5 * np.sin(2 * np.pi * 1.8 * t[walk])   # ~1.8 Hz gait
sig += np.random.normal(0, 0.15, t.size)

# Pelt search on a shift in mean+variance; penalty controls sensitivity.
algo = rpt.Pelt(model="rbf", min_size=int(1.0 * fs)).fit(sig)
bkps = algo.predict(pen=12.0)                   # indices of change points
print("boundaries at t =", [round(b / fs, 2) for b in bkps[:-1]], "s")
Listing 26.3. Explicit segmentation with the ruptures library. Pelt searches for shifts in the signal distribution and returns boundary indices near \(t=4\) and \(t=8\ \mathrm{s}\); the pen penalty is the one knob that trades missed boundaries against spurious ones. The recovered spans are then classified individually, so no classifier window ever straddles the walk-to-still transition.

As Listing 26.3 shows, boundary-finding can be a few lines rather than a hand-rolled CUSUM. The second family, implicit (dense) segmentation, skips explicit boundaries entirely: a sequence model emits a label at every timestep, and boundaries appear wherever the predicted label changes. Recurrent and temporal-convolutional networks from Chapter 14 do this naturally, learning transition dynamics from data instead of a hand-tuned detector. This is now the default for high-quality systems because it makes the boundary a learned, context-aware decision rather than a threshold on one statistic. Its cost is data hunger: it needs densely labelled streams, far more expensive to annotate than isolated clips.

The Right Tool

A hand-written offline change-point search (dynamic programming over segment costs, penalty tuning, a homemade cost model) is easily 80 to 150 lines and slow. The ruptures package collapses it to the three lines in Listing 26.3: it ships Pelt, binary segmentation, window-based and kernel cost models, and exact and approximate search, so you pick a model and a penalty and it owns the optimization. For streaming rather than offline use, river provides online change detectors (ADWIN, Page-Hinkley) behind a one-line update call. Reach for these before writing a detector; hand-roll only on a microcontroller where neither fits.

In Practice: sit-to-stand in a fall-risk wearable

A hip-worn monitor for older adults is deployed to count sit-to-stand transitions per day, a validated marker of frailty and fall risk. The vendor's first model was trained on the usual clean clips of sitting and standing and scored well in the lab, yet in the field it under-counted transitions badly. The reason was structural: sit-to-stand is neither "sitting" nor "standing," it is a distinct two-second postural event with a characteristic forward-lean and vertical-rise signature, and the model had never been shown one. It classified the transition as whichever static posture the window overlapped more, so the event the whole product existed to count fell into the gap between labels. The fix was to relabel the training data with an explicit sit-to-stand class and switch to a dense per-timestep model that could place the boundary in context; counting then recovered to clinical agreement. The lesson generalizes: the transition you are tempted to treat as boundary noise is frequently the exact quantity of interest.

Research Frontier

Dense per-timestep labeling is the bottleneck that keeps implicit segmentation out of reach for many teams, and closing that gap is an active line of work. Weakly supervised temporal action segmentation, adapted from video into the wearable-sensor setting, trains a dense boundary-aware model from only an ordered list of activities per session, using alignment techniques such as connectionist temporal classification to infer where boundaries fall during training. A second line borrows the self-supervised pretraining of Chapter 17 and points it at change sensitivity: a model pretrained to notice when two augmented windows share an underlying regime develops a boundary-relevant representation before it ever sees a labeled transition. Neither approach yet matches fully supervised labeling, but both shrink the annotation budget implicit segmentation demands.

Evaluating a segmentation without fooling yourself

Segmentation quality cannot be judged by frame accuracy alone, and here the running leakage theme from Chapter 65 bites twice. First, frame-level accuracy is dominated by long, easy, stationary spans and is nearly blind to the short transitions you most care about; a model can score 94% frame accuracy while missing half of all sit-to-stand events. Report event-level metrics too: count a predicted segment as a true positive only if it overlaps the correct ground-truth segment above some threshold, and break failures down by the classic segmentation error taxonomy rather than one F1 number. An insertion is a predicted segment with no matching ground truth, a false transition conjured from noise; a deletion is a true segment the model never found, such as a missed sit-to-stand; a merge fuses two adjacent true segments into one because the model smoothed through a short bout; and fragmentation splits one true segment into several because a noisy signal made the model flicker mid-activity. Insertions and fragmentation mean the detector is too twitchy (raise the penalty, add hysteresis); deletions and merges mean it is too sluggish (lower the penalty, shorten the window); the aggregate F1 alone hides which knob to turn. Second, because overlapping windows correlate strongly in time, a random train/test split leaks near-identical neighbours across the split and inflates every number: always split by subject and by contiguous time block, never by window.

A subtler point concerns the boundaries themselves: human annotators disagree about exactly where a transition starts by a few hundred milliseconds, so demanding frame-exact agreement penalizes a model for beating the noise floor of its own labels. Score boundaries with a tolerance window instead (a predicted boundary within, say, \(\pm 0.5\ \mathrm{s}\) of a labelled one counts as correct), matching the intrinsic imprecision of the ground truth rather than pretending it is exact.

Exercise

Take a densely labelled activity stream (for example PAMAP2 or Opportunity) and build two segmenters: a fixed \(3\ \mathrm{s}\) sliding window with majority-vote labels, and the explicit change-point approach of Listing 26.3 followed by per-segment classification. Evaluate both with (a) frame-level accuracy and (b) event-level F1 using a \(50\%\) overlap criterion and a \(\pm 0.5\ \mathrm{s}\) boundary tolerance. Show that the two methods rank differently under the two metrics, and identify which activities drive the gap.

Self-Check

1. Explain the sliding-window dilemma: what does a too-short window fail at, what does a too-long window fail at, and why can no single length fix both?

2. Why will a model trained only on pure activity clips systematically mishandle a sit-to-stand transition, and what two changes to the labelling and model fix it?

3. A model reports 94% frame accuracy but misses half the sit-to-stand events. How is this possible, and which metric would have exposed it?

What's Next

In Section 26.4, we turn from where to cut the stream to whose stream it is. Segmentation and classification both degrade when a model trained on one population meets a new user whose gait, pace, and transition style differ, so we take up user personalization and adaptation: how to bend a general model toward the individual wearing it without collecting a fresh labelled dataset for every person.