心情: 心惊肉跳(刚省了钱,又怕任务挂了) 任务: 修改代码实现 Checkpoint、配置 Slurm 自动重试、启用 EC2 Spot 实例 关键词: Checkpoint, Resume Training, Spot Instances, Slurm Requeue, SIGTERM
早上 9:30,李博士一脸沮丧地坐在屏幕前。 "YY,昨晚跑的那个 Job-103,跑到第 8000 步的时候(总共 10000 步),突然报错退出了。我看日志,好像是 AWS 强制回收了机器。"
我查了一下 CloudWatch,确实。昨晚 AWS 该区域资源紧张,我们的一台 P4d 实例因为底层硬件维护被强制重启了。 损失惨重: 20 个小时的训练成果归零。李博士必须从 Step 0 重新开始跑。
我拍了拍博士的肩膀:"这是我的锅,我没让你做'游戏存档'。今天咱们把这个补上,以后就算地震了,咱们也能从断开的地方接着跑。"
上午 10:30:算法层的改造 ------ 什么是 Checkpoint?
我给李博士科普:训练就像玩 RPG 游戏,Checkpoint 就是存档点。 如果要实现"断点续训",必须在 Python 代码里做两件事:
-
定期存档(Save): 每隔 1 小时(或 1000 步),把当前的模型权重(Weights)和优化器状态(Optimizer State)写到硬盘上。
-
启动加载(Load): 每次程序启动时,先检查硬盘上有没有存档。如果有,就加载存档,把 Step 设置为存档里的步数,而不是从 0 开始。
代码修改实战 (train_llama3.py):
李博士在我的指导下修改了代码:
import torch
import os
# ... 训练循环 ...
start_step = 0
checkpoint_dir = "/fsx/checkpoints/exp_01"
# 【关键逻辑 1:启动时检查存档】
# 如果目录下有 checkpoint_8000.pt,就自动加载
latest_ckpt = find_latest_checkpoint(checkpoint_dir)
if latest_ckpt:
print(f"Resuming from checkpoint: {latest_ckpt}")
state = torch.load(latest_ckpt)
model.load_state_dict(state['model'])
optimizer.load_state_dict(state['optimizer'])
start_step = state['step'] + 1
else:
print("No checkpoint found. Starting from scratch.")
# ... 开始训练 ...
for step in range(start_step, total_steps):
train_step()
# 【关键逻辑 2:定期保存】
# 每 1000 步保存一次,或者每 1 小时保存一次
if step % 1000 == 0:
save_path = os.path.join(checkpoint_dir, f"checkpoint_{step}.pt")
torch.save({
'step': step,
'model': model.state_dict(),
'optimizer': optimizer.state_dict()
}, save_path)
print(f"Saved checkpoint to {save_path}")
运维视角: 这些 Checkpoint 文件必须存在 FSx 上(/fsx/checkpoints/),因为 FSx 是持久化的。如果存在本地硬盘,机器一回收,存档也没了。
下午 1:00:基础设施的升级 ------ 引入 Spot 实例
既然有了"复活甲"(Checkpoint),我产生了一个大胆的想法。 P4d 实例太贵了(按需价格约 $32/小时)。 AWS 有一种 Spot 实例(竞价实例) ,价格通常只有按需价格的 30%-40% (甚至更低)。 缺点: AWS 可能会随时收回这些机器(只给 2 分钟警告)。
以前不敢用: 因为一回收,任务就挂了,钱白花。 现在敢用了: 因为我们能续训!哪怕一天断两次,只要能接上,总成本依然能省下一半。
修改 ParallelCluster 配置 (cluster-config.yaml):
YAML
Scheduling:
SlurmQueues:
- Name: spot-training-queue # 新建一个队列
ComputeResources:
- Name: p4d-spot
InstanceType: p4d.24xlarge
MinCount: 0
MaxCount: 4
# 【关键配置】开启 Spot 模式
SpotPrice: 15.0 # 我愿意出的最高价 (原价 32,我出 15)
# 或者直接不写价格,默认使用当前 Spot 市场价
更新集群:pcluster update-cluster ...
下午 3:00:Slurm 的自动重试 ------ 让机器自己"爬起来"
代码会存盘了,机器便宜了,但还有一个问题: 当 Spot 实例被回收时,AWS 会发一个中断信号。Slurm 会检测到节点挂了,任务状态变为 FAILED。 李博士不想半夜起来重新提交任务。
我们需要配置 Slurm 的 自动排队 (Requeue) 功能。
修改 Slurm 作业脚本 (submit_job.sh):
#!/bin/bash
#SBATCH --job-name=Spot_Training
#SBATCH --partition=spot-training-queue
#SBATCH --nodes=4
# ...
# 【关键参数】
#SBATCH --requeue # 告诉 Slurm:如果任务非正常中断,请自动重新排队!
# 捕捉信号的处理逻辑(高级玩法)
# 当 AWS 发出回收通知时,我们可以尝试抢在最后 2 分钟做一次紧急保存
# 但有了前面的定期保存,这一步是可选的
srun python /fsx/project/train_llama3.py ...
下午 5:00:演习时刻 ------ 拔网线!
为了验证这套系统,我们决定搞一次破坏。 李博士提交了一个 Spot 任务,4 台机器跑得正欢。进度条到了 Step 500。
我的操作: 我直接去 EC2 控制台,手动 Terminate(终止) 了其中一台 P4d 实例,模拟 AWS Spot 回收。
系统反应:
-
崩溃: 训练任务瞬间报错,Python 进程挂掉。
-
Slurm 介入: Slurm 检测到节点丢失,任务失败。
-
自动重排: 因为加了
--requeue,Slurm 立刻把这个任务重新放回了队列 (PD状态)。 -
自动修补: ParallelCluster 发现少了一台机器,立刻请求 AWS 再开一台新的 P4d Spot 实例。
-
恢复: 5 分钟后,新机器到位。任务自动重新开始运行。
-
续训: 李博士盯着日志。
-
日志显示:
No checkpoint found? 不对! -
日志显示:
Resuming from checkpoint: checkpoint_500.pt成功!
-
任务没有从 0 开始,而是从 500 开始继续跑。 虽然中间中断了 10 分钟,但进度保住了。
下午 6:00:算账
李博士看着账单预估:
-
昨天: 跑 24 小时,按需实例,花费约 $3,000。
-
今天: 跑 24 小时,Spot 实例(假设折扣 60%),花费约 $1,200。
-
风险: 可能会断 1-2 次,多花半小时重启。
结论: 省下的 $1,800 足够给整个 AI 团队买半年的咖啡了。李博士直呼:"YY,你这就是在帮公司印钱啊!"
今日总结:
-
Checkpoint 是底线: 没有存档机制的长时间训练就是耍流氓。
-
Spot + Checkpoint 是绝配: 这是云上 AI 训练降本增效的核心打法。
-
Slurm Requeue 是自动化最后一块拼图: 它实现了无人值守的故障自愈。