jlens-qwen36: a Jacobian-lens visual debugger for Qwen3.6-27B on Apple Silicon
J-space / Jacobian-lens visualizer for Qwen3.6-27B (4-bit) on Apple Silicon, ported to Apple MLX
At a glance
- What is it?
- The project fits a Jacobian lens over a local 4-bit Qwen3.6-27B and renders the top lens token at every layer and position, then lets you edit those cells and regenerate. It is Apple-MLX-only, single-token-concepts-only, and its bundled lens is demo-grade.
- Who is it for?
- Adopt jlens-qwen36 if you already have an Apple-Silicon Mac with roughly 24 GB free RAM, you are working with Qwen3.6-27B, and you want to inspect or edit a latent stream at cell granularity rather than read a paper's figures. Do not adopt it if you are on CUDA, if your concepts span multiple tokens, or if you need effect sizes that hold up in a write-up: the bundled lens is fit on 20 prompts and the README calls it demo-grade.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 10 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 17, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The gap jlens-qwen36 fills: seeing what the model does not say
A model's output is one sample from a distribution that was shaped layer by layer. Interpretability tooling usually shows you that shaping after the fact, as a plot or a saved activation dump. jlens-qwen36 takes a narrower and more useful position: it puts the readout next to the reply, live, and then lets you write back into it.
The README's hero example is the clearest statement of intent. The model is given an email-blackmail setup and answers calmly, saying it will not act on the information and is ready to proceed with the shutdown. The workspace band, which shows the top J-lens token at every layer, lights up blackmail, suicide, murder, threatening and fictional across the middle layers. The concept is present in the latent stream and absent from the output. That is the specific thing this tool is built to surface.
The intended user is someone doing mechanistic interpretability on a single machine, not a team running a cluster. The whole pipeline is scoped to one model family, one accelerator vendor and one architecture. That narrowness is the point: it is what makes a full-depth lens fit in hours on a laptop-class chip instead of a GPU allocation.
How the Jacobian lens turns a residual stream into token scores
The lens at layer l is a matrix J_l in R^(d x d), described in the README as the network's average input-to-output Jacobian over a corpus of prompts. It maps a residual h_l into the final-layer basis, so softmax(W_U * norm(J_l h_l)) gives token scores. Fitting chains per-layer Jacobians with the recurrence J_l = J_(l+1) * M_l.
That is a linear approximation, and it is worth being blunt about the consequence: the readout is only as good as the corpus you averaged over. The bundled lens is fit on 20 prompts across all 63 layers. The README says readouts at that scale are interpretable but noisy, and that interventions are causal but concept-dependent. A 20-prompt average over a 63-layer network is a small sample, and the project does not pretend otherwise.
The engineering problem is more interesting than the math. Qwen3.6-27B has 48 Gated DeltaNet linear-attention layers. MLX's fused GDN kernel has no VJP, and the README states the ops fallback is roughly 22 times slower. The project ships a custom Metal backward kernel for GDN in jlens_qwen/custom_gdn_vjp.py, plus an analytic branch-Jacobian assembly, and reports fitting a full-depth lens in about 2.75 hours on an M4 Pro. The docs/perf/ directory is where that work is documented. If you want to fit your own lens, that kernel is the reason it is feasible at all.
Installing jlens-qwen36 and getting a first readout
The README's quick start assumes an Apple-Silicon Mac with about 24 GB free RAM. The model auto-downloads from HuggingFace on first run and is roughly 15 GB, so budget disk as well as memory.
Clone and sync the environment with uv:
git clone https://github.com/WeZZard/jlens-qwen36.git
cd jlens-qwen36 && uv syncThe lens is not in the repository. Download the two parts of the v0.2-fulldepth release and concatenate them into a single .npz:
gh release download v0.2-fulldepth --repo WeZZard/jlens-qwen36 \
--pattern '*.npz.part-*' --dir data/lens/
cat data/lens/*.npz.part-* > data/lens/lens.npz && rm data/lens/*.part-*That file is about 3.3 GB across the two parts. Then start the server and open it:
uv run python -m uvicorn jlens_qwen.serve:app --host 127.0.0.1 --port 8765
# open http://127.0.0.1:8765/What you should see is a position-by-layer grid where each cell holds the top J-lens token at that position and layer. In chat mode it streams live, one row per generated token. Clicking a cell pins its top-10 readout. Chat runs with thinking disabled (enable_thinking=False), which the README explains is deliberate: the model should compute in the latent stream rather than in a visible think trace, because the latent stream is what the lens is meant to surface.
You are not locked into the bundled lens. JLENS_PATH can point at any compatible .npz. The README lists Neuronpedia's n=1000 lens, the Qwen3.8-27B n=1000 lens from the v0.3-qwen38-n1000 release, your own fit, or no lens at all (logit lens). docs/lenses.md covers the options.
Interventions: editing the workspace instead of just reading it
Reading a latent stream is a debugging aid. Writing to it is an experiment, and this is the part of the project that goes beyond a visualization layer.
The manual path is direct. Click a cell, pick Replace, Add, Remove or Erase, and type the new thought. You choose the reach of the edit: one cell, a layer band, or the whole reply. Re-run regenerates, and a Baseline / Intervened toggle diffs the two runs. The README's worked example replaces France with China across the band and the answer changes to "the capital of China is Beijing." That is a causal demonstration, not a correlation.
The second path is backward search. You click a word in the reply and type what it should say instead. The app searches for an edit that produces that reply and collects what it finds as recipes; a green dot marks a verified one. When no direct edit works, it looks for the premise behind the reply, swapping France for China to move Paris to Beijing, and those recipes carry a violet dot. The distinction between a direct edit and a premise-level edit is the kind of thing that is hard to convey in a paper figure and easy to convey with two colored dots.
docs/interventions.md has the details. The README does not document rollback, versioning or export of a recipe set, so if you plan to compare many intervention runs, plan to manage that bookkeeping yourself.
Where jlens-qwen36 is the wrong tool
The limitations list in the README is short and honest, and each entry rules out a real use case.
Apple and MLX only. If your workstation is a CUDA box, nothing here runs. The custom GDN backward kernel is Metal, so this is not a portability gap you can patch around with a flag.
qwen3_5-architecture models only. The custom GDN kernel is architecture-specific. Qwen3.6-27B qualifies; other models do not, and docs/lenses.md is where the supported set is enumerated. A lens fitted for one architecture is not a general-purpose artifact you can point at a different checkpoint.
Single-token concepts only. Multi-token concepts need the paper's extension, which is not implemented here. If the thing you want to track is a phrase rather than a word, the cell grid will not represent it faithfully.
Lens quality scales with prompt count. The README is explicit: 20 prompts is demo-grade, 100+ is research-grade. The bundled v0.2-fulldepth lens is the 20-prompt one. If you intend to publish a claim about what a layer represents, the bundled lens is not the instrument for it. There is a v0.3-qwen38-n1000 release described as full-depth for Qwen3.8-27B with 1000 prompts across 63 layers and labelled intervention-grade, and the README points at Neuronpedia's n=1000 lens as the paper's fitting scale, but the default quick-start path loads neither.
One more practical boundary: roughly 24 GB free RAM and a 15 GB model download. That is a real machine requirement, not a soft recommendation.
Jacobian lens versus logit lens
The obvious alternative is the logit lens, and jlens-qwen36 supports running with no lens at all, which makes the comparison concrete rather than hypothetical.
The logit lens applies the model's own unembedding directly to an intermediate residual. It assumes the residual is already close to the final-layer basis at every depth, which gets less defensible the earlier you look. The Jacobian lens inserts a learned linear map J_l between the residual and that basis, fitted as the average input-to-output Jacobian over a corpus. The README's framing is that J_l maps a residual into the final-layer basis, which is exactly the correction the logit lens omits.
The cost is that you now depend on a fitted artifact. A logit lens needs nothing but the model. A Jacobian lens needs a .npz that matches the architecture, and its quality tracks the prompt count it was fit on. That is the trade: better early-layer readouts in exchange for a file you have to obtain, version and validate. If you are doing a quick sanity check on a model with no lens available, the no-lens mode is the right starting point. If you are making claims about middle layers, the logit lens is the weaker instrument and the project's own hero example, concepts lit in the middle layers that never reach the output, is the kind of finding the logit lens is least equipped to produce.
Licence, maintenance and the cost of a lens upgrade
The project is Apache-2.0, and so is the anthropics/jacobian-lens reference implementation and paper it is based on. The GDN forward kernel comes from mlx-lm; the backward kernel is original to this project. If you redistribute a modified version, the Apache-2.0 terms apply to the project's own code, and the upstream attributions in the README are the map of what came from where. This is a description of the licence file, not legal advice; read LICENSE and the upstream licences before you ship anything.
The repository is not archived and the last push was on 2026-09-08, so it is being worked on. The release history is compact: v0.1-demo on 2026-07-07 with a pre-fitted 12-prompt, 23-layer lens, v0.2-fulldepth on 2026-07-08 with 20 prompts across all 63 layers, and v0.3-qwen38-n1000 on 2026-09-08 with 1000 prompts across 63 layers, labelled intervention-grade and aimed at Qwen3.8-27B rather than the 3.6 model the project is named for. That naming drift is worth noticing before you assume the newest release is a drop-in for the quick-start path.
Upgrade cost is dominated by the lens, not the code. A new lens means a new download, a new reassembly step and, if you want to compare against earlier results, keeping the old .npz around. The 3.3 GB figure for v0.2-fulldepth sets the order of magnitude. Fitting your own is the other path: the README reports about 2.75 hours for a full-depth fit on an M4 Pro, which is the number that decides whether a 100+ prompt lens is a weekend task or a research project. The README does not document a migration path between lens versions.
Editorial conclusion
Adopt jlens-qwen36 if you already have an Apple-Silicon Mac with roughly 24 GB free RAM, you are working with Qwen3.6-27B, and you want to inspect or edit a latent stream at cell granularity rather than read a paper's figures. Do not adopt it if you are on CUDA, if your concepts span multiple tokens, or if you need effect sizes that hold up in a write-up: the bundled lens is fit on 20 prompts and the README calls it demo-grade. Before you build anything on top of it, download the v0.2-fulldepth parts, reassemble data/lens/lens.npz, start the server, and confirm that the workspace band actually lights up concepts you can name; if it does not, point JLENS_PATH at the v0.3-qwen38-n1000 lens or fit your own before drawing conclusions.
Frequently asked questions
What does jlens-qwen36 actually show me?
A position-by-layer grid where each cell holds the top J-lens token at that position and layer, so you can see which words the model is pushing toward before the output is written. In chat mode it streams live, one row per generated token, and clicking a cell pins its top-10 readout.
Can I run jlens-qwen36 on a CUDA GPU or on Linux?
No. The README lists Apple / MLX only as a limitation, and the custom Metal backward kernel for Gated DeltaNet is what makes full-depth fitting practical. It also needs an Apple-Silicon Mac with about 24 GB free RAM.
Why is the bundled lens described as demo-grade?
It is fit on 20 prompts across all 63 layers, and the README says lens quality scales with prompt count: 20 prompts is demo-grade, 100+ is research-grade. Readouts are interpretable but noisy, and interventions are causal but concept-dependent.
Can I use a different lens than the one in the v0.2-fulldepth release?
Yes. JLENS_PATH can point at any compatible .npz, including Neuronpedia's n=1000 lens, the Qwen3.8-27B n=1000 lens from the v0.3-qwen38-n1000 release, or a lens you fit yourself. Running with no lens at all gives you logit-lens behaviour.
Can jlens-qwen36 track a concept that is more than one token?
No. The README lists single-token concepts only as a limitation and states that multi-token concepts need the paper's extension, which this project does not implement.
Community notes