File size: 5,469 Bytes
f065e53 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | """HF ckpt watcher โ rate-limit ์นํ ๋ฒ์ (HF ์ปค๋ฐ 128/hour ์ ํ ๋์).
ํต์ฌ: ์ปค๋ฐ ์ ์ต์ํ.
- ๋ฌด๊ฑฐ์ด ๋๊ธฐํ(ckpt+logs+plot)๋ '์ 10k ckpt๊ฐ ์๊ฒผ์ ๋๋ง' ์ํ.
- ์ํ/ckpt๋ upload_folder๋ก 'ํด๋๋น 1์ปค๋ฐ' (ํ์ผ๋ณ ์ปค๋ฐ ๊ธ์ง โ ์ด์ ๋ฒ์ ์ด 429 ์ ๋ฐ).
- ckpt: ์ต์ KEEP๊ฐ๋ง ์ ์ง (๋กํ
์ด์
). ์ํ: ์ ๋ถ ์ ์ง.
usage: python hf_ckpt_watcher.py (setsid nohup / harness background)
"""
import os, re, time
from huggingface_hub import HfApi
TOKEN = os.environ.get("HF_TOKEN", "<SET_HF_TOKEN_ENV>")
REPO = "a12s12/spatial-semanticist-L-migration"
PFX = "phase2_" # phase2 ckpt/sample/log HF ๋ค์์คํ์ด์ค (phase1 ์ฐ์ถ๋ฌผ ๋ณด์กด)
BASE = "/NHNHOME/WORKSPACE/0226010398_A/sr_diffusion/clevr_sudoku/semanticist/output/tokenizer/models_l_spatial_phase2"
MODELS_DIR = os.path.join(BASE, "models")
IMAGES_DIR = os.path.join(BASE, "images")
LOGDIR = os.path.join(BASE, "logs/semanticist")
KEEP = 2
POLL_SEC = 300 # 5๋ถ ํด๋ง (์ปค๋ฐ ์ ์ฝ)
REQUIRED = ["model.safetensors", "optimizer.bin"]
api = HfApi(token=TOKEN)
def local_complete_steps():
out = []
if not os.path.isdir(MODELS_DIR):
return out
for name in os.listdir(MODELS_DIR):
m = re.fullmatch(r"step(\d+)", name)
if m:
d = os.path.join(MODELS_DIR, name)
if all(os.path.exists(os.path.join(d, r)) for r in REQUIRED):
out.append(int(m.group(1)))
return sorted(out)
def hf_steps():
steps = set()
for f in api.list_repo_files(REPO):
m = re.match(PFX + r"step(\d+)/", f)
if m:
steps.add(int(m.group(1)))
return sorted(steps)
def sync_samples():
"""IMAGES_DIR ์ ์ฒด๋ฅผ samples_all_steps/ ๋ก upload_folder (๋ณ๊ฒฝ๋ถ๋ง 1์ปค๋ฐ)."""
if os.path.isdir(IMAGES_DIR):
api.upload_folder(folder_path=IMAGES_DIR, path_in_repo="samples_all_steps_phase2",
repo_id=REPO, repo_type="model",
allow_patterns=["*.jpg"],
commit_message="sync recon samples")
def sync_logs():
"""loss ๊ทธ๋ํ ๊ฐฑ์ + tensorboard event (์ ckpt milestone์์๋ง ํธ์ถ)."""
try:
import make_loss_plot_phase2 as make_loss_plot
make_loss_plot.main()
png = os.path.join(BASE, "loss_curves.png")
if os.path.exists(png):
api.upload_file(path_or_fileobj=png, path_in_repo="loss_curves_phase2.png",
repo_id=REPO, repo_type="model",
commit_message="update loss curves")
except Exception as e:
print(f"[watcher] plot skip: {e}", flush=True)
if os.path.isdir(LOGDIR):
api.upload_folder(folder_path=LOGDIR, path_in_repo="logs_phase2",
repo_id=REPO, repo_type="model",
allow_patterns=["events*"],
commit_message="update tb logs")
def sync_ckpts():
"""์ต์ KEEP๊ฐ ๋ก์ปฌ ckpt๋ง HF์ ์ ์ง. ๋ฐํ: ์ต์ step (์์ผ๋ฉด None)."""
local = local_complete_steps()
if not local:
return None
on_hf = set(hf_steps())
target = set(local[-KEEP:])
for s in sorted(target - on_hf):
print(f"[watcher] uploading step{s} ...", flush=True)
api.upload_folder(folder_path=os.path.join(MODELS_DIR, f"step{s}"),
path_in_repo=f"{PFX}step{s}", repo_id=REPO, repo_type="model",
commit_message=f"ckpt step{s}")
print(f"[watcher] step{s} uploaded", flush=True)
pruned = sorted(on_hf - target)
for old in pruned:
print(f"[watcher] pruning HF step{old}", flush=True)
api.delete_folder(path_in_repo=f"{PFX}step{old}", repo_id=REPO, repo_type="model",
commit_message=f"prune step{old} (keep newest {KEEP})")
# โ
private repo ์ ์ฅํ๋ ๋์: ๋กํ
์ด์
์ผ๋ก ์ง์ด ckpt๋ git history์ LFS blob์ด
# ๋จ์ ์ฉ๋์ ์ก์๋จน๋๋ค. prune์ด ์์์ผ๋ฉด history๋ฅผ squashํด์ ์ค์ ๋ก ํ์.
if pruned:
try:
api.super_squash_history(repo_id=REPO, repo_type="model")
print(f"[watcher] squashed history (freed rotated ckpt LFS)", flush=True)
except Exception as e:
print(f"[watcher] squash skip: {str(e)[:120]}", flush=True)
return local[-1]
def main():
print(f"[watcher] start (rate-safe). keep={KEEP} poll={POLL_SEC}s repo={REPO}", flush=True)
last_milestone = None
while True:
try:
local = local_complete_steps()
newest = local[-1] if local else None
# ์ 10k ckpt๊ฐ ์๊ฒผ์ ๋๋ง ๋ฌด๊ฑฐ์ด ์ ์ฒด ๋๊ธฐํ (์ปค๋ฐ ์ ์ฝ)
if newest is not None and newest != last_milestone:
print(f"[watcher] new milestone step{newest} -> full sync", flush=True)
new_top = sync_ckpts()
sync_samples()
sync_logs()
last_milestone = new_top
print(f"[watcher] synced. HF ckpt newest {KEEP} up to step{new_top}", flush=True)
else:
# milestone ์ฌ์ด์ ์ํ๋ง ๊ฐ๋ณ๊ฒ (upload_folder=๋ณ๊ฒฝ๋ถ 1์ปค๋ฐ)
sync_samples()
except Exception as e:
print(f"[watcher] error (๋ค์ ํด๋ง์ ์ฌ์๋): {str(e)[:200]}", flush=True)
time.sleep(POLL_SEC)
if __name__ == "__main__":
main()
|