From a4d099e41dbf5e5aaa2a60868aa821eb3b8ad1d7 Mon Sep 17 00:00:00 2001 From: Shaun Zhou Date: Fri, 18 Sep 2026 17:36:12 +0800 Subject: [PATCH] =?UTF-8?q?feat(ml-backend):=20=E6=8A=8A=E8=AE=AD=E7=BB=83?= =?UTF-8?q?=E5=A5=BD=E7=9A=84=20YOLO=20=E6=8E=A5=E8=BF=9B=20Label=20Studio?= =?UTF-8?q?=20=E5=81=9A=E9=A2=84=E6=A0=87=E6=B3=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ml_backend/ 是一个 label-studio-ml 后端:Label Studio 打开任务时调 /predict, 后端跑 YOLO 并把框转成 Label Studio 的百分比坐标返回,人工只需修正。 - model.py:FilmDefectDetector。权重懒加载且只加载一次(Flask 多线程,加锁); 控件名从项目的 labeling config 读取,不写死;model_version 带权重的修改时间, 换权重后 Label Studio 能区分新旧预测。 - convert.py:坐标转换,刻意不 import label_studio_ml,好在主环境里单测。 - fit():每条标注累加计数,达到 RETRAIN_AFTER 就起子进程执行 RETRAIN_COMMAND 并立刻返回——训练几十分钟,同步做会卡住 Label Studio。 - _wsgi.py 由官方模板生成,ruff 跳过,便于跟随上游。 必须用独立虚拟环境:label-studio-ml 会把 jinja2 降到 3.0.3,而 label-studio 1.23 需要 3.1,装进主环境会直接弄坏 Label Studio。PyPI 上的 1.0.9 是 2023 年的旧版, 要装 GitHub 上的现行版本。 本地用 COCO 权重端到端验证过:/health、/predict(bus.jpg 出 10 个框、坐标换算正确)、 /webhook(第 3 条标注触发重训命令并清零计数)。 Co-Authored-By: Claude Opus 5 --- ml_backend/README.md | 94 +++++++++++++++++++++++ ml_backend/_wsgi.py | 125 +++++++++++++++++++++++++++++++ ml_backend/cache.db | Bin 0 -> 12288 bytes ml_backend/convert.py | 73 ++++++++++++++++++ ml_backend/model.py | 124 ++++++++++++++++++++++++++++++ pyproject.toml | 2 + tests/test_ml_backend_convert.py | 71 ++++++++++++++++++ 7 files changed, 489 insertions(+) create mode 100644 ml_backend/README.md create mode 100644 ml_backend/_wsgi.py create mode 100644 ml_backend/cache.db create mode 100644 ml_backend/convert.py create mode 100644 ml_backend/model.py create mode 100644 tests/test_ml_backend_convert.py diff --git a/ml_backend/README.md b/ml_backend/README.md new file mode 100644 index 0000000..6bd7b5e --- /dev/null +++ b/ml_backend/README.md @@ -0,0 +1,94 @@ +# Label Studio ML Backend:用训练好的 YOLO 做预标注 + +把 `runs/detect//weights/best.pt` 接进 Label Studio,打开任务时自动给出框, +人工只需修正而不是从零画。标注攒够数量还能自动触发重训,形成"边标边训"的闭环。 + +## 为什么要单独的虚拟环境 + +`label-studio-ml` 会把 jinja2 降到 3.0.3、带进 Flask 1.1.2,而 label-studio 1.23 需要 +jinja2 3.1。装进主环境会直接弄坏 Label Studio。它本来就是个独立服务,单独建环境最省事。 + +另外 **PyPI 上的 `label-studio-ml` 是 2023 年的 1.0.9,不要用**,要装 GitHub 上的现行版本 +(本文档基于 2.0.1.dev0 验证)。 + +```bash +uv venv .venv-ml --python 3.12 +VIRTUAL_ENV=.venv-ml uv pip install \ + "label-studio-ml @ git+https://github.com/HumanSignal/label-studio-ml-backend.git" \ + ultralytics +``` + +## 启动 + +```bash +cd ml_backend +YOLO_WEIGHTS=$PWD/../runs/detect/film/weights/best.pt \ +YOLO_DEVICE=cuda \ +LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT=/home/deep/labeling \ +../.venv-ml/bin/python _wsgi.py -p 9090 +``` + +**用 `python _wsgi.py` 而不是 `label-studio-ml start .`**:后者用 PATH 上的 python 起子进程, +在独立环境里会 `ModuleNotFoundError`。 + +`LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT` 必须和启动 Label Studio 时的值一致——任务里的图片是 +`/data/local-files/?d=...`,后端要靠它把 URL 还原成磁盘路径。图片若来自上传或云存储, +则改为设置 `LABEL_STUDIO_URL` 与 `LABEL_STUDIO_API_KEY`,后端会自行下载。 + +健康检查: + +```bash +curl http://localhost:9090/health +# {"model_class":"FilmDefectDetector","status":"UP"} +``` + +## 接入项目 + +Label Studio 里:Settings → **Model** → Connect Model,URL 填 `http://localhost:9090`。 + +- 勾选 **Interactive preannotations** 后,打开任务就会实时调用模型; +- 也可以在 Data Manager 里选中若干任务 → Actions → **Retrieve Predictions** 批量拉取。 + +**类别名必须和 labeling config 里的 `