| """ |
| Faz 3 sonrası — post-hoc checkpoint EMA (weight averaging). IMU-1 (arXiv 2602.02522): sub-500M'de bedava kalite↑. |
| |
| checkpoints_350m son N ckpt'nin MODEL ağırlıklarını ortalar (uniform=soup / --decay=EMA son-ağır) → |
| ema/ckpt.pt kaydeder+push. faz5 `--ckpt ema/ckpt.pt` ile eval → tek-final vs EMA kıyası (faz5 GATE'i). |
| SADECE model ağırlıkları ortalanır (optimizer state DEĞİL); cfg en son ckpt'ten; fp32 topla→bf16 kaydet. |
| CPU yeter (GPU gerekmez). Yalnız aynı-mimari (aynı koşu) ckpt'ler → anahtar/şekil birebir. |
| |
| Kullanım: |
| HF_TOKEN=hf_xxx python faz3_ema.py --subdir checkpoints_350m --last 5 |
| HF_TOKEN=hf_xxx python faz3_ema.py --last 5 --decay 0.6 # EMA: son ckpt daha ağır |
| """ |
| import os, sys, argparse |
| import torch |
|
|
| REPO = "kdirgul/smartcore-v1" |
|
|
|
|
| |
| def ema_weights(n, decay): |
| """n ckpt için normalize ağırlık. decay=0 → uniform (soup); >0 → w_i ∝ decay^(n-1-i) (son ckpt en ağır).""" |
| w = [decay ** (n - 1 - i) for i in range(n)] if decay > 0 else [1.0] * n |
| s = sum(w) |
| return [x / s for x in w] |
|
|
|
|
| def average_states(states, weights): |
| """state_dict listesi + normalize ağırlıklar → ağırlıklı-ortalama. Float fp32'de topla→bf16; non-float sonuncu.""" |
| avg, nonfloat = {}, {} |
| for wi, sd in zip(weights, states): |
| for k, v in sd.items(): |
| if v.is_floating_point(): |
| avg[k] = (v.float() * wi) if k not in avg else (avg[k] + v.float() * wi) |
| else: |
| nonfloat[k] = v |
| out = {k: v.to(torch.bfloat16) for k, v in avg.items()} |
| out.update(nonfloat) |
| return out |
|
|
|
|
| |
| def list_ckpts(api, subdir): |
| fs = [f for f in api.list_repo_files(REPO, repo_type="model") |
| if f.startswith(f"{subdir}/step_") and f.endswith("ckpt.pt")] |
| return sorted(fs) |
|
|
|
|
| def main(): |
| ap = argparse.ArgumentParser() |
| ap.add_argument("--subdir", default="checkpoints_350m", help="pretrain ckpt namespace") |
| ap.add_argument("--last", type=int, default=5, help="son N ckpt ortalanır") |
| ap.add_argument("--decay", type=float, default=0.0, help="0=uniform(soup); >0 EMA (son ckpt en ağır)") |
| ap.add_argument("--out_subdir", default="ema", help="HF çıktı alt-dizini (ema/ckpt.pt)") |
| ap.add_argument("--out_local", default="/content/ema/ckpt.pt") |
| ap.add_argument("--no_push", action="store_true") |
| args = ap.parse_args() |
|
|
| from huggingface_hub import HfApi, hf_hub_download, get_token |
| token = os.environ.get("HF_TOKEN") or get_token() |
| api = HfApi(token=token) |
|
|
| fs = list_ckpts(api, args.subdir) |
| assert fs, f"'{args.subdir}/' altında ckpt yok" |
| sel = fs[-args.last:] |
| w = ema_weights(len(sel), args.decay) |
| print(f"[ema] {len(fs)} ckpt bulundu → son {len(sel)} ortalanıyor (decay={args.decay}):", flush=True) |
| for f, wi in zip(sel, w): |
| print(f" {f} (w={wi:.3f})", flush=True) |
|
|
| states, cfg = [], None |
| for f in sel: |
| p = hf_hub_download(REPO, f, repo_type="model", token=token) |
| st = torch.load(p, map_location="cpu", weights_only=False) |
| cfg = st["cfg"]; states.append(st["model"]) |
| avg = average_states(states, w) |
|
|
| os.makedirs(os.path.dirname(args.out_local), exist_ok=True) |
| steps = [f.split("step_")[1].split("/")[0] for f in sel] |
| torch.save({"model": avg, "cfg": cfg, "ema": True, "steps": steps, "decay": args.decay}, args.out_local) |
| print(f"[ema] kaydedildi → {args.out_local} (adımlar {steps[0]}..{steps[-1]})", flush=True) |
|
|
| if not args.no_push and token: |
| api.upload_file(path_or_fileobj=args.out_local, path_in_repo=f"{args.out_subdir}/ckpt.pt", |
| repo_id=REPO, repo_type="model", |
| commit_message=f"post-hoc EMA: son {len(sel)} {args.subdir} ckpt (decay={args.decay})") |
| print(f"[ema] HF push OK → {args.out_subdir}/ckpt.pt", flush=True) |
| print(f" faz5 kıyas: python faz5_eval.py --ckpt {args.out_subdir}/ckpt.pt --tasks xcopa,belebele,hellaswag,xnli,turkishmmlu --limit 300", flush=True) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|