Conversation
logits_scaling in the Transformers backend
tdoublep
force-pushed
the
fix-transformers-logits-scaling
branch
from
August 13, 2026 11:31
5cf2998 to
49c4f4a
Compare
Transformers scales the logits in the `ForCausalLM` wrapper, which this backend replaces, so the scale has to be folded into our logits processor. Only `logit_scale` was, so Granite-family and MiniCPM3 logprobs were `logits_scaling`x too confident and HyperCLOVAX dropped its muP multiplier. There is no config field to read the scale from and no single convention for it, so measure it instead: build the model's own `ForCausalLM` class with stubs for its decoder and head, run its `forward` over known hidden states, and take the ratio. Fall back to no scaling, with a warning, if the probe cannot be run or the transform is not linear (logit softcapping). Signed-off-by: Thomas Parnell <tpa@zurich.ibm.com> Co-authored-by: Claude Code
tdoublep
force-pushed
the
fix-transformers-logits-scaling
branch
from
August 13, 2026 11:57
49c4f4a to
2bfc4d5
Compare
Member
Author
|
Closing this. I think it is better addressed at the transformers level by making the behaviour consistent. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose
Transformers scales the logits in the
ForCausalLMwrapper. This backend only loads the decoder and provides its own head and logits processor, so whatever the wrapper does after the decoder has to be reproduced here. Onlylogit_scalewas, which means:logits_scalingx too confident: 9x foribm/PowerLM-3b, 10x foribm-granite/granite-swash-2b.Scaling does not reorder the logits, so the generated tokens are unaffected. That is why this went unnoticed, and it means the impact is on consumers of logprob values: sampling with a temperature or penalties, logprob-based evals, speculative decoding acceptance, and anything ranking sequences by score.
There is no config field or API to read the scale from, and no single convention for it either —
logit_scalemultiplies (Cohere),logits_scalingdivides (Granite) except under muP where it multiplies (HyperCLOVAX),logits_mup_width_multiplierdivides the hidden states (Inkling),dim_model_baseimplies a divisor (MiniCPM3),lm_head_multipliermultiplies (FalconH1). Rather than enumerate that, this PR measures it: at model init, build the model's ownForCausalLMclass with its decoder and head replaced by stubs (no weights, no checkpoint), run itsforwardover known hidden states, and take the ratio. Whatever the wrapper does to the logits is then the only thing left in the output, and the value is inherited from Transformers instead of transcribed from it.If the ratio is not constant across the hidden states, the transform is not a scale (final logit softcapping) and cannot be folded into a logits processor, so we warn and leave the logits alone — as today. Same if the probe cannot be run at all. Neither is a regression, but both are now visible in the log rather than silent.
Found while fixing attention sinks on the same backend (#52156), but independent of it.
Test plan
Three new tests in
tests/models/transformers/test_backend.py:test_get_logit_scalepins the measured scale for one config per convention (Granite, HyperCLOVAX, Cohere, MiniCPM3, Inkling), plus Llama for no scaling and Gemma2 to assert softcapping is not folded in as a scale. Pure CPU, no weights, runs in seconds.test_probe_logit_scale_runs_for_scaled_modelsis the canary for the approach: it sweepsMODEL_FOR_CAUSAL_LM_MAPPING, sets every float config field that looks like a logit multiplier to a non-default value, and asserts the probe can still be run for each. If a Transformers refactor breaks the probe for a model that does scale its logits, we would silently fall back to 1.0; this fails CI instead.test_logits_scalingcompares the spread of the top-5 logprobs between the native and Transformers implementations ofibm/PowerLM-3b. Logprob values have to be compared becausecheck_logprobs_closeonly compares token ids, which a missing division cannot change.Commands and results (1x GB200, transformers 5.15.0):
Mutation checks that the new tests bite:
test_logits_scalingfails on 8/8 positions, max difference 48.06 against a 0.2 tolerance.weightfrom the head stub: the sweep fails on xLSTM, which reads the head dtype off it.What the probe finds across the 173 entries of
MODEL_FOR_CAUSAL_LM_MAPPING, with default configs: 135 apply no scaling, 5 scale (Cohere/Cohere2/Cohere2Moe 0.0625, Inkling 1/24, MiniCPM3 0.1), 7 are nonlinear (Gemma2, Gemma3n, VaultGemma, RecurrentGemma, NanoChat, xLSTM — all final logit softcapping), 2 cannot have a default config built (Musicgen), and 24 wrappers cannot be run by the probe. Those 24 are encoder, seq2seq and legacy families (BERT variants, Whisper, TrOCR, XLNet, ...) whose head is notlm_head, plus Llama4/Mllama; none of them declares a logit scale, and they behave exactly as they do today.Model evaluation
Prompt logprobs for a 55-token paragraph,
ibm-granite/granite-swash-2b, bf16, against HF eager as the reference (mean logprob -1.5943). Measured on top of #52156, since that model also needs the sink fix to produce sane output at all:The remaining 0.0165 is bf16/kernel-level noise.
Notes
Attention(..., soft_cap=)handles the attention-logit variant but the final one needs handling incompute_logits. Left for a separate PR, and the probe now warns when it is hit.naver-hyperclovax/HyperCLOVAX-SEED-Think-14B, too big for an e2e test here.