feat: fine-tune the identity head, and measure what it buys
Part 2 ended on the number that matters: enrolling a real shelf from itself leaves off-the-shelf DINOv2 at ~25% top-1, because real milk cartons differ by small text and a colour band. This trains a small projection head on frozen features - 384 -> 128, cosine classifier, angular margin - with augmentation standing in for the second visit we do not have, and measures it on 72 held-out facings over 8 products. raw DINOv2 cosine top-1 2/72 (2%) balanced 1% raw, rejecting below 0.55 top-1 2/72 (2%) balanced 1% (5% of answered right) fine-tuned + margin top-1 28/72 (38%) balanced 73% fine-tuned, rejecting below 0.4 top-1 17/72 (23%) balanced 58% (94% of answered right) So fine-tuning is the difference between working and not (balanced 1% -> 73%), and with a rejection threshold the model becomes precise - it answers 18 of 72 facings and is right on 94% of them, which is the operating point a store wants. The raw model cannot do that at any threshold. Where it fails is where my labels are coarse: MEGMILK, which the contact sheet shows is probably several products under one brand, scores 1/32 because the class is internally inconsistent and the model correctly refuses most of it. Honest limit, stated in the doc: enrolment and held-out facings come from one photograph, so these are upper bounds; only a recorded second visit settles the real number. What is settled is the mechanism - a head trained on labels the pipeline can produce itself moves fine-grained identity from useless to usable. 175 tests pass. Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
7d01de8062
commit
4fd5059ef0
2 changed files with 303 additions and 0 deletions
|
|
@ -180,3 +180,55 @@ So the honest design is: **reject by default**, name the product from the tag wh
|
|||
is readable, from the packaging text when it is legible, from a fine-tuned embedding
|
||||
otherwise - and hand the remainder to the operator, whose answers become the next
|
||||
enrollment.
|
||||
|
||||
|
||||
---
|
||||
|
||||
# Part 3: fine-tuning the embedding, and what it buys
|
||||
|
||||
Part 2 ended on the number that matters: enrolling a real shelf from itself leaves
|
||||
off-the-shelf DINOv2 at ~25 % top-1, because real milk cartons differ by small text
|
||||
and a colour band. The literature's answer is fine-tuning, so this measures it.
|
||||
|
||||
`code/finetune_identity.py` trains a small projection head (384 -> 128, cosine
|
||||
classifier, angular margin) on frozen DINOv2 features, with augmentation standing in
|
||||
for the second visit we do not have. Two enrolment views per product, twelve
|
||||
augmented copies each, 72 held-out facings, 8 products. Balanced accuracy is reported
|
||||
because the shelf is unbalanced (MEGMILK has 34 facings, 酪農3.6 has two - a system
|
||||
that always answered "MEGMILK" would look respectable on the raw rate).
|
||||
|
||||
| | top-1 | balanced | answered | of those right |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| raw DINOv2 cosine | 2/72 (2 %) | 1 % | 72 | 3 % |
|
||||
| raw, rejecting below 0.55 | 2/72 (2 %) | 1 % | 44 | 5 % |
|
||||
| **fine-tuned + margin** | 28/72 (38 %) | **73 %** | 72 | 39 % |
|
||||
| **fine-tuned, rejecting below 0.4** | 17/72 (23 %) | 58 % | 18 | **94 %** |
|
||||
|
||||
Three things follow.
|
||||
|
||||
1. **Fine-tuning is not a refinement, it is the difference between working and not.**
|
||||
Balanced accuracy goes from **1 % to 73 %** on the same held-out facings. Off-the-
|
||||
shelf features cannot separate these products; a head trained on sixteen crops can.
|
||||
2. **With a rejection threshold it becomes precise**: answering only 18 of 72 facings
|
||||
and being right on 94 % of them is exactly the operating point a store wants -
|
||||
name what you are sure of, and hand the rest to the operator. The raw model cannot
|
||||
do this at any threshold (5 % of answered).
|
||||
3. **Where it fails is where the labels are coarse.** Per class, with rejection:
|
||||
明治ラブ 2/2, おいしい無調整豆乳 2/2, 低脂肪 1/1, 特濃4.5 1/2, 北海道牛乳 8/21,
|
||||
明治 おいしい牛乳 2/12, and **MEGMILK 1/32**. MEGMILK is the class I labelled most
|
||||
coarsely - the contact sheet shows what are probably several different products
|
||||
under one brand - so the class is internally inconsistent and the model correctly
|
||||
refuses most of it. Better labels, not a better model, is what that needs.
|
||||
|
||||
## The honest limit
|
||||
|
||||
Enrolment and held-out facings come from **one photograph**. Augmentation stands in
|
||||
for a second visit, which is not the same thing: a real second visit changes the
|
||||
light, the angle and the stock. So 73 % balanced and 94 % precision are **upper
|
||||
bounds**, and the only way to know the real number is to record a second visit
|
||||
(`docs/DATA.md`).
|
||||
|
||||
What is settled is the mechanism: **a head trained on labels the pipeline can produce
|
||||
itself** - tag-named facings, plus operator confirmations - moves fine-grained
|
||||
identity from useless to usable, and a rejection threshold turns it into something a
|
||||
store can act on.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue