xingxm commited on
Commit
d72282d
·
verified ·
1 Parent(s): d9e81c6

Add designcoder_evaluator_qwen3.5_9b_adamw_bs256_data37847_step100/README.md

Browse files
designcoder_evaluator_qwen3.5_9b_adamw_bs256_data37847_step100/README.md ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: other
3
+ base_model: Qwen/Qwen3.5-9B
4
+ tags:
5
+ - designcoder
6
+ - evaluator
7
+ - reward-model
8
+ - ui-evaluation
9
+ - multimodal
10
+ - image-text-to-text
11
+ language:
12
+ - en
13
+ pipeline_tag: image-text-to-text
14
+ ---
15
+
16
+ # DesignCoder Evaluator · Qwen3.5-9B · AdamW · bs256 · data37847 · step100
17
+
18
+ > **This is an evaluator, not a generation model.**
19
+ > Most checkpoints in this repository *write* HTML/CSS/JS. This one *scores* the
20
+ > rendered result: given a UI screenshot and a rubric it returns structured
21
+ > per-criterion verdicts, for use as a reward / judge model during rollout.
22
+
23
+ The lightweight companion to
24
+ [`designcoder_evaluator_qwen3.8_27b_adamw_bs256_data37847_step296`](../designcoder_evaluator_qwen3.8_27b_adamw_bs256_data37847_step296).
25
+ Roughly **1/3 the memory and 2× the speed** of the 27B, with equal or better
26
+ agreement — **provided the rubric has exactly 10 items** (see Limitations).
27
+
28
+ ## Task
29
+
30
+ Input: one full-page UI screenshot + `<surface>` (landing | dashboard) +
31
+ `<generation_brief>` + `<frozen_rubric>`.
32
+ Output: `<think>` reasoning followed by strict JSON with `dynamic_scores` (5 × 0/1/2),
33
+ `static_scores` (34 dashboard / 35 landing), `interaction_rubrics` (free text) and
34
+ `frozen_dynamic_scores.verdicts` (binary per rubric item).
35
+
36
+ ## Training
37
+
38
+ | | |
39
+ |---|---|
40
+ | Base model | Qwen3.5-9B (`Qwen3_5ForConditionalGeneration`) |
41
+ | Dataset | **DesignCoder-evaluate**, 37,847 filtered samples |
42
+ | Optimizer | AdamW, lr 1e-5, cosine, warmup 0.1, wd 0.0 |
43
+ | Precision / parallel | bf16 + DeepSpeed ZeRO-3, 8 nodes × 8 H20 = 64 GPUs |
44
+ | Global batch | 256 (per_device 2 × grad_accum 2 × 64) |
45
+ | **Exported step** | **100 / 296** (0.68 epoch), loss 0.4956 |
46
+ | Full-run final loss | 0.4358 @ step 296 |
47
+ | Frozen | vision tower (LM + projector fully tuned) |
48
+
49
+ > ⚠️ `data37847` is the **DesignCoder-evaluate** dataset (UI screenshot scoring).
50
+ > It is **not** the generation dataset behind the `data37865` / `data41287`
51
+ > checkpoints — the similar sample count is a coincidence.
52
+
53
+ ### Why step 100 and not the final checkpoint
54
+
55
+ All six checkpoints (50/100/150/200/250/296) were evaluated on a purpose-built
56
+ **out-of-distribution suite** (576 prompts: rubric lengths 5/10/15/20/25 and three
57
+ dimension-grouping shapes). Step 100 wins on OOD agreement, and the final checkpoint
58
+ is measurably worse:
59
+
60
+ | Step | OOD κ | In-dist κ | Overfit gap | Train loss |
61
+ |---:|---:|---:|---:|---:|
62
+ | 50 | 0.6568 | 0.6610 | +0.004 | – |
63
+ | **100** | **0.6943** | 0.7010 | +0.007 | 0.4956 |
64
+ | 150 | 0.6730 | 0.6908 | +0.018 | – |
65
+ | 200 | 0.6649 | 0.6758 | +0.011 | – |
66
+ | 250 | 0.6765 | 0.6571 | −0.019 | – |
67
+ | 296 (final) | 0.6501 | 0.6772 | +0.027 | 0.4358 |
68
+
69
+ This mirrors the pattern documented for the generation runs in this repo — loss keeps
70
+ falling while held-out quality degrades — except the optimum arrives **earlier**
71
+ (~34% of training vs ~75% for generation).
72
+
73
+ ### Optimizer ablation
74
+
75
+ A sibling 9B run using **Muon + `pure_bf16`** (otherwise identical data, batch, and
76
+ steps) failed badly, which is why this run uses AdamW:
77
+
78
+ | | Muon + pure_bf16 | **AdamW + ZeRO-3** |
79
+ |---|---:|---:|
80
+ | Final train loss | 0.7505 | **0.4358** |
81
+ | Truncated generations | 14 / 389 | **0 / 389** |
82
+ | Cohen's κ (25-item rubric) | 0.4831 | **0.6353** |
83
+ | Reproduces known quality ordering | ❌ inverted | ✅ correct |
84
+
85
+ The Muon arm stalled early (loss 1.376 → 1.138 over the first 50 steps) and degenerated
86
+ into repetition loops that ran into the 8192-token cap. Do not use Muon + `pure_bf16`
87
+ for this task.
88
+
89
+ ## Evaluation
90
+
91
+ The dataset has no test split, so evaluation uses **held-out inputs**: 239 generated-page
92
+ screenshots already scored check-by-check by a stronger external vision judge
93
+ (md5 check: **0 overlap** with the 37,851 training images).
94
+
95
+ | Metric | 25-item rubric (OOD) | 10-item rubric (in-dist) |
96
+ |---|---:|---:|
97
+ | JSON parse rate | 1.0000 | 1.0000 |
98
+ | Verdict-count exact match | 0.6276 | **1.0000** |
99
+ | Per-check agreement | 0.9226 | 0.9343 |
100
+ | Cohen's κ | 0.6353 | 0.6560 |
101
+ | Defect recall | 0.6581 | 0.6730 |
102
+ | Defect precision | 0.7018 | 0.7137 |
103
+ | Case-level Pearson | 0.8417 | 0.7809 |
104
+
105
+ > Numbers above are for **step 296** (measured before checkpoint selection).
106
+ > Step 100 — the weights published here — scores **higher** on the OOD suite
107
+ > (κ 0.6943 vs 0.6501); the table is retained as a conservative reference.
108
+
109
+ ### Reproducing the known quality ordering
110
+
111
+ External judge on three generation checkpoints: 4B `0.833` < 9B `0.884` < 27B `0.905`
112
+
113
+ | Scorer | 4B | 9B | 27B | Ordering |
114
+ |---|---:|---:|---:|---|
115
+ | This model (9B AdamW) | 0.855 | **0.886** | 0.910 | ✅ correct |
116
+ | 27B evaluator | 0.840 | 0.893 | 0.909 | ✅ correct |
117
+ | 9B Muon evaluator | 0.896 | 0.940 | 0.933 | ❌ inverted |
118
+
119
+ ## Usage
120
+
121
+ ```python
122
+ from transformers import AutoModelForImageTextToText, AutoProcessor, AutoTokenizer
123
+ from PIL import Image
124
+ import torch, math
125
+
126
+ MODEL = "xingxm/DesignCoder"
127
+ SUB = "designcoder_evaluator_qwen3.5_9b_adamw_bs256_data37847_step100"
128
+
129
+ tok = AutoTokenizer.from_pretrained(MODEL, subfolder=SUB, trust_remote_code=True)
130
+ proc = AutoProcessor.from_pretrained(MODEL, subfolder=SUB, trust_remote_code=True)
131
+ model = AutoModelForImageTextToText.from_pretrained(
132
+ MODEL, subfolder=SUB, dtype=torch.bfloat16, device_map="cuda", trust_remote_code=True
133
+ )
134
+
135
+ img = Image.open("screenshot.png").convert("RGB")
136
+ w, h = img.size # match training preprocessing
137
+ if w * h > 1048576:
138
+ s = math.sqrt(1048576 / (w * h))
139
+ img = img.resize((int(w * s), int(h * s)))
140
+
141
+ user = (
142
+ "<image>\n"
143
+ "Evaluate the attached UI screenshot using the selected visual criteria.\n\n"
144
+ "<surface>\nlanding\n</surface>\n\n"
145
+ "<generation_brief>\n...brief...\n</generation_brief>\n\n"
146
+ '<frozen_rubric>\n{"rubric":{"Alignment":["..."],"Layout":["..."]}}\n</frozen_rubric>'
147
+ )
148
+ text = tok.apply_chat_template(
149
+ [{"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user}],
150
+ tokenize=False, add_generation_prompt=True,
151
+ ).replace("<image>", "<|vision_start|><|image_pad|><|vision_end|>")
152
+
153
+ inputs = proc(text=[text], images=[img], return_tensors="pt").to("cuda")
154
+ out = model.generate(**inputs, max_new_tokens=4096, do_sample=False)
155
+ print(tok.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True))
156
+ ```
157
+
158
+ `SYSTEM_PROMPT` must be the one shipped with the DesignCoder-evaluate dataset. The chat
159
+ template already ends with `<think>\n`, so generated text continues *inside* the reasoning
160
+ block and carries no opening `<think>` tag.
161
+
162
+ ## Limitations
163
+
164
+ - **Feed it exactly 10 rubric items.** Every training rubric has exactly 10 (fixed shape
165
+ `Alignment:2, Layout:2, Typography:2, Components:2, Assets:1, Aesthetics:1`, 100% of
166
+ 37,847 records). Off-length rubrics degrade verdict counting badly — the dimension names
167
+ stay correct but the per-dimension item count drifts, typically by ±1:
168
+
169
+ | Rubric length | 5 | 10 | 15 | 20 | 25 |
170
+ |---|---:|---:|---:|---:|---:|
171
+ | Count-match (this model) | 0.00 | **1.00** | 0.92 | 0.94 | 0.85 |
172
+ | Count-match (27B) | 1.00 | 1.00 | 0.99 | 1.00 | 1.00 |
173
+
174
+ **If your rubric length varies, use the 27B instead** — that is its main advantage,
175
+ not raw judgement quality (OOD κ is actually 0.694 here vs 0.664 for the 27B).
176
+ - **`summary` miscounts.** `frozen_dynamic_scores.summary` is rarely self-consistent
177
+ (e.g. `"24/24"` alongside 25 verdicts). Parse the `verdicts` array; ignore `summary`.
178
+ - **`static_scores` / `dynamic_scores` unvalidated on unseen data.** No ground truth exists
179
+ for those 40 items outside the training distribution; they were only checked on a
180
+ contaminated training sample (static exact-match 0.9135, dynamic 0.8400).
181
+ - Evidence is screenshot-only; the model cannot verify interaction behaviour, yet
182
+ `interaction_rubrics` asks it to describe interactions — treat that field as speculative.