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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
23562448ba
commit
fad1fe9fbb
@@ -4,7 +4,11 @@
|
|||||||
# bash scripts/retrain_from_label_studio.sh
|
# bash scripts/retrain_from_label_studio.sh
|
||||||
#
|
#
|
||||||
# 也由 ML backend 在点击 Start Training 时自动调用(输出见 $RETRAIN_LOG)。
|
# 也由 ML backend 在点击 Start Training 时自动调用(输出见 $RETRAIN_LOG)。
|
||||||
# 四步:导出标注 -> 装配数据集 -> 训练 -> 切换权重并重启后端。
|
# 五步:导出标注 -> 装配数据集 -> 切成固定边长的片 -> 训练 -> 切换权重并重启后端。
|
||||||
|
#
|
||||||
|
# 产线要求模型输入固定为 TILE_SIZE×TILE_SIZE 且禁止 resize,所以训练数据必须先切片;
|
||||||
|
# 推理侧(可视化、批量推送、ML backend)也要用同一个尺寸,否则输入分布对不上。
|
||||||
|
# TILE_SIZE=0 可跳过切片,训练未切片的数据集(只用于和旧模型对比)。
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
REPO=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
REPO=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
@@ -15,8 +19,15 @@ IMAGES="${LS_IMAGES_DIR:-$REPO/runs/boxes_shap_label_studio/images}"
|
|||||||
STAMP=$(date +%Y%m%d_%H%M)
|
STAMP=$(date +%Y%m%d_%H%M)
|
||||||
EXPORT="${LS_EXPORT_DIR:-/tmp}/ls_export_${STAMP}.zip"
|
EXPORT="${LS_EXPORT_DIR:-/tmp}/ls_export_${STAMP}.zip"
|
||||||
DATASET="$REPO/data/film_yolo_${STAMP}"
|
DATASET="$REPO/data/film_yolo_${STAMP}"
|
||||||
|
TILED="$REPO/data/film_yolo_${STAMP}_tiled"
|
||||||
WORK_DIR="$REPO/runs/detect/film_${STAMP}"
|
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}"
|
PYTHON="${REPO_PYTHON:-$REPO/.venv/bin/python}"
|
||||||
# 用跑着 Label Studio 的那个环境导出,确保读的是同一个库
|
# 用跑着 Label Studio 的那个环境导出,确保读的是同一个库
|
||||||
LS_BIN="${LS_BIN:-/home/deep/.v/label/bin/label-studio}"
|
LS_BIN="${LS_BIN:-/home/deep/.v/label/bin/label-studio}"
|
||||||
@@ -32,20 +43,34 @@ RESTART_BACKEND="${RESTART_BACKEND:-1}"
|
|||||||
|
|
||||||
echo "===== [$(date '+%F %T')] 开始重训(项目 $PROJECT)====="
|
echo "===== [$(date '+%F %T')] 开始重训(项目 $PROJECT)====="
|
||||||
|
|
||||||
echo "--- 1/4 导出标注 -> $EXPORT"
|
echo "--- 1/5 导出标注 -> $EXPORT"
|
||||||
"$LS_BIN" export "$PROJECT" YOLO --export-path="$EXPORT"
|
"$LS_BIN" export "$PROJECT" YOLO --export-path="$EXPORT"
|
||||||
|
|
||||||
echo "--- 2/4 装配数据集 -> $DATASET"
|
echo "--- 2/5 装配数据集 -> $DATASET"
|
||||||
"$PYTHON" scripts/build_yolo_dataset.py \
|
"$PYTHON" scripts/build_yolo_dataset.py \
|
||||||
--export "$EXPORT" --images "$IMAGES" --out "$DATASET" --overwrite
|
--export "$EXPORT" --images "$IMAGES" --out "$DATASET" --overwrite
|
||||||
|
|
||||||
echo "--- 3/4 训练 -> $WORK_DIR"
|
if [ "$TILE_SIZE" -gt 0 ]; then
|
||||||
# shellcheck disable=SC2086 # EXTRA_SET 需要按空格拆成多个参数
|
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 \
|
"$PYTHON" detect.py --config configs/detect/film.yaml \
|
||||||
--work-dir "$WORK_DIR" \
|
--work-dir "$WORK_DIR" \
|
||||||
--set data.yaml="$DATASET/data.yaml" $EXTRA_SET
|
--set data.yaml="$TRAIN_DATA" $IMGSZ_SET $EXTRA_SET
|
||||||
|
|
||||||
echo "--- 4/4 切换权重"
|
echo "--- 5/5 切换权重"
|
||||||
# 指向最新权重的稳定软链接:启动脚本与 push_predictions 都读它,换模型不用改任何脚本
|
# 指向最新权重的稳定软链接:启动脚本与 push_predictions 都读它,换模型不用改任何脚本
|
||||||
ln -sfn "$WORK_DIR/weights/best.pt" "$REPO/runs/detect/current.pt"
|
ln -sfn "$WORK_DIR/weights/best.pt" "$REPO/runs/detect/current.pt"
|
||||||
|
|
||||||
@@ -54,7 +79,10 @@ if [ "$RESTART_BACKEND" = "1" ]; then
|
|||||||
# 必须显式传 YOLO_WEIGHTS 并清掉继承来的变量:本脚本可能是旧后端的子进程,
|
# 必须显式传 YOLO_WEIGHTS 并清掉继承来的变量:本脚本可能是旧后端的子进程,
|
||||||
# 它环境里已经 export 了 YOLO_WEIGHTS(旧权重)和可能的 RETRAIN_EXTRA_SET(冒烟参数),
|
# 它环境里已经 export 了 YOLO_WEIGHTS(旧权重)和可能的 RETRAIN_EXTRA_SET(冒烟参数),
|
||||||
# 不清掉的话新后端会继续用旧模型、下次训练也会沿用冒烟参数
|
# 不清掉的话新后端会继续用旧模型、下次训练也会沿用冒烟参数
|
||||||
|
# 后端的切片尺寸必须与训练一致,否则输入分布对不上(实测:640 训练的模型
|
||||||
|
# 在 160 的片上一个框都出不来)
|
||||||
env -u RETRAIN_EXTRA_SET YOLO_WEIGHTS="$REPO/runs/detect/current.pt" \
|
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"
|
bash "$REPO/scripts/start_ml_backend.sh"
|
||||||
else
|
else
|
||||||
echo "--- 跳过重启 ML backend(RESTART_BACKEND=0)"
|
echo "--- 跳过重启 ML backend(RESTART_BACKEND=0)"
|
||||||
@@ -62,3 +90,6 @@ fi
|
|||||||
|
|
||||||
echo "===== [$(date '+%F %T')] 重训完成:$WORK_DIR ====="
|
echo "===== [$(date '+%F %T')] 重训完成:$WORK_DIR ====="
|
||||||
echo "指标见 $WORK_DIR/metrics.json,曲线见 $WORK_DIR/results.png"
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user