From 6a3ec45275469747a503af87b59ce895d0b5b165 Mon Sep 17 00:00:00 2001 From: ZeroKool Date: Tue, 25 Aug 2026 18:16:50 -0700 Subject: [PATCH] Fix: ternary weight dequantization direction in convert-hf-to-gguf-bitnet.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. --- utils/convert-hf-to-gguf-bitnet.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/convert-hf-to-gguf-bitnet.py b/utils/convert-hf-to-gguf-bitnet.py index b11e831b9..75640890d 100644 --- a/utils/convert-hf-to-gguf-bitnet.py +++ b/utils/convert-hf-to-gguf-bitnet.py @@ -804,7 +804,7 @@ def write_tensors(self): data_torch = data_torch.unsqueeze(0).expand((4, *origin_shape)) >> shift data_torch = data_torch & 3 data_torch = (data_torch.float() - 1).reshape((origin_shape[0] * 4, *origin_shape[1:])) - data_torch = data_torch / scale_map[name.replace(".weight", "")].float() + data_torch = data_torch * scale_map[name.replace(".weight", "")].float() # use the first number-like part of the tensor name as the block id bid = None @@ -1102,10 +1102,10 @@ def write_tensors(self): data_torch = data_torch.unsqueeze(0).expand((4, *origin_shape)) >> shift data_torch = data_torch & 3 data_torch = (data_torch.float() - 1).reshape((origin_shape[0] * 4, *origin_shape[1:])) - # For F16/F32 output: divide by weight_scale to get full float values + # For F16/F32 output: multiply by weight_scale to get full float values # For I2_S output: keep as ternary {-1,0,1}, scale is passed separately to quantize_to_i2_s if self.ftype not in (gguf.GGMLQuantizationType.I2_S, gguf.GGMLQuantizationType.TL1, gguf.GGMLQuantizationType.TL2): - data_torch = data_torch / scale_map[name.replace(".weight", "")].float() + data_torch = data_torch * scale_map[name.replace(".weight", "")].float() # convert any unsupported data types to float32 elif data_torch.dtype not in (torch.float16, torch.float32): data_torch = data_torch.to(torch.float32)