Views
No views yet
unsloth/gemma-4-e4b-it that converts colloquial patient
diary text into a structured JSON list of medical entities with canonical
names suitable for HPO (Human Phenotype Ontology) lookup. Trained on
411 distilled (diary, target_json) pairs derived from Orphanet rare-disease
phenotypes and HPO synonyms.diary text → [LoRA-adapted Gemma 4 E4B, 4-bit] → JSON entities
→ deterministic HPO synonym lookup → HPO IDs + canonical namesunsloth/gemma-4-e4b-it in 4-bit without the adapter, apples-to-apples,
same system prompt.| Metric | Baseline | Fine-tuned | Δ |
|---|---|---|---|
| JSON schema correctness | 0%¹ | 100% | +100 pp |
| Entity type-field accuracy | 89.1% | 100% | +10.9 pp |
| Name F1 (synonym-aware) | 0.209 | 0.524 | +151% |
| HPO ID F1 (via name→HPO lookup) | 0.349 | 0.524 | +50% |
| Avg entities per example (gold = 2.93) | 5.37 (over-extract) | 2.91 (calibrated) | — |
{symptoms, triggers, body_parts, medications, lab_values}); the baseline
row above is computed after normalising those outputs to the requested
schema. Without normalisation, every baseline score would be 0.1import json
2from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
3from peft import PeftModel
4
5BASE = "unsloth/gemma-4-e4b-it"
6ADAPTER = "m0rtyddd/clario-gemma4-e4b-lora-v2"
7
8bnb = BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_compute_dtype="bfloat16")
9tok = AutoTokenizer.from_pretrained(ADAPTER)
10base = AutoModelForCausalLM.from_pretrained(BASE, quantization_config=bnb, device_map="auto")
11model = PeftModel.from_pretrained(base, ADAPTER)
12model.eval()
13
14SYSTEM = (
15 "Extract medical entities from the diary. Return strict JSON: "
16 '{"entities":[{"name_colloquial":"…","name_canonical":"…",'
17 '"hpo_id":"HP:…","type":"symptom|lab_marker|med|trigger|behavior"}]}. '
18 "Use canonical HPO names where possible. Output JSON only."
19)
20diary = (
21 "My eyes have been gritty, like there's sand in them. Mouth's been so "
22 "dry I can't swallow toast without water. Fingers ache when I type for long."
23)
24
25prompt = tok.apply_chat_template(
26 [{"role": "system", "content": SYSTEM}, {"role": "user", "content": diary}],
27 tokenize=False, add_generation_prompt=True,
28)
29ids = tok(prompt, return_tensors="pt").to(model.device)
30out = model.generate(**ids, max_new_tokens=512, do_sample=False)
31response = tok.decode(out[0][ids["input_ids"].shape[1]:], skip_special_tokens=True)
32print(json.loads(response))
33# {"entities": [
34# {"name_colloquial": "gritty eyes", "name_canonical": "Keratoconjunctivitis sicca", ...},
35# {"name_colloquial": "mouth so dry", "name_canonical": "Xerostomia", ...},
36# {"name_colloquial": "fingers ache", "name_canonical": "Arthralgia", ...},
37# ]}hpo_id field and resolve from
name_canonical via a synonym index built from HPO hp.obo
(42k normalised name → HP:ID entries). See Limitations §1.scripts/clario_extractor_service.py.backend/diary/extraction.py::process_one calls the
sidecar when CLARIO_EXTRACTOR_URL is set, otherwise falls back to
vanilla Gemma via Ollama.| Base | unsloth/gemma-4-e4b-it (4-bit NF4, BF16 compute) |
| Adapter | LoRA, r=16, α=32, dropout=0.05 |
| Target modules | q/k/v/o/gate/up/down_proj on all language-model layers |
| Optimiser | adamw_8bit |
| Learning rate | 5e-5 |
| Epochs | 1 (on top of a resumed checkpoint-50 from the v1 run) |
| Max sequence length | 1536 |
| Total steps | 22 SGD steps over 343 train examples |
| Train loss (final) | 0.478 |
| Hardware | single RTX 5060 Ti 16 GB (Blackwell sm_120) |
| Wall time | ~22 min |
paged_adamw_8bit and Windows NVIDIA driver 596.36. The resumed
run with adamw_8bit completed cleanly.m0rtyddd/clario-synthetic-diary.entities[].hpo_id from the model (see §1 below).HP:0001211, HP:0001212, HP:0001213, …). The +50% relative HPO F1 over
baseline measures how the better name extraction improves end-to-end ID
resolution once a deterministic lookup is applied. Consumers must
discard the model's hpo_id field.gpt-oss:20b teacher pipeline.
651 Orphanet disorders never appear in train, so the split is non-trivially
novel — but the val set is not a clinical golden. A manual 200-pair
golden set is the next measurement gate and was not completed within the
hackathon window.en_product4.xml, 2026-05-13 snapshot) — free for academic and
commercial use with attribution.
Orphadata: Free access products on rare diseases and orphan drugs.
INSERM 1978. https://www.orphadata.com/unsloth/gemma-4-e4b-it, used under the Gemma Terms of
Use.gpt-oss:20b (Apache-2.0).1@misc{okulov2026clario_lora_v2,
2 title = {{Clario} {Gemma 4 E4B} Symptom-Diary {LoRA} (v2)},
3 author = {Okulov, Maksim},
4 year = {2026},
5 howpublished = {\url{https://huggingface.co/m0rtyddd/clario-gemma4-e4b-lora-v2}}
6}peft 0.19.1 · trl 1.4.0 · transformers 5.8.0.dev0 ·
torch 2.11.0.dev20260108+cu128 · datasets 4.8.5 · tokenizers 0.22.2 ·
unsloth (QLoRA path) · bitsandbytes (4-bit NF4)