Use
The adapter loads through the standard classes: the base model with AutoModelForMultimodalLM, the adapter with PeftModel,
the processor from this repository (the base processor's files are here). Needs transformers 5 and peft 0.18 or newer;
thinking is off; decoding is greedy; no system prompt (the row's form is learned, and a system turn she was not trained with
makes her over-write).
import torchfrom peft import PeftModelfrom PIL import Imagefrom transformers import AutoModelForMultimodalLM, AutoProcessor repo = "AbstractPhil/qwen3.5-9b-think-symbolic-image"processor = AutoProcessor.from_pretrained(repo)model = AutoModelForMultimodalLM.from_pretrained("Qwen/Qwen3.5-9B", dtype=torch.bfloat16, device_map="cuda")model = PeftModel.from_pretrained(model, repo).eval() # .merge_and_unload() for a plain model image = Image.open("picture.jpg").convert("RGB")prompt = ("Look at this picture and think it through in writing, a closer look; then write a caption and a prompt or tags, " "and the questions a careful looker would still ask, each as `question: what -> answer [record, chain or unknown]`.")messages = [{"role": "user", "content": [{"type": "image", "image": image}, {"type": "text", "text": prompt}]}]text = processor.apply_chat_template(messages, add_generation_prompt=True, tokenize=False, enable_thinking=False)inputs = processor(text=[text], images=[image], return_tensors="pt").to(model.device)out = model.generate(**inputs, max_new_tokens=400, do_sample=False)print(processor.batch_decode(out[:, inputs["input_ids"].shape[1]:], skip_special_tokens=True)[0])
For batches set processor.tokenizer.padding_side = "left" and pass a list of texts and images. The linear-attention kernels
(flash-linear-attention, causal-conv1d, triton) make the 9B several times faster; without them transformers falls back to its
reference implementation and says so once. The original release's prompts and forms are unchanged (the whole row from a picture,
the chain and a caption from a picture, the whole row from a specification); the package
qwen-symbolic-thinking writes them (prompts.full, prompts.short,
prompts.spec_full) and parses the answers back (chain.parse).
What she does on held-out rows
Read the same way as the original: 60 pictures and 40 specifications the adapter never saw, against the dataset's own rows
(row shape, said / backed, definitions); then the medium term on two sets no training row came from: 500 National Gallery of Art
pictures (100 drawings, prints, paintings, photographs and objects each) and 200 pictures of AIC, NGA, Cleveland and the Met.
Table with columns: read, Lucia v1, the original (legacy)| read | Lucia v1 | the original (legacy) |
|---|
| row shape, pictures / specifications | 1.00 / 1.00 | 1.00 / 1.00 |
| said / backed, whole rows | 0.19 / 0.30 | 0.21 / 0.31 |
| definitions, pictures / specifications | 0.02 / 0.08 | 0.02 / 0.10 |
| 500 held-out pictures: painting / print / drawing / photograph / object named rightly of 100 | 83 / 76 / 86 / 80 / 90 | 80 / 4 / 20 / 76 / 67 |
| 200 museum pictures: painting / print / drawing / photograph / object | 13 of 27 / 38 of 39 / 12 of 13 / 11 of 13 / 66 of 66 | 11 of 27 / 7 of 38 / 1 of 13 / 12 of 13 / 58 of 63 |
The museum painting row understates her: of the 14 catalogue paintings she did not name so, 7 are watercolours or gouaches on paper that she names drawings, as the National Gallery's own catalogue does, 2 are paintings on silk she names photographs, and 5 are objects or prints that the catalogue-line ruler mislabelled as paintings. Every oil on canvas or panel is named a painting.
A second seed of the same recipe (seed 20261031, the same rows and settings) reproduced the medium term: 83 / 80 / 84 / 80 / 90 on the
500 held-out pictures and 13 of 27 / 37 of 39 / 12 of 13 / 12 of 13 / 64 of 66 on the museum pictures, with said / backed 0.20 / 0.29. Its
definitions on specification rows read 0.25 against this seed's 0.07: that 40-row read is the noisiest line of the table and does not
judge the recipe at one seed. The reads are made with qwen_symbolic_thinking.tools (probe_read, museum_read, museum_mismatch,
screen_conflicts), so any arm can be read the same way.
The rest of the row (the looking, the questions, the moods) reads at the original's level: the medium rows change the prompt
line's first term and nothing else was asked of them.
Training
Table | |
|---|
| base | Qwen/Qwen3.5-9B, bf16, the vision tower frozen |
| adapter | LoRA rank 32, alpha 64, dropout 0.05 on the language model's q, k, v, o, gate, up, down, in_proj_qkv, in_proj_z and out_proj projections (80.2 M trainable parameters) |
| data | the original recipe (951 picture rows taught twice, 500 long-look drawn rows, 2,000 synthetic closer looks) plus 17,385 museum rows: the original adapter's own rows about pictures of the National Gallery of Art, the Metropolitan Museum, the Art Institute of Chicago and the Cleveland Museum whose catalogue gives the medium, the prompt line's first term set to it (6,606 drawings, 3,523 paintings, 3,000 prints, 2,016 photographs, 2,240 objects); the 500 probe pictures held out |
| optimiser | plain Adam 2e-4, cosine schedule, 3% warmup, batch 8 x 2, two epochs (2,724 steps, 5.71 s a step, 259 minutes on an RTX PRO 6000), the loss on the answer only |
| what the medium rows did | a one-sided set (paintings, prints, drawings only) made every museum picture a painting or a print; the two-sided set (photographs and objects kept as photographs) fixed the boundary; more drawings moved the drawing term most, from 20 to 86 of 100 |
The arms, the screens, the training sets and the extractions are in
AbstractPhil/qwen-symbolic-thinking-image-training
(this adapter is lora/Qwen3.5-9B/r32/20261031_085915_G_med4_2ep/final there). The picture list the museum rows were written about is
AbstractPhil/qwen-thinking-images (manifest v1.5: the more,
more5 and probe splits).
Versions
Table with columns: version, where| version | where |
|---|
| Lucia v1 (2026-10-31) | the root of this repository; the tag lucia-v1 (revision="lucia-v1") |
| the original (E_all_2ep, 2026-10-30) | legacy/E_all_2ep/ (subfolder="legacy/E_all_2ep"), with its card and training record; the tag legacy-e-all-2ep is the pre-swap revision |
The root always holds the current version; each version keeps a tag, so PeftModel.from_pretrained(model, repo, revision="lucia-v1")
loads this one whatever the root holds later.
Limits
Quick looks are the fragile depth: one thought can name a wrong thing. Specification rows (no picture) still carry a defining
thought in about one row in ten. The medium is named from the picture, so a watercolour or gouache on paper that a museum catalogues as a painting is named a
drawing, the National Gallery's convention, which the museum rows taught; the museum rows are the adapter's own rows corrected on
one line, not human rows. English only. The row's
conventions (the steps, the depths, the question form) are the dataset's.
Files
adapter_config.json, adapter_model.safetensors (the LoRA), train_summary.json (the run's record), the base processor's
files (preprocessor_config.json, video_preprocessor_config.json, tokenizer.json, tokenizer_config.json, vocab.json,
merges.txt, chat_template.jinja) so AutoProcessor.from_pretrained works on this repository, and legacy/E_all_2ep/.
License
Apache-2.0, like Qwen3.5. The dataset's rows carry their own licences per row; the museum pictures are public-domain works
(CC0 catalogues); the COCO images the synthetic rows were written about are not redistributed.