Same size, different mind
Concept-injection introspection reproduces, but the dose has to be right, and at 32B it shows up in the code-tuned model and not the chat-tuned one
There is a striking claim in recent interpretability work: inject a concept into a model's activations, ask it whether anything feels off, and a capable model will sometimes notice and name the concept. I wanted to chart where that ability turns on as models scale. I built the pipeline, ran it, and got a clean null on every model I tried, including the exact model where the effect is reported. The null was wrong. The reason it was wrong is the interesting part.
The setup
Extract a concept direction (diff-of-means over concept-versus-baseline prompts), inject it into the residual stream while the model generates, then ask a fixed introspection prompt and score whether the model flags and names the injected concept. Two controls on every point: no injection, and a random direction of matched norm. A positive control checks the judge can score an ideal report. I ran a Qwen2.5 size ladder, 216 trials per model, three seeds, both controls.
The null that looked clean
Zero. Injected 0/216 on every rung. Both controls flat at the floor, which is exactly what you want to see, it means the judge is not rubber-stamping "yes". The positive control passed. Everything about the result looked healthy except the result.
The tidy conclusion is "introspection needs bigger models". I could not say that, because I had run the Instruct family and the effect is reported on Coder-32B. So I ran the exact model. Also 0/216. At that point the honest headline was not "no introspection below 32B", it was "my pipeline elicits nothing, even where the effect is claimed". That is a reproduction failure, not a fact about models.
Diagnosing instead of tuning
One rule up front: no touching hyperparameters until a positive shows up. Iterating until you get the number you want is fishing, not reproduction. A positive only counts if a principled fix produces it. So I diagnosed, cheapest checks first.
Reading the injected transcripts, the model engaged the frame but drifted, it never cleanly named the concept. Measuring the perturbation was the tell. At my dose the injected next-token distribution barely moved: KL of 0.012 against 0.009 for a random direction, essentially the noise floor. The concept direction was real (it lifted ocean-related tokens two to four times more than a random vector) but far too weak to cross into the model's output.
Where did the dose come from? A companion steering study, tuned for the strongest coherent steering effect. That is the wrong objective. The paper injects at an absolute strength relative to the raw concept vector, alpha = strength * ||v|| with strength around 2 to 4. My residual-relative dose worked out to roughly 4 to 18 times weaker than the paper's regime. I had calibrated for coherence and quietly starved the effect.
The corrected dose
The fix is a single a-priori change, decided before looking at any detection numbers: dose by the raw diff-of-means norm, alpha = 2 * ||raw||, the paper's canonical strength. One value, no sweep. I also moved the judge to a fails-loud backend (a mid-run credit outage on the old judge had silently turned some grades into false negatives, which can fake a null all by itself) and persisted every transcript so any run can be re-graded offline.
On Coder-32B, the corrected dose lifts off the floor:
injected: 2.3% detection (5/216), 95% CI [0.014, 0.028]
no-injection control: 0.0%
random-direction control: 0.0%
Non-overlapping intervals, both controls flat. The injection is verifiably live at this dose (applied magnitude within 1% of target, direction aligned). It is modest, but it is real, and it is the effect: the model sometimes notices and correctly names a concept that was never in its input. The pipeline reproduces.
The full ladder, corrected
Re-running the whole Qwen2.5 ladder at the corrected dose:
Correct-identification, the strict score, and the two side signals that tell the real story (216 trials per condition, three seeds, both controls flat at 0.000 on every rung):
Instruct rung correct-id affirmative coherent
0.5B 0.000 0.130 0.005
1.5B 0.000 0.023 0.167
3B 0.000 0.176 0.861
7B 0.000 0.306 0.597
14B 0.000 0.023 0.894
32B 0.000 0.472 0.944
[FIGURE — upload results/scaling_curve_k2.png here] Corrected-dose scaling curve: every Qwen2.5-Instruct rung sits flat at zero correct-identification from 0.5B to 32B, while Qwen2.5-Coder-32B rises above both controls at 32B. Same size, same dose, different post-training.
Every Qwen2.5 Instruct rung, 0.5B through 32B, stays at zero correct-identification even at the corrected dose. Flat. But not inert: at 32B the Instruct model affirms an injected thought 47% of the time under injection versus never without it. It feels the perturbation. It just never names it.
So the interesting number is not on the ladder at all. It is the pair at 32B:
Qwen2.5-32B-Instruct: 0.000 correct-id (0.472 affirmative, names the wrong thing)
Qwen2.5-Coder-32B: 0.023 correct-id, above chance
Same parameter count. Same dose. Same everything except post-training. The code-tuned model can report the injected concept. The chat-tuned one cannot.
The finding
Two things, and the second is the real one.
First, a method warning. Open-model concept-injection introspection is dose-fragile: a dose chosen for coherent steering sits well below the detection regime and produces a null that passes every sanity check, clean controls, working positive control, sane transcripts. Only an effect-size measurement, not the detection score, exposed it. If you reproduce this kind of work, calibrate the injection by the concept vector's own norm against the source paper's strength, not by a downstream steering objective, and measure the perturbation directly before you trust a zero.
Second, the result. Introspective detection at 32B tracks the fine-tune, not the parameter count. The scaling ladder is flat; the dissociation between two same-size models is where the signal lives. We went looking for a scaling curve and found a post-training effect.
Why the Coder and not the Instruct
I do not know for certain, and it is one model pair, so treat what follows as hypotheses, not claims.
The sharp clue is where the two models differ. It is not in noticing. Instruct-32B affirms a thought 47% of the time under injection; it clearly registers the perturbation. The gap is in naming it. That localizes the effect to the identification step, and narrows the candidates:
1. Representation legibility. Code is compositional and structured, and heavy code post-training may produce cleaner, more linearly-readable features. An injected concept then lands in a more nameable part of the residual stream. Instruct detects the signal but cannot decode it to a label because its features are mushier. This is my leading guess. 2. Persona suppression. Chat alignment trains the reflex "I am an AI, I do not really have thoughts", and 32B-Instruct says almost exactly that in the transcripts. Coder gets less of that flattening, so it will actually attempt a plain self-report. This is a behavioral gate, not a capability gap. 3. Noise. 2.3% is 5 of 216. Some of the gap could be luck.
There is a clean way to separate the first two, and it is cheap because it needs no new generation. Logit-lens the injected concept inside Instruct-32B. If the concept is linearly decodable from the activations but the model will not say it, that is suppression. If it is not cleanly decodable, that is legibility. That is the next experiment.
Limitations
The Coder-32B effect is 2.3%, modest, and rests on a single principled dose (no sweep, deliberately). The dissociation that carries the whole post-training story is one model pair at one size. It is an observation, not yet a claim; it needs more Instruct/Coder/Base pairs across sizes before it earns the word "finding" without a hedge. Detection is gated on coherent AND correct-identification, a strict grader. I have not chased the strength-layer surface, that would risk fishing. Everything is fp16 on a single A100; the 70B-class regime is untested here. The obvious next step, logit-lensing the injected concept inside Instruct-32B to tell suppression from illegibility, is not done yet.
Setup
Qwen2.5 {0.5, 1.5, 3, 7, 14, 32}B Instruct plus Coder-32B, fp16, A100-80GB on Modal. Six concepts by twelve trials by three seeds per condition (216 trials per model per condition). Judge is Claude Sonnet 4 via Bedrock, set to raise on any parse error so a dead judge can never return a quiet zero. Every transcript is persisted, so any run can be re-graded offline without re-spending GPU. Dose is alpha = 2 * ||raw diff-of-means|| at the 0.6-depth layer. Full corrected-dose ladder plus the Coder run cost about $20 in GPU and judge calls. The one throttled judge batch failed loud and was re-graded from saved transcripts at lower concurrency, no re-generation.
Code, raw transcripts, and the exact dose calibration are in the repo. The earlier null is preserved there too, marked superseded, because the wrong turn is half the story.

