AsianPlayer/TriDrive / code /aide /train /run_kv2_program.sh
AsianPlayer's picture
download
raw
14.4 kB
#!/usr/bin/env bash
# K-v2 programme (docs/decisions.md 2026-09-14 "K-v2 programme — S0 pre-registration", aide/docs/protocol.md C23) — detached lane runner.
#
# setsid nohup bash aide/train/run_kv2_program.sh <A|B|C|D> > aide/results/kv2/lane_<X>.log 2>&1 &
#
# Lane A S1 val screen: 9 K-v2 configs at seed 42 (cwm3d_v2[_<cfg>]_s42_v2) -> `kv2_gate.py s1` (needs aide/models/cwm3d_v2.py + its tests green; exit 4 if not)
# Lane B S3 (i): 9 MotionBERT-kp grid points at seed 42 -> `kv2_gate.py s3` -> MB-best at seeds 123/456/789/1011 if != default -> MB-best + trunk-only init
# at 5 seeds (existing motionbert_kp_batonpre_pretrain_s123 checkpoint; a d/L change needs a new pretraining = follow-up, exit 5)
# Lane C motionbert_kp_batonpre_trunkp123_s{789,1011}_v2 and cwm3d_kp_batonpre_trunkp123_s{789,1011}_v2 (recipes / inits of the existing s42/123/456 runs verbatim)
# Lane D S1b (seed 123) per S1 survivor -> `kv2_gate.py s1b` -> per continuing survivor: architecture-matched trunk-only pretraining
# (cwm3d_v2[_nospatial]_batonpre_pretrain_s123, survivor flags, --zeroshot 0; verified by pretrain-check) -> seeds 456/789/1011 and +pre at 5 seeds
# Every python launch passes the project GPU gate first (re-checked every 30 s): free VRAM >= need + 4 GB, MemAvailable >= 28 GB, fewer than 4 trainer
# processes (`pgrep -fc "python.*(train_cwm3|train_kp|pretrain)"`), OMP/MKL threads 4. One trainer per lane. Existing runs are never touched: a run whose
# run json exists is skipped; new run dirs only. PIDs -> aide/results/kv2/pids.txt. Selection is val-only inside train_kp / the pretraining script; the
# official test split is evaluated once per run by the existing harness. Config flags / run names come from aide/eval/kv2_gate.py (single source of truth).
set -u
LANE=${1:?lane A|B|C|D}
ROOT=/home/USER/cwm3
# Run from a per-launch snapshot so that editing this file while a lane is alive can never change the running lane (bash reads scripts
# incrementally; lane B of 2026-09-14 hit `flags: unbound variable` after its last run when the lane-D block was edited underneath it).
if [ -z "${KV2_SNAPSHOT:-}" ]; then
mkdir -p "$ROOT/aide/results/kv2/launcher_snapshots"
SNAP="$ROOT/aide/results/kv2/launcher_snapshots/run_kv2_program_${LANE}_$(date -u +%Y%m%dT%H%M%SZ).sh"
cp "${BASH_SOURCE[0]}" "$SNAP" && KV2_SNAPSHOT=$SNAP exec bash "$SNAP" "$@"
fi
PY=python
RUNS=$ROOT/aide/results/runs
KV2=$ROOT/aide/results/kv2
PIDS=$KV2/pids.txt
GATE=aide/eval/kv2_gate.py
GATE_OUT=${GATE_OUT:-$KV2} # decision json dir (tests / dry runs point it elsewhere)
DRY_RUN=${DRY_RUN:-0} # 1: print every assembled command instead of launching
K_CKPT=aide/results/runs/cwm3d_kp_batonpre_pretrain_s123/ckpt_best.pt
MB_CKPT=aide/results/runs/motionbert_kp_batonpre_pretrain_s123/ckpt_best.pt
export OMP_NUM_THREADS=4 MKL_NUM_THREADS=4 OPENBLAS_NUM_THREADS=4 NUMEXPR_NUM_THREADS=4
mkdir -p "$KV2"
cd "$ROOT" || exit 1
# Stage-A recipe verbatim (aide/train/run_arch_attribution.sh:24 without --loss euc = K's loss defaults euc + 0.1 NLL + 0.05 bone + 0.01 jerk);
# tensor-output models (MotionBERT-kp) add --loss euc exactly as motionbert_kp_s*_v2.
RECIPE_K="--lr 3e-4 --wd 0.05 --warmup 10 --epochs 200 --bs 32 --ema 0.999 --clip 1.0 --patience 40 --aug-window 1 --noise-px 2.0 --joint-drop 0.1 --translate 20 --scale 0.05 --flip-p 0"
RECIPE="$RECIPE_K --loss euc"
COMMON="--no-lock --max-minutes 45"
KV2_CFGS="full nospatial khead nohaz hazaux plain q060 q090 lam03"
MB_CFGS="d192 L4 lr1e-4 lr1e-3 wd0.01 wd0.1 do0.1 d192_L4 d192_L4_do0.1"
SEEDS5="42 123 456 789 1011"
log() { echo "$(date -u +%FT%TZ) [lane $LANE] $*"; }
gate() { # gate <need_gb>: block until the project launch gate is satisfied
local need=$1 free_mib avail_kb ntr ok
while :; do
ok=1
free_mib=$(nvidia-smi --query-gpu=memory.free --format=csv,noheader,nounits | head -1 | tr -d ' ')
avail_kb=$(awk '/MemAvailable/{print $2}' /proc/meminfo)
ntr=$(pgrep -fc "python.*(train_cwm3|train_kp|pretrain)")
[ "${free_mib:-0}" -lt $(( (need + 4) * 1024 )) ] && ok=0
[ "${avail_kb:-0}" -lt $(( 28 * 1024 * 1024 )) ] && ok=0
[ "${ntr:-9}" -ge 4 ] && ok=0
if [ "$ok" = 1 ]; then
log "gate OK: free VRAM ${free_mib} MiB (need $((need + 4)) GB), MemAvailable $((avail_kb / 1048576)) GB (>= 28), trainers ${ntr} (< 4)"
return 0
fi
log "gate WAIT: free VRAM ${free_mib} MiB (need $((need + 4)) GB), MemAvailable $((avail_kb / 1048576)) GB, trainers ${ntr}"
sleep 30
done
}
run() { # run <name> <need_gb> <cmd...>: gate, launch, record pid, wait
local name=$1 need=$2; shift 2
if ls "$RUNS/$name"/run_"$name"_2*.json >/dev/null 2>&1; then log "skip $name (run json exists; existing runs are never re-run)"; return 0; fi
if [ "$DRY_RUN" = 1 ]; then log "DRY $name (need ${need} GB): $*"; return 0; fi
gate "$need"
"$@" > "$RUNS/${name}_train.log" 2>&1 &
local pid=$!
echo "$(date -u +%FT%TZ) lane=$LANE name=$name pid=$pid START" >> "$PIDS"
log "launched $name pid $pid"
wait "$pid"; local rc=$?
echo "$(date -u +%FT%TZ) lane=$LANE name=$name pid=$pid END rc=$rc" >> "$PIDS"
log "finished $name rc=$rc"
return $rc
}
refs_for() { # <seed>: evaluation-only paired references (simlpe exists for 42/123/456 only; 789/1011 use the s42 run as lane C of arch_attribution did)
local s=$1 simlpe=simlpe_kp_s42_v2
case $s in 42|123|456) simlpe=simlpe_kp_s${s}_v2 ;; esac
echo "zvshrink=zv_shrink_val,simlpe=$simlpe,kp=cwm3d_kp_s${s}_v2,mb=motionbert_kp_s${s}_v2"
}
read_dec() { "$PY" $GATE read "$1" "$2" --out "$GATE_OUT"; } # <json-stem> <key>
v2_ready() { # lanes A / D: model file + its tests + trainer flags must exist (exit 4 otherwise)
[ -f aide/models/cwm3d_v2.py ] || { log "NOT READY: aide/models/cwm3d_v2.py missing"; return 1; }
[ -f aide/tests/test_cwm3d_v2.py ] || { log "NOT READY: aide/tests/test_cwm3d_v2.py missing"; return 1; }
"$PY" -m pytest -q aide/tests/test_cwm3d_v2.py > "$KV2/pytest_cwm3d_v2_lane${LANE}.log" 2>&1 || { log "NOT READY: pytest aide/tests/test_cwm3d_v2.py failed (see $KV2/pytest_cwm3d_v2_lane${LANE}.log)"; return 1; }
local help; help=$("$PY" aide/train/train_kp.py --help 2>&1)
local fl; for fl in --no-v2-spatial --no-lean-head --no-hazard --haz-mod --haz-quantile --haz-weight --no-physics --w-nll; do
echo "$help" | grep -q -- "$fl" || { log "NOT READY: train_kp.py lacks flag $fl"; return 1; }
done
log "v2 ready: model file, tests and trainer flags present"
return 0
}
run_kv2() { # <cfg> <seed> <pre:0|1> <stage-tag>
local cfg=$1 s=$2 pre=$3 stage=$4 name flags refs init=""
if [ "$pre" = 1 ]; then name=$("$PY" $GATE name kv2 "$cfg" "$s" pre); else name=$("$PY" $GATE name kv2 "$cfg" "$s"); fi
flags=$("$PY" $GATE flags kv2 "$cfg")
refs=$(refs_for "$s")
if [ "$pre" = 1 ]; then
[ -n "${V2_CKPT:-}" ] || { log "ABORT: V2_CKPT unset for $name"; return 1; }
init="--init-ckpt $V2_CKPT --init-parts trunk"
refs="$refs,seedinit=$("$PY" $GATE name kv2 "$cfg" "$s")"
fi
# shellcheck disable=SC2086
run "$name" 3 "$PY" aide/train/train_kp.py $flags --seed "$s" --name "$name" $RECIPE_K $init --refs "$refs" $COMMON \
--notes "kv2_program;${stage};cfg=${cfg}${init:+;init=$(basename "$(dirname "$V2_CKPT")"):trunk}"
}
run_mb() { # <cfg> <seed> <pre:0|1> <stage-tag>
local cfg=$1 s=$2 pre=$3 stage=$4 name flags refs init=""
if [ "$pre" = 1 ]; then name=$("$PY" $GATE name mb "$cfg" "$s" pre); else name=$("$PY" $GATE name mb "$cfg" "$s"); fi
flags=$("$PY" $GATE flags mb "$cfg")
refs=$(refs_for "$s")
if [ "$pre" = 1 ]; then
[ -n "${MB_PRE_CKPT:-}" ] || { log "ABORT: MB_PRE_CKPT unset for $name"; return 1; }
init="--init-ckpt $MB_PRE_CKPT --init-parts trunk"
refs="$refs,seedinit=$("$PY" $GATE name mb "$cfg" "$s")"
fi
# shellcheck disable=SC2086
run "$name" 3 "$PY" aide/train/train_kp.py $flags --seed "$s" --name "$name" $RECIPE $init --refs "$refs" $COMMON \
--notes "kv2_program;${stage};cfg=${cfg}${init:+;init=$(basename "$(dirname "$MB_PRE_CKPT")"):trunk}"
}
case "$LANE" in
A)
v2_ready || exit 4
for cfg in $KV2_CFGS; do run_kv2 "$cfg" 42 0 "S1" || log "WARN: $cfg seed 42 failed (rc $?), continuing the grid"; done
"$PY" $GATE s1 --out "$GATE_OUT"; rc=$?
log "S1 gate rc=$rc survivors='$(read_dec s1_decision survivors)' complete=$(read_dec s1_decision complete)"
[ "$rc" = 0 ] || { log "S1 incomplete — inspect pending runs, then re-run lane A (finished runs are skipped)"; exit 3; }
;;
B)
for cfg in $MB_CFGS; do run_mb "$cfg" 42 0 "S3i" || log "WARN: MB $cfg seed 42 failed (rc $?), continuing the grid"; done
"$PY" $GATE s3 --out "$GATE_OUT"; rc=$?
best=$(read_dec s3_decision mb_best)
log "S3 gate rc=$rc mb_best='$best' changes_dL=$(read_dec s3_decision mb_best_changes_dL) complete=$(read_dec s3_decision complete)"
[ "$rc" = 0 ] && [ -n "$best" ] || { log "S3 incomplete — re-run lane B after the pending grid runs finish"; exit 3; }
if [ "$best" = default ]; then
log "MB-best = default: 5-seed and +pre arms are the existing runs + lane C (motionbert_kp_batonpre_trunkp123_s{789,1011}_v2); nothing more to run"
else
for s in 123 456 789 1011; do run_mb "$best" "$s" 0 "S3i:mb_best_5seed" || log "WARN: MB-best $best seed $s failed"; done
if [ "$(read_dec s3_decision mb_best_changes_dL)" = 1 ]; then
pre=$(read_dec s3_decision mb_best_pretrain_run); model=$(read_dec s3_decision mb_best_model)
log "MB-best changes d/L -> new pretraining $pre (needs pretrain_cwm3d_baton.py --model $model; FOLLOW-UP if the script rejects the name)"
run "$pre" 10 "$PY" aide/train/pretrain_cwm3d_baton.py --model "$model" --seed 123 --zeroshot 0 ; rc=$?
if [ "$rc" != 0 ]; then log "FOLLOW-UP: pretraining $pre failed rc=$rc (pretrain_cwm3d_baton.py must accept --model $model); S3 (ii) for MB-best not run"; exit 5; fi
[ -f "$RUNS/$pre/ckpt_best.pt" ] && [ -f "$RUNS/$pre/TRAIN_DONE" ] || { log "ABORT: $pre has no ckpt_best.pt / TRAIN_DONE"; exit 5; }
MB_PRE_CKPT="aide/results/runs/$pre/ckpt_best.pt"
else
MB_PRE_CKPT=$MB_CKPT
fi
export MB_PRE_CKPT
for s in $SEEDS5; do run_mb "$best" "$s" 1 "S3ii:mb_best_pre_5seed" || log "WARN: MB-best+pre $best seed $s failed"; done
fi
;;
C)
export MB_PRE_CKPT=$MB_CKPT
for s in 789 1011; do run_mb default "$s" 1 "S3ii:mb_pre_5seed_completion" || log "WARN: MB+pre seed $s failed"; done
for s in 789 1011; do
name="cwm3d_kp_batonpre_trunkp123_s${s}_v2"
run "$name" 3 "$PY" aide/train/train_kp.py --model cwm3d --seed "$s" --name "$name" $RECIPE_K \
--init-ckpt "$K_CKPT" --init-parts trunk --refs "$(refs_for "$s"),seedinit=cwm3d_kp_s${s}_v2" $COMMON \
--notes "kv2_program;S2:k_pre_5seed_completion;cfg=k;init=cwm3d_kp_batonpre_pretrain_s123:trunk" || log "WARN: K+pre seed $s failed"
done
;;
D)
v2_ready || exit 4
[ "$(read_dec s1_decision complete)" = 1 ] || { log "NOT READY: s1_decision.json missing or incomplete (run lane A first)"; exit 4; }
survivors=$(read_dec s1_decision survivors)
[ -n "$survivors" ] && [ "$survivors" != "None" ] || { log "S1: no survivors — programme ends at S1 (report via report_kv2_program.py)"; exit 0; }
log "S1 survivors: $survivors"
# (1) S1b FIRST (coordinator 2026-09-14): seed 123 for every survivor, then the S1b gate
for cfg in $survivors; do run_kv2 "$cfg" 123 0 "S1b" || log "WARN: $cfg seed 123 failed"; done
cont=""
for cfg in $survivors; do
"$PY" $GATE s1b "$cfg" --out "$GATE_OUT"; rc=$?
dec=$(read_dec "s1b_${cfg}_decision" decision); log "S1b $cfg rc=$rc -> $dec: $(read_dec "s1b_${cfg}_decision" reason)"
[ "$dec" = CONTINUE ] && cont="$cont $cfg"
done
[ -n "$cont" ] || { log "S1b: no survivor CONTINUEs (decisions above: KILLED = val-killed at seed 123, PENDING = seed-123 run missing) — S2 not run"; exit 0; }
# (2) per continuing survivor: trunk-only pretraining PER ARCHITECTURE with the survivor's flags (nospatial -> cwm3d_v2_nospatial_batonpre_pretrain_s123,
# full trunk -> cwm3d_v2_batonpre_pretrain_s123), --zeroshot 0; K's checkpoint only if it loads the whole trunk; the own checkpoint must load
# every trunk tensor (0 shape-skipped / 0 missing) before any +pre fine-tune; then seeds 456/789/1011 (no pre) and 5 seeds +pre
for cfg in $cont; do
pre=$("$PY" $GATE name kv2pre "$cfg")
"$PY" $GATE pretrain-check "$cfg" --out "$GATE_OUT" || { log "pretrain-check not ready for $cfg"; exit 4; }
if [ "$(read_dec "pretrain_check_${cfg}" decision)" = REUSE_K_CKPT ]; then
V2_CKPT=$K_CKPT; log "$cfg: K's checkpoint loads the whole trunk -> reused"
else
flags=$("$PY" $GATE flags kv2 "$cfg")
# shellcheck disable=SC2086
run "$pre" 10 "$PY" aide/train/pretrain_cwm3d_baton.py $flags --seed 123 --name "$pre" --zeroshot 0 ; rc=$?
[ "$rc" = 0 ] || { log "FOLLOW-UP: pretraining $pre failed rc=$rc"; exit 5; }
[ -f "$RUNS/$pre/ckpt_best.pt" ] && [ -f "$RUNS/$pre/TRAIN_DONE" ] || { log "ABORT: $pre has no ckpt_best.pt / TRAIN_DONE"; exit 5; }
V2_CKPT="aide/results/runs/$pre/ckpt_best.pt"
"$PY" $GATE pretrain-check "$cfg" --ckpt "$V2_CKPT" --stem "pretrain_check_${cfg}_own" --out "$GATE_OUT" || { log "pretrain-check ($V2_CKPT) not ready"; exit 4; }
[ "$(read_dec "pretrain_check_${cfg}_own" trunk_complete)" = 1 ] || { log "ABORT: $V2_CKPT does not load every trunk tensor of $cfg (shape-skipped $(read_dec "pretrain_check_${cfg}_own" n_skipped_shape), missing $(read_dec "pretrain_check_${cfg}_own" n_missing_trunk))"; exit 5; }
log "$cfg: own pretraining $pre verified (loaded $(read_dec "pretrain_check_${cfg}_own" n_loaded) / $(read_dec "pretrain_check_${cfg}_own" n_trunk_tensors) trunk tensors, 0 skipped, 0 missing)"
fi
export V2_CKPT; log "$cfg: trunk-only init checkpoint $V2_CKPT"
for s in 456 789 1011; do run_kv2 "$cfg" "$s" 0 "S2" || log "WARN: $cfg seed $s failed"; done
for s in $SEEDS5; do run_kv2 "$cfg" "$s" 1 "S2:pre" || log "WARN: $cfg+pre seed $s failed"; done
done
;;
*) echo "unknown lane $LANE"; exit 2 ;;
esac
log "lane $LANE done"
[ "$DRY_RUN" = 1 ] || echo "$(date -u +%FT%TZ) lane=$LANE LANE_DONE" >> "$PIDS"

Xet Storage Details

Size:
14.4 kB
·
Xet hash:
e1e6fb481961c734918459ffa95caacf17aa4cd554bb001115c08e27ed2ba06f

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.