Commit Graph
7 Commits
Author SHA1 Message Date
Shaun ZhouandClaude Opus 5 ddd77653b5 fix(detect): 推理尺寸与训练尺寸不一致时报警
用 640 整图训练的权重跑 160 切片推理,结果看起来完全正常——照常出框、照常有置信度,
但类别系统性地错:实测 mosquito 的 TP 归零,760 张里 599 张被判成 blackdot,
而同一权重整图推理时 mosquito 有 92% 判对。这种错误不会自己暴露。

从 checkpoint 里读训练时的 imgsz,与当前切片尺寸不符就警告。三个调用点都接上:
可视化、批量推送预测、ML backend 加载权重时。visualize 的 summary.json 也补上
tile_size / tile_overlap,免得事后分不清某次产出是怎么跑的。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-24 15:33:41 +08:00
Shaun ZhouandClaude Opus 5 6e746b7351 feat(scripts): 重训后同时维护 current.pt 与 best.pt
current.pt 指向最新一次训练的结果(后端与 push_predictions 都读它),
best.pt 指向历史上 mAP50 最高的那版,留作回退与对照。

数据集每次重训都在变,这个比较只是粗略参考而非严格的同条件对比,所以只用来
维护 best.pt 这个指针,不拦截 current.pt 的切换。

用服务器上三次真实训练验证过:首次建立、更好时更新、更差时保持不变。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 16:03:00 +08:00
Shaun ZhouandClaude Opus 5 474b7b50a0 feat(detect): 推理侧改为切片,与固定 160 输入一致
训练数据已经是固定边长的片,推理还把整图交给 ultralytics 就会被 letterbox 缩放,
与模型见过的形态不符。三个调用方(可视化、批量推送预测、ML backend)统一改成
切片推理再拼回原图。

- engine/tiling.merge_tiles / tiled_predict:映射回原图、裁掉镜像填充区的框、跨片去重。
  推理框架留在调用方,几何部分可单测。
- engine/detection.nms:标准 NMS,另加按包含比例抑制。缺陷被切割线切开时,相邻片
  只看到一角,给出的小残框与完整框 IoU 很小(面积悬殊),光靠 IoU 压不掉。
- engine/tiled_inference:ultralytics 的接线,内部分批,避免片数多时 OOM。
- 三处调用点加 --tile-size / --tile-overlap(后端为 YOLO_TILE_SIZE / YOLO_TILE_OVERLAP),
  默认 160 / 32;设 0 退回整图推理,仅用于和旧模型对比。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 15:12:35 +08:00
Shaun ZhouandClaude Opus 5 dce2eef0a7 fix(ml-backend): 推理分批,避免 Retrieve Predictions 撑爆显存;新增批量推送脚本
Retrieve Predictions 会把选中的任务一次发给后端,而后端把整个列表交给
model.predict(source=paths)——ultralytics 不理会 batch 参数,会当成一个 batch,
于是显存爆掉。这与之前 visualize_detections 踩的是同一个坑。后端现在按
YOLO_BATCH(默认 16)自己分批。

另加 scripts/push_predictions.py:重训完在本地分批推理,再用批量导入接口
POST /api/projects/<id>/import/predictions 提交,不经过后端服务,显存与请求体都可控。
支持 --replace 先删同版本旧预测(预测是追加的,重复跑会堆两份)、--limit 与 --dry-run。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 12:11:06 +08:00
Shaun ZhouandClaude Opus 5 e61fd5887c feat(ml-backend): Start Training 按钮真正触发重训,启动/重训脚本进 scripts/
原先点 Label Studio 的 Start Training 只是把计数加一:START_TRAINING 和普通标注事件
走同一条阈值判断,没配 RETRAIN_COMMAND 时更是静默什么都不做。实际使用中用户点了按钮、
以为在训练,其实权重一直没变。

- Start Training 无条件触发重训;攒够 RETRAIN_AFTER 条新标注也自动触发。
- 只数 ANNOTATION_CREATED:UPDATED / DELETED 是对已有标注的修改,不该把阈值顶到。
- 已有重训在跑时重复点击被忽略,不叠加启动。
- 没配 RETRAIN_COMMAND 时打警告说明不会真的训练,并在返回值里说清楚。
- 子进程输出重定向到 RETRAIN_LOG(默认 /tmp/retrain.log),训练进度有地方看。

scripts/start_ml_backend.sh、scripts/retrain_from_label_studio.sh 放进仓库,
可从工作区直接启动。重训末尾把 runs/detect/current.pt 软链接指向新权重,
启动脚本默认读它,换模型不必改脚本。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 11:00:01 +08:00
Shaun ZhouandClaude Opus 5 1b8c8d7cac chore: 忽略 ml_backend 的运行时缓存
cache.db 是 label-studio-ml 用来存 model_version、标注计数的本地缓存,
上一提交误把它带了进去。顺带忽略后端的独立虚拟环境 .venv-ml/。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 17:36:25 +08:00
Shaun ZhouandClaude Opus 5 a4d099e41d feat(ml-backend): 把训练好的 YOLO 接进 Label Studio 做预标注
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 <noreply@anthropic.com>
2026-09-18 17:36:12 +08:00