diff --git a/lab_lib.sh b/lab_lib.sh index 6e1e082..33ebd5c 100644 --- a/lab_lib.sh +++ b/lab_lib.sh @@ -277,8 +277,12 @@ lab_cmd_addon() { # 3f. 何度流しても同じ結果 (install_addon. # corr2 (依頼 57) は corr_v1_1499 から trim2_corr_v4 で続きを学習したもの (設定 pi05_mecharm_trim_corr_v4_lora) # corr3 (依頼 97) は corr_v2_1499 から trim2_corr_v5 で続きを学習したもの (設定 pi05_mecharm_trim_corr_v5_lora) # deep1 (依頼 133) は π0.5 base から deep_even10_v1 (位置 1〜5 を各 10 本) で新しく学習したもの (設定 pi05_mecharm_deep_even10_lora. -# 中身は trim と同じ LoRA・batch 32・学習率の予定. 正規化の値はこの 50 本で測り直したもの) -LAB_MODELS="trim corr corr2 corr3 deep1" +# 中身は trim と同じ LoRA・batch 32・学習率の予定 (decay_steps 5_000 のまま 3000 ステップで終えた). 正規化の値はこの 50 本で測り直したもの) +# deep1b (依頼 135) は deep1 と同じデータ・同じ値で, decay_steps だけを num_train_steps と同じにして π0.5 base から学習するもの +# (設定 pi05_mecharm_deep_even10_b_lora). 依頼 135 から deep_even10_v1 を新しく学習するのはこちら (deep1 は学習し直さない) +# 安全範囲 (学習データ ±10°. run_policy_client.py --raw-dir) は datasets//raw_episodes. deep1・deep1b は +# deep_even10_v1, ほかは trim2 (依頼 135) +LAB_MODELS="trim corr corr2 corr3 deep1 deep1b" LAB_CORR_STEPS=1500 LAB_CORR_INIT=/workspace/ckpt/3999/params LAB_CORR2_STEPS=1500 @@ -286,7 +290,9 @@ LAB_CORR2_INIT=/workspace/ckpt/corr_v1_1499/params LAB_CORR3_STEPS=1500 LAB_CORR3_INIT=/workspace/ckpt/corr_v2_1499/params LAB_DEEP1_STEPS=3000 # 依頼 133. add_deep_config.py の --steps (推論では使わないが, 学習と同じ値で入れる) -lab_model_def() { # lab_model_def trim|corr → MODEL_REPO MODEL_CKPT MODEL_CONFIG MODEL_DATA MODEL_DESC. 知らない名前なら 1 +LAB_DEEP1B_STEPS=3000 # 依頼 135. deep1b の学習の既定のステップ数 (lab_train.sh) と, 推論で入れる設定の --steps +lab_model_def() { # lab_model_def trim|corr|... → MODEL_REPO MODEL_CKPT MODEL_CONFIG MODEL_DATA MODEL_DESC MODEL_RANGE_DATA. 知らない名前なら 1 + MODEL_RANGE_DATA=mecharm_redblock_box_teach_v1_trim2 # 安全範囲を作るデータ (依頼 135. deep1・deep1b だけ下で変える) case "$1" in trim) MODEL_REPO=PixelstoMotors/pi05-mecharm-trim-lora-v1 @@ -317,18 +323,27 @@ lab_model_def() { # lab_model_def trim|corr → MODEL_REPO MODEL_CKPT MODEL_CKPT=/workspace/ckpt/deep_v1_2999 MODEL_CONFIG=pi05_mecharm_deep_even10_lora MODEL_DATA=mecharm_redblock_box_deep_even10_v1 # 依頼 131: 位置 1〜5 を各 10 本 (50 本) - MODEL_DESC="π0.5 base から deep_even10_v1 で新しく学習した 2999 (依頼 133)" ;; + MODEL_RANGE_DATA=$MODEL_DATA # 依頼 135: 安全範囲はこの 50 本から + MODEL_DESC="π0.5 base から deep_even10_v1 で新しく学習した 2999 (依頼 133. decay_steps 5000)" ;; + deep1b) + MODEL_REPO=PixelstoMotors/pi05-mecharm-deep-even10-b-lora-v1 + MODEL_CKPT=/workspace/ckpt/deep_b_v1_$((LAB_DEEP1B_STEPS - 1)) + MODEL_CONFIG=pi05_mecharm_deep_even10_b_lora + MODEL_DATA=mecharm_redblock_box_deep_even10_v1 + MODEL_RANGE_DATA=$MODEL_DATA + MODEL_DESC="π0.5 base から deep_even10_v1 で decay_steps をステップ数と同じにして学習した $((LAB_DEEP1B_STEPS - 1)) (依頼 135)" ;; *) return 1 ;; esac } -lab_cmd_addon_model() { # lab_cmd_addon_model trim|corr|corr2|corr3|deep1 → 3f (trim は lab_cmd_addon と同じ. corr〜deep1 は学習の設定と assets も足す) +lab_cmd_addon_model() { # lab_cmd_addon_model trim|corr|corr2|corr3|deep1|deep1b → 3f (trim は lab_cmd_addon と同じ. corr〜deep1b は学習の設定と assets も足す) lab_model_def "$1" || return 1 if [ "$1" = trim ]; then lab_cmd_addon; return; fi local add="add_trim_corr_config.py /workspace/openpi --steps $LAB_CORR_STEPS --init $LAB_CORR_INIT" [ "$1" = corr2 ] && add="add_trim_corr_config.py /workspace/openpi --variant v4 --steps $LAB_CORR2_STEPS --init $LAB_CORR2_INIT" [ "$1" = corr3 ] && add="add_trim_corr_config.py /workspace/openpi --variant v5 --steps $LAB_CORR3_STEPS --init $LAB_CORR3_INIT" - [ "$1" = deep1 ] && add="add_deep_config.py /workspace/openpi --steps $LAB_DEEP1_STEPS" # 依頼 133 + [ "$1" = deep1 ] && add="add_deep_config.py /workspace/openpi --steps $LAB_DEEP1_STEPS --variant v1" # 依頼 133 (decay_steps 5000) + [ "$1" = deep1b ] && add="add_deep_config.py /workspace/openpi --steps $LAB_DEEP1B_STEPS --variant b" # 依頼 135 # assets は hf download が置いたモデルの assets/local/<データ名> を, openpi の assets/<設定>/local/ に写す # (trim の 3f が assets/pi05_mecharm_trim_lora/local/ に写すのと同じ形. corr〜corr3 は trim2 と同じ正規化の値, # deep1 は deep_even10_v1 で測り直した値) @@ -337,10 +352,14 @@ lab_cmd_addon_model() { # lab_cmd_addon_model trim|corr|corr2|corr3|deep1 # ---- lab_train.sh で作るモデル (依頼 57. --model corr | corr2. 依頼 97 で corr3. 依頼 133 で deep1) ---- # 作るモデルの名前は lab_start.sh の --model と同じ. 続きの元 (TRAIN_FROM) も lab_model_def のモデル -# deep1 は続きの元がない (π0.5 base から. TRAIN_FROM・TRAIN_REPO_IN・TRAIN_CKPT_IN・TRAIN_FROM_KIOXIA は空, TRAIN_INIT=base) -LAB_TRAIN_MODELS="corr corr2 corr3 deep1" -lab_train_def() { # lab_train_def corr|corr2|corr3|deep1 → TRAIN_* (知らない名前なら 1) +# deep1・deep1b は続きの元がない (π0.5 base から. TRAIN_FROM・TRAIN_REPO_IN・TRAIN_CKPT_IN・TRAIN_FROM_KIOXIA は空, TRAIN_INIT=base) +# TRAIN_DECAY: 学習率を下げきるステップ (decay_steps). 「steps」は --steps と同じ (依頼 135). 由来に残すのは deep1・deep1b だけ +# TRAIN_RETIRED: 1 なら新しくは学習しない (--stop で片付けるときだけ使う). deep1 は依頼 133 の 2999 (decay_steps 5000) を +# 作った形なので, 依頼 135 からは deep1b で学習する +LAB_TRAIN_MODELS="corr corr2 corr3 deep1 deep1b" +lab_train_def() { # lab_train_def corr|corr2|corr3|deep1|deep1b → TRAIN_* (知らない名前なら 1) TRAIN_STEPS=1500; TRAIN_PREP=pod_prep_trim_corr.sh # ステップ数の既定・4b で流す準備 (依頼 133 で deep1 だけ違う) + TRAIN_DECAY=steps; TRAIN_RETIRED=0 # corr〜corr3 は add_trim_corr_config.py の 1_500 (= 既定のステップ数) case "$1" in corr) # 依頼 49: 3999 から trim2_corr_v3 で続きを学習した 1499 TRAIN_FROM=trim; TRAIN_VARIANT=v3 @@ -351,18 +370,23 @@ lab_train_def() { # lab_train_def corr|corr2|corr3|deep1 → TRAIN_* ( corr3) # 依頼 97: corr2 の 1499 から trim2_corr_v5 で続きを学習する TRAIN_FROM=corr2; TRAIN_VARIANT=v5 TRAIN_FROM_KIOXIA=pi05_mecharm_trim_corr_lora_v2/1499 ;; - deep1) # 依頼 133: π0.5 base から deep_even10_v1 で新しく学習する (正規化の値は Pod で測り直す) - TRAIN_FROM=""; TRAIN_VARIANT=deep + deep1) # 依頼 133: π0.5 base から deep_even10_v1 で新しく学習した (正規化の値は Pod で測り直す) + TRAIN_FROM=""; TRAIN_VARIANT=v1; TRAIN_EXP_DEEP=deep_even10_v1 TRAIN_FROM_KIOXIA="" - TRAIN_STEPS=3000; TRAIN_PREP=pod_prep_deep.sh ;; + TRAIN_STEPS=3000; TRAIN_PREP=pod_prep_deep.sh + TRAIN_DECAY=5000; TRAIN_RETIRED=1 ;; # 依頼 135: 新しくは学習しない (deep1b で学習する) + deep1b) # 依頼 135: deep1 と同じデータ・値で, decay_steps を --steps と同じにして学習する + TRAIN_FROM=""; TRAIN_VARIANT=b; TRAIN_EXP_DEEP=deep_even10_v1_b + TRAIN_FROM_KIOXIA="" + TRAIN_STEPS=$LAB_DEEP1B_STEPS; TRAIN_PREP=pod_prep_deep.sh ;; *) return 1 ;; esac lab_model_def "$1" || return 1 TRAIN_DATA=$MODEL_DATA; TRAIN_CONFIG=$MODEL_CONFIG; TRAIN_REPO_OUT=$MODEL_REPO TRAIN_CKPT_NAME=${MODEL_REPO#*/pi05-}; TRAIN_CKPT_NAME=pi05_${TRAIN_CKPT_NAME//-/_} # 例 pi05_mecharm_trim_corr_lora_v2 TRAIN_EXP=trim_corr_${TRAIN_VARIANT} - if [ -z "$TRAIN_FROM" ]; then # deep1: 続きの元を取らない - TRAIN_EXP=deep_even10_v1 + if [ -z "$TRAIN_FROM" ]; then # deep1・deep1b: 続きの元を取らない + TRAIN_EXP=$TRAIN_EXP_DEEP TRAIN_REPO_IN=""; TRAIN_CKPT_IN=""; TRAIN_INIT=base return 0 fi @@ -371,8 +395,8 @@ lab_train_def() { # lab_train_def corr|corr2|corr3|deep1 → TRAIN_* ( lab_model_def "$1" # MODEL_* は作るモデルのものに戻す } -lab_cmd_addon_train() { # lab_cmd_addon_train corr|corr2|corr3|deep1 → lab_train.sh の 3f - # corr は lab_cmd_addon と同じ (3999 を取っているので trim の assets も写す). corr2・corr3・deep1 は 3999 を取らないので +lab_cmd_addon_train() { # lab_cmd_addon_train corr|corr2|corr3|deep1|deep1b → lab_train.sh の 3f + # corr は lab_cmd_addon と同じ (3999 を取っているので trim の assets も写す). corr2・corr3・deep1・deep1b は 3999 を取らないので # addon と trim2 の設定だけを入れる (学習の設定と正規化の値は pod_prep_trim_corr.sh・pod_prep_deep.sh が置く) if [ "$1" = corr ]; then lab_cmd_addon; return; fi echo "cd /workspace/openpi && python3 /workspace/r2r2r/pi05/openpi_addon/install_addon.py /workspace/openpi && python3 /workspace/r2r2r/add_trim_config.py /workspace/openpi" diff --git a/lab_start.sh b/lab_start.sh index 6b33165..5fe03d2 100755 --- a/lab_start.sh +++ b/lab_start.sh @@ -18,9 +18,12 @@ # deep1 = π0.5 base から deep_even10_v1 (位置 1〜5 を各 10 本の 50 本) で新しく学習した 2999 # (PixelstoMotors/pi05-mecharm-deep-even10-lora-v1. 依頼 133. lab_train.sh --model deep1 で作る. # Pod の /workspace/ckpt/deep_v1_2999. 正規化の値はこの 50 本で測り直したもの (モデルの assets にある)) -# 取る場所・設定・assets・サーバーの起動・つなぎ直しの確かめが選んだモデルに合う. 安全範囲 (学習データ ±10°) -# と位置合わせのお手本はどれも trim2 の raw_episodes (corr_v3・corr_v4 と合わせても範囲はほぼ同じで, trim2 の方が狭い. -# corr_v5 は 11-06 の 184650 の j2 が trim2 の上限 +31.4° を最大 11° 超える) +# deep1b = deep1 と同じデータで decay_steps をステップ数と同じにして学習した 2999 (依頼 135. lab_train.sh --model deep1b. +# PixelstoMotors/pi05-mecharm-deep-even10-b-lora-v1. Pod の /workspace/ckpt/deep_b_v1_2999) +# 取る場所・設定・assets・サーバーの起動・つなぎ直しの確かめが選んだモデルに合う. 安全範囲 (学習データ ±10°. +# run_policy_client.py --raw-dir) は, deep1・deep1b は deep_even10_v1 の raw_episodes (依頼 135), ほかは trim2 の +# raw_episodes (corr_v3・corr_v4 と合わせても範囲はほぼ同じで, trim2 の方が狭い. corr_v5 は 11-06 の 184650 の j2 が +# trim2 の上限 +31.4° を最大 11° 超える). 位置合わせ (camera_align.py) のお手本はどれも trim2 の raw_episodes # つなぎ直しで, 残っている Pod のサーバーが別のモデルで動いていれば, そのことを出して止まる # # 1. Pod を作る (A40×1・Runpod Pytorch 2.8.0・コンテナのディスク 200GB・22/tcp を外へ出す) @@ -68,7 +71,7 @@ while [ $# -gt 0 ]; do --dry-run) DRY_RUN=1; shift ;; --model) [ $# -ge 2 ] || { echo "[ERROR] --model には ${LAB_MODELS// / か } を付ける" >&2; exit 2; } # 値がないと shift 2 が失敗して回り続ける (codex R1) MODEL="$2"; shift 2 ;; - -h|--help) sed -n '2,42p' "$0"; exit 0 ;; + -h|--help) sed -n '2,45p' "$0"; exit 0 ;; *) echo "[ERROR] 知らない引数: $1 (./lab_start.sh --help)" >&2; exit 2 ;; esac done @@ -105,8 +108,11 @@ lab_need_key "$HF_TOKEN_FILE" "Hugging Face の鍵" [ -s "$POD_SSH_KEY.pub" ] || { echo "[ERROR] SSH の公開鍵がありません: $POD_SSH_KEY.pub" >&2; exit 1; } ADD_TRIM=/Volumes/KIOXIA/r2r2r-data/lab/Log11/pod/add_trim_config.py [ -f "$ADD_TRIM" ] || { echo "[ERROR] add_trim_config.py がありません: $ADD_TRIM" >&2; exit 1; } -RAW=/Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes +RAW=/Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes # 位置合わせのお手本 (どのモデルも trim2) [ -d "$RAW/$EPISODE" ] || { echo "[ERROR] お手本がありません: $RAW/$EPISODE" >&2; exit 1; } +RAW_RANGE=/Volumes/KIOXIA/r2r2r-data/datasets/$MODEL_RANGE_DATA/raw_episodes # 安全範囲を作る学習データ (lab_lib.sh の lab_model_def. 依頼 135) +[ -d "$RAW_RANGE" ] || { echo "[ERROR] 安全範囲を作る学習データがありません: $RAW_RANGE" >&2; exit 1; } +echo "[RANGE ] 安全範囲 (学習データ ±10°) は $RAW_RANGE から (--model $MODEL)" lab_ssh_opts HF_REPO=$MODEL_REPO # --model で選んだモデル (lab_lib.sh の lab_model_def) @@ -445,12 +451,12 @@ while :; do [ $rc = 0 ] || [ $rc = 130 ] || fail "camera_align.py が失敗 (終了コード $rc)" stage_done stage "8. 実機推論 ($round 回目)" - python3 pi05/run_policy_client.py --raw-dir "$RAW" --steps-per-chunk 10 --via-stride 3 \ + python3 pi05/run_policy_client.py --raw-dir "$RAW_RANGE" --steps-per-chunk 10 --via-stride 3 \ --settle-retries 0 --state-from-target --execute --ignore-framing --model "$MODEL_TAG" rc=$? else stage "8. 推論のクライアント (ドライラン・--dry-keys. アームには触らない)" - python3 pi05/run_policy_client.py --raw-dir "$RAW" --steps-per-chunk 10 --via-stride 3 \ + python3 pi05/run_policy_client.py --raw-dir "$RAW_RANGE" --steps-per-chunk 10 --via-stride 3 \ --settle-retries 0 --state-from-target --dry-keys --ignore-framing --model "$MODEL_TAG" rc=$? fi diff --git a/lab_train.sh b/lab_train.sh index d09309c..d1f31ca 100755 --- a/lab_train.sh +++ b/lab_train.sh @@ -15,12 +15,15 @@ # (設定 pi05_mecharm_trim_corr_v4_lora. 3c で取るのは 3999 ではなく corr の 1499) # corr3 = corr2 の 1499 から trim2_corr_v5 で続きを学習 → PixelstoMotors/pi05-mecharm-trim-corr-lora-v3 # (依頼 97. 設定 pi05_mecharm_trim_corr_v5_lora. 3c で取るのは corr2 の 1499) -# deep1 = π0.5 base から deep_even10_v1 (位置 1〜5 を各 10 本の 50 本) で新しく学習 -# → PixelstoMotors/pi05-mecharm-deep-even10-lora-v1 (依頼 133. 設定 pi05_mecharm_deep_even10_lora. +# deep1b = π0.5 base から deep_even10_v1 (位置 1〜5 を各 10 本の 50 本) で新しく学習 +# → PixelstoMotors/pi05-mecharm-deep-even10-b-lora-v1 (依頼 135. 設定 pi05_mecharm_deep_even10_b_lora. +# 学習率の予定は trim と同じ warmup 300・peak 5e-5・decay_lr 5e-6 で, decay_steps だけ --steps と同じ. # 3c は飛ばす (続きの元を取らない). 4b は pod_prep_deep.sh で, 正規化の値を Pod で測り直し, # lab//train/norm_stats.json に持ち帰って sha256 を由来に残す) -# --steps / --init / --exp-name 学習 (既定 1500 (deep1 は 3000) / 続きの元の params (deep1 は base) / -# trim_corr_v3・trim_corr_v4・trim_corr_v5・deep_even10_v1) +# deep1 = deep1b と同じで decay_steps 5000 のもの (依頼 133 の 2999 → PixelstoMotors/pi05-mecharm-deep-even10-lora-v1. +# 設定 pi05_mecharm_deep_even10_lora). 依頼 135 から新しくは学習しない (--stop の片付けだけ) +# --steps / --init / --exp-name 学習 (既定 1500 (deep1b は 3000) / 続きの元の params (deep1b は base) / +# trim_corr_v3・trim_corr_v4・trim_corr_v5・deep_even10_v1_b) # 続きの元: corr は /workspace/ckpt/3999/params, corr2 は /workspace/ckpt/corr_v1_1499/params, # corr3 は /workspace/ckpt/corr_v2_1499/params # --poll-min 進みを見る間 (既定 5 分) @@ -70,18 +73,26 @@ while [ $# -gt 0 ]; do --stall-min) lab_need_val 30 "$@"; STALL_MIN="$2"; shift 2 ;; --stop) MODE=stop; shift ;; --dry-run) DRY_RUN=1; shift ;; - -h|--help) sed -n '2,39p' "$0"; exit 0 ;; + -h|--help) sed -n '2,45p' "$0"; exit 0 ;; *) echo "[ERROR] 知らない引数: $1 (./lab_train.sh --help)" >&2; exit 2 ;; esac done [ -n "$LOG" ] || { echo "[ERROR] --log Log17 のように Log 番号を付けてください" >&2; exit 2; } lab_train_def "$MODEL" || { echo "[ERROR] --model は ${LAB_TRAIN_MODELS// / か }: $MODEL" >&2; exit 2; } +if [ $MODE = train ] && [ "$TRAIN_RETIRED" = 1 ]; then # 依頼 135: deep1 (decay_steps 5000) は学習し直さない + echo "[ERROR] --model $MODEL は学習し終えたモデル (decay_steps $TRAIN_DECAY) で, 新しくは学習しません. deep_even10_v1 を学習するなら --model deep1b" >&2 + exit 2 +fi [ -n "$INIT" ] || INIT=$TRAIN_INIT [ -n "$STEPS" ] || STEPS=$TRAIN_STEPS # corr〜corr3 は 1500, deep1 は 3000 (依頼 133) [ -n "$EXP" ] || EXP=$TRAIN_EXP num() { awk -v x="$1" 'BEGIN { exit !(x ~ /^[0-9]+(\.[0-9]+)?$/ && x > 0) }'; } for v in LIMIT_HOURS POLL_MIN STALL_MIN; do num "${!v}" || { echo "[ERROR] $v は 0 より大きい数: ${!v}" >&2; exit 2; }; done [[ "$STEPS" =~ ^[1-9][0-9]*$ ]] || { echo "[ERROR] --steps は 1 以上の整数: $STEPS" >&2; exit 2; } +DECAY_STEPS=$TRAIN_DECAY; [ "$DECAY_STEPS" = steps ] && DECAY_STEPS=$STEPS # 学習率を下げきるステップ (依頼 135) +if [ "$TRAIN_PREP" = pod_prep_deep.sh ] && [ "$DECAY_STEPS" -le 300 ]; then # add_deep_config.py の warmup 300 より後で下げきる + echo "[ERROR] --model $MODEL の decay_steps ($DECAY_STEPS) が warmup 300 以下. --steps を 301 以上にする" >&2; exit 2 +fi [[ "$EXP" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "[ERROR] --exp-name は英数字と _ . - だけ: $EXP" >&2; exit 2; } [[ "$INIT" =~ ^[A-Za-z0-9_./-]+$ ]] || { echo "[ERROR] --init に使えない文字がある: $INIT" >&2; exit 2; } case "$GPU" in *" "*) GPU_ID="$GPU" ;; *) GPU_ID="NVIDIA $GPU" ;; esac @@ -112,7 +123,7 @@ use_model() { # use_model corr|corr2|corr3|deep1 → 作るモデ CKPT_IN=$TRAIN_CKPT_IN # 3c で続きの元を置く場所 (deep1 は空) CKPT_IN_KIOXIA="" # HF_REPO_IN と同じもの (由来の指紋を取る. 依頼 54. deep1 は空) [ -n "$TRAIN_FROM_KIOXIA" ] && CKPT_IN_KIOXIA="$CKPT_ROOT/$TRAIN_FROM_KIOXIA" - PREP_VARIANT=$TRAIN_VARIANT # pod_prep_trim_corr.sh の 3 つ目 (v3 / v4 / v5) + PREP_VARIANT=$TRAIN_VARIANT # pod_prep_trim_corr.sh の 3 つ目 (v3 / v4 / v5). pod_prep_deep.sh の 2 つ目 (v1 / b. 依頼 135) PREP_SH=$TRAIN_PREP # 4b で流す準備 (deep1 は pod_prep_deep.sh. 依頼 133) } use_model "$MODEL" @@ -216,6 +227,7 @@ prov_spec() { # 由来の spec (lab_train.sh が知っているこ --arg pod "$POD_ID" --arg gpu "$GPU_USED" --arg cph "$COST_PER_HR" --arg yen "$YEN_PER_USD" \ --arg t0 "$(iso_of "$POD_T0")" --arg t1 "$(iso_of "$t_end")" --arg tt "$(iso_of "$TRAIN_T0")" --arg rented "$rented" \ --arg cost_line "${COST_LINE:-}" --arg norm_sha "$NORM_SHA" --arg norm_pod "$NORM_POD" --arg norm_local "$NORM_LOCAL" \ + --arg deep "$([ "$PREP_SH" = pod_prep_deep.sh ] && echo 1)" --arg decay "${DECAY_STEPS:-}" --arg variant "$PREP_VARIANT" \ 'def num: if . == "" then null else tonumber end; def str: if . == "" then null else . end; {producer: {script: $script, argv: $argv, dry_run: ($dry == "1")}, external_code: $openpi, @@ -226,7 +238,10 @@ prov_spec() { # 由来の spec (lab_train.sh が知っているこ config: ({name: $config, exp_name: $exp, num_train_steps: ($steps | num), init_params: ($init | str), data_name: $data, limit_hours: ($limit | num), checkpoint_step: ($step | str)} + (if $norm_sha == "" then {} else {norm_stats: {source: "Pod で compute_norm_stats.py を回して測り直した (依頼 133)", - sha256: $norm_sha, pod_path: ("/workspace/openpi/" + $norm_pod), lab_copy: ($norm_local | str)}} end)), + sha256: $norm_sha, pod_path: ("/workspace/openpi/" + $norm_pod), lab_copy: ($norm_local | str)}} end) + # 依頼 135: deep の学習率の予定 (deep1 = decay_steps 5000 / deep1b = num_train_steps と同じ) + + (if $deep == "1" then {lr_schedule: {warmup_steps: 300, peak_lr: 5e-5, decay_steps: ($decay | num), decay_lr: 5e-6, + source: ("add_deep_config.py --variant " + $variant)}} else {} end)), compute: {provider: "runpod", pod_id: ($pod | str), gpu: ($gpu | str), started_at: ($t0 | str), train_started_at: ($tt | str), ended_at: ($t1 | str), duration_sec: ($rented | num), cost_per_hr_usd: ($cph | num), cost_usd: (if $rented == "" then null else (($rented | tonumber) / 3600 * ($cph | tonumber) * 100 | round / 100) end), @@ -649,6 +664,7 @@ if [ $MODE = stop ]; then STEPS=$(jq -r '.steps // ""' "$STATE"); INIT=$(jq -r '.init // ""' "$STATE"); LIMIT_HOURS=$(jq -r '.limit_hours // ""' "$STATE") PROV_START=$(jq -c '.prov_start // empty' "$STATE") NORM_SHA=$(jq -r '.norm_sha256 // ""' "$STATE"); NORM_POD=$(jq -r '.norm_pod // ""' "$STATE"); NORM_LOCAL=$(jq -r '.norm_local // ""' "$STATE") # 依頼 133 + DECAY_STEPS=$TRAIN_DECAY; [ "$DECAY_STEPS" = steps ] && DECAY_STEPS=$STEPS # 依頼 135 (記録のモデルとステップ数から) [ "$PREP_SH" = pod_prep_deep.sh ] && PREP_LOG=/workspace/logs/prep_deep.log OPENPI_JSON=$(jq -c '.openpi // [{name: "openpi", commit: null, dirty: null, error: "記録にない (--stop)"}]' "$STATE") || OPENPI_JSON='[]' POD_EXP_DIR=/workspace/openpi/checkpoints/$CONFIG/$EXP; TRAIN_LOG=/workspace/logs/train_$EXP.log @@ -660,6 +676,7 @@ fi echo "[LAB ] $(date '+%Y-%m-%d %H:%M:%S') 学習 GPU=$GPU_ID$([ $FALLBACK = 1 ] && echo " (借りられなければ ${FALLBACK_GPUS[*]})")" echo " --model $MODEL (${HF_REPO_IN:-π0.5 base} → $HF_REPO_OUT) データ $DATA_NAME" echo " $CONFIG --exp-name=$EXP steps=$STEPS init=$INIT 上限 $LIMIT_HOURS 時間 記録 $LOGDIR$([ $DRY_RUN = 1 ] && echo ' ドライラン (Pod は借りない)')" +[ "$PREP_SH" = pod_prep_deep.sh ] && echo " 学習率: warmup 300・peak 5e-5・decay_steps ${DECAY_STEPS}・decay_lr 5e-6 (add_deep_config.py --variant ${PREP_VARIANT})" # ---- 0. 送るものと Mac の道具 ---- stage "0. 送るものと Mac の道具を確かめる" @@ -821,10 +838,10 @@ LAB_TO=$LAB_TO_LONG pod_scp "$TAR" "root@$POD_IP:/workspace/data/" || wrap_fail LAB_TO=$LAB_TO_SHORT pssh "ls -l /workspace/data/$(basename "$TAR")" || wrap_fail prep "送った tar が見えない" stage_done if [ "$PREP_SH" = pod_prep_deep.sh ]; then # deep1 (依頼 133): 正規化の値を Pod で測り直す - stage "4b. pod_prep_deep.sh $STEPS (変換・正規化の値を測り直す)" + stage "4b. pod_prep_deep.sh $STEPS $PREP_VARIANT (変換・正規化の値を測り直す)" # 出力は corr と同じくその場で画面とターミナルの記録に流し, Mac 側にも写しを残して NORM の行を読む (codex R1: $(...) に溜めない) PREP_OUT=$(dest_of prep_deep_mac.txt) - LAB_TO=$LAB_TO_LONG pssh "set -o pipefail; bash /workspace/r2r2r/pi05/openpi_addon/pod_prep_deep.sh $STEPS 2>&1 | tee $PREP_LOG" | tee "$PREP_OUT" \ + LAB_TO=$LAB_TO_LONG pssh "set -o pipefail; bash /workspace/r2r2r/pi05/openpi_addon/pod_prep_deep.sh $STEPS $PREP_VARIANT 2>&1 | tee $PREP_LOG" | tee "$PREP_OUT" \ || wrap_fail prep "pod_prep_deep.sh に失敗 (上の出力を見る)" nl=$(grep -E '^NORM sha256=[0-9a-f]{64} path=assets/[A-Za-z0-9_./-]+/norm_stats\.json$' "$PREP_OUT" | tail -1) [ -n "$nl" ] || wrap_fail prep "pod_prep_deep.sh から正規化の値の sha256 が返らない" diff --git a/lab_train_fakes.sh b/lab_train_fakes.sh index 7b86d96..756a95f 100644 --- a/lab_train_fakes.sh +++ b/lab_train_fakes.sh @@ -28,9 +28,9 @@ # hf_same Hugging Face にもう同じ中身がある (上げ直さない. 由来も足さない. 前の回の由来がある. 依頼 54) # hf_prov_hang 由来を Hugging Face へ足すのが返らない (LAB_PROV_TO を小さくして使う. codex R2) # openpi_partial Pod の openpi のコミットの行だけ出て ssh が切れる (codex R1) -# norm_missing deep1: pod_prep_deep.sh が正規化の値の sha256 の行を出さずに終わる (依頼 133) -# norm_fetch_fail deep1: 測り直した正規化の値を 4b で持ち帰れない (学習は続け, あとでチェックポイントの assets から取る. 依頼 133) -# norm_bad deep1: Pod が言う正規化の値の sha256 と, 持ち帰ったもの・チェックポイントの assets の中身が違う (依頼 133) +# norm_missing deep1・deep1b: pod_prep_deep.sh が正規化の値の sha256 の行を出さずに終わる (依頼 133) +# norm_fetch_fail deep1・deep1b: 測り直した正規化の値を 4b で持ち帰れない (学習は続け, あとでチェックポイントの assets から取る. 依頼 133) +# norm_bad deep1・deep1b: Pod が言う正規化の値の sha256 と, 持ち帰ったもの・チェックポイントの assets の中身が違う (依頼 133) LAB_DRY_ROOT=${LAB_DRY_ROOT:-$(mktemp -d /tmp/lab_train_dry.XXXXXX)} LAB_FAKE_SCENARIO=${LAB_FAKE_SCENARIO:-success} @@ -166,7 +166,7 @@ pod_ssh() { *"pod_prep_trim_corr.sh"*) echo "== 5. 準備できました (ドライラン)" ;; *"pod_prep_deep.sh"*) # 依頼 133: 正規化の値を測り直し, sha256 の行を出す echo "== 4. 正規化の値をこの 50 本で測り直す (ドライラン)" - [ "$LAB_FAKE_SCENARIO" = norm_missing ] || echo "NORM sha256=$([ "$LAB_FAKE_SCENARIO" = norm_bad ] && printf 'a%.0s' {1..64} || echo $FAKE_NORM_SHA) path=assets/pi05_mecharm_deep_even10_lora/local/mecharm_redblock_box_deep_even10_v1/norm_stats.json" + [ "$LAB_FAKE_SCENARIO" = norm_missing ] || echo "NORM sha256=$([ "$LAB_FAKE_SCENARIO" = norm_bad ] && printf 'a%.0s' {1..64} || echo $FAKE_NORM_SHA) path=assets/$CONFIG/local/mecharm_redblock_box_deep_even10_v1/norm_stats.json" # 設定の名前は作るモデルのもの (依頼 135) echo "== 5. 準備できました (ドライラン)" ;; *": lt=openpi_rev"*) [ "$LAB_FAKE_SCENARIO" = openpi_unknown ] && { echo "fatal: not a git repository"; return 128; } diff --git a/pi05/openpi_addon/add_deep_config.py b/pi05/openpi_addon/add_deep_config.py index f3409c1..e4c260a 100755 --- a/pi05/openpi_addon/add_deep_config.py +++ b/pi05/openpi_addon/add_deep_config.py @@ -1,44 +1,73 @@ #!/usr/bin/env python3 -"""openpi config.py に deep_even10_v1 (位置 1〜5 を各 10 本にそろえた 50 本) 用の学習設定 pi05_mecharm_deep_even10_lora を足す -(2026-11-09 依頼 133. lab_train.sh --model deep1). +"""openpi config.py に deep_even10_v1 (位置 1〜5 を各 10 本にそろえた 50 本) 用の学習設定を足す +(2026-11-09 依頼 133. lab_train.sh --model deep1. 依頼 135 で --variant b (lab_train.sh --model deep1b) を足した). π0.5 base から新しく学習する (続きの学習ではない). 中身は trim の pi05_mecharm_trim_lora と同じ LoRA・batch 32・ -学習率の予定 (warmup 300 / peak 5e-5 / decay_steps 5_000 / decay_lr 5e-6)・weight_loader (π0.5 base) で, -次の 3 か所だけ変える (add_trim_config.py と同じ方式): - name pi05_mecharm_trim_lora -> pi05_mecharm_deep_even10_lora +学習率の予定 (warmup 300 / peak 5e-5 / decay_lr 5e-6)・weight_loader (π0.5 base) で, 次の所だけ変える +(add_trim_config.py と同じ方式): + name pi05_mecharm_trim_lora -> --variant v1: pi05_mecharm_deep_even10_lora + --variant b: pi05_mecharm_deep_even10_b_lora repo_id local/mecharm_redblock_box_teach_v1_trim2 -> local/mecharm_redblock_box_deep_even10_v1 num_train_steps 4_000 -> --steps (依頼 133 は 3000) + decay_steps 5_000 -> --variant v1: 5_000 のまま (依頼 133 の 2999 はこれで学習した) + --variant b: --steps と同じ (依頼 135. 学習の終わりで decay_lr まで下げきる) 正規化の値は写さず, Pod で compute_norm_stats.py をこの設定で回して測り直す (pod_prep_deep.sh). install_addon.py と add_trim_config.py (lab/Log11/pod/) を実行した後に 1 回実行: - python3 add_deep_config.py /workspace/openpi --steps 3000 -何度実行しても同じ結果 (二重に入れない). 入っている設定と --steps が違うときは止まる. + python3 add_deep_config.py /workspace/openpi --steps 3000 --variant b +何度実行しても同じ結果 (二重に入れない). 入っている設定と --steps が違うときは止まる. v1 と b は名前が違うので両方入れられる. """ import argparse import re import sys from pathlib import Path -NAME = "pi05_mecharm_deep_even10_lora" REPO_ID = "local/mecharm_redblock_box_deep_even10_v1" -MARK_A = " # >>> r2r2r mecharm deep_even10_v1 (train config)\n" -MARK_B = " # <<< r2r2r mecharm deep_even10_v1 (train config)\n" TRIM_A = " # >>> r2r2r mecharm teach_v1_trim2 (train config)\n" TRIM_B = " # <<< r2r2r mecharm teach_v1_trim2 (train config)\n" BASE_LOADER = 'weight_loader=weight_loaders.CheckpointWeightLoader("gs://openpi-assets/checkpoints/pi05_base/params"),' -# trim と同じであることを確かめる値 (写したあとも変わっていないこと) -SAME_AS_TRIM = ("batch_size=32,", "warmup_steps=300,", "peak_lr=5e-5,", "decay_steps=5_000,", "decay_lr=5e-6,", BASE_LOADER) +TRIM_DECAY = "decay_steps=5_000," +WARMUP = 300 # trim と同じ warmup_steps. decay_steps はこれより大きくないと下げる区間がない +# trim と同じであることを確かめる値 (写したあとも変わっていないこと). decay_steps は --variant ごとに見る +SAME_AS_TRIM = ("batch_size=32,", f"warmup_steps={WARMUP},", "peak_lr=5e-5,", "decay_lr=5e-6,", BASE_LOADER) +# --variant ごとの設定の名前・印・decay_steps (None は --steps と同じ) +VARIANTS = { + "v1": {"name": "pi05_mecharm_deep_even10_lora", "mark": "deep_even10_v1", "decay": 5000}, # 依頼 133 (今回の 2999) + "b": {"name": "pi05_mecharm_deep_even10_b_lora", "mark": "deep_even10_v1 b", "decay": None}, # 依頼 135 +} +# 依頼 133 の形を残す (テスト・ほかから読む名前) +NAME = VARIANTS["v1"]["name"] -def make_block(s, steps): +def marks(variant): + m = VARIANTS[variant]["mark"] + return (f" # >>> r2r2r mecharm {m} (train config)\n", f" # <<< r2r2r mecharm {m} (train config)\n") + + +MARK_A, MARK_B = marks("v1") + + +def decay_of(variant, steps): + d = VARIANTS[variant]["decay"] + return steps if d is None else d + + +def decay_text(n): + """openpi の書き方に合わせる (5000 -> 5_000)""" + return f"decay_steps={n:_}," + + +def make_block(s, steps, variant="v1"): assert s.count(TRIM_A) == 1 and s.count(TRIM_B) == 1, "先に install_addon.py と add_trim_config.py を実行する" block = s[s.index(TRIM_A):s.index(TRIM_B) + len(TRIM_B)] + ma, mb = marks(variant) reps = [ - (TRIM_A, MARK_A), - (TRIM_B, MARK_B), - ('name="pi05_mecharm_trim_lora"', f'name="{NAME}"'), + (TRIM_A, ma), + (TRIM_B, mb), + ('name="pi05_mecharm_trim_lora"', f'name="{VARIANTS[variant]["name"]}"'), ('repo_id="local/mecharm_redblock_box_teach_v1_trim2"', f'repo_id="{REPO_ID}"'), ("num_train_steps=4_000,", f"num_train_steps={steps},"), + (TRIM_DECAY, decay_text(decay_of(variant, steps))), ] for a, b in reps: assert block.count(a) == 1, f"見つからない/重複: {a!r}" @@ -52,27 +81,36 @@ def main(argv=None): ap = argparse.ArgumentParser() ap.add_argument("openpi_root") ap.add_argument("--steps", type=int, required=True, help="学習のステップ数 (num_train_steps. 依頼 133 は 3000)") + ap.add_argument("--variant", choices=sorted(VARIANTS), required=True, + help="v1 = decay_steps 5_000 (依頼 133 の 2999) / b = decay_steps を --steps と同じに (依頼 135)") args = ap.parse_args(argv) if args.steps <= 0: sys.exit("[ERROR] --steps は 1 以上") + decay = decay_of(args.variant, args.steps) + if decay <= WARMUP: + sys.exit(f"[ERROR] decay_steps ({decay}) が warmup_steps ({WARMUP}) 以下. --steps を大きくする") + name = VARIANTS[args.variant]["name"] + ma, mb = marks(args.variant) cfg = Path(args.openpi_root).resolve() / "src" / "openpi" / "training" / "config.py" s = cfg.read_text() - if MARK_A in s: - have = s[s.index(MARK_A):] - have = have[:have.index(MARK_B)] + if ma in s: + have = s[s.index(ma):] + have = have[:have.index(mb)] m = re.search(r"num_train_steps=(\d+),", have) - if not (f'name="{NAME}"' in have and f'repo_id="{REPO_ID}"' in have and all(x in have for x in SAME_AS_TRIM)): + if not (f'name="{name}"' in have and f'repo_id="{REPO_ID}"' in have and all(x in have for x in SAME_AS_TRIM)): sys.exit("[ERROR] 入っている設定の名前・データ名・学習の値が違う. openpi の config.py を git checkout してから入れ直す") if not (m and int(m.group(1)) == args.steps): sys.exit("[ERROR] 別の --steps で入っている. openpi の config.py を git checkout してから入れ直す") + if have.count(decay_text(decay)) != 1: + sys.exit(f"[ERROR] 入っている設定の decay_steps が {decay} ではない. openpi の config.py を git checkout してから入れ直す") print("already installed (config.py は変更なし)") return 0 - if f'name="{NAME}"' in s: - sys.exit(f"[ERROR] {NAME} がもう別の形で入っている. openpi の config.py を git checkout してから入れ直す") - block = make_block(s, args.steps) + if f'name="{name}"' in s: + sys.exit(f"[ERROR] {name} がもう別の形で入っている. openpi の config.py を git checkout してから入れ直す") + block = make_block(s, args.steps, args.variant) j = s.index(TRIM_B) + len(TRIM_B) cfg.write_text(s[:j] + block + s[j:]) - print(f"installed: {NAME} (repo_id={REPO_ID}, {args.steps} steps, init=π0.5 base)") + print(f"installed: {name} (repo_id={REPO_ID}, {args.steps} steps, decay_steps {decay}, init=π0.5 base)") return 0 diff --git a/pi05/openpi_addon/pod_prep_deep.sh b/pi05/openpi_addon/pod_prep_deep.sh index 41048a6..51d2fc5 100755 --- a/pi05/openpi_addon/pod_prep_deep.sh +++ b/pi05/openpi_addon/pod_prep_deep.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # pod_prep_deep.sh — Pod で deep_even10_v1 (位置 1〜5 を各 10 本にそろえた 50 本) の学習の準備をする (2026-11-09 依頼 133). -# 学習は回さない. lab_train.sh --model deep1 の 4b が呼ぶ. +# 学習は回さない. lab_train.sh --model deep1b (依頼 135) の 4b が呼ぶ (deep1 は依頼 133 で呼んだ形). # # 前提: lab_train.sh 3a〜3f で Pod を準備してある (openpi・install_addon.py・add_trim_config.py). 続きの元のモデルは取らない # (π0.5 base から. 重みは学習の最初に openpi が gs://openpi-assets から取る) @@ -8,12 +8,15 @@ # /workspace/data/mecharm_redblock_box_deep_even10_v1_raw_episodes.tar # /workspace/r2r2r/pi05/ (add_deep_config.py と convert_to_lerobot.py を使う) # -# bash /workspace/r2r2r/pi05/openpi_addon/pod_prep_deep.sh -# 例: bash ... 3000 +# bash /workspace/r2r2r/pi05/openpi_addon/pod_prep_deep.sh +# 例: bash ... 3000 b +# は add_deep_config.py の --variant (依頼 135): +# v1 = 設定 pi05_mecharm_deep_even10_lora・decay_steps 5_000 (依頼 133 の 2999. lab_train.sh --model deep1) +# b = 設定 pi05_mecharm_deep_even10_b_lora・decay_steps を と同じに (lab_train.sh --model deep1b) # # すること (何度流しても同じ結果): # 1. tar を /workspace/data/<データ名>/ に広げ, エピソードの本数を確かめる (Mac で作ったときは 50 本) -# 2. openpi の config.py に pi05_mecharm_deep_even10_lora を足す (--steps) +# 2. openpi の config.py に の設定を足す (--steps --variant) # 3. LeRobot 形式に変換 (repo_id local/<データ名>) # 4. 正規化の値をこの 50 本で測り直す (RUNPOD_TRAIN.md 4. の compute_norm_stats.py をこの設定で). # 置く場所は openpi が学習で読む assets/<設定の名前>/local/<データ名>/norm_stats.json. @@ -22,9 +25,13 @@ set -euo pipefail STEPS="${1:?学習のステップ数 (例 3000)}" +VARIANT="${2:?add_deep_config.py の --variant (v1 か b)}" NAME=mecharm_redblock_box_deep_even10_v1 -CONFIG=pi05_mecharm_deep_even10_lora -EXP=deep_even10_v1 +case "$VARIANT" in # 何かする前に確かめる (依頼 135) + v1) CONFIG=pi05_mecharm_deep_even10_lora; EXP=deep_even10_v1 ;; + b) CONFIG=pi05_mecharm_deep_even10_b_lora; EXP=deep_even10_v1_b ;; + *) echo "[ERROR] は v1 か b: $VARIANT" >&2; exit 2 ;; +esac N_MAC=50 TAR=/workspace/data/${NAME}_raw_episodes.tar DATA=/workspace/data/$NAME @@ -49,8 +56,8 @@ mv "$TMP" "$DATA" echo " エピソード $N 本 (Mac で作ったときは $N_MAC 本)" cd "$OPENPI" -echo "== 2. 学習設定 $CONFIG を足す (steps=$STEPS. π0.5 base から)" -python3 "$ADDON/add_deep_config.py" "$OPENPI" --steps "$STEPS" +echo "== 2. 学習設定 $CONFIG を足す (steps=${STEPS}・variant ${VARIANT}. π0.5 base から)" +python3 "$ADDON/add_deep_config.py" "$OPENPI" --steps "$STEPS" --variant "$VARIANT" echo "== 3. LeRobot 形式に変換 (local/$NAME)" uv run python /workspace/r2r2r/pi05/convert_to_lerobot.py --raw-dir "$DATA/raw_episodes" --repo-id "local/$NAME" diff --git a/pi05/test_merge_and_config.py b/pi05/test_merge_and_config.py index 31cc80c..b6ce35e 100644 --- a/pi05/test_merge_and_config.py +++ b/pi05/test_merge_and_config.py @@ -335,27 +335,78 @@ class TestConfigV5(unittest.TestCase): class TestConfigDeep(unittest.TestCase): - """依頼 133: deep_even10_v1 の設定は trim と同じで, 名前・データ名・ステップ数だけ違う. π0.5 base から.""" + """依頼 133: deep_even10_v1 の設定は trim と同じで, 名前・データ名・ステップ数だけ違う. π0.5 base から. + 依頼 135: --variant v1 は decay_steps 5_000 のまま (依頼 133 の 2999), --variant b は decay_steps を --steps と同じにする.""" def test_same_as_trim_except_data_and_steps(self): with tempfile.TemporaryDirectory() as td, mock.patch("sys.stdout"): cfg = fake_config(Path(td)) self.assertEqual(atc.main([td, "--variant", "v5", "--steps", "1500", "--init", "/workspace/ckpt/corr_v2_1499/params"]), 0) # corr の設定と並べて入る - self.assertEqual(adc.main([td, "--steps", "3000"]), 0) + self.assertEqual(adc.main([td, "--steps", "3000", "--variant", "v1"]), 0) c = configs(cfg) new, trim = c["pi05_mecharm_deep_even10_lora"], c["pi05_mecharm_trim_lora"] self.assertIn("local/mecharm_redblock_box_deep_even10_v1", new["data"]) self.assertEqual(new["num_train_steps"], "3000") self.assertIn("gs://openpi-assets/checkpoints/pi05_base/params", new["weight_loader"]) for k in set(trim) - {"name", "data", "num_train_steps"}: - self.assertEqual(new[k], trim[k], k) + self.assertEqual(new[k], trim[k], k) # v1 は学習率の予定も trim と同じ (decay_steps 5000) + self.assertIn("decay_steps=5000", new["lr_schedule"]) self.assertEqual(new["data"].replace("deep_even10_v1", "teach_v1_trim2"), trim["data"]) self.assertEqual(trim["num_train_steps"], "4000") # trim2 の設定は変えない text = cfg.read_text() - self.assertEqual(adc.main([td, "--steps", "3000"]), 0) # 二重に入れない + self.assertEqual(adc.main([td, "--steps", "3000", "--variant", "v1"]), 0) # 二重に入れない self.assertEqual(cfg.read_text(), text) with self.assertRaises(SystemExit): - adc.main([td, "--steps", "2000"]) # 違うステップ数では止まる + adc.main([td, "--steps", "2000", "--variant", "v1"]) # 違うステップ数では止まる + self.assertEqual(cfg.read_text(), text) + + def test_variant_b_decay_equals_steps(self): + """依頼 135: b は decay_steps = num_train_steps. ほかは v1 (trim) と同じ. v1 と並べて入る""" + for steps in (3000, 2000): + with self.subTest(steps=steps), tempfile.TemporaryDirectory() as td, mock.patch("sys.stdout"): + cfg = fake_config(Path(td)) + self.assertEqual(adc.main([td, "--steps", "3000", "--variant", "v1"]), 0) + self.assertEqual(adc.main([td, "--steps", str(steps), "--variant", "b"]), 0) + c = configs(cfg) + b, v1, trim = (c["pi05_mecharm_deep_even10_b_lora"], c["pi05_mecharm_deep_even10_lora"], + c["pi05_mecharm_trim_lora"]) + self.assertEqual(b["num_train_steps"], str(steps)) + self.assertEqual(b["lr_schedule"], trim["lr_schedule"].replace("decay_steps=5000", f"decay_steps={steps}")) + self.assertIn("warmup_steps=300", b["lr_schedule"]) + self.assertIn("peak_lr=5e-05", b["lr_schedule"]) + self.assertIn("decay_lr=5e-06", b["lr_schedule"]) + self.assertEqual(b["data"], v1["data"]) + for k in set(trim) - {"name", "data", "num_train_steps", "lr_schedule"}: + self.assertEqual(b[k], trim[k], k) + self.assertIn("decay_steps=5000", v1["lr_schedule"]) # v1 (今回の 2999) は変わらない + text = cfg.read_text() + self.assertEqual(adc.main([td, "--steps", str(steps), "--variant", "b"]), 0) # 二重に入れない + self.assertEqual(cfg.read_text(), text) + with self.assertRaises(SystemExit): + adc.main([td, "--steps", str(steps + 500), "--variant", "b"]) # 違うステップ数では止まる + self.assertEqual(cfg.read_text(), text) + + def test_variant_b_refuses_changed_decay(self): + """入っている b の decay_steps がステップ数と違えば止まる (手で書き換えたものを使わない)""" + with tempfile.TemporaryDirectory() as td, mock.patch("sys.stdout"): + cfg = fake_config(Path(td)) + self.assertEqual(adc.main([td, "--steps", "3000", "--variant", "b"]), 0) + cfg.write_text(cfg.read_text().replace("decay_steps=3_000,", "decay_steps=5_000,")) + text = cfg.read_text() + with self.assertRaises(SystemExit): + adc.main([td, "--steps", "3000", "--variant", "b"]) + self.assertEqual(cfg.read_text(), text) + + def test_variant_required_and_warmup(self): + with tempfile.TemporaryDirectory() as td, mock.patch("sys.stdout"), mock.patch("sys.stderr"): + cfg = fake_config(Path(td)) + text = cfg.read_text() + with self.assertRaises(SystemExit): + adc.main([td, "--steps", "3000"]) # --variant なしでは入れない + with self.assertRaises(SystemExit): + adc.main([td, "--steps", "3000", "--variant", "c"]) + with self.assertRaises(SystemExit): + adc.main([td, "--steps", "300", "--variant", "b"]) # decay_steps が warmup 300 以下 self.assertEqual(cfg.read_text(), text) def test_requires_trim_first(self): @@ -363,18 +414,31 @@ class TestConfigDeep(unittest.TestCase): cfg = Path(td) / "src" / "openpi" / "training" / "config.py" cfg.parent.mkdir(parents=True) cfg.write_text("_CONFIGS = []\n") - with self.assertRaises(AssertionError): - adc.main([td, "--steps", "3000"]) + for v in ("v1", "b"): + with self.assertRaises(AssertionError): + adc.main([td, "--steps", "3000", "--variant", v]) def test_pod_prep_deep_computes_norm(self): sh = _HERE / "openpi_addon" / "pod_prep_deep.sh" self.assertEqual(subprocess.run(["bash", "-n", str(sh)]).returncode, 0) code = "\n".join(ln for ln in sh.read_text().splitlines() if not ln.lstrip().startswith("#")) - for x in ('uv run scripts/compute_norm_stats.py --config-name "$CONFIG"', "CONFIG=pi05_mecharm_deep_even10_lora", + for x in ('uv run scripts/compute_norm_stats.py --config-name "$CONFIG"', + "v1) CONFIG=pi05_mecharm_deep_even10_lora; EXP=deep_even10_v1 ;;", + "b) CONFIG=pi05_mecharm_deep_even10_b_lora; EXP=deep_even10_v1_b ;;", "NAME=mecharm_redblock_box_deep_even10_v1", "NORM_DST=assets/$CONFIG/local/$NAME/norm_stats.json", - 'echo "NORM sha256=$SHA path=$NORM_DST"', 'add_deep_config.py" "$OPENPI" --steps "$STEPS"'): + 'echo "NORM sha256=$SHA path=$NORM_DST"', 'add_deep_config.py" "$OPENPI" --steps "$STEPS" --variant "$VARIANT"'): self.assertIn(x, code) + def test_pod_prep_deep_unknown_variant_stops_first(self): + """依頼 135: が v1・b 以外 (またはない) なら, tar を見る前に止まる""" + sh = _HERE / "openpi_addon" / "pod_prep_deep.sh" + r = subprocess.run(["bash", str(sh), "3000", "c"], capture_output=True, text=True) + self.assertEqual(r.returncode, 2) + self.assertIn(" は v1 か b: c", r.stderr) + r = subprocess.run(["bash", str(sh), "3000"], capture_output=True, text=True) + self.assertNotEqual(r.returncode, 0) + self.assertNotIn("tar がない", r.stderr) + class TestPodPrep(unittest.TestCase): """依頼 48: pod_prep_trim_corr.sh は正規化の値を計算し直さず, 3999 の学習の trim2 の値を置く.""" diff --git a/test_lab_start.sh b/test_lab_start.sh index 394bc5c..bcb8528 100755 --- a/test_lab_start.sh +++ b/test_lab_start.sh @@ -24,7 +24,8 @@ setup() { # 1 つの場合ごとに新しい一時フォルダ T=$(mktemp -d) mkdir -p "$T/bin" "$T/api" "$T/keys" "$T/pi05" \ "$T/data/lab/Log11/pod" "$T/data/lab/LogT/pod" \ - "$T/data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes/run_0001_20261019_194818" + "$T/data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes/run_0001_20261019_194818" \ + "$T/data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes" echo "x" > "$T/data/lab/Log11/pod/add_trim_config.py" echo "rk" > "$T/keys/runpod"; echo "hk" > "$T/keys/hf"; echo "k" > "$T/keys/id"; echo "k.pub" > "$T/keys/id.pub" sed -e "s#/Volumes/KIOXIA/r2r2r-data#$T/data#g" -e 's#^LISTEN_WAIT=30; LISTEN_POLL=5#LISTEN_WAIT=2; LISTEN_POLL=1#' \ @@ -340,6 +341,7 @@ check "終了コード 0" '[ $RC = 0 ]' check "3999 を取り, trim の設定でサーバーを起動" 'has ssh.log "hf download PixelstoMotors/pi05-mecharm-trim-lora-v1 --local-dir /workspace/ckpt/3999" && has ssh.log "--policy.config=pi05_mecharm_trim_lora --policy.dir=/workspace/ckpt/3999 2>&1"' check "3f は今までと同じ (corr の設定は足さない)" 'has ssh.log "$(. "$HERE/lab_lib.sh"; lab_cmd_addon)" && ! has ssh.log "add_trim_corr_config.py"' check "クライアントに --model trim を渡す・安全範囲は trim2" 'has py.log "--model trim (PixelstoMotors/pi05-mecharm-trim-lora-v1)" && has py.log "datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes "' +check "trim の安全範囲 (run_policy_client.py --raw-dir) は trim2 のまま・deep_even10_v1 は使わない (依頼 135)" 'has py.log "run_policy_client.py --raw-dir $T/data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes --steps-per-chunk" && ! has py.log "deep_even10_v1" && has out.txt "[RANGE ] 安全範囲 (学習データ ±10°) は $T/data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes から (--model trim)"' check "記録 lab_pod.json に model=trim" '[ "$(jq -r .model "$T/data/lab/LogT/pod/lab_pod.json")" = trim ]' check "lab_start_*.txt にモデルが残る" 'grep -qF "[MODEL ] --model trim" "$T/data/lab/LogT"/lab_start_*.txt && grep -qF "モデル trim (PixelstoMotors/pi05-mecharm-trim-lora-v1)" "$T/data/lab/LogT"/lab_start_*.txt' rm -rf "$T" @@ -416,23 +418,43 @@ echo "== 34d. --model deep1 (依頼 133) で新しく作る → π0.5 base か setup; run --model deep1 check "終了コード 0" '[ $RC = 0 ]' check "deep1 のモデルを /workspace/ckpt/deep_v1_2999 に取る (ほかのモデルは取らない)" 'has ssh.log "hf download PixelstoMotors/pi05-mecharm-deep-even10-lora-v1 --local-dir /workspace/ckpt/deep_v1_2999" && ! has ssh.log "hf download PixelstoMotors/pi05-mecharm-trim-lora-v1 " && ! has ssh.log "hf download PixelstoMotors/pi05-mecharm-trim-corr-lora-v"' -check "3f で学習と同じ deep の設定 (3000 ステップ) を足し, モデルの assets (測り直した正規化の値) を写す" 'has ssh.log "add_deep_config.py /workspace/openpi --steps 3000" && ! has ssh.log "add_trim_corr_config.py" && has ssh.log "mkdir -p assets/pi05_mecharm_deep_even10_lora/local && cp -r /workspace/ckpt/deep_v1_2999/assets/local/mecharm_redblock_box_deep_even10_v1 assets/pi05_mecharm_deep_even10_lora/local/"' +check "3f で学習と同じ deep の設定 (3000 ステップ・--variant v1 = decay_steps 5000) を足し, モデルの assets (測り直した正規化の値) を写す" 'has ssh.log "add_deep_config.py /workspace/openpi --steps 3000 --variant v1 &&" && ! has ssh.log "add_trim_corr_config.py" && has ssh.log "mkdir -p assets/pi05_mecharm_deep_even10_lora/local && cp -r /workspace/ckpt/deep_v1_2999/assets/local/mecharm_redblock_box_deep_even10_v1 assets/pi05_mecharm_deep_even10_lora/local/"' check "deep1 の設定とフォルダでサーバーを起動" 'has ssh.log "--policy.config=pi05_mecharm_deep_even10_lora --policy.dir=/workspace/ckpt/deep_v1_2999 2>&1"' check "クライアントに --model deep1 を渡す" 'has py.log "--model deep1 (PixelstoMotors/pi05-mecharm-deep-even10-lora-v1)"' +check "deep1 の安全範囲 (run_policy_client.py --raw-dir) は deep_even10_v1 の raw_episodes・trim2 は使わない (依頼 135)" 'has py.log "run_policy_client.py --raw-dir $T/data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes --steps-per-chunk" && ! has py.log "teach_v1_trim2" && has out.txt "[RANGE ] 安全範囲 (学習データ ±10°) は $T/data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes から (--model deep1)"' check "記録に model=deep1" '[ "$(jq -r .model "$T/data/lab/LogT/pod/lab_pod.json")" = deep1 ]' rm -rf "$T" +setup; rmdir "$T/data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes"; run --model deep1 +check "deep1 で deep_even10_v1 の raw_episodes がない → API を呼ばずに止まる (依頼 135)" '[ $RC = 1 ] && has out.txt "安全範囲を作る学習データがありません: $T/data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes" && [ ! -s "$T/calls.log" ]' +rm -rf "$T" +setup; rmdir "$T/data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes"; run --model corr3 +check "corr3 は deep_even10_v1 がなくても trim2 で進む (依頼 135)" '[ $RC = 0 ] && has py.log "run_policy_client.py --raw-dir $T/data/datasets/mecharm_redblock_box_teach_v1_trim2/raw_episodes --steps-per-chunk"' +rm -rf "$T" setup; state $ID_OLD; pod 200 "$RUNNING"; probe 1 1 1 1 1; serve_dir /workspace/ckpt/corr_v3_1499; run --model deep1 check "corr3 のサーバーが動いている Pod に --model deep1 → 止まり, --model corr3 を案内" '[ $RC = 1 ] && has out.txt "別のモデル (--model corr3)で動いています" && ! has ssh.log "tmux new" && ! has ssh.log "pkill"' rm -rf "$T" check "フォルダからモデルの名前 (deep1)" '[ "$(. "$HERE/lab_lib.sh"; lab_model_of_dir /workspace/ckpt/deep_v1_2999)" = deep1 ]' +echo "== 34e. --model deep1b (依頼 135) → decay_steps をステップ数と同じにして学習したモデルを取り, b の設定・assets を足して起動" +setup; run --model deep1b +check "終了コード 0" '[ $RC = 0 ]' +check "deep1b のモデルを /workspace/ckpt/deep_b_v1_2999 に取る (deep1 のモデルは取らない)" 'has ssh.log "hf download PixelstoMotors/pi05-mecharm-deep-even10-b-lora-v1 --local-dir /workspace/ckpt/deep_b_v1_2999" && ! has ssh.log "hf download PixelstoMotors/pi05-mecharm-deep-even10-lora-v1 "' +check "3f で add_deep_config.py --steps 3000 --variant b を足し, モデルの assets を pi05_mecharm_deep_even10_b_lora に写す" 'has ssh.log "add_deep_config.py /workspace/openpi --steps 3000 --variant b &&" && ! has ssh.log "--variant v1" && has ssh.log "mkdir -p assets/pi05_mecharm_deep_even10_b_lora/local && cp -r /workspace/ckpt/deep_b_v1_2999/assets/local/mecharm_redblock_box_deep_even10_v1 assets/pi05_mecharm_deep_even10_b_lora/local/"' +check "deep1b の設定とフォルダでサーバーを起動" 'has ssh.log "--policy.config=pi05_mecharm_deep_even10_b_lora --policy.dir=/workspace/ckpt/deep_b_v1_2999 2>&1"' +check "deep1b の安全範囲も deep_even10_v1 の raw_episodes" 'has py.log "--model deep1b (PixelstoMotors/pi05-mecharm-deep-even10-b-lora-v1)" && has py.log "run_policy_client.py --raw-dir $T/data/datasets/mecharm_redblock_box_deep_even10_v1/raw_episodes --steps-per-chunk"' +rm -rf "$T" +setup; state $ID_OLD; pod 200 "$RUNNING"; probe 1 1 1 1 1; serve_dir /workspace/ckpt/deep_v1_2999; run --model deep1b +check "deep1 のサーバーが動いている Pod に --model deep1b → 止まり, --model deep1 を案内" '[ $RC = 1 ] && has out.txt "別のモデル (--model deep1)で動いています" && ! has ssh.log "tmux new" && ! has ssh.log "pkill"' +rm -rf "$T" +check "フォルダからモデルの名前 (deep1b)" '[ "$(. "$HERE/lab_lib.sh"; lab_model_of_dir /workspace/ckpt/deep_b_v1_2999)" = deep1b ]' + echo "== 35. --model に知らない名前 → 何もせずに止まる" setup; run --model foo -check "終了コード 2・使える名前を出す・API を呼ばない" '[ $RC = 2 ] && has out.txt "--model は trim か corr か corr2 か corr3 か deep1" && [ ! -s "$T/calls.log" ]' +check "終了コード 2・使える名前を出す・API を呼ばない" '[ $RC = 2 ] && has out.txt "--model は trim か corr か corr2 か corr3 か deep1 か deep1b" && [ ! -s "$T/calls.log" ]' rm -rf "$T" setup (cd "$T" && PATH="$T/bin:$PATH" perl -e 'alarm 10; exec @ARGV' ./lab_start.sh --log LogT --dry-run --model < /dev/null > "$T/out.txt" 2>&1); RC=$? -check "--model だけで値がない → 回り続けずに終了コード 2 (codex R1)" '[ $RC = 2 ] && has out.txt "--model には trim か corr か corr2 か corr3 か deep1 を付ける"' +check "--model だけで値がない → 回り続けずに終了コード 2 (codex R1)" '[ $RC = 2 ] && has out.txt "--model には trim か corr か corr2 か corr3 か deep1 か deep1b を付ける"' rm -rf "$T" for a in "--gpu" "--episode" "--log" "--gpu --dry-run" "--episode --log LogT" "--log --dry-run"; do setup diff --git a/test_lab_train.sh b/test_lab_train.sh index e0de0fc..f368a6c 100755 --- a/test_lab_train.sh +++ b/test_lab_train.sh @@ -237,48 +237,66 @@ TD="$T/data/lab/LogT/train" check "--stop (--model なし): 記録の model=corr3 の設定・exp でログと一覧を取る" '[ $(n_call DELETE) = 1 ] && [ -f "$TD/train_trim_corr_v5.log" ] && has ssh.log "cd /workspace/openpi/checkpoints/pi05_mecharm_trim_corr_v5_lora/trim_corr_v5" && [ "$(jq -r .config.data_name "$TD/train_provenance.json")" = mecharm_redblock_box_teach_v1_trim2_corr_v5 ]' rm -rf "$T" -echo "== 12d. --model deep1 (依頼 133): π0.5 base から deep_even10_v1 で新しく学習する (正規化の値は Pod で測り直す)" -run success --model deep1 --limit-hours 7 -CKD="$T/data/checkpoints/pi05_mecharm_deep_even10_lora_v1" +echo "== 12d. --model deep1b (依頼 135. 依頼 133 の deep1 と同じ流れで decay_steps だけステップ数と同じ): π0.5 base から deep_even10_v1 で新しく学習する (正規化の値は Pod で測り直す)" +run success --model deep1b --limit-hours 7 +CKD="$T/data/checkpoints/pi05_mecharm_deep_even10_b_lora_v1" NSHA=c1448b9e612668eedc320cc285053fcee62dc9afb40e435c70948441d486b8e2 -check "終了コード 0・画面に --model deep1 (π0.5 base → 上げる先)・steps 3000 (既定)・上限 7 時間" '[ $RC = 0 ] && has out.txt "--model deep1 (π0.5 base → PixelstoMotors/pi05-mecharm-deep-even10-lora-v1)" && has out.txt "steps=3000 init=base 上限 7 時間"' +check "終了コード 0・画面に --model deep1b (π0.5 base → 上げる先)・steps 3000 (既定)・上限 7 時間・decay_steps 3000" '[ $RC = 0 ] && has out.txt "--model deep1b (π0.5 base → PixelstoMotors/pi05-mecharm-deep-even10-b-lora-v1)" && has out.txt "steps=3000 init=base 上限 7 時間" && has out.txt "decay_steps 3000・decay_lr 5e-6 (add_deep_config.py --variant b)"' check "3c は続きの元を取らない (hf download を Pod で流さない)" '! has ssh.log "hf download" && has out.txt "3c. 続きの元は取らない (π0.5 base から"' check "3f は addon と trim2 の設定だけ (3999 の assets を写さない)" 'has ssh.log "add_trim_config.py /workspace/openpi" && ! has ssh.log "cp -r /workspace/ckpt/"' -check "deep_even10_v1 の tar だけを送り, pod_prep_deep.sh 3000 (pod_prep_trim_corr.sh は流さない)" 'has scp.log "mecharm_redblock_box_deep_even10_v1_raw_episodes.tar root@" && ! has scp.log "corr_v" && has ssh.log "bash /workspace/r2r2r/pi05/openpi_addon/pod_prep_deep.sh 3000 2>&1 | tee /workspace/logs/prep_deep.log" && ! has ssh.log "pod_prep_trim_corr.sh"' -check "測り直した正規化の値を学習の前に持ち帰る (lab//train/norm_stats.json・sha256 が Pod と同じ)" '[ "$(shasum -a 256 "$TD/norm_stats.json" | cut -d" " -f1)" = $NSHA ] && [ $(line_of out.txt "[NORM ] 正規化の値") -lt $(line_of out.txt "=== 5. 学習を始める") ] && [ "$(jq -r .norm_sha256 "$TD/lab_pod.json")" = $NSHA ]' -check "学習は pi05_mecharm_deep_even10_lora --exp-name=deep_even10_v1" 'has ssh.log "uv run scripts/train.py pi05_mecharm_deep_even10_lora --exp-name=deep_even10_v1 --overwrite > /workspace/logs/train_deep_even10_v1.log"' -check "最後は 2999 を Hugging Face の pi05-mecharm-deep-even10-lora-v1 へ上げ, KIOXIA の checkpoints/pi05_mecharm_deep_even10_lora_v1/2999 に assets ごと置く" 'has ssh.log "hf_upload_ckpt.py --dir /workspace/openpi/checkpoints/pi05_mecharm_deep_even10_lora/deep_even10_v1/2999 --repo PixelstoMotors/pi05-mecharm-deep-even10-lora-v1" && [ -f "$CKD/2999/_CHECKPOINT_METADATA" ] && [ -f "$CKD/2999/assets/local/mecharm_redblock_box_deep_even10_v1/norm_stats.json" ] && has out.txt "assets の正規化の値は測り直したものと sha256 が同じ"' -check "記録 lab_pod.json に model・設定・exp・init・steps" '[ "$(jq -r "[.model,.config,.exp,.init,.steps] | join(\" \")" "$TD/lab_pod.json")" = "deep1 pi05_mecharm_deep_even10_lora deep_even10_v1 base 3000" ]' +check "deep_even10_v1 の tar だけを送り, pod_prep_deep.sh 3000 b (pod_prep_trim_corr.sh は流さない)" 'has scp.log "mecharm_redblock_box_deep_even10_v1_raw_episodes.tar root@" && ! has scp.log "corr_v" && has ssh.log "bash /workspace/r2r2r/pi05/openpi_addon/pod_prep_deep.sh 3000 b 2>&1 | tee /workspace/logs/prep_deep.log" && ! has ssh.log "pod_prep_trim_corr.sh"' +check "測り直した正規化の値を学習の前に持ち帰る (lab//train/norm_stats.json・sha256 が Pod と同じ)" '[ "$(shasum -a 256 "$TD/norm_stats.json" | cut -d" " -f1)" = $NSHA ] && [ $(line_of out.txt "[NORM ] 正規化の値") -lt $(line_of out.txt "=== 5. 学習を始める") ] && [ "$(jq -r .norm_sha256 "$TD/lab_pod.json")" = $NSHA ] && [ "$(jq -r .norm_pod "$TD/lab_pod.json")" = assets/pi05_mecharm_deep_even10_b_lora/local/mecharm_redblock_box_deep_even10_v1/norm_stats.json ]' +check "学習は pi05_mecharm_deep_even10_b_lora --exp-name=deep_even10_v1_b (deep1 の設定・exp は使わない)" 'has ssh.log "uv run scripts/train.py pi05_mecharm_deep_even10_b_lora --exp-name=deep_even10_v1_b --overwrite > /workspace/logs/train_deep_even10_v1_b.log" && ! has ssh.log "train.py pi05_mecharm_deep_even10_lora "' +check "最後は 2999 を Hugging Face の pi05-mecharm-deep-even10-b-lora-v1 へ上げ, KIOXIA の checkpoints/pi05_mecharm_deep_even10_b_lora_v1/2999 に assets ごと置く (deep1 の置き場所には置かない)" 'has ssh.log "hf_upload_ckpt.py --dir /workspace/openpi/checkpoints/pi05_mecharm_deep_even10_b_lora/deep_even10_v1_b/2999 --repo PixelstoMotors/pi05-mecharm-deep-even10-b-lora-v1" && [ -f "$CKD/2999/_CHECKPOINT_METADATA" ] && [ -f "$CKD/2999/assets/local/mecharm_redblock_box_deep_even10_v1/norm_stats.json" ] && has out.txt "assets の正規化の値は測り直したものと sha256 が同じ" && [ ! -e "$T/data/checkpoints/pi05_mecharm_deep_even10_lora_v1" ]' +check "記録 lab_pod.json に model・設定・exp・init・steps" '[ "$(jq -r "[.model,.config,.exp,.init,.steps] | join(\" \")" "$TD/lab_pod.json")" = "deep1b pi05_mecharm_deep_even10_b_lora deep_even10_v1_b base 3000" ]' PD="$TD/train_provenance.json" check "由来: base から (pi05_base・gs://)・データの tar の sha256・正規化の値の sha256" '[ "$(jq -r ".inputs[1] | [.kind,.id,.url,.path] | join(\" \")" "$PD")" = "checkpoint pi05_base gs://openpi-assets/checkpoints/pi05_base/params " ] && [ "$(jq -r ".config.init_params" "$PD")" = base ] && [ "$(jq -r ".inputs[0].tar | [.path,.sha256] | join(\" \")" "$PD")" = "datasets/mecharm_redblock_box_deep_even10_v1/mecharm_redblock_box_deep_even10_v1_raw_episodes.tar $(shasum -a 256 "$T/data/datasets/mecharm_redblock_box_deep_even10_v1/mecharm_redblock_box_deep_even10_v1_raw_episodes.tar" | cut -d" " -f1)" ] && [ "$(jq -r ".config.norm_stats.sha256" "$PD")" = $NSHA ]' -check "由来: 出力・チェックポイント側と Hugging Face にも同じもの" '[ "$(jq -r ".outputs[0] | [.path,.hf_repo] | join(\" \")" "$PD")" = "checkpoints/pi05_mecharm_deep_even10_lora_v1/2999 PixelstoMotors/pi05-mecharm-deep-even10-lora-v1" ] && cmp -s "$PD" "$CKD/2999/train_provenance.json" && has calls.log "hf upload PixelstoMotors/pi05-mecharm-deep-even10-lora-v1 train_provenance.json"' +check "由来 (依頼 135): 設定の名前と学習率の予定 (decay_steps = num_train_steps = 3000・warmup 300・peak 5e-5・decay_lr 5e-6・--variant b)" '[ "$(jq -r ".config | [.name, .num_train_steps, .lr_schedule.decay_steps, .lr_schedule.warmup_steps, .lr_schedule.peak_lr, .lr_schedule.decay_lr, .lr_schedule.source] | join(\" \")" "$PD")" = "pi05_mecharm_deep_even10_b_lora 3000 3000 300 0.00005 0.000005 add_deep_config.py --variant b" ]' +check "由来: 出力・チェックポイント側と Hugging Face にも同じもの" '[ "$(jq -r ".outputs[0] | [.path,.hf_repo] | join(\" \")" "$PD")" = "checkpoints/pi05_mecharm_deep_even10_b_lora_v1/2999 PixelstoMotors/pi05-mecharm-deep-even10-b-lora-v1" ] && cmp -s "$PD" "$CKD/2999/train_provenance.json" && has calls.log "hf upload PixelstoMotors/pi05-mecharm-deep-even10-b-lora-v1 train_provenance.json"' check "続きの元のチェックポイント (3999 など) を偽物でも置かない・鍵を出さない" '[ ! -e "$T/data/checkpoints/pi05_mecharm_trim_lora_v1" ] && no_secret' check "見張りは上限 7 時間 + 60 分" '[ "$(jq -r ".watchdog_epoch - .created_epoch" "$TD/lab_pod.json")" = 28800 ]' rm -rf "$T" -run norm_missing --model deep1 -check "deep1: 正規化の値の sha256 が返らない → 学習を始めずログを持ち帰って消す" '[ $RC = 1 ] && has notify.log "正規化の値の sha256 が返らない" && ! has ssh.log "lt=start_train" && [ $(n_call DELETE) = 1 ] && [ -f "$TD/prep_deep.log" ]' +run success --model deep1b --steps 2000 +check "deep1b --steps 2000 → pod_prep_deep.sh 2000 b・由来の decay_steps も 2000 (ステップ数に合わせる. 依頼 135)" '[ $RC = 0 ] && has ssh.log "pod_prep_deep.sh 2000 b 2>&1" && [ "$(jq -r ".config | [.num_train_steps, .lr_schedule.decay_steps] | join(\" \")" "$TD/train_provenance.json")" = "2000 2000" ] && [ -f "$T/data/checkpoints/pi05_mecharm_deep_even10_b_lora_v1/1999/_CHECKPOINT_METADATA" ]' rm -rf "$T" -run norm_fetch_fail --model deep1 -check "deep1: 4b で正規化の値を持ち帰れなくても学習は続け, KIOXIA のチェックポイントの assets から持ち帰る (codex R1)" '[ $RC = 0 ] && has out.txt "正規化の値を持ち帰れなかった" && has out.txt "正規化の値をチェックポイントの assets から持ち帰った" && [ "$(shasum -a 256 "$TD/norm_stats.json" | cut -d" " -f1)" = c1448b9e612668eedc320cc285053fcee62dc9afb40e435c70948441d486b8e2 ] && [ "$(jq -r ".config.norm_stats.lab_copy" "$TD/train_provenance.json")" = "$TD/norm_stats.json" ] && [ "$(jq -r .norm_local "$TD/lab_pod.json")" = "$TD/norm_stats.json" ]' +run success --model deep1b --steps 300 +check "deep1b --steps 300 → decay_steps が warmup 300 以下なので Pod を借りずに終了コード 2" '[ $RC = 2 ] && has out.txt "decay_steps (300) が warmup 300 以下" && [ ! -s "$T/calls.log" ]' rm -rf "$T" -run norm_bad --model deep1 -check "deep1: 正規化の値の中身が Pod の sha256 と違う → 学習のあと KIOXIA に置けたとせず失敗で通知 (codex R1)" '[ $RC = 1 ] && [ ! -e "$TD/norm_stats.json" ] && has notify.log "assets の正規化の値が測り直したもの" && has notify.log "測り直した正規化の値を lab/ に持ち帰れなかった" && [ $(n_call DELETE) = 1 ]' -rm -rf "$T" -PRE='D="$T/data/checkpoints/pi05_mecharm_deep_even10_lora_v1/2999"; while IFS=$(printf "\t") read -r f n; do mkdir -p "$D/$(dirname "$f")"; head -c "$n" /dev/zero > "$D/$f"; done <<< "$(printf "_CHECKPOINT_METADATA\t426\nassets/local/mecharm_redblock_box_deep_even10_v1/norm_stats.json\t2120\nparams/_METADATA\t33052\nparams/d/0123abcd\t1048576\nparams/manifest.ocdbt\t118\n")"; head -c 2120 /dev/zero | tr "\0" x > "$D/assets/local/mecharm_redblock_box_deep_even10_v1/norm_stats.json"' run success --model deep1 +check "deep1 (依頼 133 の decay_steps 5000) は新しくは学習しない → 終了コード 2・deep1b を案内・API を呼ばない (依頼 135)" '[ $RC = 2 ] && has out.txt "--model deep1 は学習し終えたモデル (decay_steps 5000) で, 新しくは学習しません" && has out.txt "--model deep1b" && [ ! -s "$T/calls.log" ]' +rm -rf "$T" +run norm_missing --model deep1b +check "deep1b: 正規化の値の sha256 が返らない → 学習を始めずログを持ち帰って消す" '[ $RC = 1 ] && has notify.log "正規化の値の sha256 が返らない" && ! has ssh.log "lt=start_train" && [ $(n_call DELETE) = 1 ] && [ -f "$TD/prep_deep.log" ]' +rm -rf "$T" +run norm_fetch_fail --model deep1b +check "deep1b: 4b で正規化の値を持ち帰れなくても学習は続け, KIOXIA のチェックポイントの assets から持ち帰る (codex R1)" '[ $RC = 0 ] && has out.txt "正規化の値を持ち帰れなかった" && has out.txt "正規化の値をチェックポイントの assets から持ち帰った" && [ "$(shasum -a 256 "$TD/norm_stats.json" | cut -d" " -f1)" = c1448b9e612668eedc320cc285053fcee62dc9afb40e435c70948441d486b8e2 ] && [ "$(jq -r ".config.norm_stats.lab_copy" "$TD/train_provenance.json")" = "$TD/norm_stats.json" ] && [ "$(jq -r .norm_local "$TD/lab_pod.json")" = "$TD/norm_stats.json" ]' +rm -rf "$T" +run norm_bad --model deep1b +check "deep1b: 正規化の値の中身が Pod の sha256 と違う → 学習のあと KIOXIA に置けたとせず失敗で通知 (codex R1)" '[ $RC = 1 ] && [ ! -e "$TD/norm_stats.json" ] && has notify.log "assets の正規化の値が測り直したもの" && has notify.log "測り直した正規化の値を lab/ に持ち帰れなかった" && [ $(n_call DELETE) = 1 ]' +rm -rf "$T" +PRE='D="$T/data/checkpoints/pi05_mecharm_deep_even10_b_lora_v1/2999"; while IFS=$(printf "\t") read -r f n; do mkdir -p "$D/$(dirname "$f")"; head -c "$n" /dev/zero > "$D/$f"; done <<< "$(printf "_CHECKPOINT_METADATA\t426\nassets/local/mecharm_redblock_box_deep_even10_v1/norm_stats.json\t2120\nparams/_METADATA\t33052\nparams/d/0123abcd\t1048576\nparams/manifest.ocdbt\t118\n")"; head -c 2120 /dev/zero | tr "\0" x > "$D/assets/local/mecharm_redblock_box_deep_even10_v1/norm_stats.json"' +run success --model deep1b PRE="" -check "deep1: KIOXIA に 2999 がもとからあり, 名前・バイト数は同じでも正規化の値の中身が違う → 合ったとせず上書きもしない (codex R1)" '[ $RC = 1 ] && has notify.log "名前・バイト数は Pod 側と同じだが assets の正規化の値が測り直したもの" && [ "$(head -c 1 "$T/data/checkpoints/pi05_mecharm_deep_even10_lora_v1/2999/assets/local/mecharm_redblock_box_deep_even10_v1/norm_stats.json")" = x ]' +check "deep1b: KIOXIA に 2999 がもとからあり, 名前・バイト数は同じでも正規化の値の中身が違う → 合ったとせず上書きもしない (codex R1)" '[ $RC = 1 ] && has notify.log "名前・バイト数は Pod 側と同じだが assets の正規化の値が測り直したもの" && [ "$(head -c 1 "$T/data/checkpoints/pi05_mecharm_deep_even10_b_lora_v1/2999/assets/local/mecharm_redblock_box_deep_even10_v1/norm_stats.json")" = x ]' rm -rf "$T" rm -rf "$T" -run success --model deep1 --init /workspace/ckpt/3999/params -check "deep1 に --init で続きの元を渡す → Pod を借りずに止まる" '[ $RC = 1 ] && has out.txt "π0.5 base から学習する (--init /workspace/ckpt/3999/params は使えない)" && [ $(n_call "POST /pods") = 0 ]' +run success --model deep1b --init /workspace/ckpt/3999/params +check "deep1b に --init で続きの元を渡す → Pod を借りずに止まる" '[ $RC = 1 ] && has out.txt "π0.5 base から学習する (--init /workspace/ckpt/3999/params は使えない)" && [ $(n_call "POST /pods") = 0 ]' rm -rf "$T" T=$(mktemp -d); mkdir -p "$T/data/lab/LogT/train" "$T/fake" jq -n '{id:"dryrunpod0001", kind:"train", model:"deep1", config:"pi05_mecharm_deep_even10_lora", gpu:"NVIDIA A40", costPerHr:0.44, created_epoch:1793449178, created_at:"2026-10-31 21:19:38", exp:"deep_even10_v1", steps:3000, init:"base", train_started_epoch:1793449300, norm_sha256:"c1448b9e612668eedc320cc285053fcee62dc9afb40e435c70948441d486b8e2", norm_pod:"assets/pi05_mecharm_deep_even10_lora/local/mecharm_redblock_box_deep_even10_v1/norm_stats.json", norm_local:""}' > "$T/data/lab/LogT/train/lab_pod.json" echo "NVIDIA A40" > "$T/fake/created" (cd "$HERE" && LAB_DRY_ROOT="$T" ./lab_train.sh --log LogT --dry-run --stop < /dev/null > "$T/out.txt" 2>&1); RC=$? TD="$T/data/lab/LogT/train" -check "--stop (--model なし): 記録の model=deep1 の設定・exp・準備のログでログと一覧を取り, 由来に正規化の値の sha256" '[ $(n_call DELETE) = 1 ] && [ -f "$TD/train_deep_even10_v1.log" ] && [ -f "$TD/prep_deep.log" ] && has ssh.log "cd /workspace/openpi/checkpoints/pi05_mecharm_deep_even10_lora/deep_even10_v1" && [ "$(jq -r .config.data_name "$TD/train_provenance.json")" = mecharm_redblock_box_deep_even10_v1 ] && [ "$(jq -r .config.norm_stats.sha256 "$TD/train_provenance.json")" = c1448b9e612668eedc320cc285053fcee62dc9afb40e435c70948441d486b8e2 ]' +check "--stop (--model なし): 記録の model=deep1 (依頼 133 で始めた学習) の設定・exp・準備のログでログと一覧を取り, 由来に正規化の値の sha256" '[ $(n_call DELETE) = 1 ] && [ -f "$TD/train_deep_even10_v1.log" ] && [ -f "$TD/prep_deep.log" ] && has ssh.log "cd /workspace/openpi/checkpoints/pi05_mecharm_deep_even10_lora/deep_even10_v1" && [ "$(jq -r .config.data_name "$TD/train_provenance.json")" = mecharm_redblock_box_deep_even10_v1 ] && [ "$(jq -r .config.norm_stats.sha256 "$TD/train_provenance.json")" = c1448b9e612668eedc320cc285053fcee62dc9afb40e435c70948441d486b8e2 ]' +check "--stop の deep1: 由来の学習率の予定は decay_steps 5000 (--variant v1. 依頼 135)" '[ "$(jq -r ".config | [.name, .num_train_steps, .lr_schedule.decay_steps, .lr_schedule.source] | join(\" \")" "$TD/train_provenance.json")" = "pi05_mecharm_deep_even10_lora 3000 5000 add_deep_config.py --variant v1" ]' +rm -rf "$T" +T=$(mktemp -d); mkdir -p "$T/data/lab/LogT/train" "$T/fake" +jq -n '{id:"dryrunpod0001", kind:"train", model:"corr3", config:"pi05_mecharm_trim_corr_v5_lora", gpu:"NVIDIA A40", costPerHr:0.44, created_epoch:1793449178, created_at:"2026-10-31 21:19:38", exp:"trim_corr_v5", steps:1500, train_started_epoch:1793449300}' > "$T/data/lab/LogT/train/lab_pod.json" +echo "NVIDIA A40" > "$T/fake/created" +(cd "$HERE" && LAB_DRY_ROOT="$T" ./lab_train.sh --log LogT --dry-run --stop < /dev/null > "$T/out.txt" 2>&1); RC=$? +TD="$T/data/lab/LogT/train" +check "corr3 の由来には学習率の予定を足さない (corr〜corr3 は変えない. 依頼 135)" '[ $(n_call DELETE) = 1 ] && [ "$(jq -r ".config | has(\"lr_schedule\")" "$TD/train_provenance.json")" = false ]' rm -rf "$T" echo "== 13. 見守る途中で止められても Pod を残さない (TERM. Ctrl-C・HUP も同じ扱い)"