Vocetta2-1m / train /average_checkpoints.py
arthurblg1802's picture
Upload folder using huggingface_hub
76a9daf verified
Raw History Blame Contribute Delete
2.86 kB
"""Average checkpoints into a soup (the lever that carried this release).
Same-basin checkpoints from the same recipe, saved at different points of
short continuation runs, average into a better model than any member: on this
line the final soups scored +0.09 SCOREQ above their best single member, for
zero training.
# uniform average
python average_checkpoints.py --out decoder_soup.pt a.pt b.pt c.pt d.pt
# weighted average (the acoustic blend used weights 2,2,1,1,1)
python average_checkpoints.py --out acoustic_G4.pt \
--weights 2,2,1,1,1 bs015.pt ft2.pt t1.pt t2.pt t3.pt
Rules (measured on this line):
- Only average checkpoints with IDENTICAL key sets; the script refuses to mix
different architectures.
- Float tensors are averaged; non-float tensors are copied from the first
member.
- Members must share a basin (same recipe, diverged only by training noise
and step count). Mixing distant runs loses.
- 2 to 6 members is the proven range; more members dilute. The shipped
decoder is the average of its two best soups, which itself beat larger
blends, and the shipped acoustic is a pair-heavy blend of five
checkpoints.
- Averaging can be applied to soups again (nested), e.g. the shipped decoder
is avg(e5, ss2) where e5 and ss2 are themselves averages.
"""
import argparse
import torch
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--out", required=True)
ap.add_argument("--weights", default=None,
help="comma-separated per-input weights (default: uniform)")
ap.add_argument("inputs", nargs="+", help="checkpoint files to average")
args = ap.parse_args()
weights = None
if args.weights:
weights = [float(x) for x in args.weights.split(",")]
if len(weights) != len(args.inputs):
raise SystemExit(f"{len(weights)} weights for {len(args.inputs)} inputs")
else:
weights = [1.0] * len(args.inputs)
tot = sum(weights)
sds = [torch.load(p, map_location="cpu", weights_only=False) for p in args.inputs]
ks0 = set(sds[0].keys())
for p, sd in zip(args.inputs[1:], sds[1:]):
if set(sd.keys()) != ks0:
raise SystemExit(f"key mismatch: {p} does not match {args.inputs[0]}")
out = {}
for k in sds[0].keys():
ref = sds[0][k]
if torch.is_tensor(ref) and ref.is_floating_point():
acc = None
for w, sd in zip(weights, sds):
v = sd[k].float() * (w / tot)
acc = v if acc is None else acc + v
out[k] = acc
else:
out[k] = ref
torch.save(out, args.out)
n = sum(v.numel() for v in out.values() if torch.is_tensor(v))
print(f"averaged {len(sds)} checkpoints -> {args.out} ({n:,} parameters)")
if __name__ == "__main__":
main()