J-Lens for images β€” reading what a model "thinks" about each part of a picture

When a vision-language model looks at an image, it breaks it into a grid of small squares ("patches") and quietly forms impressions about each one before it writes a single word. This is a tool for reading those impressions. For any patch β€” or any word the model writes back β€” it shows the words that patch is disposed to make the model say: a peek at the model's private notes.

It builds on Anthropic's Jacobian lens β€” a method for reading a language model's internal "workspace" β€” and extends it, here, to the image side of the vision-language model Qwen3.5-27B. This repo is the fitted lens (jimage_final_lens.pt) plus a short usage script.

The striking thing: the readouts are inferences, not labels. A patch of blank sky can read distant city; a small shape on a roof can read cross β†’ church; a person too small to make out can still read purple (their jacket). Three examples:


πŸ›• A temple β€” and inferences beyond it

temple

  1. The tower β†’ tower Β· tall Β· towering. It also correctly names the building (Meenakshi Amman Temple, Madurai), and nearby patches read temple Β· Hindu Β· religious, pyramid (its stepped shape) and even scaffolding / unfinished.
  2. Empty sky β†’ city Β· urban Β· distant. From an almost featureless hazy patch, the model infers a distant city.
  3. A smaller structure beside the tower β†’ smaller Β· taller Β· shorter. Not a label β€” the model is comparing sizes between buildings.

β›ͺ A church β€” recognising a symbol, and a tiny detail

church

  1. The cross on the steeple β†’ cross Β· crucifix. From a small shape it infers the Christian symbol (and, in the readout for the tower below it, church Β· bell Β· Christianity).
  2. A distant pedestrian β†’ purple β€” the model reads the colour of the person's jacket, and the patch below reads walking. A fine-grained inference about someone barely a few pixels tall.

🏟️ A stadium β€” inferring the sport

stadium

  1. The green field β†’ football Β· football-field Β· stadium. From grass and painted lines it infers the sport β€” and other patches read touchdown Β· NCAA Β· seating.

Every readout is verified by the model's own vocabulary head, so these are genuinely the words each patch pushes the model toward β€” a small window into how it sees.


For the curious β€” how it works

The lens is a set of matrices J_l (one per layer). Applying J_l to a patch's internal vector and decoding with the model's own output layer gives the readout:

readout_l(h) = softmax( W_U Β· norm( J_l Β· h ) ), where J_l = E[ βˆ‚h_final,t' / βˆ‚h_l,t ]

J_l is the average effect a patch's activation has on what the model eventually says. We fit it with image tokens as the source and the text that follows the image as the target β€” so it captures how each patch shapes the model's answer. (Plain "logit-lens" fails on image tokens; the Jacobian's future-averaging is what makes this work.)

How it was fit

  • Model: Qwen/Qwen3.5-27B (bf16, 64 layers, d_model 5120; unified vision-language, early fusion; image placeholder token id 248056).
  • Data: 30 single-image windows (retrieval converges from as few as 3; see the paper's corpus-size study) from MMDU-45k (one image + the turn describing it).
  • Sources = image-token positions; targets = the answer text. Source layers L16–62 (step 2, 24 layers), target L62, seq 256, bf16 (never quantized β€” the lens is a derivative on a low-variance subspace, so precision matters).
  • Layer note (a real finding): image-source influence β€–JΜ„_lβ€– sits on a broad plateau from L16 through L54 before collapsing (L62 β‰ˆ 0 β€” it's the target layer), but readout quality peaks at L40–50 (held-out retrieval maximum at L46 β€” a good default; L50 is equivalent). L62 is a degenerate/empty layer.

Usage

pip install "transformers==5.8.1" accelerate flash-linear-attention pillow \
    git+https://github.com/anthropics/jacobian-lens.git
python apply_example.py your_image.jpg
from jlens.lens import JacobianLens
lens = JacobianLens.load("jimage_final_lens.pt")     # source layers 16..62; use 46
# h = a patch's residual [d] from Qwen3.5's language_model at layer 46
logits = model.lm_head(model.norm(lens.transport(h, 46)))   # top-k words = the readout

apply_example.py (with the bundled mm.py adapter) shows the full path, including mapping an image-token index to its (row, col) patch via the processor's image_grid_thw (patch 16 Β· spatial-merge 2 β†’ each token β‰ˆ a 32Γ—32-px region).

Files

  • jimage_final_lens.pt β€” the fitted lens (layers 16–62, n=30; ~1.2 GB)
  • jimage_L48-62_n120_lens.pt β€” the original release (layers 48–62, n=120; ~400 MB), kept for reproducibility β€” per-layer Jacobians are independent, so its layers agree with the full-depth fit
  • apply_example.py, mm.py β€” minimal usage + the multimodal jlens adapter
  • examples/ β€” the highlighted example images above

Notes

  • Chinese words appear in some readouts because Qwen3.5's vocabulary is multilingual β€” the same concept often surfaces in several languages (e.g. city / εŸŽεΈ‚).

License & lineage

cc-by-nc-4.0, inherited from the MMDU data used to fit the lens (non-commercial). The base model is Apache-2.0; the Jacobian-lens method and jlens library are Apache-2.0 (anthropics/jacobian-lens). Example images are Wikipedia-sourced via MMDU, shown for illustration.

Acknowledgements

Method: Verbalizable Representations Form a Global Workspace in Language Models (Anthropic, 2026). Model: Qwen team. Fitting data: MMDU (Dong, Zhang et al.).

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Model tree for kieraisverybored/jlens-qwen3.5-27b

Base model

Qwen/Qwen3.5-27B
Finetuned
(347)
this model

Space using kieraisverybored/jlens-qwen3.5-27b 1