diff --git a/wiki/concepts/vision-review-outbox.md b/wiki/concepts/vision-review-outbox.md index 8d1959b..f6f8fdc 100644 --- a/wiki/concepts/vision-review-outbox.md +++ b/wiki/concepts/vision-review-outbox.md @@ -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 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 - 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). **Deploy notes.** Bind the published port to the host's **Netbird address** (`COLLECTOR_BIND`), diff --git a/wiki/decisions/bodytype-classifier-training.md b/wiki/decisions/bodytype-classifier-training.md new file mode 100644 index 0000000..c9597a9 --- /dev/null +++ b/wiki/decisions/bodytype-classifier-training.md @@ -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. diff --git a/wiki/entities/opencv-anpr-service.md b/wiki/entities/opencv-anpr-service.md index 41215f6..e923097 100644 --- a/wiki/entities/opencv-anpr-service.md +++ b/wiki/entities/opencv-anpr-service.md @@ -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) +> 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 (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 diff --git a/wiki/index.md b/wiki/index.md index 20c40fb..eb8118b 100644 --- a/wiki/index.md +++ b/wiki/index.md @@ -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. - [[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. +- [[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. - [[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). diff --git a/wiki/log.md b/wiki/log.md index deba920..08875ba 100644 --- a/wiki/log.md +++ b/wiki/log.md @@ -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: 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]]. + +## [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."