v5d: save best-epoch checkpoint during SFT
Browse files- training/train_sft.py +24 -1
training/train_sft.py
CHANGED
|
@@ -382,6 +382,8 @@ def main() -> None:
|
|
| 382 |
|
| 383 |
t_start = time.time()
|
| 384 |
global_step = 0
|
|
|
|
|
|
|
| 385 |
for epoch in range(cfg.num_epochs):
|
| 386 |
random.shuffle(examples)
|
| 387 |
total_loss = 0.0
|
|
@@ -399,7 +401,16 @@ def main() -> None:
|
|
| 399 |
log({"step": global_step, "train/epoch": epoch + 1, "train/example": i, "train/loss": total_loss / i})
|
| 400 |
log({"step": global_step, "train/epoch": epoch + 1, "train/loss": total_loss / max(len(examples), 1)})
|
| 401 |
if cfg.eval_every_epoch:
|
| 402 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 403 |
|
| 404 |
summary = {
|
| 405 |
"run_finished_at": datetime.now(timezone.utc).isoformat(),
|
|
@@ -430,6 +441,18 @@ def main() -> None:
|
|
| 430 |
)
|
| 431 |
print(f"[push] done -> https://huggingface.co/{repo_id}", flush=True)
|
| 432 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 433 |
|
| 434 |
def _parse_args() -> SftConfig:
|
| 435 |
p = argparse.ArgumentParser()
|
|
|
|
| 382 |
|
| 383 |
t_start = time.time()
|
| 384 |
global_step = 0
|
| 385 |
+
best_eval_score = -1.0
|
| 386 |
+
best_epoch_dir: Optional[Path] = None
|
| 387 |
for epoch in range(cfg.num_epochs):
|
| 388 |
random.shuffle(examples)
|
| 389 |
total_loss = 0.0
|
|
|
|
| 401 |
log({"step": global_step, "train/epoch": epoch + 1, "train/example": i, "train/loss": total_loss / i})
|
| 402 |
log({"step": global_step, "train/epoch": epoch + 1, "train/loss": total_loss / max(len(examples), 1)})
|
| 403 |
if cfg.eval_every_epoch:
|
| 404 |
+
eval_result = evaluate(epoch + 1)
|
| 405 |
+
log(eval_result)
|
| 406 |
+
score = eval_result.get("eval/avg_grader_score", 0.0)
|
| 407 |
+
if score > best_eval_score:
|
| 408 |
+
best_eval_score = score
|
| 409 |
+
best_epoch_dir = artifact_dir / f"best_epoch_{epoch+1}"
|
| 410 |
+
best_epoch_dir.mkdir(parents=True, exist_ok=True)
|
| 411 |
+
model.save_pretrained(str(best_epoch_dir))
|
| 412 |
+
tokenizer.save_pretrained(str(best_epoch_dir))
|
| 413 |
+
print(f"[checkpoint] new best epoch {epoch+1}: score={score:.4f}", flush=True)
|
| 414 |
|
| 415 |
summary = {
|
| 416 |
"run_finished_at": datetime.now(timezone.utc).isoformat(),
|
|
|
|
| 441 |
)
|
| 442 |
print(f"[push] done -> https://huggingface.co/{repo_id}", flush=True)
|
| 443 |
|
| 444 |
+
if best_epoch_dir and best_epoch_dir.exists():
|
| 445 |
+
best_repo = f"{repo_id}-best"
|
| 446 |
+
create_repo(repo_id=best_repo, repo_type="model", exist_ok=True, private=False, token=token)
|
| 447 |
+
HfApi(token=token).upload_folder(
|
| 448 |
+
folder_path=str(best_epoch_dir),
|
| 449 |
+
repo_id=best_repo,
|
| 450 |
+
repo_type="model",
|
| 451 |
+
token=token,
|
| 452 |
+
commit_message=f"Best epoch checkpoint (score={best_eval_score:.4f})",
|
| 453 |
+
)
|
| 454 |
+
print(f"[push] best checkpoint -> https://huggingface.co/{best_repo}", flush=True)
|
| 455 |
+
|
| 456 |
|
| 457 |
def _parse_args() -> SftConfig:
|
| 458 |
p = argparse.ArgumentParser()
|