From 6e746b7351c6759a38542d1f5cb8eb3bbc83bcc8 Mon Sep 17 00:00:00 2001 From: Shaun Zhou Date: Wed, 23 Sep 2026 16:03:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(scripts):=20=E9=87=8D=E8=AE=AD=E5=90=8E?= =?UTF-8?q?=E5=90=8C=E6=97=B6=E7=BB=B4=E6=8A=A4=20current.pt=20=E4=B8=8E?= =?UTF-8?q?=20best.pt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit current.pt 指向最新一次训练的结果(后端与 push_predictions 都读它), best.pt 指向历史上 mAP50 最高的那版,留作回退与对照。 数据集每次重训都在变,这个比较只是粗略参考而非严格的同条件对比,所以只用来 维护 best.pt 这个指针,不拦截 current.pt 的切换。 用服务器上三次真实训练验证过:首次建立、更好时更新、更差时保持不变。 Co-Authored-By: Claude Opus 5 --- ml_backend/README.md | 9 ++++++--- scripts/retrain_from_label_studio.sh | 26 +++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/ml_backend/README.md b/ml_backend/README.md index 6cb7965..6c27778 100644 --- a/ml_backend/README.md +++ b/ml_backend/README.md @@ -83,9 +83,12 @@ Label Studio 里:Settings → **Model** → Connect Model,URL 填 `http://lo `ANNOTATION_CREATED`,`UPDATED` / `DELETED` 是对已有标注的修改,不该把阈值顶到; - 已有重训在跑时重复点击会被忽略,不会叠起多个训练。 -重训脚本做四件事:导出标注 → `build_yolo_dataset.py` 装配 → `detect.py` 训练 → -把 `runs/detect/current.pt` 软链接指向新权重并重启本服务。启动脚本默认读这个软链接, -所以换模型不用改任何脚本。**权重只在进程启动时加载一次**,这也是重训末尾要重启服务的原因。 +重训脚本做五件事:导出标注 → `build_yolo_dataset.py` 装配 → `tile_dataset.py` 切成固定 +边长的片 → `detect.py` 训练 → 更新软链接并重启本服务。 + +两个软链接:**`current.pt`** 指向最新一次训练的结果(本服务与 `push_predictions.py` 都读它), +**`best.pt`** 指向历史上 mAP50 最高的那版,留作回退与对照。换模型不用改任何脚本。 +**权重只在进程启动时加载一次**,这也是重训末尾要重启服务的原因。 训练进度看 `$RETRAIN_LOG`(默认 `/tmp/retrain.log`);Label Studio 网页端不显示训练进度。 diff --git a/scripts/retrain_from_label_studio.sh b/scripts/retrain_from_label_studio.sh index 6fd810b..a759ff7 100755 --- a/scripts/retrain_from_label_studio.sh +++ b/scripts/retrain_from_label_studio.sh @@ -71,9 +71,31 @@ echo "--- 4/5 训练 -> $WORK_DIR" --set data.yaml="$TRAIN_DATA" $IMGSZ_SET $EXTRA_SET echo "--- 5/5 切换权重" -# 指向最新权重的稳定软链接:启动脚本与 push_predictions 都读它,换模型不用改任何脚本 +# 两个稳定软链接,换模型不用改任何脚本: +# current.pt —— 最新一次训练的结果,后端与 push_predictions 都读它 +# best.pt —— 历史上 mAP50 最高的那版,留作回退与对照 ln -sfn "$WORK_DIR/weights/best.pt" "$REPO/runs/detect/current.pt" +map50_of() { # $1 = 指向某次训练 weights/best.pt 的路径 + local metrics + metrics="$(dirname "$(dirname "$1")")/metrics.json" + [ -f "$metrics" ] || return 1 + "$PYTHON" -c "import json,sys; print(json.load(open(sys.argv[1]))[\"map50\"])" "$metrics" +} + +NEW_MAP=$(map50_of "$WORK_DIR/weights/best.pt" || echo "") +OLD_BEST=$(readlink -f "$REPO/runs/detect/best.pt" 2>/dev/null || echo "") +OLD_MAP=$([ -n "$OLD_BEST" ] && map50_of "$OLD_BEST" || echo "") + +# 数据集每次重训都在变,这个比较只是粗略参考,不是严格的同条件对比 +if [ -z "$OLD_MAP" ] || [ -z "$NEW_MAP" ] || \ + awk "BEGIN{exit !($NEW_MAP >= $OLD_MAP)}"; then + ln -sfn "$WORK_DIR/weights/best.pt" "$REPO/runs/detect/best.pt" + echo " best.pt 更新为本次(mAP50 ${NEW_MAP:-未知},此前 ${OLD_MAP:-无})" +else + echo " best.pt 保持不变(本次 mAP50 $NEW_MAP < 此前 $OLD_MAP)" +fi + if [ "$RESTART_BACKEND" = "1" ]; then echo "--- 重启 ML backend" # 必须显式传 YOLO_WEIGHTS 并清掉继承来的变量:本脚本可能是旧后端的子进程, @@ -90,6 +112,8 @@ fi echo "===== [$(date '+%F %T')] 重训完成:$WORK_DIR =====" echo "指标见 $WORK_DIR/metrics.json,曲线见 $WORK_DIR/results.png" +echo "current.pt -> $(readlink "$REPO/runs/detect/current.pt")" +echo "best.pt -> $(readlink "$REPO/runs/detect/best.pt")" if [ "$TILE_SIZE" -gt 0 ]; then echo "推理请用同样的尺寸:--tile-size $TILE_SIZE --tile-overlap $TILE_OVERLAP" fi