Upload README.md with huggingface_hub
Browse files
README.md
CHANGED
|
@@ -52,6 +52,7 @@ Each model answered the same calorie question on the same dishes
|
|
| 52 |
| Qwen3.5-9B (Q8_0, thinking off) ‡ | 304 | 117.2 | 64.5 | 65.8% | +6.1 |
|
| 53 |
| MiniCPM-V 4.6 (1.3B) | 304 | 163.3 | 97.0 | 52.6% | +75.5 |
|
| 54 |
| Gemma-4-12B † | 100 | 176.8 | 90.5 | 58.0% | −43.3 |
|
|
|
|
| 55 |
| CalorieCLIP | 304 | 196.2 | 133.2 | 42.1% | +121.1 |
|
| 56 |
|
| 57 |
† Gemma-4-12B was scored on a seeded 100-dish paired subset of the same 304 dishes (compute budget);
|
|
@@ -59,6 +60,33 @@ all parse rates were 100%.
|
|
| 59 |
‡ Qwen3.5-9B ran as an 8-bit GGUF (Q8_0) via llama.cpp with 4k context and thinking disabled.
|
| 60 |
Notably it matches the 2B (117.2 vs 116.4 MAE) — 4.5× more parameters bought no accuracy on this task,
|
| 61 |
suggesting the zero-shot VLM error floor is portion-mass ambiguity, not model capacity.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
## Training method
|
| 64 |
|
|
@@ -107,7 +135,7 @@ MODEL_ID = "google/siglip2-base-patch16-224"
|
|
| 107 |
processor = AutoImageProcessor.from_pretrained(MODEL_ID)
|
| 108 |
vision = SiglipVisionModel.from_pretrained(MODEL_ID)
|
| 109 |
head = NutritionHead()
|
| 110 |
-
state = torch.load(hf_hub_download("Dralkh/CalorieK", "
|
| 111 |
vision.load_state_dict(state["vision_state"]); head.load_state_dict(state["head_state"])
|
| 112 |
vision.eval(); head.eval()
|
| 113 |
|
|
|
|
| 52 |
| Qwen3.5-9B (Q8_0, thinking off) ‡ | 304 | 117.2 | 64.5 | 65.8% | +6.1 |
|
| 53 |
| MiniCPM-V 4.6 (1.3B) | 304 | 163.3 | 97.0 | 52.6% | +75.5 |
|
| 54 |
| Gemma-4-12B † | 100 | 176.8 | 90.5 | 58.0% | −43.3 |
|
| 55 |
+
| MiniMax M3 (hosted, thinking) | 304 | 193.5 | 120.5 | 44.7% | +103.7 |
|
| 56 |
| CalorieCLIP | 304 | 196.2 | 133.2 | 42.1% | +121.1 |
|
| 57 |
|
| 58 |
† Gemma-4-12B was scored on a seeded 100-dish paired subset of the same 304 dishes (compute budget);
|
|
|
|
| 60 |
‡ Qwen3.5-9B ran as an 8-bit GGUF (Q8_0) via llama.cpp with 4k context and thinking disabled.
|
| 61 |
Notably it matches the 2B (117.2 vs 116.4 MAE) — 4.5× more parameters bought no accuracy on this task,
|
| 62 |
suggesting the zero-shot VLM error floor is portion-mass ambiguity, not model capacity.
|
| 63 |
+
MiniMax M3 (a hosted reasoning model, chain-of-thought enabled) reinforces the point from the other
|
| 64 |
+
direction: extensive per-dish reasoning did not help — it lands second-to-last with a strong +104 kcal
|
| 65 |
+
systematic overestimate. Reasoning does not fix calibration.
|
| 66 |
+
|
| 67 |
+
## Out-of-distribution evaluation (MM-Food-100K, 500 wild food photos)
|
| 68 |
+
|
| 69 |
+
To measure generalization beyond Nutrition5k plating, both regression models were scored on a seeded
|
| 70 |
+
500-image sample of [MM-Food-100K](https://huggingface.co/datasets/Humanbased-AI/MM-Food-100K)
|
| 71 |
+
(restaurant/home photos; `food_prob ≥ 0.9`). **Caveat: these labels are crowd/AI estimates, not weighed
|
| 72 |
+
measurements** — absolute errors include label noise no model can beat, so the relative comparison is
|
| 73 |
+
the meaningful signal.
|
| 74 |
+
|
| 75 |
+
| | CalorieK | CalorieCLIP | |
|
| 76 |
+
|---|---|---|---|
|
| 77 |
+
| MAE | **164.9 kcal** | 206.4 kcal | 1.25× better |
|
| 78 |
+
| Median error | **139.1 kcal** | 179.9 kcal | 1.3× better |
|
| 79 |
+
| Within ±100 kcal | **36.0%** | 25.6% | |
|
| 80 |
+
| Bias | **−4.8 kcal** | +98.3 kcal | |
|
| 81 |
+
| R² | **+0.24** | −0.08 | |
|
| 82 |
+
|
| 83 |
+
Findings, stated honestly:
|
| 84 |
+
- CalorieK beats CalorieCLIP out-of-distribution on every metric, keeps **near-zero bias** on food it
|
| 85 |
+
has never seen, and retains real signal (R² +0.24) where CalorieCLIP is worse than predicting the
|
| 86 |
+
mean (R² −0.08).
|
| 87 |
+
- But CalorieK degrades ~3× versus in-distribution (51.4 → 164.9 kcal MAE): it is substantially a
|
| 88 |
+
specialist in Nutrition5k-style plated dishes. For wild-photo deployment, mixing diverse weakly
|
| 89 |
+
labeled data into training is the indicated next step.
|
| 90 |
|
| 91 |
## Training method
|
| 92 |
|
|
|
|
| 135 |
processor = AutoImageProcessor.from_pretrained(MODEL_ID)
|
| 136 |
vision = SiglipVisionModel.from_pretrained(MODEL_ID)
|
| 137 |
head = NutritionHead()
|
| 138 |
+
state = torch.load(hf_hub_download("Dralkh/CalorieK", "caloriek.pt"), map_location="cpu", weights_only=False)
|
| 139 |
vision.load_state_dict(state["vision_state"]); head.load_state_dict(state["head_state"])
|
| 140 |
vision.eval(); head.eval()
|
| 141 |
|