news 2026/10/8 2:11:53

YOLO 训练做成网页版:FastAPI + SQLite 单机部署的需求设计与技术选型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLO 训练做成网页版:FastAPI + SQLite 单机部署的需求设计与技术选型

YOLO 训练做成网页版:FastAPI + SQLite 单机部署的需求设计与技术选型(系列第 2 篇)

公司内部做工业视觉检测,数据集散落各台机器、训练要开命令行敲yolo、训完的 .pt 文件满天飞——现成的 CVAT、ClearML 这类平台要么不管训练要么部署太重。这篇文章讲我怎么用 FastAPI + SQLite + Vue 3 在一台 Windows 机器上搭出"数据 → 标注 → 训练 → 模型"全链路的网页版训练平台,重点是动手之前的需求砍法和技术选型理由。适合想在公司内网给小团队搭内部工具、不想碰 Docker 和 K8s 的个人开发者。

背景:为什么不用现成的

痛点很具体:

  • 数据集散落在各台机器的文件夹里,"这个模型用的哪版数据"没人说得清
  • 标注靠 LabelImg 单机标,多人协作靠拷文件夹
  • 训练要开命令行敲 yolo 命令,非算法同事根本没法自助
  • 训练完的 .pt 文件满天飞,哪个是最新的、指标多少,全靠文件名和记忆

先看了一圈现成方案:CVAT 和 LabelStudio 标注很强但不管训练;各种 MLOps 平台(ClearML、Weights & Biases)管训练但对标注和数据集快照的支持要么没有要么很贵;而且它们几乎都要 Docker + 数据库 + 一堆服务,给公司内部的 Windows 机器部署简直是灾难。

结论:自己写一个,把"数据 → 标注 → 训练 → 模型"整条链路塞进一个程序里。

需求:就做这七件事

动手前把需求砍到最小集合,每条都有明确的边界:

  1. 素材库:导入 YOLO/VOC 格式的 zip,自动识别格式,VOC 内部统一转 YOLO 存储
  2. 在线标注:浏览器里画框/移动/删除/改类别,支持模型预标注(带 conf 阈值滑杆)
  3. 数据集版本快照:随时"保存版本",可回滚查看;训练任务必须引用版本,保证可追溯
  4. 在线训练:选数据集版本 + 填几个参数就能发起,实时曲线,串行队列(一次只跑一个),中断可续训
  5. 模型版本管理:模型是"系列",每次训练完自动入库一个新版本(.pt + 指标 + 来源 + 操作人)
  6. 试模型:网页上传图片,选模型版本,出检测框
  7. 登录与两级权限:admin 管账号和删除,member 干其余所有事

其中第 3 条是整个系统的灵魂,后面单独讲。

技术选型:每一项都是"够用就好"

后端:FastAPI + SQLite,单进程托管一切

浏览器 ←→ FastAPI(单进程) ├── /api/* 业务接口 ├── /* 前端打包后的静态页 └── /data/* 图片等静态文件

不装数据库、不装 nginx、不要 Docker。理由:

  • 元数据量级小:50 万张图的标注元数据也就 1GB 左右,SQLite 绰绰有余(理论上限 281TB),而且单文件意味着备份 = 拷目录
  • 并发低:公司内部同时使用的人一只手数得过来,SQLite 开了 WAL(读不阻塞写)之后,写锁完全不是瓶颈
  • 部署是最大约束:目标机器是公司的 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 一个独立的yoloCLI 子进程:

  • 崩溃隔离:训练 OOM、CUDA 报错、超时,死的只是子进程,Web 服务毫发无损
  • 停止简单:psutil 杀掉整棵进程树就行,不用管 PyTorch 内部的线程清理
  • 日志天然解耦:子进程输出写日志文件,接口按 offset 增量读,服务重启日志不丢
  • 断点续训白送:ultralytics 原生支持从 last.pt 恢复,接上就行

核心代码剥掉项目里的数据库和队列逻辑后就这么点,可以直接抄:

importosimportsubprocessimportpsutildefstart_train(data_yaml,weights,epochs=100,batch=16,imgsz=640):cmd=["yolo","detect","train",f"data={data_yaml}",f"model={weights}",f"epochs={epochs}",f"batch={batch}",f"imgsz={imgsz}"]returnsubprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True,encoding="utf-8",# 子进程输出含中文类名,默认 GBK 解码会崩errors="replace",bufsize=1,env={**os.environ,"PYTHONIOENCODING":"utf-8"},)defstop_train(pid):proc=psutil.Process(pid)procs=proc.children(recursive=True)+[proc]forpinprocs:p.terminate()_,alive=psutil.wait_procs(procs,timeout=1)forpinalive:# Windows 上 terminate 杀不干净,要补 killp.kill()

代价是要自己解析子进程输出(进度条用\r刷新、results.csv 列名随版本变化等),怎么解析、怎么画实时曲线、怎么做断点续训,是系列第 3 篇的全部内容。

认证: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),选完安装目录自动复制程序、建虚拟环境、装依赖、建桌面快捷方式。双击桌面图标启动服务、自动开浏览器,关窗即停服务。不会敲命令的同事也能自己装、自己用——这才是内部工具的真正验收标准。

落地效果:一组真实数字

不吹"效果显著",就摆数字(均为 2026 年 10 月从仓库统计):

  • 代码量:后端 Python 约 7800 行,前端 Vue/JS 约 9600 行——一个人业余时间能维护的上限差不多就这规模
  • 页面数:7 个页面,覆盖了上面全部 7 条需求,没有一个是"先放着以后再做"的半成品
  • 测试:13 个 pytest 用例,只覆盖认证、数据集、训练三条核心路径。测试数量偏少,这是事实,原因是业余项目优先保功能落地;好在核心路径有兜底,改代码时心里有底
  • 训练参数:用户能碰的只有 4 个,默认值来自 ultralytics
  • 部署:新机器安装 = 图形向导点几下,不用敲一行命令

这套选型什么时候不适用

选型没有银弹,这套方案在以下场景会直接失效,提前说清楚:

  • 多人同时训练:串行队列一次只跑一个任务。要并行训练得引入任务队列(Celery/Dramatiq)+ 多 worker,SQLite 也得换成 PostgreSQL,整套"单进程单文件"的简洁性就没了
  • 多机多卡调度、分布式训练:subprocess 只能管本机进程,跨机器调度去看 ClearML、Kubeflow 这类正经 MLOps 平台
  • 公网部署、陌生人注册:两级角色 + 无审计日志在内部小团队够用,放到公网上从合规到安全都不够
  • 几十人并发使用:WAL 也救不了高频并发写,而且单进程托管静态图片文件会先成为瓶颈(元数据本身反而不是问题)
  • 团队已有专职运维和容器基建:那"免运维"这个最大卖点对你没有价值,直接上标准技术栈更划算

一句话:这套选型的价值密度全部集中在"一台 Windows 机器、几个人用、维护者不是运维"这个场景里,出了这个圈,请重新选型。

明确不做的(和需求清单同等重要)

写进 PLAN.md 的"二期也不做"清单:

  • Docker 化 —— 单机工具,引入 Docker 是纯负担
  • nginx —— FastAPI 自己托管静态文件够了
  • 细粒度权限、审计日志、强制改密 —— 内部小团队用不上
  • 多项目工作区、数据挖掘/难例挖掘 —— 超出现阶段的实际痛点

内部工具最大的死因不是功能少,是过度设计拖到烂尾。每一条"不做"都是在保护这个项目能被做完。

小结

  • 内部工具选型,在"够用"和"好维护"之间,永远选好维护。
  • FastAPI 单进程 + SQLite 单文件,对"几个人用、一台机器"的系统不是妥协而是最优解——备份 = 拷目录。
  • 训练必须引用数据集版本快照,这一条把标注工具升级成可复现的训练平台,成本只有一张版本表。
  • 给非算法用户用的工具,暴露的参数越少越好;改默认值永远比加 UI 控件便宜。
  • "明确不做"清单和需求清单一样重要,每一条"不做"都是在保护项目能被做完。

FAQ

Q1:SQLite 多人同时用不会锁库吗?
开 WAL 模式(一行PRAGMA journal_mode=WAL),读不阻塞写。这个系统写操作本来就少(保存标注、任务入库),最重的写——训练进度——又被串行队列天然串行化了。真正扛不住的是几十人并发,那是这套选型明确放弃的场景。

Q2:为什么不用 Docker 部署?
目标机器是公司的 Windows 台式机,维护者不是运维。Windows 上跑 Docker 要先过 Docker Desktop + WSL2 这道坎,光装环境就能劝退一半人,和"双击即用"的目标直接冲突。有容器基建的团队本来也不在这篇文章的适用场景里。

Q3:subprocess 调 yolo CLI,网页上的实时曲线数据从哪来?
不解析终端进度条,读文件:训练时 ultralytics 会在 runs 目录持续写results.csv,后端轮询这个文件算进度、喂给 ECharts 画曲线。完整实现(含\r进度条、断点续训)在系列第 3 篇。

Q4:只有一个用户,也要做 JWT 登录吗?
多人共用一台机器就要,操作人字段后面排查问题全靠它。真·单用户可以把登录中间件摘掉,但建议留着——内部工具的"只有我一个人用"通常是暂时的。

Q5:训练参数只暴露四个,同事要调别的怎么办?
改代码里的默认值,重新发布。一次改动几行;加一个 UI 参数要考虑校验、提示、持久化、文档,成本高一个量级,还增加被乱调的风险。这个权衡在内部工具里几乎总是改代码赢。


技术栈:FastAPI · SQLite · Vue 3 · ultralytics · PyInstaller

系列导航

  • 系列第 1 篇:开发总览——23 个实践教训
  • 系列第 2 篇:需求设计与技术选型(本篇)
  • 系列第 3 篇:subprocess 训练进程管理
  • 系列第 4 篇:标注数据一致性的 4 个设计
  • 系列第 5 篇:Windows 双击即用与 PyInstaller 打包
  • 系列第 6 篇:业余时间做内部工具不烂尾的心得

有问题欢迎评论区交流。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 2:11:53

Python 工具找不到配置文件时先核对工作目录

大家好,我是 SiKi老师。给游戏项目写一个小工具,明明配置就在脚本旁边,从另一个目录启动却读不到,这是为什么?相对路径的起点通常是进程当前工作目录,而不是脚本所在目录。先把两者打印出来,再决…

作者头像 李华
网站建设 2026/10/8 2:11:34

道影子猜想:上古人类肉身拓扑沉降演化思辨

摘要 后世学界普遍将上古“伏羲女娲人首蛇身”的记载归为远古神话艺术想象。本猜想提出:上古神话并非杜撰,而是上古人类真实生命体态与生理演化的原始纪实。千万年来,人类生命存在持续性向下拓扑沉降的维度降级现象,肉身由高维螺…

作者头像 李华
网站建设 2026/10/8 2:11:15

第 1 篇:嵌入式简历的“量产级“写法——从“我做过“到“我解决过“

先看两份简历 这两份简历,写的是同一个项目。 简历 A 会被扔进"待定"堆。简历 B 会被约面。 差别不在项目本身,在写法。 一、面试官看简历的 30 秒 面试官的扫描路径 我作为面试官看简历时,30 秒内的扫描路径是这样的: 第 1-5 秒:基本信息。 学历、年限、…

作者头像 李华
网站建设 2026/10/8 2:10:59

SpringBoot医院预约挂号系统开发实战:架构设计、并发防超卖与部署

1. 医院预约痛点与这套SpringBoot系统实际要解决的问题先说个我当年的感受:为什么计算机毕业设计题目里,医疗类项目永远是大热门?因为围绕它是能"稀里哗啦"列出一堆需求的——预约挂号、医生排班、电子病历、药品管理、收费结算、统…

作者头像 李华
网站建设 2026/10/8 2:09:17

在通信与控制层面,VM 和 VP 的定位差异体现得最为彻底,VM 的目标是“让 PLC 工程师自己就能搞定视觉通信”,VP 的目标是“给系统集成商最大的控制权”

在通信与控制层面,VM 和 VP 的定位差异体现得最为彻底。VM 的目标是“让 PLC 工程师自己就能搞定视觉通信”,VP 的目标是“给系统集成商最大的控制权”。 VM:面向 PLC 工程师的“开箱即用”通信 VM 把通信做成了图形化配置,不需要…

作者头像 李华