YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
SlowestLooser FoodJSON+Activity v2
HuggingFace: https://huggingface.co/Juhuu/slowestlooser-v2-4bit Published: 2026-05-13 Status: Active. Supersedes v1 (food-only).
What this model does
QLoRA fine-tune of mlx-community/Qwen3-1.7B-4bit that handles both the SlowestLooser
food-text path AND the activity-text path. iOS routes by system prompt; the model emits
the matching JSON schema.
- Food queries (system:
foodAnalysisSystemNoTools): CompositeDTO JSON withitems[] - Activity queries (system:
activityAnalysisSystemwith embedded 40-entry MET catalog): Single object{"name", "minutes", "calories"}(multi-activity sums into one row)
Base model
mlx-community/Qwen3-1.7B-4bit (1.72B params, pre-quantized 4-bit MXFP)
Training (QLoRA via mlx-lm on M4 Pro)
| Setting | Value |
|---|---|
| Method | QLoRA |
| Optimizer | AdamW |
| Iterations | 1500 (vs v1's 1000) |
| Batch size | 4 |
| Learning rate | 2e-4 |
| LoRA rank | 32 |
| LoRA target layers | last 16 attention layers |
| Max seq length | 2048 |
| Trainable params | 4.98M (0.29% of base) |
| Wall time | 135 min |
| Peak memory | 16.4 GB |
Loss trajectory
| Iter | Val loss | Train loss |
|---|---|---|
| 1 | 2.892 | β |
| 500 | 0.068 | 0.073 |
| 1000 | 0.061 | 0.056 |
| 1500 | 0.046 | 0.047 |
Val loss still improving at iter 1500 β extending from v1's 1000 was worth it. v1 plateaued at val=0.053 (iter 900).
Dataset
4802 records (4321 train + 481 holdout).
| Slice | Records | Share |
|---|---|---|
| Food | 3644 | 84% |
| Activity | 677 | 16% |
Food dataset (unchanged from v1):
- 38 curated DB entries oversampled to 20 variants each
- 400 simple-label OFF entries Γ 4 variants
- 30% multi-ingredient queries; 60% include β₯1 curated seed
Activity dataset (new in v2):
- 40-entry curated MET catalog (daily life β endurance sports; broad β specific)
- 15 variants per activity via GPT-4o-mini synthesis (durations, intensity modifiers, Swiss German + English synonyms)
- 10% multi-activity composites
- Calorie targets computed deterministically:
MET Γ 70kg Γ hours
See data/activity_catalog.json for the full 40-entry MET catalog used in both training data and the iOS system prompt.
Eval: Matrix sweep against iOS QualitySpec (real production prompts)
Real prompts mirrored from SlowestLooser/Services/QualitySpecs.swift::QualitySpec.all.
Food (39 prompts)
| Base Qwen3 1.7B | v1 FT | v2 FT | |
|---|---|---|---|
| Valid JSON | 1/39 (3%) | 39/39 (100%) | 39/39 (100%) |
| Avg latency | 3.10s | 1.69s | ~2.4s |
Activity (19 prompts; one empty-string prompt skipped)
| v2 FT | |
|---|---|
| Valid JSON | 19/19 (100%) |
| Avg latency | ~0.7s (no DB prefetch needed) |
Single-activity calorie sanity
The v2 model emits deterministic, MET-formula-correct calories:
| Prompt | Output | Expected | Status |
|---|---|---|---|
| Joggen / 30 min Joggen | 30min / 245kcal | 7.0 Γ 70 Γ 0.5 = 245 | exact |
| Velofahren | 30min / 210kcal | 6.0 Γ 70 Γ 0.5 = 210 | exact |
| Schwimmen | 30min / 280kcal | 8.0 Γ 70 Γ 0.5 = 280 | exact |
| HIIT | 30min / 350kcal | 10.0 Γ 70 Γ 0.5 = 350 | exact |
| Sprinten | 30min / 420kcal | 12.0 Γ 70 Γ 0.5 = 420 | exact |
| 1 Stunde Velo | 60min / 420kcal | 6.0 Γ 70 Γ 1.0 = 420 | exact |
| 45 Minuten Schwimmen | 45min / 385kcal | 8.0 Γ 70 Γ 0.75 = 420 | ~ 9% under |
| 2h Wandern | 120min / 700kcal | 5.5 Γ 70 Γ 2 = 770 | ~ 9% under |
| Spazieren | 30min / 122kcal | 3.5 Γ 70 Γ 0.5 = 122.5 | exact |
Multi-activity calorie sums
Less precise β model interpolates between known activities:
| Prompt | v2 Output | Catalog-strict sum |
|---|---|---|
| Joggen und Schwimmen | 60min / 595kcal | 245 + 280 = 525 |
| 30 min Velo dann 15 min Joggen | 60min / 560kcal | 210 + 122.5 = 332.5 |
Acceptable for ship; could tighten in v3 with more multi-activity training samples.
Drift comparison (the v1 production motivation)
Activity calorie variance for "30 min Joggen" in v1.30.x production releases:
| Release | Output | Source |
|---|---|---|
| v1.30.1 | 168 | Base Qwen |
| v1.30.3 | 147 | OpenAI (consistent across runs) |
| v1.30.4 | 140 | Base Qwen |
| v1.30.5 | 110 | Base Qwen β drift continued |
| v2 FT | 245 | Deterministic |
v2 emits the canonical MET-formula answer (245) on every run.
Known issues
g_wasserfiltering (carried over from v1) β water entry with 0 kcal gets filtered byload_vector_index. Query "Wasser" matches "Birken Wasser" from BM25. Relax tocals < 0in v3.dlunit not parsed (carried over from v1) β1.5dl Milchembeds quantity in name. Add dl/cl to QuantityParser.- Multi-activity calorie sum is approximate β see table above. ~12% over on tested cases. Acceptable for ship but could tighten with more multi-activity training data.
2h Wandernslight under-estimate β model used MET=5 instead of catalog 5.5 (700 vs 770 kcal).
Notes on design
- Design B chosen for activity MET routing: 40-entry catalog embedded in system prompt (compact
Name=METcomma-separated, ~250 tokens). Avoided full RAG (Design C) β Swift-side activity matcher would have added infrastructure for a 40-entry static dataset. - MET catalog Python β Swift byte-equal:
prompt.py::SYSTEM_ACTIVITYandPromptTemplates.swift::activityAnalysisSystemare intentionally byte-equal. Training prompt = production prompt = no drift. - One combined model (vs two separate adapters): saves a download + a model load on iOS. The 1.7B has capacity for both schemas.
Commits
- Activity training + model card scaffolding:
b4ee122 - 40-entry catalog + Design B compact MET prompt:
8011d10(Python) +a6a4eef(iOS) - v2 dataset + training: (commit hash after this docs commit)
Why retired v1
v1 was food-only. Users were typing "30 min joggen" and getting drifting calorie values (168/140/110 across releases). v2 trains one combined model on both schemas with deterministic calorie math baked in.
- Downloads last month
- 27