VLAlert / training /SFT /train_sft_x.sh
AsianPlayer's picture
Add VLAlert code
1e05592 verified
Raw
History Blame Contribute Delete
4.34 kB
#!/bin/bash
# VLAlert-X Phase 2.1 — SFT Qwen3-VL-4B on merged GPT-5 distilled CoT corpus.
#
# Reuses the existing per-frame CoT-belief trainer (training/VLA/train_cot_belief.py)
# but with:
# - LoRA r=64 (was 32) for richer adaptation under the 1338-clip extension
# - Lower LR (5e-5) since we're warm-starting from a checkpoint that has
# already been SFT'd on the 2k-clip corpus
# - Source manifest: data/cot_corpus_v2/vlalert_x_sft.jsonl (1338 records)
# - Optional union with the existing perframe corpus
#
# Preconditions:
# - data/cot_corpus_v2/vlalert_x_sft.jsonl (run merge_gpt5_into_cot_manifest.py)
# - checkpoints/VLA/qwen3vl4b_cot_belief_perframe/best/ (warm-start)
# - models/Qwen3-VL-4B-Instruct/ (base VLM)
#
# Usage:
# bash training/SFT/train_sft_x.sh # full run (~3 GPU-hr)
# bash training/SFT/train_sft_x.sh --debug # 16-clip smoke
# bash training/SFT/train_sft_x.sh --no_union # GPT-5 corpus only
set -euo pipefail
cd "$(dirname "$0")/../.."
ROOT=$(pwd)
MODEL_PATH="${MODEL_PATH:-$ROOT/models/Qwen3-VL-4B-Instruct}"
GPT5_JSONL="${GPT5_JSONL:-$ROOT/data/cot_corpus_v2/vlalert_x_sft.jsonl}"
LEGACY_JSONL="${LEGACY_JSONL:-$ROOT/data/vla_cot_belief/train_perframe_union.jsonl}"
UNION_JSONL="${UNION_JSONL:-$ROOT/data/cot_corpus_v2/vlalert_x_train_union.jsonl}"
# Inference path for cot_belief_dataset; the dataset itself reads
# `video_path` from each record so VIDEO_DIR is a fallback only.
VIDEO_DIR="${VIDEO_DIR:-$ROOT/nexar-collision-prediction/train}"
RESUME="${RESUME:-$ROOT/checkpoints/VLA/qwen3vl4b_cot_belief_perframe/best}"
OUT_DIR="${OUT_DIR:-$ROOT/checkpoints/sft_x}"
EPOCHS="${EPOCHS:-2}" # warm-start: 2 epoch enough on 1338 clips
BATCH_SIZE="${BATCH_SIZE:-1}"
GRAD_ACCUM="${GRAD_ACCUM:-8}"
LR="${LR:-5e-5}" # lower than 1e-4 (warm-start, smaller corpus)
N_FRAMES="${N_FRAMES:-8}"
LORA_R="${LORA_R:-64}" # ↑ from 32 to 64 (VLAlert-X plan)
MAX_LEN="${MAX_LEN:-4096}"
RESIZE_SHORT="${RESIZE_SHORT:-336}"
# ── flags ──
USE_UNION=1
DEBUG_ARGS=""
for arg in "$@"; do
case "$arg" in
--no_union) USE_UNION=0 ;;
--debug) DEBUG_ARGS="--max_samples 16 --epochs 1 --log_every 1" ;;
esac
done
# ── build training manifest ──
mkdir -p "$(dirname "$UNION_JSONL")"
n_gpt5=$(wc -l < "$GPT5_JSONL")
if [[ $USE_UNION -eq 1 && -f "$LEGACY_JSONL" ]]; then
cat "$GPT5_JSONL" "$LEGACY_JSONL" > "$UNION_JSONL"
n_legacy=$(wc -l < "$LEGACY_JSONL")
n_total=$(wc -l < "$UNION_JSONL")
echo "[union] gpt5=$n_gpt5 legacy=$n_legacy total=$n_total$UNION_JSONL"
else
cp "$GPT5_JSONL" "$UNION_JSONL"
n_total=$(wc -l < "$UNION_JSONL")
echo "[union] gpt5-only=$n_gpt5$UNION_JSONL"
fi
# ── pre-flight ──
for f in "$MODEL_PATH" "$UNION_JSONL"; do
if [[ ! -e "$f" ]]; then
echo "[FAIL] missing: $f" >&2
exit 2
fi
done
RESUME_ARG=""
if [[ -n "$RESUME" && -e "$RESUME/adapter_config.json" ]]; then
RESUME_ARG="--resume $RESUME"
echo "[resume] warm-start from $RESUME"
else
echo "[resume] no warm-start — fresh LoRA init"
fi
mkdir -p "$OUT_DIR"
LOG_FILE="$ROOT/runs/vlalert_x/phase2_1_sft_$(date +%Y%m%d_%H%M%S).log"
mkdir -p "$(dirname "$LOG_FILE")"
echo "[config] EPOCHS=$EPOCHS BATCH=$BATCH_SIZE GRAD_ACCUM=$GRAD_ACCUM"
echo " LR=$LR LORA_R=$LORA_R N_FRAMES=$N_FRAMES"
echo " OUT_DIR=$OUT_DIR"
echo " LOG_FILE=$LOG_FILE"
# IMPORTANT: route through the fast wrapper that applies the
# Conv3d→Linear patch (PR/qwen3vl_patch_embed_conv3d_slowdown.md).
# Skipping this gives ~17s/it (not the ~0.3s/it expected on RTX 5090).
python -m tools.run_train_cot_belief_fast \
--model_name "$MODEL_PATH" \
--cot_jsonl "$UNION_JSONL" \
--video_dir "$VIDEO_DIR" \
--out_dir "$OUT_DIR" \
--epochs "$EPOCHS" \
--batch_size "$BATCH_SIZE" \
--grad_accum "$GRAD_ACCUM" \
--lr "$LR" \
--n_frames "$N_FRAMES" \
--lora_r "$LORA_R" \
--max_len "$MAX_LEN" \
--resize_short "$RESIZE_SHORT" \
--per_frame \
$RESUME_ARG \
--save_every_epoch \
$DEBUG_ARGS \
2>&1 | tee "$LOG_FILE"
echo
echo "[done] checkpoint: $OUT_DIR/best/"
echo "[next] bash scripts/run_vlalert_x_pipeline.sh phase2_2_full"