Eval write-up: full run report, r16-vs-r32 head-to-head, overnight reproducibility check — markdown only
Documentation: how the run was evaluated and how it compares to the rank-16 sibling
The previously-published sibling repos Maelstrome/lora-wave-session-r32-{gguf,merged,onnx,onnx-fused,report,mediapipe} have all been consolidated into this repo and deleted. The current gguf/ subdir is a fresh build from a PEFT re-merge (the original unsloth-merged base produced corrupt all-<pad> output and was never trustworthy). Any external link to the old sibling URLs will 404 — update to the appropriate subdir of this repo.
Sibling runs
This is the rank-32 / 1-epoch A100 training of the WAVE corpus. The rank-16 / 3-epoch RTX 5080 sibling lives at Maelstrome/lora-wave-session (same subdir layout: adapter at root, gguf/ subdir). On the same frozen 428-row test split, this rank-32 run is measurably stronger on every probability metric:
Trained for the WAVE app, a wellness/reflection tool — not a medical device, not clinical decision support, not a substitute for professional advice. Use under the Gemma Terms of Use.
Try it
🌊 Interactive demo:Maelstrome/lora-wave-session-demo — Gradio Space with surface-specific example prompts (backed by the rank-16 sibling; weights swap is a one-line config change).
The Q4_K_M is split into 5 ≤512 MB shards; llama.cpp and wllama both
auto-discover shards 2–5 from the first. Single-file Ollama / LM Studio
imports work the same way.
See mediapipe/README.md for full Android/iOS
instructions and the sample WAVE prompt/output JSONs that ship next to the
.litertlm for sanity-checking your wiring.
Example prompts
The model expects a system prompt establishing it as WAVE, plus a per-surface user prompt with <surface>, <patient_context>, and <task> blocks. Output is strict JSON.
phase_narration (six-line meditation)
<surface>phase_narration</surface>
<chunk>Number 5 of 5 - Close. Purpose: invite comparison to the start, normalize any outcome, and prepare for a final check-in.</chunk>
<patient_context>{"chunkNumber":5,"matType":"none","medicationStatus":"none","startingIntensityBand":"1-6","trigger":"unknown","usedSubstanceToday":false}</patient_context>
<task>Generate exactly 6 patient-facing narration lines. Return only strict JSON. Schema: {"lines":["...", ...]}</task>
Expected output (use max_new_tokens ≥ 384):
{"lines":["You've made it to the end of this practice.","Check in with your urge now — has anything shifted?","...","...","...","..."]}
reflection (post-session card)
<surface>reflection</surface>
<patient_context>{"durationSeconds":780,"endingIntensity":2,"intakeIntensity":7,"matType":"buprenorphine","medicationStatus":"on_time","sessionsCount":12,"trigger":"stress","usedSubstanceToday":false}</patient_context>
<task>Write the post-session reflection card. Return only strict JSON. Schema: {"insight":"...","journalPromptQuestion":"...","nextSteps":{"a":"...","b":"...","c":"...","d":"..."}}</task>
check_in (multi-turn)
<surface>check_in</surface>
<specialized_surface>lora-check-in-1</specialized_surface>
<patient_context>{"intakeIntensity":7,"matType":"buprenorphine","trigger":"stress"}</patient_context>
<task>Open turn 1: ask the patient to rate their current urge intensity 1-10. Schema: {"reply":"...","endConversation":null}</task>
Training
Base
unsloth/gemma-4-E2B-it
Method
QLoRA (4-bit) via Unsloth FastModel
Adapter rank / alpha / dropout
32 / 32 / 0
Target modules
All language + attention + MLP layers (vision/audio frozen)
Trainable parameters
25.3 M
Optimizer
adamw_8bit
LR
2e-4, cosine schedule
Warmup
21 steps (~3%)
Weight decay
0.001
Max grad norm
0.3
Batch / grad-accum
1 / 8 (effective 8)
Max sequence length
4096 (preflight max = 2,227, no truncation)
Epochs
1 (428 steps)
Chat template
gemma-4 (non-thinking, leading <bos> stripped)
Response masking
train_on_responses_only (Gemma 4 markers)
Hardware
NVIDIA A100 80 GB SXM4 (Thunder Compute)
Backend
Unsloth 2026.5.2 + Torch 2.11.0 + CUDA 13.0
Final training loss: 0.241. Wall clock: ~2h 26m train + ~1h 15m eval.
Evaluation
Held-out completion eval (n=428, full test split)
Metric
Base Gemma 4 E2B
This adapter
Delta
Completion NLL
4.9312
4.5576
−0.374
Completion perplexity
138.55
95.35
−43.20
Paired wins (LoRA assigned higher prob to reference)
—
428 / 428 (100%)
—
Mean per-example NLL Δ
—
0.508 nats
95% bootstrap CI [0.477, 0.537]
Median per-example NLL Δ
—
0.454 nats
—
Sign-test p-value
—
2.89 × 10⁻¹²⁹
overwhelming
Surface coverage on test split: check_in 144, phase_narration 147, reflection 137.
Generation eval (n=60 balanced, LoRA-only, 4bit)
Gate
All 60
check_in (20)
phase_narration (20)
reflection (20)
Style pass
100%
100%
100%
100%
Medical-directive pass
100%
—
—
—
No-markdown / no-analysis-voice
100%
—
—
—
JSON validity (160-tok cap)
75%
100%
25%
100%
JSON validity (384-tok cap on phase)
~95%
100%
85%
100%
Schema pass (384-tok cap on phase)
~90%
90%
80%
100%
Known quirks
Phase narration needs max_new_tokens ≥ 384 — the original 160-token cap truncated the JSON close on most phase prompts. check_in is fine at 96; reflection at 192.
Residual phase JSON-close defect. After raising the phase budget to 384 tokens, 4/20 phase examples still emit "} (missing ]) instead of "]}. A reproducibility re-run on those 4 IDs produced byte-identical outputs (8/8 matched the originals exactly), confirming this is a deterministic learned defect on a small subset of phase prompts — not sampling noise. Recommended fix at inference time: a deterministic JSON-repair pass that detects an unclosed lines array and inserts the missing ]. See Maelstrome/lora-wave-session-r32-report for the full diagnosis and patch.