fix: keep only latest DeepSpeed checkpoint

pull/791/head
baihaochen 2026-06-03 21:35:47 +08:00
parent 0425c4f622
commit e9c93951cc
4 changed files with 83 additions and 2 deletions

View File

@ -27,6 +27,7 @@ from trainer.trainer_utils import (
add_model_profile_args, add_model_profile_args,
apply_model_profile, apply_model_profile,
build_lm_config, build_lm_config,
assert_no_training_output_collision,
init_model, init_model,
is_main_process, is_main_process,
get_cuda_peak_memory_gb, get_cuda_peak_memory_gb,
@ -149,6 +150,7 @@ def parse_args():
parser.add_argument("--data_path", type=str, default="../dataset/sft_t2t_mini.jsonl") parser.add_argument("--data_path", type=str, default="../dataset/sft_t2t_mini.jsonl")
parser.add_argument("--from_weight", default="pretrain", type=str) parser.add_argument("--from_weight", default="pretrain", type=str)
parser.add_argument("--from_resume", default=0, type=int, choices=[0, 1]) parser.add_argument("--from_resume", default=0, type=int, choices=[0, 1])
parser.add_argument("--overwrite_output", default=0, type=int, choices=[0, 1])
parser.add_argument("--use_wandb", action="store_true") parser.add_argument("--use_wandb", action="store_true")
parser.add_argument("--wandb_project", type=str, default="SearchLM-Full-SFT-DS") parser.add_argument("--wandb_project", type=str, default="SearchLM-Full-SFT-DS")
parser.add_argument("--use_compile", default=0, type=int, choices=[0, 1]) parser.add_argument("--use_compile", default=0, type=int, choices=[0, 1])
@ -181,6 +183,14 @@ if __name__ == "__main__":
num_key_value_heads=args.num_key_value_heads, num_key_value_heads=args.num_key_value_heads,
intermediate_size=args.intermediate_size, intermediate_size=args.intermediate_size,
) )
assert_no_training_output_collision(
lm_config,
weight=args.save_weight,
save_dir=args.save_dir,
checkpoint_dir="../checkpoints",
from_resume=args.from_resume,
overwrite=args.overwrite_output,
)
model, tokenizer = init_model(lm_config, args.from_weight, device=args.device) model, tokenizer = init_model(lm_config, args.from_weight, device=args.device)
if args.use_compile == 1: if args.use_compile == 1:

View File

@ -27,6 +27,7 @@ from trainer.trainer_utils import (
add_model_profile_args, add_model_profile_args,
apply_model_profile, apply_model_profile,
build_lm_config, build_lm_config,
assert_no_training_output_collision,
init_model, init_model,
is_main_process, is_main_process,
get_cuda_peak_memory_gb, get_cuda_peak_memory_gb,
@ -149,6 +150,7 @@ def parse_args():
parser.add_argument("--data_path", type=str, default="../dataset/pretrain_t2t_mini.jsonl") parser.add_argument("--data_path", type=str, default="../dataset/pretrain_t2t_mini.jsonl")
parser.add_argument("--from_weight", default="none", type=str) parser.add_argument("--from_weight", default="none", type=str)
parser.add_argument("--from_resume", default=0, type=int, choices=[0, 1]) parser.add_argument("--from_resume", default=0, type=int, choices=[0, 1])
parser.add_argument("--overwrite_output", default=0, type=int, choices=[0, 1])
parser.add_argument("--use_wandb", action="store_true") parser.add_argument("--use_wandb", action="store_true")
parser.add_argument("--wandb_project", type=str, default="SearchLM-Pretrain-DS") parser.add_argument("--wandb_project", type=str, default="SearchLM-Pretrain-DS")
parser.add_argument("--use_compile", default=0, type=int, choices=[0, 1]) parser.add_argument("--use_compile", default=0, type=int, choices=[0, 1])
@ -181,6 +183,14 @@ if __name__ == "__main__":
num_key_value_heads=args.num_key_value_heads, num_key_value_heads=args.num_key_value_heads,
intermediate_size=args.intermediate_size, intermediate_size=args.intermediate_size,
) )
assert_no_training_output_collision(
lm_config,
weight=args.save_weight,
save_dir=args.save_dir,
checkpoint_dir="../checkpoints",
from_resume=args.from_resume,
overwrite=args.overwrite_output,
)
model, tokenizer = init_model(lm_config, args.from_weight, device=args.device) model, tokenizer = init_model(lm_config, args.from_weight, device=args.device)
if args.use_compile == 1: if args.use_compile == 1:

View File

@ -30,6 +30,7 @@ from trainer.trainer_utils import (
add_model_profile_args, add_model_profile_args,
apply_model_profile, apply_model_profile,
build_lm_config, build_lm_config,
assert_no_training_output_collision,
init_model, init_model,
is_main_process, is_main_process,
get_cuda_peak_memory_gb, get_cuda_peak_memory_gb,
@ -253,6 +254,7 @@ def parse_args():
parser.add_argument("--temperature", type=float, default=0.8) parser.add_argument("--temperature", type=float, default=0.8)
parser.add_argument("--from_weight", default="full_sft_search", type=str) parser.add_argument("--from_weight", default="full_sft_search", type=str)
parser.add_argument("--from_resume", default=0, type=int, choices=[0, 1]) parser.add_argument("--from_resume", default=0, type=int, choices=[0, 1])
parser.add_argument("--overwrite_output", default=0, type=int, choices=[0, 1])
parser.add_argument("--use_wandb", action="store_true") parser.add_argument("--use_wandb", action="store_true")
parser.add_argument("--wandb_project", type=str, default="SearchShortQA-GRPO-DS") parser.add_argument("--wandb_project", type=str, default="SearchShortQA-GRPO-DS")
parser.add_argument("--use_compile", default=0, type=int, choices=[0, 1]) parser.add_argument("--use_compile", default=0, type=int, choices=[0, 1])
@ -290,6 +292,14 @@ if __name__ == "__main__":
num_key_value_heads=args.num_key_value_heads, num_key_value_heads=args.num_key_value_heads,
intermediate_size=args.intermediate_size, intermediate_size=args.intermediate_size,
) )
assert_no_training_output_collision(
lm_config,
weight=args.save_weight,
save_dir=args.save_dir,
checkpoint_dir="../checkpoints",
from_resume=args.from_resume,
overwrite=args.overwrite_output,
)
model, tokenizer = init_model(lm_config, args.from_weight, device=args.device) model, tokenizer = init_model(lm_config, args.from_weight, device=args.device)
if args.use_compile == 1: if args.use_compile == 1:

View File

@ -8,6 +8,7 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
import json import json
import random import random
import math import math
import shutil
import numpy as np import numpy as np
import torch import torch
import torch.distributed as dist import torch.distributed as dist
@ -80,11 +81,16 @@ def unwrap_model(model):
return getattr(raw_model, "_orig_mod", raw_model) return getattr(raw_model, "_orig_mod", raw_model)
def get_model_weight_path(lm_config, save_dir="../out", weight="model", epoch=None, step=None):
model_suffix = get_model_suffix(lm_config)
step_suffix = f"_epoch{epoch}_step{step}" if epoch is not None and step is not None else ""
return f"{save_dir}/{weight}_{lm_config.hidden_size}{model_suffix}{step_suffix}.pth"
def save_model_weights(lm_config, model, save_dir="../out", weight="model", dtype=torch.float16): def save_model_weights(lm_config, model, save_dir="../out", weight="model", dtype=torch.float16):
"""保存一份轻量推理权重;DeepSpeed完整训练状态由 save_checkpoint 单独保存。""" """保存一份轻量推理权重;DeepSpeed完整训练状态由 save_checkpoint 单独保存。"""
os.makedirs(save_dir, exist_ok=True) os.makedirs(save_dir, exist_ok=True)
model_suffix = get_model_suffix(lm_config) ckp_path = get_model_weight_path(lm_config, save_dir=save_dir, weight=weight)
ckp_path = f"{save_dir}/{weight}_{lm_config.hidden_size}{model_suffix}.pth"
raw_model = unwrap_model(model) raw_model = unwrap_model(model)
state_dict = raw_model.state_dict() state_dict = raw_model.state_dict()
state_dict = {k: v.detach().to(dtype).cpu() for k, v in state_dict.items()} state_dict = {k: v.detach().to(dtype).cpu() for k, v in state_dict.items()}
@ -218,6 +224,48 @@ def _candidate_deepspeed_tags(lm_config, weight, save_dir="../checkpoints"):
return dedup return dedup
def _prune_old_deepspeed_step_tags(lm_config, weight, keep_tag, save_dir="../checkpoints"):
if not os.path.exists(save_dir):
return
base_tag = get_deepspeed_tag(lm_config, weight)
prefix = f"{base_tag}_epoch"
for name in os.listdir(save_dir):
if name == keep_tag or not name.startswith(prefix):
continue
path = os.path.join(save_dir, name)
if os.path.isdir(path):
shutil.rmtree(path, ignore_errors=True)
def assert_no_training_output_collision(lm_config, weight, save_dir="../out", checkpoint_dir="../checkpoints", from_resume=0, overwrite=0):
if int(from_resume) == 1 or int(overwrite) == 1:
return
base_tag = get_deepspeed_tag(lm_config, weight)
latest_weight = get_model_weight_path(lm_config, save_dir=save_dir, weight=weight)
latest_file = get_deepspeed_latest_file(lm_config, weight, checkpoint_dir)
existing = []
if os.path.exists(latest_weight):
existing.append(latest_weight)
if os.path.exists(latest_file):
existing.append(latest_file)
if os.path.exists(os.path.join(checkpoint_dir, base_tag)):
existing.append(os.path.join(checkpoint_dir, base_tag))
if os.path.exists(checkpoint_dir):
prefix = f"{base_tag}_epoch"
for name in os.listdir(checkpoint_dir):
if name.startswith(prefix):
existing.append(os.path.join(checkpoint_dir, name))
break
if not existing:
return
raise RuntimeError(
"检测到同名训练产物,已阻止覆盖。"
f" save_weight={weight}, hidden_size={lm_config.hidden_size}, existing={existing}. "
"如果是继续训练,请加 --from_resume 1;如果是新实验,请换一个 --save_weight;"
"如果确认要覆盖/复用旧目录,请显式加 --overwrite_output 1。"
)
def save_deepspeed_checkpoint(model_engine, lm_config, weight, epoch=0, step=0, wandb=None, save_dir="../checkpoints", **kwargs): def save_deepspeed_checkpoint(model_engine, lm_config, weight, epoch=0, step=0, wandb=None, save_dir="../checkpoints", **kwargs):
"""DeepSpeed checkpoint 必须所有 rank 都调用,不能只在 rank0 保存。""" """DeepSpeed checkpoint 必须所有 rank 都调用,不能只在 rank0 保存。"""
os.makedirs(save_dir, exist_ok=True) os.makedirs(save_dir, exist_ok=True)
@ -237,7 +285,10 @@ def save_deepspeed_checkpoint(model_engine, lm_config, weight, epoch=0, step=0,
model_engine.save_checkpoint(save_dir, tag=tag, client_state=client_state) model_engine.save_checkpoint(save_dir, tag=tag, client_state=client_state)
if is_main_process(): if is_main_process():
_write_deepspeed_latest_tag(lm_config, weight, tag, save_dir) _write_deepspeed_latest_tag(lm_config, weight, tag, save_dir)
_prune_old_deepspeed_step_tags(lm_config, weight, keep_tag=tag, save_dir=save_dir)
Logger(f"DeepSpeed checkpoint saved: {save_dir}/{tag}") Logger(f"DeepSpeed checkpoint saved: {save_dir}/{tag}")
if dist.is_initialized():
dist.barrier()
return tag return tag