Fix: ternary weight dequantization uses division instead of multiplication - #616
Open
puddintaim1975-dev wants to merge 1 commit into
Open
Conversation
…tnet.py
The converter divides ternary weights by weight_scale instead of multiplying,
producing dequantized values approximately 2.4x too small.
BitNet's weight_scale = mean(|W|) per output row (absmean quantization).
Correct dequantization: ternary_value * weight_scale
Wrong (current): ternary_value / weight_scale
For scale ~1.555:
Correct: {-1, 0, +1} * 1.555 = {-1.555, 0, 1.555}
Wrong: {-1, 0, +1} / 1.555 = {-0.643, 0, 0.643}
Affects F16 and F32 GGUF conversions. I2_S output is unaffected
(ternary values stored directly, scale passed separately).
Verified on BitNet-b1.58-2B-4T: F16 output produces coherent text after fix.
This was referenced Aug 26, 2026
Open
Author
Reference Implementation ProofThe HuggingFace transformers
# AutoBitLinear.forward() for offline-quantized models:
output = F.linear(input, weight) * self.weight_scale
# weight = unpacked ternary {-1, 0, +1}
# self.weight_scale = mean(|W|) per row (stored in checkpoint)The formula is: The online quantization path ( Model format check:
No known model stores |
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.
Bug
The converter dequantizes ternary weights for F16/F32 GGUF output by dividing by
weight_scaleinstead of multiplying. This produces weight values approximately 2.4× too small, resulting in completely garbled output from F16/F32 GGUF conversions.Root Cause
BitNet uses absmean quantization:
weight_scale = mean(|W|)per output row. The dequantization should reconstruct original weight magnitudes by multiplying ternary values by this scale:Why both paths need
*(not just BitnetModel)The LlamaModel path (line 807) and BitnetModel path (line 1108) both process the same
weight_scaleformat from the checkpoint.weight_scaleis defined byAutoBitLinearin the HuggingFace transformers implementation asmean(|W|)per row — the direct mean, not its inverse. The offline dequantization formula isternary * weight_scale, verified insrc/transformers/integrations/bitnet.py:No known model uses
weight_scale = 1/mean(|W|)(the inverse). The 1bitLLM models (1bitLLM/bitnet_b1_58-large,1bitLLM/bitnet_b1_58-3B) use online quantization withBitLinearand have noweight_scaletensors at all. The only model withweight_scaleismicrosoft/bitnet-b1.58-2B-4T, which usesBitnetForCausalLM→BitnetModelpath.Changes
Two lines in
utils/convert-hf-to-gguf-bitnet.py:LlamaModel.write_tensors):data_torch / scale_map→data_torch * scale_mapBitnetModel.write_tensors):data_torch / scale_map→data_torch * scale_mapNumerical Proof
For
model.layers.0.mlp.gate_proj:weight_scale= 1.5547 (=mean(|W|)of original BF16 weight)| Operation | Range |
mean(|W|)| Verdict ||-----------|-------|-------------|---------|
|
ternary * scale| [-1.5547, 1.5547] | 0.9463 | ✓ Correct ||
ternary / scale| [-0.6432, 0.6432] | 0.3915 | ✗ 2.42× too small |Verification
Tested on BitNet-b1.58-2B-4T (commit 390c30775):
Note: I2_S output format is unaffected — ternary values are stored directly and scale is passed separately to
quantize_to_i2_s.Environment
-DBITNET_ARM_TL1=OFF -DBITNET_X86_TL2=OFFAlso required for correct F16 output: Fix #588 (LLM_FFN_SILU → LLM_FFN_RELU_SQR).