Files
ModelTrainEval/scripts/retrain_from_label_studio.sh
T
Shaun ZhouandClaude Opus 5 fad1fe9fbb feat(scripts): 重训流程加入切片步骤
产线要求模型输入固定为 TILE_SIZE×TILE_SIZE 且禁止 resize,训练数据必须先切片,
否则训出来的模型与推理侧的输入形态对不上——实测 640 训练的模型在 160 的片上
一个框都出不来。

五步:导出 -> 装配 -> 切片 -> 训练(imgsz 跟着 TILE_SIZE)-> 切权重并重启后端。
重启时把 YOLO_TILE_SIZE / YOLO_TILE_OVERLAP 一并传给后端,保证两侧尺寸一致。
TILE_SIZE=0 可跳过切片,只用于和旧模型对比。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 15:22:58 +08:00

96 lines
4.4 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 从 Label Studio 的最新标注重新训练检测模型,并让 ML backend 用上新权重。
#
# bash scripts/retrain_from_label_studio.sh
#
# 也由 ML backend 在点击 Start Training 时自动调用(输出见 $RETRAIN_LOG)。
# 五步:导出标注 -> 装配数据集 -> 切成固定边长的片 -> 训练 -> 切换权重并重启后端。
#
# 产线要求模型输入固定为 TILE_SIZE×TILE_SIZE 且禁止 resize,所以训练数据必须先切片;
# 推理侧(可视化、批量推送、ML backend)也要用同一个尺寸,否则输入分布对不上。
# TILE_SIZE=0 可跳过切片,训练未切片的数据集(只用于和旧模型对比)。
set -euo pipefail
REPO=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
cd "$REPO"
PROJECT="${LS_PROJECT_ID:-39}"
IMAGES="${LS_IMAGES_DIR:-$REPO/runs/boxes_shap_label_studio/images}"
STAMP=$(date +%Y%m%d_%H%M)
EXPORT="${LS_EXPORT_DIR:-/tmp}/ls_export_${STAMP}.zip"
DATASET="$REPO/data/film_yolo_${STAMP}"
TILED="$REPO/data/film_yolo_${STAMP}_tiled"
WORK_DIR="$REPO/runs/detect/film_${STAMP}"
# 切片参数。改尺寸时这里与 configs/detect/film.yaml 的 imgsz 会一起生效(见第 4 步)
TILE_SIZE="${TILE_SIZE:-160}"
TILE_OVERLAP="${TILE_OVERLAP:-32}"
# >0.5 保证一个框最多出现在一片里,不会在相邻片各留一个残框
MIN_VISIBILITY="${MIN_VISIBILITY:-0.6}"
PYTHON="${REPO_PYTHON:-$REPO/.venv/bin/python}"
# 用跑着 Label Studio 的那个环境导出,确保读的是同一个库
LS_BIN="${LS_BIN:-/home/deep/.v/label/bin/label-studio}"
[ -x "$LS_BIN" ] || LS_BIN="$REPO/.venv/bin/label-studio"
export LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT="${LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT:-/home/deep/labeling}"
# 训练参数的额外覆盖,冒烟测试时用,如 RETRAIN_EXTRA_SET="train.epochs=1 train.fraction=0.02"
EXTRA_SET="${RETRAIN_EXTRA_SET:-}"
# 训完要不要重启 ML backend。被后端调用时必须重启(权重只在启动时加载一次);
# 纯手动路线不跑后端的话设 0,免得凭空起一个常驻进程占显存
RESTART_BACKEND="${RESTART_BACKEND:-1}"
echo "===== [$(date '+%F %T')] 开始重训(项目 $PROJECT)====="
echo "--- 1/5 导出标注 -> $EXPORT"
"$LS_BIN" export "$PROJECT" YOLO --export-path="$EXPORT"
echo "--- 2/5 装配数据集 -> $DATASET"
"$PYTHON" scripts/build_yolo_dataset.py \
--export "$EXPORT" --images "$IMAGES" --out "$DATASET" --overwrite
if [ "$TILE_SIZE" -gt 0 ]; then
echo "--- 3/5 切成 ${TILE_SIZE}x${TILE_SIZE} 的片 -> $TILED"
"$PYTHON" scripts/tile_dataset.py \
--data "$DATASET/data.yaml" --out "$TILED" \
--size "$TILE_SIZE" --overlap "$TILE_OVERLAP" \
--min-visibility "$MIN_VISIBILITY" --overwrite
TRAIN_DATA="$TILED/data.yaml"
IMGSZ_SET="train.imgsz=$TILE_SIZE"
else
echo "--- 3/5 跳过切片(TILE_SIZE=0),直接用未切片的数据集"
TRAIN_DATA="$DATASET/data.yaml"
IMGSZ_SET=""
fi
echo "--- 4/5 训练 -> $WORK_DIR"
# shellcheck disable=SC2086 # EXTRA_SET / IMGSZ_SET 需要按空格拆成多个参数
"$PYTHON" detect.py --config configs/detect/film.yaml \
--work-dir "$WORK_DIR" \
--set data.yaml="$TRAIN_DATA" $IMGSZ_SET $EXTRA_SET
echo "--- 5/5 切换权重"
# 指向最新权重的稳定软链接:启动脚本与 push_predictions 都读它,换模型不用改任何脚本
ln -sfn "$WORK_DIR/weights/best.pt" "$REPO/runs/detect/current.pt"
if [ "$RESTART_BACKEND" = "1" ]; then
echo "--- 重启 ML backend"
# 必须显式传 YOLO_WEIGHTS 并清掉继承来的变量:本脚本可能是旧后端的子进程,
# 它环境里已经 export 了 YOLO_WEIGHTS(旧权重)和可能的 RETRAIN_EXTRA_SET(冒烟参数),
# 不清掉的话新后端会继续用旧模型、下次训练也会沿用冒烟参数
# 后端的切片尺寸必须与训练一致,否则输入分布对不上(实测:640 训练的模型
# 在 160 的片上一个框都出不来)
env -u RETRAIN_EXTRA_SET YOLO_WEIGHTS="$REPO/runs/detect/current.pt" \
YOLO_TILE_SIZE="$TILE_SIZE" YOLO_TILE_OVERLAP="$TILE_OVERLAP" \
bash "$REPO/scripts/start_ml_backend.sh"
else
echo "--- 跳过重启 ML backend(RESTART_BACKEND=0)"
fi
echo "===== [$(date '+%F %T')] 重训完成:$WORK_DIR ====="
echo "指标见 $WORK_DIR/metrics.json,曲线见 $WORK_DIR/results.png"
if [ "$TILE_SIZE" -gt 0 ]; then
echo "推理请用同样的尺寸:--tile-size $TILE_SIZE --tile-overlap $TILE_OVERLAP"
fi