diff --git a/lab_lib.sh b/lab_lib.sh index e9a0db7..6e1e082 100644 --- a/lab_lib.sh +++ b/lab_lib.sh @@ -276,13 +276,16 @@ lab_cmd_addon() { # 3f. 何度流しても同じ結果 (install_addon. # 入っている設定と --steps / --init が違うと止まる. 推論では weight_loader は使わない) # 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) -LAB_MODELS="trim corr corr2 corr3" +# 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" LAB_CORR_STEPS=1500 LAB_CORR_INIT=/workspace/ckpt/3999/params LAB_CORR2_STEPS=1500 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 case "$1" in trim) @@ -309,25 +312,35 @@ lab_model_def() { # lab_model_def trim|corr → MODEL_REPO MODEL_CKPT MODEL_CONFIG=pi05_mecharm_trim_corr_v5_lora MODEL_DATA=mecharm_redblock_box_teach_v1_trim2_corr_v5 # 依頼 97: corr_v4b の 7 本 + 11-06 のゲームパッドの 4 本 MODEL_DESC="corr2 の 1499 から trim2_corr_v5 で続きを学習した 1499 (依頼 97)" ;; + deep1) + MODEL_REPO=PixelstoMotors/pi05-mecharm-deep-even10-lora-v1 + 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)" ;; *) return 1 ;; esac } -lab_cmd_addon_model() { # lab_cmd_addon_model trim|corr|corr2|corr3 → 3f (trim は lab_cmd_addon と同じ. corr〜corr3 は学習の設定と assets も足す) +lab_cmd_addon_model() { # lab_cmd_addon_model trim|corr|corr2|corr3|deep1 → 3f (trim は lab_cmd_addon と同じ. corr〜deep1 は学習の設定と assets も足す) lab_model_def "$1" || return 1 if [ "$1" = trim ]; then lab_cmd_addon; return; fi - local corr_args="--steps $LAB_CORR_STEPS --init $LAB_CORR_INIT" - [ "$1" = corr2 ] && corr_args="--variant v4 --steps $LAB_CORR2_STEPS --init $LAB_CORR2_INIT" - [ "$1" = corr3 ] && corr_args="--variant v5 --steps $LAB_CORR3_STEPS --init $LAB_CORR3_INIT" + 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 # assets は hf download が置いたモデルの assets/local/<データ名> を, openpi の assets/<設定>/local/ に写す - # (trim の 3f が assets/pi05_mecharm_trim_lora/local/ に写すのと同じ形. 中身は trim2 と同じ正規化の値) - echo "cd /workspace/openpi && python3 /workspace/r2r2r/pi05/openpi_addon/install_addon.py /workspace/openpi && python3 /workspace/r2r2r/add_trim_config.py /workspace/openpi && python3 /workspace/r2r2r/pi05/openpi_addon/add_trim_corr_config.py /workspace/openpi $corr_args && mkdir -p assets/$MODEL_CONFIG/local && cp -r $MODEL_CKPT/assets/local/$MODEL_DATA assets/$MODEL_CONFIG/local/" + # (trim の 3f が assets/pi05_mecharm_trim_lora/local/ に写すのと同じ形. corr〜corr3 は trim2 と同じ正規化の値, + # deep1 は deep_even10_v1 で測り直した値) + echo "cd /workspace/openpi && python3 /workspace/r2r2r/pi05/openpi_addon/install_addon.py /workspace/openpi && python3 /workspace/r2r2r/add_trim_config.py /workspace/openpi && python3 /workspace/r2r2r/pi05/openpi_addon/$add && mkdir -p assets/$MODEL_CONFIG/local && cp -r $MODEL_CKPT/assets/local/$MODEL_DATA assets/$MODEL_CONFIG/local/" } -# ---- lab_train.sh で作るモデル (依頼 57. --model corr | corr2. 依頼 97 で corr3) ---- +# ---- lab_train.sh で作るモデル (依頼 57. --model corr | corr2. 依頼 97 で corr3. 依頼 133 で deep1) ---- # 作るモデルの名前は lab_start.sh の --model と同じ. 続きの元 (TRAIN_FROM) も lab_model_def のモデル -LAB_TRAIN_MODELS="corr corr2 corr3" -lab_train_def() { # lab_train_def corr|corr2|corr3 → TRAIN_* (知らない名前なら 1) +# 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) + TRAIN_STEPS=1500; TRAIN_PREP=pod_prep_trim_corr.sh # ステップ数の既定・4b で流す準備 (依頼 133 で deep1 だけ違う) case "$1" in corr) # 依頼 49: 3999 から trim2_corr_v3 で続きを学習した 1499 TRAIN_FROM=trim; TRAIN_VARIANT=v3 @@ -338,20 +351,29 @@ lab_train_def() { # lab_train_def corr|corr2|corr3 → 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 + TRAIN_FROM_KIOXIA="" + TRAIN_STEPS=3000; 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 + TRAIN_REPO_IN=""; TRAIN_CKPT_IN=""; TRAIN_INIT=base + return 0 + fi lab_model_def "$TRAIN_FROM" || return 1 TRAIN_REPO_IN=$MODEL_REPO; TRAIN_CKPT_IN=$MODEL_CKPT; TRAIN_INIT=$MODEL_CKPT/params lab_model_def "$1" # MODEL_* は作るモデルのものに戻す } -lab_cmd_addon_train() { # lab_cmd_addon_train corr|corr2|corr3 → lab_train.sh の 3f - # corr は lab_cmd_addon と同じ (3999 を取っているので trim の assets も写す). corr2・corr3 は 3999 を取らないので - # addon と trim2 の設定だけを入れる (学習の設定と正規化の値は pod_prep_trim_corr.sh が置く) +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 を取らないので + # 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 41ec59b..6b33165 100755 --- a/lab_start.sh +++ b/lab_start.sh @@ -15,6 +15,9 @@ # 依頼 57. lab_train.sh --model corr2 で作る. Pod の /workspace/ckpt/corr_v2_1499) # corr3 = corr2 の 1499 から trim2_corr_v5 で続きを学習した 1499 (PixelstoMotors/pi05-mecharm-trim-corr-lora-v3. # 依頼 97. lab_train.sh --model corr3 で作る. Pod の /workspace/ckpt/corr_v3_1499) +# 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° 超える) diff --git a/lab_train.sh b/lab_train.sh index 5e5efe3..d09309c 100755 --- a/lab_train.sh +++ b/lab_train.sh @@ -15,7 +15,12 @@ # (設定 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) -# --steps / --init / --exp-name 学習 (既定 1500 / 続きの元の params / trim_corr_v3・trim_corr_v4・trim_corr_v5) +# deep1 = π0.5 base から deep_even10_v1 (位置 1〜5 を各 10 本の 50 本) で新しく学習 +# → PixelstoMotors/pi05-mecharm-deep-even10-lora-v1 (依頼 133. 設定 pi05_mecharm_deep_even10_lora. +# 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) # 続きの元: corr は /workspace/ckpt/3999/params, corr2 は /workspace/ckpt/corr_v1_1499/params, # corr3 は /workspace/ckpt/corr_v2_1499/params # --poll-min 進みを見る間 (既定 5 分) @@ -24,7 +29,7 @@ # 1. Pod を作る (名前 p2m-train-. lab_start.sh の p2m- とは別). 記録は lab//train/lab_pod.json # 2. SSH が通るまで待ち, Pod 側に上限の見張りを置く (上限 + 60 分で, Runpod が Pod に入れる Pod 用の鍵で自分を消す) # 3. 準備 (lab_start.sh 3a〜3f と同じ. サーバーは起動しない) -# 4. tar を /workspace/data/ へ送り, pod_prep_trim_corr.sh +# 4. tar を /workspace/data/ へ送り, pod_prep_trim_corr.sh (deep1 は pod_prep_deep.sh ) # 5. 学習を setsid nohup で始める (Mac とのつながりが切れても止まらない). ログ /workspace/logs/train_.log, # GPU の様子は 30 秒おきに /workspace/logs/gpu_train.csv # 6. 数分おきにステップ・loss を出す @@ -49,7 +54,7 @@ ORIG_ARGS=("$@") # 由来に残す (依頼 54) # shellcheck source=lab_lib.sh . "$HERE/lab_lib.sh" -GPU=A40; FALLBACK=1; LIMIT_HOURS=5; STEPS=1500; INIT=""; EXP=""; MODEL=corr # INIT・EXP の既定は --model から +GPU=A40; FALLBACK=1; LIMIT_HOURS=5; STEPS=""; INIT=""; EXP=""; MODEL=corr # STEPS・INIT・EXP の既定は --model から POLL_MIN=5; STALL_MIN=30; LOG=""; DRY_RUN=0; MODE=train while [ $# -gt 0 ]; do case "$1" in @@ -72,6 +77,7 @@ 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; } [ -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 @@ -95,17 +101,19 @@ if [ $DRY_RUN = 1 ]; then fi CKPT_ROOT="$DATA_ROOT/checkpoints" ADD_TRIM="$DATA_ROOT/lab/Log11/pod/add_trim_config.py" -use_model() { # use_model corr|corr2|corr3 → 作るモデルで決まる値 (lab_lib.sh の lab_train_def. 依頼 57 / 97) +use_model() { # use_model corr|corr2|corr3|deep1 → 作るモデルで決まる値 (lab_lib.sh の lab_train_def. 依頼 57 / 97 / 133) lab_train_def "$1" || return 1 DATA_NAME=$TRAIN_DATA # corr: ..._trim2_corr_v3 / corr2: ..._trim2_corr_v4b (依頼 58) / corr3: ..._trim2_corr_v5 TAR="$DATA_ROOT/datasets/$DATA_NAME/${DATA_NAME}_raw_episodes.tar" CONFIG=$TRAIN_CONFIG # corr: pi05_mecharm_trim_corr_lora / corr2: ..._v4_lora / corr3: ..._v5_lora - HF_REPO_IN=$TRAIN_REPO_IN # 続きの元 (corr: 3999 / corr2: corr の 1499 / corr3: corr2 の 1499. lab_start.sh と同じ) + HF_REPO_IN=$TRAIN_REPO_IN # 続きの元 (corr: 3999 / corr2: corr の 1499 / corr3: corr2 の 1499. lab_start.sh と同じ. deep1 は空) HF_REPO_OUT=$TRAIN_REPO_OUT # 上げる先 (非公開). 名前は checkpoints/ のフォルダ名に合わせる CKPT_NAME=$TRAIN_CKPT_NAME # KIOXIA の checkpoints/<これ>/<ステップ>/ - CKPT_IN=$TRAIN_CKPT_IN # 3c で続きの元を置く場所 - CKPT_IN_KIOXIA="$CKPT_ROOT/$TRAIN_FROM_KIOXIA" # HF_REPO_IN と同じもの (由来の指紋を取る. 依頼 54) + 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_SH=$TRAIN_PREP # 4b で流す準備 (deep1 は pod_prep_deep.sh. 依頼 133) } use_model "$MODEL" POD_EXP_DIR=/workspace/openpi/checkpoints/$CONFIG/$EXP @@ -114,6 +122,9 @@ GPU_LOG=/workspace/logs/gpu_train.csv RUN_LOG=/workspace/logs/train_run.log EXIT_FILE=/workspace/logs/train_exit_code PREP_LOG=/workspace/logs/prep_trim_corr.log +[ "$PREP_SH" = pod_prep_deep.sh ] && PREP_LOG=/workspace/logs/prep_deep.log +NORM_SHA=""; NORM_POD=""; NORM_LOCAL="" # deep1: Pod で測り直した正規化の値 (sha256・Pod の openpi からの相対・持ち帰った先. 依頼 133) +TAR_SHA="" # deep1: 送る tar の sha256 (由来. 依頼 133) LAST_STEP=$((STEPS - 1)) # openpi は 0 から数え, 最後のステップ (num_train_steps - 1) を必ず保存する # ---- 書く前に KIOXIA のマウントを確かめる ---- @@ -158,17 +169,22 @@ PROV_PROCESS=""; PROV_ERROR=""; PROV_CKPT_DIR=""; PROV_HF=0; PROV_DONE=0 OPENPI_JSON='[]' # 3f のあとに Pod の openpi のコミットと変更の有無を入れる iso_of() { [ -n "${1:-}" ] && [ "$1" != 0 ] && date -r "$1" '+%Y-%m-%dT%H:%M:%S%z' | sed -E 's/([0-9]{2})$/:\1/'; } prov_inputs() { # 入力 (学習データ・続きの元 (corr は 3999・corr2 は corr の 1499・corr3 は corr2 の 1499)・設定を足すスクリプト) を spec の inputs の形で出す + # deep1 (依頼 133): 続きの元は π0.5 base (gs://. KIOXIA に原本がないので指紋はない). データには tar の sha256 も付ける local init_ck="" - case "$INIT" in "$CKPT_IN"/*) init_ck="$CKPT_IN_KIOXIA" ;; esac + [ -n "$CKPT_IN" ] && case "$INIT" in "$CKPT_IN"/*) init_ck="$CKPT_IN_KIOXIA" ;; esac jq -n --arg data "$DATA_NAME" --arg data_dir "$DATA_ROOT/datasets/$DATA_NAME" \ --arg init "$INIT" --arg init_ck "$init_ck" --arg init_id "${CKPT_IN_KIOXIA#"$CKPT_ROOT"/}" --arg repo_in "$HF_REPO_IN" \ - --arg add_trim "$ADD_TRIM" \ - '{inputs: ([{kind: "dataset", id: $data, path: $data_dir}] - + [{kind: "checkpoint", id: (if $init_ck == "" then (if $init == "" then null else $init end) else $init_id end), + --arg add_trim "$ADD_TRIM" --arg tar "${TAR#"$DATA_ROOT"/}" --arg tar_sha "$TAR_SHA" --arg base "$PI05_BASE" \ + '{inputs: ([{kind: "dataset", id: $data, path: $data_dir} + + (if $tar_sha == "" then {} else {tar: {path: $tar, sha256: $tar_sha}} end)] + + (if $init == "base" then [{kind: "checkpoint", id: "pi05_base", path: null, hf_repo: null, pod_path: null, url: $base, + note: "π0.5 base から新しく学習 (続きの学習ではない)"}] + else [{kind: "checkpoint", id: (if $init_ck == "" then (if $init == "" then null else $init end) else $init_id end), path: (if $init_ck == "" then null else $init_ck end), - hf_repo: (if $init_ck == "" then null else $repo_in end), pod_path: (if $init == "" then null else $init end)}] + hf_repo: (if $init_ck == "" then null else $repo_in end), pod_path: (if $init == "" then null else $init end)}] end) + [{kind: "config", id: "add_trim_config.py", path: $add_trim}])}' } +PI05_BASE=gs://openpi-assets/checkpoints/pi05_base/params # --init base の重み (install_addon.py の weight_loader と同じ) PROV_CODE_ARGS=(--repo-dir "${LAB_PROV_REPO:-$HERE}" --code-path lab_train.sh --code-path lab_lib.sh --code-path provenance.py --code-path pi05) prov_prepare() { # 始める前にコードと入力の指紋を測って PROV_START に持つ (終わったときに測ると, 途中で変えたものになる) local out @@ -199,7 +215,7 @@ prov_spec() { # 由来の spec (lab_train.sh が知っているこ --arg hf "${HF_MSG:-}" --arg kx "${KIOXIA_MSG:-}" --arg prov_hf "$PROV_HF" \ --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 cost_line "${COST_LINE:-}" --arg norm_sha "$NORM_SHA" --arg norm_pod "$NORM_POD" --arg norm_local "$NORM_LOCAL" \ '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, @@ -207,8 +223,10 @@ prov_spec() { # 由来の spec (lab_train.sh が知っているこ code: ($start.code // null), outputs: (if $ck_dir == "" then [] else [{kind: "checkpoint", id: ($ckpt_name + "/" + $step), path: $ck_dir, hf_repo: (if $prov_hf == "1" then $repo_out else null end)}] end), # 指紋 files-v2 は由来のファイルを数えない (依頼 56) - 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)}, + 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)), 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), @@ -465,12 +483,34 @@ compare_with_pod() { # compare_with_pod フォルダ → Pod 側の一覧 head -20 <<<"$d" | sed 's/^/ /' return 1 } +norm_ok() { # norm_ok チェックポイントのフォルダ → deep1 でなければ 0. assets の正規化の値が Pod で測り直したもの (sha256) と同じなら 0 (依頼 133) + [ -z "$NORM_SHA" ] && return 0 + local nf="$1/assets/local/$DATA_NAME/norm_stats.json" + [ -f "$nf" ] && [ "$(shasum -a 256 "$nf" | cut -d' ' -f1)" = "$NORM_SHA" ] +} +norm_from_ckpt() { # deep1: 4b で持ち帰れなかった正規化の値を, KIOXIA に置いたチェックポイントの assets から lab//train/ に写す (codex R1) + [ -n "$NORM_SHA" ] && [ -z "$NORM_LOCAL" ] || return 0 + local d="$CKPT_ROOT/$CKPT_NAME/${CKPT_STEP:-}" f + if [ -n "${CKPT_STEP:-}" ] && norm_ok "$d" && f=$(dest_of norm_stats.json) && cp "$d/assets/local/$DATA_NAME/norm_stats.json" "$f" \ + && [ "$(shasum -a 256 "$f" | cut -d' ' -f1)" = "$NORM_SHA" ]; then + NORM_LOCAL=$f + echo "[NORM ] 正規化の値をチェックポイントの assets から持ち帰った → $NORM_LOCAL" + save_state '. + {norm_local:$l}' --arg l "$NORM_LOCAL" + return 0 + fi + echo "[NORM ] 正規化の値を lab/ に持ち帰れなかった" + return 1 +} place_ckpt() { # place_ckpt hf|scp → KIOXIA の checkpoints//<ステップ>/ に置いて確かめる. KIOXIA_MSG に結果 local how="$1" parent="$CKPT_ROOT/$CKPT_NAME" dest tmp n bytes dest="$parent/$CKPT_STEP" if [ ! -d "$DATA_ROOT" ]; then KIOXIA_MSG="KIOXIA がマウントされていない (置けなかった)"; return 1; fi if [ -e "$dest" ] || [ -L "$dest" ]; then # 前からあるものは上書き・移動・削除しない. 中身が同じかだけを見る - if compare_with_pod "$dest"; then KIOXIA_MSG="$dest はもとからあり, Pod 側と全部同じ (書き換えていない)"; return 0; fi + if compare_with_pod "$dest"; then + # deep1: 名前・バイト数が同じでも, 正規化の値の中身が違えば合ったとしない (codex R1) + norm_ok "$dest" || { KIOXIA_MSG="$dest はもとからあり, 名前・バイト数は Pod 側と同じだが assets の正規化の値が測り直したもの (sha256 ${NORM_SHA:0:12}…) と違う・ない. 上書きしなかった"; return 1; } + KIOXIA_MSG="$dest はもとからあり, Pod 側と全部同じ (書き換えていない)"; return 0 + fi KIOXIA_MSG="$dest がもとからあり, Pod 側と違う. 上書きしなかった"; return 1 fi mkdir -p "$parent" || { KIOXIA_MSG="$parent を作れない"; return 1; } @@ -499,6 +539,10 @@ place_ckpt() { # place_ckpt hf|scp → KIOXIA の checkpoints/&1 | tee $PREP_LOG" \ - || wrap_fail prep "pod_prep_trim_corr.sh に失敗 (上の出力を見る)" +if [ "$PREP_SH" = pod_prep_deep.sh ]; then # deep1 (依頼 133): 正規化の値を Pod で測り直す + stage "4b. pod_prep_deep.sh $STEPS (変換・正規化の値を測り直す)" + # 出力は 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" \ + || 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 が返らない" + NORM_SHA=$(sed -E 's/^NORM sha256=([0-9a-f]{64}) .*/\1/' <<<"$nl"); NORM_POD=${nl##* path=} + # 測り直した値を学習の前に持ち帰る (学習が落ちても残る). 持ち帰ったものの sha256 が Pod と同じかを確かめる. + # 持ち帰れなければ, 学習のあとにチェックポイントの assets から取り直す (norm_from_ckpt. codex R1) + NORM_LOCAL=$(dest_of norm_stats.json) + for _ in 1 2; do + if pod_scp "root@$POD_IP:/workspace/openpi/$NORM_POD" "$NORM_LOCAL" >/dev/null \ + && [ "$(shasum -a 256 "$NORM_LOCAL" | cut -d' ' -f1)" = "$NORM_SHA" ]; then + echo "[NORM ] 正規化の値 (sha256 ${NORM_SHA:0:12}…) を持ち帰った → $NORM_LOCAL"; break + fi + rm -f "$NORM_LOCAL" + done + if [ ! -f "$NORM_LOCAL" ]; then + echo "[WARN ] 正規化の値を持ち帰れなかった (または sha256 が違う). 学習のあとチェックポイントの assets から取り直す" + NORM_LOCAL="" + fi + save_state '. + {norm_sha256:$s, norm_pod:$p, norm_local:$l}' --arg s "$NORM_SHA" --arg p "$NORM_POD" --arg l "$NORM_LOCAL" +else + stage "4b. pod_prep_trim_corr.sh $STEPS $INIT $PREP_VARIANT" + LAB_TO=$LAB_TO_LONG pssh "set -o pipefail; bash /workspace/r2r2r/pi05/openpi_addon/pod_prep_trim_corr.sh $STEPS $INIT $PREP_VARIANT 2>&1 | tee $PREP_LOG" \ + || wrap_fail prep "pod_prep_trim_corr.sh に失敗 (上の出力を見る)" +fi stage_done # ---- 5. 学習を始める ---- diff --git a/lab_train_fakes.sh b/lab_train_fakes.sh index 8b8e861..7b86d96 100644 --- a/lab_train_fakes.sh +++ b/lab_train_fakes.sh @@ -28,6 +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) LAB_DRY_ROOT=${LAB_DRY_ROOT:-$(mktemp -d /tmp/lab_train_dry.XXXXXX)} LAB_FAKE_SCENARIO=${LAB_FAKE_SCENARIO:-success} @@ -37,6 +40,7 @@ if [ -d /Volumes/KIOXIA/r2r2r-data ]; then for f in "/Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2_corr_v3/mecharm_redblock_box_teach_v1_trim2_corr_v3_raw_episodes.tar" \ "/Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2_corr_v4b/mecharm_redblock_box_teach_v1_trim2_corr_v4b_raw_episodes.tar" \ "/Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2_corr_v5/mecharm_redblock_box_teach_v1_trim2_corr_v5_raw_episodes.tar" \ + "/Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_deep_even10_v1/mecharm_redblock_box_deep_even10_v1_raw_episodes.tar" \ /Volumes/KIOXIA/r2r2r-data/lab/Log11/pod/add_trim_config.py "$RUNPOD_KEY_FILE" "$HF_TOKEN_FILE"; do echo "[DRY ] 本物の $(basename "$f"): $([ -s "$f" ] && echo ある || echo ない)" done @@ -48,7 +52,7 @@ RUNPOD_KEY_FILE="$LAB_DRY_ROOT/keys/runpod_api_key.txt"; HF_TOKEN_FILE="$LAB_DRY POD_SSH_KEY="$LAB_DRY_ROOT/keys/id_ed25519" mkdir -p "$LAB_DRY_ROOT/keys" "$LAB_DRY_ROOT/fake" "$DATA_ROOT/lab/Log11/pod" \ "$DATA_ROOT/datasets/mecharm_redblock_box_teach_v1_trim2_corr_v3" "$DATA_ROOT/datasets/mecharm_redblock_box_teach_v1_trim2_corr_v4b" \ - "$DATA_ROOT/datasets/mecharm_redblock_box_teach_v1_trim2_corr_v5" + "$DATA_ROOT/datasets/mecharm_redblock_box_teach_v1_trim2_corr_v5" "$DATA_ROOT/datasets/mecharm_redblock_box_deep_even10_v1" [ -s "$RUNPOD_KEY_FILE" ] || echo "DRY-RUNPOD-SECRET-KEY" > "$RUNPOD_KEY_FILE" [ -s "$HF_TOKEN_FILE" ] || echo "hf_DRYSECRETTOKEN" > "$HF_TOKEN_FILE" [ -s "$POD_SSH_KEY" ] || echo "dry-ssh-key" > "$POD_SSH_KEY" @@ -58,10 +62,16 @@ for v in v3 v4b v5; do # --model corr は corr_v3, corr2 は corr_v4b (依 TARF="$DATA_ROOT/datasets/mecharm_redblock_box_teach_v1_trim2_corr_$v/mecharm_redblock_box_teach_v1_trim2_corr_${v}_raw_episodes.tar" [ -f "$TARF" ] || echo "dry tar $v" > "$TARF" done +TARF="$DATA_ROOT/datasets/mecharm_redblock_box_deep_even10_v1/mecharm_redblock_box_deep_even10_v1_raw_episodes.tar" # deep1 (依頼 133) +[ -f "$TARF" ] || echo "dry tar deep_even10_v1" > "$TARF" # 続きの元 (由来の指紋を取る. 依頼 54): corr は 3999, corr2 は corr の 1499 (依頼 57. lab_train_def が決めた選んだモデルのものだけ. # corr の 1499 を置くと, corr の学習で置く先と重なる) -FAKE_INIT="$DATA_ROOT/checkpoints/${TRAIN_FROM_KIOXIA:-pi05_mecharm_trim_lora_v1/3999}" -[ -e "$FAKE_INIT" ] || { mkdir -p "$FAKE_INIT/params"; echo '{"dry": 3999}' > "$FAKE_INIT/_CHECKPOINT_METADATA"; head -c 100 /dev/zero > "$FAKE_INIT/params/x"; } +# deep1 (依頼 133) は続きの元がない (π0.5 base から) ので置かない +if [ "${TRAIN_INIT:-}" != base ]; then + FAKE_INIT="$DATA_ROOT/checkpoints/${TRAIN_FROM_KIOXIA:-pi05_mecharm_trim_lora_v1/3999}" + [ -e "$FAKE_INIT" ] || { mkdir -p "$FAKE_INIT/params"; echo '{"dry": 3999}' > "$FAKE_INIT/_CHECKPOINT_METADATA"; head -c 100 /dev/zero > "$FAKE_INIT/params/x"; } +fi +FAKE_NORM_SHA=c1448b9e612668eedc320cc285053fcee62dc9afb40e435c70948441d486b8e2 # 0 が 2120 バイト (fake_ckpt_files の norm_stats.json と同じ) touch "$LAB_DRY_ROOT/calls.log" "$LAB_DRY_ROOT/ssh.log" "$LAB_DRY_ROOT/scp.log" CREATE_WAIT=0; SSH_WAIT=0; LOGIN_WAIT=0; GONE_WAIT=0; FETCH_WAIT=0 @@ -123,15 +133,15 @@ pod_ssh() { printf '%s\n----\n' "$*" >> "$LAB_DRY_ROOT/ssh.log" cat > /dev/null [ -f "$LAB_DRY_ROOT/fake/deleted" ] && return 255 - local n + local n done_line="STATUS run=0 exit=0 age=4 bar=${STEPS:-1500} logstep=$(( ${STEPS:-1500} - 100 )) loss=0.0215 ckpt=$(( ${STEPS:-1500} - 1 )) cm=1" case "$*" in *": lt=watchdog"*) [ "$LAB_FAKE_SCENARIO" = wd_missing ] && echo "WATCHDOG run=1 pod=1 key=0 ctl=0" || echo "WATCHDOG run=1 pod=1 key=1 ctl=1" ;; *": lt=start_train"*) [ "$LAB_FAKE_SCENARIO" = late_start ] && sleep 9; echo STARTED ;; *": lt=status"*) n=$(fake_n status) - if [ "$LAB_FAKE_SCENARIO" = late_start ]; then echo "STATUS run=0 exit=0 age=4 bar=1500 logstep=1400 loss=0.0215 ckpt=1499 cm=1"; return 0; fi + if [ "$LAB_FAKE_SCENARIO" = late_start ]; then echo "$done_line"; return 0; fi if [ "$LAB_FAKE_SCENARIO" = late_finish ]; then # 確かめている間に上限を過ぎ, 学習も終わる - sleep 9; echo "STATUS run=0 exit=0 age=4 bar=1500 logstep=1400 loss=0.0215 ckpt=1499 cm=1"; return 0 + sleep 9; echo "$done_line"; return 0 fi case "$LAB_FAKE_SCENARIO:$n" in crash:1|killed:1|stall:1|timeout:*|*:1) @@ -139,7 +149,7 @@ pod_ssh() { else echo "STATUS run=1 exit=- age=3 bar=$((n * 100)) logstep=$((n * 100)) loss=0.0412 ckpt=- cm=0"; fi ;; crash:*) echo "STATUS run=0 exit=1 age=5 bar=650 logstep=600 loss=0.0399 ckpt=- cm=0" ;; killed:*) echo "STATUS run=0 exit=- age=5 bar=650 logstep=600 loss=0.0399 ckpt=- cm=0" ;; - *) echo "STATUS run=0 exit=0 age=4 bar=1500 logstep=1400 loss=0.0215 ckpt=1499 cm=1" ;; + *) echo "$done_line" ;; esac ;; *": lt=list_ckpt"*|*": lt=ls_all"*) if [ "$LAB_FAKE_SCENARIO" = list_partial ] && [[ "$*" == *"lt=list_ckpt"* ]]; then @@ -154,6 +164,10 @@ pod_ssh() { [ "$LAB_FAKE_SCENARIO" = hf_same ] && { echo "HFUP same PixelstoMotors/pi05-mecharm-trim-corr-lora-v1 にはもう同じ中身がある (5 ファイル). 上げ直さない"; return 0; } echo "HFUP ok PixelstoMotors/pi05-mecharm-trim-corr-lora-v1 に 5 ファイルを上げ, 名前・バイト数が全部合うのを確かめた" ;; *"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" + echo "== 5. 準備できました (ドライラン)" ;; *": lt=openpi_rev"*) [ "$LAB_FAKE_SCENARIO" = openpi_unknown ] && { echo "fatal: not a git repository"; return 128; } echo "OPENPI 215abfb1234567890abcdef1234567890abcdef0" @@ -171,6 +185,9 @@ pod_scp() { case "$*" in *":/workspace/openpi/checkpoints/"*) fake_make_ckpt "$last" ;; *":/workspace/logs/watchdog.log"*) return 1 ;; # 見張りが働いていなければ, ないのがふつう + *":/workspace/openpi/assets/"*"/norm_stats.json "*) # deep1: 測り直した正規化の値 (依頼 133) + [ "$LAB_FAKE_SCENARIO" = norm_fetch_fail ] && return 1 + head -c 2120 /dev/zero > "$last" ;; *" root@"*":/workspace/logs/"*|"root@"*":/workspace/logs/"*) echo "dry log line (${1##*/})" > "$last" ;; esac return 0 diff --git a/pi05/openpi_addon/add_deep_config.py b/pi05/openpi_addon/add_deep_config.py new file mode 100755 index 0000000..f3409c1 --- /dev/null +++ b/pi05/openpi_addon/add_deep_config.py @@ -0,0 +1,80 @@ +#!/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). + +π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 + repo_id local/mecharm_redblock_box_teach_v1_trim2 -> local/mecharm_redblock_box_deep_even10_v1 + num_train_steps 4_000 -> --steps (依頼 133 は 3000) +正規化の値は写さず, 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 が違うときは止まる. +""" +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) + + +def make_block(s, steps): + 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)] + reps = [ + (TRIM_A, MARK_A), + (TRIM_B, MARK_B), + ('name="pi05_mecharm_trim_lora"', f'name="{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},"), + ] + for a, b in reps: + assert block.count(a) == 1, f"見つからない/重複: {a!r}" + block = block.replace(a, b) + for x in SAME_AS_TRIM: + assert block.count(x) == 1, f"trim の設定に見つからない: {x!r}" + return block + + +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)") + args = ap.parse_args(argv) + if args.steps <= 0: + sys.exit("[ERROR] --steps は 1 以上") + 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)] + 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)): + 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 してから入れ直す") + 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) + 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)") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/pi05/openpi_addon/pod_prep_deep.sh b/pi05/openpi_addon/pod_prep_deep.sh new file mode 100755 index 0000000..41048a6 --- /dev/null +++ b/pi05/openpi_addon/pod_prep_deep.sh @@ -0,0 +1,67 @@ +#!/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 3a〜3f で Pod を準備してある (openpi・install_addon.py・add_trim_config.py). 続きの元のモデルは取らない +# (π0.5 base から. 重みは学習の最初に openpi が gs://openpi-assets から取る) +# Mac から次の 2 つを送ってある: +# /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 +# +# すること (何度流しても同じ結果): +# 1. tar を /workspace/data/<データ名>/ に広げ, エピソードの本数を確かめる (Mac で作ったときは 50 本) +# 2. openpi の config.py に pi05_mecharm_deep_even10_lora を足す (--steps) +# 3. LeRobot 形式に変換 (repo_id local/<データ名>) +# 4. 正規化の値をこの 50 本で測り直す (RUNPOD_TRAIN.md 4. の compute_norm_stats.py をこの設定で). +# 置く場所は openpi が学習で読む assets/<設定の名前>/local/<データ名>/norm_stats.json. +# 最後に「NORM sha256=<64 桁> path=」の1行を出す (lab_train.sh が由来に残し, 持ち帰る) +# 5. 学習を始めるコマンドを出す (流さない) +set -euo pipefail + +STEPS="${1:?学習のステップ数 (例 3000)}" +NAME=mecharm_redblock_box_deep_even10_v1 +CONFIG=pi05_mecharm_deep_even10_lora +EXP=deep_even10_v1 +N_MAC=50 +TAR=/workspace/data/${NAME}_raw_episodes.tar +DATA=/workspace/data/$NAME +OPENPI=/workspace/openpi +ADDON=/workspace/r2r2r/pi05/openpi_addon +NORM_DST=assets/$CONFIG/local/$NAME/norm_stats.json # $OPENPI からの相対 + +[ -f "$TAR" ] || { echo "[ERROR] tar がない: $TAR" >&2; exit 1; } +# shellcheck disable=SC1091 +[ -f "$HOME/.local/bin/env" ] && source "$HOME/.local/bin/env" + +echo "== 1. tar を広げる → $DATA" +# 前に広げたものへ上書きしない (pod_prep_trim_corr.sh と同じ. 空の一時フォルダに広げて確かめてから入れ替える) +TMP="$DATA.tmp.$$" +rm -rf "$TMP" +mkdir -p "$TMP" +tar xf "$TAR" -C "$TMP" +N=$(find "$TMP/raw_episodes" -mindepth 2 -maxdepth 2 -name episode.npz | wc -l | tr -d ' ') +[ "$N" = "$N_MAC" ] || { echo "[ERROR] tar のエピソードが $N 本 (Mac で作ったときは $N_MAC 本): $TAR" >&2; rm -rf "$TMP"; exit 1; } +rm -rf "$DATA" +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 "== 3. LeRobot 形式に変換 (local/$NAME)" +uv run python /workspace/r2r2r/pi05/convert_to_lerobot.py --raw-dir "$DATA/raw_episodes" --repo-id "local/$NAME" + +echo "== 4. 正規化の値をこの $N 本で測り直す (compute_norm_stats.py --config-name $CONFIG)" +rm -f "$NORM_DST" # 前の回の値を残さない (測れなかったときに古い値で学習しない) +uv run scripts/compute_norm_stats.py --config-name "$CONFIG" +[ -s "$NORM_DST" ] || { echo "[ERROR] 正規化の値ができていない: $OPENPI/$NORM_DST" >&2; exit 1; } +SHA=$(sha256sum "$NORM_DST" | cut -d' ' -f1) +echo " $OPENPI/$NORM_DST (sha256 ${SHA:0:12}…)" +echo "NORM sha256=$SHA path=$NORM_DST" + +echo "== 5. 準備できました. 学習はこのコマンドで始める (このスクリプトでは流さない):" +echo " cd $OPENPI && XLA_PYTHON_CLIENT_MEM_FRACTION=0.9 uv run scripts/train.py $CONFIG --exp-name=$EXP --overwrite 2>&1 | tee /workspace/logs/train_$EXP.log" diff --git a/pi05/test_merge_and_config.py b/pi05/test_merge_and_config.py index eb4d17d..31cc80c 100644 --- a/pi05/test_merge_and_config.py +++ b/pi05/test_merge_and_config.py @@ -36,6 +36,7 @@ sys.path.insert(0, str(_HERE)) sys.path.insert(0, str(_HERE / "openpi_addon")) import add_trim_corr_config as atc # noqa: E402 +import add_deep_config as adc # noqa: E402 import build_correction_episodes as bce # noqa: E402 import merge_raw_episodes as mre # noqa: E402 @@ -333,6 +334,48 @@ class TestConfigV5(unittest.TestCase): self.assertEqual(cfg.read_text(), text) +class TestConfigDeep(unittest.TestCase): + """依頼 133: deep_even10_v1 の設定は trim と同じで, 名前・データ名・ステップ数だけ違う. π0.5 base から.""" + 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) + 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["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(cfg.read_text(), text) + with self.assertRaises(SystemExit): + adc.main([td, "--steps", "2000"]) # 違うステップ数では止まる + self.assertEqual(cfg.read_text(), text) + + def test_requires_trim_first(self): + with tempfile.TemporaryDirectory() as td: + 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"]) + + 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", + "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"'): + self.assertIn(x, code) + + class TestPodPrep(unittest.TestCase): """依頼 48: pod_prep_trim_corr.sh は正規化の値を計算し直さず, 3999 の学習の trim2 の値を置く.""" SH = _HERE / "openpi_addon" / "pod_prep_trim_corr.sh" diff --git a/test_lab_start.sh b/test_lab_start.sh index c481953..394bc5c 100755 --- a/test_lab_start.sh +++ b/test_lab_start.sh @@ -412,13 +412,27 @@ check "corr2 のサーバーが動いている Pod に --model corr3 → 止ま rm -rf "$T" check "フォルダからモデルの名前 (corr3)" '[ "$(. "$HERE/lab_lib.sh"; lab_model_of_dir /workspace/ckpt/corr_v3_1499)" = corr3 ]' +echo "== 34d. --model deep1 (依頼 133) で新しく作る → π0.5 base から deep_even10_v1 で学習したモデルを取り, deep の設定・assets を足して起動" +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 "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 "記録に model=deep1" '[ "$(jq -r .model "$T/data/lab/LogT/pod/lab_pod.json")" = deep1 ]' +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 "== 35. --model に知らない名前 → 何もせずに止まる" setup; run --model foo -check "終了コード 2・使える名前を出す・API を呼ばない" '[ $RC = 2 ] && has out.txt "--model は trim か corr か corr2 か corr3" && [ ! -s "$T/calls.log" ]' +check "終了コード 2・使える名前を出す・API を呼ばない" '[ $RC = 2 ] && has out.txt "--model は trim か corr か corr2 か corr3 か deep1" && [ ! -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 を付ける"' +check "--model だけで値がない → 回り続けずに終了コード 2 (codex R1)" '[ $RC = 2 ] && has out.txt "--model には trim か corr か corr2 か corr3 か deep1 を付ける"' 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 d0f84fd..e0de0fc 100755 --- a/test_lab_train.sh +++ b/test_lab_train.sh @@ -237,6 +237,50 @@ 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" +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 "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" ]' +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 "続きの元のチェックポイント (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" ]' +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" ]' +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 +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 ]' +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 ]' +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 ]' +rm -rf "$T" + echo "== 13. 見守る途中で止められても Pod を残さない (TERM. Ctrl-C・HUP も同じ扱い)" T=$(mktemp -d) (cd "$HERE" && LAB_DRY_ROOT="$T" LAB_FAKE_SCENARIO=timeout LAB_DRY_POLL_SEC=1 ./lab_train.sh --log LogT --dry-run --limit-hours 1 < /dev/null > "$T/out.txt" 2>&1) &