androidli's picture
laya multilingual ONNX int4 (MatMulNBits + GatherBlockQuantized), tokenizer/config + scripts
ad024da verified
Raw History Blame Contribute Delete
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()