from transformers import FineGrainedFP8Config, AutoModelForImageTextToText, AutoTokenizer, AutoProcessorimport torch model_id = "google/gemma-4-31B-it" quant_config = FineGrainedFP8Config( # block_size=64, not the default 128: unlike the sibling MoE checkpoint # (gemma-4-26B-A4B-it), every LM dim here -- hidden_size=5376, # intermediate_size=21504 -- IS divisible by 128, so the default block_size # loads and runs without any shape error. But it silently produces NaNs # starting in layer 1's self_attn.q_proj that corrupt every later layer and # eventually crash generate()'s sampling step with a CUDA device-side # assert ("probability tensor contains either inf, nan or element < 0"). # 64x64 blocks give the FP8 kernel finer-grained per-block scales and the # NaNs disappear -- verified clean logits/hidden_states and correct # generation with block_size=64. This is a numerical-precision/kernel # issue, not a shape mismatch: passing the shape-divisibility check is NOT # sufficient evidence that a block size is safe for this model, always # check actual output values (not just that loading/forward "succeeds"). weight_block_size=(64, 64), # model.vision_tower's MLP intermediate_size is 4304 = 16*269 (269 is # prime), so it isn't divisible by 64 (or 128) -- excluded explicitly # rather than relying on the quantizer's shape-mismatch fallback (which # silently leaves a layer in BF16 if its shape doesn't tile, but still # wraps it in the FP8 module class). lm_head is excluded too, the # conventional choice for numerical stability of the output projection. modules_to_not_convert=["model.vision_tower", "lm_head"],) model = AutoModelForImageTextToText.from_pretrained( model_id, quantization_config=quant_config, dtype="auto", device_map="auto",)tok = AutoTokenizer.from_pretrained(model_id)proc = AutoProcessor.from_pretrained(model_id) model.save_pretrained("gemma-4-31B-it-FP8")tok.save_pretrained("gemma-4-31B-it-FP8")proc.save_pretrained("gemma-4-31B-it-FP8")