# ๐Ÿฆพ Pusher-v5 PPO // ์ธ๊ณต์ง€๋Šฅ ๋กœ๋ณดํ‹ฑ์Šค ๊ด€์ œ ํ—ˆ๋ธŒ & ์‹ค์‹œ๊ฐ„ ์ œ์–ด ์ฝ•ํ• [![Language: English](https://img.shields.io/badge/Language-English-blue)](README.md) [![Language: ํ•œ๊ตญ์–ด](https://img.shields.io/badge/Language-ํ•œ๊ตญ์–ด-green)](README_KR.md) [![Hugging Face Hub](https://img.shields.io/badge/๐Ÿค—%20Hugging%20Face-Model%20Hub-orange)](https://huggingface.co/hwihwalab/pusher-v5-ppo) [![GitHub Repository](https://img.shields.io/badge/GitHub-Repository-181717?style=flat&logo=github)](https://github.com/Hwihwa-Lab/pusher-v5-ppo) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/Hwihwa-Lab/pusher-v5-ppo/blob/main/LICENSE) [![Gymnasium](https://img.shields.io/badge/Gymnasium-MuJoCo%20Pusher--v5-0080FF)](https://gymnasium.farama.org/environments/mujoco/pusher/) [![PyTorch](https://img.shields.io/badge/PyTorch-2.0+-EE4C2C?style=flat&logo=pytorch)](https://pytorch.org) [![Stable-Baselines3](https://img.shields.io/badge/Stable--Baselines3-PPO-brightgreen)](https://stable-baselines3.readthedocs.io) > **Gymnasium MuJoCo 7์ž์œ ๋„ ๋กœ๋ด‡ ํŒ” ์—ฐ์† ์ œ์–ด ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ & PPO ์‹ฌ์ธต ๊ฐ•ํ™”ํ•™์Šต ํ†ตํ•ฉ ํ”Œ๋žซํผ** > *[ ๐ŸŒ English Documentation ](README.md) | [ ๐Ÿ‡ฐ๐Ÿ‡ท ํ•œ๊ตญ์–ด ๋งค๋‰ด์–ผ ](README_KR.md)* ๋ณธ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋Š” [Gymnasium](https://gymnasium.farama.org/environments/mujoco/pusher/)์˜ ๋ฌผ๋ฆฌ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ํ™˜๊ฒฝ์ธ `Pusher-v5`์—์„œ 7์ž์œ ๋„(7-DOF) ๋กœ๋ด‡ ํŒ”์ด ์›ํ†ตํ˜• ๋ฌผ์ฒด๋ฅผ ๋ชฉํ‘œ ์ง€์ ๊นŒ์ง€ ์ •ํ™•ํ•˜๊ฒŒ ๋ฐ€์–ด ๋„ฃ๋„๋ก ํ•™์Šต์‹œํ‚ค๋Š” **Stable-Baselines3 PPO ๊ฐ•ํ™”ํ•™์Šต ์‹œ์Šคํ…œ** ๋ฐ **์‹ค์‹œ๊ฐ„ 30 FPS ์›น ๊ด€์ œ ์ฝ•ํ•(FastAPI + WebSocket)**์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. --- ## ๐ŸŒŸ ํ•ต์‹ฌ ์„ฑ๋Šฅ ๋ฐ ๋ชจ๋ธ ์ŠคํŽ™ | ํ•ญ๋ชฉ | ์ƒ์„ธ ๊ทœ๊ฒฉ ๋ฐ ๋ฒค์น˜๋งˆํฌ ๊ฒฐ๊ณผ | | :--- | :--- | | **ํ•™์Šต ํ™˜๊ฒฝ** | Gymnasium MuJoCo `Pusher-v5` (7์ž์œ ๋„ ๋กœ๋ด‡ ๋งค๋‹ˆํ“ฐ๋ ˆ์ดํ„ฐ) | | **๊ด€์ธก ๊ณต๊ฐ„ (Observation)** | 23์ฐจ์› ์—ฐ์† ๋ฒกํ„ฐ (๊ด€์ ˆ ๊ฐ๋„ 7, ๊ด€์ ˆ ๊ฐ์†๋„ 7, ํŒ ์œ„์น˜ 3, ๋ฌผ์ฒด ์œ„์น˜ 3, ๊ณจ ์œ„์น˜ 3) | | **ํ–‰๋™ ๊ณต๊ฐ„ (Action)** | 7์ฐจ์› ์—ฐ์† ๋ชจํ„ฐ ํ† ํฌ ์ œ์–ด๊ฐ’ (`Box[-2.0, 2.0]`, float32) | | **ํ•™์Šต ์•Œ๊ณ ๋ฆฌ์ฆ˜** | Proximal Policy Optimization (PPO, `MlpPolicy`) | | **๋ฐฑ์—”๋“œ ํ”„๋ ˆ์ž„์›Œํฌ** | Stable-Baselines3 / PyTorch / FastAPI / Starlette WebSockets | | **์ดˆ๊ธฐ ๊ธฐ์ดˆ ์ ์ˆ˜ (Step 0)** | **`-57.51 pts`** (๋ฌด์ž‘์œ„ ๊ด€์ ˆ ํƒ์ƒ‰, ํŒ”-๋ฌผ์ฒด ๊ฑฐ๋ฆฌ 0.215m) | | **์ตœ์ข… ์ˆ˜๋ ด ์ ์ˆ˜ (Step 300k+)**| **`-32.42 ยฑ 4.30 pts`** *(์ตœ๊ณ  ์—ํ”ผ์†Œ๋“œ: **`-26.15 pts`**)* | | **ํŒ”-๋ฌผ์ฒด ์ ‘์ด‰ ์ •๋ฐ€๋„** | **`0.028 m (2.8cm)`** (์›ํ†ต ๋ฌผ์ฒด ์™„๋ฒฝ ํฌ์ฐฉ ๋ฐ ๋ฐ€์ฐฉ) | | **๋ชฉํ‘œ ์ง€์  ๊ทผ์ ‘ ์ •๋ฐ€๋„** | **`0.054 m`** (๋ชฉํ‘œ ๊ณจ๋Œ€ ์•ˆ์ฐฉ ๋ฐ ํ‘ธ์‹ฑ ์™„๋ฃŒ) | --- ## ๐Ÿ›๏ธ ์‹œ์Šคํ…œ ์•„ํ‚คํ…์ฒ˜ ๋ฐ ๋ฐ์ดํ„ฐ ํ๋ฆ„ ```mermaid flowchart TD subgraph Live_Web_Cockpit ["1-ํ™”๋ฉด ์ œ๋กœ ์Šคํฌ๋กค ๋กœ๋ณดํ‹ฑ์Šค ๊ด€์ œ ์ฝ•ํ•"] W1["HTML5 / CSS3 / Vanilla JS ํด๋ผ์ด์–ธํŠธ"] <-->|"WebSocket /ws/simulation @ 30 FPS"| S1["FastAPI ๊ณ ์„ฑ๋Šฅ ์„œ๋ฒ„"] S1 -->|"Base64 JPEG ๋ฌผ๋ฆฌ ์ŠคํŠธ๋ฆผ"| W1 S1 -->|"7-DOF ๋ฐ”์ดํด๋ผ ๋ชจํ„ฐ ํ† ํฌ (-2.0 ~ +2.0 Nm)"| W1 S1 -->|"3D ๋ฐ์นด๋ฅดํŠธ ์ขŒํ‘œ: ํŒ, ๋ฌผ์ฒด, ๊ณจ๋Œ€"| W1 W1 -->|"์ œ์–ด ๋ช…๋ น: Start, Pause, Step, Reset, Policy"| S1 end subgraph Analytics_Deck ["4-ํƒญ ๋ถ„์„ ๋ฐ ๋ฆฌํ”Œ๋ ˆ์ด ๋ฑ"] T1["1ํƒญ: ์‹ค์‹œ๊ฐ„ ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ์ฐจํŠธ (Raw ๋ณด์ƒ & 20-Ep ์ด๋™ํ‰๊ท ์„ )"] T2["2ํƒญ: ๋งˆ์ผ์Šคํ†ค ๋ฆฌํ”Œ๋ ˆ์ด ๊ฐค๋Ÿฌ๋ฆฌ (16:9 ์™€์ด๋“œ ๋น„๋””์˜ค ์นด๋“œ)"] T3["3ํƒญ: ์‹ค์‹œ๊ฐ„ PPO ํ„ฐ๋ฏธ๋„ ๋กœ๊ทธ (ํ•™์Šต ์ง„ํ–‰๋ฅ  & FPS ์ฝ˜์†”)"] T4["4ํƒญ: ํ™˜๊ฒฝ ์ŠคํŽ™ & ๋ณด์ƒ ํ•จ์ˆ˜ ์ˆ˜ํ•™ ๊ณต์‹"] end subgraph Deep_RL_Pipeline ["PPO ๊ฐ•ํ™”ํ•™์Šต ํŒŒ์ดํ”„๋ผ์ธ"] TR1["train.py / ๋ฐฑ๊ทธ๋ผ์šด๋“œ ํ•™์Šต ์Šค๋ ˆ๋“œ"] --> TR2["Pusher-v5 ๋ฌผ๋ฆฌ ์—”์ง„"] TR2 --> TR3["VisualProgressCallback"] TR3 --> TR4["Step 0๋ถ€ํ„ฐ 300k๊นŒ์ง€์˜ ๋น„๋””์˜ค(MP4) ๋ฐ GIF"] TR3 --> TR5["4๋ถ„ํ•  ํ•™์Šต ๋ถ„์„ ๊ทธ๋ž˜ํ”„ & metrics.json"] TR4 & TR5 --> TR6["์›ํด๋ฆญ ์••์ถ• ํŒจํ‚ค์ง€: ppo_pusher_bundle.zip"] end ``` --- ## ๐ŸŽฎ ๊ด€์ œ ์„ผํ„ฐ ์ฃผ์š” ๊ธฐ๋Šฅ 1. **์ดˆ์ €์ง€์—ฐ 30 FPS ์‹ค์‹œ๊ฐ„ ๋ฌผ๋ฆฌ ์บ”๋ฒ„์Šค**: - ์›น์†Œ์ผ“์„ ํ†ตํ•œ ๊ณ ์† ๋ Œ๋”๋ง ์ŠคํŠธ๋ฆผ ๋ฐ ์‹ค์‹œ๊ฐ„ FPS ์นด์šดํ„ฐ. - 7๊ฐœ ๊ด€์ ˆ ๋ชจํ„ฐ ํ† ํฌ๋ฅผ ์ค‘์•™ 0.0 ๊ธฐ์ค€์œผ๋กœ ์–‘์ˆ˜(์ฒญ๋ก์ƒ‰, Cyan)์™€ ์Œ์ˆ˜(์žฅ๋ฏธ์ƒ‰, Rose)๋กœ ์‹ค์‹œ๊ฐ„ ์‹œ๊ฐํ™”ํ•˜๋Š” **7-DOF ๋ฐ”์ดํด๋ผ ํ† ํฌ ๊ฒŒ์ด์ง€**. - ๋กœ๋ด‡ ์†๊ฐ€๋ฝ ๋(Tip), ๋ฌผ์ฒด(Object), ๊ณจ๋Œ€(Goal)์˜ 3D ๊ณต๊ฐ„ ์ขŒํ‘œ X, Y, Z ๋ฏธํ„ฐ ๋‹จ์œ„ ์ •๋ฐ€ ์ถ”์ . - ํ‚ค๋ณด๋“œ ๋‹จ์ถ•ํ‚ค ์ง€์› (Space ์‹œ์ž‘/์ผ์‹œ์ •์ง€, R ๋ฆฌ์…‹, S 1์Šคํ… ์ „์ง„, H HUD ์˜จ์˜คํ”„). 2. **๊ฐ•ํ™”ํ•™์Šต ์˜ˆ์‚ฐ ํ”„๋ฆฌ์…‹ ์…€๋ ‰ํ„ฐ**: - `500 Ep (50k Steps โ€ข ~12s) - ๋น ๋ฅธ ํ…Œ์ŠคํŠธ` - `2,000 Ep (200k Steps โ€ข ~45s) - ๊ธฐ๋ณธ ํ‘ธ์‹ฑ ํ•™์Šต` - `5,000 Ep (500k Steps โ€ข ~1.8m) โ˜… ์ถ”์ฒœ ์™„์„ฑํ˜• ์ •์ฑ…` - `10,000 Ep (1M Steps โ€ข ~3.5m) - ์ดˆ์ •๋ฐ€ ์ˆ˜๋ ด` - `โš™๏ธ ์‚ฌ์šฉ์ž ์ •์˜(Custom) ์Šคํ… ์„ค์ •` 3. **16:9 ์™€์ด๋“œ ๋ฉ€ํ‹ฐ ๋น„๋””์˜ค ์นด๋“œ ๊ฐค๋Ÿฌ๋ฆฌ**: - ์Šคํ… 0๋ถ€ํ„ฐ ์ตœ์ข… ์Šคํ…๊นŒ์ง€ AI์˜ ์„ฑ์žฅ ๊ณผ์ •์„ ๋„ทํ”Œ๋ฆญ์Šค ์ธ๋„ค์ผ์ฒ˜๋Ÿผ ํ•œ๋ˆˆ์— ๊ฐ€๋กœ๋กœ ๋น„๊ตํ•˜๋Š” ๋น„๋””์˜ค ์นด๋“œ ๋ฆด. - ์นด๋“œ๋ณ„ ๋…๋ฆฝ **`MP4 ๋น„๋””์˜ค`** ๋ฐ **`GIF ์• ๋‹ˆ๋ฉ”์ด์…˜`** ์ฆ‰์‹œ ๋‹ค์šด๋กœ๋“œ ๊ธฐ๋Šฅ. 4. **๋‹จ์ผ ZIP ํŒŒ์ผ ์ž๋™ ํŒจํ‚ค์ง•**: - ์ƒ๋‹จ `Download Bundle` ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ํ•™์Šต๋œ ๋ชจ๋ธ ๊ฐ€์ค‘์น˜(`ppo_pusher.zip`), ๋น„๋””์˜ค, ์ฐจํŠธ, ๋ฉ”ํŠธ๋ฆญ์ด ํ•˜๋‚˜์˜ ์••์ถ• ํŒŒ์ผ(`ppo_pusher_bundle.zip`)๋กœ ์ฆ‰์‹œ ๋‹ค์šด๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค. --- ## ๐Ÿš€ ๋น ๋ฅธ ์‹œ์ž‘ ๊ฐ€์ด๋“œ (Quickstart) ### 1. ํ™˜๊ฒฝ ์„ค์น˜ ```bash git clone https://github.com/Hwihwa-Lab/pusher-v5-ppo.git cd pusher-v5-ppo pip install -r requirements.txt ``` ### 2. ์‹ค์‹œ๊ฐ„ ์›น ๊ด€์ œ ์„ผํ„ฐ ์‹คํ–‰ ```bash python app.py ``` ๋ธŒ๋ผ์šฐ์ €์—์„œ **`http://localhost:8000`** ์ ‘์†. ### 3. ํ—ˆ๊น…ํŽ˜์ด์Šค ์›ํด๋ฆญ ์ž๋™ ๋ฐฐํฌ ```bash python deploy_to_hf.py ``` ### 4. CLI ๊ธฐ๋ฐ˜ ๋…๋ฆฝ ํ•™์Šต ๋ฐ ๋ชจ๋ธ ํ‰๊ฐ€ ```bash # PPO ์—์ด์ „ํŠธ ํ•™์Šต ์‹คํ–‰ python train.py --timesteps 300000 --eval_freq 30000 # ํ•™์Šต ์™„๋ฃŒ๋œ ๋ชจ๋ธ ๋…๋ฆฝ ํ‰๊ฐ€ ๋ฐ ๋น„๋””์˜ค ์ถ”์ถœ python evaluate.py --model_path ./results/ppo_pusher.zip --episodes 5 ``` --- ## ๐Ÿ 5์ค„ ํŒŒ์ด์ฌ ๋น ๋ฅธ ํ‰๊ฐ€ ์Šค๋‹ˆํŽซ (Quick Evaluation) ๋ณธ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์˜ ํ•™์Šต ์™„๋ฃŒ ๊ฐ€์ค‘์น˜๋ฅผ ๋ถˆ๋Ÿฌ์™€ 5์ค„์˜ ํŒŒ์ด์ฌ ์ฝ”๋“œ๋กœ ์ฆ‰์‹œ ์‹œ๋ฎฌ๋ ˆ์ด์…˜์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: ```python import gymnasium as gym from stable_baselines3 import PPO # 1. Pusher-v5 ํ™˜๊ฒฝ ์ดˆ๊ธฐํ™” ๋ฐ ์™„์„ฑ ๊ฐ€์ค‘์น˜ ๋กœ๋“œ env = gym.make("Pusher-v5", render_mode="human") model = PPO.load("results/ppo_pusher.zip") # 2. ๊ฒฐ์ •๋ก ์  ํ‘ธ์‹ฑ ์ œ์–ด ๋กค์•„์›ƒ ์‹คํ–‰ obs, _ = env.reset() done = False while not done: action, _ = model.predict(obs, deterministic=True) obs, reward, terminated, truncated, _ = env.step(action) done = terminated or truncated env.close() ``` --- ## โŒจ๏ธ ํ‚ค๋ณด๋“œ ๋‹จ์ถ•ํ‚ค ์•ˆ๋‚ด (Keyboard Shortcuts) | ๋‹จ์ถ•ํ‚ค | ์กฐ์ž‘ ๊ธฐ๋Šฅ | ์„ค๋ช… | | :---: | :--- | :--- | | **`Space`** | **์‹œ์ž‘ / ์ผ์‹œ์ •์ง€** | ์‹ค์‹œ๊ฐ„ 30 FPS MuJoCo ๋ฌผ๋ฆฌ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ํ† ๊ธ€ | | **`R`** | **ํ™˜๊ฒฝ ์ดˆ๊ธฐํ™” (Reset)** | ๋กœ๋ด‡ ํŒ”, ์›ํ†ต ๋ฌผ์ฒด, ๋ชฉํ‘œ ๊ณจ๋Œ€๋ฅผ ์ƒˆ๋กœ์šด ๋žœ๋ค ์œ„์น˜๋กœ ์žฌ๋ฐฐ์น˜ | | **`S`** | **1์Šคํ… ์ „์ง„ (Step Once)** | ๋ฌผ๋ฆฌ ์—”์ง„์„ 1๋‹จ์œ„ ํƒ€์ž„์Šคํ…(0.05์ดˆ) ์ „์ง„ | | **`H`** | **HUD ์˜จ์˜คํ”„ ํ† ๊ธ€** | ์บ”๋ฒ„์Šค ํ™”๋ฉด ์œ„ ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ์˜ค๋ฒ„๋ ˆ์ด ํ‘œ์‹œ/์ˆจ๊น€ | --- ## ๐Ÿ›ก๏ธ AI ์—”์ง€๋‹ˆ์–ด๋ง ๊ฑฐ๋ฒ„๋„Œ์Šค ๋ฐ ๋ฌธ์„œ ์ฒด๊ณ„ ๋ณธ ์‹œ์Šคํ…œ์€ ๊ฐ•ํ™”ํ•™์Šต ์‹œ๋ฎฌ๋ ˆ์ด์…˜์˜ ๋ฌผ๋ฆฌ์  ๋ฌด๊ฒฐ์„ฑ์„ ๋ณด์กดํ•˜๊ณ  ๋ฐ”์ด๋ธŒ-์ฝ”๋”ฉ ๋“œ๋ฆฌํ”„ํŠธ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์ •๋ฐ€ํ•œ ์—”์ง€๋‹ˆ์–ด๋ง ๋ฌธ์„œ ํ”„๋กœํ† ์ฝœ(GitHub ์ œ๊ณต)์„ ์ค€์ˆ˜ํ•ฉ๋‹ˆ๋‹ค: - **[`.cursorrules`](https://github.com/Hwihwa-Lab/pusher-v5-ppo/blob/main/.cursorrules)**: AI ์ฝ”๋”ฉ ๋ฐฉ์–ด ๋ฐ ๊ทœ์น™ ๋งˆ์Šคํ„ฐ ํ—Œ๋ฒ• - **[`DOCS_AI_CODING_PROTOCOL.md`](https://github.com/Hwihwa-Lab/pusher-v5-ppo/blob/main/DOCS_AI_CODING_PROTOCOL.md)**: ์ฝ”๋”ฉ ํ‘œ์ค€ ๋ฐ ์ „์ฒด ๋ฌธ์„œ ๋งต - **[`DOCS_SYSTEM_ARCHITECTURE.md`](https://github.com/Hwihwa-Lab/pusher-v5-ppo/blob/main/DOCS_SYSTEM_ARCHITECTURE.md)**: ํ’€์Šคํƒ ์‹œ์Šคํ…œ ๋ฐ WebSocket ์•„ํ‚คํ…์ฒ˜ ๋ช…์„ธ์„œ - **[`DOCS_DATA_SCHEMA.md`](https://github.com/Hwihwa-Lab/pusher-v5-ppo/blob/main/DOCS_DATA_SCHEMA.md)**: ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ํŒจํ‚ท ํ”„๋กœํ† ์ฝœ ๋ฐ REST ๋ฐ์ดํ„ฐ ์Šคํ‚ค๋งˆ - **[`DOCS_MODEL_EVALUATION_AND_HF_DEPLOY.md`](https://github.com/Hwihwa-Lab/pusher-v5-ppo/blob/main/DOCS_MODEL_EVALUATION_AND_HF_DEPLOY.md)**: ๋ฒค์น˜๋งˆํฌ ํ‰๊ฐ€ ๋ฐ ํ—ˆ๊น…ํŽ˜์ด์Šค ๋ฐฐํฌ ๊ทœ๊ฒฉ์„œ ## ๐Ÿ“‚ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํŒŒ์ผ ๊ตฌ์„ฑ (Repository Contents) * `README.md`: ์˜๋ฌธ ๊ธ€๋กœ๋ฒŒ ๋ชจ๋ธ ์นด๋“œ ๋ฐ ๋ฒค์น˜๋งˆํฌ ๊ฐ€์ด๋“œ. * `README_KR.md`: ํ•œ๊ตญ์–ด ์ข…ํ•ฉ ๊ธฐ์ˆ  ๋งค๋‰ด์–ผ ([ํ•œ๊ตญ์–ด ๋งค๋‰ด์–ผ](README_KR.md)). * `app.py`: FastAPI ๊ณ ์„ฑ๋Šฅ ๋ฐฑ์—”๋“œ ๋ฐ 30 FPS WebSocket ๋ฌผ๋ฆฌ ์ŠคํŠธ๋ฆฌ๋ฐ ์„œ๋ฒ„. * `train.py`: Stable-Baselines3 PPO 7์ž์œ ๋„ ๋กœ๋ด‡ ํŒ” ๊ฐ•ํ™”ํ•™์Šต ์—”์ง„ (`VisualProgressCallback` ๋‚ด์žฅ). * `evaluate.py`: ๋…๋ฆฝ 5ํšŒ ์—ฐ์† ๋กค์•„์›ƒ ์„ฑ๋Šฅ ํ‰๊ฐ€๊ธฐ ๋ฐ ๋น„๋””์˜ค ๋…นํ™”๊ธฐ. * `visualizer.py`: ๋…๋ฆฝ Matplotlib ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ ์‹œ๊ฐํ™” ๋ฐ ๊ทธ๋ž˜ํ”„ ์ƒ์„ฑ ๋ชจ๋“ˆ. * `web/`: 1-ํ™”๋ฉด ์ œ๋กœ ์Šคํฌ๋กค ์›น ๊ด€์ œ ์ฝ•ํ• ํ”„๋ก ํŠธ์—”๋“œ (`app.js`, `index.html`, `style.css`). * `results/ppo_pusher.zip`: 300,000 ์Šคํ… ์™„์„ฑํ˜• PPO ์‹ ๊ฒฝ๋ง ๊ฐ€์ค‘์น˜ (ํ‰๊ท  -32.4 pts). * `ppo_pusher_bundle.zip`: ๊ฐ€์ค‘์น˜, 12๊ฐœ ์ฒดํฌํฌ์ธํŠธ ๋น„๋””์˜ค, ๋ถ„์„ ์ฐจํŠธ๊ฐ€ ํฌํ•จ๋œ ๋‹จ์ผ ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ ์••์ถ•๋ณธ. * `deploy_to_hf.py`: ํ—ˆ๊น…ํŽ˜์ด์Šค ๋ชจ๋ธ ํ—ˆ๋ธŒ ์›ํด๋ฆญ ์ž๋™ ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ. * `requirements.txt` & `packages.txt`: ํŒŒ์ด์ฌ ํŒจํ‚ค์ง€ ๋ฐ OS ์˜์กด์„ฑ ๋ช…์„ธ์„œ. --- ## ๐Ÿ”— ์˜คํ”ˆ์†Œ์Šค ๊ณต์‹ ๋งํฌ (Open Source Hubs) - ๐Ÿ™ **GitHub ์ €์žฅ์†Œ**: [https://github.com/Hwihwa-Lab/pusher-v5-ppo](https://github.com/Hwihwa-Lab/pusher-v5-ppo) - ๐Ÿค— **Hugging Face ๋ชจ๋ธ ํ—ˆ๋ธŒ**: [https://huggingface.co/hwihwalab/pusher-v5-ppo](https://huggingface.co/hwihwalab/pusher-v5-ppo) --- ## ๐Ÿ“„ ๋ผ์ด์„ ์Šค ๋ณธ ํ”„๋กœ์ ํŠธ๋Š” [MIT License](https://github.com/Hwihwa-Lab/pusher-v5-ppo/blob/main/LICENSE)๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. --- *Trained and deployed with [Pusher AI Hub](https://huggingface.co/hwihwalab/pusher-v5-ppo) by **hwihwalab**.*