ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

开发 YOLO 训练管理平台的 23 个实践教训

开发 YOLO 训练管理平台的 23 个实践教训

开发 YOLO 训练管理平台的 23 个实践教训

做视觉项目的人应该都有过这种经历:数据集在谁的电脑上、哪个版本,训练参数改了几轮之后完全记不清,模型存了一堆名字叫 final_final_v3 的权重文件,想复现一个三天前的实验结果却发现环境已经面目全非。更难受的是,团队里一旦超过两个人,每个人都有自己的数据划分习惯和标注规范,跑出来的指标根本没可比性。这些问题我当时统统踩过一遍,最后被逼着开发了一个面向 YOLO 训练的管理平台,从数据到训练再到部署验证,把整个流程串起来。这个项目从第一行代码到勉强能用的 v1.0,再到后来支撑多个业务线的稳定运行,前前后后折腾了大半年,中间踩的坑比想象中多得多。

这篇文章把这些实践教训整理成 23 条,覆盖平台架构、数据管理、训练调度、部署分析和工程化落地几个层面。如果你正准备做类似的 MLOps 工具,或者想给自己的 YOLO 训练流程搭一套规范化的管理方案,这些经验应该能帮你少走不少弯路。

1. 立项与整体架构:先想清楚平台到底解决什么问题

1.1 平台的核心价值不是“管模型”,而是“管实验”

很多团队一提做 MLOps 平台,第一反应就是把模型文件集中管理起来,给每个模型配一个下载按钮和部署状态。但实际做下来你会发现,模型文件本身只是产物,真正需要被管理的是“实验”这个概念——谁在什么数据集上、用了什么参数、跑了多少次迭代、结果指标是多少、当时的训练日志和权重文件在哪。模型只是实验的一种产出物,如果平台只盯着模型管理,那本质上就是个网盘加个列表页面,对训练流程的规范化和可复现性没有任何帮助。

我在设计之初就把“实验”作为平台的一等公民,每次训练任务对应一条实验记录,这条记录会绑定数据集版本、代码版本、配置参数、训练日志、指标曲线和产出的权重文件。这样后续追溯能力就完全不一样了:任何人打开平台,都能看到一条完整的时间线,知道某个模型的出处、依据和效果。

如果你也打算做类似的平台,建议第一步就想清楚核心实体是什么,否则后面数据结构会很混乱。实验记录 > 模型文件,这是第一条心得。

1.2 架构选型:先把 Django + Celery + Redis 这套组合说清楚

平台的服务端我选的是 Django,配合 Django REST Framework 提供 API,后台任务用 Celery 异步执行,Redis 同时承担消息队列和缓存职责,数据库用的 PostgreSQL。这个选型不算新颖,但胜在稳定、生态成熟、招人容易。

为什么不用 Go 或者 Node.js?坦白说,训练管理平台的核心瓶颈不在并发请求量,而在业务逻辑复杂度:数据集的版本关系、任务状态的流转、资源分配的并发控制,这些用 Django 的 ORM 和 Admin 后台来搞会快很多。而且做 CV 方向的人本身就更熟悉 Python,后续在平台里写数据增强脚本、评估逻辑、对接训练框架的集成代码,全部可以复用 Python 生态。

Celery 在训练平台里承担的任务比较杂:启动训练容器、轮询训练状态、处理数据集校验、发送通知消息。刚开始我只把训练任务交给 Celery,后来发现数据预处理和导出权重这种耗时操作也应该走异步,不然 Web 请求会被拖死。

1.3 从单体到模块化:平台按五层拆分

平台的逻辑架构我拆成了五块:

  • 基础设施层:GPU 服务器、存储、Docker、Kubernetes(早期直接 docker-compose)
  • 数据层:数据集仓库、标注信息、版本管理
  • 训练层:任务配置、资源调度、训练执行、日志采集
  • 评估层:指标计算、可视化对比、数据集测试
  • 应用层:Web 界面、API、用户权限

这五层构成了平台的骨架。最开始我犯的一个错误是把训练和评估耦合在一起——训练完直接在同一个进程里跑评估,后来想独立做模型对比测试就变得很别扭。后来把评估拆成单独的服务,从实验记录里取权重和测试集重新计算指标,灵活性一下子高了很多。各层职责要清晰,否则业务一复杂就寸步难行。

2. 数据管理与标签体系:所有模型效果的根基

2.1 统一数据集规范:路径、格式、命名

YOLO 训练的数据组织方式看起来很简单,一个 images 目录加一个 labels 目录,每张图对应一个 txt 文件。但团队协作起来,问题就来了:有的人标注是 PASCAL VOC 格式,有的人是 COCO 格式,有的人直接从 Roboflow 导出了 YOLO 格式但类别顺序和团队约定的不一致。如果不做统一规范,一个数据集换一种工具就全部乱套。

平台里我强制规定了一种标准目录结构:

dataset/ ├── config.yaml ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/

所有上传的数据集都必须是这个结构,config.yaml 里记录类别名列表和对应的 id 映射。如果不是这个结构,平台会自动尝试转换——VOC 转 YOLO、COCO JSON 转 YOLO 都提供转换脚本。但说实话,转换脚本只能解决格式问题,解决不了标注质量问题,某些数据集转换后很多框会偏移甚至错位,必须抽样可视化检查。

2.2 数据集版本管理:一个 Hash 解决数据漂移

训练管理平台最容易被低估的功能是数据版本管理。我采用的是类似 Git 的思路,对数据集内容做整体哈希,任何一张图片或一个标注文件发生变化,数据集的版本号就变化。版本之间不做增量存储,而是直接存完整快照,配合 NAS 或者对象存储,存储成本其实是可控的,但带来的确定性和心智负担降低是巨大的。

实际运行中这个设计救过我好几次。有一次一个标注外包团队给数据集加了几千张新图,如果按原始数据集继续训练,前面所有实验的结果都不可比。但因为平台里每个训练实验绑定了确切的数据集版本,对比时能明确看到哪些实验是基于旧数据集的、哪些是基于新数据集的,不会出现“明明模型提升了一个点但实际是数据变了”的伪提升。

**训练实验必须绑定数据版本,这是一条红线。**我在代码里做的是:数据集版本号作为训练任务参数的一部分,启动训练时会把这个版本号写入训练记录,模型评估时的数据集也是从版本快照中取,保证整个链路一致。

2.3 类的热词:类别排序、标注规范、长尾分布排查

YOLO 数据集的 config.yaml 中类别 id 的顺序必须固定,这是我踩过的一个比较深的坑。因为 YOLO 标注文件里每个目标的第一个数字就是类别 id,如果有人往 classes 列表中间插了一个新类别,后面所有类别的 id 全部后移,那你之前的所有模型训练结果等于作废——模型输出的是新 id,但训练时学的是旧 id,评估指标会直接崩掉。

在平台上,数据集创建后类别列表不允许修改,只能通过创建新版本的方式调整。如果非要改,平台会给出明确的风险提示,并要求二次确认。另外平台还会自动生成类别的数量分布直方图,帮助我们发现长尾问题——有些类别样本很少,模型对这个类别的识别效果会明显偏差,这种事在训练之前就应该知道,而不是等模型上线后被业务部门发现。

2.4 标注质量的自动化检查

纯靠人工看标注,1 万张图能把人看吐。我在平台里加了三个自动检查任务:

  • 检查 bbox 是否超出图片边界
  • 检查是否存在完全重合的重复框
  • 检查类别 id 是否越界

这三个检查都是毫秒级的操作,数据上传之后立即跑,有问题直接在数据集中标记出来。另外还做了一个弱监督的粗筛:用一个大模型(比如 YOLOv8 的官方预训练权重)先推理一遍,把那些模型很有把握但标注却完全不同的样本筛选出来,提示人工检查。这个方案不能直接判定标注错误,但能显著缩小人工复核的范围。

3. 训练流程与实验管理:让每一次训练都有迹可循

3.1 训练配置模板化:把超参数变成配置文件

训练 YOLO 时涉及的超参数很多:模型结构、输入尺寸、batch size、学习率、优化器、权重衰减、数据增强策略、训练轮数、预训练权重、类别数,还有各种 loss 的权重系数。如果全靠命令行传参,不仅容易出错,而且历史的实验记录里根本看不出来当时用的什么参数。

平台里我实现了一套训练配置模板机制。每个模板是一份 YAML 文件,包含所有参数和对应的说明。启动训练时用户选择一个模板,可以在界面上修改关键参数,系统会记录完整展开后的配置内容。这样同一个实验,任何人回看时都能精确知道它每一参数的值是什么。

3.2 资源调度:GPU 分配与队列管理

训练管理平台最核心的高频操作是资源调度。团队里可能有几台 GPU 服务器,上面跑着不同的任务。平台里我实现了一个简单的队列机制:每个训练任务进入队列,如果有空闲的 GPU 卡,调度器自动分配并启动;如果没卡,任务就挂起等待,用户可以设置优先级。

这个机制比想象中要难搞的地方在于,GPU 显存大小不一样,模型大小也不一样。比如一张 24G 的卡可以跑 YOLOv5m 配 batch size 64,但 11G 的卡只能跑更小的配置。平台里我给每张卡设定了显存容量和最大并发任务数,调度时根据任务配置的显存预估和卡的实际剩余显存做匹配。当然预估只是预估,实际训练跑起来显存占用可能有浮动,所以我用 docker stats 实时监控,超过安全水位就自动 kill 任务。

早期我还试着在平台里做 GPU 虚拟化切分,后来发现弊大于利——显存碎片化严重,训练性能下降明显,而且 LlamaFactory 这类框架自己也能适配单卡多任务,虚拟化切分反而多此一举。最终我选择的是:一张卡同时只跑一个训练任务,但允许推理任务共享显存。

3.3 训练状态机设计:提交、排队、运行、成功、失败

训练任务的状态管理是一个容易做乱的地方。我的设计是七个状态:

  • 待执行
  • 排队中
  • 运行中
  • 暂停
  • 成功
  • 失败
  • 取消

这里最关键的是“暂停”和“失败”需要分开处理。很多平台把停止和失败混在一起,出问题排查时根本分不清是用户主动 kill 的,还是程序崩溃导致训练中断。在我的实现里,如果是用户主动取消,状态置为“已取消”,如果是训练异常退出,则状态置为“失败”,并保留退出码和错误日志。

还有一个容易忽略的点:**训练任务的中途断点续跑。**YOLO 官方代码支持从上次保存的 checkpoint 继续训练,但如果平台不做任何处理,任务一中断用户就得从头再跑。我后来在平台里加了自动续训逻辑:检测到任务失败或意外退出时,如果该实验配置了 checkpoint 路径且存在最近的 checkpoint 文件,会询问用户是否从该 checkpoint 继续。

3.4 损失函数与训练曲线监控

实时监控训练损失和验证指标是训练管理平台的核心功能之一。YOLO 训练过程中损失函数一般包含三部分:分类损失(Classification Loss)、边界框回归损失(BBox Regression Loss)和置信度损失(Confidence Loss),通过一定的权重求和得到总损失。

loss_all = (box_loss * 0.05 + cls_loss * 0.5 + dfl_loss * 1.5)

这个权重比例在不同 YOLO 版本里可能有微调,v5 到 v8 的顺序和分档都变了好几次。如果你在训练中看到某个损失值下降异常快或波动非常大,先不要急着调超参,先确认一下损失函数的实现版本和你加载的权重是否匹配。

在平台上我做了两件与此相关的事:

  • 把训练日志中每一步的 loss 指标解析成结构化数据,存入时序数据库,前端用图表展示实时曲线
  • 对 loss 曲线的异常情况做告警。比如训练早期 loss 猛烈下降后直线走平,伴随验证集 mAP 下降,这大概率是过拟合了

为了达到监控的目的,平台接管了训练进程的 stdout 输出。由于 YOLO 训练大部分时间会输出进度条,用 \r 刷新,直接按行读根本读不到完整信息,我改成手工解析 ANSI 控制字符,把进度条里的数值抓取出来。这个活比较琐碎,但对使用者来说体验提升非常明显。

3.5 用 PyCharm 配置 Run/Debug 环境的坑

这个教训来自我自己平时调试平台的经历。平台启动训练时实际上是起了一个子进程或容器,如果想在本地调试训练流程,直接用 PyCharm 的 Run 按钮可以很快跑起来。但要注意,PyCharm 默认会把环境变量继承下来,比如 CUDA_VISIBLE_DEVICES 如果设置不对,训练子进程会发现明明有 GPU 却用不了。

后来我在启动训练的入口代码里强制检查CUDA_VISIBLE_DEVICES环境变量,如果在本地调试且没有手动设置,就给一个默认的显式值。另外 PyCharm 的 Python Console 里跑训练任务经常出现 subprocess 的 stdout 缓冲问题,日志输出不实时,我在单元测试里会禁用行缓冲,但日常调试时建议直接用 Terminator 或系统终端,省得被缓冲问题干扰。

3.6 从 checkpoint 到最终权重:导出时的标签处理技巧

在训练平台中,YOLO 训练完成之后通常有两个产物:一个是训练过程中每轮保存的 checkpoint,一个是最终用于推理的权重文件。这里有个很多人忽略的细节:**最终权重文件的导出需要同时包含类别信息。**在部署时,YOLO 推理代码本身不会从权重文件里反推出类别名列表,必须由平台在导出时写入 classes 信息或生成配套的配置文件。

平台里我在导出路径下固定生成一个metadata.json,内容包含:

{ "model_name": "yolov5s_custom_v3", "classes": ["person", "car", "bike"], "input_size": [640, 640], "conf_threshold": 0.25, "iou_threshold": 0.45, "dataset_version": "acc7f2" }

这样模型部署之后,上下游系统可以直接读这个文件,而不是每个服务各自维护一份类别列表。之前业务方就出现过一次模型更新后还沿用旧的类别顺序,结果线上推理结果全部错位的严重事故。根源就是没有把类别信息和权重一起传递。

4. 推理部署与分析:模型管理平台的最后一公里

4.1 多种导出格式与硬件适配

训练平台不只是把权重保存下来就够了,后续还要支持模型导出和部署验证。YOLO 官方支持导出成 ONNX、TorchScript、TensorRT Engine 等格式。不同的部署场景需要不同的格式:

  • 服务器端 GPU 上低延迟推理,选 TensorRT 格式
  • 移动端或边缘设备,选 ONNX 配合相应推理框架
  • 服务端高吞吐批量推理,TorchScript 或 ONNX 都可以

平台里我实现了导出队列,用户提交导出请求后,系统会在对应的 GPU 机器上执行导出脚本。这里要特别注意一个点:TensorRT Engine 是针对特定 GPU 架构生成的,不同算力的卡不能混用。比如在 A100 上导出的 TensorRT Engine 不能直接拿到 RTX 3090 上部署,必须重新导出。平台里我记录了每台服务器 GPU 的架构类型,导出时标注适用的设备型号,避免算法同学拿一个 TensorRT 文件到处用。

4.2 边缘部署:AGX Orin 上的环境搭建

最近有不少项目要在 AGX Orin 这类边缘设备上部署 YOLO,它们在开发机训练,到边缘设备上推理,中间环境传承的问题比较多。AGX Orin 是 ARM 架构,Python 版本、PyTorch 版本的选择都需要注意,JetPack 版本直接影响 PyTorch 和 TensorRT 能否正常安装。

在平台上,针对这类边缘设备我专门出了一个部署包生成功能:基于开发机的训练产物,自动打包成一个带依赖的目录,包含运行脚本、模型文件、配置文件、必要依赖的离线安装命令。核心是引导用户先在 AGX Orin 上跑一次环境自检脚本:

python scripts/check_env.py

这个脚本会检查 JetPack 版本、CUDA 版本、TensorRT 版本、PyTorch 是否编译可用。如果版本不匹配会给出具体建议的对应版本号。实际经验是,在 AGX Orin 上常见的坑是 PyTorch 官方 pip 包不支持 ARM 架构,必须从 JetPack 的环境装 NVIDIA 提供的预编译版本。这个点你写在任何技术文档里都容易被忽略,但在部署现场踩一次就记住了。

4.3 评估集与模型对比:指标不是越多越好

平台里的模型评估模块,我一开始设计的时候塞了一大堆指标:mAP@0.5、mAP@0.5:0.95、Precision、Recall、F1-score、各种类别分别的 AP。后来发现界面上一屏放不下,业务方根本看不明白,算法同事对比模型时也被大量指标搞得无所适从。

迭代后我改成默认只展示三个核心指标:

  • mAP@0.5
  • mAP@0.5:0.95
  • 中等难度样本上的 F1-score

其余指标放到详情页里,对比模型时默认以 mAP@0.5:0.95 为主指标排序。指标做减法,比做加法更能提升平台的实用价值。

模型对比的实际操作流程,我建议这样走:

  1. 选定同一测试数据集版本
  2. 从实验记录里选取需要对比的模型权重
  3. 批量执行推理脚本,生成每个模型的预测结果
  4. 统一计算指标,生成对比表格
  5. 抽取若干典型图片可视化放图对比

第 3 步是最耗时的,所以评估任务必须异步执行。平台上跑一次 1000 张图的验证集,如果用单卡推理可能要等十几分钟。用户可以直接关掉页面,收到通知后再回来看结果。

4.4 不只会检测也要会分析:与多模态 AI 结合的一个案例

之前有个“智慧交通事故检测分析系统”的需求,要做车辆的实时检测,还要分析事故场景中的具体行为。这种场景如果只用目标检测模型,只能输出画面中有车辆、人这些对象,至于“这辆车是否违章、这个人是否在车内”这类语义问题就答不上来。

我的做法是目标检测模型负责定位,把识别到的目标区域截图后,送入多模态大模型去做语义分析和行为判定。这套方案已经能解决相当多的实际问题:比如检测模型把图像中的目标框出并在区域内提取一帧,多模态模型根据这张裁剪图和额外的提示词,回答“该区域是否疑似事故碰撞区域”“当前场景是否存在异常行为”。这种结合说起来简单,但平台侧要支持的东西不少:

  • 提供友好的裁剪、保存、批量推理的能力
  • 支持定义提示词模板,方便不同业务复用
  • 保留每张图片的分析记录,方便后续复盘和优化模板

如果说整个平台是一把锤子,那结合多模态分析就是往工具箱里加了一把多功能扳手。很多业务方真正想要的不是一个单独的检测框,而是一个“看得懂图又能给出结论”的环节。

4.5 实时推理服务的灰度发布

平台最终要对接线上的推理服务,比如某个视频流分析系统。这时候如果直接替换模型,风险比较大,尤其新模型表现不如预期时,影响面是实时的。我在平台里加了一个“灰度发布”的概念:推理服务可以同时配置多个模型,并给每个模型分配流量权重。比如新模型先放 10% 的流量,跑一段时间对比线上指标,如果稳定再逐步放大。

这个功能不复杂,本质上就是一个流量比例配置表 + 推理服务加载不同模型。但它给团队带来的信心是很大的——算法团队不再怕上线,运营过程中如果出了异常,一键回滚到旧模型即可。

5. 平台工程化与运维:稳定压倒一切

5.1 用户权限:算法同学互不干扰

训练管理平台如果是团队共用的,必须考虑多用户隔离。我的方案是“项目空间” + “角色权限”:

  • 每个项目空间有自己的数据集、实验、模型
  • 项目成员分为管理员、开发者、只读访客
  • 实验记录和模型默认只对项目成员可见

如果少了这层,最直接的后果是:A 同学在平台上删掉一个数据集,B 同学第二天训练全部失败;C 同学把实验配置改成了新的学习率,D 同学误以为这是基准配置,跑出了一堆无效实验。我早期没有做严格隔离,团队七八个人用起来鸡飞狗跳,后来上了权限隔离,这类问题几乎消失。

5.2 任务可视化:进度、日志、资源占用三合一

训练任务详情页我做了三栏布局:左侧显示整体进度,包括当前 epoch 数、迭代步数、预计剩余时间;中间是实时日志流,可以滚动查看最近 2000 行;右侧是 GPU 利用率和显存占用图表,用 nvidia-smi 采样数据。

这样的设计看起来是给管理员的,其实最终受益的人是算法工程师自己。他们能一眼看到某个实验还在不在跑、显存是不是被占满了、日志有没有报错。很多 GPU 任务其实不是模型本身问题,而是机器上其他任务挤占了资源导致训练卡死,图表一出来问题立刻定位。

5.3 日志收集的一种快速方案:Docker + JSON + 轮询

训练平台要对多个训练任务的日志做集中收集,最简单的方案是用 Docker 启动训练容器,容器日志通过 Docker SDK 输出,然后在平台里实时解析。但有一个坑:如果训练任务在 Docker 容器内,日志里中文可能编码不对,会漏掉关键错误信息,需要提前统一设置 UTF-8 编码。

另一种稍微轻量点的方案是:训练进程直接把日志写到指定的 JSON 文件,一个日志条目是一行 JSON。平台的任务监控进程用tail -f方式读取文件,按行解析后入库。这种方式比 Docker SDK 更可控,我最后是在这个方案上加了一个日志轮转,日志文件超过 100MB 自动切割,防止磁盘被单个任务写满。

5.4 告警通知:训练失败和早停的区分处理

训练失败会自动发送通知到用户的即时通讯工具,最初我只做了这种失败通知。但实习了之后,发现更常见的场景不是失败,而是训练看起来在跑但其实已经毫无进展——loss 不再下降,验证集指标长时间不动。

这种情况下平台应该自动早停,但早停之前最好通知用户,让用户决定是继续跑还是杀掉任务。我在平台里实现了一个简单策略:如果验证集 mAP 连续 20 个 epoch 没有提升,就发一条“疑似收敛停滞”的告警信息,附带最近 20 个 epoch 的指标趋势;如果连续 50 个 epoch 没有提升,自动标记为“可取消”,等用户确认后停止。

5.5 批量启动一键部署脚本

团队里经常要做环境初始化,比如新加入的算法同学需要在自己机器上配置一套能跑通平台训练的环境。这事儿如果全靠人工按文档操作,至少要半天,而且每个人装出来的环境都可能不一样。我在平台仓库里写了一个一键部署脚本,原理上就是把环境检查、依赖安装、目录创建、配置生成全部自动化。

脚本核心逻辑很简单,就是按平台运行所需的依赖清单逐一安装,并在最后做一个启动自检:

python manage.py check --deploy

这个命令会检查数据库连接、缓存连接、关键目录权限等。如果自检没过,脚本会给出明确的失败原因,比如“PostgreSQL 连接超时”或“Redis 密码错误”,而不是让用户在启动后看到一个难以理解的服务报错。

5.6 平台自身的监控与可观测性

训练管理平台自己也是一个软件,它也会挂。平台挂了之后,忙碌的训练任务本身还会继续跑,但用户没法看日志、没法提交新任务、没法获知训练状态。所以对平台自身的监控很重要。

我关注的几个核心监控指标:

  • API 响应延迟(正常情况 95% 请求低于 300ms)
  • Celery 任务队列堆积长度
  • 每台 GPU 服务器的资源水位
  • 训练日志写入量瞬时速率

这些指标统一推到 Prometheus + Grafana。有一次训练高峰期,大量实验同时启动,数据增强进程把 CPU 跑满,导致 API 响应变得很慢,我在监控上看到 P95 延迟飙升到 2 秒,马上定位到问题并加了资源限制,控制每台机器同时启动的训练任务数。

5.7 存储扩容与数据备份策略

数据集和模型权重文件体积非常大,一个 YOLO 训练实验产出的中间文件可能多达几十 GB。平台的存储设计如果没有未雨绸缪,很容易在运行 3 个月后爆掉。

我的思路是分级存储:

  • 热数据(近期实验、常用数据集)放在高速存储上
  • 冷数据(超过 90 天未访问的数据集和模型)自动迁移到廉价存储
  • 所有数据定期保存快照,备份周期按数据重要程度区分

磁盘告警阈值设置为整体使用率的 80%,到 90% 时自动暂停新训练任务的启动,防止因为磁盘满了导致训练写入失败、进程崩溃。这个机制一度被认为是多余的,但后来真有一次存储空间耗尽导致两个训练任务失败,恢复后大家就再也没有经历过类似问题了。

6. 23 条实践教训速查清单

前面每一节其实都蕴含了若干教训,这里我按“干条”的方式汇总成 23 条,有需要的可以直接收藏:

  1. 平台核心实体是“实验”,不是“模型”,数据模型设计从实验出发。
  2. 前后端分离要学会,但不是核心,优先把后端业务逻辑做扎实。
  3. 数据集目录结构和 config.yaml 必须统一,上传时强制校验。
  4. 数据集一旦生成,类别顺序禁止修改;要改就创建新版本。
  5. 每个训练实验必须绑定数据集版本号和代码版本号。
  6. 训练参数一律用配置文件传递,不在代码里硬编码。
  7. 状态机要区分“用户取消”和“异常失败”,二者不可混为一谈。
  8. 日志采集必须处理 ANSI 控制字符,否则拿不到进度条里的指标。
  9. 权重导出时同时导出类别信息,避免部署时类别错位。
  10. TensorRT Engine 绑定 GPU 架构,不能跨卡搬运。
  11. 指标默认只展示前三个核心项,其余放进详情页。
  12. 评估任务必须异步执行,不能阻塞主流程。
  13. 面向边缘设备时要提前生成环境自检脚本,而不是给一份安装文档。
  14. 多模态分析调用时,先把检测区域裁剪出来,再送大模型,性能会好很多。
  15. 模型灰度发布功能要尽早加,避免上线事故。
  16. 权限隔离从第一天开始做,别等团队变大了再补。
  17. 训练日志要轮转,防止单个文件无限膨胀。
  18. 告警要区分“失败”和“疑似停滞”,两种处理策略完全不同。
  19. 一键部署脚本里必须带环境自检,能在启动前发现依赖问题。
  20. 平台自身也需要监控,API 延迟、队列长度、磁盘水位是核心指标。
  21. 存储要分层,冷热分离 + 自动迁移,否则迟早爆盘。
  22. 训练进程的 stdout 缓冲会导致日志不实时,要在调用处设置环境变量关闭行缓冲。
  23. 所有错误提示必须包含上下文信息(比如数据集版本、任务 id),否则排查问题如同大海捞针。

7. 最后说两句亲身感想

如果把“开发一个训练管理平台”当作一个纯技术项目来看,那它确实包含了不少工程细节——上面 23 条里有一半都属于那种“不踩一次根本想不到”的坑。但以我个人的体会,它更接近一个流程治理项目,技术选型反而不是最大的变量。真正决定平台能不能被团队用起来的,是那套围绕数据、代码、参数、产物建立的绑定和审计关系,以及权限设计、告警设计、可视化细节带来的使用体验。

这个平台后来逐步扩展到支撑不止 YOLO 模型的训练,像检测类模型、分类模型、实例分割模型也都可以跑在上面。结构上只要把“算法框架”抽象成配置项,就能复用整条链路。如果你们团队也在规划类似的事情,我另外的建议是:先不要把“训练平台”想得太重,可以用 TensorBoard 加一套文件目录规范先顶起来,等痛点足够明显了再往平台化迭代。方向对了,节奏慢一点都来得及。

要让我重做一次,我会在项目早期强行做一件事:把数据集、代码、参数、产物的绑定关系钉死在后台逻辑里,而不是靠人的自觉。这套关系一旦稳定下来,平台就已经成功了一半。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进