import gptoss_linear_experts # noqa: F401 (registers GptOssLinearExperts)from compressed_tensors.quantization import ( QuantizationArgs, QuantizationScheme, QuantizationStrategy, QuantizationType,)from datasets import load_datasetfrom transformers import AutoModelForCausalLM, AutoTokenizer from llmcompressor import oneshotfrom llmcompressor.modifiers.gptq import GPTQModifierfrom llmcompressor.utils import load_context MODEL_ID = "unsloth/gpt-oss-20b-BF16"SAVE_DIR = "./gpt-oss-20b-BF16-w4a16-llmcompressor-v0.13.0"CALIB_SIZE = 128MAX_SEQ_LENGTH = 2048# gpt-oss layers are 2880 wide, which 128 does not divide.GROUP_SIZE = 64 IGNORE = [ "lm_head", r"re:.*\.router$", r"re:.*\.router\..*", r"re:.*\.gate$", r"re:.*\.mlp\.gate$",] def _to_text(example: dict) -> dict: parts = [m.get("content", "") for m in example.get("messages", [])] return {"text": "\n".join(p for p in parts if p)} # Step 1: Load the BF16 model inside load_context(), which linearizes the fused# 3D expert tensors into per-expert Linear submodules.print(f"loading {MODEL_ID}", flush=True)with load_context(): model = AutoModelForCausalLM.from_pretrained(MODEL_ID, dtype="bfloat16")tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) experts = model.model.layers[0].mlp.expertsprint("linearized experts class:", type(experts).__name__, flush=True) # Step 2: Load calibration data. GPTQ is data-driven: it needs real activations# to build the per-layer Hessians used to compensate the rounding error.print(f"loading {CALIB_SIZE} calibration samples", flush=True)calib = load_dataset("HuggingFaceH4/ultrachat_200k", split=f"train_sft[:{CALIB_SIZE}]")calib = calib.map(_to_text, remove_columns=calib.column_names, desc="text")if not getattr(tokenizer, "pad_token", None): tokenizer.pad_token = tokenizer.eos_tokencalib = calib.map( lambda e: tokenizer( e["text"], truncation=True, max_length=MAX_SEQ_LENGTH, add_special_tokens=False, ), remove_columns=["text"], desc="tokenize",) # Step 3: Build the W4A16 recipe with an explicit group size instead of the# scheme="W4A16" preset, which would imply the unusable group_size=128.weights = QuantizationArgs( num_bits=4, type=QuantizationType.INT, symmetric=True, strategy=QuantizationStrategy.GROUP, group_size=GROUP_SIZE,)recipe = GPTQModifier( targets="Linear", config_groups={"group_0": QuantizationScheme(targets=["Linear"], weights=weights)}, ignore=IGNORE,) # Step 4: One-shot quantize with calibration and save in compressed-tensors format.print("quantizing with GPTQ (W4A16, group 64)", flush=True)oneshot(model=model, dataset=calib, recipe=recipe, max_seq_length=MAX_SEQ_LENGTH) print(f"saving to {SAVE_DIR}", flush=True)model.save_pretrained(SAVE_DIR, save_compressed=True)tokenizer.save_pretrained(SAVE_DIR)print("done", flush=True)