Groot-1 / scripts /soup.py
marcyano's picture
Groot 1 v1.1: Folha correction (87.8% vs Jev 85.8%), S+M ensemble
17a55cc verified
Raw History Blame Contribute Delete
1.81 kB
"""Weight-space interpolation ("model soup") of Groot checkpoints with the same architecture.
python scripts/soup.py --out runs/soup --models A B [C ...] [--weights 0.5 0.5]
Floating-point tensors are averaged with the given weights; the tokenizer, configs and
calibration are copied from the first model. Recalibrate afterwards with
`groot-eval --calibrate-on val.jsonl --save-calibration`.
"""
import argparse
import shutil
from pathlib import Path
from safetensors.torch import load_file, save_file
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--models", nargs="+", required=True)
ap.add_argument("--weights", nargs="+", type=float, default=None)
ap.add_argument("--out", required=True)
args = ap.parse_args()
w = args.weights or [1.0 / len(args.models)] * len(args.models)
if len(w) != len(args.models) or abs(sum(w) - 1) > 1e-6:
raise SystemExit("weights must match models and sum to 1")
states = [load_file(str(Path(m) / "model.safetensors")) for m in args.models]
keys = states[0].keys()
if any(s.keys() != keys for s in states):
raise SystemExit("checkpoints have different tensors")
out = {}
for k in keys:
t = states[0][k]
out[k] = sum(wi * s[k].float() for wi, s in zip(w, states)).to(t.dtype) if t.is_floating_point() else t
dst = Path(args.out)
if dst.exists():
raise SystemExit(f"{dst} exists")
shutil.copytree(args.models[0], dst, ignore=shutil.ignore_patterns("model.safetensors", "optimizer.pt"))
save_file({k: v.contiguous() for k, v in out.items()}, str(dst / "model.safetensors"), metadata={"format": "pt", "family": "groot", "soup": str(w)})
print(f"soup of {len(args.models)} models (weights {w}) -> {dst}")
if __name__ == "__main__":
main()