shangkuns commited on
Commit
9bca14a
·
verified ·
1 Parent(s): 2e348c7

Upload folder using huggingface_hub

Browse files
DOCKERFILE.dockerfile ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ARG COSMOS_RL_BUILD_MODE=efa
2
+ ARG COSMOS_RL_EXTRAS=""
3
+
4
+ ARG CUDA_VERSION=12.8.1
5
+
6
+ FROM nvcr.io/nvidia/cuda:${CUDA_VERSION}-devel-ubuntu22.04 AS no-efa-base
7
+
8
+ # System dependencies for eval
9
+ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
10
+ python3.10-dev python3-dev python3-distutils \
11
+ libgl1 libglib2.0-0 libglu1-mesa \
12
+ curl git ca-certificates \
13
+ tmux zip unzip vim \
14
+ && rm -rf /var/lib/apt/lists/*
15
+
16
+ # Install uv package manager
17
+ RUN curl -LsSf https://astral.sh/uv/install.sh | sh
18
+ ENV PATH="/root/.local/bin:$PATH"
19
+
20
+ # Install Miniconda
21
+ RUN curl -fsSL -o /tmp/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh \
22
+ && bash /tmp/miniconda.sh -b -p /opt/miniconda3 \
23
+ && rm /tmp/miniconda.sh
24
+ ENV PATH="/opt/miniconda3/bin:$PATH"
25
+
26
+ ENV EVAL_ROOT=/opt/eval
27
+
28
+ # Clone b1k-eval-utils
29
+ RUN mkdir -p ${EVAL_ROOT} \
30
+ && git clone https://github.com/littlespray/b1k-eval-utils.git ${EVAL_ROOT}/b1k-eval-utils
31
+
32
+ # Clone and setup openpi-comet
33
+ SHELL ["/bin/bash", "-c"]
34
+ RUN cd ${EVAL_ROOT} \
35
+ && git clone https://github.com/mli0603/openpi-comet.git --depth=1 \
36
+ && cd openpi-comet \
37
+ && cp ${EVAL_ROOT}/b1k-eval-utils/eval_openpi.sh ${EVAL_ROOT}/openpi-comet/eval_openpi.sh \
38
+ && cp ${EVAL_ROOT}/b1k-eval-utils/patch_comet_safetensors.sh ${EVAL_ROOT}/openpi-comet/patch_comet_safetensors.sh \
39
+ && GIT_LFS_SKIP_SMUDGE=1 uv sync \
40
+ && GIT_LFS_SKIP_SMUDGE=1 uv pip install -e . \
41
+ && uv pip install transformers==4.53.2 \
42
+ && cp -r ./src/openpi/models_pytorch/transformers_replace/* .venv/lib/python3.11/site-packages/transformers/ \
43
+ && bash patch_comet_safetensors.sh
44
+
45
+
46
+ # Clone BEHAVIOR-1K and install bddl/OmniGibson into openpi-comet venv
47
+ RUN git clone -b v3.7.2 --single-branch --depth=1 \
48
+ https://github.com/StanfordVL/BEHAVIOR-1K.git ${EVAL_ROOT}/BEHAVIOR-1K
49
+
50
+ RUN cd ${EVAL_ROOT}/BEHAVIOR-1K \
51
+ && source ${EVAL_ROOT}/openpi-comet/.venv/bin/activate \
52
+ && uv pip install -e bddl3 \
53
+ && uv pip install -e "OmniGibson[eval]" \
54
+ && deactivate
55
+
56
+ # Prepare b1k environment
57
+ # RUN cd ${EVAL_ROOT}/BEHAVIOR-1K \
58
+ # && cp ${EVAL_ROOT}/b1k-eval-utils/patch_setup.sh patch_setup.sh \
59
+ # && bash patch_setup.sh \
60
+
61
+ WORKDIR ${EVAL_ROOT}/BEHAVIOR-1K
62
+
63
+ # Run b1k setup.sh (separate layer — most likely to fail, easy to retry)
64
+ ENV TORCH_CUDA_ARCH_LIST="8.0 9.0+PTX"
65
+ RUN cd ${EVAL_ROOT}/BEHAVIOR-1K \
66
+ && source /opt/miniconda3/etc/profile.d/conda.sh \
67
+ && conda deactivate \
68
+ && ./setup.sh --new-env --omnigibson --bddl --joylo --eval --primitives \
69
+ --accept-conda-tos --accept-nvidia-eula
70
+
71
+ RUN source /opt/miniconda3/etc/profile.d/conda.sh \
72
+ && conda activate behavior \
73
+ && pip install scipy==1.11.4 \
74
+ && pip uninstall -y numpy \
75
+ && pip uninstall -y numpy \
76
+ && pip install numpy==1.26.4 opencv-contrib-python==4.10.0.84
77
+
78
+
79
+
80
+ # RUN cd ${EVAL_ROOT}/BEHAVIOR-1K \
81
+ # && source /opt/miniconda3/etc/profile.d/conda.sh \
82
+ # && conda activate behavior \
83
+ # && python -c "from omnigibson.utils.asset_utils import download_omnigibson_robot_assets; download_omnigibson_robot_assets()"
84
+ # && python -c "from omnigibson.utils.asset_utils import download_behavior_1k_assets; download_behavior_1k_assets(accept_license=True)"
85
+ # && python -c "from omnigibson.utils.asset_utils import download_2025_challenge_task_instances; download_2025_challenge_task_instances()"
86
+
87
+ # Copy eval script
88
+ RUN cp ${EVAL_ROOT}/b1k-eval-utils/eval_b1k.sh ${EVAL_ROOT}/BEHAVIOR-1K/eval_b1k.sh
89
+
90
+ # Download HF models into the image for offline eval.
91
+ # Append new model repos here, separated by spaces.
92
+
93
+ # ENV HF_MODEL_REPOS="shangkuns/pt12-3w-comet2-20260204022222 shangkuns/pt12-2w-comet2-20260205151209 shangkuns/cosmos-pi05-sft-pt12-20260203145532 shangkuns/pt12-3w-20260204011934"
94
+ # RUN pip install --no-cache-dir "huggingface_hub[cli]" \
95
+ # && mkdir -p /opt/eval/models \
96
+ # && for repo in $HF_MODEL_REPOS; do \
97
+ # name="${repo#*/}"; \
98
+ # hf download "$repo" --repo-type model --local-dir "/opt/eval/models/$name"; \
99
+ # done
100
+
101
+
102
+ RUN cd ${EVAL_ROOT} \
103
+ && cp -r openpi-comet/src/behavior/learning/* BEHAVIOR-1K/OmniGibson/omnigibson/learning/
104
+
105
+ #
106
+ ENV OPENPI_DATA_HOME=/opt/openpi-cache
107
+ RUN mkdir -p /opt/openpi-cache/big_vision \
108
+ && cp ${EVAL_ROOT}/b1k-eval-utils/tokenizer.model /opt/openpi-cache/big_vision/paligemma_tokenizer.model
109
+
110
+ # Install huggingface-hub in the default Python environment
111
+ RUN python -m pip install huggingface-hub wandb
batch_eval_lep.sh ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ task_names=(
5
+ "turning_on_radio"
6
+ # "picking_up_trash"
7
+ # "picking_up_toys"
8
+ # "rearranging_kitchen_furniture"
9
+ # "putting_up_Christmas_decorations_inside"
10
+ # "sorting_vegetables"
11
+ # "putting_shoes_on_rack"
12
+ # "boxing_books_up_for_storage"
13
+ # "storing_food"
14
+ # "sorting_household_items"
15
+ # "wash_a_baseball_cap"
16
+ # "wash_dog_toys"
17
+ # "hanging_pictures"
18
+ # "attach_a_camera_to_a_tripod"
19
+ # "clean_a_trumpet"
20
+ # "spraying_for_bugs"
21
+ # "make_microwave_popcorn"
22
+ # "freeze_pies"
23
+ )
24
+
25
+ node_per_task=2
26
+ gpu_per_node=4
27
+ episodes_per_task=10
28
+ num_test_per_episode=2
29
+ ckpt_basename="pi05-b1kpt12-cs32"
30
+
31
+ total_work=$((episodes_per_task * num_test_per_episode))
32
+ base=$((total_work / node_per_task))
33
+ rem=$((total_work % node_per_task))
34
+
35
+ submitted=0
36
+
37
+ for task_name in "${task_names[@]}"; do
38
+ for (( node = 0; node < node_per_task; node++ )); do
39
+ if (( node < rem )); then
40
+ node_trials=$((base + 1))
41
+ node_start=$((node * (base + 1)))
42
+ else
43
+ node_trials=$base
44
+ node_start=$((rem * (base + 1) + (node - rem) * base))
45
+ fi
46
+
47
+ job_command="$(cat <<EOF
48
+ export TASK_NAME="${task_name}"
49
+ export CKPT_BASENAME="${ckpt_basename}"
50
+ export PATH_TO_CKPT=/tmp/\${CKPT_BASENAME}
51
+ export OPENPI_DATA_HOME=/opt/openpi-cache
52
+ export EPISODES_PER_TASK=${episodes_per_task}
53
+ export EVAL_START=${node_start}
54
+ export EVAL_COUNT=${node_trials}
55
+ export NUM_GPU=${gpu_per_node}
56
+
57
+ EVAL_ROOT=/opt/eval
58
+
59
+ cd \${EVAL_ROOT}/BEHAVIOR-1K
60
+ source /opt/miniconda3/etc/profile.d/conda.sh
61
+ conda activate behavior
62
+ python -c "from omnigibson.utils.asset_utils import download_omnigibson_robot_assets; download_omnigibson_robot_assets()"
63
+ python -c "from omnigibson.utils.asset_utils import download_behavior_1k_assets; download_behavior_1k_assets(accept_license=True)"
64
+ python -c "from omnigibson.utils.asset_utils import download_2025_challenge_task_instances; download_2025_challenge_task_instances()"
65
+ conda deactivate
66
+
67
+ hf download shangkuns/\${CKPT_BASENAME} --local-dir \${PATH_TO_CKPT}
68
+ mkdir -p \${PATH_TO_CKPT}/assets/behavior-1k/2025-challenge-demos
69
+ cp \${EVAL_ROOT}/b1k-eval-utils/norm_stats.json \${PATH_TO_CKPT}/assets/behavior-1k/2025-challenge-demos/
70
+
71
+ cd \${EVAL_ROOT}/b1k-eval-utils
72
+ bash patch_walltime.sh \${EVAL_ROOT}/BEHAVIOR-1K/OmniGibson/omnigibson/learning
73
+ bash run_dual_eval.sh
74
+
75
+ hf upload shangkuns/\${CKPT_BASENAME} /opt/eval/eval_output \${TASK_NAME} --repo-type dataset
76
+ EOF
77
+ )"
78
+
79
+ lep job create \
80
+ --resource-shape "my.${gpu_per_node}xl40s" \
81
+ --node-group oci-ord-lepton-001 \
82
+ --num-workers 1 \
83
+ --container-image littlespray/b1k-eval-light:v4 \
84
+ --command "${job_command}" \
85
+ --intra-job-communication=true \
86
+ --env NVIDIA_DRIVER_CAPABILITIES=all \
87
+ --env DEBIAN_FRONTEND=noninteractive \
88
+ --secret HUGGING_FACE_HUB_TOKEN=SHANGKUN_HF_NV.shangkuns \
89
+ --ttl-seconds-after-finished 259200 \
90
+ --log-collection true \
91
+ --queue-priority 8 \
92
+ --can-preempt \
93
+ --name "${task_name//_/-}-n${node}"
94
+
95
+ submitted=$((submitted + 1))
96
+ done
97
+ done
98
+
99
+ echo "Submitted ${submitted} jobs."
eval_b1k.sh ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export ROOT_DIR=${ROOT_DIR:-/opt/eval}
2
+ export OMNIGIBSON_HEADLESS=${OMNIGIBSON_HEADLESS:-1}
3
+ export NUM_GPU=${NUM_GPU:-1}
4
+ export BASE_PORT=${BASE_PORT:-8000}
5
+ export TASK_NAME=${TASK_NAME:-turning_on_radio}
6
+ export LOG_PATH=${LOG_PATH:-${ROOT_DIR}/eval_output}
7
+ mkdir -p "$LOG_PATH"
8
+
9
+ TOTAL=${EVAL_COUNT:-4}
10
+ START=${EVAL_START_IDX:-0}
11
+
12
+ BASE=$((TOTAL / NUM_GPU))
13
+ REM=$((TOTAL % NUM_GPU))
14
+
15
+ for ((gpu = 0; gpu < NUM_GPU; gpu++)); do
16
+ if ((gpu < REM)); then
17
+ count=$((BASE + 1))
18
+ s=$((START + gpu * (BASE + 1)))
19
+ else
20
+ count=$BASE
21
+ s=$((START + REM * (BASE + 1) + (gpu - REM) * BASE))
22
+ fi
23
+ (( count == 0 )) && continue
24
+
25
+ CUDA_VISIBLE_DEVICES="$gpu" \
26
+ python "${ROOT_DIR}/BEHAVIOR-1K/OmniGibson/omnigibson/learning/eval_custom.py" \
27
+ policy=websocket \
28
+ task.name=$TASK_NAME \
29
+ log_path=$LOG_PATH \
30
+ env_wrapper._target_=omnigibson.learning.wrappers.RGBWrapper \
31
+ save_rollout=false \
32
+ perturb_pose=false \
33
+ use_parallel_evaluator=true \
34
+ parallel_evaluator_start_idx=$s \
35
+ parallel_evaluator_end_idx=$((s + count)) \
36
+ model.port=$((BASE_PORT + gpu)) \
37
+ > "$LOG_PATH/${gpu}_stdout.log" 2>&1 &
38
+ done
39
+ wait
eval_b1k_json.py ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ 统计 Hugging Face dataset 仓库中所有 exp 的逐轮成功率。
4
+
5
+ 目录结构约定:task/exp/metrics/*.json
6
+ 示例文件:
7
+ turning-on-radio/turning_on_radio_xxx_20260216085234/metrics/turning_on_radio_109_0.json
8
+
9
+ 规则:
10
+ 1. 对同一个 episode id,收集所有 exp 中对应 json;
11
+ 2. 按 exp 目录末尾时间戳升序排序;
12
+ 3. 第 i 轮平均值 = 所有 episode 各自第 i 个结果的 q_score.final 平均;
13
+ 4. 当任一 episode 不足第 i 个结果时停止;
14
+ 5. 输出每轮平均值,以及这些平均值的平均值。
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import argparse
20
+ import json
21
+ import re
22
+ from collections import defaultdict
23
+ from dataclasses import dataclass
24
+ from statistics import mean
25
+ from typing import DefaultDict, Dict, List, Tuple
26
+
27
+ from huggingface_hub import HfApi, hf_hub_download
28
+
29
+
30
+ EXP_TIMESTAMP_RE = re.compile(r"_(\d{14})$")
31
+ EPISODE_ID_RE = re.compile(r"_(\d+)_\d+\.json$")
32
+ METRICS_JSON_RE = re.compile(r"^[^/]+/[^/]+/metrics/[^/]+\.json$")
33
+
34
+
35
+ @dataclass(frozen=True)
36
+ class EpisodeEval:
37
+ task: str
38
+ episode_id: str
39
+ timestamp: str
40
+ q_score: float
41
+ wall_time: float | None
42
+ path: str
43
+
44
+
45
+ def parse_args() -> argparse.Namespace:
46
+ parser = argparse.ArgumentParser(
47
+ description="计算 HF dataset 仓库中逐轮 episode 成功率均值。"
48
+ )
49
+ parser.add_argument(
50
+ "repo_id",
51
+ help="HF dataset 仓库名,例如 shangkuns/pt12-3w-comet2-20260204022222",
52
+ )
53
+ parser.add_argument(
54
+ "--repo-type",
55
+ default="dataset",
56
+ choices=["dataset", "model", "space"],
57
+ help="HF 仓库类型,默认 dataset。",
58
+ )
59
+ return parser.parse_args()
60
+
61
+
62
+ def extract_timestamp_from_exp(exp_name: str) -> str | None:
63
+ match = EXP_TIMESTAMP_RE.search(exp_name)
64
+ return match.group(1) if match else None
65
+
66
+
67
+ def extract_episode_id_from_filename(filename: str) -> str | None:
68
+ match = EPISODE_ID_RE.search(filename)
69
+ return match.group(1) if match else None
70
+
71
+
72
+ def collect_episode_evals(
73
+ repo_id: str, repo_type: str
74
+ ) -> Dict[Tuple[str, str], List[EpisodeEval]]:
75
+ api = HfApi()
76
+ repo_files = api.list_repo_files(repo_id=repo_id, repo_type=repo_type)
77
+
78
+ episode_to_evals: DefaultDict[Tuple[str, str], List[EpisodeEval]] = defaultdict(list)
79
+
80
+ for path in repo_files:
81
+ if not METRICS_JSON_RE.match(path):
82
+ continue
83
+
84
+ parts = path.split("/")
85
+ # task / exp / metrics / xxx.json
86
+ if len(parts) != 4:
87
+ continue
88
+
89
+ task_name = parts[0]
90
+ exp_name = parts[1]
91
+ file_name = parts[3]
92
+
93
+ timestamp = extract_timestamp_from_exp(exp_name)
94
+ if timestamp is None:
95
+ continue
96
+
97
+ episode_id = extract_episode_id_from_filename(file_name)
98
+ if episode_id is None:
99
+ continue
100
+
101
+ local_path = hf_hub_download(repo_id=repo_id, repo_type=repo_type, filename=path)
102
+ with open(local_path, "r", encoding="utf-8") as f:
103
+ content = json.load(f)
104
+
105
+ q_score = float(content["q_score"]["final"])
106
+ wall_time = None
107
+ if isinstance(content.get("time"), dict) and "wall_time" in content["time"]:
108
+ wall_time = float(content["time"]["wall_time"])
109
+
110
+ episode_to_evals[(task_name, episode_id)].append(
111
+ EpisodeEval(
112
+ task=task_name,
113
+ episode_id=episode_id,
114
+ timestamp=timestamp,
115
+ q_score=q_score,
116
+ wall_time=wall_time,
117
+ path=path,
118
+ )
119
+ )
120
+
121
+ return dict(episode_to_evals)
122
+
123
+
124
+ def compute_round_means(
125
+ episode_to_evals: Dict[Tuple[str, str], List[EpisodeEval]]
126
+ ) -> Tuple[List[float], List[EpisodeEval]]:
127
+ if not episode_to_evals:
128
+ return [], []
129
+
130
+ for evals in episode_to_evals.values():
131
+ evals.sort(key=lambda x: x.timestamp)
132
+
133
+ max_rounds = min(len(evals) for evals in episode_to_evals.values())
134
+ if max_rounds == 0:
135
+ return [], []
136
+
137
+ round_means: List[float] = []
138
+ used_evals: List[EpisodeEval] = []
139
+ episode_keys = sorted(episode_to_evals.keys(), key=lambda x: (x[0], int(x[1])))
140
+ for round_idx in range(max_rounds):
141
+ this_round = [episode_to_evals[key][round_idx] for key in episode_keys]
142
+ values = [item.q_score for item in this_round]
143
+ used_evals.extend(this_round)
144
+ round_means.append(mean(values))
145
+ return round_means, used_evals
146
+
147
+
148
+ def compute_wall_time_stats(used_evals: List[EpisodeEval]) -> Tuple[Dict[str, float], float | None]:
149
+ task_to_times: DefaultDict[str, List[float]] = defaultdict(list)
150
+ for item in used_evals:
151
+ if item.wall_time is not None:
152
+ task_to_times[item.task].append(item.wall_time)
153
+
154
+ task_means = {task: mean(times) for task, times in task_to_times.items() if times}
155
+ if not task_means:
156
+ return task_means, None
157
+ overall_task_mean = mean(task_means.values())
158
+ return task_means, overall_task_mean
159
+
160
+
161
+ def main() -> None:
162
+ args = parse_args()
163
+
164
+ episode_to_evals = collect_episode_evals(args.repo_id, args.repo_type)
165
+ if not episode_to_evals:
166
+ print("未找到符合 task/exp/metrics/*.json 结构且可解析 episode/timestamp 的文件。")
167
+ return
168
+
169
+ round_means, used_evals = compute_round_means(episode_to_evals)
170
+ if not round_means:
171
+ print("没有可计算的轮次(可能某些 episode 无有效结果)。")
172
+ return
173
+
174
+ print(f"repo: {args.repo_id}")
175
+ print(f"episode 数量: {len(episode_to_evals)}")
176
+ print(f"可计算轮次: {len(round_means)}")
177
+ for idx, value in enumerate(round_means, start=1):
178
+ print(f"第{idx}轮平均成功率: {value:.6f}")
179
+
180
+ overall = mean(round_means)
181
+ print(f"各轮平均值的平均值: {overall:.6f}")
182
+
183
+ task_means, overall_task_mean = compute_wall_time_stats(used_evals)
184
+ if not task_means:
185
+ print("参与 q-score 统计的样本中未找到 time.wall_time,跳过用时统计。")
186
+ return
187
+
188
+ print("各 task 平均 episode 用时:")
189
+ for task in sorted(task_means.keys()):
190
+ print(f"- {task}: {task_means[task]:.6f}")
191
+ print(f"所有 task 平均用时: {overall_task_mean:.6f}")
192
+
193
+
194
+ if __name__ == "__main__":
195
+ main()
eval_openpi.sh ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ TASK_NAME="${TASK_NAME:-cook_hot_dogs}"
5
+ CONFIG="${CONFIG:-pi05_b1k-base}"
6
+ PATH_TO_CKPT="${PATH_TO_CKPT:-/workspace/comet-pi05-b1k-pt50-ptm}"
7
+ PORT="${PORT:-8000}"
8
+
9
+ exec uv run scripts/serve_b1k.py \
10
+ --task_name "$TASK_NAME" \
11
+ --port "$PORT" \
12
+ policy:checkpoint \
13
+ --policy.config "$CONFIG" \
14
+ --policy.dir "$PATH_TO_CKPT"
norm_stats.json ADDED
@@ -0,0 +1,280 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "norm_stats": {
3
+ "state": {
4
+ "mean": [
5
+ -0.0007269753236491301,
6
+ 0.0005871853206764299,
7
+ -0.00037458819335587837,
8
+ 1.1220268708249688,
9
+ -1.6177487234510692,
10
+ -0.7037017488819726,
11
+ 5.178331679376605e-6,
12
+ -0.3819930547932975,
13
+ 0.0636385481520187,
14
+ -0.036330391163538304,
15
+ -0.5923406613420917,
16
+ 0.27300850717924735,
17
+ 0.06392431067313378,
18
+ -0.23112970703914087,
19
+ -0.4207707978561216,
20
+ -0.06831169401385709,
21
+ 0.0243751039006989,
22
+ -0.6469659968323186,
23
+ -0.12143957181667113,
24
+ 0.14976853360672077,
25
+ 0.130979475995902,
26
+ 0.08751846492897486,
27
+ 0.07864204959670715,
28
+ 0.0,
29
+ 0.0,
30
+ 0.0,
31
+ 0.0,
32
+ 0.0,
33
+ 0.0,
34
+ 0.0,
35
+ 0.0,
36
+ 0.0
37
+ ],
38
+ "std": [
39
+ 0.02199286521964398,
40
+ 0.01975387071601238,
41
+ 0.027931866359868447,
42
+ 0.43276966334356803,
43
+ 0.7793389811993767,
44
+ 0.4685643576682486,
45
+ 0.00007484946359270099,
46
+ 0.3495479212084949,
47
+ 0.14741056781059605,
48
+ 0.4086500808617923,
49
+ 0.45792477411954097,
50
+ 0.4472871307419175,
51
+ 0.4255421558256086,
52
+ 0.5117453304941143,
53
+ 0.3626599950258924,
54
+ 0.14969143472339538,
55
+ 0.3820732235768937,
56
+ 0.5161227600903063,
57
+ 0.42373140636873546,
58
+ 0.4839893318550682,
59
+ 0.4912556634102691,
60
+ 0.026452244464230175,
61
+ 0.03343202113475432,
62
+ 0.0,
63
+ 0.0,
64
+ 0.0,
65
+ 0.0,
66
+ 0.0,
67
+ 0.0,
68
+ 0.0,
69
+ 0.0,
70
+ 0.0
71
+ ],
72
+ "q01": [
73
+ -0.2613118174761534,
74
+ -0.25247000222802163,
75
+ -0.29782283776104446,
76
+ 0.4487143921405077,
77
+ -2.5600294193029405,
78
+ -1.8321708536148071,
79
+ -0.0007136256152298301,
80
+ -2.105691260957718,
81
+ -0.17450003325939178,
82
+ -2.230193532180786,
83
+ -1.6330286006212233,
84
+ -1.411822907948494,
85
+ -1.0471969878673553,
86
+ -1.5708000659942627,
87
+ -1.91510467287302,
88
+ -0.849857434040308,
89
+ -1.6666959735155105,
90
+ -1.6168319140672684,
91
+ -2.3132422157287595,
92
+ -1.0375005970597269,
93
+ -1.570798397064209,
94
+ 0.0,
95
+ 6.501830672824839e-8,
96
+ 0.0,
97
+ 0.0,
98
+ 0.0,
99
+ 0.0,
100
+ 0.0,
101
+ 0.0,
102
+ 0.0,
103
+ 0.0,
104
+ 0.0
105
+ ],
106
+ "q99": [
107
+ 0.2542599085897208,
108
+ 0.2542742739796637,
109
+ 0.30030812568068505,
110
+ 1.716256618249416,
111
+ -0.3974635741829872,
112
+ 0.1548894247069954,
113
+ 0.0005543326894694459,
114
+ 0.37031316382586876,
115
+ 0.9278406740725034,
116
+ 1.91316024565696,
117
+ 0.09516809616684914,
118
+ 2.356194269657135,
119
+ 1.0472097456455232,
120
+ 1.517247679293155,
121
+ 0.36055924194753,
122
+ 0.17450004816055298,
123
+ 1.6652137512683844,
124
+ 0.0914144726134836,
125
+ 1.6950346724390943,
126
+ 1.0471995235800744,
127
+ 1.56818338637352,
128
+ 0.10000000149011612,
129
+ 0.10000000149011612,
130
+ 0.0,
131
+ 0.0,
132
+ 0.0,
133
+ 0.0,
134
+ 0.0,
135
+ 0.0,
136
+ 0.0,
137
+ 0.0,
138
+ 0.0
139
+ ]
140
+ },
141
+ "actions": {
142
+ "mean": [
143
+ 0.04972944409549112,
144
+ 0.003690394849812747,
145
+ -0.0029349690138718285,
146
+ 1.122085165280863,
147
+ -1.6180965905064797,
148
+ -0.7043162896930203,
149
+ 0.0,
150
+ -0.382348926450185,
151
+ 0.06370661786532403,
152
+ -0.03661990100703703,
153
+ -0.5924590200206378,
154
+ 0.2750886004936484,
155
+ 0.06431002005410705,
156
+ -0.23144970536531229,
157
+ 0.4210819724256232,
158
+ -0.4212541587202455,
159
+ -0.06839742236782122,
160
+ 0.024488948505712194,
161
+ -0.6468751837828239,
162
+ -0.12300583051861715,
163
+ 0.1507738855467879,
164
+ 0.13119771996817328,
165
+ 0.20474606868389103,
166
+ 0.0,
167
+ 0.0,
168
+ 0.0,
169
+ 0.0,
170
+ 0.0,
171
+ 0.0,
172
+ 0.0,
173
+ 0.0,
174
+ 0.0
175
+ ],
176
+ "std": [
177
+ 0.13018670744434768,
178
+ 0.07378928098443516,
179
+ 0.11732054405741633,
180
+ 0.4326509056935839,
181
+ 0.7791573756824512,
182
+ 0.46843066554707175,
183
+ 0.0,
184
+ 0.34964716759678244,
185
+ 0.14751595777952248,
186
+ 0.41012746451839815,
187
+ 0.4580261645466588,
188
+ 0.4486994719029271,
189
+ 0.4276847015578234,
190
+ 0.5130942257930428,
191
+ 0.9070220542734125,
192
+ 0.3626863499954174,
193
+ 0.14981404106277435,
194
+ 0.3835292477509873,
195
+ 0.5164924719696999,
196
+ 0.4256839962396164,
197
+ 0.48620749808715086,
198
+ 0.49307611404915425,
199
+ 0.9788150111676108,
200
+ 0.0,
201
+ 0.0,
202
+ 0.0,
203
+ 0.0,
204
+ 0.0,
205
+ 0.0,
206
+ 0.0,
207
+ 0.0,
208
+ 0.0
209
+ ],
210
+ "q01": [
211
+ -0.34868216374218464,
212
+ -0.3418492655992508,
213
+ -0.2999999523162842,
214
+ 0.44999998807907104,
215
+ -2.560204291343689,
216
+ -1.8325998783111572,
217
+ 0.0,
218
+ -2.1070283545494077,
219
+ -0.1745000034570694,
220
+ -2.230065962147713,
221
+ -1.6336875317335129,
222
+ -1.4130705307483673,
223
+ -1.0471980571746826,
224
+ -1.5707999467849731,
225
+ -1.0,
226
+ -1.9151124985814094,
227
+ -0.8498951392710209,
228
+ -1.6674389212727547,
229
+ -1.616815806877613,
230
+ -2.3132428766012194,
231
+ -1.0385202980041504,
232
+ -1.5707999467849731,
233
+ -1.0,
234
+ 0.0,
235
+ 0.0,
236
+ 0.0,
237
+ 0.0,
238
+ 0.0,
239
+ 0.0,
240
+ 0.0,
241
+ 0.0,
242
+ 0.0
243
+ ],
244
+ "q99": [
245
+ 0.34921015488803386,
246
+ 0.34948091855645175,
247
+ 0.2999999523162842,
248
+ 1.7161086392402647,
249
+ -0.4000000059604645,
250
+ 0.1548384608328342,
251
+ 0.0,
252
+ 0.3706086640655987,
253
+ 0.9287866185903546,
254
+ 1.9131654405593803,
255
+ 0.09510681201517582,
256
+ 2.3561956882476807,
257
+ 1.0471980571746826,
258
+ 1.5173104754924756,
259
+ 1.0,
260
+ 0.3605582716763002,
261
+ 0.1745000034570694,
262
+ 1.6659219603061668,
263
+ 0.09357282519340515,
264
+ 1.6951719859242371,
265
+ 1.0471980571746826,
266
+ 1.5707999467849731,
267
+ 1.0,
268
+ 0.0,
269
+ 0.0,
270
+ 0.0,
271
+ 0.0,
272
+ 0.0,
273
+ 0.0,
274
+ 0.0,
275
+ 0.0,
276
+ 0.0
277
+ ]
278
+ }
279
+ }
280
+ }
patch_comet_safetensors.sh ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ ROOT_DIR="${ROOT_DIR:-$SCRIPT_DIR}"
6
+
7
+ POLICY_CONFIG_FILE="${ROOT_DIR}/src/openpi/policies/policy_config.py"
8
+ MODEL_FILE="${ROOT_DIR}/src/openpi/models/model.py"
9
+
10
+ python3 - "$POLICY_CONFIG_FILE" "$MODEL_FILE" <<'PY'
11
+ import pathlib
12
+ import sys
13
+
14
+ policy_config_file = pathlib.Path(sys.argv[1])
15
+ model_file = pathlib.Path(sys.argv[2])
16
+
17
+
18
+ def replace_or_exit(path: pathlib.Path, old: str, new: str, label: str) -> None:
19
+ text = path.read_text()
20
+ if new in text:
21
+ print(f"[skip] {path.name}: {label} already applied")
22
+ return
23
+ if old not in text:
24
+ raise SystemExit(f"[error] Could not find expected block for {label} in {path}")
25
+ path.write_text(text.replace(old, new))
26
+ print(f"[ok] {path.name}: {label}")
27
+
28
+
29
+ replace_or_exit(
30
+ policy_config_file,
31
+ """ # Check if this is a PyTorch model by looking for model.safetensors\n weight_path = os.path.join(checkpoint_dir, "model.safetensors")\n is_pytorch = os.path.exists(weight_path)\n""",
32
+ """ # Check if this is a PyTorch model by looking for either a monolithic\n # safetensors file or a sharded safetensors index.\n single_weight_path = os.path.join(checkpoint_dir, "model.safetensors")\n sharded_index_path = os.path.join(checkpoint_dir, "model.safetensors.index.json")\n if os.path.exists(single_weight_path):\n weight_path = single_weight_path\n elif os.path.exists(sharded_index_path):\n weight_path = sharded_index_path\n else:\n weight_path = single_weight_path\n is_pytorch = os.path.exists(single_weight_path) or os.path.exists(sharded_index_path)\n""",
33
+ "detect sharded safetensors checkpoints",
34
+ )
35
+
36
+ replace_or_exit(
37
+ model_file,
38
+ """import abc\nfrom collections.abc import Sequence\nimport dataclasses\nimport enum\nimport logging\nimport pathlib\nfrom typing import Generic, TypeVar\n""",
39
+ """import abc\nfrom collections.abc import Sequence\nimport dataclasses\nimport enum\nimport json\nimport logging\nimport pathlib\nfrom typing import Generic, TypeVar\n""",
40
+ "add json import",
41
+ )
42
+
43
+ replace_or_exit(
44
+ model_file,
45
+ """ def load_pytorch(self, train_config, weight_path: str):\n logger.info(f"train_config: {train_config}")\n model = pi0_pytorch.PI0Pytorch(config=train_config.model)\n safetensors.torch.load_model(model, weight_path)\n return model\n""",
46
+ """ def load_pytorch(self, train_config, weight_path: str):\n logger.info(f"train_config: {train_config}")\n model = pi0_pytorch.PI0Pytorch(config=train_config.model)\n\n weight_path_obj = pathlib.Path(weight_path)\n if weight_path_obj.name == "model.safetensors.index.json":\n if not weight_path_obj.exists():\n raise FileNotFoundError(f"Sharded index file not found at {weight_path_obj}")\n\n with weight_path_obj.open("r", encoding="utf-8") as f:\n index_data = json.load(f)\n\n weight_map = index_data.get("weight_map", {})\n if not isinstance(weight_map, dict) or not weight_map:\n raise ValueError(\n f"Invalid sharded checkpoint index at {weight_path_obj}: missing non-empty weight_map"\n )\n\n shard_names = sorted(set(weight_map.values()))\n missing_shards = [\n shard_name for shard_name in shard_names if not (weight_path_obj.parent / shard_name).exists()\n ]\n if missing_shards:\n raise FileNotFoundError(\n f"Missing safetensors shards under {weight_path_obj.parent}: {missing_shards}"\n )\n\n state_dict = {}\n for shard_name in shard_names:\n shard_path = weight_path_obj.parent / shard_name\n shard_state = safetensors.torch.load_file(str(shard_path))\n duplicated_keys = set(state_dict).intersection(shard_state)\n if duplicated_keys:\n raise ValueError(\n f"Duplicated tensor keys detected across shards: {sorted(duplicated_keys)[:10]}"\n )\n state_dict.update(shard_state)\n\n missing_keys, unexpected_keys = model.load_state_dict(state_dict, strict=False)\n if unexpected_keys:\n raise ValueError(\n f"Unexpected checkpoint keys when loading sharded safetensors: {unexpected_keys[:10]}"\n )\n if missing_keys:\n logger.warning(\n "Missing model keys when loading sharded safetensors (showing first 10): %s",\n missing_keys[:10],\n )\n else:\n safetensors.torch.load_model(model, str(weight_path_obj))\n\n return model\n""",
47
+ "load sharded safetensors index",
48
+ )
49
+ PY
50
+
51
+ echo "Patch applied successfully."
patch_walltime.sh ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Usage:
5
+ # bash apply_eval_custom_wall_time_patch.sh /path/to/dir/containing/eval_custom.py
6
+ # bash apply_eval_custom_wall_time_patch.sh /path/to/eval_custom.py
7
+
8
+ TARGET="${1:-.}"
9
+ if [[ -d "$TARGET" ]]; then
10
+ FILE="$TARGET/eval_custom.py"
11
+ else
12
+ FILE="$TARGET"
13
+ fi
14
+
15
+ if [[ ! -f "$FILE" ]]; then
16
+ echo "[ERROR] eval_custom.py not found: $FILE"
17
+ exit 1
18
+ fi
19
+
20
+ python3 - "$FILE" <<'PY'
21
+ import re
22
+ import sys
23
+ from pathlib import Path
24
+
25
+ path = Path(sys.argv[1])
26
+ text = path.read_text()
27
+ original = text
28
+
29
+ if 'metrics["time"]["wall_time"]' in text:
30
+ print(f"[INFO] Already patched: {path}")
31
+ sys.exit(0)
32
+
33
+ # 1) Initialize wall_time once per instance loop.
34
+ pat1 = r"(\n\s*done = False\n)(\s*if config\.write_video:)"
35
+ rep1 = r"\1 wall_time = 0.0\n\2"
36
+ text, n1 = re.subn(pat1, rep1, text, count=1)
37
+
38
+ # 2) Accumulate per-step wall time.
39
+ pat2 = (
40
+ r"(\n\s*time_start = time\.time\(\)\n"
41
+ r"\s*terminated, truncated, info = evaluator\.step\(\)\n"
42
+ r"\s*time_step = time\.time\(\) - time_start\n)"
43
+ )
44
+ rep2 = r"\1 wall_time += time_step\n"
45
+ text, n2 = re.subn(pat2, rep2, text, count=1)
46
+
47
+ # 3) Persist wall_time in metrics["time"].
48
+ pat3 = (
49
+ r"(\n\s*for metric in evaluator\.metrics:\n"
50
+ r"\s*metrics\.update\(metric\.gather_results\(\)\)\n)"
51
+ )
52
+ rep3 = r'\1 metrics["time"]["wall_time"] = wall_time\n'
53
+ text, n3 = re.subn(pat3, rep3, text, count=1)
54
+
55
+ if not (n1 == 1 and n2 == 1 and n3 == 1):
56
+ print("[ERROR] Patch failed: target code pattern not found (file may differ from expected).")
57
+ print(f"[DEBUG] replacements: n1={n1}, n2={n2}, n3={n3}")
58
+ sys.exit(2)
59
+
60
+ if text == original:
61
+ print(f"[INFO] No changes made: {path}")
62
+ sys.exit(0)
63
+
64
+ path.write_text(text)
65
+ print(f"[OK] Patched: {path}")
66
+ PY
run_dual_eval.sh ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ TASK_NAME="${TASK_NAME:-turning_on_radio}"
5
+ PATH_TO_CKPT="${PATH_TO_CKPT:-/opt/eval/models/cosmos-pi05-sft-pt12-20260203145532}"
6
+ CONDA_ROOT="${CONDA_ROOT:-/opt/miniconda3}"
7
+ CONDA_ENV="${CONDA_ENV:-behavior}"
8
+ OPENPI_DATA_HOME="${OPENPI_DATA_HOME:-/opt/openpi-cache}"
9
+ BASE_PORT="${BASE_PORT:-8000}"
10
+ EPISODES_PER_TASK="${EPISODES_PER_TASK:-10}"
11
+ EVAL_START="${EVAL_START:-0}"
12
+ EVAL_COUNT="${EVAL_COUNT:-10}"
13
+ NUM_GPU="${NUM_GPU:-1}"
14
+
15
+
16
+ LOG_DIR="${LOG_DIR:-/opt/eval/eval_output}"
17
+ mkdir -p "$OPENPI_DATA_HOME" "$LOG_DIR"
18
+ export OPENPI_DATA_HOME
19
+
20
+ echo "[INFO] task=$TASK_NAME gpus=$NUM_GPU eval_start=$EVAL_START eval_count=$EVAL_COUNT"
21
+
22
+ # Launch one openpi policy server per GPU
23
+ OPENPI_PIDS=()
24
+ for ((gpu = 0; gpu < NUM_GPU; gpu++)); do
25
+ (
26
+ cd /opt/eval/openpi-comet
27
+ CUDA_VISIBLE_DEVICES="$gpu" \
28
+ TASK_NAME="$TASK_NAME" \
29
+ PATH_TO_CKPT="$PATH_TO_CKPT" \
30
+ PORT="$((BASE_PORT + gpu))" \
31
+ bash /opt/eval/openpi-comet/eval_openpi.sh
32
+ ) >"$LOG_DIR/openpi_gpu${gpu}.log" 2>&1 &
33
+ OPENPI_PIDS+=("$!")
34
+ done
35
+
36
+ # Run eval with episode wrapping
37
+ (
38
+ source "$CONDA_ROOT/etc/profile.d/conda.sh"
39
+ conda activate "$CONDA_ENV"
40
+ cd /opt/eval/BEHAVIOR-1K
41
+
42
+ _remaining=$EVAL_COUNT
43
+ _cursor=$EVAL_START
44
+
45
+ while (( _remaining > 0 )); do
46
+ _s=$((_cursor % EPISODES_PER_TASK))
47
+ _chunk=$((EPISODES_PER_TASK - _s))
48
+ (( _chunk > _remaining )) && _chunk=$_remaining
49
+
50
+ echo "[INFO] eval chunk: episodes ${_s}..$((_s + _chunk - 1)) (${_chunk} trials)"
51
+
52
+ TASK_NAME="$TASK_NAME" \
53
+ LOG_PATH="$LOG_DIR" \
54
+ NUM_GPU="$NUM_GPU" \
55
+ BASE_PORT="$BASE_PORT" \
56
+ EVAL_START_IDX="$_s" \
57
+ EVAL_COUNT="$_chunk" \
58
+ bash /opt/eval/BEHAVIOR-1K/eval_b1k.sh
59
+
60
+ _cursor=$((_cursor + _chunk))
61
+ _remaining=$((_remaining - _chunk))
62
+ done
63
+ ) >"$LOG_DIR/behavior_eval.log" 2>&1 &
64
+ B1K_PID=$!
65
+
66
+ wait "$B1K_PID" || true
67
+ for pid in "${OPENPI_PIDS[@]}"; do
68
+ kill "$pid" 2>/dev/null || true
69
+ wait "$pid" || true
70
+ done
71
+
72
+ echo "[INFO] done."
tokenizer.model ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8986bb4f423f07f8c7f70d0dbe3526fb2316056c17bae71b1ea975e77a168fc6
3
+ size 4264023
tokenizer_config.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "tokenizer_class": "PaligemmaTokenizer",
3
+ "max_len": 200,
4
+ "auto_map": {
5
+ "AutoTokenizer": ["tokenizer_pi05.PaligemmaTokenizer", null]
6
+ }
7
+ }