Files
wehub-resource-sync 542cfa195c
CI / Frontend build (push) Failing after 9m6s
CI / Plugin validate (push) Failing after 9m27s
CI / Python lint (push) Failing after 16m1s
CI / Tests (push) Successful in 18m0s
Deploy / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:27 +08:00

7.5 KiB
Raw Permalink Blame History

Compressed-image SFT — Final Results

Target: maximize GPT-4.1 LLM-judge accuracy on test_hn_with_answer.jsonl (500 examples) for Qwen3-VL-4B under 2x / 3x / 5x / 9x pixel compression, without inflating visual-token count at inference.

Judge: gpt-4.1-2025-04-14, SimpleQA A/B/C template (sft/eval_baseline.py).

Headline table

Compression base (no SFT) best specialized best universal (one adapter) specialized config
0x (original) 0.958 ceiling
2x 0.904 0.948 0.940 r=128, 2ep, lr 2e-5 (LLM-only sufficient)
3x 0.826 0.894 0.892 r=256, 1ep, lr 1e-5, LLM+ViT
5x 0.554 0.730 0.692 r=128, 2ep, lr 3e-5, LLM+ViT
9x 0.180 0.378 0.302 r=512, 2ep, lr 1e-5, LLM+ViT

Universal adapter is within 0.27.6 points of each specialized adapter — at 3x essentially tied, at 9x still meaningfully behind.

Key breakthrough: unfreezing the ViT

lora_target: all alone does not train the ViT in LlamaFactory — freeze_vision_tower: true is the default. Setting it to false unlocks LoRA on the ViT and gives big jumps at high compression:

Compression LLM-only LLM+ViT Δ
2x 0.948 0.948 0
3x 0.882 0.894 +1.2
5x 0.634 0.730 +9.6
9x 0.272 0.378 +8.2

Rule: the harder the compression, the more the ViT needs adaptation. 2x base is already near-ceiling so ViT LoRA adds nothing; 9x has the most distribution shift from pretraining so ViT capacity helps most (+30% relative).

What worked

  1. LoRA + ViT unfrozen → primary mechanism for 5x/9x gains.
  2. Bigger LoRA rank at high compression: 9x went r=32→128→256→512 for +2.4, +1.2, +0.8, +0.8 (diminishing but monotonic).
  3. Mixed-compression training (universal adapter): single LoRA trained on 2x+3x+5x+9x samples simultaneously. With r=256 + ViT unfrozen, the gap to specialized is only 0.27.6 points.
  4. Two epochs at 2x/3x/5x; one epoch at 9x (overfits faster with bigger rank).

What did not work

Experiment Outcome
Full FT (lr 1e-5) at 5x/9x grad_norm 10+, output collapses (empty strings, 0000 artifacts). 9x fell to 0.176, below base 0.180. LoRA's implicit regularization wins.
Pre-upscaling 9x → original dim before ViT Gives big accuracy boost (EM 0.78) but inflates visual tokens ~9×, defeating the compression use case. Rejected.
LoRA dropout 0.1 at 9x No change in ceiling (~0.27).
More epochs (3+) at 5x 0.620 at 3ep < 0.634 at 2ep. Overfit.
Rank 256 at 5x 0.656 < 0.730 at r=128. Capacity saturated at r=128 for 5x.
Think / CoT training (30k GPT-generated reasoning traces, <think>reasoning</think>answer targets) 9x peak 0.286 < non-think 0.378. Traces generated blindly by GPT (no image) produce plausible-but-wrong reasoning at 9x where the image is unreadable. Model learns to fabricate confident reasoning that doesn't help the final answer.

Compression-damage vs SFT-recovery

0x : ██████████████████████████████████████████ 0.958 (ceiling)
2x : ███████████████████████████████████████▏    0.904 → SFT 0.948  (1.0 gap, 81% recovered)
3x : ████████████████████████████████████        0.826 → SFT 0.894  (6.4 gap, 52% recovered)
5x : ████████████████████████                    0.554 → SFT 0.730  (22.8 gap, 44% recovered)
9x : ███████▌                                    0.180 → SFT 0.378  (58.0 gap, 25% recovered)

SFT recovery at 2x/3x: 50-80%. At 5x/9x: 25-44% — residual gap is the physical compression limit the ViT can't undo.

Full experiment matrix

2x

run r ep lr ViT LLM-judge peak
base 0.904
v1 (LLM-only) 128 2 2e-5 frozen 0.948
llmvit-v1 128 2 2e-5 trained 0.948 (tied)

3x

run r ep lr ViT peak
base 0.826
v1 32 1 1e-5 frozen 0.854
v2 128 2 2e-5 frozen 0.878
v3 256 2 2e-5 frozen 0.882
llmvit-v1 256 2 2e-5 trained 0.884 (then collapsed)
llmvit-v2 256 1 1e-5 trained 0.894

5x

run r ep lr ViT peak
base 0.554
v1 32 1 1e-5 frozen 0.598
v2 32 2 2e-5 frozen 0.628
v3 128 2 3e-5 frozen 0.634
v4 256 2 2e-5 frozen 0.620
v5 128 3 3e-5 frozen 0.620
fullft 1 1e-5 n/a 0.562 (bad)
llmvit-v1 128 2 3e-5 trained 0.730
llmvit-v2 256 2 2e-5 trained 0.656

9x

run r ep lr ViT peak
base 0.180
v1 32 1 1e-5 frozen 0.228
v2 128 2 3e-5 frozen 0.252
v3 256 1 2e-5 frozen 0.264
v4 512 1 1.5e-5 frozen 0.272
v5 256 2 1e-5 dropout 0.1 frozen 0.266
fullft 1 1e-5 n/a 0.176 (broken)
llmvit-v1 256 1 2e-5 trained 0.354
llmvit-v2 512 2 1e-5 trained 0.378
think-v1 256 2 1e-5 trained (+think) 0.286 (worse)

Universal (one adapter for all compressions)

Trained on concatenated 2x+3x+5x+9x data (416k examples, 1 epoch).

run r lr ViT 2x 3x 5x 9x
v1 128 2e-5 frozen 0.924 0.862 0.620 0.250
llmvit-v1 128 2e-5 trained 0.920 0.844 0.664 0.272
llmvit-v2 256 1e-5 trained 0.940 0.892 0.692 0.302

Shipped artifacts (HuggingFace)

Five LoRA adapters pushed to Chrisyichuan:

  • qwen3vl-4b-wiki-screenshot-2x-lora — 0.948 at 2x
  • qwen3vl-4b-wiki-screenshot-3x-lora — 0.894 at 3x
  • qwen3vl-4b-wiki-screenshot-5x-lora — 0.730 at 5x
  • qwen3vl-4b-wiki-screenshot-9x-lora — 0.378 at 9x
  • qwen3vl-4b-wiki-screenshot-universal-lora — one LoRA, 0.940/0.892/0.692/0.302 across all four compressions

Practical recommendation

For most deployments, ship the universal adapter: 1.28 GB LoRA, one merge at load time, handles any of 2x/3x/5x/9x with near-specialized accuracy (within 0.27.6 LLM-judge points). If you know you'll only ever serve a specific compression level, use the specialized adapter for that level.

For 2x/3x the adapters are essentially production-ready (<10 point drop from uncompressed). 5x is usable when accuracy tolerance is moderate. 9x remains difficult — 0.378 is still far from the 0.958 ceiling, and this is dominated by the physical unreadability of 9×-compressed text rather than a capacity bottleneck we can solve with more training.

Files