资讯动态

YOLOv5训练中断怎么办?详解resume接续训练机制与实操指南

发布时间:2026/9/29 1:09:58 来源:尧图企业网站定制
训练目标检测模型的人十有八九都经历过“跑到一半断了”的崩溃时刻。我在 Jetson Nano 上用 YOLOv5 做水果识别项目时两万多张图、100 轮训练要跑将近两天结果第三天醒来发现 SSH 早就断了权重停在 37 轮。所以这篇博客我就专门讲讲 YOLOv5 的 resume 接续训练它恢复的到底是什么、怎么一条命令无缝接上、--epochs 有哪些隐藏逻辑以及我在接续训练里踩过的各种坑。无论你是刚跑通自己的数据集还是正在做车牌识别、目标检测部署这类长周期项目这篇文章都能帮你少走弯路。1. 先把概念理清resume 到底在恢复什么1.1 为什么训练中断是常态而不是意外很多人以为训练中断是小概率事件等真遇到了才开始手忙脚乱。我自己经历了几个项目之后反而把中断当成了长训练任务的默认状态。原因很简单一次像样的目标检测训练数据集动辄上万张100 轮跑下来少说也要十几个小时多的能到两三天。在这个时间跨度里断电、断网、SSH 超时、云服务器实例被回收、显存被其他进程抢走导致 OOM随便哪一个环节出问题进程就没了。还有个容易被忽略的场景手动中断。我自己就经常在训练跑到一半的时候因为想调学习率或者想看看中间效果直接 CtrlC。这时候如果没有接续训练的能力前面的算力就全白费了。更要命的是深度学习训练本身带随机性即使同样的参数、同样的数据、同样的随机种子重训一次的结果也不会完全一样。如果项目还涉及到后面要写报告、做对比实验从头再来不仅浪费算力还会让实验结果变得不可复现。所以我的第一个建议是从项目开始的第一天就把“随时可能中断”当成前提来设计实验。而 YOLOv5 官方其实早就把这件事想好了它提供了非常成熟的 resume 机制只是入口藏得比较深很多人不知道而已。1.2 YOLOv5 每轮训练到底在存什么要理解 resume先得知道 YOLOv5 的存档机制。训练过程中每一轮结束代码都会往runs/train/exp*/weights/目录下写两个文件last.pt和best.pt。last.pt是最近一轮完成后的完整状态快照best.pt则是验证集指标默认是 mAP0.5:0.95 和 mAP0.5 的组合适应度最好的那一次快照。很多新手会把这两个文件当成普通的“权重文件”实际上它们内部装的东西远比想象中多model网络模型结构、所有权重参数、BN 层的统计量ema指数滑动平均模型这是 YOLOv5 训练时真正用于验证和最终推理的那份模型optimizer优化器的完整状态包括带动量时的一阶动量、二阶动量缓冲epoch已经完成的轮数best_fitness当前最优适应度值training_results训练曲线数据用于拼接成完整的 results 曲线opt训练参数的快照包括 data 配置路径、超参数文件、batch_size、imgsz、设备等hyp超参数字典你注意看这些字段就会发现 resume 并不只是“拿回权重”而是把整个训练状态完整地接过来。这就是它能无缝续跑的根本原因。1.3 resume 和“加载权重重训”是两码事这是我在社区里看到被误解最多的一个点。很多人以为用--weights last.pt --epochs 100接着跑就是续训了其实完全不是一回事。真正的 resume 和普通的加载权重启动差别非常大对比项--resume 接续训练--weights 加载权重冷启动模型权重从断点恢复从断点恢复优化器状态完整恢复重新初始化学习率调度器恢复当时的进度从头开始epoch 计数从上次轮数1 开始从 0 开始EMA 模型恢复重新构建超参数沿用断点保存的使用当前命令行传入的训练曲线拼接延续新开一条曲线看到没有--weights的方式只恢复了模型权重而优化器的动量和学习率调度全丢了。这会导致一个很尴尬的结果训练好像从上次的损失值附近继续但优化器的“惯性”没了学习率也回到了初始值实际效果跟重新训差不多甚至可能因为损失状态和优化器状态不匹配而出现震荡。所以如果你真想接着上次的进度跑请务必用--resume不要用--weights装样子。2. 接续训练前的检查与准备2.1 先确认 last.pt 是完好的我在实战中养成的一个习惯是任何续训操作之前先花两分钟确认断点文件是健康的。原因很简单如果你是在训练过程中被强杀进程或者 CtrlC 恰好赶上了 PyTorch 正在写文件的时间点last.pt有可能是半个文件直接 load 会报错或者加载出损坏的权重。检查方式很直接。先看文件大小和修改时间ls -lh runs/train/exp5/weights/正常的 last.pt 通常有几十到几百 MB具体大小取决于你用的是 s、m、l 还是 x 版本。如果发现文件只有几 KB那基本可以断定是写坏的这种情况就别硬续了去找更早的存档或者 best.pt 保底。接着再用 Python 加载一次验证import torch ckpt torch.load(runs/train/exp5/weights/last.pt, map_locationcpu) print(ckpt[epoch]) print(ckpt[opt])能正常打印出 epoch 和 opt说明文件结构完整可以放心继续。这一步别看简单能在关键时刻帮你省下好几个小时的无用功。2.2 环境、数据集、缓存三件套接续训练最怕的就是“环境漂移”。我的原则是能用和上次训练相同的 Python 环境就不要随便升级。torch 和 torchvision 的大版本如果变了轻则加载 checkpoint 时出现 key 匹配警告重则直接报错。特别是有些老项目用的是 YOLOv5 早期版本新版 torch 对旧 checkpoint 的兼容性并没有那么乐观。数据集路径也很关键。--resume恢复的是 checkpoint 里保存的配置如果你的数据集目录在这期间被移动过、重命名过训练会在加载数据的时候就报错。所以续训前先确认 data yaml 里写的路径还活着或者准备好用新的 data 文件覆盖指定。还有一个容易被忽视的是缓存。如果你上次训练用了--cache ram或者--cache disk数据集会被缓存成内存张量或者磁盘缓存文件。resume 之后代码会尝试复用缓存但缓存文件的构建时间戳、数据集的哈希信息如果对不上它会选择重新构建。在 Jetson Nano 这类内存紧张的设备上--cache ram续训时内存可能直接爆掉我的做法是续训时改用--cache disk或者干脆不用缓存等确认能跑起来再根据资源情况调整。2.3 从训练日志里确认上次跑到哪在敲续训命令之前我强烈建议先看一眼训练目录里的opt.yaml和results.csv老版本是results.txt。opt.yaml记录了完整的训练参数包括当初设的总轮数、batch_size、imgsz、超参数文件路径。results.csv的最后一行能看到你已经跑完的轮数和当时的各项损失、指标。这一步不是为了仪式感而是因为续训命令的输出有时候会误导人。比如你看到“Epoch 38/100”如果不知道原来的 100 是从哪来的就没办法判断这个 100 是你要的总轮数还是别的什么。先查日志心里有数了再操作后面出了问题也更容易定位。3. 三种接续训练方式按需选择3.1 方式一--resume 全自动接续最省心的做法就是直接复用 YOLOv5 官方给的自动恢复机制python train.py --resume执行之后代码会自动去runs/train/目录下扫描各个 exp 子目录找最新的weights/last.pt然后读取里面的完整训练状态恢复到中断前的进度。使用这种方式时你不需要重新指定--data、--hyp、--batch-size、--imgsz这些参数因为它们都已经存在 checkpoint 里了代码会把它们一并恢复过来。但这里有个隐藏的坑自动扫描是按目录名字符串排序来找“最新”的。也就是说exp10 在字符串顺序上排在 exp2 前面如果你的实验目录多了自动选择可能选到不是你想要的那次训练。所以我的经验是只有当你确定runs/train/下面只有一次实验或者最近一次实验就是你要续的那个时才放心用这种最简写法。3.2 方式二--resume 指定 last.pt 精确恢复当你有多个实验目录或者想接着很早以前某一次训练继续时直接把权重文件路径传给--resume是最稳妥的python train.py --resume runs/train/exp5/weights/last.pt这种方式本质上和方式一做的事情一样都是完整恢复训练状态唯一区别是你手动指定了断点文件的来源绕开了自动扫描的不确定性。我个人的习惯是只要runs/train/下的实验目录超过两个一律用完整路径。这里多提醒一句有人会想“既然 resume 能指定文件那我指 best.pt 是不是也行”技术上确实可以因为 best.pt 里也有完整的训练状态但我不推荐这么干。best.pt 保存的是历史最优那一步的模型和对应的优化器状态它的优化器动量和学习率状态停留在过去某一步。你续训的目的是顺着最近的训练轨迹继续走而不是跳回历史最优重新走一遍。正常续训请认准 last.pt。3.3 方式三--weights 手动续训及其局限名字叫“手动续训”其实不准确准确说法是“加载权重重新开始”。命令长这样python train.py --weights runs/train/exp5/weights/last.pt \ --data fruit.yaml --epochs 100 --batch-size 16这段命令只是把 last.pt 里的模型权重拿出来当作预训练权重来初始化一次全新的训练。优化器重新建、学习率从头调、epoch 从 0 开始、训练曲线另起一行。这和在第 1.3 节里对比表说的情况一模一样。那什么时候可以用这种方式我一般只在两种情况下用它一是你想彻底改变训练配置比如换了数据集、改了类别数原来的优化器状态已经毫无意义二是你想故意让学习率从头开始把续训变成一次“带有成熟权重的重新训练”。除此之外只要你是想接上次的进度老老实实用--resume。3.4 --epochs 参数里藏着的两个坑关于续训时--epochs的行为网上的说法非常乱因为 YOLOv5 不同版本的处理逻辑确实有差别。在较新的 v6.0 之后--resume会用 checkpoint 里保存的opt覆盖大部分命令行参数也就是说你续训时再传--epochs 150很可能是无效的最后还是按原来保存的总轮数跑。如果你确实想延长总轮数比如原来设了 100 轮跑到 37 轮中断了现在想一口气跑到 150 轮我的做法是直接改 checkpoint 里的配置。用一段小脚本把 epochs 改掉再存回去import torch ckpt torch.load(runs/train/exp5/weights/last.pt, map_locationcpu) print(current epoch:, ckpt[epoch]) print(target epochs before:, ckpt[opt].epochs) ckpt[opt].epochs 150 # 如果 opt 是 dict 就写成 ckpt[opt][epochs] 150 torch.save(ckpt, runs/train/exp5/weights/last.pt)改完再执行--resume控制台就会显示目标总轮数变成 150。注意改之前一定先备份原文件。这个方法我用了很多次适用于大多数版本的 YOLOv5比猜命令行参数的行为靠谱得多。4. 实操演示从第 37 轮把训练接回 100 轮4.1 复现场景与完整命令我用一个实际场景把整个流程串起来。项目背景是水果识别数据集放在datasets/fruit/下数据配置文件是fruit.yaml我在一台 8G 显存的 GPU 上训练 YOLOv5s初始命令是python train.py --data fruit.yaml --weights yolov5s.pt \ --epochs 100 --batch-size 16 --imgsz 640 \ --cache disk --device 0 --name fruit_run训练正常推进到第 37 轮session 断了。这时候我先按第 2 节的方法检查断点文件确认runs/train/fruit_run/weights/last.pt是完好的epoch 字段显示 37然后直接执行python train.py --resume runs/train/fruit_run/weights/last.pt控制台会先打印一行类似这样的信息Resuming training from runs/train/fruit_run/weights/last.pt紧接着加载数据和模型然后进入正常的训练循环。输出格式和初训时一样但 Epoch 那一列会从 38 开始Epoch gpu_mem box obj cls labels img_size 38/100 4.21G 0.0421 0.0113 0 27 640看到38/100你就放心吧说明代码准确读取了断点里的轮数接下来会一直跑到第 100 轮。训练过程中results.csv也会接着上次的数据继续追加最后画出来的曲线是从第 1 轮到第 100 轮完整的而不是断成两截。4.2 怎么确认真的接续成功很多人续训跑起来之后就不管了直到最后发现曲线不对才回来查。我建议从三个维度确认接续成功。第一是看控制台轮数。这是最直接的信号如果显示的是1/100那肯定有问题说明你用了--weights而不是--resume。第二是看训练目录文件的变化。续训会在同一个实验目录里继续写last.pt和best.pt如果代码新建了一个 exp 目录那就是恢复失败了。第三是看曲线连续性。训练结束后打开results.png正常情况是一条从第 1 轮到第 100 轮的连续曲线中途不会出现断线或指标的异常跳变。如果你同时用了 TensorBoard也可以在--logdir runs/train/fruit_run下面看到完整的训练历史。4.3 中途想调整超参数的特殊情况续训过程中遇到想调超参数的情况我建议你分清楚“能不能调”和“该不该调”。在较新的 YOLOv5 版本里--resume会恢复 checkpoint 里保存的hyp和opt命令行里传的--hyp大概率不生效。所以如果你真想改学习率、改损失权重最干净的办法是先结束当前训练把断点保存好然后用--weights加载上一轮的权重重新开始一轮带新超参数的训练。但我在实战中要给你一句忠告不到万不得已别在续训时改超参数。因为学习率调度器、优化器动量和损失函数是配套工作的你单独改了lr0或者改了loss权重相当于让一辆高速行驶的车突然换个方向盘损失曲线很容易直接飞掉出现 NaN 也不是没见过。如果只是想让损失继续下降优先考虑保持超参数不变延长总轮数而不是中途换方案。5. 常见问题与排查技巧实录5.1 高频报错排查速查表我整理了这几年来续训时遇到的高频问题做成一个速查表大家可以直接对照。现象可能原因解决办法resume 后训练立刻结束目标总轮数小于等于已完成轮数按 3.4 节方法修改 checkpoint 中的 epochs提示找不到 last.pt自动扫描选错目录或目录被清理使用--resume加完整路径CUDA out of memory恢复后显存占用比初训高调小 batch-size或清理显存后重试加载权重 shape 不匹配改了模型结构、类别数或用了不匹配版本确认 nc 一致不一致则冷启动训练dataloader 报找不到数据数据集路径变更检查 data yaml用新路径覆盖results.csv 解析报错文件在中断时被写坏删除 results.csv 让代码重建曲线会从当前轮开始多卡 DDP 无法接续启动方式与上次不一致使用与上次相同的多卡启动命令加--resume续训后 loss 出现 NaN断点损坏或超参数被意外修改回滚到更早的备份断点重新续训5.2 我踩过的四个典型坑第一个坑是断点文件损坏。有一次训练在云服务器上跑实例被强制回收我拿回快照后直接--resume结果报错说 checkpoint 读取失败。排查半天发现 last.pt 在写入过程中被打断文件只有几十 KB。后来我养成了习惯重要实验每隔一段时间手动备份一次 last.pt 到别的目录宁可多占点存储也不赌运气。第二个坑是跨环境续训。我在一台卡上训练到一半因为资源调度问题换到了另一台机器torch 版本从 1.10 升到了 1.13结果 resume 后模型加载是成功了但 EMA 相关的参数在评估阶段频繁报错。最后只能退回旧环境重新跑。从那以后我接续训练前一定会核对 torch 版本绝不心存侥幸。第三个坑跟缓存有关。在 Jetson Nano 上做车牌识别时我初训用了--cache ram中断后续训忘记调整结果内存直接被数据集缓存打满系统卡死。这个问题在内存紧张的设备上尤其明显续训前先想清楚缓存策略。第四个坑是 best.pt 和 last.pt 用反了。我见过有人把 best.pt 拿去续训理由是“这个效果最好”结果训练曲线直接从历史某一步重新开始后续几十轮白白浪费。记住一个口诀续训用 last部署用 best。5.3 长任务训练前就做好的保全措施接续训练固然能救急但更聪明的做法是提前避免中断带来的损失。我现在跑长任务之前一定会做四件事。第一开启--save-period。这个参数可以每 N 轮额外保存一份权重。比如--save-period 10每 10 轮就多存一个last10.pt、last20.pt万一最近的 last.pt 损坏了最多损失 10 轮的进度。第二训练进程务必用 tmux、screen 或者 nohup 挂起来。我自己的标准操作是tmux new -s train进入会话再跑训练这样即使 SSH 断了训练也能在后台继续。第三训练前检查磁盘剩余空间。YOLOv5 每轮都要写权重断点文件、缓存文件加起来占的空间不小。磁盘写满导致的训练中断是我见过最冤的一种。第四合理设置 workers。数据加载的--workers调得太高会导致内存压力增大尤其在 Docker 或小内存设备上进程很容易被系统 OOM killer 杀掉。我一般会根据机器内存和数据集体积把 workers 控制在 4 到 8 之间。最后说点实在的体会。resume 这个功能我在水果识别、车牌识别这些项目里反复用过每次都能把我从“从头再训”的绝望边缘拉回来。其实 YOLOv5 的设计逻辑一直很清晰它把训练状态完整落到磁盘上就是为了让你在任意一轮中断后都能原样接上。你只需要记住三条铁律——续训认准 last.pt、命令行参数交给 checkpoint、延长轮数去改 saved opt。把这三点刻在脑子里以后无论训练断多少次你都能淡定地一条命令接回来。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑