oev-demo / oev /soup.py
divyanshudhruv's picture
Upload folder using huggingface_hub
51da1ee verified
Raw History Blame
1.38 kB
"""Weight-average checkpoints that share a loss basin (same warm-start parent).
python -m oev.soup --checkpoints a.pt,b.pt,c.pt --out soup.pt
"""
import argparse
import torch
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--checkpoints", required=True, help="comma-separated .pt paths sharing an architecture")
ap.add_argument("--out", required=True)
args = ap.parse_args()
paths = [p.strip() for p in args.checkpoints.split(",") if p.strip()]
assert len(paths) >= 2, "soup needs at least two checkpoints"
states = []
config = None
for p in paths:
ckpt = torch.load(p, map_location="cpu", weights_only=True)
if config is None:
config = ckpt.get("config")
states.append(ckpt["state"])
keys = states[0].keys()
for s in states[1:]:
assert s.keys() == keys, "checkpoint state dicts disagree; soup only works within one training lineage"
soup = {}
for k in keys:
acc = states[0][k].float()
for s in states[1:]:
acc = acc + s[k].float()
soup[k] = (acc / len(states)).to(states[0][k].dtype)
torch.save({"config": config, "state": soup}, args.out)
print(f"soup of {len(paths)} checkpoints -> {args.out} "
f"({sum(v.numel() for v in soup.values())} params averaged)")
if __name__ == "__main__":
main()