Text Generation
PEFT
Safetensors
GGUF
English
gemma
gemma-4
lora
unsloth
clinical
wellness
structured-output
json
sft
trl
conversational
Instructions to use Maelstrome/lora-wave-session-r32 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use Maelstrome/lora-wave-session-r32 with PEFT:
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("unsloth/gemma-4-e2b-it-unsloth-bnb-4bit") model = PeftModel.from_pretrained(base_model, "Maelstrome/lora-wave-session-r32") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use Maelstrome/lora-wave-session-r32 with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf Maelstrome/lora-wave-session-r32:Q4_K_M # Run inference directly in the terminal: llama cli -hf Maelstrome/lora-wave-session-r32:Q4_K_M
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf Maelstrome/lora-wave-session-r32:Q4_K_M # Run inference directly in the terminal: llama cli -hf Maelstrome/lora-wave-session-r32:Q4_K_M
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf Maelstrome/lora-wave-session-r32:Q4_K_M # Run inference directly in the terminal: ./llama-cli -hf Maelstrome/lora-wave-session-r32:Q4_K_M
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf Maelstrome/lora-wave-session-r32:Q4_K_M # Run inference directly in the terminal: ./build/bin/llama-cli -hf Maelstrome/lora-wave-session-r32:Q4_K_M
Use Docker
docker model run hf.co/Maelstrome/lora-wave-session-r32:Q4_K_M
- LM Studio
- Jan
- vLLM
How to use Maelstrome/lora-wave-session-r32 with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "Maelstrome/lora-wave-session-r32" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Maelstrome/lora-wave-session-r32", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/Maelstrome/lora-wave-session-r32:Q4_K_M
- Ollama
How to use Maelstrome/lora-wave-session-r32 with Ollama:
ollama run hf.co/Maelstrome/lora-wave-session-r32:Q4_K_M
- Unsloth Studio
How to use Maelstrome/lora-wave-session-r32 with Unsloth Studio:
Install Unsloth Studio (macOS, Linux, WSL)
curl -fsSL https://unsloth.ai/install.sh | sh # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for Maelstrome/lora-wave-session-r32 to start chatting
Install Unsloth Studio (Windows)
irm https://unsloth.ai/install.ps1 | iex # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for Maelstrome/lora-wave-session-r32 to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for Maelstrome/lora-wave-session-r32 to start chatting
- Docker Model Runner
How to use Maelstrome/lora-wave-session-r32 with Docker Model Runner:
docker model run hf.co/Maelstrome/lora-wave-session-r32:Q4_K_M
- Lemonade
How to use Maelstrome/lora-wave-session-r32 with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull Maelstrome/lora-wave-session-r32:Q4_K_M
Run and chat with the model
lemonade run user.lora-wave-session-r32-Q4_K_M
List all available models
lemonade list
- Atomic Chat
Upload folder using huggingface_hub
Browse files- report/.gitattributes +35 -0
- report/COMPARISON.md +75 -0
- report/MORNING_REPORT.md +116 -0
- report/README.md +51 -0
- report/REPORT.md +451 -0
report/.gitattributes
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
report/COMPARISON.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# `local_final` vs `remote_final` β head-to-head
|
| 2 |
+
|
| 3 |
+
Two WAVE-session LoRA fine-tunes of `unsloth/gemma-4-E2B-it`, trained against the **same** source dataset (`models/datasets/lora-wave-session-expanded.jsonl`) with the **same** stratification seed (`7`). Test-split `example_id`s are byte-identical (428 examples, same 144/147/137 surface counts; the only file-level diff is CRLF vs LF in the JSONLs).
|
| 4 |
+
|
| 5 |
+
The two runs differ in hardware and a few hyperparameters. This file is the head-to-head; for the full per-run details see:
|
| 6 |
+
|
| 7 |
+
- [`local_final/REPORT.md`](./local_final/REPORT.md) β RTX 5080, r=16, 3 epochs
|
| 8 |
+
- [`remote_final/REPORT.md`](./remote_final/REPORT.md) β A100 80 GB, r=32, 1 epoch (Β§10 of that doc is the source of this file)
|
| 9 |
+
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
## Recipes
|
| 13 |
+
|
| 14 |
+
| | `local_final` | `remote_final` |
|
| 15 |
+
|---|---|---|
|
| 16 |
+
| Hardware | RTX 5080 (Windows, local) | A100 80 GB SXM4 (Linux, Thunder Compute) |
|
| 17 |
+
| LoRA rank | 16 | **32** |
|
| 18 |
+
| LoRA alpha | 32 | 32 |
|
| 19 |
+
| Epochs | **3** (1,152 steps) | 1 (428 steps) |
|
| 20 |
+
| Warmup | 64 steps | 21 steps |
|
| 21 |
+
| LR / schedule / batch / accum / wd / grad-norm | same: 2e-4 cosine, 1Γ8, 0.001, 0.3 | same |
|
| 22 |
+
|
| 23 |
+
---
|
| 24 |
+
|
| 25 |
+
## Quantitative β full 428-row held-out test split
|
| 26 |
+
|
| 27 |
+
| | `local_final` | `remote_final` |
|
| 28 |
+
|---|---|---|
|
| 29 |
+
| LoRA completion NLL | 4.7149 | **4.5576** |
|
| 30 |
+
| LoRA perplexity | 111.59 | **95.35** |
|
| 31 |
+
| Paired wins vs base | 386 / 428 (90.2%) | **428 / 428 (100%)** |
|
| 32 |
+
| Mean NLL Ξ vs base | 0.327 nats | **0.508 nats** |
|
| 33 |
+
| Sign-test p-value | 9.5 Γ 10β»β·ΒΉ | **2.9 Γ 10β»ΒΉΒ²βΉ** |
|
| 34 |
+
|
| 35 |
+
**`remote_final` is stronger on every probability metric.** Higher rank captured the WAVE distribution more decisively in fewer steps; the effect size is 55% larger and the win rate is perfect (no test prompt was worse than base) vs ~10% losses for the local run.
|
| 36 |
+
|
| 37 |
+
---
|
| 38 |
+
|
| 39 |
+
## Generation eval β apples-to-apples on the 6 overlapping `example_id`s
|
| 40 |
+
|
| 41 |
+
`local_final` only ran an 8-example smoke; `remote_final` ran 60. Overlap = 6.
|
| 42 |
+
|
| 43 |
+
| ID | Surface | Local | Remote | Notes |
|
| 44 |
+
|---|---|---|---|---|
|
| 45 |
+
| `15f8662e` | check_in | β json+schema+safety, F1=1.000 | β json+schema+safety, F1=1.000 | **Identical** reply to reference β only whitespace differs |
|
| 46 |
+
| `2bf8ec4f` | check_in | β F1=1.000 | β F1=1.000 | Both identical to reference |
|
| 47 |
+
| `2510fd56` | phase | β F1=0.483 | β F1=**0.512** | Remote slightly closer to reference |
|
| 48 |
+
| `30b744c4` | phase | β json+schema+safety, **183 tok** | β json fail, 160 tok (**truncated by cap**) | Local won by having a bigger token budget at gen time, not a model difference |
|
| 49 |
+
| `afedb9b7` | reflection | β F1=**0.429** | β F1=0.355 | Local slightly closer to reference |
|
| 50 |
+
| `ec718a0e` | reflection | β F1=0.423 | β F1=0.416, ROUGE-L=**0.315** | Tie on F1; remote slightly better ROUGE-L |
|
| 51 |
+
|
| 52 |
+
Pass rates on overlap: local **6/6**, remote **5/6** (the one miss is a 160-token cap artifact β when we raised the cap to 384 for the phase rerun, remote cleared most of the phase failures too).
|
| 53 |
+
|
| 54 |
+
---
|
| 55 |
+
|
| 56 |
+
## Verdict
|
| 57 |
+
|
| 58 |
+
- **Both models are clinically equivalent** on identical prompts. Word choice differs but pose, safety, structure, and JSON shape are the same.
|
| 59 |
+
- **`remote_final` is the better adapter for the WAVE distribution** β stronger NLL evidence, perfect paired-win rate, smaller p-value, and a more recent (post-`local_final`) eval/repair pipeline.
|
| 60 |
+
- Recommend shipping `remote_final/adapter/` (or its merged/GGUF derivatives) with `--phase-max-new-tokens β₯ 256` plus the JSON-repair post-process described in `remote_final/REPORT.md Β§9.2` to close the residual phase-close defect.
|
| 61 |
+
|
| 62 |
+
---
|
| 63 |
+
|
| 64 |
+
## Artifact layout (mirrors are byte-exact within each run)
|
| 65 |
+
|
| 66 |
+
| | `local_final/` | `remote_final/` |
|
| 67 |
+
|---|---|---|
|
| 68 |
+
| `adapter/adapter_model.safetensors` | 202,775,888 B (r=16) | 202,775,888 B (r=32, different content) |
|
| 69 |
+
| `merged-16bit/model.safetensors` | 10,246,621,886 B | 10,246,621,886 B |
|
| 70 |
+
| `gguf/gemma-4-e2b-it.Q4_K_M.gguf` | 3,427,878,240 B | 3,427,878,240 B |
|
| 71 |
+
| `gguf/gemma-4-e2b-it.BF16-mmproj.gguf` | 986,833,280 B | 986,833,280 B |
|
| 72 |
+
| `train.jsonl` / `validation.jsonl` / `test.jsonl` | same example_ids | same example_ids |
|
| 73 |
+
| `REPORT.md` | full run report | full run report |
|
| 74 |
+
|
| 75 |
+
(File **sizes** are identical for the merged/GGUF/mmproj because both are the same base + tokenizer at the same quantization; the **content** differs because the embedded adapter is different.)
|
report/MORNING_REPORT.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Overnight WAVE LoRA β Morning Report
|
| 2 |
+
|
| 3 |
+
**Run:** `runs/lora-wave-session/overnight-primary-r32-a32-lr2e-4-1epoch`
|
| 4 |
+
**Adapter:** `runs/.../adapter/adapter_model.safetensors` (~194 MB)
|
| 5 |
+
**Date:** 2026-05-11
|
| 6 |
+
|
| 7 |
+
## TL;DR
|
| 8 |
+
|
| 9 |
+
- Adapter trained successfully on full WAVE combined dataset (4,277 examples).
|
| 10 |
+
- **Completion eval (n=428 held-out):** LoRA NLL **4.558** vs base **4.931** β **31.98% paired delta**, **428/428 win rate**, sign test p β 2.9e-129. Strong, statistically significant improvement on every test example.
|
| 11 |
+
- **Generation eval (n=60 balanced, LoRA-only, 4bit):** JSON validity **75.0%**, schema **71.7%**, style **100%**, safety **73.3%**. Overall gate `pass=false`, but the failure mode is concentrated in one surface and one root cause.
|
| 12 |
+
- **Per surface:**
|
| 13 |
+
- **reflection:** 100% across every gate. Ship-ready.
|
| 14 |
+
- **check_in:** 100% JSON valid; 90% schema; 95% safety; 90% turn-sequence. Strong.
|
| 15 |
+
- **phase_narration:** **25%** JSON valid β cascade to schema/safety/6-line at 25%. **Root cause: outputs are being truncated at the 160-token cap before the JSON closes**, not poor clinical content. Style pass is still 100% on the truncated rows.
|
| 16 |
+
- Latency on A100 4bit: mean **52s/example**, p95 **78s**.
|
| 17 |
+
|
| 18 |
+
## Training Run
|
| 19 |
+
|
| 20 |
+
- Model: `unsloth/gemma-4-E2B-it`, 4-bit QLoRA via Unsloth `FastModel`
|
| 21 |
+
- Dataset: `models/datasets/lora-wave-session-expanded.jsonl` (4277 rows: 1534 check_in, 1553 phase_narration, 1190 reflection)
|
| 22 |
+
- Split (seed 7, stratified): 3421 train / 428 val / 428 test
|
| 23 |
+
- LoRA: r=32, alpha=32, dropout=0.0, all language + attention + MLP layers
|
| 24 |
+
- Epochs: 1.0 (428 steps), batch 1 Γ grad_accum 8 = 16 effective
|
| 25 |
+
- LR: 2e-4 cosine, warmup 21 steps, weight decay 0.001, max grad norm 0.3
|
| 26 |
+
- max_seq_length: 4096; preflight max-row tokens: 2227 (no truncation needed)
|
| 27 |
+
- Chat template: `gemma-4` (Unsloth); response-only training via `train_on_responses_only`
|
| 28 |
+
- Wall clock: ~2h 26m on A100 80GB SXM4
|
| 29 |
+
- Final `train_loss`: **0.241**
|
| 30 |
+
|
| 31 |
+
## Quantitative β Completion NLL (whole frozen test split, n=428)
|
| 32 |
+
|
| 33 |
+
| Metric | Base Gemma | LoRA | Delta |
|
| 34 |
+
|---|---|---|---|
|
| 35 |
+
| Completion NLL | 4.9312 | **4.5576** | **β0.374** |
|
| 36 |
+
| Perplexity | 138.55 | **95.35** | **β43.2** |
|
| 37 |
+
| Paired wins | β | **428 / 428** | **100%** |
|
| 38 |
+
| Mean NLL delta | β | **0.508** | 95% bootstrap CI [0.477, 0.537] |
|
| 39 |
+
| Sign-test p-value | β | **2.89e-129** | overwhelmingly significant |
|
| 40 |
+
|
| 41 |
+
Bookmark this table β it is the strongest claim: on the same frozen held-out prompts, the LoRA assigns higher probability to the reference WAVE-style JSON completion than base Gemma on **every** test example, with effect size **β 0.5 nats / example** and tight CI.
|
| 42 |
+
|
| 43 |
+
## Quality β Generation gates (LoRA-only, 60 examples, balanced 20/20/20, 4bit Unsloth inference)
|
| 44 |
+
|
| 45 |
+
Overall gate `pass=false` driven by phase_narration truncation only.
|
| 46 |
+
|
| 47 |
+
| Metric | All 60 | check_in (n=20) | phase_narration (n=20) | reflection (n=20) |
|
| 48 |
+
|---|---|---|---|---|
|
| 49 |
+
| JSON validity | 75.0% | **100%** | **25%** | **100%** |
|
| 50 |
+
| Schema pass | 71.7% | 90% | 25% | **100%** |
|
| 51 |
+
| Style pass | 100% | 100% | 100% | 100% |
|
| 52 |
+
| Safety pass | 73.3% | 95% | 25% | **100%** |
|
| 53 |
+
| Patient-facing | 85.0% | β | β | β |
|
| 54 |
+
| No analysis voice | 100% | β | β | β |
|
| 55 |
+
| No markdown | 100% | β | β | β |
|
| 56 |
+
| Medical directive | 100% | β | β | β |
|
| 57 |
+
| Phase 6-line | 75% | β | 25% | n/a |
|
| 58 |
+
| Reflection next-step | 100% | n/a | n/a | 100% |
|
| 59 |
+
| Check-in turn seq | 96.7% | 90% | n/a | n/a |
|
| 60 |
+
| Token F1 (vs ref) | 0.434 | 0.490 | 0.382 | 0.429 |
|
| 61 |
+
| ROUGE-L F1 | 0.303 | 0.456 | 0.171 | 0.282 |
|
| 62 |
+
| Mean latency | 52.0s | 32.3s | 70.7s | 53.2s |
|
| 63 |
+
| p95 latency | 77.8s | 51.6s | 118.8s | 66.0s |
|
| 64 |
+
| Mean gen tokens | 120 | 77 | **159** | 123 |
|
| 65 |
+
|
| 66 |
+
## Failure root cause
|
| 67 |
+
|
| 68 |
+
All 15 `phase_narration` JSON-invalid rows show the same pattern: the model writes well-formed clinical content but never reaches the closing `]}` because generation is stopped at the **160 token cap** (run flag `--phase-max-new-tokens=160`). Style pass is 100% on the truncated rows β the prose is fine, the wrapper just doesn't close. Almost every phase row used the full 160 tokens (mean = 159).
|
| 69 |
+
|
| 70 |
+
Example (truncation visible):
|
| 71 |
+
|
| 72 |
+
```
|
| 73 |
+
gen: {
|
| 74 |
+
"lines": [
|
| 75 |
+
"Let's bring our attention to sound right now. You don't need to find a perfect sound. Just notice what is present in this moment.",
|
| 76 |
+
"Maybe there is a steady hum from the refrigerator, a β cut off here
|
| 77 |
+
```
|
| 78 |
+
|
| 79 |
+
The two `check_in` schema failures are different: the LoRA wrote `endConversation: null` instead of the structured object the reference expects on session-end turns. Real-world rare; fixable with one more pass of synthetic end-of-session data.
|
| 80 |
+
|
| 81 |
+
The reflection surface is genuinely clean (100% across every gate, sensible 6-line / next-step structure).
|
| 82 |
+
|
| 83 |
+
## Files (on VM under `~/wave-work/Wave/models/runs/lora-wave-session/overnight-primary-r32-a32-lr2e-4-1epoch/`)
|
| 84 |
+
|
| 85 |
+
- `adapter/adapter_model.safetensors` β trained LoRA
|
| 86 |
+
- `adapter/adapter_config.json` β PEFT config
|
| 87 |
+
- `run-config.json` β full hyperparameters + dataset counts
|
| 88 |
+
- `train.jsonl`, `validation.jsonl`, `test.jsonl` β frozen splits (seed 7)
|
| 89 |
+
- `token-length-report.json` β preflight evidence (max=2227, no truncation)
|
| 90 |
+
- `validation-eval.json` β validation completion metrics
|
| 91 |
+
- `tuning-summary.json` β full per-candidate validation log
|
| 92 |
+
- `eval.json` β full base-vs-LoRA completion comparison (the headline numbers above)
|
| 93 |
+
- `generation-eval.json` β generation gate aggregate (this report's quality section)
|
| 94 |
+
- `generation-eval-progress.jsonl` β 60 per-example generated outputs + checks
|
| 95 |
+
- `MORNING_REPORT.md` β this file
|
| 96 |
+
- `README.md` β short auto summary written at training end
|
| 97 |
+
|
| 98 |
+
## What to show judges
|
| 99 |
+
|
| 100 |
+
1. **Headline:** "LoRA wins on 428/428 held-out prompts vs base Gemma 4 E2B, p β 1e-129, NLL β0.37."
|
| 101 |
+
2. **Quality:** reflection 100% / check_in ~90β100% on every behavior gate (JSON, schema, safety, style, turn sequence).
|
| 102 |
+
3. **Per-example artifact:** `generation-eval-progress.jsonl` has prompt β generated β reference β gate results for 60 outputs.
|
| 103 |
+
|
| 104 |
+
## Honest caveats
|
| 105 |
+
|
| 106 |
+
- Generation gate `pass=false` overall. Disclose this β drives quality engineering choices.
|
| 107 |
+
- Phase narration outputs are clinically good but **truncated** because of an inference-time token budget set conservatively; not a model failure. A rerun with `--phase-max-new-tokens=384` would almost certainly clear most of the phase_narration JSON failures. Cheap fix.
|
| 108 |
+
- Two check-in schema misses involve missing `endConversation` object on end-of-session turns.
|
| 109 |
+
- Base-vs-LoRA *generation* (not completion) comparison was skipped to save time; completion eval already proved the win. Could be added if judges ask.
|
| 110 |
+
- 4bit Unsloth inference is slow (mean 52s/example, p95 78s). For deployment to E2B on-device, expect much faster after export/optimization.
|
| 111 |
+
|
| 112 |
+
## Recommended next action
|
| 113 |
+
|
| 114 |
+
1. **Cheapest:** rerun generation eval on phase_narration only with `--phase-max-new-tokens=384` (β 20 min, costs little). Expected: phase_narration JSON validity / schema / 6-line jumps from 25% β 80β95%. Almost certainly enough to flip overall gate to `pass=true`.
|
| 115 |
+
2. **If retraining:** the conservative LR=2e-5 / 3-epoch fallback is still available, but the completion-side metrics already look strong; no obvious reason to redo training tonight.
|
| 116 |
+
3. **For deployment:** the adapter is in `adapter/`. Merge + export (GGUF / safetensors) when ready.
|
report/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: gemma
|
| 3 |
+
tags:
|
| 4 |
+
- gemma
|
| 5 |
+
- gemma-4
|
| 6 |
+
- lora
|
| 7 |
+
- report
|
| 8 |
+
- documentation
|
| 9 |
+
- clinical
|
| 10 |
+
- wellness
|
| 11 |
+
language:
|
| 12 |
+
- en
|
| 13 |
+
datasets:
|
| 14 |
+
- Maelstrome/lora-wave-session-dataset
|
| 15 |
+
---
|
| 16 |
+
|
| 17 |
+
# lora-wave-session-r32 β training report
|
| 18 |
+
|
| 19 |
+
Documentation-only repo. Contains the full training/eval write-up for the **rank-32 / 1-epoch A100** WAVE fine-tune of Gemma 4 E2B Instruct, plus the head-to-head comparison against its rank-16 sibling.
|
| 20 |
+
|
| 21 |
+
## Documents
|
| 22 |
+
|
| 23 |
+
| File | What's in it |
|
| 24 |
+
|---|---|
|
| 25 |
+
| [`REPORT.md`](./REPORT.md) | Full run report β dataset, training, quantitative eval (NLL/PPL/paired-wins/p-value), generation gates, phase-rerun + reproducibility check, artifact manifest, reproducibility commands, export commands, caveats, recommendations. ~28 KB / 451 lines. |
|
| 26 |
+
| [`COMPARISON.md`](./COMPARISON.md) | Head-to-head vs the rank-16 / 3-epoch sibling run (`lora-wave-session`). Same dataset, same seed, same test split. r32 wins on every probability metric. |
|
| 27 |
+
| [`MORNING_REPORT.md`](./MORNING_REPORT.md) | First-pass overnight summary written immediately after training completed. Preserved for history; superseded by `REPORT.md`. |
|
| 28 |
+
|
| 29 |
+
## Linked artifacts
|
| 30 |
+
|
| 31 |
+
- π¦₯ **Adapter:** [`Maelstrome/lora-wave-session-r32`](https://huggingface.co/Maelstrome/lora-wave-session-r32) β PEFT adapter (~194 MB)
|
| 32 |
+
- π» **Merged bf16:** [`Maelstrome/lora-wave-session-r32-merged`](https://huggingface.co/Maelstrome/lora-wave-session-r32-merged) β drop-in for `transformers`/vLLM (~10 GB)
|
| 33 |
+
- π§ **GGUF Q4_K_M:** [`Maelstrome/lora-wave-session-r32-gguf`](https://huggingface.co/Maelstrome/lora-wave-session-r32-gguf) β llama.cpp / Ollama / wllama (~4 GB)
|
| 34 |
+
- π **Dataset:** [`Maelstrome/lora-wave-session-dataset`](https://huggingface.co/datasets/Maelstrome/lora-wave-session-dataset) β 4,277 examples, frozen splits (seed `7`)
|
| 35 |
+
- π **Sibling run (rank-16):** [`Maelstrome/lora-wave-session`](https://huggingface.co/Maelstrome/lora-wave-session) β same dataset, different recipe
|
| 36 |
+
|
| 37 |
+
## Headline numbers
|
| 38 |
+
|
| 39 |
+
On the same frozen 428-row held-out test split:
|
| 40 |
+
|
| 41 |
+
| | rank-16 sibling | **rank-32 (this report)** |
|
| 42 |
+
|---|---|---|
|
| 43 |
+
| LoRA completion NLL | 4.7149 | **4.5576** |
|
| 44 |
+
| LoRA perplexity | 111.59 | **95.35** |
|
| 45 |
+
| Paired wins vs base | 386 / 428 (90.2%) | **428 / 428 (100%)** |
|
| 46 |
+
| Mean NLL Ξ vs base | 0.327 nats | **0.508 nats** |
|
| 47 |
+
| Sign-test p-value | 9.5 Γ 10β»β·ΒΉ | **2.9 Γ 10β»ΒΉΒ²βΉ** |
|
| 48 |
+
|
| 49 |
+
## License
|
| 50 |
+
|
| 51 |
+
Gemma Terms of Use. See [https://ai.google.dev/gemma/terms](https://ai.google.dev/gemma/terms).
|
report/REPORT.md
ADDED
|
@@ -0,0 +1,451 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# WAVE LoRA β Remote A100 Final Run Report
|
| 2 |
+
|
| 3 |
+
**Run ID:** `overnight-primary-r32-a32-lr2e-4-1epoch`
|
| 4 |
+
**Hardware:** Thunder Compute, NVIDIA A100-SXM4-80GB, Ubuntu 22.04, 8 vCPU, 64 GB RAM
|
| 5 |
+
**Date trained:** 2026-05-11 (β 2h 26m wall clock)
|
| 6 |
+
**Date evaluated:** 2026-05-11 (completion eval + 60-sample generation eval + phase-rerun)
|
| 7 |
+
**Adapter:** `runs/lora-wave-session/overnight-primary-r32-a32-lr2e-4-1epoch/adapter/adapter_model.safetensors` (~194 MB, 25.3 M trainable params)
|
| 8 |
+
|
| 9 |
+
---
|
| 10 |
+
|
| 11 |
+
## 0. TL;DR
|
| 12 |
+
|
| 13 |
+
- **Completion eval on the full frozen test split (n=428):** LoRA beats base Gemma 4 E2B on **428/428** examples, mean NLL Ξ **+0.508 nats** (perplexity 138.5 β 95.4), sign-test p β **2.9 Γ 10β»ΒΉΒ²βΉ**.
|
| 14 |
+
- **Generation quality (n=60 balanced):** style 100%, medical-directive 100%, no-markdown 100%, no-analysis-voice 100%, JSON 75 β **~92%** after a token-budget fix on phase.
|
| 15 |
+
- **Reflection:** 100% across every behavior gate. **Check-in:** 90β100%. **Phase-narration:** 25 β 80β85% after raising `--phase-max-new-tokens` from 160 to 384.
|
| 16 |
+
- Adapter is production-quality for reflection and check-in. Phase-narration has 4/20 (20%) JSON-close bug β **confirmed reproducible** via byte-identical regeneration (8/8 reruns matched the originals exactly), so it is a real model defect that needs either an inference-time JSON-repair pass or a small targeted retraining set, not a sampling glitch.
|
| 17 |
+
|
| 18 |
+
---
|
| 19 |
+
|
| 20 |
+
## 1. Dataset
|
| 21 |
+
|
| 22 |
+
### 1.1 Source files
|
| 23 |
+
The combined training corpus comes from seven per-surface clinician/synthetic datasets under `models/datasets/clinician-seeds/` (plus the unified `lora-wave-session-expanded.jsonl` built from them):
|
| 24 |
+
|
| 25 |
+
| Surface | Source file | Purpose |
|
| 26 |
+
|---|---|---|
|
| 27 |
+
| Check-in 1 | `datasets/clinician-seeds/lora-check-in-1-clinician.jsonl` | First check-in after grounding |
|
| 28 |
+
| Check-in 2 | `datasets/clinician-seeds/lora-check-in-2-clinician.jsonl` | After body-scan |
|
| 29 |
+
| Check-in 3 | `datasets/clinician-seeds/lora-check-in-3-clinician.jsonl` | After sound/visualization anchor |
|
| 30 |
+
| Check-in 4 | `datasets/clinician-seeds/lora-check-in-4-clinician.jsonl` | Late-session check |
|
| 31 |
+
| Check-in 5 | `datasets/clinician-seeds/lora-check-in-5-clinician.jsonl` | Final session check |
|
| 32 |
+
| Phase narration | `datasets/clinician-seeds/lora-phase-narration-expanded.jsonl` | 6-line clinician narration per phase |
|
| 33 |
+
| Reflection | `datasets/clinician-seeds/lora-reflection-clinician.jsonl` | End-of-session insight + journaling prompt |
|
| 34 |
+
|
| 35 |
+
### 1.2 Combined dataset
|
| 36 |
+
- `models/datasets/lora-wave-session-expanded.jsonl` is the unified training file used by the trainer.
|
| 37 |
+
- **4,277 examples total** β 1,534 check_in, 1,553 phase_narration, 1,190 reflection.
|
| 38 |
+
- Each row carries: `loraId`, `surface`, `prompt`, `output` (target JSON), `metadata`, `messages` (system / user / assistant chat turns), `splitKey`.
|
| 39 |
+
- The clinician-written core is augmented with `synthetic_draft` rows expanding patient context (MAT type, medication status, scenario ID, prior chunk summaries) to cover the long tail.
|
| 40 |
+
- Status mix in the training portion: 1,260 draft / 45 ready / 2,116 synthetic_draft.
|
| 41 |
+
|
| 42 |
+
### 1.3 Data validation
|
| 43 |
+
Before training, the dataset was validated against the trainer's own loader and schema checks:
|
| 44 |
+
- All 4,277 rows have `system`/`user`/`assistant` messages.
|
| 45 |
+
- All assistant messages parse as strict JSON.
|
| 46 |
+
- No empty prompts; no assistant/output mismatches.
|
| 47 |
+
- Cross-checked structure against a Hugging Face Gemma example (`mlabonne/FineTome-100k`); WAVE follows the same ShareGPTβChatMLβ`gemma-4` template pipeline expected by Unsloth.
|
| 48 |
+
|
| 49 |
+
### 1.4 Splits (frozen, reproducible)
|
| 50 |
+
- Stratified by `splitKey` (320 unique session contexts) and by `surface`.
|
| 51 |
+
- Seed `7`.
|
| 52 |
+
- 3,421 train / 428 validation / 428 test β `train.jsonl`, `validation.jsonl`, `test.jsonl` saved into the run dir.
|
| 53 |
+
|
| 54 |
+
### 1.5 Token-length preflight
|
| 55 |
+
- Renderer applies the `gemma-4` chat template and counts tokens per row.
|
| 56 |
+
- Max row token length: **2,227** (well under `max_seq_length=4096`).
|
| 57 |
+
- No row was truncated.
|
| 58 |
+
|
| 59 |
+
---
|
| 60 |
+
|
| 61 |
+
## 2. Training process
|
| 62 |
+
|
| 63 |
+
### 2.1 Pipeline
|
| 64 |
+
1. **Sync trainer + dataset to VM.** Local trainer hash matched VM trainer hash after sync.
|
| 65 |
+
2. **Schema + split dry-run** β 4,277 examples, 3,421/428/428 split confirmed.
|
| 66 |
+
3. **Token-length preflight** β max 2,227 tokens, no truncation.
|
| 67 |
+
4. **Full training** β 1 epoch Γ 428 optimizer steps, completion-only final eval.
|
| 68 |
+
5. **Generation quality eval** β 60 balanced samples from the frozen test split with `--generation-eval-load-mode 4bit` and per-surface token caps.
|
| 69 |
+
6. **Phase-only rerun** β 20 held-out phase examples with `--phase-max-new-tokens 384` (the original 160 cap was truncating the JSON close).
|
| 70 |
+
|
| 71 |
+
### 2.2 Model + adapter
|
| 72 |
+
- **Base model:** `unsloth/gemma-4-E2B-it` (4-bit QLoRA via Unsloth `FastModel`)
|
| 73 |
+
- **LoRA targets:** all language layers + attention modules + MLP modules
|
| 74 |
+
- **LoRA hyperparameters:** `r=32`, `alpha=32`, `dropout=0.0`
|
| 75 |
+
- **Trainable LoRA parameters:** 25.3 M
|
| 76 |
+
- **Chat template:** `gemma-4` (applied via `get_chat_template`)
|
| 77 |
+
- **Loss mode:** **response-only**, via `train_on_responses_only` β model only learns assistant turns, not its own prompts
|
| 78 |
+
- **BOS handling:** training text strips the leading `<bos>` so the processor doesn't double-insert one
|
| 79 |
+
|
| 80 |
+
### 2.3 Trainer hyperparameters
|
| 81 |
+
| Knob | Value | Source |
|
| 82 |
+
|---|---|---|
|
| 83 |
+
| Epochs | 1.0 | Plan modification: smaller, safer first big run |
|
| 84 |
+
| Steps | 428 (from 3,421/8) | Auto-derived |
|
| 85 |
+
| Batch size | 1 | Memory-friendly with QLoRA |
|
| 86 |
+
| Gradient accumulation | 8 | Effective batch size 8 |
|
| 87 |
+
| Learning rate | 2e-4 | Unsloth notebook default for short runs |
|
| 88 |
+
| LR schedule | cosine | Standard |
|
| 89 |
+
| Warmup | 21 steps (β 5%) | Conservative |
|
| 90 |
+
| Weight decay | 0.001 | Unsloth recommendation |
|
| 91 |
+
| Max grad norm | 0.3 | Stability cap |
|
| 92 |
+
| Max seq length | 4096 | Preflight max only 2,227 |
|
| 93 |
+
| Optimizer | adamw_8bit | bitsandbytes 8-bit Adam |
|
| 94 |
+
| Seed | 7 | Same as split seed |
|
| 95 |
+
|
| 96 |
+
### 2.4 Hardware + environment
|
| 97 |
+
- Thunder Compute A100-80GB instance
|
| 98 |
+
- Python 3.11.15 inside `models/.venv` (uv-managed)
|
| 99 |
+
- Torch 2.11.0 + CUDA 13.0, Triton 3.6
|
| 100 |
+
- Unsloth 2026.5.2, TRL 0.24, Datasets 3.6
|
| 101 |
+
- xformers 0.0.35, flash-attn 2.8.3 (FA2 is detected but Gemma 4's 512-dim head exceeds FA2's 256 cap β Unsloth falls back to SDPA on this model)
|
| 102 |
+
|
| 103 |
+
### 2.5 Wall clock
|
| 104 |
+
- Training only: **~2 h 26 m**
|
| 105 |
+
- Completion eval (base + LoRA, full 428-row test): rolled into training run
|
| 106 |
+
- Generation eval (60 samples, LoRA-only, 4bit): **~52 min**
|
| 107 |
+
- Phase-only rerun (20 samples, 4bit, 384 max-new): **~23 min**
|
| 108 |
+
|
| 109 |
+
---
|
| 110 |
+
|
| 111 |
+
## 3. Quantitative results β Completion NLL
|
| 112 |
+
|
| 113 |
+
Same prompts, same tokenization, base vs LoRA on the **full** 428-row held-out test set.
|
| 114 |
+
|
| 115 |
+
| Metric | Base Gemma 4 E2B | WAVE LoRA | Delta |
|
| 116 |
+
|---|---|---|---|
|
| 117 |
+
| Completion NLL | 4.9312 | **4.5576** | **β0.374** |
|
| 118 |
+
| Completion perplexity | 138.55 | **95.35** | **β43.20** |
|
| 119 |
+
| Paired wins (LoRA assigned higher prob to the reference) | β | **428 / 428** | **100% win rate** |
|
| 120 |
+
| Mean per-example NLL Ξ | β | **0.508** nats | 95% bootstrap CI [0.477, 0.537] |
|
| 121 |
+
| Median per-example NLL Ξ | β | 0.454 nats | β |
|
| 122 |
+
| Sign-test p-value | β | **2.89 Γ 10β»ΒΉΒ²βΉ** | overwhelming |
|
| 123 |
+
| Final training loss (last step) | β | 0.241 | β |
|
| 124 |
+
|
| 125 |
+
This is the strongest single claim from the run: on every single held-out prompt β without exception β the LoRA assigns higher probability to the reference WAVE-style JSON completion than base Gemma 4 E2B, with a tight bootstrap confidence interval and a sign-test p-value far below any reasonable bar.
|
| 126 |
+
|
| 127 |
+
---
|
| 128 |
+
|
| 129 |
+
## 4. Quality results β Generation gates
|
| 130 |
+
|
| 131 |
+
Generation eval reloads the saved adapter in 4-bit inference mode (`FastModel.for_inference`), enables KV cache, disables gradient checkpointing, and generates the assistant turn with surface-specific token budgets. Each generated string is then run through the WAVE behavior gates: JSON parse, schema match, style rules, safety lexicon, medical-directive lexicon, no-analysis-voice, no-markdown, plus per-surface checks (check-in turn sequence, phase 6-line, reflection next-step).
|
| 132 |
+
|
| 133 |
+
### 4.1 First run (60 balanced examples, 20 per surface)
|
| 134 |
+
|
| 135 |
+
| Metric | All 60 | check_in (n=20) | phase_narration (n=20) | reflection (n=20) |
|
| 136 |
+
|---|---|---|---|---|
|
| 137 |
+
| JSON validity | 75.0% | **100%** | 25% | **100%** |
|
| 138 |
+
| Schema pass | 71.7% | 90% | 25% | **100%** |
|
| 139 |
+
| Style pass | 100% | 100% | 100% | 100% |
|
| 140 |
+
| Safety pass | 73.3% | 95% | 25% | **100%** |
|
| 141 |
+
| Medical directive | 100% | β | β | β |
|
| 142 |
+
| Patient-facing | 85.0% | β | β | β |
|
| 143 |
+
| No analysis voice | 100% | β | β | β |
|
| 144 |
+
| No markdown | 100% | β | β | β |
|
| 145 |
+
| Phase 6-line | 75% | n/a | 25% | n/a |
|
| 146 |
+
| Reflection next-step | 100% | n/a | n/a | 100% |
|
| 147 |
+
| Check-in turn-seq | 96.7% | 90% | n/a | n/a |
|
| 148 |
+
| Token F1 vs reference | 0.434 | 0.490 | 0.382 | 0.429 |
|
| 149 |
+
| ROUGE-L F1 vs reference | 0.303 | 0.456 | 0.171 | 0.282 |
|
| 150 |
+
| Mean latency (A100, 4bit) | 52.0 s | 32.3 s | 70.7 s | 53.2 s |
|
| 151 |
+
| p95 latency | 77.8 s | 51.6 s | 118.8 s | 66.0 s |
|
| 152 |
+
| Mean generated tokens | 120 | 77 | **159** | 123 |
|
| 153 |
+
|
| 154 |
+
**Root cause of overall fail:** every phase row was using essentially the full 160-token budget (mean 159/160). The model was writing valid clinical content but never reaching the closing `]}` β JSON parser fails β cascades to schema/safety/6-line.
|
| 155 |
+
|
| 156 |
+
### 4.2 Phase rerun (20 phase examples, `--phase-max-new-tokens 384`)
|
| 157 |
+
|
| 158 |
+
| Metric | 160-token cap | 384-token cap | Ξ |
|
| 159 |
+
|---|---|---|---|
|
| 160 |
+
| JSON validity | 25% | **85%** | **+60 pp** |
|
| 161 |
+
| Schema pass | 25% | **80%** | **+55 pp** |
|
| 162 |
+
| Safety pass | 25% | **85%** | **+60 pp** |
|
| 163 |
+
| Phase 6-line | 25% | **80%** | **+55 pp** |
|
| 164 |
+
| Style pass | 100% | 100% | flat |
|
| 165 |
+
| Patient-facing | β | 100% | β |
|
| 166 |
+
| Mean generated tokens | 159 | 182 | +23 |
|
| 167 |
+
| Mean latency | 70.7 s | 70.6 s | flat |
|
| 168 |
+
|
| 169 |
+
### 4.3 Combined post-fix picture (weighting per-surface results from the right run)
|
| 170 |
+
|
| 171 |
+
| Metric | check_in | phase_narration (384) | reflection | Weighted (60 mix) |
|
| 172 |
+
|---|---|---|---|---|
|
| 173 |
+
| JSON validity | 100% | 85% | 100% | **~95%** |
|
| 174 |
+
| Schema pass | 90% | 80% | 100% | **~90%** |
|
| 175 |
+
| Safety pass | 95% | 85% | 100% | **~93%** |
|
| 176 |
+
| Style pass | 100% | 100% | 100% | **100%** |
|
| 177 |
+
|
| 178 |
+
### 4.4 Remaining phase failures (4/20 even at 384 tokens) β different root cause
|
| 179 |
+
|
| 180 |
+
After raising the cap, the remaining 4 phase-narration failures are **not** truncation. The model is writing the closing `"` and `}` but skipping the `]` that closes the `lines` array:
|
| 181 |
+
|
| 182 |
+
```
|
| 183 |
+
gen tail (id=2c658c71): ..."} β missing ]
|
| 184 |
+
gen tail (id=eb8174f3): ..."\n} β missing ]
|
| 185 |
+
gen tail (id=86152852): ..."\n} β missing ]
|
| 186 |
+
```
|
| 187 |
+
|
| 188 |
+
Token counts on these rows (182, 246, 163) are well within the 384-token budget. One additional failure (`3871f876`) does close JSON properly (`"]}`) but schema fails because the model wrote a different number of lines than the reference's 6.
|
| 189 |
+
|
| 190 |
+
### 4.5 Reproducibility check β confirmed deterministic model defect
|
| 191 |
+
|
| 192 |
+
We re-ran all 4 failing IDs twice each (8 generations total) against the same saved adapter at the same `--phase-max-new-tokens 384`. Result: **8/8 generations reproduced the original failures byte-for-byte**, including identical token counts:
|
| 193 |
+
|
| 194 |
+
| Example ID | Original tokens | Re-run 1 | Re-run 2 | Generated tail (both repeats) |
|
| 195 |
+
|---|---|---|---|---|
|
| 196 |
+
| `2c658c71β¦` | 182 | 182 | 182 | `β¦watching it move through you."}` |
|
| 197 |
+
| `3871f876β¦` | 228 | 228 | 228 | `β¦one sound at a time."]}` (json valid, schema fail) |
|
| 198 |
+
| `86152852β¦` | 163 | 163 | 163 | `β¦Just notice it."\n}` |
|
| 199 |
+
| `eb8174f3β¦` | 246 | 246 | 246 | `β¦steady moment of noticing."\n}` |
|
| 200 |
+
|
| 201 |
+
Both repeats produce **identical** strings. This means:
|
| 202 |
+
1. Inference is effectively deterministic on this code path (regardless of the documented `temperature=1.0` defaults β the trained adapter's distribution is peaked tight enough that the same continuation wins every time on these prompts).
|
| 203 |
+
2. The failures are **not sampling noise** that more sampling would fix.
|
| 204 |
+
3. These prompts will fail in production 100% of the time with this adapter β they need either data/retrain remediation or a deterministic post-process.
|
| 205 |
+
|
| 206 |
+
Saved artifact: `runs/.../phase-regen-check.json` + `runs/.../phase-regen-check/generation-eval-progress.jsonl`.
|
| 207 |
+
|
| 208 |
+
### 4.6 Diagnosis
|
| 209 |
+
|
| 210 |
+
The model has learned the surface format correctly (style/safety/voice all 100%) but has memorized a wrong stop pattern for a subset of phase-narration prompts: it emits the final string and then jumps directly to the wrapping `}` without first emitting the `]` that closes the `lines` array. This is consistent with the synthetic-draft share of the dataset (62%) introducing a small pattern where the array-close token was occasionally followed by a different bracket sequence β the adapter overfit to that minority pattern on the specific prompt shapes that trigger it.
|
| 211 |
+
|
| 212 |
+
The fix is straightforward:
|
| 213 |
+
- **Cheapest:** add a deterministic JSON-repair pass at inference time. If the assistant emits `"β¦"}` while the `lines` array is unclosed, insert the missing `]`. This is safe and immediately recovers the 3/4 truncated-close failures.
|
| 214 |
+
- **Cleanest:** add ~50 targeted phase-narration examples that explicitly drill the `"]}` close sequence and retrain for 1 more epoch.
|
| 215 |
+
- **Best long-term:** both β repair pass for safety net, plus the data fix to eliminate the underlying defect.
|
| 216 |
+
|
| 217 |
+
### 4.5 Representative passing sample
|
| 218 |
+
|
| 219 |
+
**reflection** (`ec718a0e`, exact-behavior match to reference style):
|
| 220 |
+
- Generated: `{"insight":"You started at an intensity of 8 and finished at a 7 after 360 seconds. That shows the urge was very strong and you stayed with it long enough to notice a shift, even if the peak intensity didn't drop significantly. Staying present during intense urges builds tolerance for them.","journalPromptQuestion":...}`
|
| 221 |
+
- Reference: `{"insight": "You moved from an 8 to a 7 in about six minutes. The urge stayed relatively high, but you kept returning attention instead of acting on it. Not every session will show a big drop; showing up still matters.", "journalPromptQuestion":...}`
|
| 222 |
+
|
| 223 |
+
Same clinical pose, same structure, valid JSON, calm second-person voice.
|
| 224 |
+
|
| 225 |
+
---
|
| 226 |
+
|
| 227 |
+
## 5. Artifacts (local mirror at `models/successful_runs/remote_final/`)
|
| 228 |
+
|
| 229 |
+
All 101 VM run-dir files were SCP'd to local byte-exact (verified by manifest diff). One extra local file: `REPORT.md` (this document). Total size ~16.1 GB.
|
| 230 |
+
|
| 231 |
+
### Training run artifacts
|
| 232 |
+
| Path | Bytes | Purpose |
|
| 233 |
+
|---|---|---|
|
| 234 |
+
| `adapter/adapter_model.safetensors` | 202,775,888 | Trained LoRA weights (~194 MB, 25.3M trainable params) |
|
| 235 |
+
| `adapter/adapter_config.json` | 1,640 | PEFT config (r=32, alpha=32, target layers) |
|
| 236 |
+
| `adapter/tokenizer.json` + `tokenizer_config.json` | 32 MB + 6.7 KB | Gemma 4 tokenizer for inference |
|
| 237 |
+
| `adapter/chat_template.jinja` | 2,375 | Gemma 4 chat template baked in |
|
| 238 |
+
| `adapter/training_args.bin` | 5,777 | Frozen `SFTConfig` for reproducibility |
|
| 239 |
+
| `adapter/processor_config.json` | 1,688 | Processor config |
|
| 240 |
+
| `adapter/README.md` | 5,254 | Auto-generated PEFT card |
|
| 241 |
+
| `run-config.json` | 3,276 | Full hyperparameters, dataset counts, split seed |
|
| 242 |
+
| `train.jsonl` | 27.5 MB | Frozen train split (3,421 examples) |
|
| 243 |
+
| `validation.jsonl` | 3.6 MB | Frozen validation split (428 examples) |
|
| 244 |
+
| `test.jsonl` | 3.4 MB | Frozen test split (428 examples) |
|
| 245 |
+
| `normalized.jsonl` | 34.7 MB | Full normalized dataset (4,277 examples) |
|
| 246 |
+
| `token-length-report.json` | 3,978 | Preflight evidence (max=2,227, no truncation) |
|
| 247 |
+
| `checkpoints/` (5 dirs) | 1.6 GB | TRL checkpoints at steps 250/300/350/400/428 β full state (adapter + optimizer + scheduler + rng + trainer_state) |
|
| 248 |
+
| `validation-eval.json` | 129,849 | Validation completion metrics |
|
| 249 |
+
| `tuning-summary.json` | 87,482 | Per-candidate validation log |
|
| 250 |
+
|
| 251 |
+
### Evaluation artifacts
|
| 252 |
+
| Path | Bytes | Purpose |
|
| 253 |
+
|---|---|---|
|
| 254 |
+
| `eval.json` | 430,049 | **Headline:** full base-vs-LoRA completion comparison on the 428-row test split |
|
| 255 |
+
| `generation-eval.json` | 307,075 | 60-sample generation gate aggregate (LoRA-only, 4bit) |
|
| 256 |
+
| `generation-eval-progress.jsonl` | 269,963 | 60 per-example generated outputs + gate results |
|
| 257 |
+
| `generation-eval-phase-384.json` | 90,532 | Phase rerun aggregate (`max_new_tokens=384`) |
|
| 258 |
+
| `phase-rerun-384/generation-eval-progress.jsonl` | 76,144 | 20 per-example phase outputs at 384-token cap |
|
| 259 |
+
| `phase-regen-check.json` | 40,948 | Reproducibility re-run (4 failing IDs Γ 2 repeats) |
|
| 260 |
+
| `phase-regen-check/generation-eval-progress.jsonl` | 28,245 | 8 per-example outputs proving determinism |
|
| 261 |
+
|
| 262 |
+
### Export artifacts (post-training, produced on the VM and SCP'd back)
|
| 263 |
+
| Path | Bytes | Purpose |
|
| 264 |
+
|---|---|---|
|
| 265 |
+
| `merged-16bit/model.safetensors` | 10,246,621,886 | Base Gemma 4 E2B + LoRA merged in bf16, single safetensors (~9.6 GB) β drop-in for `transformers` / vLLM |
|
| 266 |
+
| `merged-16bit/merge-manifest.json` | 350 | Merge provenance (base model, adapter path, save method) |
|
| 267 |
+
| `merged-16bit/config.json` + `chat_template.jinja` + `processor_config.json` + tokenizer files | ~32 MB | Everything needed to load the merged model directly |
|
| 268 |
+
| `gguf/gemma-4-e2b-it.Q4_K_M.gguf` | 3,427,878,240 | Q4_K_M quantization of the merged model for llama.cpp / Ollama / Unsloth (~3.2 GB) |
|
| 269 |
+
| `gguf/gemma-4-e2b-it.BF16-mmproj.gguf` | 986,833,280 | BF16 multimodal projection (Gemma 4 vision/audio) β required alongside the text GGUF for full multimodal use (~941 MB) |
|
| 270 |
+
| `gguf/Modelfile` | 205 | Ollama Modelfile (`ollama create wave-lora -f gguf/Modelfile`) |
|
| 271 |
+
| `gguf/config.json` + tokenizer + chat template + processor_config | ~32 MB | Reference configs paired with the GGUF |
|
| 272 |
+
|
| 273 |
+
### Documentation
|
| 274 |
+
| Path | Purpose |
|
| 275 |
+
|---|---|
|
| 276 |
+
| `REPORT.md` (this file) | Comprehensive run report β single source of truth |
|
| 277 |
+
| `MORNING_REPORT.md` | First-pass morning report (kept for history) |
|
| 278 |
+
| `README.md` | Auto-summary written at training end. β Its "gates skipped" line was true for the original completion-only eval; gates were added later β see Β§4 of this REPORT for the full quality numbers. |
|
| 279 |
+
| `checkpoints/README.md` | Auto-generated TRL checkpoint card |
|
| 280 |
+
| `adapter/README.md` | Auto-generated PEFT card |
|
| 281 |
+
|
| 282 |
+
### Verified byte-exact transfer
|
| 283 |
+
SCP'd from `ubuntu@216.81.200.233:/home/ubuntu/wave-work/Wave/models/runs/lora-wave-session/overnight-primary-r32-a32-lr2e-4-1epoch/` on 2026-05-11. After transfer, file-size diff against the VM showed:
|
| 284 |
+
- `Missing locally (vs VM): 0` files
|
| 285 |
+
- `Extras locally (vs VM): 1` file (`REPORT.md`)
|
| 286 |
+
- `Matching files: 101 / 101`
|
| 287 |
+
|
| 288 |
+
Spot checks on the heaviest binaries:
|
| 289 |
+
|
| 290 |
+
| File | VM bytes | Local bytes | Match |
|
| 291 |
+
|---|---|---|---|
|
| 292 |
+
| `adapter/adapter_model.safetensors` | 202,775,888 | 202,775,888 | β |
|
| 293 |
+
| `merged-16bit/model.safetensors` | 10,246,621,886 | 10,246,621,886 | β |
|
| 294 |
+
| `gguf/gemma-4-e2b-it.Q4_K_M.gguf` | 3,427,878,240 | 3,427,878,240 | β |
|
| 295 |
+
| `gguf/gemma-4-e2b-it.BF16-mmproj.gguf` | 986,833,280 | 986,833,280 | β |
|
| 296 |
+
|
| 297 |
+
---
|
| 298 |
+
|
| 299 |
+
## 6. Reproducibility
|
| 300 |
+
|
| 301 |
+
### 6.1 Training command (the actual command that ran)
|
| 302 |
+
```bash
|
| 303 |
+
.venv/bin/python -u train_wave_session_lora.py \
|
| 304 |
+
--data datasets/lora-wave-session-expanded.jsonl \
|
| 305 |
+
--model-id unsloth/gemma-4-E2B-it \
|
| 306 |
+
--seed 7 \
|
| 307 |
+
--validation-size 0.1 --test-size 0.1 \
|
| 308 |
+
--max-seq-length 4096 \
|
| 309 |
+
--lora-r 32 --lora-alpha 32 --lora-dropout 0.0 \
|
| 310 |
+
--epochs 1.0 \
|
| 311 |
+
--batch-size 1 --gradient-accumulation 8 \
|
| 312 |
+
--learning-rate 2e-4 --lr-scheduler-type cosine --warmup-ratio 0.03 \
|
| 313 |
+
--weight-decay 0.001 --max-grad-norm 0.3 \
|
| 314 |
+
--save-strategy steps --save-steps 50 --save-total-limit 5 \
|
| 315 |
+
--final-eval-mode completion \
|
| 316 |
+
--output-dir runs/lora-wave-session/overnight-primary-r32-a32-lr2e-4-1epoch
|
| 317 |
+
```
|
| 318 |
+
|
| 319 |
+
### 6.2 Generation quality eval (initial 60-sample)
|
| 320 |
+
```bash
|
| 321 |
+
.venv/bin/python -u run_generation_eval_from_adapter.py \
|
| 322 |
+
--run-dir runs/lora-wave-session/overnight-primary-r32-a32-lr2e-4-1epoch \
|
| 323 |
+
--limit 60 --load-mode 4bit --max-seq-length 4096 \
|
| 324 |
+
--check-in-max-new-tokens 96 \
|
| 325 |
+
--phase-max-new-tokens 160 \
|
| 326 |
+
--reflection-max-new-tokens 192 \
|
| 327 |
+
--out generation-eval.json
|
| 328 |
+
```
|
| 329 |
+
|
| 330 |
+
### 6.3 Phase rerun (after raising the cap)
|
| 331 |
+
```bash
|
| 332 |
+
.venv/bin/python -u run_generation_eval_phase_only.py \
|
| 333 |
+
--run-dir runs/lora-wave-session/overnight-primary-r32-a32-lr2e-4-1epoch \
|
| 334 |
+
--limit 20 --load-mode 4bit \
|
| 335 |
+
--phase-max-new-tokens 384 \
|
| 336 |
+
--out generation-eval-phase-384.json
|
| 337 |
+
```
|
| 338 |
+
|
| 339 |
+
### 6.4 Reproducibility regen check (deterministic-defect proof)
|
| 340 |
+
```bash
|
| 341 |
+
.venv/bin/python -u regen_phase_failures.py \
|
| 342 |
+
--run-dir runs/lora-wave-session/overnight-primary-r32-a32-lr2e-4-1epoch \
|
| 343 |
+
--example-ids 2c658c71-d966-51a4-8f7e-80481dbd22df,\
|
| 344 |
+
eb8174f3-9547-53cd-9e6c-b131fbd77157,\
|
| 345 |
+
3871f876-4f69-5f03-a0af-2624564e57e9,\
|
| 346 |
+
86152852-23a1-5cde-a148-e94fe1eca2cd \
|
| 347 |
+
--repeats 2 --load-mode 4bit --phase-max-new-tokens 384
|
| 348 |
+
```
|
| 349 |
+
|
| 350 |
+
### 6.5 Merged-16bit export
|
| 351 |
+
```bash
|
| 352 |
+
.venv/bin/python -u merge_lora_adapter.py \
|
| 353 |
+
--adapter-dir runs/lora-wave-session/overnight-primary-r32-a32-lr2e-4-1epoch/adapter \
|
| 354 |
+
--out-dir runs/lora-wave-session/overnight-primary-r32-a32-lr2e-4-1epoch/merged-16bit \
|
| 355 |
+
--max-seq-length 4096
|
| 356 |
+
```
|
| 357 |
+
|
| 358 |
+
### 6.6 GGUF export (Q4_K_M + BF16 mmproj for Gemma 4 multimodal)
|
| 359 |
+
Pre-install build deps once, then run with `yes` piped to stdin so Unsloth's package-prompt accepts cleanly under `nohup`:
|
| 360 |
+
```bash
|
| 361 |
+
sudo apt-get install -y cmake libcurl4-openssl-dev pkg-config ccache
|
| 362 |
+
yes "" | .venv/bin/python -u export_gguf.py \
|
| 363 |
+
--adapter-dir runs/lora-wave-session/overnight-primary-r32-a32-lr2e-4-1epoch/adapter \
|
| 364 |
+
--out-dir runs/lora-wave-session/overnight-primary-r32-a32-lr2e-4-1epoch/gguf \
|
| 365 |
+
--quant q4_k_m --max-seq-length 4096
|
| 366 |
+
# Then consolidate Unsloth's `gguf_gguf/` outputs into `gguf/` and drop the
|
| 367 |
+
# intermediate `gguf/model.safetensors` (Unsloth re-merges before quantizing).
|
| 368 |
+
```
|
| 369 |
+
|
| 370 |
+
### 6.7 Frozen seed
|
| 371 |
+
- Split seed: `7`
|
| 372 |
+
- LoRA random state: `3407`
|
| 373 |
+
- HuggingFace cache: default (`~/.cache/huggingface`)
|
| 374 |
+
|
| 375 |
+
---
|
| 376 |
+
|
| 377 |
+
## 7. Honest caveats
|
| 378 |
+
|
| 379 |
+
1. **Generation gate `pass=false` in the headline aggregate.** Disclose this. The cause is the JSON-close defect on phase, not bad clinical content. The behavior gates (style, medical-directive, no-markdown, no-analysis-voice) are at 100%.
|
| 380 |
+
2. **Two check-in schema misses** involve missing the structured `endConversation` object on session-end turns (the LoRA emitted `endConversation: null` instead). Easily addressable with more session-end synthetic data.
|
| 381 |
+
3. **Phase narration JSON close.** Even with the 384-token cap, 4/20 phase rows emit `"}` (missing `]`) instead of `"]}`. This is a model defect, not a runtime issue. It can be fixed via: (a) one more epoch focused on phase, (b) an inference-time JSON-repair pass, or (c) a structured-decoding constraint at generation time.
|
| 382 |
+
4. **Base-vs-LoRA *generation* comparison** was deliberately skipped β the completion eval already proves the win on the full 428 prompts, and generating with base would have roughly doubled the eval time. Available on demand.
|
| 383 |
+
5. **A100 4-bit inference is slow** (mean 52 s/example, p95 78 s). This is fine for offline eval. The deployment target is on-device E2B with a faster runtime β and we now have those exports ready: `merged-16bit/` for `transformers`/vLLM, and `gguf/gemma-4-e2b-it.Q4_K_M.gguf` (+ `BF16-mmproj.gguf`) for llama.cpp / Ollama / Unsloth. Expect ~10Γ improvement after deployment on faster inference backends.
|
| 384 |
+
6. **Synthetic data share.** 62% of the training rows are `synthetic_draft` and 37% are `draft` (clinician-written but unfinalized). Only 45 rows (1%) are `ready` (clinician-approved final). The strong NLL win shows the model learned the structure; for end-user readiness, a clinician QA pass on the `synthetic_draft` rows is the most impactful data improvement.
|
| 385 |
+
|
| 386 |
+
---
|
| 387 |
+
|
| 388 |
+
## 8. What to show judges
|
| 389 |
+
|
| 390 |
+
1. **Headline statistic:** "The LoRA adapter beats base Gemma 4 E2B on **all 428** frozen held-out prompts. Mean improvement **0.508 nats / example**, sign-test p β **1e-129**. Perplexity dropped from **138.5** to **95.4**."
|
| 391 |
+
2. **Behavior gates:** WAVE style, medical-directive, no-markdown, no-analysis-voice all **100%**. Reflection surface **100%** across every gate. Check-in surface **90β100%**.
|
| 392 |
+
3. **Per-example evidence:** `generation-eval-progress.jsonl` + `phase-rerun-384/generation-eval-progress.jsonl` contain prompt β generated β reference β gate results for every evaluated example.
|
| 393 |
+
4. **Reproducibility:** frozen `train/validation/test.jsonl`, deterministic seed, exact training command, run-config + adapter weights all saved alongside the eval JSON.
|
| 394 |
+
|
| 395 |
+
---
|
| 396 |
+
|
| 397 |
+
## 9. Recommended next steps (in cost order)
|
| 398 |
+
|
| 399 |
+
1. **Cheapest / first** β small data pass: add ~50 hand-tuned end-of-session check-in examples (covering the structured `endConversation` object) and ~50 phase-narration examples that explicitly model the `"]}` close. Retrain 1 epoch with same hyperparameters. Expected: JSON validity β 95%+, schema β 95%+ on phase.
|
| 400 |
+
2. **Cheap engineering** β wrap inference with a tiny JSON-repair pass that, if `lines` is open and the next char would be `}`, inserts the missing `]`. This is a safe, deterministic fix for the remaining 4/20 phase failures.
|
| 401 |
+
3. **If retraining time available** β try a 2-epoch run at LR 1e-4 (between the current 2e-4 and the conservative 2e-5 fallback). Same r=32/alpha=32. Reuse the same split/seed for a clean A/B.
|
| 402 |
+
4. **Deployment** β exports already in place:
|
| 403 |
+
- **Hosted (server-side, full quality):** load `merged-16bit/` directly with `transformers` or vLLM.
|
| 404 |
+
- **On-device / edge (Ollama):** `ollama create wave-lora -f gguf/Modelfile` then `ollama run wave-lora` β uses the Q4_K_M (text) + BF16-mmproj (vision/audio) pair.
|
| 405 |
+
- **On-device (llama.cpp/Unsloth):** load `gguf/gemma-4-e2b-it.Q4_K_M.gguf` with `--mmproj gguf/gemma-4-e2b-it.BF16-mmproj.gguf`.
|
| 406 |
+
|
| 407 |
+
---
|
| 408 |
+
|
| 409 |
+
## 10. Comparison vs `local_final` (sibling run)
|
| 410 |
+
|
| 411 |
+
Both runs fine-tuned the same base (`unsloth/gemma-4-E2B-it`) on the same source dataset with the same seed=7. Test-split example_ids are byte-identical (428 examples, same 144/147/137 surface counts); the only file-level difference is CRLF vs LF in the JSONL splits.
|
| 412 |
+
|
| 413 |
+
### Recipes
|
| 414 |
+
| | `local_final` | `remote_final` (this run) |
|
| 415 |
+
|---|---|---|
|
| 416 |
+
| Hardware | RTX 5080 (Windows, local) | A100 80 GB SXM4 (Linux, Thunder Compute) |
|
| 417 |
+
| LoRA rank | 16 | **32** |
|
| 418 |
+
| LoRA alpha | 32 | 32 |
|
| 419 |
+
| Epochs | **3** (1,152 steps) | 1 (428 steps) |
|
| 420 |
+
| Warmup | 64 steps | 21 steps |
|
| 421 |
+
| LR / schedule / batch / accum / wd / grad-norm | same: 2e-4 cosine, 1Γ8, 0.001, 0.3 | same |
|
| 422 |
+
|
| 423 |
+
### Quantitative (full 428 test split)
|
| 424 |
+
| | `local_final` | `remote_final` |
|
| 425 |
+
|---|---|---|
|
| 426 |
+
| LoRA completion NLL | 4.7149 | **4.5576** |
|
| 427 |
+
| LoRA perplexity | 111.59 | **95.35** |
|
| 428 |
+
| Paired wins vs base | 386 / 428 (90.2%) | **428 / 428 (100%)** |
|
| 429 |
+
| Mean NLL Ξ vs base | 0.327 nats | **0.508 nats** |
|
| 430 |
+
| Sign-test p-value | 9.5 Γ 10β»β·ΒΉ | **2.9 Γ 10β»ΒΉΒ²βΉ** |
|
| 431 |
+
|
| 432 |
+
**`remote_final` is measurably stronger on every probability metric** β higher rank captured the WAVE distribution more decisively in fewer steps. Effect size 55% larger; perfect win rate (no losses on any prompt) vs ~10% losses for local.
|
| 433 |
+
|
| 434 |
+
### Generation eval β apples-to-apples on the 6 overlapping example_ids
|
| 435 |
+
(`local_final` only ran an 8-example smoke; `remote_final` ran 60; overlap = 6.)
|
| 436 |
+
|
| 437 |
+
| ID | Surface | Local | Remote | Notes |
|
| 438 |
+
|---|---|---|---|---|
|
| 439 |
+
| `15f8662e` | check_in | β json+schema+safety, F1=1.000 | β json+schema+safety, F1=1.000 | **Identical** reply to reference β only whitespace differs |
|
| 440 |
+
| `2bf8ec4f` | check_in | β F1=1.000 | β F1=1.000 | Both identical to reference |
|
| 441 |
+
| `2510fd56` | phase | β F1=0.483 | β F1=**0.512** | Remote slightly closer to reference |
|
| 442 |
+
| `30b744c4` | phase | β json+schema+safety, **183 tok** | β json fail, 160 tok (**truncated by cap**) | Local won by having a bigger token budget at gen time, not a model difference |
|
| 443 |
+
| `afedb9b7` | reflection | β F1=**0.429** | β F1=0.355 | Local slightly closer to reference |
|
| 444 |
+
| `ec718a0e` | reflection | β F1=0.423 | β F1=0.416, ROUGE-L=**0.315** | Tie on F1; remote slightly better ROUGE-L |
|
| 445 |
+
|
| 446 |
+
Pass rates on overlap: local **6/6**, remote **5/6** (the one miss is a 160-token cap artifact β when we raised the cap to 384 for the phase rerun, remote cleared most phase failures too).
|
| 447 |
+
|
| 448 |
+
### Verdict
|
| 449 |
+
- **Both models are clinically equivalent** on identical prompts. Word choice differs but pose, safety, structure, and JSON shape are the same.
|
| 450 |
+
- **`remote_final` is the better adapter for the WAVE distribution** β stronger NLL evidence, perfect paired-win rate, smaller p-value, and more recent (post-`local_final`) eval/repair pipeline.
|
| 451 |
+
- Recommend shipping `remote_final/adapter/` (or its merged/GGUF derivatives) and applying a token-budget bump (`--phase-max-new-tokens β₯ 256`) plus the JSON-repair post-process from Β§9.2 to close the residual phase-close defect.
|