docs: richer report — data figures, success/failure frames, commit-pinned repo links
- experiment_charts.py generates 4 figures from experiments/ artifacts: held-out per-film F1, 16-combo ranking, DE search landscape, and the Downton detector-vs-tracker ghost timeline (replaces the blank title-card screenshot) - new frames: 19-correct wedding shot (success case), Many Saints ghost-vs-unknown frame (three error classes in one image) - rename rep4-optimizer-results.md -> model-bakeoff.md; rep4 kept only as the on-disk artifact prefix, explained once - repo file references are now links via https://REPOLINK/<path> placeholders; build_site.sh pins them to the HEAD commit's raw URLs and fails the build if a linked path doesn't exist at HEAD - drop references to removed scripts (scene_score.py, score_config.py) and to session-memory names; mark artifact-registry paths with their pull commands - commit readme_example.jpg + pipeline_topology.svg so README renders on the plain Gitea repo view - deploy_pages.sh: push built site/ to the gitea-pages branch
This commit is contained in:
+82
-41
@@ -1,9 +1,28 @@
|
||||
# Deep dive: LVFace-B Glint360K
|
||||
|
||||
LVFace won the model bake-off (see `best-model.md`) and is the shipped default
|
||||
embedder. This page is the honest accounting of how it actually performs —
|
||||
including where it's wrong, and one case where the ground truth itself is
|
||||
wrong and LVFace is right.
|
||||
LVFace won the model bake-off (see [Which model is best?](best-model.md)) and is
|
||||
the shipped default embedder. This page is the honest accounting of how it
|
||||
actually performs — what a good second looks like, where it's wrong and *why*,
|
||||
and one case where the ground truth itself is wrong and LVFace is right.
|
||||
|
||||
## What good looks like
|
||||
|
||||
Before the failure analysis, the ceiling. This is a single sampled second from
|
||||
Downton Abbey's wedding scene — a packed, hat-heavy, period-costume group shot,
|
||||
about as hostile as ensemble framing gets:
|
||||
|
||||

|
||||
*Frame `downton_abbey/best/best_t00127.jpg` from the `montage-frames` artifact
|
||||
package (`scripts/artifacts/pull_artifacts.sh montage-frames
|
||||
Downton_Abbey__A_New_Era`) — green = identified, blue = detected but unknown.*
|
||||
|
||||
**Nineteen named faces in one frame, all nineteen correct** — Jim Carter half
|
||||
behind a flower arrangement, Penelope Wilton at a three-quarter turn, Lesley
|
||||
Nicol under a hat brim. The blue "unknown" boxes are the honest cases: faces the
|
||||
detector found but the matcher declined to name rather than guess. The one miss
|
||||
at this second is Maggie Smith — not on screen in this framing, but X-Ray marks
|
||||
her present for the scene. That distinction (on-screen face vs. scene-level
|
||||
ground truth) sets up everything below.
|
||||
|
||||
## Training vs. held-out: the generalization gap
|
||||
|
||||
@@ -11,6 +30,8 @@ The shipped config (`prob_threshold=0.754, anneal_sec=35.54,
|
||||
extinction_sec=57.43, expand_gallery=true`) was tuned against 4 films. Scored
|
||||
against the 5 films the optimizer never saw:
|
||||
|
||||

|
||||
|
||||
| film | F1 | P | R | TPI | FPI | misid | FN |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| Benny & Joon | 83.0% | 89.1% | 77.7% | 15125 | 1846 | 0 | 4337 |
|
||||
@@ -22,9 +43,10 @@ against the 5 films the optimizer never saw:
|
||||
|
||||
**67.4% held-out vs. 75.3% on training** — an ~8pp drop, and a **37pp spread
|
||||
between the best and worst held-out film**. The config does not generalize
|
||||
uniformly; two films are outright failure cases, for two different reasons.
|
||||
uniformly; two films are outright failure cases, for reasons that turn out to
|
||||
be one mechanism.
|
||||
|
||||
## Failure mode 1: frozen-bbox "ghost tracks"
|
||||
## The failure mode: frozen-bbox "ghost tracks"
|
||||
|
||||
Both Many Saints of Newark (974 misIDs) and Downton Abbey (FN=80084, the worst
|
||||
recall of the five) trace to the same root cause, verified directly against
|
||||
@@ -36,51 +58,69 @@ inferred from the score alone.
|
||||
At this second, three of the four labeled boxes ("Jon Bernthal", "Joey Diaz",
|
||||
"Billy Magnussen") sit over empty background — a blurred wall, hanging
|
||||
plates — with no face in them. The real face in frame carries a second,
|
||||
colliding label from another frozen box.
|
||||
colliding label from another frozen box. And it isn't an isolated second — the
|
||||
same signature recurs throughout the film:
|
||||
|
||||

|
||||

|
||||
*Frame `many_saints_intervals/w002_worst_t01382.jpg`, same artifact package —
|
||||
one frame, three distinct error classes.*
|
||||
|
||||
This is the starkest case: **15 actors named, all wrong, over a completely
|
||||
blank closing title card.** Confirmed against the dump directly: `face_count`
|
||||
is 0 from this point onward (no detector output at all), yet the same 15
|
||||
identities keep appearing with the *exact same bounding box, unchanged to the
|
||||
pixel*, for 57+ consecutive seconds.
|
||||
This frame is worth reading closely, because it separates three things that a
|
||||
single aggregate F1 number smears together. The two green labels ("Jon Bernthal
|
||||
100%", "Michela De Rossi 100%") float over a staircase and a policeman's back —
|
||||
frozen boxes from a previous shot, reported at full confidence. Meanwhile the
|
||||
two *real* frontal faces in frame get honest blue "unknown 0%" boxes (they're
|
||||
uncredited day-players with no gallery reference — the
|
||||
[gallery coverage gap](gallery-scope.md)), and three more people simply face
|
||||
away from camera, invisible to any face detector but still "present" in X-Ray's
|
||||
scene-level ground truth. Precision failure, gallery-coverage failure, and the
|
||||
face-vs-presence ceiling — one frame.
|
||||
|
||||
### The mechanism, measured
|
||||
|
||||
The starkest case is Downton Abbey's hard cut from a packed group shot into a
|
||||
long blank credits sequence. Plotting the detector's per-second `face_count`
|
||||
(from the dump HDF5, independent of the tracker) against what the tracker
|
||||
reports makes the failure legible at a glance:
|
||||
|
||||

|
||||
|
||||
From the cut onward the detector sees **zero faces for nearly a minute** — and
|
||||
the tracker keeps reporting the last group shot's 15 identities the entire
|
||||
time, each with the *exact same bounding box, unchanged to the pixel* (verified
|
||||
for Hugh Bonneville: `(1743.2, 0.0, 171.3, 317.8)` at every sampled second for
|
||||
57+ seconds). The staircase decay at the right edge is the extinction window
|
||||
finally expiring, actor by actor.
|
||||
|
||||
This is `SceneTrackerFunc::active_[actor_idx].last_bbox`
|
||||
(`src/nodes/scene_tracker_node.hpp`) being re-emitted unchanged — the
|
||||
extinction state machine working exactly as coded, not a bug. The film cuts
|
||||
from a packed group shot straight into 40+ seconds of blank titles/credits,
|
||||
and `extinction_sec=57.4` is comfortably long enough to bridge that entire gap
|
||||
without expiring, so the tracker faithfully reports "last known position" for
|
||||
a cast that is no longer on screen at all. `extinction_sec` was tuned toward
|
||||
long windows specifically because they bridge real gaps (occlusion, a turned
|
||||
face) in most training footage — this is the cost side of that trade,
|
||||
surfacing only when a film has a long enough faceless stretch to expose it.
|
||||
|
||||
## Failure mode 2: a genuine misID (for contrast)
|
||||
|
||||
Not every held-out failure is a ghost. This is a real face, correctly
|
||||
detected, confidently misidentified:
|
||||
|
||||
*(same many_saints_ghost_fpi.jpg frame above also shows Leslie Odom Jr.'s box
|
||||
carrying a second, colliding "Michael Gandolfini" label — two real tracks'
|
||||
frozen positions happening to overlap, not a detection error.)*
|
||||
([`src/nodes/scene_tracker_node.hpp`](https://REPOLINK/src/nodes/scene_tracker_node.hpp))
|
||||
being re-emitted unchanged — the
|
||||
extinction state machine working exactly as coded, not a bug. `extinction_sec`
|
||||
was tuned to 57.4s specifically because long windows bridge real gaps
|
||||
(occlusion, a turned face) in most footage; a hard cut into long faceless
|
||||
footage is the one case where that same bridging manufactures ghosts, and the
|
||||
training films never contained one long enough to punish it. The optimizer
|
||||
"discovered" the plateau at the top of its search range for a reason that only
|
||||
generalizes to films that never go faceless for a minute.
|
||||
|
||||
## Where LVFace beat X-Ray
|
||||
|
||||
Not every "misID" is actually wrong. `second_score.py` counts a name as a true
|
||||
out-of-cast misID whenever the named actor isn't in X-Ray's credited cast list
|
||||
for the film at all — but X-Ray's cast list is itself incomplete.
|
||||
Not every flagged "misID" is actually wrong.
|
||||
[`scripts/optimizer/second_score.py`](https://REPOLINK/scripts/optimizer/second_score.py)
|
||||
counts a name as a true out-of-cast misID whenever the named actor isn't in
|
||||
X-Ray's credited cast list for the film at all — but X-Ray's cast list is
|
||||
itself incomplete.
|
||||
|
||||

|
||||
|
||||
Germar Terrell Gardner — a real, clean, high-confidence detection — is counted
|
||||
as a misID here because he doesn't appear in X-Ray's `people.csv` for The Many
|
||||
Saints of Newark at all. But Jellyfin's independent cast metadata *does* credit
|
||||
him for this exact film (cross-checked via `experiments/manifests/
|
||||
jellyfin_casts.json`, a completely separate data source from X-Ray). This
|
||||
isn't a lookalike error or a gallery mixup — it's the pipeline correctly
|
||||
recognising a real cast member that one ground-truth source happened to omit.
|
||||
him for this exact film (cross-checked via
|
||||
`experiments/manifests/jellyfin_casts.json` from the `experiment-data` artifact
|
||||
package, a completely separate data source from X-Ray). This isn't a lookalike error or a gallery mixup — it's the
|
||||
pipeline correctly recognising a real cast member that one ground-truth source
|
||||
happened to omit.
|
||||
|
||||
This doesn't mean every flagged misID is secretly correct — Many Saints'
|
||||
974-count total is still overwhelmingly the frozen-bbox failure mode above,
|
||||
@@ -91,8 +131,9 @@ in the other direction too.
|
||||
|
||||
## Summary
|
||||
|
||||
LVFace is the right default: it wins the model comparison outright, and its
|
||||
failures are traceable, understood, and mostly attributable to one tunable
|
||||
knob (`extinction_sec`) rather than the embedder itself. The held-out
|
||||
LVFace is the right default: it wins the model comparison outright, it can name
|
||||
19 faces correctly in a single hostile group shot, and its failures are
|
||||
traceable, understood, and mostly attributable to one tunable knob
|
||||
(`extinction_sec`) rather than the embedder itself. The held-out
|
||||
generalization gap (75.3% → 67.4%) is real and should be treated as the honest
|
||||
expected performance, not the training-set number.
|
||||
|
||||
Reference in New Issue
Block a user