CoolFace
Modelpublic

NateRunsA-LIST/alist-food-text-qwen35

sourceHugging Faceapache-2.0updated 13d agoView on Hugging Face
0likes7.5kdownloads
Model Card

A-LIST food-text extraction — Qwen3.5-2B + LoRA (GGUF)

On-device food-diary nutrition extraction for the A-LIST iOS app (Locomo mode). Runs in llama.cpp (qwen35 arch — needs b10369 or newer).

Files

filewhatsha256
Qwen3.5-2B-Q4_K_M.ggufBase model, Q4KM quant (mirror of unsloth/Qwen3.5-2B-GGUF, byte-identical)aaf42c8b7c3cab2bf3d69c355048d4a0ee9973d48f16c731c0520ee914699223
alist-food-lora-v2-f16.ggufFood-text extraction LoRA (r=64, alpha=64, f16)3bd0c6625857bcbaa0c13223e2c177654c798db232dd3257baba924e5e4d2ec3

Adapter lineage

  • —Dataset: food-text-unified:v1 (66.5k scrubbed food-diary rows, teacher food_text_v1)
  • —Recipe: qlora_r64_sft(3ep), converted from PEFT safetensors with llama.cpp convert_lora_to_gguf.py (--outtype f16)
  • —Eval (1,500-row frozen eval, left-pad, production post-processing): calories 0.761 · protein 0.856 · carbs 0.775 · fat 0.769 · fiber 0.939 · sugar 0.856 · sat-fat 0.873 · water 0.903 · GI±10 0.905 · micros mean 0.862 · parse_fail 0/1500
  • —Ladder on the same eval: LFM2.5-shipped 0.397 → MiniLM encoder e2e 0.591 → LoRA r32 0.701 → this adapter 0.761 (teacher ceiling 0.976)

Prompt contract (SFT student inference contract)

The adapter is conditioned on this exact raw-text prompt — no chat template, no `<think>` block (the long food_text_v1 teacher prompt labeled the corpus but is NOT the inference prompt):

Extract nutrition from this food diary entry. Estimate typical portions when unstated; copy user-stated numbers verbatim and list those fields in user_stated_fields. Output only JSON.
Entry: {entry}
###JSON

It replies with a single JSON object: macros + sugar/saturatedfat, `waterml, glycemicindex`, 15 `micronutrients`, `userstatedfields`, `mealtitle`. Consumers should apply the dataset's post-processing (stated-calories reconcile, sugar/sat-fat subset invariant, calories derived 4/4/9 when unstated, GL derivation) — the eval numbers above are measured on post-processed outputs.

Both the base model and the fine-tune are Apache 2.0.

mmproj Q8_0 (2026-08-24)

mmproj-Qwen3.5-2B-Q8_0.gguf (365 MB, sha256 5eda1f66…) is the vision tower requantized to Q80 from the original Qwen3.5-2B weights — current for iOS (`qwenmmprojv2q8). On the frozen 215-product supplement eval every gated structured field holds within 1 point of the F16 numbers (micros improve, .862 vs .848). mmproj-Qwen3.5-2B-F16.gguf` stays for rollback.

v3pos — positional output contract (2026-09-12)

filewhatsha256
alist-food-lora-v3pos-f16.ggufFood-text extraction LoRA, positional output contract (r=64, alpha=64, f16, 87,307,648 bytes)79b3edaad9f9ad49d3e0807aa95f54dc0aa48e9dbfa044444eee53b80bab1ee6

Same prompt as v2, byte for byte. The reply is {"n":[24 numbers],"s":[stated field names],"t":"meal title"} with n in this order: calories, protein, carbohydrates, fat, fiber, sugar, saturatedfat, waterml, glycemicindex, vitaminA, vitaminC, vitaminD, vitaminE, vitaminK, vitaminB6, vitaminB12, folate, calcium, iron, magnesium, zinc, potassium, selenium, omega3 (integers when integral, else ≤2 decimals). Consumers expand `n` back to the keyed fields (`lab/teacher/foodtextstudentv3pos.py` in ProtocolsBackend) and apply the same post-processing as v2. ~98 output tokens vs ~222 ⇒ 5.9 s vs 9.8 s per entry on an iPhone 16.

  • —Recipe: lora_r64_bf16(2ep, lr 2e-4, bs 4x8) on the augmented train set (119,718 rows: v1 + 55,457 paraphrases + 4,995 hardcases); converted with llama.cpp convert_lora_to_gguf.py --outtype f16.
  • —Eval (bf16, 1,500 frozen rows): calories 0.7587 · carbs 0.7527 · protein 0.8393 · micros 0.8644 · parse_fail 1/1500.
  • —Production Q4 artifact (llama.cpp, 1,000 reference rows): calories 0.739 · carbs 0.738 · micros 0.840 · parse_fail 0 — parity with the v2 artifact (0.748 / 0.735 / 0.821).
  • —alist-food-lora-v2-f16.gguf stays: A-LIST 2.51 and 2.52 pin it by sha256. Do not modify or remove it.

Medication-label text adapter v1 (2026-09-13)

filewhatsha256
alist-med-label-lora-v1-f16.ggufMedication-label OCR-text extraction LoRA (r=64, alpha=128, f16, 87,307,264 bytes)0b345bebf0db54a19680d63333b903d324c434fbb7b0d37f30ba389eda0f6eaf

The OCR rung for the Medications protocol in signed-out mode: Apple Vision OCR text of a pharmacy sticker, compounding vial, Drug Facts panel, stock bottle or Supplement Facts panel in, one structured medication record out. Trained on medication-label-text:v1 (12k synthetic labels with exact targets, no patient data; checkpoint 600 of a 1-epoch mlx-lm run, picked on val loss). Same base, same target modules as the other adapters; never stack with the food or supplement adapter on one context.

Eval on the shipped stack (Q4KM base + this adapter, 1,827 rows whose drugs never appear in training): parsefail 0 · kind .999 · rxonly 1.000 · servingdesc .993 · frequencyunit .990 · dosesperday .989 · dosesperweek .999 · servingstrength .973 · strengthunit .951 · name .951 · all core fields .879. Zero-shot base: .000.

Prompt contract (medication_label_student_v1): Qwen3.5 chat template with thinking disabled, ONE user turn = instruction + OCR text (≤2000 chars), no prefill, greedy, stop <|im_end|>, max 400 tokens, cut the first balanced JSON object:

<|im_start|>user
Extract the medication or supplement from this label OCR text as one JSON object. Read amounts exactly as printed; null means not printed. Output only JSON.

{ocr_text}<|im_end|>
<|im_start|>assistant
<think>

</think>

Output keys in order: kind, name, brand, rxonly, form, route, servingdesc, servingstrength, strengthunit, dosesperday, dosesperweek, frequencyunit, ingredients[{name, amount, unit}]. Cadence is the canonical trio (`asdirected → doses_per_day 0; everyotherday` → 0.5; weekly → dosesperweek).