我把 YOLO 训练做成了网页版:需求设计与技术选型(系列第 2 篇)
本系列记录一个人从零开发 YOLO 训练管理平台的完整过程。第 1 篇是《开发 YOLO 训练管理平台的 23 个实践教训》,这篇回到起点,讲讲动手之前的需求设计和技术选型------哪些要了,哪些坚决不要,以及为什么。
背景:为什么不用现成的
公司做工业视觉检测,数据集越攒越多,痛点很具体:
- 数据集散落在各台机器的文件夹里,"这个模型用的哪版数据"没人说得清
- 标注靠 LabelImg 单机标,多人协作靠拷文件夹
- 训练要开命令行敲 yolo 命令,非算法同事根本没法自助
- 训练完的 .pt 文件满天飞,哪个是最新的、指标多少,全靠文件名和记忆
先看了一圈现成方案:CVAT 和 LabelStudio 标注很强但不管训练;各种 MLOps 平台(ClearML、Weights & Biases)管训练但对标注和数据集快照的支持要么没有要么很贵;而且它们几乎都要 Docker + 数据库 + 一堆服务,给公司内部的 Windows 机器部署简直是灾难。
结论:自己写一个,把"数据 → 标注 → 训练 → 模型"整条链路塞进一个程序里。
需求:就做这七件事
动手前把需求砍到最小集合,每条都有明确的边界:
- 素材库:导入 YOLO/VOC 格式的 zip,自动识别格式,VOC 内部统一转 YOLO 存储
- 在线标注:浏览器里画框/移动/删除/改类别,支持模型预标注(带 conf 阈值滑杆)
- 数据集版本快照 :随时"保存版本",可回滚查看;训练任务必须引用版本,保证可追溯
- 在线训练:选数据集版本 + 填几个参数就能发起,实时曲线,串行队列(一次只跑一个),中断可续训
- 模型版本管理:模型是"系列",每次训练完自动入库一个新版本(.pt + 指标 + 来源 + 操作人)
- 试模型:网页上传图片,选模型版本,出检测框
- 登录与两级权限:admin 管账号和删除,member 干其余所有事
其中第 3 条是整个系统的灵魂,后面单独讲。
技术选型:每一项都是"够用就好"
后端:FastAPI + SQLite,单进程托管一切
bash
浏览器 ←→ FastAPI(单进程)
├── /api/* 业务接口
├── /* 前端打包后的静态页
└── /data/* 图片等静态文件
不装数据库、不装 nginx、不要 Docker。理由:
- 元数据量级小 :50 万张图的标注元数据也就 1GB 左右,SQLite 绰绰有余(理论上限 281TB),而且单文件意味着备份 = 拷目录
- 并发低:公司内部同时使用的人一只手数得过来,SQLite 的写锁完全不是瓶颈
- 部署是最大约束:目标机器是公司的 Windows 台式机,维护者不是专业运维。每多一个组件(PostgreSQL、nginx、Redis),部署失败率和后续维护成本就翻一倍
这个决定事后看非常正确:整个系统在新机器上的安装就是"建 venv → pip install → 双击启动"。
前端:Vue 3 + Vite + Pinia + ECharts,自绘 CSS
没套 Element Plus / Quasar 这类 UI 库,原因有两个:一是页面一共就七个(登录/数据集/数据集详情/标注/训练/模型库/试模型),自己写卡片和按钮的 CSS 量比引入 UI 库再定制样式的量还小;二是想要浅色极简的风格,UI 库的默认主题改起来反而费劲。ECharts 只用来做训练实时曲线。
训练:subprocess 调 yolo CLI,而不是在进程内 import
这是选型里最重要的一个技术决定。训练不在 FastAPI 进程内跑,而是 spawn 一个独立的 yolo CLI 子进程:
- 崩溃隔离:训练 OOM、CUDA 报错、超时,死的只是子进程,Web 服务毫发无损
- 停止简单:psutil 杀掉整棵进程树就行,不用管 PyTorch 内部的线程清理
- 日志天然解耦:子进程输出写日志文件,接口按 offset 增量读,服务重启日志不丢
- 断点续训白送:ultralytics 原生支持从 last.pt 恢复,接上就行
代价是要自己解析子进程输出(进度条用 \r 刷新、results.csv 列名随版本变化等),这些坑在第 1 篇里讲过了。
认证:JWT + 两级角色,不做细粒度权限
python-jose 签 JWT,passlib/bcrypt 存密码哈希,角色只有 admin / member 两级。明确不做:按数据集授权的细粒度权限、审计日志、强制改密、SSO。内部小团队工具,这些全是过度设计------PLAN.md 里专门有一节"明确不做",和需求清单一样重要。
三个关键设计决策
1. 训练必须引用数据集版本,而不是"当前数据"
标注是会持续修改的。如果训练直接读当前数据,那"这个模型到底是哪版标注训出来的"永远说不清------三周后有人改了 200 张图的标注,你对比两个模型的指标时将无从下手。
所以:数据集可以随时"保存版本"(快照当时的全部标注和类别表),发起训练时下拉框里只能选版本。训练读的是快照内容,之后怎么改标注都不影响已发起的任务,旧版本还能回滚查看、对比、恢复。
这一条让系统从"标注工具"升级成了"可复现的训练平台",成本只是多一张版本表。
2. 类别跟着数据集走,且顺序永不可变
标注里存的是 class id,id 的含义由类别表顺序决定。所以类别表一旦确定只能追加不能重排,标注页新增类别也是追加到尾部。多数据集合并训练时,先校验类别名一致性,合并后统一重写所有标签的 class id。(这条的教训意义第 1 篇详细写过。)
3. 训练参数只暴露四个
模型规格(n/s/m)、epochs、batch、imgsz------就这四个,其余全部用 ultralytics 默认值。不是偷懒,是刻意的:内部工具的用户大部分不是算法工程师,参数越多,乱调的可能性越大,出了问题越难排查。真有特殊需求,改代码里的默认值比加 UI 控件便宜。
部署形态:Windows 优先,装给非技术同事用
主环境是公司的 Windows 机器,Ubuntu 只是备选。为了这一条做了一串配套决定:
- 后端全部 pathlib,禁手拼路径、禁
os.system,从第一天就杜绝 POSIX-only 调用 - Windows 下训练进程是 spawn 模式,杀进程用 psutil 杀整棵树(
terminate在 Windows 上杀不干净) - zip 解压做 cp437→GBK 兜底(同事在 Windows 上打的包里全是中文文件名)
- 所有程序生成的路径保持纯 ASCII,避开中文用户名/中文路径的深坑
start.bat第一行chcp 65001,控制台中文不乱码
最后还写了一个图形安装向导(installer.py):自动检测 Python、磁盘、显卡(按 CUDA 版本自动选 GPU 版 torch),选完安装目录自动复制程序、建虚拟环境、装依赖、建桌面快捷方式。双击桌面图标启动服务、自动开浏览器,关窗即停服务。不会敲命令的同事也能自己装、自己用------这才是内部工具的真正验收标准。
明确不做的(和需求清单同等重要)
写进 PLAN.md 的"二期也不做"清单:
- Docker 化 ------ 单机工具,引入 Docker 是纯负担
- nginx ------ FastAPI 自己托管静态文件够了
- 细粒度权限、审计日志、强制改密 ------ 内部小团队用不上
- 多项目工作区、数据挖掘/难例挖掘 ------ 超出现阶段的实际痛点
内部工具最大的死因不是功能少,是过度设计拖到烂尾。每一条"不做"都是在保护这个项目能被做完。
小结
整个选型可以浓缩成一句话:
在"够用"和"好维护"之间,永远选好维护。
单进程、单文件数据库、单目录数据、两级权限、四个训练参数------每个决定单独看都"不够专业",合在一起却是一个真的能落地、一个人能长期维护的系统。
下一篇讲技术含量最高的一块:《subprocess 管训练进程:日志、实时曲线、断点续训全解析》------怎么把一个命令行训练工具包装成网页上可排队、可停止、可续训、有实时曲线的训练系统。
技术栈:FastAPI · SQLite · Vue 3 · ultralytics
系列目录
- 第 1 篇《开发 YOLO 训练管理平台的 23 个实践教训》
- 第 2 篇《我把 YOLO 训练做成了网页版:需求设计与技术选型》(本篇)
有问题欢迎评论区交流。