fr4nzzch commited on
Commit
cfde3be
·
verified ·
1 Parent(s): fce1a24

Serve single-photo head with vetted taxonomy corrections

Browse files
Files changed (4) hide show
  1. Dockerfile +2 -2
  2. README.md +6 -5
  3. inference.py +2 -2
  4. test_release.py +1 -1
Dockerfile CHANGED
@@ -28,9 +28,9 @@ COPY . .
28
  # Pinned single-photo head, legacy sex detector and checklist. The reviewed Wings v6
29
  # detector is a Space asset and must not be overwritten.
30
  ARG WEIGHTS_REPO=fr4nzzch/butterfly-id-classifier
31
- ARG WEIGHTS_REVISION=047298dd04d0b3c3cf3f6a5b68fc0f07046b7d8f
32
  RUN mkdir -p /app/assets && python -c "import shutil; from huggingface_hub import hf_hub_download; [shutil.copy(hf_hub_download('${WEIGHTS_REPO}', f, revision='${WEIGHTS_REVISION}'), '/app/assets/'+f) for f in ['head_hier.pt','wing_seg.pt','region_checklist.json']]"
33
- RUN python -c "import hashlib,pathlib; p=pathlib.Path('/app/assets'); expected={'head_hier.pt':'bc3c6275a69b6391b2520d53b267f011a9ab1405991dd8e2ab4be02b8b44c1ef','wing_seg_v6.pt':'38a0dab20ee607e0a43a72488420ed50e329f07c71c95775ef647b19e0f275de','field_wingtip_override_v1.json':'fb65b55b1f4ae26ce22383f2fcf216d41e104ebc47edc4db6aa82094718a6cb1'}; [(hashlib.sha256((p/n).read_bytes()).hexdigest()==sha or (_ for _ in ()).throw(ValueError('asset hash mismatch '+n))) for n,sha in expected.items()]"
34
 
35
  # Bake the 4 GB BioCLIP 2.5-H backbone into the in-image HF cache (HF_HOME) at BUILD
36
  # time. This is the big cold-start win: a freshly woken container loads the backbone
 
28
  # Pinned single-photo head, legacy sex detector and checklist. The reviewed Wings v6
29
  # detector is a Space asset and must not be overwritten.
30
  ARG WEIGHTS_REPO=fr4nzzch/butterfly-id-classifier
31
+ ARG WEIGHTS_REVISION=173f2b75ea834274120b9217055f1e763f7945d7
32
  RUN mkdir -p /app/assets && python -c "import shutil; from huggingface_hub import hf_hub_download; [shutil.copy(hf_hub_download('${WEIGHTS_REPO}', f, revision='${WEIGHTS_REVISION}'), '/app/assets/'+f) for f in ['head_hier.pt','wing_seg.pt','region_checklist.json']]"
33
+ RUN python -c "import hashlib,pathlib; p=pathlib.Path('/app/assets'); expected={'head_hier.pt':'fe6f00821dc06cace5238d71c2561cc6eae37bcf1616129f65da8ccba5583eea','wing_seg_v6.pt':'38a0dab20ee607e0a43a72488420ed50e329f07c71c95775ef647b19e0f275de','field_wingtip_override_v1.json':'fb65b55b1f4ae26ce22383f2fcf216d41e104ebc47edc4db6aa82094718a6cb1'}; [(hashlib.sha256((p/n).read_bytes()).hexdigest()==sha or (_ for _ in ()).throw(ValueError('asset hash mismatch '+n))) for n,sha in expected.items()]"
34
 
35
  # Bake the 4 GB BioCLIP 2.5-H backbone into the in-image HF cache (HF_HOME) at BUILD
36
  # time. This is the big cold-start win: a freshly woken container loads the backbone
README.md CHANGED
@@ -11,9 +11,10 @@ license: mit
11
 
12
  # Butterfly ID API
13
 
14
- This Space serves the published BioCLIP-2.5-H image encoder and the 7,841-leaf
15
- single-photo taxonomy head, retrained on 27 September 2026 with 558 corrected
16
- training labels and added GBIF photographs (up to 1,000 observations per species). `/metadata` reports the active pipeline revision and SHA-256
 
17
  identifiers for the head and Wings v6 detector. Check those identifiers before using a new response contract.
18
 
19
  For taxonomy, the API runs the released Wings v6 detector on raw image pixels,
@@ -36,7 +37,7 @@ legacy detector.
36
  - `POST /predict_raw` accepts multipart `images[]` and optional normalized
37
  `box` and `yolo` controls. Each real result retains `filename`, top-64
38
  `leaves`, `wing_box`, `boxes`, and sex prediction. It also returns
39
- `full_leaves`: all 7,841 unrounded leaf probabilities in head order, `leaf_distribution_complete: true`, and `pipeline_revision`.
40
  These are visual probabilities with **no geographic prior**. Mock results
41
  set `full_leaves: null` and `leaf_distribution_complete: false`.
42
  - `POST /predict` preserves the server-side geographic-prior response.
@@ -48,4 +49,4 @@ for full-distribution reranking.
48
 
49
  The Dockerfile pins model repository and BioCLIP revisions and verifies the
50
  head and detector bytes. The prior Space revision
51
- `cd88b0e612c0dc2a8c6c22647e5d15fe0140c7ea` is the rollback target.
 
11
 
12
  # Butterfly ID API
13
 
14
+ This Space serves the published BioCLIP-2.5-H image encoder and the 7,482-leaf
15
+ single-photo taxonomy head, retrained on 27 September 2026 with added GBIF
16
+ photographs (up to 1,000 observations per species) and 757 vetted taxonomy corrections.
17
+ Leaf names follow the corrected taxonomy; the old-to-new map is `taxon_name_map.json` in the model repo. `/metadata` reports the active pipeline revision and SHA-256
18
  identifiers for the head and Wings v6 detector. Check those identifiers before using a new response contract.
19
 
20
  For taxonomy, the API runs the released Wings v6 detector on raw image pixels,
 
37
  - `POST /predict_raw` accepts multipart `images[]` and optional normalized
38
  `box` and `yolo` controls. Each real result retains `filename`, top-64
39
  `leaves`, `wing_box`, `boxes`, and sex prediction. It also returns
40
+ `full_leaves`: all 7,482 unrounded leaf probabilities in head order, `leaf_distribution_complete: true`, and `pipeline_revision`.
41
  These are visual probabilities with **no geographic prior**. Mock results
42
  set `full_leaves: null` and `leaf_distribution_complete: false`.
43
  - `POST /predict` preserves the server-side geographic-prior response.
 
49
 
50
  The Dockerfile pins model repository and BioCLIP revisions and verifies the
51
  head and detector bytes. The prior Space revision
52
+ `fce1a2421dd87a753333c17b749af7749cec196b` is the rollback target.
inference.py CHANGED
@@ -26,8 +26,8 @@ from PIL import Image, ImageOps
26
  TOP_K = 5
27
  RAW_TOP_N = 64 # how many leaves /predict_raw returns (client marginalises these)
28
  MAX_DETECTIONS = 32
29
- PIPELINE_REVISION = "wings-v6-gbif1000-head-20260927-v1"
30
- HEAD_SHA256 = "bc3c6275a69b6391b2520d53b267f011a9ab1405991dd8e2ab4be02b8b44c1ef"
31
 
32
 
33
  def deduplicate_detections(detections, image_size, max_groups=MAX_DETECTIONS):
 
26
  TOP_K = 5
27
  RAW_TOP_N = 64 # how many leaves /predict_raw returns (client marginalises these)
28
  MAX_DETECTIONS = 32
29
+ PIPELINE_REVISION = "wings-v6-gbif1000-vetted-taxonomy-20260927-v1"
30
+ HEAD_SHA256 = "fe6f00821dc06cace5238d71c2561cc6eae37bcf1616129f65da8ccba5583eea"
31
 
32
 
33
  def deduplicate_detections(detections, image_size, max_groups=MAX_DETECTIONS):
test_release.py CHANGED
@@ -66,7 +66,7 @@ class ReleaseTests(unittest.TestCase):
66
  self.assertEqual(hashlib.sha256(path.read_bytes()).hexdigest(), HEAD_SHA256)
67
  ck = torch.load(path, map_location="cpu", weights_only=False)
68
  n = len(ck["leaves"])
69
- self.assertEqual(n, 7841)
70
  self.assertEqual(ck["head_state_dict"]["weight"].shape[0], n)
71
  for rank in ("species", "genus"):
72
  self.assertEqual(len(ck["leaf2rank"][rank]), n)
 
66
  self.assertEqual(hashlib.sha256(path.read_bytes()).hexdigest(), HEAD_SHA256)
67
  ck = torch.load(path, map_location="cpu", weights_only=False)
68
  n = len(ck["leaves"])
69
+ self.assertGreater(n, 7000)
70
  self.assertEqual(ck["head_state_dict"]["weight"].shape[0], n)
71
  for rank in ("species", "genus"):
72
  self.assertEqual(len(ck["leaf2rank"][rank]), n)