OpenCGM-StateEventSource ↗

How it works

1

Start with one day, exactly as it was recorded

A glucose sensor reports every five minutes, so a day is a row of 288 numbers. Real sensors miss readings — the sensor warms up, it falls off, someone showers. Most software quietly fills those gaps in by drawing a straight line through them.

This model does not. A model cannot distinguish an interpolated reading from a measured one, so interpolation teaches it the shape of the interpolation. Each of the 288 slots carries a flag marking whether a reading is real, and every later step reads that flag.

2

Split the day into a slow part and a fast part

A glucose trace is really two things layered on top of each other. There is the slow drift across the day — where your body sits when nothing much is happening. And there are the spikes: a meal, exercise, a dose of insulin.

A smoothing filter separates them. It looks only backwards in time, so no reading is influenced by the future. The smoothed line is the state stream; the remainder is the event stream. Both are kept.

The filter width is not fixed in advance. It is a trained parameter, so the model arrives at its own boundary between slow and fast.

3

Hide part of the day and make the model guess it

The day is divided into 24 hourly patches and between half and 60% of them are hidden, redrawn for every window. One copy of the model sees what is left and predicts the hidden patches. A second copy, updated slowly from the first, sees everything and provides the target. No labels are involved.

Doing this well requires learning how glucose behaves: how a spike decays, how overnight differs from afternoon. The model is also given the time of day, which the ablations show is load-bearing.

Patches are hidden before any normalisation or statistics are computed. Hiding them afterwards leaks information about the hidden patches into the visible ones, and the resulting model scores well for the wrong reason.

4

Keep the fingerprint, throw away the scaffolding

After training, the prediction machinery is discarded. What remains is the encoder that maps 288 readings to 128 numbers: 435,633 parameters, about 2 MB. The demo page runs it in the browser; the API runs the same file server-side.

5

Check whether the fingerprint is any good

The encoder is frozen and its 128 numbers are handed to a logistic regression. If a linear classifier can answer a clinical question from them, the information was already present in the representation.

Splits are by person, not by day. Testing on a different day from someone who also appears in training measures how well the model recognises that person.

The 5-fold split is repeated 10 times across 14 clinical questions and 5 independently trained models. Reported numbers are the mean and spread, not the best run.