fix(tools): clamp block max in block_fp8 to avoid NaN weights from all-zero blocks - #2256
Merged
Merged
Conversation
…l-zero blocks block_fp8 (the default quantization strategy of convert_hf_to_fp8.py) computed scale = block_max / FP8_MAX without clamping the block max away from zero, unlike channel_fp8 and tensor_fp8 which both clamp to 1e-12. For an all-zero 128x128 block (padding vocab rows, unused MoE experts, zeroed gate weights, ...), block_max == 0 gives scale == 0 and qweight == 0/0 == NaN, silently writing NaN weights into the converted checkpoint; dequantization then propagates NaN through the forward pass. Near-zero blocks similarly produced inf that got clamped to garbage values. Clamp the block max to 1e-12, matching the other two strategies, and add CPU unit tests that fail without the fix.
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.
Problem
block_fp8— the default quantization strategy oftools/convert_hf_to_fp8.py— computeswithout clamping the block max away from zero, unlike
channel_fp8andtensor_fp8in the same file, which both do.clamp(min=1e-12).For an all-zero 128×128 block (padding vocab rows, unused MoE experts, zeroed gate weights — all present in large MoE checkpoints),
block_max == 0givesscale == 0andqweight == 0 / 0 == NaN(torch.clamp(NaN)stays NaN). The NaN weights are silently written into the converted safetensors, and dequantization (NaN * 0 = NaN) then poisons the forward pass of the converted model. Near-zero blocks similarly yieldinf, which gets clamped to ±448 — a wrong value with no error raised.Reproduction (CPU):
Fix
Clamp the block max to
1e-12, exactly matchingchannel_fp8/tensor_fp8:An all-zero block then quantizes to zeros with a tiny scale, and dequantizes back to exact zeros. Non-zero blocks are completely unaffected.
Tests
Added
tests/test_block_fp8_zero_block.py(CPU,@pytest.mark.unit):All 3 tests pass locally, plus
ruffandblack --checkare clean.