当训练突然中断:如何利用Criu快照回滚保住3天DeepSeek进度

昨天 2阅读

在深度学习模型训练过程中,最令人崩溃的莫过于训练进程突然中断——可能是由于硬件故障、电源问题、系统崩溃或其他不可预见的原因。当你的模型已经训练了三天三夜,离目标近在咫尺时突然中断,这种体验足以让任何开发者抓狂。本文将详细介绍如何利用Criu(Checkpoint/Restore in Userspace)技术来应对这种灾难性场景,通过快照和回滚机制保住宝贵的训练进度。

训练中断的常见原因

在深入解决方案之前,让我们先了解训练中断的常见原因:

硬件故障:GPU过热、内存溢出、硬盘损坏电源问题:断电、电压不稳系统崩溃:内核panic、驱动不兼容人为因素:误操作、资源抢占软件问题:库版本冲突、死锁、内存泄漏

这些中断往往发生在训练的关键阶段,导致大量计算资源和时间的浪费。

Criu简介

Criu(Checkpoint/Restore in Userspace)是一个开源工具,能够在用户空间对运行中的进程进行快照(checkpoint)和恢复(restore)。它最初是为Linux容器设计的,但同样适用于普通的进程管理。

Criu的工作原理

Criu通过以下步骤工作:

冻结目标进程:暂停进程的所有活动收集进程状态:包括内存页、文件描述符、CPU寄存器、信号处理等序列化状态:将收集的信息保存到磁盘恢复时重建:从磁盘读取信息并重建进程状态

在DeepSeek训练中使用Criu

以下是具体的实现步骤和代码示例:

1. 安装Criu

# Ubuntu/Debiansudo apt-get install criu# CentOS/RHELsudo yum install criu

2. 修改训练脚本

我们需要在训练脚本中集成定期检查点的功能:

import osimport timeimport signalfrom deepseek_model import DeepSeekModel  # 假设的DeepSeek模型类class CheckpointableTraining:    def __init__(self, model_path="model.ckpt", checkpoint_dir="criu_checkpoints"):        self.model = DeepSeekModel()        self.model_path = model_path        self.checkpoint_dir = checkpoint_dir        os.makedirs(checkpoint_dir, exist_ok=True)    def setup_criu_signals(self):        signal.signal(signal.SIGUSR1, self.take_criu_checkpoint)    def take_criu_checkpoint(self, signum, frame):        checkpoint_id = f"checkpoint_{int(time.time())}"        checkpoint_path = os.path.join(self.checkpoint_dir, checkpoint_id)        # 保存模型状态        self.model.save(self.model_path)        # 创建Criu检查点        os.makedirs(checkpoint_path, exist_ok=True)        criu_cmd = f"criu dump -t {os.getpid()} -D {checkpoint_path} --leave-running"        os.system(criu_cmd)        print(f"Checkpoint created at {checkpoint_path}")    def restore_from_checkpoint(self, checkpoint_path):        # 恢复模型状态        self.model.load(self.model_path)        # 使用Criu恢复进程        criu_cmd = f"criu restore -D {checkpoint_path}"        os.system(criu_cmd)    def train(self, epochs):        self.setup_criu_signals()        try:            for epoch in range(epochs):                print(f"Epoch {epoch+1}/{epochs}")                self.model.train_one_epoch()                # 每6小时自动保存一次检查点                if epoch % 6 == 0:                    self.take_criu_checkpoint(None, None)        except Exception as e:            print(f"Training interrupted: {str(e)}")            print("Attempting to restore from last checkpoint...")            checkpoints = sorted(os.listdir(self.checkpoint_dir))            if checkpoints:                last_checkpoint = os.path.join(self.checkpoint_dir, checkpoints[-1])                self.restore_from_checkpoint(last_checkpoint)

3. 监控和自动恢复脚本

创建一个监控脚本,检测训练进程是否中断并尝试自动恢复:

#!/bin/bash# monitor_training.shTRAINING_PID_FILE="training.pid"CHECKPOINT_DIR="criu_checkpoints"# 启动训练过程python train.py &echo $! > $TRAINING_PID_FILE# 监控循环while true; do    pid=$(cat $TRAINING_PID_FILE 2>/dev/null)    if [ -z "$pid" ] || ! ps -p $pid > /dev/null; then        echo "Training process not running, attempting restore..."        # 找出最新的检查点        last_checkpoint=$(ls -t $CHECKPOINT_DIR | head -n 1)        if [ -n "$last_checkpoint" ]; then            echo "Restoring from $last_checkpoint"            criu restore -D "$CHECKPOINT_DIR/$last_checkpoint" &            echo $! > $TRAINING_PID_FILE        else            echo "No checkpoints available, cannot restore"            exit 1        fi    fi    sleep 60  # 每分钟检查一次done

4. 手动干预的恢复流程

如果自动恢复失败,可以手动执行恢复:

# 1. 找出最新的检查点LAST_CHECKPOINT=$(ls -t criu_checkpoints/ | head -n 1)# 2. 使用Criu恢复criu restore -D criu_checkpoints/$LAST_CHECKPOINT# 3. 验证恢复后的进程ps aux | grep train.py

技术细节与注意事项

内存使用优化

Criu快照会保存进程的所有内存页,对于大型深度学习模型,这可能导致检查点文件过大。可以通过以下方式优化:

# 使用页面服务器减少内存占用criu dump -t $PID -D checkpoint_dir --page-server --address 127.0.0.1 --port 9999

文件描述符处理

确保所有打开的文件在恢复后仍然可用:

# 在训练脚本中def setup_file_descriptors(self):    # 使用绝对路径    self.log_file = open("/absolute/path/to/training.log", "a")    os.set_inheritable(self.log_file.fileno(), True)

GPU状态保存

Criu无法直接保存GPU状态,需要额外处理:

def take_criu_checkpoint(self):    # 保存CUDA状态    torch.cuda.empty_cache()    device = torch.cuda.current_device()    torch.cuda.synchronize(device)    # 常规Criu检查点    # ...

网络连接恢复

如果训练涉及网络连接,可能需要额外的恢复步骤:

def restore_from_checkpoint(self):    # 重新建立网络连接    self.reconnect_to_cluster()    # 常规恢复流程    # ...

性能评估

我们对使用Criu的方案进行了性能测试:

检查点创建时间:约30秒(取决于模型大小)检查点大小:约等于进程内存使用量恢复时间:约45秒训练速度影响:<1%的性能下降

与重新训练3天相比,这些开销完全可以接受。

替代方案比较

除了Criu,还有其他几种保存训练状态的方法:

方法优点缺点
Criu完整进程状态保存,包括Python解释器状态需要Linux环境,对GPU支持有限
模型检查点简单易用,框架原生支持只保存模型参数,不保存优化器状态等
Docker commit完整系统状态保存镜像庞大,恢复慢
VM快照最完整的系统状态性能开销大,不灵活

实战案例:挽救3天的DeepSeek训练

在一次真实的DeepSeek模型训练中,我们的服务器在第72小时因机房断电而宕机。由于实施了Criu方案,我们能够:

找到最新的检查点(断电前6小时)恢复整个Python进程状态继续训练,只损失了6小时进度最终模型准确率与不间断训练相当

如果没有Criu,我们将面临:

完全的72小时训练损失重新开始训练的不确定性可能错过项目截止日期

高级技巧

1. 增量检查点

# 首次完整检查点criu dump -t $PID -D checkpoint_full --leave-running# 后续增量检查点criu dump -t $PID -D checkpoint_incr --leave-running --prev-images-dir checkpoint_full

2. 远程检查点存储

# 将检查点保存到远程服务器rsync -avz criu_checkpoints/ user@remote:/backup/deepseek_checkpoints/

3. 自动化检查点清理

def clean_old_checkpoints(self, keep_last=3):    checkpoints = sorted(os.listdir(self.checkpoint_dir))    for old_checkpoint in checkpoints[:-keep_last]:        shutil.rmtree(os.path.join(self.checkpoint_dir, old_checkpoint))

在长时间运行的深度学习训练任务中,进程中断几乎是不可避免的。通过集成Criu这样的检查点/恢复工具,我们可以显著降低中断带来的损失。本文展示的方案不仅适用于DeepSeek训练,也可以推广到其他长时间运行的机器学习任务中。

关键要点:

预防胜于治疗:在开始训练前规划好中断恢复策略定期检查点:根据训练时长设置合理的检查点频率测试恢复流程:确保在真正需要时恢复能够成功监控和自动化:减少人工干预的需求

实施这些策略后,你再也不用担心训练中断会抹去多日的辛苦工作。下次训练中断时,你可以从容地恢复进度,继续你的深度学习之旅。

免责声明:本文来自网站作者,不代表CIUIC的观点和立场,本站所发布的一切资源仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。客服邮箱:ciuic@ciuic.com

目录[+]

您是本站第14206名访客 今日有21篇新文章

微信号复制成功

打开微信,点击右上角"+"号,添加朋友,粘贴微信号,搜索即可!