ysharma HF Staff commited on
Commit
88c08a8
·
verified ·
1 Parent(s): 01e29bd

Rewriter Arena: 9B vs 0.8B vs 2B rewriters side by side

Browse files
Files changed (5) hide show
  1. README.md +32 -7
  2. app.py +202 -0
  3. requirements.txt +7 -0
  4. rewriters.py +160 -0
  5. sheets.json +282 -0
README.md CHANGED
@@ -1,13 +1,38 @@
1
  ---
2
- title: Image21 Rewriter Arena
3
- emoji: 🔥
4
- colorFrom: pink
5
- colorTo: gray
6
  sdk: gradio
7
  sdk_version: 6.28.0
8
- python_version: '3.12'
9
  app_file: app.py
10
- pinned: false
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Qwen-Image 2.1 Rewriter Arena
3
+ emoji: ⚖️
4
+ colorFrom: indigo
5
+ colorTo: pink
6
  sdk: gradio
7
  sdk_version: 6.28.0
8
+ python_version: "3.12"
9
  app_file: app.py
10
+ startup_duration_timeout: 1h
11
+ short_description: 9B vs 0.8B vs 2B prompt rewriters, rendered side by side
12
+ license: other
13
+ license_name: qwen-research
14
+ models:
15
+ - Qwen/Qwen-Image-2.1
16
+ - Qwen/Qwen-Image-2.1-PE-T2I
17
+ - ysharma/image21-pocket-rewriter-0.8B
18
+ - ysharma/image21-pocket-rewriter-2B
19
+ datasets:
20
+ - ysharma/image21-rewriter-distill
21
+ tags:
22
+ - text-to-image
23
+ - prompt-rewriting
24
+ - distillation
25
+ - benchmark
26
  ---
27
 
28
+ # Qwen-Image 2.1 Rewriter Arena
29
+
30
+ One request goes through the official 9B prompt rewriter
31
+ ([Qwen/Qwen-Image-2.1-PE-T2I](https://huggingface.co/Qwen/Qwen-Image-2.1-PE-T2I)), the two pocket distillations
32
+ ([0.8B](https://huggingface.co/ysharma/image21-pocket-rewriter-0.8B), [2B](https://huggingface.co/ysharma/image21-pocket-rewriter-2B)),
33
+ and optionally no rewriter at all. Every rewrite is rendered by [Qwen-Image 2.1](https://huggingface.co/Qwen/Qwen-Image-2.1) with a
34
+ shared seed and shown in a grid with its prompt, token count and rewrite time. A second tab shows the 40-request evaluation
35
+ contact sheets from [ysharma/image21-rewriter-distill](https://huggingface.co/datasets/ysharma/image21-rewriter-distill)
36
+ without using any GPU.
37
+
38
+ Built with Qwen. All models are under the Qwen Research License (non-commercial research use).
app.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import spaces # must be imported before torch on ZeroGPU
2
+
3
+ import json
4
+ import os
5
+ import random
6
+
7
+ import gradio as gr
8
+ import torch
9
+ from diffusers import QwenImage21Pipeline
10
+
11
+ from rewriters import STUDENT_IDS, TEACHER_ID, load_student, load_teacher, size_for, student_rewrite, teacher_rewrite
12
+
13
+ IMAGE_ID = "Qwen/Qwen-Image-2.1"
14
+ DATASET_ID = "ysharma/image21-rewriter-distill"
15
+ MAX_SEED = 2**31 - 1
16
+ ARMS = ["Raw request (no rewriter)", "Official 9B rewriter", "0.8B pocket rewriter", "2B pocket rewriter"]
17
+ DEFAULT_ARMS = ARMS[1:]
18
+ HERE = os.path.dirname(os.path.abspath(__file__))
19
+
20
+ with open(os.path.join(HERE, "sheets.json"), encoding="utf-8") as f:
21
+ SHEETS = json.load(f) # [{"id","request","lang","category","sheet_url"}]
22
+ SHEET_CHOICES = [(f"[{s['lang']}] {s['request'][:90]}", s["id"]) for s in SHEETS]
23
+ OVERVIEW_URL = f"https://huggingface.co/datasets/{DATASET_ID}/resolve/main/renders/sheets/sheet_overview.png"
24
+
25
+ # ----------------------------------------------------------------- models (module scope, eager)
26
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
27
+ pipe = QwenImage21Pipeline.from_pretrained(IMAGE_ID, dtype=torch.bfloat16).to(device)
28
+ teacher_tok, teacher, teacher_system = load_teacher(device)
29
+ students = {size: load_student(size, device) for size in STUDENT_IDS}
30
+
31
+
32
+ def est_seconds(arms, steps, megapixels, teacher_tokens) -> int:
33
+ t = 10
34
+ if ARMS[1] in arms:
35
+ t += 20 + int(teacher_tokens) * 0.035 # 9B with thinking
36
+ t += 8 * sum(a in arms for a in ARMS[2:]) # students
37
+ t += len(arms) * (10 + int(steps) * 0.6 * float(megapixels))
38
+ return int(t)
39
+
40
+
41
+ def compare_duration(request, arms=DEFAULT_ARMS, steps=28, megapixels=1.0, seed=0, randomize_seed=True,
42
+ teacher_tokens=3072, *a, **k):
43
+ return est_seconds(arms, steps, megapixels, teacher_tokens)
44
+
45
+
46
+ def _rewrite(arm, request, seed, teacher_tokens):
47
+ if arm == ARMS[0]:
48
+ return {"prompt": request, "ratio": "3:2", "parse_ok": True, "tokens": 0, "seconds": 0.0, "thinking": ""}
49
+ if arm == ARMS[1]:
50
+ return teacher_rewrite(teacher_tok, teacher, teacher_system, request, seed=seed, max_new_tokens=int(teacher_tokens))
51
+ tok, model = students["0.8B" if arm == ARMS[2] else "2B"]
52
+ return student_rewrite(tok, model, request, seed=seed)
53
+
54
+
55
+ def _stats(arm, r, w=None, h=None):
56
+ if arm == ARMS[0]:
57
+ s = "No rewrite."
58
+ else:
59
+ s = f"{r['tokens']} tokens in {r['seconds']:.1f} s"
60
+ if r.get("thinking"):
61
+ s += f" (thinking included)"
62
+ s += f". Ratio {r['ratio']}."
63
+ if not r["parse_ok"]:
64
+ s += " Not valid JSON; raw text used."
65
+ if w:
66
+ s += f" Rendered {w}x{h}."
67
+ return s
68
+
69
+
70
+ @spaces.GPU(size="xlarge", duration=compare_duration)
71
+ def compare(request: str, arms: list = DEFAULT_ARMS, steps: int = 28, megapixels: float = 1.0, seed: int = 0,
72
+ randomize_seed: bool = True, teacher_tokens: int = 3072):
73
+ """Rewrite one request with each selected rewriter, then render every rewrite with Qwen-Image 2.1 at the same seed.
74
+ Yields progressively: prompts first, then one image per arm."""
75
+ request = (request or "").strip()
76
+ if not request:
77
+ raise gr.Error("Type an image request first.")
78
+ if not arms:
79
+ raise gr.Error("Select at least one arm.")
80
+ if randomize_seed:
81
+ seed = random.randint(0, MAX_SEED)
82
+ seed = int(seed)
83
+ cols = {a: {"image": None, "prompt": "", "stats": "waiting" if a in arms else "not selected"} for a in ARMS}
84
+
85
+ def emit(msg):
86
+ return tuple([cols[a]["image"] for a in ARMS] + [cols[a]["prompt"] for a in ARMS]
87
+ + [cols[a]["stats"] for a in ARMS] + [seed, msg])
88
+
89
+ rewrites = {}
90
+ for a in ARMS:
91
+ if a not in arms:
92
+ continue
93
+ cols[a]["stats"] = "rewriting..."
94
+ yield emit(f"Rewriting with {a}...")
95
+ r = _rewrite(a, request, seed, teacher_tokens)
96
+ rewrites[a] = r
97
+ cols[a]["prompt"], cols[a]["stats"] = r["prompt"], _stats(a, r)
98
+ yield emit(f"Rewrote with {a}.")
99
+ for a in ARMS:
100
+ if a not in arms:
101
+ continue
102
+ cols[a]["stats"] += " Rendering..."
103
+ yield emit(f"Rendering {a}...")
104
+ r = rewrites[a]
105
+ w, h = size_for(r["ratio"], megapixels)
106
+ image = pipe(prompt=r["prompt"], width=w, height=h, num_inference_steps=int(steps),
107
+ generator=torch.Generator(device="cuda").manual_seed(seed)).images[0]
108
+ cols[a]["image"], cols[a]["stats"] = image, _stats(a, r, w, h)
109
+ yield emit(f"Rendered {a}.")
110
+ yield emit(f"Done. Seed {seed}, {int(steps)} steps, about {megapixels} megapixels per image.")
111
+
112
+
113
+ def show_sheet(sheet_id):
114
+ s = next((x for x in SHEETS if x["id"] == sheet_id), None)
115
+ if not s:
116
+ return None, ""
117
+ return s["sheet_url"], f"**Request ({s['lang']}, {s['category']}):** {s['request']}"
118
+
119
+
120
+ # ----------------------------------------------------------------- UI
121
+ CSS = """
122
+ #col-container { max-width: 1500px; margin: 0 auto; }
123
+ .dark .gradio-container { color: var(--body-text-color); }
124
+ """
125
+
126
+ INTRO = f"""
127
+ # ⚖️ Qwen-Image 2.1 Rewriter Arena
128
+ One request, several prompt rewriters, same seed, side by side. Compare the **official 9B rewriter**
129
+ ([{TEACHER_ID}](https://huggingface.co/{TEACHER_ID}): 1,700-word system prompt, thinking on, about 1,600 tokens per rewrite) against
130
+ its two pocket distillations, [0.8B](https://huggingface.co/{STUDENT_IDS['0.8B']}) and [2B](https://huggingface.co/{STUDENT_IDS['2B']})
131
+ (no system prompt, no thinking, about 450 tokens), and the raw request with no rewriting. Each rewrite is rendered by
132
+ [Qwen-Image 2.1](https://huggingface.co/{IMAGE_ID}) at the ratio that rewriter chose.
133
+
134
+ A live run with the 9B arm takes 2 to 3 minutes of GPU time. The **Pre-rendered comparisons** tab shows the 40-request evaluation
135
+ grid at no cost. Research demo under the Qwen Research License (non-commercial). Built with Qwen.
136
+ """
137
+
138
+ ABOUT = """
139
+ **What the 40-request evaluation found.** Rendered-text OCR word accuracy: raw request 0.46, official 9B rewriter 0.80,
140
+ 0.8B student 0.57, 2B student 0.55. The students keep roughly a quarter to a third of the teacher's gain, close to the teacher on Latin
141
+ text and further behind on Chinese and Japanese. A pairwise vision-model judge could not separate the arms.
142
+ On text-level checks over 300 held-out requests the students match the teacher on valid JSON and allowed ratios (99 to 100%) and on
143
+ keeping the user's quoted text (53% and 60% versus 53%), at about 28% of the tokens.
144
+
145
+ **How to read a live run.** Look for the quoted text in each image, whether the ratio suits the subject, and whether the rewrite
146
+ invented things the request did not ask for. The seed is shared, but each arm renders at its own ratio, so compositions differ.
147
+
148
+ **Fairness notes.** The teacher runs with its official protocol: system prompt, thinking, presence penalty 1.5, sampling at
149
+ temperature 1.0. The token cap for the teacher is adjustable; its mean is about 1,600 and a few requests need more. The students use the
150
+ same sampling with no system prompt. All arms use 28 steps here for speed; the evaluation used 40.
151
+
152
+ Data, predictions and all 41 contact sheets: [ysharma/image21-rewriter-distill](https://huggingface.co/datasets/ysharma/image21-rewriter-distill).
153
+ The students were trained end to end by ML Intern in HuggingChat for about 16 USD.
154
+ """
155
+
156
+ EXAMPLES = [[s["request"]] for s in SHEETS[:12]]
157
+
158
+ with gr.Blocks(title="Qwen-Image 2.1 Rewriter Arena") as demo:
159
+ with gr.Column(elem_id="col-container"):
160
+ gr.Markdown(INTRO)
161
+ with gr.Tabs():
162
+ with gr.Tab("Live comparison"):
163
+ with gr.Row():
164
+ request = gr.Textbox(label="Your request (any language)", lines=2, scale=4,
165
+ placeholder='a man at a bus stop "next bus 15 min"')
166
+ run = gr.Button("Compare", variant="primary", scale=1)
167
+ arms = gr.CheckboxGroup(ARMS, value=DEFAULT_ARMS, label="Arms to run")
168
+ with gr.Accordion("Settings", open=False):
169
+ steps = gr.Slider(8, 50, value=28, step=1, label="Steps")
170
+ megapixels = gr.Slider(0.5, 1.5, value=1.0, step=0.25, label="Megapixels per image")
171
+ teacher_tokens = gr.Slider(1024, 6144, value=3072, step=256, label="Token cap for the 9B rewriter",
172
+ info="Its rewrite plus thinking averages about 1,600 tokens")
173
+ seed = gr.Slider(0, MAX_SEED, value=0, step=1, label="Seed (shared by all arms)")
174
+ randomize_seed = gr.Checkbox(True, label="Randomize seed")
175
+ status = gr.Markdown("")
176
+ images, prompts, stats = [], [], []
177
+ with gr.Row():
178
+ for a in ARMS:
179
+ with gr.Column():
180
+ images.append(gr.Image(label=a, format="png", image_mode="RGBA", height=360))
181
+ stats.append(gr.Markdown(""))
182
+ prompts.append(gr.Textbox(label="Prompt sent to the image model", lines=6, interactive=False))
183
+ gr.Examples(examples=EXAMPLES, inputs=[request], label="Held-out evaluation requests (fills the box; press Compare)",
184
+ cache_examples=False)
185
+ gr.on([run.click, request.submit], compare,
186
+ [request, arms, steps, megapixels, seed, randomize_seed, teacher_tokens],
187
+ images + prompts + stats + [seed, status], api_name="compare")
188
+ with gr.Tab("Pre-rendered comparisons (no GPU)"):
189
+ gr.Markdown("The 40 evaluation requests rendered at 40 steps, seed 0, with the raw request, the 9B teacher and both "
190
+ "students. Columns follow the sheet's own header.")
191
+ sheet_pick = gr.Dropdown(SHEET_CHOICES, value=SHEET_CHOICES[0][1], label="Request")
192
+ sheet_caption = gr.Markdown("")
193
+ sheet_img = gr.Image(label="Contact sheet", height=760)
194
+ sheet_pick.change(show_sheet, sheet_pick, [sheet_img, sheet_caption], api_visibility="private")
195
+ demo.load(show_sheet, sheet_pick, [sheet_img, sheet_caption], api_visibility="private")
196
+ with gr.Accordion("Overview sheet (all 40 requests)", open=False):
197
+ gr.Image(value=OVERVIEW_URL, label="Overview", height=900)
198
+ with gr.Accordion("About the numbers and fairness", open=False):
199
+ gr.Markdown(ABOUT)
200
+
201
+ if __name__ == "__main__":
202
+ demo.queue(max_size=20).launch(theme=gr.themes.Citrus(), css=CSS)
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ diffusers @ git+https://github.com/huggingface/diffusers.git
2
+ transformers>=5.17.0
3
+ accelerate
4
+ torchvision
5
+ safetensors
6
+ pillow
7
+ json_repair
rewriters.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Prompt rewriters for Qwen-Image 2.1: the official 9B teacher and the two pocket students.
2
+
3
+ Teacher protocol follows QwenLM/Qwen-Image-2.1 prompt_rewrite/ (system prompt, thinking on,
4
+ presence penalty 1.5, JSON answer parsed last-span-first). Student protocol follows the
5
+ model cards: raw request as the only user turn, no system prompt, thinking disabled.
6
+ """
7
+ import json
8
+ import time
9
+
10
+ import torch
11
+ from huggingface_hub import hf_hub_download
12
+ from transformers import AutoModelForCausalLM, AutoTokenizer, LogitsProcessor, LogitsProcessorList
13
+
14
+ try:
15
+ import json_repair
16
+ except ImportError: # pragma: no cover
17
+ json_repair = None
18
+
19
+ TEACHER_ID = "Qwen/Qwen-Image-2.1-PE-T2I"
20
+ STUDENT_IDS = {
21
+ "0.8B": "ysharma/image21-pocket-rewriter-0.8B",
22
+ "2B": "ysharma/image21-pocket-rewriter-2B",
23
+ }
24
+
25
+ # ~1 megapixel sizes per ratio, multiples of 32 (same table as the distillation image eval).
26
+ RATIO_SIZES = {
27
+ "1:1": (1024, 1024), "3:2": (1248, 832), "2:3": (832, 1248),
28
+ "16:9": (1376, 768), "9:16": (768, 1376), "4:3": (1184, 896),
29
+ "3:4": (896, 1184), "2:1": (1472, 736), "1:2": (736, 1472),
30
+ "21:9": (1568, 672), "9:21": (672, 1568), "4:5": (928, 1152),
31
+ "5:4": (1152, 928), "3:1": (1728, 576), "1:3": (576, 1760),
32
+ }
33
+ DEFAULT_RATIO = "3:2"
34
+ SAMPLING = dict(do_sample=True, temperature=1.0, top_p=0.95, top_k=20)
35
+
36
+
37
+ def size_for(ratio: str, megapixels: float = 1.0) -> tuple[int, int]:
38
+ """(width, height) for a ratio, scaled to about `megapixels`, multiples of 32."""
39
+ w, h = RATIO_SIZES.get(ratio, RATIO_SIZES[DEFAULT_RATIO])
40
+ s = (megapixels * 1_000_000 / (w * h)) ** 0.5
41
+ return max(256, int(w * s) // 32 * 32), max(256, int(h * s) // 32 * 32)
42
+
43
+
44
+ # ----------------------------------------------------------------------------- parsing (from pe_core.py)
45
+ def split_thinking(text: str) -> tuple[str, str]:
46
+ if "</think>" in text:
47
+ think, _, answer = text.partition("</think>")
48
+ if "<think>" in think:
49
+ think = think.partition("<think>")[2]
50
+ return think.strip(), answer.strip()
51
+ if "<think>" in text:
52
+ return text.partition("<think>")[2].strip(), ""
53
+ return "", text.strip()
54
+
55
+
56
+ def _balanced_spans(answer: str) -> list[str]:
57
+ spans, depth, start = [], 0, -1
58
+ for i, ch in enumerate(answer):
59
+ if ch == "{":
60
+ if depth == 0:
61
+ start = i
62
+ depth += 1
63
+ elif ch == "}" and depth:
64
+ depth -= 1
65
+ if depth == 0 and start >= 0:
66
+ spans.append(answer[start:i + 1])
67
+ return spans
68
+
69
+
70
+ def _as_obj(candidate: str):
71
+ try:
72
+ obj = json.loads(candidate)
73
+ except json.JSONDecodeError:
74
+ if json_repair is None:
75
+ return None
76
+ obj = json_repair.repair_json(candidate, return_objects=True)
77
+ if isinstance(obj, list):
78
+ obj = obj[0] if obj else None
79
+ return obj if isinstance(obj, dict) else None
80
+
81
+
82
+ def parse_answer(answer: str) -> dict:
83
+ answer = (answer or "").strip()
84
+ for candidate in reversed(_balanced_spans(answer)):
85
+ obj = _as_obj(candidate)
86
+ if obj is None:
87
+ continue
88
+ rewritten = obj.get("rewritten_prompt") or obj.get("rewrited_prompt")
89
+ if not isinstance(rewritten, str) or not rewritten.strip():
90
+ continue
91
+ ratio = str(obj.get("wh_ratio") or "").strip()
92
+ return {"prompt": rewritten.strip(), "ratio": ratio if ratio in RATIO_SIZES else DEFAULT_RATIO,
93
+ "ratio_raw": ratio, "parse_ok": True}
94
+ return {"prompt": answer, "ratio": DEFAULT_RATIO, "ratio_raw": "", "parse_ok": False}
95
+
96
+
97
+ class PresencePenalty(LogitsProcessor):
98
+ """vLLM-style presence penalty: subtract a constant from every already-generated token."""
99
+
100
+ def __init__(self, penalty: float, prompt_len: int):
101
+ self.penalty, self.prompt_len = penalty, prompt_len
102
+
103
+ def __call__(self, input_ids, scores):
104
+ for b in range(input_ids.shape[0]):
105
+ generated = input_ids[b, self.prompt_len:]
106
+ if generated.numel():
107
+ scores[b, generated.unique()] -= self.penalty
108
+ return scores
109
+
110
+
111
+ # ----------------------------------------------------------------------------- loading
112
+ def load_student(size: str, device):
113
+ rid = STUDENT_IDS[size]
114
+ tok = AutoTokenizer.from_pretrained(rid)
115
+ model = AutoModelForCausalLM.from_pretrained(rid, dtype=torch.bfloat16).to(device).eval()
116
+ return tok, model
117
+
118
+
119
+ def load_teacher(device):
120
+ tok = AutoTokenizer.from_pretrained(TEACHER_ID)
121
+ model = AutoModelForCausalLM.from_pretrained(TEACHER_ID, dtype=torch.bfloat16).to(device).eval()
122
+ system_prompt = open(hf_hub_download(TEACHER_ID, "system_prompt.txt"), encoding="utf-8").read().strip()
123
+ return tok, model, system_prompt
124
+
125
+
126
+ # ----------------------------------------------------------------------------- rewriting (call inside @spaces.GPU)
127
+ @torch.inference_mode()
128
+ def student_rewrite(tok, model, request: str, seed: int = 0, max_new_tokens: int = 1024) -> dict:
129
+ prompt = tok.apply_chat_template([{"role": "user", "content": request}], add_generation_prompt=True, tokenize=False)
130
+ ids = tok(prompt, return_tensors="pt").to(model.device)
131
+ torch.manual_seed(seed)
132
+ t = time.time()
133
+ out = model.generate(**ids, max_new_tokens=max_new_tokens, pad_token_id=tok.pad_token_id or tok.eos_token_id, **SAMPLING)
134
+ gen = out[0, ids["input_ids"].shape[1]:]
135
+ text = tok.decode(gen, skip_special_tokens=True)
136
+ _, answer = split_thinking(text) # the template pre-fills an empty think block; strip it if echoed
137
+ res = parse_answer(answer or text)
138
+ res.update(seconds=time.time() - t, tokens=int(gen.numel()), thinking="", raw=text)
139
+ return res
140
+
141
+
142
+ @torch.inference_mode()
143
+ def teacher_rewrite(tok, model, system_prompt: str, request: str, seed: int = 0, max_new_tokens: int = 3072) -> dict:
144
+ prompt = tok.apply_chat_template(
145
+ [{"role": "system", "content": system_prompt}, {"role": "user", "content": request}],
146
+ add_generation_prompt=True, tokenize=False, enable_thinking=True)
147
+ ids = tok(prompt, return_tensors="pt").to(model.device)
148
+ prompt_len = ids["input_ids"].shape[1]
149
+ torch.manual_seed(seed)
150
+ t = time.time()
151
+ out = model.generate(**ids, max_new_tokens=max_new_tokens, pad_token_id=tok.eos_token_id,
152
+ logits_processor=LogitsProcessorList([PresencePenalty(1.5, prompt_len)]), **SAMPLING)
153
+ gen = out[0, prompt_len:]
154
+ text = tok.decode(gen, skip_special_tokens=True)
155
+ thinking, answer = split_thinking(text)
156
+ res = parse_answer(answer)
157
+ if not answer: # ran out of tokens inside the think block
158
+ res.update(prompt=request, parse_ok=False)
159
+ res.update(seconds=time.time() - t, tokens=int(gen.numel()), thinking=thinking, raw=text)
160
+ return res
sheets.json ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": "r06876",
4
+ "request": "صورة واسعة لمنظر طبيعي في جبال تايبه، مع شمس تشرق، تظهر في الصورة علامة تُكتب: \"الشمس تُشعل الجبال\".",
5
+ "lang": "ar",
6
+ "category": "scene",
7
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r06876.png"
8
+ },
9
+ {
10
+ "id": "r06236",
11
+ "request": "أظهر واجهة تطبيق موبايل عصرية تُظهر تطبيقًا لصحة الأذن، مع خلفية بيضاء وعناصر تفاعلية، ووضع العنوان بوضوح: \"تتبع صحة الأذن بشكل دقيق وسريع\".",
12
+ "lang": "ar",
13
+ "category": "ui_screen",
14
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r06236.png"
15
+ },
16
+ {
17
+ "id": "r02923",
18
+ "request": "生成一个带有“快乐星球”的圆形贴纸图标",
19
+ "lang": "zh",
20
+ "category": "sticker",
21
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r02923.png"
22
+ },
23
+ {
24
+ "id": "r07058",
25
+ "request": "一位老人在公园长椅上阅读,\"今日好书推荐:《平凡的世界》\"",
26
+ "lang": "zh",
27
+ "category": "photo",
28
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r07058.png"
29
+ },
30
+ {
31
+ "id": "r08005",
32
+ "request": "\"प्रोडक्ट के नाम: स्मार्ट वॉटर बॉटल\" के साथ एक फिजिकल प्रोडक्ट का कमरे में शॉट बनाएं",
33
+ "lang": "hi",
34
+ "category": "product_shot",
35
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r08005.png"
36
+ },
37
+ {
38
+ "id": "r02789",
39
+ "request": "「未来の街」を表すシンプルなboldな形のスタンプタイプの絵文字風グラフィックを作成してください。図形は円で、内側に「未来の街」の文字を明確に配置してください。",
40
+ "lang": "ja",
41
+ "category": "sticker",
42
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r02789.png"
43
+ },
44
+ {
45
+ "id": "r08309",
46
+ "request": "リアルな写真に「北海道の冬の風景」を表示してください。雪景色の山々と道の駅の前で、人々が歩いている様子を撮影してください。",
47
+ "lang": "ja",
48
+ "category": "photo",
49
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r08309.png"
50
+ },
51
+ {
52
+ "id": "r00535",
53
+ "request": "「未来の街」をテーマにしたデジタルアート。空が青く、建物はスカイラインで織りなされる。光が差し込み、歩く人々がいる。背景に「未来都市の夢」を表示。",
54
+ "lang": "ja",
55
+ "category": "illustration",
56
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r00535.png"
57
+ },
58
+ {
59
+ "id": "r02651",
60
+ "request": "Affichez l'interface d'un jeu mobile moderne sur un écran, avec en haut une bandeau en lettres blanches sur fond noir : \"Bataille des Étoiles – Mode Épreuve\".",
61
+ "lang": "fr",
62
+ "category": "ui_screen",
63
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r02651.png"
64
+ },
65
+ {
66
+ "id": "r09463",
67
+ "request": "Un produit électronique sur fond blanc avec une affiche en double quotes\"Alimentation rapide 30 minutes\" double quotes.",
68
+ "lang": "fr",
69
+ "category": "product_shot",
70
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r09463.png"
71
+ },
72
+ {
73
+ "id": "r01195",
74
+ "request": "Create a diagram showing the water cycle with \"Evaporation, Condensation, Precipitation\" labeled in green.",
75
+ "lang": "en",
76
+ "category": "infographic",
77
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r01195.png"
78
+ },
79
+ {
80
+ "id": "r09188",
81
+ "request": "A futuristic cyberpunk woman with glowing eyes and neon clothing, standing in a rain-soaked city, wearing a digital tattoo with \"NEON WARDEN\" on her arm.",
82
+ "lang": "en",
83
+ "category": "portrait",
84
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r09188.png"
85
+ },
86
+ {
87
+ "id": "r06943",
88
+ "request": "A breathtaking sunset over a sprawling desert city at dusk, with towering neon skyscrapers and flying drones, 3:2, featuring in the foreground a large holographic sign that reads \"FUTURE SKIES\".",
89
+ "lang": "en",
90
+ "category": "scene",
91
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r06943.png"
92
+ },
93
+ {
94
+ "id": "r01048",
95
+ "request": "A close-up portrait of a young woman with glowing eyes and a cosmic blue aura. \"Starborn Voyager\" the type must be perfectly legible.",
96
+ "lang": "en",
97
+ "category": "portrait",
98
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r01048.png"
99
+ },
100
+ {
101
+ "id": "r07829",
102
+ "request": "A realistic photograph of a bustling street market in Tokyo, with vendors selling fresh seafood and vegetables, showing a sign that reads \"Fresh Sushi & Seafood Daily.\"",
103
+ "lang": "en",
104
+ "category": "photo",
105
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r07829.png"
106
+ },
107
+ {
108
+ "id": "r05306",
109
+ "request": "A sleek wireless earbud on a white background with \"SoundBloom Pro\" keep the lettering crisp and readable.",
110
+ "lang": "en",
111
+ "category": "product_shot",
112
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r05306.png"
113
+ },
114
+ {
115
+ "id": "r00409",
116
+ "request": "A sunset city skyline with \"Future Horizon\" displayed on a digital billboard.",
117
+ "lang": "en",
118
+ "category": "scene",
119
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r00409.png"
120
+ },
121
+ {
122
+ "id": "r02853",
123
+ "request": "A modern mobile app interface with a dark theme, showing a chat window and notifications, with the text \"Active Chat Session\" displayed prominently at the top of the screen.",
124
+ "lang": "en",
125
+ "category": "ui_screen",
126
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r02853.png"
127
+ },
128
+ {
129
+ "id": "r03194",
130
+ "request": "Create a bold, die-cut sticker badge with a simple circular shape and a bright green outline. The text \"ACCESS GRANTED\" is centered in the badge. Aspect ratio: 2:3.",
131
+ "lang": "en",
132
+ "category": "sticker",
133
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r03194.png"
134
+ },
135
+ {
136
+ "id": "r06208",
137
+ "request": "Create a bold die-cut sticker with a simple geometric shape resembling a star. Display the text \"NEVER GIVE UP\" in large, clean letters on the sticker surface.",
138
+ "lang": "en",
139
+ "category": "sticker",
140
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r06208.png"
141
+ },
142
+ {
143
+ "id": "r08023",
144
+ "request": "\"Rise of the Future\" wide poster with tech elements and vibrant colors",
145
+ "lang": "en",
146
+ "category": "poster",
147
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r08023.png"
148
+ },
149
+ {
150
+ "id": "r07272",
151
+ "request": "A cyberpunk cityscape in digital art style, 2:1, with glowing neon signs; \"FUTURE NOVA\" written in bold neon blue on a rooftop billboard.",
152
+ "lang": "en",
153
+ "category": "illustration",
154
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r07272.png"
155
+ },
156
+ {
157
+ "id": "r09762",
158
+ "request": "Create a diagram showing the water cycle with \"Evaporation\" clearly labeled in the top section.",
159
+ "lang": "en",
160
+ "category": "infographic",
161
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r09762.png"
162
+ },
163
+ {
164
+ "id": "r08914",
165
+ "request": "A sleek wireless charger on a white background with \"Quick Charge 30W\" displayed.",
166
+ "lang": "en",
167
+ "category": "product_shot",
168
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r08914.png"
169
+ },
170
+ {
171
+ "id": "r05251",
172
+ "request": "a man at a bus stop \"next bus 15 min\"",
173
+ "lang": "en",
174
+ "category": "photo",
175
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r05251.png"
176
+ },
177
+ {
178
+ "id": "r03071",
179
+ "request": "\"Rise of the Future\" landscape poster with tech and humans merging",
180
+ "lang": "en",
181
+ "category": "poster",
182
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r03071.png"
183
+ },
184
+ {
185
+ "id": "r05643",
186
+ "request": "A sunset over a bustling city skyline with neon lights; \"Future Urban Hub\" no spelling mistakes.",
187
+ "lang": "en",
188
+ "category": "scene",
189
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r05643.png"
190
+ },
191
+ {
192
+ "id": "r05855",
193
+ "request": "भारत के गंगा नदी के किनारे खुली जंगली चट्टानों के बीच सुंदर दृश्य, रात के समय धुएं वाली दूर से चमकती दृश्य एवं गांव के लोगों के दृ���्य।",
194
+ "lang": "hi",
195
+ "category": "scene",
196
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r05855.png"
197
+ },
198
+ {
199
+ "id": "r03918",
200
+ "request": "Diagrama de barras con datos del 2020 al 2023",
201
+ "lang": "es",
202
+ "category": "infographic",
203
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r03918.png"
204
+ },
205
+ {
206
+ "id": "r03912",
207
+ "request": "رقم عظيم لصورة واسعة من ممرات مدينة عصرية في الصباح، تظهر المباني العالية والشوارع المزدحمة بالكثير من الناس والسيارات المضيئة.",
208
+ "lang": "ar",
209
+ "category": "scene",
210
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r03912.png"
211
+ },
212
+ {
213
+ "id": "r07792",
214
+ "request": "Solar power: bright future, clean energy, green skies",
215
+ "lang": "en",
216
+ "category": "poster",
217
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r07792.png"
218
+ },
219
+ {
220
+ "id": "r00050",
221
+ "request": "Bold yellow circle sticker with a smiling face inside",
222
+ "lang": "en",
223
+ "category": "sticker",
224
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r00050.png"
225
+ },
226
+ {
227
+ "id": "r09743",
228
+ "request": "A futuristic app dashboard on a smartphone screen showing real-time data, charts, and interactive buttons in a sleek, dark theme.",
229
+ "lang": "en",
230
+ "category": "ui_screen",
231
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r09743.png"
232
+ },
233
+ {
234
+ "id": "r01846",
235
+ "request": "Create a labeled diagram showing the water cycle with clear headings, numbered steps, and callouts for evaporation, condensation, precipitation, and collection. Include a simple flowchart style with arrows and icons.",
236
+ "lang": "en",
237
+ "category": "infographic",
238
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r01846.png"
239
+ },
240
+ {
241
+ "id": "r08972",
242
+ "request": "A sleek silver wireless charger placed on a minimalist white background with soft natural light and clean product presentation.",
243
+ "lang": "en",
244
+ "category": "product_shot",
245
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r08972.png"
246
+ },
247
+ {
248
+ "id": "r05594",
249
+ "request": "A bold red circle die-cut sticker, emoji-like, simple, small badge, clean outline, white background",
250
+ "lang": "en",
251
+ "category": "sticker",
252
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r05594.png"
253
+ },
254
+ {
255
+ "id": "r01832",
256
+ "request": "A man walking through a snow-covered forest at dawn.",
257
+ "lang": "en",
258
+ "category": "photo",
259
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r01832.png"
260
+ },
261
+ {
262
+ "id": "r02456",
263
+ "request": "A vibrant sunset over a sprawling desert city with golden towers and flying drones.",
264
+ "lang": "en",
265
+ "category": "scene",
266
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r02456.png"
267
+ },
268
+ {
269
+ "id": "r01193",
270
+ "request": "A sleek silver smartphone lying on a minimalist white background, clean product shot, studio lighting, sharp focus, realistic, high detail, product catalog style.",
271
+ "lang": "en",
272
+ "category": "product_shot",
273
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r01193.png"
274
+ },
275
+ {
276
+ "id": "r07065",
277
+ "request": "A sunset over a snow-capped mountain range. the type must be perfectly legible",
278
+ "lang": "en",
279
+ "category": "scene",
280
+ "sheet_url": "https://huggingface.co/datasets/ysharma/image21-rewriter-distill/resolve/main/renders/sheets/sheet_r07065.png"
281
+ }
282
+ ]