# Qwen-Image-2.1 Pocket rewriter: 12x smaller than the 9B, and it made all four test images less faithful to the prompt

> Satyajit Ghana — Head of Engineering @ Inkers Technology
> canonical: https://ai.thesatyajit.com/articles/qwen-image-2-1-pocket-rewriter
> date: 2026-09-23
> tags: image-generation, diffusion, qwen, distillation, small-models, evaluation, benchmarks, open-weights, on-device, explainer
Qwen-Image-2.1's README recommends running every request through a prompt rewriter
before the image model sees it. The rewriter is a separate 9B model.
[The piece on the release](/articles/qwen-image-2-1) measured it at 9,409,813,744
parameters and 18.82 GB, and found that the low-VRAM deployment guides drop it
entirely. The guides people follow on small GPUs skip the path Qwen recommends.

A day after the release, ML-Intern-lab published two distilled replacements, a 0.8B and
a 2B. The pitch that came with them, in full:

> Qwen-Image 2.1 draws its best pictures when a 9B "prompt rewriter" expands your
> request first. That is 20 GB in bf16 and uses 1700 words as system prompt, and thinks
> for ~1,600 tokens before answering. We shrank it to 0.8B. It fits on a laptop now.

Every clause of that is checkable, and I checked it against the files rather than the
cards: the safetensors headers over HTTP range reads, the teacher's shipped system
prompt, the 8,797 teacher labels in the distillation dataset, the training arguments,
and the evaluation's raw score file. Most of the pitch holds up at about the precision a
pitch has. Two parts do not. The thinking is half the number quoted. And "best pictures"
rests on an evaluation whose picture judge's verdicts were never actually read: the
parser picked image A every time. I then ran all four rewriters on four CPU cores.
"Fits on a laptop" holds: the 0.8B rewrites in 14 s. "Best pictures" does not, on this
evidence. All four images I rendered from its rewrites came out less faithful to the
request than the raw prompt's.

I label each claim below. **Measured** means I computed it from a file I read.
**Reported** means someone else's number that I could not check. **Reasoned** means my
inference from things I measured.

<ModelCard
  repo="ML-Intern-lab/Qwen-Image-2.1-PE-T2I-Pocket-0.8B"
  claimed="0.8B, fits on a laptop"
  note="Measured from the safetensors header: 752,393,024 parameters in 320 tensors, all BF16, embeddings tied, no MTP weights. 254,279,680 of them (33.8%) are the 248,320-row embedding table. The base checkpoint, Qwen3.5-0.8B, is 873,438,784 with its vision tower; this is the text-only language model. The Q8_0 GGUF in the same repo is 811,843,104 bytes. The Hub's repo size counts superseded uploads, and its 'quantized' relation comes from that GGUF tag: the weights are a full fine-tune. The LICENSE is the Qwen Research License Agreement, non-commercial, byte-identical to Qwen's."
/>

<ModelCard
  repo="ML-Intern-lab/Qwen-Image-2.1-PE-T2I-Pocket-2B"
  claimed="2B"
  note="Measured from the safetensors header: 1,881,825,088 parameters in 320 tensors, all BF16, 27.0% of them embeddings. Same 24-layer hybrid stack as the 0.8B (18 linear-attention layers, 6 full-attention), at twice the width. One 3,763,692,048-byte safetensors file; the Hub's repo size counts a superseded upload of it. No GGUF is published. Same Qwen Research License."
/>

## What the rewriter does

The job is narrow. A user types "a corgi playing guitar in the rain". The rewriter returns
one line of JSON: a long English description of the finished picture, and an aspect
ratio.

```json
{"rewritten_prompt": "The image is a wide cinematic photograph of ...", "wh_ratio": "3:2"}
```

Qwen's version is `Qwen/Qwen-Image-2.1-PE-T2I`, a fine-tuned Qwen3.5 9B vision-language
model. It is told how to do the job by a system prompt that ships inside the checkpoint
as `system_prompt.txt`. It thinks before it answers, and Qwen's own runner says thinking
is required: *"both models were trained with a `<think>` block and degrade without
it."* The runner also pins a `presence_penalty` of 1.5 for this task, and warns that a
wrong value "quietly changes the distribution you sample from."

The pocket models do the same job with none of that. There is no system prompt, and the
chat template has been flipped. The teacher's template opens a `<think>` block unless
told not to. The students' template closes it unless told to. The instructions and the
JSON format are in the weights.

## The 9B, clause by clause

| The pitch says | The files say | |
|---|---|---|
| "20 GB in bf16" | 18,819,627,488 bytes across four shards: **18.82 GB**, or 17.53 GiB. Qwen's own `prompt_rewrite/README.md` says "~20 GB", so the rounding is Qwen's. | Measured |
| "1700 words as system prompt" | `system_prompt.txt` is **1,721 words**, 10,045 bytes and **2,426 tokens** with the shipped tokenizer. It is byte-identical to `prompt_rewrite/prompts/system_prompt_t2i.txt` in the GitHub repo at `fb7ae1d`. | Measured |
| "thinks for ~1,600 tokens before answering" | Qwen documents no such figure, only a `max_new_tokens` of 16,256. The number comes from ML-Intern-lab's own labelling run, and it is the **whole generation**: 1,666 tokens per rewrite on average over 8,797 rows. The stored reasoning is **805 tokens** of that (median 742). The other ~860 are the answer. | Measured |
| "a 9B" | 9,409,813,744 parameters. 456,010,480 of them are a vision tower that text-to-image never uses, and the embeddings are untied, so 2,034,237,440 are vocabulary. The text path is 8,953,803,264, which is exactly what the community GGUF of it declares. | Measured |

Only the third row is wrong. It is wrong by a factor of two, and it matters because it is
the cost the students exist to remove. ML-Intern-lab's own arena Space gets it right,
"its rewrite plus thinking averages about 1,600 tokens", and the pitch compressed that
into something it isn't.

The dataset card gives the mean as 1,672 with a maximum of 4,360. Over the full
`labels_full.jsonl` I get 1,666.0 and 7,330. Probably a different subset. It changes
nothing above.

The system prompt deserves one more line, because it explains the whole distillation.
It walks the model through eight steps, from "Read the brief and split it in two" to
"Close with the whole frame". It asks for "about twenty sentences and four to five
hundred words", and for "the same size whether the brief was three words or three
hundred". It allows fifteen aspect ratios. The teacher overshoots the length: its rewrites
have a median of **599 words**, and the 90th percentile is 1,031.

<Callout type="note">
One thing in Qwen's own release that the pocket card quietly fixes. The PE-T2I model
card's diffusers example maps `wh_ratio` to a render size through a table of seven
ratios, and anything else falls back to `(2048, 2048)`. The system prompt allows fifteen.
In the 8,797 teacher answers, 390 (4.4%) name one of the eight the table lacks: 272 of
them are `1:2`, which the system prompt recommends "for a phone screen or a tall standing
banner". Copy Qwen's snippet and those render as squares. The pocket card's table covers
all fifteen.
</Callout>

<RewriterLedger />

## What was distilled, and from what

The pipeline is all in the dataset repo,
[`ML-Intern-lab/Qwen-Image-2.1-rewriter-distill`](https://huggingface.co/datasets/ML-Intern-lab/Qwen-Image-2.1-rewriter-distill),
code included. It is more complete than most releases I read. In order:

1. **8,797 synthetic requests.** A small instruct model wrote them over a ten-category
   taxonomy; the smoke run's summary names `Qwen/Qwen3-4B-Instruct-2507`. 32% are
   non-English, spread over seven languages. The median request is 14 words. No real
   user wrote any of them. **Reported** for the generator, **Measured** for the counts.
2. **Teacher labels.** The official `prompt_rewrite/` code at `fb7ae1d`, run through
   vLLM on one A100 with the official sampling, presence penalty included, in 2 h 37 min
   (**Reported**). 8,796 of the 8,797 answers parse (**Measured**). The reasoning is kept
   in the file and never trained on.
3. **A hard filter.** A row survives only if its JSON parses, its ratio is allowed, every
   quoted string from the request appears verbatim, a ratio the user stated is respected,
   the rewrite is English, it is **80–400 words** long, and it names no ratio or
   resolution in the prose. 1,840 rows pass: 1,776 for training, 64 held out.
4. **SFT.** I disassembled both `training_args.bin` files with `pickletools` rather than
   unpickling them. They say what the cards say: learning rate `1e-05` on a cosine
   schedule, 2 epochs, batch 8 with 4 accumulation steps, `max_length` 1024,
   `assistant_only_loss` on, seed 42. That is about 111 optimizer steps over 678,150
   target tokens per epoch (**Measured**, counting the assistant turns with the shipped
   tokenizer). The final loss was 1.693 for the 0.8B and 1.462 for the 2B
   (**Reported**).

The training arguments also carry `push_to_hub: True` and
`hub_model_id: ysharma/image21-pocket-rewriter-0.8B`, which is where the trainer sent the
weights; the published copies live in the `ML-Intern-lab` org. The Studio Space says the rewriters "were trained end to end by
ML Intern in HuggingChat for about 16 USD". So an agent trained them, and an agent is
reviewing them. I have tried to hold both to the same standard.

A `check_de2.log` in the dataset shows they checked the one detail distillations usually
get wrong. The supervised span begins with the empty `<think>\n\n</think>\n\n` block,
so the student learns to emit it itself. The card's `llama-cli` example, which leaves
the block out of the prompt, is therefore consistent with training. The same example
names a file, `Qwen-Image-2.1-PE-T2I-Pocket-0.8B-Q8_0.gguf`, that is not in the repo.
The GGUF that is there is called `image21-pocket-rewriter-0.8B-Q8_0.gguf`.

### The length filter chose the curriculum

The dataset card is candid about the 400-word ceiling. It says the ceiling "does most of
the cutting, and it is not a neutral cut", and that the students "learn the teacher's
style at roughly half the teacher's length". Counting words the way `filter.py` does, I
get 2,289 of the 8,797 teacher rewrites at 400 or fewer, 26.0% of them. The card's
sample says 21%.

What the card does not say is *which* rewrites are short. I joined the 1,776 training
rows back to the request metadata on the request text. All 1,776 match.

<CurriculumSkew />

**Measured.** The short rewrites are the images the system prompt tells the teacher to
describe briefly: a single centred emblem. 71.5% of sticker requests and 70.9% of logo
requests made it into training. 1.3% of posters did, and 0.6% of UI screens. The
students saw 11 posters, 19 infographics and 5 app screens. That is 35 examples of the
layouts built around legible text, out of 2,485 such requests the teacher labelled. Rendered
text is also exactly where the teacher's gain shows up in the evaluation below.

**Reasoned.** None of that is visible in the text metrics, because the held-out set is
stratified like the corpus, not like the training set. It shows up in the pictures.

<Figure
  src="/articles/qwen-image-2-1-pocket-rewriter/fig1.png"
  alt="Four generated images for the request 'Rise of the Future landscape poster with tech and humans merging', labelled raw, teacher, student08 and student2b. The raw request produced a glowing figure on a mountain with no text. The teacher's rewrite produced a poster with the headline 'RISE OF THE FUTURE', a tagline and a man and a cyborg in front of a city. The 0.8B student's produced a square, orange cityscape of towers with no text. The 2B student's produced a grey coastal city with silhouettes and no text."
  caption="A poster request, the category the students saw eleven examples of. Only the teacher's rewrite produced a poster with the title on it, and the 0.8B also chose a square ratio for a request that said 'landscape'. One seed per arm, rendered by Qwen-Image-2.1 at about 1 MP (ML-Intern-lab distillation dataset, contact sheet r03071)."
/>

## Their evidence

### Text-level: format parity, against a strawman

The cards' table is over 300 held-out requests, and I checked each column against the
prediction files it came from (**Reported**, reconciled):

| | 9B teacher | Pocket-0.8B | Pocket-2B | untuned 2B |
|---|---:|---:|---:|---:|
| rows | 300 | 300 | 300 | 80 |
| valid JSON | 100.0% | 99.7% | 100.0% | 77.5% |
| allowed ratio | 100.0% | 99.3% | 99.7% | 20.0% |
| quoted text kept verbatim | 53.1% | 53.1% | 60.2% | 3.7% |
| same ratio as the teacher | — | 57.7% | 67.3% | 8.8% |
| generated tokens, mean | 1,630.8 | 453.1 | 482.8 | 6,106.4 |

The format parity is real, and the teacher only keeps quoted text verbatim 53% of the
time, which surprised me. Three things in that table need saying.

The last column is not a no-rewriter arm. It is untuned Qwen3.5-2B given the teacher's
full system prompt and told to think, and its median generation is exactly 6,144 tokens.
That is the cap. 75 of its 80 rows hit it, and only 8 ever close their `</think>`. Its
"77.5% valid JSON" is the parser, with `json_repair` behind it, pulling an object out of
unfinished reasoning. Run `json.loads` on what follows the think block and the count is
0 of 80, against 297 of 300 for the 0.8B (**Measured**). The column shows that
fine-tuning beats prompting a small model. It shows nothing about pictures.

The latency row, which I left out, is `gen_student.py`'s batch time divided by the batch
size of 8, on hardware the card does not state. The card's CPU figure, about 6.85
tokens/s and 66 s per rewrite on a Xeon 8375C with 12 threads, is not what the
published CPU file shows. `preds_0.8b_cpu.jsonl` is six rewrites from a single fp32
`transformers` batch, each carrying the batch time divided by six: 549.6 s. The 6.85
may well be right for `llama.cpp`, but its log is not published. **Reported**, and not
reproducible from the files.

And every metric in the table checks the rewrite's *format*. None of them looks at an
image.

### Image-level: the no-rewriter arm exists, and the judge did not judge

This is the part of the release I most wanted to see, and it exists. Forty of the
held-out requests, over-sampled for quoted text, were rendered four ways: the raw
request, the teacher's rewrite, and each student's. That is 160 images, and the first
arm is exactly the control "best pictures" needs. Two instruments score them: a
pairwise vision-language judge, and OCR on the text the request asked for. I re-read
both from `eval/score_4arm.json`.

<EvalAudit />

**Measured.** The judge is `Qwen/Qwen3.5-9B`, run greedy with a 256-token budget. It
answers in its reasoning voice ("The user wants me to choose between Image A and Image
B…"), and `score_images.py` takes as its verdict the first standalone `A` or `B` in
the upper-cased reply. The restatement supplies one, and so does the English article
"a". In all 240 comparisons the winner is the image in the A slot. The plan hands out
each request's six A slots 2/2/1/1 over the four arms, so the per-arm "wins" in the
published table (62, 62, 60, 56) are the per-arm A-slot counts, to the unit. The arena
Space summarises this arm as "a pairwise vision-model judge could not separate the arms".
That is true, but not for the reason it suggests. The judge could not have separated
anything.

**Measured.** The OCR arm works, and it says something specific. Across 29 quoted spans
and 112 reference words, the teacher's renders get 89 words right, the raw requests
52, the 0.8B 64 and the 2B 61. That is where the Space's "roughly a quarter to a third
of the teacher's gain" comes from, and as a point estimate it is fair. Compared span by
span, though, the teacher beats no rewriter 11 to 2 (sign test, p = 0.022). The 0.8B's
record against no rewriter is 8 to 7, and the 2B's is 6 to 3. **Reasoned:** on this
sample the 9B's effect on rendered text is detectable, and the pocket models' is not.
That is not evidence that they do nothing. It is the absence of evidence that they do
something, which is the part the pitch skipped.

Three things about the renders limit how far any of this goes. The render script
derives each image's seed from `blake2b("seed|request|arm")`. Its docstring says the arms
are "the only thing that varies within a row", but the arm name is inside the hash, so
every arm also gets different noise. Each arm renders at the ratio its rewriter chose.
And everything is at about 1 MP, where the teacher's own card renders at about 4 MP.

The contact sheets show what the numbers hide.

<Figure
  src="/articles/qwen-image-2-1-pocket-rewriter/fig2.png"
  alt="Four generated circular sticker images for a Chinese request asking for a round sticker icon with the words 快乐星球, meaning Happy Planet. Raw: a small round sticker of the Earth and a smiling cloud with 快乐星球 written across the top. Teacher: a large vivid sticker with 快乐星球 and HAPPY PLANET in bubble letters over a cartoon Earth, a rocket and a city. 0.8B student: a blue star above a blue blob, no text. 2B student: a gold ringed-planet emblem on a dark background, no text."
  caption="A sticker, the students' best-represented category, 71.5% of such requests in training. Neither student's rewrite contains the requested words, so neither image has them. The raw request, with no rewriter at all, rendered them. One seed per arm (ML-Intern-lab distillation dataset, contact sheet r02923)."
/>

<Figure
  src="/articles/qwen-image-2-1-pocket-rewriter/fig3.png"
  alt="Four generated images for a Hindi request for a product shot of a physical product in a room, named स्मार्ट वॉटर बॉटल, smart water bottle. Raw: a lilac flask on a table with the entire Hindi request printed as five lines of text beside it. Teacher: a black smart bottle with a digital display on a wooden table in a living room, with the headline स्मार्ट वॉटर बॉटल and a subheading. 0.8B student: a white ceramic vessel next to a candlestick with a small, misspelled label. 2B student: a clear bottle of blue water on a kitchen counter, no text."
  caption="The OCR metric's blind spot. With no rewriter, the image model painted the whole request into the picture, instruction and all, and OCR scored that arm best at 5 of 6 words. The teacher's rewrite produced the product advert the request asked for, with the name as a headline and no 'product name:' in front of it, and scored 1 of 6 (ML-Intern-lab distillation dataset, contact sheet r08005)."
/>

<Figure
  src="/articles/qwen-image-2-1-pocket-rewriter/fig4.png"
  alt="Four generated images for a Chinese request for an elderly man reading on a park bench with the caption 今日好书推荐：《平凡的世界》. Raw: an old man on a bench, no text. Teacher: an old man reading, with the full caption as a stylised title. 0.8B student: a middle-aged woman on a bench with the full caption set cleanly across the top. 2B student: an old man holding a book titled 平凡的世界, without the rest of the caption."
  caption="The 0.8B's best case. OCR scores its caption and book title 16 of 16, level with the teacher. The subject is wrong: the request asked for an old man, and no metric in the evaluation checks that (ML-Intern-lab distillation dataset, contact sheet r07058)."
/>

So, "best pictures". **Reasoned:** the evidence that the 9B improves Qwen-Image-2.1's
pictures is its effect on rendered text, measured on 29 spans. Nothing in the release
measures composition, faithfulness to the request, or anything a person would call
better, because the one instrument meant to do that was reading its own seating plan.
For the pocket models, the evidence that they improve on no rewriter has not been
collected yet. The next two sections are about what they cost while that question is
open.

## The other cost: the rewrite is part of the denoiser's sequence

The pitch counts the rewriter's cost. It does not count what the rewrite costs the image
model, and on this architecture that is not free. Qwen-Image-2.1's denoiser is
single-stream: the prompt's text tokens are part of the one sequence that all 32 blocks
process. A request as typed gives the denoiser 34 text tokens on average. The 0.8B's
rewrite gives it 446, and the 9B's gives it 827 (**Measured**, with the image model's
own tokenizer and template, over the same 300 requests).

Whether the denoiser pays for those tokens once or on every step depends on the prefix
cache, which [the parent piece](/articles/qwen-image-2-1#block-causal-attention-and-why-the-cache-is-exact)
covers. diffusers keeps the prefix's K and V after the first step. stable-diffusion.cpp,
the runtime the parent used for its CPU run, does not. At `2bb7294` it builds the full
joint sequence, runs all 32 blocks over it, and slices the prefix off only at the end,
on every step. I read that in `src/model/diffusion/qwen_image_2_1.hpp`.

<SequenceTax />

**Reasoned.** vLLM-Omni's recipe says that "plain short-prompt text-to-image has almost
nothing to cache". That is true of a 34-token request. It stops being true the moment
you follow Qwen's advice and put a rewriter in front, and it is least true where the
pitch points. On four CPU cores at 512², with no cache, a 0.8B rewrite should make every
one of 40 denoising steps about 39% more expensive. The parent measured a 512² step on
this machine at around 100 s. If the arithmetic holds, the rewrite adds more than
twenty minutes of denoiser time per image. The rewriter itself, by the card's own CPU
figure, takes about a minute. The text encoder also has to prefill 446 tokens instead of
34, which the parent timed at 6–9 s for the short prompt. The next section is the test.

## The run on four cores

On 23 September, once the parent article's gallery had finished with the machine, I ran
all four rewriters on it: four cores of an Intel Xeon at 2.80GHz, 15 GB of RAM under a
13.36 GiB memory cgroup, and no GPU. Each rewriter got the gallery's seven prompts, one
rewrite per prompt, seed 42.

- **The 0.8B** ran twice: through `llama.cpp` from ML-Intern-lab's own Q8_0 GGUF, and
  through `transformers` in fp32, which is the card's own recipe.
- **The 2B** has no published GGUF, so I converted it to Q8_0 myself with the same
  flags the card names.
- **The 9B** is a community Q4_K_M GGUF, run with Qwen's own text-to-image settings: the
  shipped system prompt, thinking on, presence penalty 1.5.

Every rewrite, every counter and the exact commands are in
[`data/rewrites.json`](/articles/qwen-image-2-1-pocket-rewriter/data/rewrites.json).

<CpuRewriters />

**Measured.** Three runs overlapped a wait loop of mine that turned out to be spinning
a full core. I re-ran two of them. With the same seed they produced byte-identical text,
so only the clock was affected. The table excludes the third, a `transformers` run that
decoded at 0.78 tokens/s against a median of 7.6. What the rest say:

- **The 0.8B takes 14.1 s per rewrite through `llama.cpp`**, decoding at 35.0 tokens/s
  on four cores with 1.74 GiB resident. The card's CPU figure is 6.85 tokens/s on
  twelve threads of a Xeon 8375C. On this box the GGUF path is five times that.
- **The card's own `transformers` recipe is four times slower**: 7.6 tokens/s and
  55 s per rewrite, at 4.77 GiB. `transformers` warns why on every load. Without
  `flash-linear-attention` and `causal_conv1d` installed, the gated-delta-rule layers
  fall back to their reference PyTorch kernels. That is the likely setup on any laptop
  without a CUDA toolchain.
- **The 9B fits.** It ran at 10.13 GiB resident, inside the same cgroup that holds the
  image model. What it costs is time: 387 s per rewrite, **27x the 0.8B**. 66 s of that
  is reading Qwen's 2,473-token system prompt before the first new token. `llama.cpp`
  pays that on every request unless the prompt prefix is cached. The rest is decoding at
  3.0 tokens/s.
- **The 9B thought for 477 to 851 tokens, median 623.** Its median total generation was
  934 tokens, against 1,666 in ML-Intern-lab's labels. It also wrote shorter rewrites
  than in those labels: a median of 348 words against 599. I cannot tell whether that
  comes from Q4_K_M or from these prompts, whose median of 26 words is nearly twice the
  synthetic median of 14, so I leave it unexplained. On the pitch's point, this
  run agrees with the files: the thinking is well under 1,600 tokens.

The rewrites themselves, checked against the prompts:

| | 0.8B · llama.cpp | 0.8B · transformers | 2B | 9B |
|---|---|---|---|---|
| strictly valid JSON | 7 / 7 | 7 / 7 | 7 / 7 | 7 / 7 |
| quoted strings (g1, g2), as exact quoted units | 5 / 6: `"Demos,"` gained a comma | 6 / 6 | 6 / 6 | 4 / 6: two split across lines, plus six strings it invented |
| RGBA sentence kept (g5) | no | no | no | no |
| g5 background described as | "the white background" | "plain off-white" | "warm studio-lit" | "checkerboard transparency preview" |
| three red cups, two blue bowls (g3) | three *pairs* of red cups | two red bowls, blue between them | kept | kept |
| words, median | 342 | 342 | 316 | 348 |
| text tokens into the denoiser, median | 432 | 441 | 406 | 424 |

The raw prompts give the denoiser a median of 41 text tokens. Three things in that table
matter for the images:

- **The quoted text almost survives everywhere.** The 0.8B quoted the poster's last
  word as `"Demos,"`, with a comma inside the quotes. The 9B's two misses are not
  corruption. Every character is there, but it lays the strings out itself:
  `"面馆" followed by "NOODLE BAR"`, and the poster's title as `"EDGE AI" on the first
  line and "SUMMIT" directly below it`. That follows its system prompt's instruction to
  describe a line break as a second line. It is still a layout decision the user did not
  ask for. This probably pulls down the teacher's 53% verbatim rate on the card as well.
  On the noodle shop the 9B also invents signage of its own: six more quoted strings,
  among them "面条是我们的热情" ("noodles are our passion") and "Noodle Menu".
- **Nobody kept the transparency sentence.** The first prediction holds at the text
  level for all four rewriters, including the teacher. Every rewrite instead describes a
  background, and the 9B's is the most striking. It rewrites "the background is
  transparent" into "a gray-and-white checkerboard transparency preview background",
  which asks the image model to *paint* the checkerboard that image editors use to show
  transparency.
- **Both 0.8B samples broke the count** that the raw prompt gets right. The 2B and the
  9B kept it.

<Callout type="note">
Every rewrite here is one sample. Both runtimes sample, at temperature 1.0, so the two
0.8B runs of the same prompt share only 4–10% of their words (difflib word-level ratio,
per prompt), even though they agree on everything structural. Each image pair below
therefore compares one draw of a rewriter against the raw prompt. A different draw
could give a different picture. I do not generalise from one pair.
</Callout>

### The pairs

Each pair re-renders a gallery prompt from the rewrite with the gallery's exact command.
That is Q4_K denoiser, 512², 20 Euler steps on the vendor's schedule, cfg 1.0, seed 42,
and `--mmap`, with only the prompt text changed. Each image sits next to the raw-prompt
image the parent published. The gallery renders square, so I held the square and
discarded the rewriters' ratio choices. Per-step times are medians over the 20 steps.

Four prompts, chosen before any output: the transparent glass bottle, the conference
poster, the bilingual noodle-shop sign, and the cups and bowls. The 0.8B rewrote all
four, and the 9B rewrote the glass and the poster. That is six renders, 36 to 49 minutes each, and all six are below.

What I expected, written down before any of this ran:

1. Neither pocket model will carry the transparency sentence through intact, and the
   rewritten glass bottle will lose whatever alpha channel the raw prompt's image has.
   Not one of the 8,797 requests asked for RGBA, and not one of the 1,776 training
   targets contains the word.
2. Each denoising step will cost 1.3–1.5x the raw prompt's with a 0.8B rewrite, and
   more with the 9B's, as the table above derives.
3. The 9B will think for roughly 500 to 1,200 tokens per prompt, not 1,600. Its own
   labels put the 10th and 90th percentiles at 500 and 1,175.

One more, added at 13:27 UTC on 23 September, after reading the rewrites and before the
9B's glass bottle rendered:

4. The 9B's glass bottle will show a painted checkerboard in opaque pixels, with little
   or no real transparency. Its rewrite asks for "a gray-and-white checkerboard
   transparency preview background … evenly spaced square tiles in alternating very
   light gray and off-white". That describes the pattern an image editor *displays*
   behind a transparent layer, not a transparent layer.

How each came out is tallied at the end of this section.

### The glass bottle

<Figure
  src="/articles/qwen-image-2-1/cpu2-gal-g5-transparent-glass.png"
  alt="Two panels. Left: a green glass bottle of olive oil with a cork stopper over a grey checkerboard, but the background is solid white and the checkerboard shows only in a thin fringe around the bottle and one small blob at the lower right. Right: the alpha channel, white almost everywhere, with a thin black outline around the bottle."
  caption="Raw prompt, from the parent's gallery: the output over a checkerboard, and its alpha channel beside it, white meaning opaque. The background came back opaque white, with only a fringe around the bottle, 1.30% of the pixels, below alpha 32. Q4_K, 512², 20 steps, cfg 1.0, seed 42, on four CPU cores."
/>

<Figure
  src="/articles/qwen-image-2-1-pocket-rewriter/pair-g5_transparent_glass-p08-q8.png"
  alt="Two panels. Left: a dark olive-green glass bottle with a cork stopper standing on a pale grey-white studio backdrop with a soft shadow; no checkerboard shows anywhere. Right: the alpha channel, pure white across the whole frame."
  caption="The same command with only the prompt swapped for the 0.8B's rewrite, one sample. There is no transparency left at all: 100.00% of pixels are at alpha 250 or above. The bottle has become dark tinted glass on a studio backdrop, as the rewrite described. Q4_K, 512², 20 steps, cfg 1.0, seed 42, on four CPU cores."
/>

**Measured.** The rewrite dropped both sentences that ask for transparency, and not
only those. Neither 0.8B sample contains "transparent", "transparency", "alpha", "RGBA",
"cutout" or "isolated" anywhere. In their place this one wrote "a soft gray drop shadow
onto the white background" and "a clean white-to-light-gray surface with soft vignette
shading", and the image did exactly that. The raw prompt had already failed at this
size, since its background came back opaque white too. So the rewrite did not break a
working alpha channel. It removed the fringe that was left, 1.30% of the frame below
alpha 32, down to nothing. The first prediction holds, in the least interesting way it
could. It also changed the object: the request's "clear glass bottle of green olive oil"
became dark tinted glass, following the rewrite's "dark brown or charcoal glass".

The cost came almost entirely after the rewriter:

| | raw prompt | 0.8B rewrite |
|---|---:|---:|
| rewrite, 0.8B on `llama.cpp` | — | 10.6 s |
| text tokens into the denoiser | 41 | 351 |
| text encoding (Qwen3-VL, Q4_K_M) | 5.53 s | 70.19 s |
| denoising step, median of 20 | 88.57 s | 134.59 s |
| VAE decode | 41.15 s | 51.69 s |
| whole image | 1,819.53 s | 2,860.40 s |

The rewrite took 10.6 s and the image took 1,041 s longer. **Measured:** the text
encoder alone went from 5.5 s to 70.2 s, because it now prefills 351 tokens instead of
41. **Measured:** the denoising step rose 1.52x. **Reasoned:** that is above the 1.29x my
cost model gives for this pair and just above the range I predicted. Part of that is
noise. The raw gallery's per-step medians run from 88.6 s to 103.9 s across prompts of
almost the same length, and this prompt's was the fastest of them, which flatters any
ratio taken against it. Over all six pairs this turns out to be the model's largest
miss.

<Figure
  src="/articles/qwen-image-2-1-pocket-rewriter/pair-g5_transparent_glass-t9b-q4km.png"
  alt="Two panels. Left: a clear glass bottle with a cork stopper, full of yellow-green olive oil with sprigs of narrow dark-green leaves suspended in it, standing on a faint grey-and-white checkerboard. Right: the alpha channel, white across the whole frame."
  caption="The 9B's rewrite, one sample, same command. The checkerboard is painted: 100.00% of pixels are at alpha 250 or above, so what reads as a transparency preview is opaque grey and white squares. The glass is clear and the oil green, as asked; the leaves in the oil are the rewrite's addition. Q4_K, 512², 20 steps, cfg 1.0, seed 42, on four CPU cores."
/>

**Measured.** The fourth prediction holds. The rewrite asked for a "gray-and-white
checkerboard transparency preview background" and got one, painted in opaque pixels:
100.00% of the frame at alpha 250 or above, the same as the 0.8B's studio backdrop, and
less transparency than the raw prompt's 1.30% fringe. The student forgot the
instruction. The teacher kept the words "transparency", "alpha" and "cutout" and turned
them into a *picture* of transparency, the thing an image editor shows in place of it.
Neither reached a transparent pixel. The teacher did get the object right where the
student did not: clear glass, green oil, cork. It also added something nobody asked
for, "numerous olive leaves and thin stems" suspended in the oil, and the image drew
them.

The 9B's rewrite ran 474 s on four cores. Its 464 text tokens made each step 1.42x the
raw prompt's, against 1.40x from the cost model, and the image took 756 s longer. Rewrite
and image together, the teacher added 1,231 s to this picture and the student 1,052 s.

### The poster

<div className="grid gap-x-4 sm:grid-cols-2">

<Figure
  src="/articles/qwen-image-2-1/cpu2-gal-g2-poster-layout.png"
  alt="A minimalist cream poster. At the top, bold black sans-serif text reads 'EDGE AI SUMMIT' with 'Bengaluru, 14 November 2026' in smaller type beneath it; along the bottom, three words in a row read 'Talks', 'Workshops', 'Demos'. The middle is empty."
  caption="Raw prompt, from the parent's gallery. All five text elements are exact. Q4_K, 512², 20 steps, cfg 1.0, seed 42, on four CPU cores."
/>

<Figure
  src="/articles/qwen-image-2-1-pocket-rewriter/pair-g2_poster_layout-p08-q8.png"
  alt="A cream poster. At the top, 'EDGE', 'AI' and 'SUMMIT' in large black serif capitals spread across the width; in the middle, 'Bengaluru, 14' on one line, with a stray mark above the vowel after the l, and 'November 2026' on the next; along the bottom, 'Talks Workshops Demos,' in dark navy with a trailing comma. The rest is empty."
  caption="The 0.8B's rewrite, one sample, same command. Not text-perfect: 'Demos,' carries a trailing comma, and 'Bengaluru' has a stray mark above the vowel after the l. The date also breaks over two lines. The comma and the line break are both in the rewrite text; the stray mark is the image model's own."
/>

</div>

**Measured.** The raw prompt had every string right. The 0.8B's rewrite kept the
strings but wrapped them in layout the user never asked for. The words are spread "with
'EDGE' aligned on the left, 'AI' centered, and 'SUMMIT' aligned on the right". The date
gets "a two-line layout". And the last word is quoted as `"Demos,"`. The image model
drew all three. It then added an error of its own, a small stray mark above the vowel
after the "l" in "Bengaluru", which the rewrite does not contain. The strings themselves
were not corrupted. The same letters rendered less exactly inside a prompt five times
longer. The middle of the poster is as empty as before. On the one category the
students saw eleven examples of, the rewrite made the text worse and the layout no
better.

| | raw prompt | 0.8B rewrite |
|---|---:|---:|
| rewrite, 0.8B on `llama.cpp` | — | 12.8 s |
| text tokens into the denoiser | 75 | 371 |
| text encoding | 9.07 s | 40.92 s |
| denoising step, median of 20 | 99.83 s | 134.82 s |
| whole image | 2,097.40 s | 2,809.56 s |

The step rose **1.35x**, against 1.27x from the cost model. That is inside the
predicted 1.3–1.5x range, and above the model. The image took 712 s longer, against a
12.8 s rewrite.

<div className="grid gap-x-4 sm:grid-cols-2">

<Figure
  src="/articles/qwen-image-2-1-pocket-rewriter/pair-g2_poster_layout-t9b-q4km.png"
  alt="A cream poster. At the top centre, 'EDGE AI' and 'SUMMIT' on two lines in heavy black sans-serif capitals; beneath them, 'Bengaluru, 14 November 2026' in smaller type; a wide empty middle; along the bottom, 'Talks', 'Workshops' and 'Demos' evenly spaced."
  caption="The 9B's rewrite, one sample, same command. All five strings are exact, checked at three times zoom. The title breaks over two lines because the rewrite says so; the raw prompt's image kept it on one. Q4_K, 512², 20 steps, cfg 1.0, seed 42, on four CPU cores."
/>

</div>

**Measured.** The teacher's rewrite gave back what the student's took away. Every string
is exact, and the only change to the layout is the one its rewrite asked for, the title
on two lines. That rewrite was also the shortest of the six, 299 text tokens against the
0.8B's 371, and each step cost 1.05x the raw prompt's against 1.21x from the cost model.
That is the model's second-largest miss, in the other direction, and it sits inside the
spread of the raw gallery's own per-step medians. On the one prompt where the raw image
was already exact, the 9B matched it and did not beat it. What matching it cost was the
rewrite: 362 s on four cores, for an image that took 66 s longer than the raw prompt's.

One pair proves nothing about text in general. It does line up with the release's own
OCR arm, where the teacher beats no rewriter on rendered text and the 0.8B does not.

### The noodle-shop sign

<div className="grid gap-x-4 sm:grid-cols-2">

<Figure
  src="/articles/qwen-image-2-1/cpu2-gal-g1-bilingual-sign.png"
  alt="A small shop front at night under a glowing red sign. The sign reads 面馆 in white characters, then a garbled glyph, then 'BAR'; the word 'NOODLE' is missing. Steam drifts out of the lit doorway and the wet street in front reflects the light."
  caption="Raw prompt, from the parent's gallery. The sign is above the door, the Chinese and 'BAR' are exact, and 'NOODLE' is a garbled glyph. Steam rises from the doorway. Q4_K, 512², 20 steps, cfg 1.0, seed 42, on four CPU cores."
/>

<Figure
  src="/articles/qwen-image-2-1-pocket-rewriter/pair-g1_bilingual_sign-p08-q8.png"
  alt="A shop front at night seen at an angle. The door stands open onto a lit interior; to its right, a vertical black sign on the wall carries, top to bottom, a red 面, a small illegible Latin word, a red 馆 and 'BAR'. Two red bowls sit on a ledge in front of the door, a figure stands at the right edge, and the wet street reflects the sign. There is no steam."
  caption="The 0.8B's rewrite, one sample, same command. The sign has moved from above the door to the wall beside it, and the steam is gone. 'NOODLE' is still illegible, now wedged between the two characters. The bowls on the ledge and the figure at the right come from the rewrite, not the request."
/>

</div>

**Measured.** This rewrite does not expand the request. It replaces it with a
description of a different photograph. The request puts the sign "above the door"; the
rewrite mounts "a vertical black sign … on the exterior wall" to "the right of the
counter". The request has "steam rising from the doorway"; in the rewrite, steam
survives only in a list of the palette's colours, "warm yellows from the lanterns,
steam, and reflections". It then adds "two slender, glossy black ramen bowls" as the
foreground subject, "a small dark figure" on the far right, and "additional smaller dark
Chinese characters" on the sign. The image model drew the rewrite, not the request: a
sign beside the door, no steam, two bowls, a figure. The one thing a rewrite could have
fixed is the word the raw image garbled, and it is still garbled.

The rewrite also reads like a caption more than a prompt. It hedges about its own
scene, a figure "likely" standing near the door, a puddle "shaped like a small bowl-like
basin". That is the register of someone describing an image they are looking at, and
the image model takes every hedge as an instruction.

At 500 text tokens this was the longest of the six rewrites. Each step cost 1.33x the
raw prompt's, against 1.42x from the cost model, and the image took 754 s longer after an
18.6 s rewrite.

### The cups and bowls

<div className="grid gap-x-4 sm:grid-cols-2">

<Figure
  src="/articles/qwen-image-2-1/cpu2-gal-g3-counting-spatial.png"
  alt="On a glossy white surface, three red cups on the left fused into a single three-lobed object with shared walls, and two separate blue bowls on the right, each reflected in the surface."
  caption="Raw prompt, from the parent's gallery. Three red cups to the left of two blue bowls, seen from the front: count and placement right, though the three cups have fused into one piece. Q4_K, 512², 20 steps, cfg 1.0, seed 42, on four CPU cores."
/>

<Figure
  src="/articles/qwen-image-2-1-pocket-rewriter/pair-g3_counting_spatial-p08-q8.png"
  alt="Four separate glossy red ceramic cups with white interiors grouped at the front left of a white table, and one large blue bowl with pale rings behind them on the right, in front of blurred dark kitchen cabinets, seen from slightly above."
  caption="The 0.8B's rewrite, one sample, same command. Four red cups and one blue bowl, where the request asked for three and two, seen from above rather than from the front. The cups are separate now, which is the one improvement."
/>

</div>

**Measured.** The raw image got the count right. The rewrite could not have: it opens
with "three sets of small ceramic bowls", then describes "a pair of red ceramic cups", "a
second pair of red cups" and "a third pair", which is six, and "an oval blue glass bowl",
which is one. It also moves the camera, "viewed slightly from above", against the
request's "photographed from the front". The image model followed the rewrite on the
bowl and the camera, and neither the rewrite nor the request on the cups: it drew four.
What did improve is that the cups are separate cups with white interiors, where the raw
image had fused three into one piece. That is a rendering change, and it has nothing to
do with the count. On a prompt that is nothing but a count, the rewrite broke the count.

From 28 text tokens to 432, each step cost 1.45x, against 1.39x from the cost model. The
image took 669 s longer after a 14.5 s rewrite.

### Six pairs, added up

Every check below is one the request itself states: a count, a string, a position, an
alpha value. None of them is a judgement of taste.

| the request asked for | raw prompt | 0.8B rewrite | 9B rewrite |
|---|---|---|---|
| glass: a transparent background | no: 1.30% of pixels below alpha 32 | no: 0.00% | no: 0.00%, a painted checkerboard |
| glass: clear glass, green oil, cork | yes | no: dark tinted glass | yes, plus leaves in the oil |
| poster: five strings, exact | 5 of 5 | 3 of 5 | 5 of 5 |
| sign: above the door | yes | no: on the wall beside it | not run |
| sign: 面馆, NOODLE and BAR legible | 2 of 3 | 2 of 3 | not run |
| sign: steam from the doorway | yes | no | not run |
| cups: three red, two blue | yes, fused | four red, one blue | not run |
| cups: red to the left of blue | yes | yes | not run |
| cups: photographed from the front | yes | no: from above | not run |

Of the nine checks, the raw prompts pass seven and the 0.8B's rewrites pass one. On
every prompt, the rewritten image is less faithful to the request than the raw one.
The 9B's rewrites pass two of the three checks they were given, and fail the third
along with everyone else.

The step cost, pair by pair:

| pair | text tokens, raw to rewrite | step, measured | step, cost model |
|---|---:|---:|---:|
| glass · 0.8B | 41 to 351 | 1.52x | 1.29x |
| poster · 0.8B | 75 to 371 | 1.35x | 1.27x |
| sign · 0.8B | 49 to 500 | 1.33x | 1.42x |
| cups · 0.8B | 28 to 432 | 1.45x | 1.39x |
| glass · 9B | 41 to 464 | 1.42x | 1.40x |
| poster · 9B | 75 to 299 | 1.05x | 1.21x |

And the four predictions:

1. **Holds.** No rewrite, from either student or from the teacher, kept the
   transparency sentence, and both rewritten bottles came back with no transparent
   pixel at all. The raw prompt had failed here first, so what the rewrites lost was a
   1.30% fringe, not a working alpha channel.
2. **Half holds.** The 0.8B's steps cost 1.33x to 1.52x, a mean of 1.41x: three of four
   inside the predicted range and one just above it. "More with the 9B's" is wrong, and
   the table above already said so before anything rendered. What sets the step is the
   rewrite's length, not which model wrote it, and this run's 9B wrote no longer than
   the 0.8B did, a median of 424 text tokens against 432. The cost model itself held:
   measured over modelled runs from 0.87 to 1.18 across the six, a mean of 1.02.
3. **Holds.** The 9B thought for 477 to 851 tokens, six of seven prompts inside the
   range and one just under it.
4. **Holds.** The checkerboard is in the pixels, and every pixel is opaque.

These are one draw of each rewriter on four prompts, and I do not generalise the
counts. What six pairs can show is *how* a rewrite fails, and all four of the 0.8B's
fail the same way. The rewrite replaces the request with a description of a picture,
and the image model draws the description. On a CPU that costs 33% to 57% more time
per image, 669 s to 1,041 s here, of which the rewriter itself is 11 s to 19 s.

## The licence

The pocket models are for research and evaluation, and nothing else.

| Component | Licence |
|---|---|
| Base weights: `Qwen/Qwen3.5-0.8B`, `Qwen/Qwen3.5-2B` | Apache-2.0 |
| Teacher: `Qwen/Qwen-Image-2.1-PE-T2I` | Qwen Research License, non-commercial |
| The image model they exist to feed: `Qwen/Qwen-Image-2.1` | Qwen Research License, non-commercial |
| Distillation dataset (teacher outputs) | Qwen Research License, by the distributor |
| **The pocket models** | **Qwen Research License, by the distributor** |

**Measured:** six `LICENSE` files in that chain are byte-identical, SHA-256
`8dc973f0…`. They are Qwen-Image-2.1's, PE-T2I's, the GitHub repo's, the dataset's and
both pocket models'.
The agreement grants use "FOR NON-COMMERCIAL PURPOSES ONLY" and defines non-commercial
as "for research or evaluation purposes only".

**Reasoned**, and I am not a lawyer. Three separate things put the students under those
terms. The distributor licenses them that way, in the README, the NOTICE and the
LICENSE. The teacher outputs they were trained on could only be generated by using a
research-licensed model, which is itself only permitted for research or evaluation.
And the one thing a pocket rewriter is for is feeding Qwen-Image-2.1, which carries the
same restriction. Pairing one with an Apache-2.0 image model such as Qwen-Image-2512
does not change the terms you received the rewriter under.

Their compliance is better than most repackaging I have read. There is a copy of the
agreement (§3(a)), a NOTICE with the exact attribution text (§3(c)), a modification
statement (§3(b)), and "Built with Qwen" at the top of every card (§4(b)). Three gaps
remain:

- The model cards' metadata says `license: other` with no `license_name` or
  `license_link`, so the Hub cannot filter them as `qwen-research`. The dataset card
  carries both fields, and both Spaces carry `license_name`.
- There is no copy of Apache-2.0 for the base weights, which that licence's §4(a) asks
  for. The README names the licence and links the base model.
- Clause 4(c) of the Qwen agreement reads:

  > You shall not use "Qwen" as the primary name or identifier of any derivative works
  > or products; reasonable descriptive use (e.g., "fine-tuned from Qwen Image") is
  > permitted.

  The repos are named `Qwen-Image-2.1-PE-T2I-Pocket-0.8B` and `-2B`. The training runs
  pushed to `image21-pocket-rewriter-*`, and the GGUF still carries that name. Whether the new names are descriptive use or primary naming is Qwen's call, not
  mine. I would have kept the old ones.

## The take

The distillation is competent, and its paper trail is unusually good. The teacher
labels, the filter flags, the training arguments, the prediction files, the evaluation
code and its raw scores are all published. That is the only reason I could find the
problems. The pocket models do what the text metrics say: they emit the teacher's JSON
at the teacher's format reliability, with no system prompt and no thinking, in 453
tokens instead of 1,666. As a replacement for the 9B's *output format*, they work.

As a replacement for the 9B's *effect on pictures*, nothing shows it yet, and my own
run points the other way. The one image metric in the release that measures anything
detects the teacher's gain on rendered text and cannot tell either student from no
rewriter at all. The one that was meant to measure everything else picked image A 240
times. On four CPU renders, the 0.8B's rewrites made every picture less faithful to its
request: a broken count, two new text errors, a sign moved off the door, and the last of
the transparency gone. Each time, the rewrite replaced the request with a description
of a picture, and the image model drew the description. The training set offers a
reason to expect exactly that: a length cut turned into a curriculum of stickers and
logos, with 35 examples of the text-heavy layouts where a rewriter earns its keep.

The teacher is no clean win on a CPU either. It matched the raw prompt's poster, string
for string, six minutes of rewriting later, and it answered the transparent bottle with
a painted checkerboard. On these four prompts, one draw each, the request as the user
typed it was the most faithful prompt and the fastest one.

Two fixes are cheap, and I would make them before the next release. First, give the
judge enough tokens to finish, parse its verdict from after its reasoning, and re-score
the 240 pairs. That needs no new renders. Second, raise the word ceiling, or truncate
long rewrites instead of discarding them, so posters and screens survive into the
training set. That costs one more fine-tune, at about 16 USD by their own account.

<ChangeMyMind>
  <Falsifier claim="The published pairwise-judge result measures slot position, not image preference.">
    **Measured**, from `eval/score_4arm.json`: in 240 of 240 `judge.units` the winner is
    `arm_a`, and the per-arm win counts equal the per-arm A-slot counts exactly. What
    would narrow this is a re-score of the same 240 pairs with the verdict parsed from
    the end of the reply, or with the judge's thinking turned off. If that still gives
    every arm about 50%, then "the judge could not separate the arms" becomes a real
    finding. My point would shrink to "it had not been measured", and the pocket models
    would have a second null result rather than a missing one.
  </Falsifier>

  <Falsifier claim="On the release's own OCR data, the 9B's rendered-text gain over no rewriter is detectable and neither student's is.">
    **Measured** from `ocr.by_request`, with an exact two-sided sign test per span: the
    teacher beats no rewriter 11–2 (p = 0.022), the 0.8B goes 8–7 (p = 1.00) and the 2B
    goes 6–3 (p = 0.51). This is 29 spans with one seed per arm, and the seeds differ
    between arms. A re-render of the same 40 requests at several shared seeds, where
    either student beats no rewriter at p below 0.05, falsifies the second half. The
    teacher coming out level with no rewriter over more seeds falsifies the first.
  </Falsifier>

  <Falsifier claim="The 400-word ceiling made the training set 70% stickers and logos, and that, more than model size, is why the students lose the teacher's text gain.">
    The composition is **Measured**: 1,236 of 1,776 rows, joined on request text. The
    causal half is **Reasoned**. A student trained on a category-balanced set, with long
    rewrites truncated rather than dropped, would test it. If that student shows the
    same OCR gap on posters and screens, the gap is capacity rather than curriculum and
    I have the explanation wrong. The 2B does not settle it: 2.5x the parameters, the
    same curriculum, and 61 words against the 0.8B's 64.
  </Falsifier>

  <Falsifier claim="The teacher thinks for about 800 tokens per rewrite, not 1,600; the 1,600 is thinking plus answer.">
    **Measured**: I tokenized the stored `thinking` text of all 8,797 rows with the
    shipped tokenizer and got a mean of 805 against a mean `gen_tokens` of 1,666. The
    two halves sum to within 0.6% of the total. If the stored `thinking` field is a
    trimmed or post-processed copy of what the model generated, my count is low. The
    9B arm of the CPU run measured it directly, on the gallery's seven prompts at
    Q4_K_M: 477 to 851 thinking tokens, median 623. That is one quantisation and one
    sample per prompt, so it corroborates the count rather than replacing it.
  </Falsifier>

  <Falsifier claim="On a runtime without the prefix cache, at 512², a 0.8B rewrite makes each denoising step about 39% more expensive.">
    **Reasoned**, from a cost model with no timing in it: 13d² multiply-accumulates per
    token per layer, plus block-causal attention pairs, at 1,024 image tokens and 34
    against 446 text tokens. The four-core run timed it, **Measured**: 1.33x to 1.52x
    across the 0.8B's four pairs, a mean of 1.41x, and measured over modelled from 0.87
    to 1.18 across all six pairs. The one ratio below 1.2x, the 9B's poster at 1.05x,
    came from a 299-token rewrite the model itself put at 1.21x. The same pairs landing
    below 1.2x or above 1.6x on other hardware, or at other image sizes, would mean the
    model is missing something that dominates there, and the claim that the denoiser,
    not the rewriter, is where a rewritten prompt costs time on a CPU should go with
    it.
  </Falsifier>

  <Falsifier claim="The pocket models can be used for research and evaluation only.">
    **Reasoned** from three licences, none of which I am qualified to interpret
    authoritatively. A statement from Qwen that models trained on PE-T2I's outputs are
    not bound by the Research License, or a relicensing by ML-Intern-lab that Qwen
    accepts, would change the first two legs. It would not change the third: every
    image these rewriters are for is made by a model under that licence.
  </Falsifier>
</ChangeMyMind>

---

*Sources, all read directly. The two model repos,
[`ML-Intern-lab/Qwen-Image-2.1-PE-T2I-Pocket-0.8B`](https://huggingface.co/ML-Intern-lab/Qwen-Image-2.1-PE-T2I-Pocket-0.8B)
and [`-2B`](https://huggingface.co/ML-Intern-lab/Qwen-Image-2.1-PE-T2I-Pocket-2B): README,
LICENSE, NOTICE, `config.json`, `chat_template.jinja`, `training_args.bin` (disassembled,
never unpickled), and both safetensors headers by range read. The dataset
[`ML-Intern-lab/Qwen-Image-2.1-rewriter-distill`](https://huggingface.co/datasets/ML-Intern-lab/Qwen-Image-2.1-rewriter-distill)
at `48e631c`: its card, the scripts in `code/` that label, filter, train, render and
score, `labels_full.jsonl`,
`requests_full.jsonl`, `sft_train.jsonl`, the five prediction files,
`eval/metrics.md`, `eval/score_4arm.json` and four of its contact sheets. The Spaces
[`Qwen-Image-2.1-rewriter-arena`](https://huggingface.co/spaces/ML-Intern-lab/Qwen-Image-2.1-rewriter-arena)
and [`Qwen-Image-2.1-pocket-studio`](https://huggingface.co/spaces/ML-Intern-lab/Qwen-Image-2.1-pocket-studio).
Qwen's [`Qwen/Qwen-Image-2.1-PE-T2I`](https://huggingface.co/Qwen/Qwen-Image-2.1-PE-T2I):
README, `system_prompt.txt`, `chat_template.jinja`, `config.json`, the safetensors
index and all four shard headers. The GitHub repo
[`QwenLM/Qwen-Image-2.1`](https://github.com/QwenLM/Qwen-Image-2.1) at `fb7ae1d`, for
`prompt_rewrite/`. The base models [`Qwen/Qwen3.5-0.8B`](https://huggingface.co/Qwen/Qwen3.5-0.8B)
and [`Qwen/Qwen3.5-2B`](https://huggingface.co/Qwen/Qwen3.5-2B), for licence and size. The
community GGUF [`prithivMLmods/Qwen-Image-2.1-PE-T2I-GGUF`](https://huggingface.co/prithivMLmods/Qwen-Image-2.1-PE-T2I-GGUF),
for file sizes and its declared parameter count. The diffusers
`pipeline_qwenimage21.py` and stable-diffusion.cpp at `2bb7294`, for how each builds the
denoiser's text sequence. Word counts split on whitespace; token counts use each model's
shipped `tokenizer.json`. The four figures are the dataset's own contact sheets, served
locally with a NOTICE and the licence beside them.*
