import torchfrom transformers import AutoModelForCausalLM, AutoTokenizerfrom datasets import load_dataset from llmcompressor import oneshotfrom llmcompressor.modifiers.quantization.gptq import GPTQModifierfrom llmcompressor.utils import load_context model_id = "ibm-granite/granite-4.0-h-tiny"output_dir = "./granite-4.0-h-tiny-w4a16-llmcompressor"CALIB_SIZE = 128MAX_SEQ_LENGTH = 2048 # Step 1: Load the BF16 model inside load_context(), which linearizes the MoE# experts so GPTQ can target them as ordinary Linear modules.with load_context(AutoModelForCausalLM): model = AutoModelForCausalLM.from_pretrained( model_id, torch_dtype=torch.bfloat16, device_map="cpu", trust_remote_code=True, )tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True) # Step 2: Build the GPTQ calibration set.ds = load_dataset("HuggingFaceH4/ultrachat_200k", split=f"train_sft[:{CALIB_SIZE}]")ds = ds.map( lambda ex: {"text": "\n".join(m["content"] for m in ex["messages"] if m.get("content"))}, remove_columns=ds.column_names,)if not getattr(tokenizer, "pad_token", None): tokenizer.pad_token = tokenizer.eos_tokencalib_ds = ds.map( lambda ex: tokenizer( ex["text"], truncation=True, max_length=MAX_SEQ_LENGTH, add_special_tokens=False ), remove_columns=["text"],) # Step 3: Define the W4A16 GPTQ recipe. Experts, shared MLP, Mamba projections# and attention are all quantized; only lm_head and the MoE router are skipped.recipe = GPTQModifier( scheme="W4A16", targets=["Linear"], ignore=["lm_head", "re:.*block_sparse_moe.router"],) # Step 4: One-shot quantize with calibration data and save in# compressed-tensors format.oneshot( model=model, dataset=calib_ds, recipe=recipe, max_seq_length=MAX_SEQ_LENGTH, tokenizer=tokenizer, output_dir=output_dir, trust_remote_code_model=True,) # Smoke testinputs = tokenizer("What are we having for dinner?", return_tensors="pt")with torch.no_grad(): output = model.generate(**inputs, max_new_tokens=30)print(tokenizer.decode(output[0], skip_special_tokens=True))