Instructions to use arabellako22/ppo-pusher-v5-vecnormalize with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- stable-baselines3
How to use arabellako22/ppo-pusher-v5-vecnormalize with stable-baselines3:
from huggingface_sb3 import load_from_hub checkpoint = load_from_hub( repo_id="arabellako22/ppo-pusher-v5-vecnormalize", filename="{MODEL FILENAME}.zip", ) - Notebooks
- Google Colab
- Kaggle
Download safe_mujoco_render.py from arabellako22/ppo-pusher-v5-vecnormalize: direct link, hf CLI and curl.
- Browser
- Download file 14.4 kB
-
https://huggingface.co/arabellako22/ppo-pusher-v5-vecnormalize/resolve/main/safe_mujoco_render.py
- Command line
-
hf download hf://arabellako22/ppo-pusher-v5-vecnormalize/safe_mujoco_render.py
-
curl -L -o safe_mujoco_render.py https://huggingface.co/arabellako22/ppo-pusher-v5-vecnormalize/resolve/main/safe_mujoco_render.py
14.4 kB
| """Safe subprocess-isolated PPO evaluation and MuJoCo rendering. | |
| This module prevents native OpenGL conflicts by keeping Stable-Baselines3 | |
| model inference and MuJoCo frame rendering in separate Python processes. | |
| It targets Gymnasium MuJoCo environments with continuous Box actions, | |
| Stable-Baselines3 PPO checkpoints, and VecNormalize statistics. | |
| """ | |
| __version__ = "1.0.0" | |
| __all__ = ["evaluate_and_render_ppo_mujoco"] | |
| # Q3. μ¬μ¬μ© κ°λ₯ν PPO/MuJoCo λΆλ¦¬ νκ°Β·λ λλ§ λꡬ | |
| # | |
| # λμ: | |
| # - Gymnasium MuJoCo μ°μ νλ(Box) νκ²½ | |
| # - Stable-Baselines3 PPO 체ν¬ν¬μΈνΈ | |
| # - VecNormalize ν΅κ³ | |
| # | |
| # ν΅μ¬: | |
| # - SB3 νλ‘μΈμ€λ νκ°μ νλ μμ±λ§ μν | |
| # - MuJoCo νλ‘μΈμ€λ λ λλ§λ§ μν | |
| # - λμΌ seed/νλμ 보μ μΌμΉλ₯Ό μλ κ²μ¦ | |
| import json | |
| import math | |
| import os | |
| import subprocess | |
| import sys | |
| from pathlib import Path | |
| GENERIC_EVALUATION_WORKER = r""" | |
| import json | |
| from pathlib import Path | |
| import gymnasium as gym | |
| import numpy as np | |
| from stable_baselines3 import PPO | |
| from stable_baselines3.common.env_util import make_vec_env | |
| from stable_baselines3.common.evaluation import evaluate_policy | |
| from stable_baselines3.common.vec_env import VecNormalize | |
| request = json.loads(input()) | |
| env_id = request["env_id"] | |
| model_path = Path(request["model_path"]) | |
| stats_path = Path(request["stats_path"]) | |
| device = request["device"] | |
| n_eval_episodes = int( | |
| request["n_eval_episodes"] | |
| ) | |
| n_random_episodes = int( | |
| request["n_random_episodes"] | |
| ) | |
| eval_seed = int(request["eval_seed"]) | |
| random_seed = int(request["random_seed"]) | |
| replay_seed = int(request["replay_seed"]) | |
| max_replay_steps = request[ | |
| "max_replay_steps" | |
| ] | |
| if max_replay_steps is not None: | |
| max_replay_steps = int(max_replay_steps) | |
| model = PPO.load( | |
| model_path, | |
| device=device, | |
| ) | |
| # ------------------------------------------------------------ | |
| # 1. μ κ·ν ν΅κ³λ₯Ό μ μ©ν PPO νκ° | |
| # ------------------------------------------------------------ | |
| eval_raw_env = make_vec_env( | |
| env_id, | |
| n_envs=1, | |
| seed=eval_seed, | |
| ) | |
| eval_env = VecNormalize.load( | |
| stats_path, | |
| eval_raw_env, | |
| ) | |
| eval_env.training = False | |
| eval_env.norm_reward = False | |
| try: | |
| mean_reward, std_reward = evaluate_policy( | |
| model, | |
| eval_env, | |
| n_eval_episodes=n_eval_episodes, | |
| deterministic=True, | |
| warn=False, | |
| ) | |
| finally: | |
| eval_env.close() | |
| # ------------------------------------------------------------ | |
| # 2. λλ€ μ μ± κΈ°μ€μ | |
| # ------------------------------------------------------------ | |
| random_env = gym.make(env_id) | |
| random_rewards = [] | |
| try: | |
| for episode_index in range( | |
| n_random_episodes | |
| ): | |
| episode_seed = ( | |
| random_seed + episode_index | |
| ) | |
| obs, info = random_env.reset( | |
| seed=episode_seed | |
| ) | |
| random_env.action_space.seed( | |
| episode_seed | |
| ) | |
| episode_reward = 0.0 | |
| terminated = False | |
| truncated = False | |
| while not (terminated or truncated): | |
| action = ( | |
| random_env.action_space.sample() | |
| ) | |
| ( | |
| obs, | |
| reward, | |
| terminated, | |
| truncated, | |
| info, | |
| ) = random_env.step(action) | |
| episode_reward += float(reward) | |
| random_rewards.append(episode_reward) | |
| finally: | |
| random_env.close() | |
| random_mean_reward = float( | |
| np.mean(random_rewards) | |
| ) | |
| random_std_reward = float( | |
| np.std(random_rewards) | |
| ) | |
| # ------------------------------------------------------------ | |
| # 3. κ²°μ λ‘ μ μ¬μ νλ μμ± | |
| # ------------------------------------------------------------ | |
| replay_raw_env = make_vec_env( | |
| env_id, | |
| n_envs=1, | |
| seed=replay_seed, | |
| ) | |
| replay_env = VecNormalize.load( | |
| stats_path, | |
| replay_raw_env, | |
| ) | |
| replay_env.training = False | |
| replay_env.norm_reward = False | |
| actions = [] | |
| reference_reward = 0.0 | |
| try: | |
| obs = replay_env.reset() | |
| environment_limit = int( | |
| replay_raw_env.get_attr( | |
| "spec" | |
| )[0].max_episode_steps | |
| or 1000 | |
| ) | |
| replay_limit = environment_limit | |
| if max_replay_steps is not None: | |
| replay_limit = min( | |
| replay_limit, | |
| max_replay_steps, | |
| ) | |
| for step_index in range(replay_limit): | |
| action, state = model.predict( | |
| obs, | |
| deterministic=True, | |
| ) | |
| if not hasattr( | |
| replay_env.action_space, | |
| "low", | |
| ): | |
| raise TypeError( | |
| "μ΄ λꡬλ μ°μ νλ(Box) νκ²½μ " | |
| "λμμΌλ‘ ν©λλ€." | |
| ) | |
| env_action = np.clip( | |
| action[0], | |
| replay_env.action_space.low, | |
| replay_env.action_space.high, | |
| ).astype(np.float64) | |
| actions.append(env_action.tolist()) | |
| obs, rewards, dones, infos = ( | |
| replay_env.step( | |
| env_action[None, :] | |
| ) | |
| ) | |
| reference_reward += float(rewards[0]) | |
| if bool(dones[0]): | |
| break | |
| finally: | |
| replay_env.close() | |
| if not actions: | |
| raise RuntimeError( | |
| "μ¬μν νλμ μμ±νμ§ λͺ»νμ΅λλ€." | |
| ) | |
| improvement = ( | |
| float(mean_reward) - random_mean_reward | |
| ) | |
| result = { | |
| "env_id": env_id, | |
| "algorithm": "PPO", | |
| "mean_reward": float(mean_reward), | |
| "std_reward": float(std_reward), | |
| "n_eval_episodes": n_eval_episodes, | |
| "eval_seed": eval_seed, | |
| "random_mean_reward": random_mean_reward, | |
| "random_std_reward": random_std_reward, | |
| "n_random_episodes": n_random_episodes, | |
| "random_seed": random_seed, | |
| "improvement_over_random": improvement, | |
| "training_success": improvement > 0.0, | |
| "replay_seed": replay_seed, | |
| "reference_reward": reference_reward, | |
| "actions": actions, | |
| } | |
| print( | |
| json.dumps( | |
| result, | |
| ensure_ascii=False, | |
| ), | |
| flush=True, | |
| ) | |
| """ | |
| GENERIC_RENDER_WORKER = r""" | |
| import json | |
| import os | |
| from pathlib import Path | |
| import gymnasium as gym | |
| import numpy as np | |
| request = json.loads(input()) | |
| env_id = request["env_id"] | |
| replay_seed = int(request["replay_seed"]) | |
| actions = request["actions"] | |
| width = int(request["width"]) | |
| height = int(request["height"]) | |
| video_path_value = request["video_path"] | |
| video_path = ( | |
| Path(video_path_value) | |
| if video_path_value is not None | |
| else None | |
| ) | |
| render_env = gym.make( | |
| env_id, | |
| render_mode="rgb_array", | |
| width=width, | |
| height=height, | |
| ) | |
| frames = [] | |
| episode_reward = 0.0 | |
| steps = 0 | |
| fps = int( | |
| render_env.metadata.get( | |
| "render_fps", | |
| 30, | |
| ) | |
| ) | |
| try: | |
| obs, info = render_env.reset( | |
| seed=replay_seed | |
| ) | |
| first_frame = render_env.render() | |
| if first_frame is None: | |
| raise RuntimeError( | |
| "μ΄κΈ° RGB νλ μ μμ± μ€ν¨" | |
| ) | |
| frames.append( | |
| np.asarray( | |
| first_frame, | |
| dtype=np.uint8, | |
| ) | |
| ) | |
| for step_index, action_values in enumerate( | |
| actions | |
| ): | |
| action = np.asarray( | |
| action_values, | |
| dtype=np.float64, | |
| ) | |
| action = np.clip( | |
| action, | |
| render_env.action_space.low, | |
| render_env.action_space.high, | |
| ) | |
| ( | |
| obs, | |
| reward, | |
| terminated, | |
| truncated, | |
| info, | |
| ) = render_env.step(action) | |
| episode_reward += float(reward) | |
| steps = step_index + 1 | |
| frame = render_env.render() | |
| if frame is None: | |
| raise RuntimeError( | |
| f"{steps}λ²μ§Έ RGB νλ μ μμ± μ€ν¨" | |
| ) | |
| frames.append( | |
| np.asarray( | |
| frame, | |
| dtype=np.uint8, | |
| ) | |
| ) | |
| if terminated or truncated: | |
| break | |
| finally: | |
| render_env.close() | |
| if steps != len(actions): | |
| raise RuntimeError( | |
| "νλ λ°°μ΄μ λκΉμ§ μ¬μνμ§ λͺ»νμ΅λλ€." | |
| ) | |
| video_bytes = None | |
| if video_path is not None: | |
| import imageio.v2 as imageio | |
| video_path.parent.mkdir( | |
| parents=True, | |
| exist_ok=True, | |
| ) | |
| imageio.mimsave( | |
| video_path, | |
| frames, | |
| fps=fps, | |
| codec="libx264", | |
| quality=8, | |
| macro_block_size=16, | |
| ) | |
| if ( | |
| not video_path.is_file() | |
| or video_path.stat().st_size == 0 | |
| ): | |
| raise RuntimeError( | |
| "λΉλμ€ νμΌ μμ± μ€ν¨" | |
| ) | |
| video_bytes = video_path.stat().st_size | |
| result = { | |
| "render_backend": os.environ["MUJOCO_GL"], | |
| "rendered_reward": episode_reward, | |
| "steps": steps, | |
| "frames": len(frames), | |
| "fps": fps, | |
| "width": width, | |
| "height": height, | |
| "video_path": ( | |
| str(video_path) | |
| if video_path is not None | |
| else None | |
| ), | |
| "video_bytes": video_bytes, | |
| } | |
| print( | |
| json.dumps( | |
| result, | |
| ensure_ascii=False, | |
| ), | |
| flush=True, | |
| ) | |
| """ | |
| def _run_json_worker( | |
| *, | |
| code, | |
| payload, | |
| worker_env, | |
| label, | |
| timeout_seconds, | |
| ): | |
| result = subprocess.run( | |
| [ | |
| sys.executable, | |
| "-u", | |
| "-X", | |
| "faulthandler", | |
| "-c", | |
| code, | |
| ], | |
| env=worker_env, | |
| input=json.dumps( | |
| payload, | |
| ensure_ascii=False, | |
| ), | |
| text=True, | |
| capture_output=True, | |
| timeout=timeout_seconds, | |
| ) | |
| if result.returncode != 0: | |
| raise RuntimeError( | |
| f"{label} μ€ν¨ " | |
| f"(return code {result.returncode})\n" | |
| f"stdout tail:\n" | |
| f"{result.stdout[-3000:]}\n" | |
| f"stderr tail:\n" | |
| f"{result.stderr[-6000:]}" | |
| ) | |
| try: | |
| return json.loads( | |
| result.stdout.strip().splitlines()[-1] | |
| ) | |
| except ( | |
| IndexError, | |
| json.JSONDecodeError, | |
| ) as exc: | |
| raise RuntimeError( | |
| f"{label} κ²°κ³Ό JSON νμ± μ€ν¨" | |
| ) from exc | |
| def evaluate_and_render_ppo_mujoco( | |
| *, | |
| env_id, | |
| model_path, | |
| vecnormalize_path, | |
| video_path=None, | |
| device="cpu", | |
| n_eval_episodes=30, | |
| n_random_episodes=30, | |
| eval_seed=1000, | |
| random_seed=2000, | |
| replay_seed=42, | |
| max_replay_steps=None, | |
| width=640, | |
| height=480, | |
| backends=("egl", "osmesa"), | |
| timeout_seconds=180, | |
| ): | |
| """ | |
| PPO/VecNormalize λͺ¨λΈμ νκ°νκ³ MuJoCo μμμ | |
| λ³λ νλ‘μΈμ€μμ μμ νκ² λ λλ§ν©λλ€. | |
| video_path=Noneμ΄λ©΄ νλ μ μμ±κΉμ§λ§ κ²μ¬νκ³ | |
| νμΌμ λ§λ€μ§ μμ΅λλ€. | |
| """ | |
| model_path = Path(model_path) | |
| vecnormalize_path = Path( | |
| vecnormalize_path | |
| ) | |
| for path in ( | |
| model_path, | |
| vecnormalize_path, | |
| ): | |
| if not path.is_file(): | |
| raise FileNotFoundError(path) | |
| if n_eval_episodes < 1: | |
| raise ValueError( | |
| "n_eval_episodesλ 1 μ΄μμ΄μ΄μΌ ν©λλ€." | |
| ) | |
| if n_random_episodes < 1: | |
| raise ValueError( | |
| "n_random_episodesλ 1 μ΄μμ΄μ΄μΌ ν©λλ€." | |
| ) | |
| if not backends: | |
| raise ValueError( | |
| "λ λλ§ λ°±μλκ° νλ μ΄μ νμν©λλ€." | |
| ) | |
| evaluation_payload = { | |
| "env_id": env_id, | |
| "model_path": str(model_path), | |
| "stats_path": str(vecnormalize_path), | |
| "device": device, | |
| "n_eval_episodes": n_eval_episodes, | |
| "n_random_episodes": ( | |
| n_random_episodes | |
| ), | |
| "eval_seed": eval_seed, | |
| "random_seed": random_seed, | |
| "replay_seed": replay_seed, | |
| "max_replay_steps": ( | |
| max_replay_steps | |
| ), | |
| } | |
| evaluation_env = os.environ.copy() | |
| evaluation_env.pop("MUJOCO_GL", None) | |
| evaluation_env.pop( | |
| "PYOPENGL_PLATFORM", | |
| None, | |
| ) | |
| evaluation_result = _run_json_worker( | |
| code=GENERIC_EVALUATION_WORKER, | |
| payload=evaluation_payload, | |
| worker_env=evaluation_env, | |
| label="SB3 νκ°Β·νλ μμ±", | |
| timeout_seconds=timeout_seconds, | |
| ) | |
| actions = evaluation_result.pop( | |
| "actions" | |
| ) | |
| render_payload = { | |
| "env_id": env_id, | |
| "replay_seed": replay_seed, | |
| "actions": actions, | |
| "width": width, | |
| "height": height, | |
| "video_path": ( | |
| str(Path(video_path)) | |
| if video_path is not None | |
| else None | |
| ), | |
| } | |
| render_errors = {} | |
| render_result = None | |
| for backend in backends: | |
| worker_env = os.environ.copy() | |
| worker_env["MUJOCO_GL"] = backend | |
| worker_env[ | |
| "PYOPENGL_PLATFORM" | |
| ] = backend | |
| worker_env.pop("DISPLAY", None) | |
| try: | |
| render_result = _run_json_worker( | |
| code=GENERIC_RENDER_WORKER, | |
| payload=render_payload, | |
| worker_env=worker_env, | |
| label=( | |
| "MuJoCo λ λλ§ " | |
| f"({backend})" | |
| ), | |
| timeout_seconds=( | |
| timeout_seconds | |
| ), | |
| ) | |
| break | |
| except RuntimeError as exc: | |
| render_errors[backend] = str(exc) | |
| if render_result is None: | |
| raise RuntimeError( | |
| "λͺ¨λ λ λλ§ λ°±μλκ° μ€ν¨νμ΅λλ€:\n" | |
| + json.dumps( | |
| render_errors, | |
| indent=2, | |
| ensure_ascii=False, | |
| ) | |
| ) | |
| reference_reward = float( | |
| evaluation_result[ | |
| "reference_reward" | |
| ] | |
| ) | |
| rendered_reward = float( | |
| render_result["rendered_reward"] | |
| ) | |
| replay_verified = math.isclose( | |
| reference_reward, | |
| rendered_reward, | |
| rel_tol=1e-7, | |
| abs_tol=1e-7, | |
| ) | |
| if not replay_verified: | |
| raise RuntimeError( | |
| "λμΌ νλ μ¬μ 보μ λΆμΌμΉ: " | |
| f"reference={reference_reward}, " | |
| f"rendered={rendered_reward}" | |
| ) | |
| if ( | |
| render_result["frames"] | |
| != render_result["steps"] + 1 | |
| ): | |
| raise RuntimeError( | |
| "νλ μ μκ° steps + 1κ³Ό " | |
| "μΌμΉνμ§ μμ΅λλ€." | |
| ) | |
| return { | |
| **evaluation_result, | |
| **render_result, | |
| "replay_reward_verified": True, | |
| } | |