CoolFace
Modelpublic

riazmo/design-critic-vlm-3b-lora

sourceHugging Facecc-by-nc-4.0updated 2mo agoView on Hugging Face
0likes5downloads
Model Card

Design/UX Critic VLM — Qwen2.5-VL-3B + LoRA (web, v2 / Run 3)

A specialist LoRA adapter that turns Qwen2.5-VL-3B into an on-device UX design critic: given a single UI screenshot, it emits a structured JSON critique (Nielsen heuristics + accessibility, each finding with verdict / severity / evidence / recommendation).

It runs on a laptop, offline, for free. Scoped to web UIs — see Limitations.

Built by a designer as a learning project. The honest write-up (including the two times it failed completely) is the real story — see Provenance below.

TL;DR — what the fine-tune buys you

Same base, same prompt, same images. Measured on 34 hand-built gold screenshots (flag precision/recall on warn/fail findings — not naive agreement, which a "everything's fine" model games).

Modelvalid JSONflag recallflag precisionF1
Base (Qwen2.5-VL-3B, no adapter)0%0%0%0%
+ this adapter (Run 3 + sanitizer)67.6%29%100%45%

The untuned base cannot emit a single valid critique. This adapter — ~37M trainable params (~1.2% of the model), 142 MB — is the entire difference.

By slice (read this, not the overall):

SliceF1
desktop web87.5%ship this
mobile web57.1%usable
native android / iOS0%out of scope

Overall recall looks low only because half the gold is native, where the adapter is silent by design. On web it's strong, with 100% precision — zero false alarms.

Usage

python
import torch
from transformers import AutoModelForImageTextToText, AutoProcessor
from peft import PeftModel

base = "Qwen/Qwen2.5-VL-3B-Instruct"
model = AutoModelForImageTextToText.from_pretrained(base, torch_dtype="auto", device_map={"": "mps"})
model = PeftModel.from_pretrained(model, "riazmo/design-critic-vlm-3b-lora")  # this repo
processor = AutoProcessor.from_pretrained(base)

messages = [{"role": "user", "content": [
    {"type": "image", "image": "screenshot.png"},
    {"type": "text",  "text": INSTRUCTION},   # the v2 rubric instruction (see repo)
]}]
# → generate, then run the JSON sanitizer (drops hallucinated criterion IDs, clamps enums) before parsing.

An inference sanitizer is part of the ship path: it repairs the 3B's occasional malformed output so what you measure is what ships. Without it, valid-JSON rate drops ~20 points.

Limitations (please read)

  • —Web only. On native mobile-app screenshots it emits valid JSON but flags nothing — it was not trained on enough bad native examples. Treat any native output as unverified.
  • —Single screenshot. No flow/sequence reasoning; no interaction or scroll state.
  • —Static, estimated evidence. Contrast etc. are judged by eye, not measured — not a WCAG compliance certifier.
  • —Conservative. Misses ~half of real issues on harder web cases. A first-pass assistant, not a sign-off. Small gold set (n=34) — treat percentages as directional.

Method

Knowledge distillation, not prompt tricks. The base 3B rubber-stamped everything (0% recall) because real production screens are ~mostly well-designed. The fix was to relabel the training corpus with a retrieval-augmented stronger teacher (Qwen3-VL-32B + ~6 similar expert critiques; verified 71% recall with no leakage), then distill onto the 3B. QLoRA, rank 16, α 32, vision tower frozen.

Training data & license

Distilled from a corpus of public web UIs, government sites, and design references, plus research datasets. Because that mix includes research-only / portfolio-only sources, this adapter is released `cc-by-nc-4.0` (non-commercial, research/learning use). Do not use commercially. The base model is subject to its own (Qwen) license.

Provenance / write-up

Full engineering story — the failures, the ceiling-check breakthrough, the metric traps — in the accompanying article. This is a learning artifact, published to show the process honestly, not a production model.