docs(wiki): phase B training path and hardware decisions — recorded, not built
Claude-Session: https://claude.ai/code/session_01FWncR69HgGPuei1dLrW3cU
This commit is contained in:
@@ -106,7 +106,8 @@ Three surfaces, nothing else — it must not grow into a fleet console:
|
|||||||
- **`GET /export/labels.csv`** — reviewed, usable rows: item, booth, crop path, the reviewer's
|
- **`GET /export/labels.csv`** — reviewed, usable rows: item, booth, crop path, the reviewer's
|
||||||
label, the operator's category + classes, the camera's class + confidence, downgraded, at.
|
label, the operator's category + classes, the camera's class + confidence, downgraded, at.
|
||||||
Crops are not packaged: the phase-B trainer runs **on the same host** (its GPU) and reads them
|
Crops are not packaged: the phase-B trainer runs **on the same host** (its GPU) and reads them
|
||||||
off the volume — `docker-compose.collector.yml` carries the `trainer` seam as a commented
|
off the volume ([[bodytype-classifier-training]]: CPU-only, the Xeon is enough) —
|
||||||
|
`docker-compose.collector.yml` carries the `trainer` seam as a commented
|
||||||
`profiles: [train]` one-off job (next increment).
|
`profiles: [train]` one-off job (next increment).
|
||||||
|
|
||||||
**Deploy notes.** Bind the published port to the host's **Netbird address** (`COLLECTOR_BIND`),
|
**Deploy notes.** Bind the published port to the host's **Netbird address** (`COLLECTOR_BIND`),
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
---
|
||||||
|
title: Body-type classifier (phase B) — training path and hardware
|
||||||
|
type: decision
|
||||||
|
status: decided 2026-09-07; NOT built (user: "no build just yet")
|
||||||
|
related: [vision-review-outbox, opencv-anpr-service, venue-modules, fleet-deployment-komodo, technology-stack]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Body-type classifier (phase B) — training path and hardware
|
||||||
|
|
||||||
|
The Car Wash category suggestion needs SUV vs sedan, which the phase-A COCO detector cannot give
|
||||||
|
([[opencv-anpr-service]] §Vehicle body type). Phase B is a **classifier over the detector's crop**,
|
||||||
|
trained on the reviewer's labels gathered through the [[vision-review-outbox]]. This page records
|
||||||
|
what the loop looks like, what it runs on, and what is deliberately not done. Discussed and decided
|
||||||
|
with the user on 2026-09-07; **nothing here is built yet** — the user will say when.
|
||||||
|
|
||||||
|
## The loop (as designed)
|
||||||
|
|
||||||
|
Today the loop stops at the reviewer's verdict: the collector holds labels + crops and can export
|
||||||
|
`labels.csv`. Nothing trains, nothing updates a booth. The rest of the path, each step a place
|
||||||
|
where a person decides:
|
||||||
|
|
||||||
|
1. **Train** — a one-off job (`apps/trainer`, Python/uv like the vision service) on the
|
||||||
|
collector's host reads the export and the crops straight off the collector volume, splits by
|
||||||
|
TIME (validation = newer cars than training, so the number reflects tomorrow's traffic), and
|
||||||
|
fine-tunes a small **BSD-licensed torchvision backbone** (the licence rule applies to weights as
|
||||||
|
much as code; timm/ImageNet weights only if their terms are checked). Outputs three files: the
|
||||||
|
ONNX classifier, a sidecar (class list, preprocessing constants, version), and a metrics report
|
||||||
|
(accuracy per class + confusion matrix). It **refuses to write the model** below a validation
|
||||||
|
floor the owner sets — a bad model never becomes a file. Class imbalance (nine sedans in ten)
|
||||||
|
is weighted in the loss and reported; classes with too few labels are dropped from that run.
|
||||||
|
2. **Evaluate before anything ships** — the owner reads the report. 85–95 % on frontal gate views
|
||||||
|
is the expectation once tuned; enough to *flag*, never to *bill* (the flag records, the site
|
||||||
|
threshold exists for exactly this).
|
||||||
|
3. **Publish** — weights are not code and do not live in git: a versioned file in the Gitea
|
||||||
|
package registry / a release asset, fetched by URL like the YOLOX weights.
|
||||||
|
4. **Bake and build** — the vision Dockerfile fetches that version at build time; a second stage in
|
||||||
|
`vehicle.py` runs the classifier on the detector's box and replaces `car` with the finer class
|
||||||
|
when confident, else keeps YOLOX's answer. One model path setting like the YOLOX one; off when
|
||||||
|
unset. The contract, the mapping chips and the flag do not change — the vocabulary already holds
|
||||||
|
sedan/hatchback/suv/minivan/pickup.
|
||||||
|
5. **Deploy** — a TAG bump on the booth's stack. **A booth gets a model the way it gets code**: a
|
||||||
|
pinned release you can see and roll back. No runtime model fetch (air-gapped appliance,
|
||||||
|
read-only model path — [[vision-service-hardening]]).
|
||||||
|
|
||||||
|
Retrain when the labels have grown meaningfully (every few hundred new verdicts at first). First
|
||||||
|
run needs roughly **200 reviewed crops per class that matters** (Vetura and SUV at least). Once a
|
||||||
|
model exists, its predictions on the *unlabelled* pile checked against a small reviewed sample are
|
||||||
|
the ongoing accuracy check without labelling everything — which is why every entry is sent, not a
|
||||||
|
sample ([[vision-review-outbox]] §The entry stream).
|
||||||
|
|
||||||
|
## Hardware (decided 2026-09-07)
|
||||||
|
|
||||||
|
What the owner has: an **NVIDIA Quadro FX 3800** (in hand, not installed), and in
|
||||||
|
`art-docker-station` an **Intel Xeon E3-1225 v5** (4 Skylake cores, AVX2, no AVX-512) with the
|
||||||
|
**Intel HD P530** iGPU.
|
||||||
|
|
||||||
|
- **Quadro FX 3800 — stays in the drawer.** 2009, GT200, compute capability 1.3, 1 GB. CUDA dropped
|
||||||
|
that generation in 2015; no PyTorch build of the last decade can use it. Installing it buys a
|
||||||
|
heater and a driver problem.
|
||||||
|
- **HD P530 — not for training.** Usable for *inference* via OpenVINO, irrelevant here: inference
|
||||||
|
runs on the booths' CPUs, which already do YOLOX in ~250 ms.
|
||||||
|
- **The Xeon does the job.** The problem is small (a few thousand 224-px crops, ten classes, a
|
||||||
|
small pretrained backbone). Two modes the trainer should offer:
|
||||||
|
- *Feature extraction + a small head* — run every crop once through the frozen backbone, cache
|
||||||
|
the feature vectors, train a classifier on top: minutes for a few thousand crops, seconds to
|
||||||
|
retrain when labels arrive. Expected to carry most of the accuracy on frontal gate views.
|
||||||
|
- *Full fine-tune* — unfreeze and train end to end: roughly an hour per run on four cores with
|
||||||
|
a mobile-sized backbone. The step to take when the cheap mode plateaus.
|
||||||
|
Training is occasional and unattended, so an hour on a CPU is a non-issue; the data is already
|
||||||
|
on that host, so nothing moves.
|
||||||
|
- **Consequences for the build:** the trainer image is **CPU-only PyTorch** (< 1 GB, not the 5 GB
|
||||||
|
CUDA build); the `trainer` seam in `docker-compose.collector.yml` drops the NVIDIA device
|
||||||
|
reservation (one-line change if a modern card ever lands in the host; the trainer should pick
|
||||||
|
up CUDA when present).
|
||||||
|
- **If faster is ever wanted:** a used mid-range card of the last few generations (~€200) turns
|
||||||
|
the hour into a minute, given a slot and a PSU. **Renting a cloud GPU is rejected**: the crops
|
||||||
|
would leave the premises, and even scrubbed of plates and site that runs against the whole
|
||||||
|
privacy design of the outbox.
|
||||||
|
|
||||||
|
## Not built
|
||||||
|
|
||||||
|
`apps/trainer`, the classifier stage in `vehicle.py`, the publish step, the compose `trainer`
|
||||||
|
profile (still commented, still with the GPU reservation to remove). First real run waits for
|
||||||
|
the first few hundred reviewed labels on the collector.
|
||||||
@@ -251,6 +251,9 @@ service's `/health` each tick and shows a **"Vision" chip** in the booth footer
|
|||||||
|
|
||||||
## Vehicle body type (advisory) — the vehicle stage, phase A (2026-09-06)
|
## Vehicle body type (advisory) — the vehicle stage, phase A (2026-09-06)
|
||||||
|
|
||||||
|
> Phase B (the classifier that knows SUV from sedan), its training loop and the hardware it runs on
|
||||||
|
> are decided on [[bodytype-classifier-training]] — not built yet.
|
||||||
|
|
||||||
`/analyze` populates `vehicle.body_type` + `vehicle.confidence` from the shared vocabulary
|
`/analyze` populates `vehicle.body_type` + `vehicle.confidence` from the shared vocabulary
|
||||||
(car, sedan, hatchback, suv, minivan, pickup, van, truck, bus, motorcycle). Node records it beside
|
(car, sedan, hatchback, suv, minivan, pickup, van, truck, bus, motorcycle). Node records it beside
|
||||||
the plate and the Car Wash desk pre-selects the category the site maps it to; the operator
|
the plate and the Car Wash desk pre-selects the category the site maps it to; the operator
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ Counts: 4 sources · 19 entities · 47 concepts · 8 decision records.
|
|||||||
- [[dingtian-vs-mqtt]] — transport choice: direct HTTP/UDP now, MQTT parked until multi-lane scale.
|
- [[dingtian-vs-mqtt]] — transport choice: direct HTTP/UDP now, MQTT parked until multi-lane scale.
|
||||||
- [[session-model]] — business layer start: session = projection; transient-first; pay-on-foot. New event types.
|
- [[session-model]] — business layer start: session = projection; transient-first; pay-on-foot. New event types.
|
||||||
- [[vision-service]] — build a host-side ANPR + vehicle-verification service; replaces edge-LPR; scoped AGPL exception.
|
- [[vision-service]] — build a host-side ANPR + vehicle-verification service; replaces edge-LPR; scoped AGPL exception.
|
||||||
|
- [[bodytype-classifier-training]] — phase B (SUV vs sedan) training path: trainer on the collector host → evaluate → publish weights → bake into the vision image → TAG bump; runs on the Xeon E3-1225 v5 CPU (feature-extraction head first, full fine-tune later), Quadro FX 3800 unusable, cloud GPU rejected (crops stay on premises). Decided 2026-09-07, NOT built.
|
||||||
- [[vision-service-packaging]] — the vision service lives in this monorepo (apps/vision/), separate process, wired into Turbo via a package.json shim; uv-managed Python.
|
- [[vision-service-packaging]] — the vision service lives in this monorepo (apps/vision/), separate process, wired into Turbo via a package.json shim; uv-managed Python.
|
||||||
- [[event-streams-split]] — split the signed business ledger (ledger_events) from unsigned device telemetry (device_events).
|
- [[event-streams-split]] — split the signed business ledger (ledger_events) from unsigned device telemetry (device_events).
|
||||||
- [[desktop-shell-tauri]] — ✅ Tauri v2 chosen over Electron for the desktop kiosk shell; thin wrapper, server keeps all logic. Best case Ubuntu 26.04 LTS (resolves WebKitGTK); worst case Windows+WSL → kiosk browser, no native shell. Auto-updater mirrors signed releases to public `mca/public_releases` (source repo is private — field appliances have no Gitea creds).
|
- [[desktop-shell-tauri]] — ✅ Tauri v2 chosen over Electron for the desktop kiosk shell; thin wrapper, server keeps all logic. Best case Ubuntu 26.04 LTS (resolves WebKitGTK); worst case Windows+WSL → kiosk browser, no native shell. Auto-updater mirrors signed releases to public `mca/public_releases` (source repo is private — field appliances have no Gitea creds).
|
||||||
|
|||||||
+10
@@ -3133,3 +3133,13 @@ in-process) + `enqueueEntry()` (crop + camera class, no order/operator/category)
|
|||||||
kind column, operator agreement is wash-only. Setup line shows "1 in N entries sampled". Also:
|
kind column, operator agreement is wash-only. Setup line shows "1 in N entries sampled". Also:
|
||||||
Setup → Car wash is a two-column grid (the master-data card was squeezed at max-w-2xl). Tests
|
Setup → Car wash is a two-column grid (the master-data card was squeezed at max-w-2xl). Tests
|
||||||
on both sides. Updated [[vision-review-outbox]].
|
on both sides. Updated [[vision-review-outbox]].
|
||||||
|
|
||||||
|
## [2026-09-07] decide | Phase B training path + hardware — recorded, not built
|
||||||
|
User asked "now what about the training" and then "let's talk hardware". Recorded on the new
|
||||||
|
[[bodytype-classifier-training]]: the five-step loop (train on the collector host → evaluate with a
|
||||||
|
floor → publish weights to the registry → bake into the vision image → TAG bump; a booth gets a
|
||||||
|
model the way it gets code, never a runtime fetch); ~200 reviewed crops per class before the first
|
||||||
|
run; the Quadro FX 3800 is unusable (cc 1.3), the HD P530 irrelevant, the Xeon E3-1225 v5 is enough
|
||||||
|
(feature-extraction head in minutes, full fine-tune ~1 h); trainer image = CPU-only torch, the
|
||||||
|
compose seam drops the GPU reservation; cloud GPU rejected (crops stay on premises). Linked from
|
||||||
|
[[opencv-anpr-service]], [[vision-review-outbox]], index. User: "No build just yet."
|
||||||
|
|||||||
Reference in New Issue
Block a user