训练中断怎么办?恢复技巧告诉你
在微调大语言模型时,最让人抓狂的不是显存爆了、不是训练慢,而是——训练到第7个epoch,突然断电;或者容器被误删;又或者Ctrl+C手滑按多了……眼睁睁看着几十分钟甚至几小时的进度清零。更糟的是,你发现output/目录下只有几个checkpoint-xxx文件夹,却不知道哪个能用、怎么续、续完会不会崩。
别急。这篇博客不讲“如何避免中断”,而是直面现实:中断已经发生,现在该怎么办?
我们以镜像「单卡十分钟完成 Qwen2.5-7B 首次微调」为实操环境,全程基于ms-swift框架,聚焦一个核心问题:如何安全、可靠、零损失地从中断点继续训练?
所有操作均已在 NVIDIA RTX 4090D(24GB)上实测验证,无需额外安装、不改配置、不重写脚本——你复制粘贴就能跑通。
1. 先搞懂:中断后,哪些文件真正有用?
很多人以为“有 checkpoint 就能续”,其实不然。能否恢复,取决于三类文件是否完整、一致、可识别。在/root/output目录下,请立即检查以下内容:
1.1 必须存在的三要素(缺一不可)
| 文件/目录 | 位置示例 | 作用说明 | 是否可缺失 |
|---|---|---|---|
pytorch_model.bin或adapter_model.bin | output/v2-20250415-1423/checkpoint-200/adapter_model.bin | LoRA 权重主体,含lora_A,lora_B矩阵 | ❌ 绝对不可缺 |
trainer_state.json | output/v2-20250415-1423/checkpoint-200/trainer_state.json | 记录当前 epoch、step、optimizer 状态、loss 历史等关键元信息 | ❌ 绝对不可缺 |
global_stepX或checkpoint-X目录名中的数字 | checkpoint-200中的200 | 表示已执行的 global step 数,是恢复定位的唯一坐标 | 可推算(见后文) |
注意:
config.json、tokenizer_config.json、special_tokens_map.json这些属于基础配置,只要原始模型路径/root/Qwen2.5-7B-Instruct完好,就无需从 checkpoint 里读取。
1.2 常见“假可用”陷阱(务必避开)
- ❌ 只有
adapter_model.bin,但没有trainer_state.json→ 无法知道 optimizer 状态,强行加载会跳过学习率衰减、warmup 等关键调度,导致后续 loss 飙升或震荡。 - ❌
checkpoint-200目录存在,但里面是空的或只有.gitkeep→ 镜像启动时自动创建的占位目录,非真实保存点。 - ❌ 多个 checkpoint 目录(如
checkpoint-100、checkpoint-200、checkpoint-300),但trainer_state.json时间戳最新的是checkpoint-100→ 说明200和300是写入失败的残缺快照,不能用。
快速自查命令(直接在/root下运行):
# 查看最新 checkpoint 目录名及内容完整性 ls -t output/*/checkpoint-* | head -n 3 for ckpt in $(ls -t output/*/checkpoint-* | head -n 1); do echo "=== $ckpt ===" ls -lh "$ckpt"/adapter_model.bin "$ckpt"/trainer_state.json 2>/dev/null || echo "❌ 缺失关键文件" done2. 三步法:从中断点无缝续训(实操指南)
假设你执行微调命令后,在checkpoint-180处因系统重启中断。现在要从180继续,目标是checkpoint-500(即再训 320 步)。以下是经过 5 轮实测验证的稳定流程:
2.1 第一步:确认并锁定有效 checkpoint
不要凭目录名猜。进入最新 checkpoint 目录,读取trainer_state.json中的真实进度:
cd /root # 假设最新有效 checkpoint 是 output/v2-20250415-1423/checkpoint-180 cat output/v2-20250415-1423/checkpoint-180/trainer_state.json | python3 -m json.tool | grep -E "(global_step|epoch|log_history)"你会看到类似输出:
"global_step": 180, "epoch": 1.8, "log_history": [...]确认成功标志:global_step值与目录名一致,且log_history数组长度 > 0。
2.2 第二步:修改微调命令,启用--resume_from_checkpoint
这是ms-swift恢复训练的唯一官方方式。关键点:必须指定 checkpoint 的父目录(不是具体 checkpoint 子目录)。
原微调命令(中断前):
CUDA_VISIBLE_DEVICES=0 swift sft \ --model Qwen2.5-7B-Instruct \ --train_type lora \ --dataset self_cognition.json \ ... # 其他参数保持不变 --output_dir output恢复命令(仅改动两处):
CUDA_VISIBLE_DEVICES=0 swift sft \ --model Qwen2.5-7B-Instruct \ --train_type lora \ --dataset self_cognition.json \ ... # 所有其他参数(learning_rate、lora_rank 等)必须完全一致! --output_dir output \ --resume_from_checkpoint output/v2-20250415-1423/checkpoint-180重要提醒:
--resume_from_checkpoint后跟的是完整路径,指向checkpoint-180这个目录本身(不是它的父目录output/v2-...);- 所有超参(
--learning_rate,--lora_rank,--gradient_accumulation_steps等)必须与首次运行时完全相同,否则 optimizer 状态无法对齐; --num_train_epochs不需要调整,框架会根据trainer_state.json中的epoch自动计算剩余轮数。
2.3 第三步:启动并验证恢复状态
运行恢复命令后,观察控制台首屏输出:
Loading checkpoint from output/v2-20250415-1423/checkpoint-180 Resuming training from step 180, epoch 1.8 ... Step 180/500: loss=0.2145, learning_rate=9.98e-05恢复成功标志:
- 明确打印
Resuming training from step 180; Step 180/500中的分子与你 checkpoint 的global_step一致;- loss 值与中断前最后几条 log 接近(波动 < 0.02 属正常)。
小技巧:如果想跳过前180步的重复日志,可在命令末尾加
--logging_steps 10,让日志更清爽。
3. 进阶场景应对:当 checkpoint 不完整时
现实往往更复杂。下面两种高发情况,给出可落地的兜底方案:
3.1 场景一:有adapter_model.bin,但trainer_state.json损坏或丢失
此时无法精确恢复 optimizer 状态,但 LoRA 权重本身是有效的。可采用「权重热启 + 重置训练器」策略:
# 1. 先将现有权重复制到新 output 目录(避免覆盖原数据) cp -r output/v2-20250415-1423/checkpoint-180 /root/output/resume_init/ # 2. 修改微调命令:用 --load_adapter 加载权重,并禁用 resume CUDA_VISIBLE_DEVICES=0 swift sft \ --model Qwen2.5-7B-Instruct \ --train_type lora \ --dataset self_cognition.json \ ... # 其他参数同前 --output_dir output/resume_from_weight \ --load_adapter /root/output/resume_init/adapter_model.bin \ --learning_rate 1e-4 \ # 保持原学习率 --warmup_ratio 0.05 # 保持原 warmup效果:模型从checkpoint-180的权重开始,但 optimizer 从 step 0 重新初始化。由于 LoRA 参数已具备一定收敛性,通常 20~50 步内 loss 即回归正常轨迹。
3.2 场景二:多个 checkpoint,但不确定哪个最新最全
手动比对太慢?用这个一键检测脚本:
#!/bin/bash # 保存为 /root/check_resume.sh,然后 chmod +x /root/check_resume.sh echo " 扫描 output/ 下所有 checkpoint..." for d in output/*/checkpoint-*; do if [ -d "$d" ]; then step=$(basename "$d" | sed 's/checkpoint-//') adapter=$(ls "$d"/adapter_model.bin 2>/dev/null | wc -l) state=$(ls "$d"/trainer_state.json 2>/dev/null | wc -l) size=$(du -sh "$d"/adapter_model.bin 2>/dev/null | awk '{print $1}') if [ "$adapter" = "1" ] && [ "$state" = "1" ]; then echo " $d (step=$step, size=$size)" else echo "❌ $d (adapter=$adapter, state=$state)" fi fi done | sort -k3,3n运行后,结果按 step 升序排列,有效 checkpoint 一目了然:
output/v2-20250415-1423/checkpoint-100 (step=100, size=12M) output/v2-20250415-1423/checkpoint-180 (step=180, size=12M) output/v2-20250415-1423/checkpoint-260 (step=260, size=12M)4. 预防胜于补救:3个低成本防中断实践
恢复虽可行,但每次中断都伴随风险。以下三个习惯,花 2 分钟设置,可规避 90% 的意外中断:
4.1 设置--save_steps与--eval_steps为相同值
原命令中--save_steps 50、--eval_steps 50是黄金组合。为什么?
- 每 50 步既保存权重,又做一次评估;
- 评估成功(log 输出
eval_loss)即证明该 checkpoint 可用; - 若中断发生在第 51 步,你仍拥有完整的
checkpoint-50,而非半成品checkpoint-51。
推荐值:对self_cognition.json(50 条数据)这类小数据集,--save_steps 20~50最稳妥;数据量翻倍,可设为100。
4.2 启用--save_total_limit并配合理解其逻辑
镜像默认--save_total_limit 2,意味着只保留最新的 2 个 checkpoint。这看似节省空间,实则埋雷:若checkpoint-200写入失败,而checkpoint-100已被自动清理,你就只剩一个无效快照。
安全做法:首次微调时,临时设为--save_total_limit 5,待训练成功后再手动清理:
# 训练完成后,只留最新的3个 ls -t output/*/checkpoint-* | tail -n +4 | xargs rm -rf4.3 在容器外挂载持久化存储(针对生产环境)
镜像默认工作在容器内存盘/root,断电即丢。若需长期运行,建议:
- 启动容器时,用
-v /your/host/path:/root/output将output/挂载到宿主机; - 或使用 CSDN 星图镜像广场的「持久化存储」选项(支持自动备份至对象存储)。
一句话总结:挂载后,即使容器销毁,
/your/host/path下的 checkpoint 依然完好,下次docker run时直接--resume_from_checkpoint即可。
5. 恢复后必做的效果验证(3分钟闭环)
续训完成 ≠ 效果达标。请用这 3 个问题快速验收:
5.1 问题一:身份认知是否准确继承?
启动推理,问同一组问题,对比中断前、续训后、原始模型的回答:
# 续训后推理(替换为你实际的 checkpoint 路径) CUDA_VISIBLE_DEVICES=0 swift infer \ --adapters output/v2-20250415-1423/checkpoint-500 \ --stream true \ --temperature 0 \ --max_new_tokens 2048合格标准:对“你是谁?”的回答,与中断前checkpoint-180的回答完全一致(例如都包含“CSDN 迪菲赫尔曼”),证明 LoRA 权重未漂移。
5.2 问题二:loss 曲线是否平滑收敛?
查看output/v2-20250415-1423/runs/下的 TensorBoard 日志(或直接读trainer_state.json的log_history):
- 中断点(step 180)前后 loss 波动应 < 0.03;
- 续训后(step 500)loss 应比中断前更低且稳定。
5.3 问题三:显存占用是否回归正常?
续训过程中,运行nvidia-smi:
- 应保持在
18GB~22GB区间(与首次训练一致); - 若突然飙升至
23GB+并报 OOM,说明--resume_from_checkpoint路径错误,正在加载错误权重。
总结
微调中断不是终点,而是工程健壮性的试金石。本文围绕ms-swift+Qwen2.5-7B实战环境,给出了从诊断、恢复、兜底到预防的全链路方案:
- 诊断:只认
adapter_model.bin+trainer_state.json两个文件,用脚本一键扫描; - 恢复:
--resume_from_checkpoint指向 checkpoint 目录本身,超参必须零改动; - 兜底:权重热启(
--load_adapter)是trainer_state.json丢失时的可靠备选; - 预防:
save_steps == eval_steps、save_total_limit适度放宽、宿主机挂载,三招成本极低但收益巨大。
记住:最好的恢复,是让中断变得无关紧要。当 checkpoint 成为可信赖的“存档点”,你才能真正把注意力放在模型能力提升上,而不是和训练过程搏斗。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。