yuta24's picture
v0.1.0-alpha: PyTorch LoRA + heads, code, model card
e379257 verified
|
Raw History Blame Contribute Delete
4.84 kB
# RESULTS (Windows + ROCm PC, measured 2026-10-22 JST)
Hardware/software: Ryzen AI 9 HX 370 / Radeon 890M (gfx1150), Windows 11, torch 2.14.0+rocm10.1.0,
transformers 5.20.2, peft 0.21.2. Model Qwen/Qwen3.5-2B, bf16, batch 1, LoRA r=32 (LM only) + head (22.1M).
Trainable params 53.3M. flash-linear-attention / causal_conv1d NOT installed (reference PyTorch path).
Synthetic data only (DUMMY teacher) -> accuracy numbers are not meaningful.
## Training run (`runs/train-syn-2b-390.log`)
200 optimizer steps, max_pixels=430080 (390 image tokens, ~860 total tokens), AOTriton OFF.
| step | loss | kd | ce |
| ---: | ---: | ---: | ---: |
| 10 | 0.997 | 0.816 | 1.207 |
| 50 | 0.353 | 0.325 | 0.379 |
| 100 | 0.310 | 0.290 | 0.329 |
| 150 | 0.311 | 0.302 | 0.310 |
| 200 | 0.224 | 0.187 | 0.292 |
4.5-4.6 s/step, peak allocated 10.49 GB. Checkpoint: adapter 125 MB + head 44 MB.
Eval on 20 synthetic records (60 questions, `runs/eval-syn-2b-390.json`): accuracy 1.0, NLL 0.202,
Brier 0.052, ECE(15) 0.182 (under-confident: it imitates the dummy teacher's soft targets), teacher agreement 0.917.
## bench.py (`runs/bench-default.json`, `runs/bench-aotriton.json`)
iPhone portrait 1179x2556 synthetic screenshots, 3 questions / 15 options. Train = fwd+bwd+AdamW step, median of 8.
Inference = preprocess + forward, LoRA NOT merged, median of 8.
| setting | image tokens | seq tokens | train s/step | train peak GB | infer ms | infer peak GB |
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
| default, 430080 px | 390 | 861 | 4.64 | 10.48 | 2359 | 5.07 |
| default, 1048576 px | 987 | 1458 | 16.37 | 14.65 | 8731 | 6.95 |
| AOTriton=1, 430080 px | 390 | 861 | 4.02 | 10.28 | 1653 | 4.79 |
| AOTriton=1, 1048576 px | 987 | 1458 | 8.45 | 14.16 | 3199 | 4.90 |
infer.py with LoRA merged (AOTriton OFF, 390 tokens): forward 1856-1866 ms, total ~1885 ms.
=> `TORCH_ROCM_AOTRITON_ENABLE_EXPERIMENTAL=1` is now set by default (os.environ.setdefault) in
train.py / infer.py / bench.py / scripts/evaluate.py. Set it to 0 to disable.
## Local Clef-Flash teacher (measured 2026-10-23 JST)
Teacher: `Cloudflare/clef-flash` (Apache-2.0; Qwen3.5-9B backbone + JointSchemaHead width 1024 / 4 layers),
revision `f0b7d3cd65296abfe459e6c228b618f5d8b26d2f`, 19.08 GB safetensors (4 shards + 0.24 GB head), downloaded in 246 s.
Loaded in bf16 entirely on the 890M (`load_release_model`, ~23 s, 17.75 GB allocated). No offload needed.
`scripts/label_local_clef.py`, AOTriton=1, batch 1, 3 questions / 15 options, reference (slow) Gated-DeltaNet path.
| max_pixels | image tokens | seq tokens | first item s | median s/image | peak alloc GB | peak reserved GB |
| ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| 430080 (= student) | 390 | ~860 | 27-29 (warm-up) | 3.10 | 18.02 | 18.14 |
| 1048576 | 987 | ~1455 | 68 (warm-up) | 5.53 | 18.20 | 18.51 |
Release default is `longest_edge=16777216` (native res, ~2.9k tokens for 1179x2556); not benchmarked.
Logs/stats: `runs/clef-label-{train,eval}-390.{log,json}`, `runs/clef-label-eval-987.{log,json}`.
Teacher vs synthetic gold (`scripts/teacher_report.py`, `runs/teacher-report-clef-390.json`):
| setting | records | questions | accuracy | record exact | NLL | mean conf | ECE(15) |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| 390 tok, train+eval | 80 | 240 | 1.000 | 1.000 | 0.035 | 0.966 | 0.034 |
| 987 tok, eval | 20 | 60 | 1.000 | 1.000 | 0.036 | 0.965 | 0.035 |
Example (syn-train-0000, gold error_dialog / true / retry): screen error_dialog 0.976, has_error true 0.967,
primary_button retry 0.846 (none 0.060, ok 0.036, delete 0.029, login 0.029). `primary_button` is the least confident field.
## Student distilled from Clef-Flash labels (`runs/train-clef-2b-390.log`)
Qwen3.5-2B + LoRA r=32 + head, `train.py --data data/synthetic/train.clef.jsonl --steps 200 --max-pixels 430080`
(alpha 0.7, T 2.0, beta 0.1, AOTriton=1). 727 s total, ~3.5 s/step, peak 10.29 GB. Checkpoint `checkpoints/clef-2b-390`.
Loss 2.05 (step 10) -> 0.42 (50) -> 0.27 (100) -> 0.082 (200); KD 0.031 at step 200.
Eval on `data/synthetic/eval.clef.jsonl` (20 records / 60 questions, `scripts/evaluate.py`; teacher agreement = argmax vs Clef-Flash):
| student | accuracy | NLL | Brier | ECE(15) | teacher agreement |
| --- | ---: | ---: | ---: | ---: | ---: |
| clef-2b-390 (Clef labels) | 1.000 | 0.032 | 0.0025 | 0.031 | 1.000 |
| syn-2b-390 (dummy labels) | 1.000 | 0.202 | 0.052 | 0.182 | 1.000 |
| Clef-Flash teacher itself | 1.000 | 0.036 | - | 0.034 | - |
=> The real teacher's sharper, well-calibrated targets fix the under-confidence of the dummy-label student
(ECE 0.18 -> 0.03). Student inference is ~1.65 s/image vs teacher 3.10 s/image at 390 tokens (~1.9x faster, 18 GB -> ~5 GB).
Caveat: 60 train / 20 eval synthetic images, easy task -> saturated accuracy; needs real iOS screenshots.