← the lab · the atelier

Sheet 10 · machine learning

Poi head detection

Finding the spinning heads of poi in videos the model has never seen, and measuring honestly whether each change helps.

Problem

V1 ON MY OWN PRACTICE VIDEO0.82F101V1 ON VIDEOS IT HAD NEVER SEEN0.47F148 frames from seven videos01the number that mattersV1 ON MY OWN PRACTICE VIDEO0.82F101V1 ON VIDEOS IT HAD NEVER SEEN0.47F148 frames from seven videos01the number that matters
FIG. 1The same model, two kinds of video. F1 of the first fine-tuned model (v1). The rule runs from 0 to 1.
Source: Poi head detection sheet, Problem; project.json timeline 2026-10-16

Poi are weights on cords, spun around the body in patterns. A sister project, Prop Theory, wants to turn any video of a move into an animation, and that starts with knowing where each poi head is in every frame. The heads are small, fast and blurred; on fire poi they are a flame, on LED poi a streak of light, in contact moves they sit in the hand.

I started from BlurBall, a detector built for blurred table-tennis balls, and fine-tuned it on frames I labelled. The first model did well on my own practice video and much worse on videos it had never seen. That second number is the one that matters.

Approach

720168100486112345tests: read once, at the end1Training720 framesevery run startsfrom the originalweights2Validation168 framesfrozen; 290 heads,7 videos; picks themodel3iso1100 framesmy own video4wild48 frames7 videos5wild261 frames8 new videos72016810012345tests: read once, at the end1Training · 720 framesevery run starts from the original weights2Validation · 168 framesfrozen; 290 heads, 7 videos; picks the model3iso1 · 100 framesmy own video4wild · 48 frames7 videos5wild2 · 61 frames8 new videos
FIG. 2Where every labelled frame goes. Training and the frozen validation set do all the work; the three test sets are a final look, never a selection tool.
Source: Poi head detection sheet, Approach (Data, Rules of the bench)

I label frames by hand. Every training run starts from the original BlurBall weights, a frozen validation set picks the model, and the test sets are a final look, never a selection tool.

24 configs x 4 epochs23 random configs + the baseline8 configs x 10 epochsbest 8 continue3 configs x 20 epochs x 2 seedsfinalists: c19, c06, baseline24 configs x 4 epochs23 random configs + the baseline8 configs x 10 epochsbest 8 continue3 configs x 20 epochs x 2 seedsfinalists: c19, c06, baseline
FIG. 3Night search, successive halving. 38 full trials, 9.4 hours of GPU time on one GTX 1070.
Source: poi-det/hpbench.py (plan full), bench/results.jsonl, 2026-10-17

Before tuning anything, I sorted the first model's errors on unseen videos by cause. Poi type explained far more of them than head size did.

Results

v1's 95% interval, resampled by video0.200.300.400.500.600.70F1 on the wild test setv10.47baseline, 2 seeds0.49 to 0.53c06, 2 seeds0.53 to 0.54c19, 2 seeds0.52 to 0.53v1's 95% interval, resampled by video0.200.300.400.500.600.70F1 on the wild test setv10.47baseline, 2 seedsc06, 2 seedsc19, 2 seeds
FIG. 4Every finalist lands inside the noise. With 48 wild frames, the interval around v1 is wider than every difference the search produced. Each finalist is drawn as its two seeds.
Source: poi-det/bench/full_final.json, 2026-10-17; interval from the sheet's Results
RunVal F1 (selection)iso1 F1 (final look)wild F1 (final look)
full_r2_base_e20_s00.590.850.53
full_r2_base_e20_s10.630.830.49
full_r2_c06_e20_s00.660.640.53
full_r2_c06_e20_s10.670.800.54
full_r2_c19_e20_s00.680.750.52
full_r2_c19_e20_s10.660.800.53
TABLE 1Finalists after the search. The two winners on validation (c19, c06) do not carry over to unseen videos: on wild they sit inside the baseline's own seed-to-seed spread.
Source: poi-det/bench/full_final.json, 2026-10-17 08:37

Tuning did not measurably beat the baseline. Two seeds of the same settings already differ by up to 0.04 F1.

0.200.380.550.720.90231391584720training framesval F1LEDcontactallfire
FIG. 5More of the same data helps slowly. Winning settings, one seed per point. Overall F1 moves from 0.61 to 0.66; per-type lines jump around because each type has only a few validation heads.
Source: poi-det/bench/curve.json, 2026-10-17 10:41
0.000.250.500.751.00v1 detector0.43small hosted model, zero shot0.50fast hosted model, zero shot0.78
FIG. 6A general model as labeller. F1 on wild2 (61 frames, 102 heads), labelled without any model suggestion on screen. The hosted model is slow (about 8 s a frame) and not a fixed version, so it is used to label training data, not to run in the product.
Source: poi-det/bench/scorecard.json, 2026-10-17 01:10

Training on the hosted model's labels landed inside the noise as well. The validation labels also still follow my old labelling rule, which penalises the hosted model's labels on fire and LED frames.

What I learned

2913182012341Fire or LED trails29 errorsone fire video alone gave232Contact moves13 errorshead in the hand3False hits18 errorson the body or thebackground4The rest20 errorsnot in these three groups2913182012341Fire or LED trails · 29 errorsone fire video alone gave 232Contact moves · 13 errorshead in the hand3False hits · 18 errorson the body or the background4The rest · 20 errorsnot in these three groups
FIG. 7What v1 got wrong on the wild set. The 80 errors sorted by cause before any tuning. Small heads explained only 5 of 41 misses: the poi type mattered more than the size.
Source: Poi head detection sheet, Approach (Error dump first)
  • Freeze the yardstick first. The validation set used to change whenever a new video was added, so early models were never comparable.
  • Count the noise before reading a gain. Every comparison now shows the seed spread next to the difference.
  • Label rules must be physical. I had marked the middle of a light streak; the head is at the leading edge, and on fire poi at the wick. The frames are being relabelled.
  • Settings were not the bottleneck. Targeted frames per poi type and better labels are.

Open questions

  • For a data scientist: how do I get a test set big enough to see per-type effects without labelling thousands of frames? Is the seed-pair spread a fair noise floor?
  • For a cloud architect: training moves to GCP next. What is the simplest setup for queued GPU jobs that write the same log files back here?

all lab sheets · Prop Theory →