Download scripts/int4_quantize.py from androidli/laya-multilingual-onnx-int4: direct link, hf CLI and curl.
- Browser
- Download file 2.41 kB
-
https://huggingface.co/androidli/laya-multilingual-onnx-int4/resolve/main/scripts/int4_quantize.py
- Command line
-
hf download hf://androidli/laya-multilingual-onnx-int4/scripts/int4_quantize.py
-
curl -L -o int4_quantize.py https://huggingface.co/androidli/laya-multilingual-onnx-int4/resolve/main/scripts/int4_quantize.py
2.41 kB
| """int4 (weight-only, block-wise) quantization of the laya ONNX graph via ONNX Runtime's | |
| MatMulNBits path — the same mechanism ORT/GenAI use for 4-bit LLM weights. | |
| python int4_quantize.py --src laya-zh-fp32-clean.onnx --dst laya-zh-int4-b32.onnx \ | |
| --block-size 32 --symmetric | |
| Only `MatMul` nodes whose weight input is a constant get replaced by `com.microsoft::MatMulNBits` | |
| (attention QK^T / softmax·V keep their fp32 kernel). No calibration data needed (RTN). | |
| """ | |
| import argparse, collections, os, time | |
| import onnx | |
| from onnxruntime.quantization.matmul_nbits_quantizer import ( | |
| DefaultWeightOnlyQuantConfig, MatMulNBitsQuantizer) | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--src", required=True) | |
| ap.add_argument("--dst", required=True) | |
| ap.add_argument("--bits", type=int, default=4) | |
| ap.add_argument("--block-size", type=int, default=32) | |
| ap.add_argument("--symmetric", action="store_true") | |
| ap.add_argument("--accuracy-level", type=int, default=None) | |
| a = ap.parse_args() | |
| src, dst = os.path.expanduser(a.src), os.path.expanduser(a.dst) | |
| cfg = DefaultWeightOnlyQuantConfig(block_size=a.block_size, is_symmetric=a.symmetric, | |
| bits=a.bits, accuracy_level=a.accuracy_level) | |
| print(f"int{a.bits} weight-only: block_size={a.block_size} symmetric={a.symmetric} " | |
| f"accuracy_level={a.accuracy_level} | {src} -> {dst}", flush=True) | |
| t0 = time.perf_counter() | |
| q = MatMulNBitsQuantizer(model=src, algo_config=cfg) # first positional arg is `model` | |
| print("config loaded, quantizing ...", flush=True) | |
| q.process() | |
| q.model.save_model_to_file(dst, use_external_data_format=True) | |
| dt = time.perf_counter() - t0 | |
| m = onnx.load(dst, load_external_data=False) | |
| ops = collections.Counter(n.op_type for n in m.graph.node) | |
| size = os.path.getsize(dst) + (os.path.getsize(dst + ".data") if os.path.exists(dst + ".data") else 0) | |
| src_size = os.path.getsize(src) + (os.path.getsize(src + ".data") if os.path.exists(src + ".data") else 0) | |
| print(f"done in {dt:.1f}s | {src_size/1e6:.0f}MB -> {size/1e6:.0f}MB ({src_size/size:.1f}x smaller)") | |
| print(f"node mix: MatMul={ops.get('MatMul',0)} MatMulNBits={ops.get('MatMulNBits',0)} " | |
| f"Gemm={ops.get('Gemm',0)} total={sum(ops.values())}") | |
| print("INT4_QUANTIZE_DONE") | |
| if __name__ == "__main__": | |
| main() | |