"""Compress the Kev Core ML package and leave the FP16 source untouched.""" from __future__ import annotations import argparse import time from pathlib import Path import coremltools as ct import coremltools.optimize.coreml as cto from assets import ROOT def package_bytes(path: Path) -> int: return sum(item.stat().st_size for item in path.rglob("*") if item.is_file()) def main() -> None: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--length", type=int, default=128) parser.add_argument("--max-options", type=int, default=32) parser.add_argument("--precision", choices=("e8", "w8", "w8mlp", "w8mlp12", "w4"), required=True) parser.add_argument("--build-dir", type=Path, default=ROOT / "build") args = parser.parse_args() stem = f"kev_0_6b_fp16_L{args.length}_options{args.max_options}" source = args.build_dir / f"{stem}.mlpackage" target = args.build_dir / f"{stem.replace('fp16', args.precision)}.mlpackage" model = ct.models.MLModel(str(source), compute_units=ct.ComputeUnit.CPU_ONLY) metadata = cto.get_weights_metadata(model, weight_threshold=2048) embeddings = [ name for name, weight in metadata.items() if weight.child_ops and weight.child_ops[0].op_type == "gather" ] linear = [ name for name, weight in metadata.items() if len(weight.val.shape) == 2 and weight.child_ops and weight.child_ops[0].op_type == "linear" ] if args.precision == "e8": names = embeddings config = cto.OpLinearQuantizerConfig(mode="linear_symmetric", dtype="int8", granularity="per_channel") compressed = cto.linear_quantize_weights( model, cto.OptimizationConfig(op_name_configs={name: config for name in names}) ) elif args.precision in ("w8", "w8mlp", "w8mlp12"): names = embeddings + linear if args.precision == "w8mlp": names = embeddings + [name for name in linear if "_mlp_" in name] elif args.precision == "w8mlp12": names = embeddings + [ name for name in linear if "_mlp_" in name and int(name.split("_", 2)[1]) % 2 == 0 ] config = cto.OpLinearQuantizerConfig(mode="linear_symmetric", dtype="int8", granularity="per_channel") compressed = cto.linear_quantize_weights( model, cto.OptimizationConfig(op_name_configs={name: config for name in names}) ) else: names = embeddings + linear config = cto.OpPalettizerConfig(mode="kmeans", nbits=4, granularity="per_tensor", num_kmeans_workers=8) compressed = cto.palettize_weights( model, cto.OptimizationConfig(op_name_configs={name: config for name in names}) ) compressed.user_defined_metadata["precision"] = args.precision compressed.short_description = (model.short_description or "") + f" [{args.precision}]" started = time.perf_counter() compressed.save(str(target)) print( f"Saved {target.name}: {package_bytes(target) / 1e6:.0f} MB from " f"{package_bytes(source) / 1e6:.0f} MB; {len(names)} compressed constants; " f"save {time.perf_counter() - started:.1f} s", flush=True, ) if __name__ == "__main__": main()