tinybaselines 0.1.0a1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tinybaselines-0.1.0a1/LICENSE +21 -0
- tinybaselines-0.1.0a1/MANIFEST.in +1 -0
- tinybaselines-0.1.0a1/PKG-INFO +94 -0
- tinybaselines-0.1.0a1/README.md +82 -0
- tinybaselines-0.1.0a1/examples/__init__.py +0 -0
- tinybaselines-0.1.0a1/examples/lunar_lander/__init__.py +0 -0
- tinybaselines-0.1.0a1/examples/lunar_lander/config.py +5 -0
- tinybaselines-0.1.0a1/examples/lunar_lander/reference.py +335 -0
- tinybaselines-0.1.0a1/examples/lunar_lander/requirements-reference.txt +2 -0
- tinybaselines-0.1.0a1/examples/lunar_lander/requirements.txt +4 -0
- tinybaselines-0.1.0a1/examples/lunar_lander/results/2026-10-26/phase-initial-tinygrad-thread4.jsonl +1 -0
- tinybaselines-0.1.0a1/examples/lunar_lander/results/2026-10-26/phase-initial-torch.jsonl +6 -0
- tinybaselines-0.1.0a1/examples/lunar_lander/results/2026-10-26/phase-protocol.json +53 -0
- tinybaselines-0.1.0a1/examples/lunar_lander/results/2026-10-26/phase-tinygrad-thread10.jsonl +6 -0
- tinybaselines-0.1.0a1/examples/lunar_lander/results/2026-10-26/phase-tinygrad-thread4-error.jsonl +2 -0
- tinybaselines-0.1.0a1/examples/lunar_lander/results/2026-10-26/phase-torch-thread4.jsonl +6 -0
- tinybaselines-0.1.0a1/examples/lunar_lander/results/2026-10-26/protocol.json +15 -0
- tinybaselines-0.1.0a1/examples/lunar_lander/results/2026-10-26/tinygrad-cpu.jsonl +8 -0
- tinybaselines-0.1.0a1/examples/lunar_lander/results/2026-10-26/torch-cpu.jsonl +8 -0
- tinybaselines-0.1.0a1/examples/lunar_lander/run_learning.py +81 -0
- tinybaselines-0.1.0a1/examples/lunar_lander/train.py +156 -0
- tinybaselines-0.1.0a1/examples/pendulum/__init__.py +0 -0
- tinybaselines-0.1.0a1/examples/pendulum/env.py +10 -0
- tinybaselines-0.1.0a1/examples/pendulum/requirements.txt +2 -0
- tinybaselines-0.1.0a1/examples/pendulum/results/cpu-2.jsonl +101 -0
- tinybaselines-0.1.0a1/examples/pendulum/results/cpu-3.jsonl +101 -0
- tinybaselines-0.1.0a1/examples/pendulum/results/cpu.jsonl +101 -0
- tinybaselines-0.1.0a1/examples/pendulum/train.py +70 -0
- tinybaselines-0.1.0a1/pyproject.toml +19 -0
- tinybaselines-0.1.0a1/requirements.txt +2 -0
- tinybaselines-0.1.0a1/setup.cfg +4 -0
- tinybaselines-0.1.0a1/tests/test_checkpoint.py +138 -0
- tinybaselines-0.1.0a1/tests/test_core.py +86 -0
- tinybaselines-0.1.0a1/tests/test_evaluation.py +76 -0
- tinybaselines-0.1.0a1/tests/test_learning.py +47 -0
- tinybaselines-0.1.0a1/tests/test_learning_run.py +81 -0
- tinybaselines-0.1.0a1/tests/test_parity.py +214 -0
- tinybaselines-0.1.0a1/tests/test_pendulum.py +48 -0
- tinybaselines-0.1.0a1/tests/test_rollout.py +157 -0
- tinybaselines-0.1.0a1/tests/test_training.py +96 -0
- tinybaselines-0.1.0a1/tinybaselines/__init__.py +5 -0
- tinybaselines-0.1.0a1/tinybaselines/evaluation.py +6 -0
- tinybaselines-0.1.0a1/tinybaselines/policies.py +75 -0
- tinybaselines-0.1.0a1/tinybaselines/ppo.py +155 -0
- tinybaselines-0.1.0a1/tinybaselines/rollout.py +80 -0
- tinybaselines-0.1.0a1/tinybaselines/training.py +32 -0
- tinybaselines-0.1.0a1/tinybaselines.egg-info/PKG-INFO +94 -0
- tinybaselines-0.1.0a1/tinybaselines.egg-info/SOURCES.txt +49 -0
- tinybaselines-0.1.0a1/tinybaselines.egg-info/dependency_links.txt +1 -0
- tinybaselines-0.1.0a1/tinybaselines.egg-info/requires.txt +2 -0
- tinybaselines-0.1.0a1/tinybaselines.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Utkarsh Gill
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
recursive-include examples *.py *.txt *.json *.jsonl
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tinybaselines
|
|
3
|
+
Version: 0.1.0a1
|
|
4
|
+
Summary: PPO in tinygrad.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: tinygrad==0.14.0
|
|
10
|
+
Requires-Dist: numpy<3,>=1.26
|
|
11
|
+
Dynamic: license-file
|
|
12
|
+
|
|
13
|
+
# tinybaselines
|
|
14
|
+
|
|
15
|
+
PPO in tinygrad. Continuous actions clipped to `[-1, 1]`.
|
|
16
|
+
|
|
17
|
+
Python 3.11 and clang.
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
python3.11 -m venv .venv
|
|
21
|
+
source .venv/bin/activate
|
|
22
|
+
pip install -e . -r examples/lunar_lander/requirements.txt
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Train, save, and load.
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from statistics import mean
|
|
29
|
+
from tinybaselines import GaussianActorCritic, PPO, evaluate, learn
|
|
30
|
+
from examples.lunar_lander.config import OBS_SCALE
|
|
31
|
+
from examples.lunar_lander.train import make_env
|
|
32
|
+
|
|
33
|
+
env = make_env(4, asynchronous=False)
|
|
34
|
+
policy = GaussianActorCritic(state_dim=8, action_dim=2, hidden_dim=128,
|
|
35
|
+
actor_layers=4, critic_layers=4, observation_scale=OBS_SCALE)
|
|
36
|
+
ppo = PPO(policy, pi_lr=1e-3, vf_lr=1e-3, gamma=0.99, lamda=0.95,
|
|
37
|
+
K_epochs=20, eps_clip=0.2, batch_size=10_000, vf_coef=1.0, entropy_coef=0.001)
|
|
38
|
+
|
|
39
|
+
for step in learn(env, ppo, total_timesteps=2_000_000, rollout_steps=20_000):
|
|
40
|
+
print(step.timesteps, step.rollout_seconds, step.update_seconds)
|
|
41
|
+
|
|
42
|
+
ppo.save('ppo.safetensors')
|
|
43
|
+
ppo = PPO.load('ppo.safetensors')
|
|
44
|
+
policy = ppo.actor_critic
|
|
45
|
+
observations, _ = env.reset()
|
|
46
|
+
actions, raw_actions = policy.act(observations, deterministic=True)
|
|
47
|
+
env.close()
|
|
48
|
+
|
|
49
|
+
eval_env = make_env(4, asynchronous=False)
|
|
50
|
+
returns = evaluate(eval_env, policy, num_episodes=100)
|
|
51
|
+
eval_env.close()
|
|
52
|
+
print(mean(returns))
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`total_timesteps` and `rollout_steps` count transitions across all environments.
|
|
56
|
+
Use a positive multiple of `env.num_envs` for `rollout_steps`. Training stops
|
|
57
|
+
after the first full rollout that reaches the budget. Each step contains completed
|
|
58
|
+
`episode_returns` and collection/update times. The caller owns the environment.
|
|
59
|
+
Use a separate environment for evaluation during training.
|
|
60
|
+
`evaluate` resets its environment and returns complete episode returns. It samples
|
|
61
|
+
actions by default; `deterministic=True` uses clipped policy means.
|
|
62
|
+
`actions` go to the environment; `raw_actions` go into PPO.
|
|
63
|
+
The collector uses Gymnasium vector environments with same-step autoreset.
|
|
64
|
+
For time-limit bootstrapping, the collector reads pre-reset observations from `infos['final_obs']`.
|
|
65
|
+
|
|
66
|
+
PPO checkpoints contain the policy, observation scale, Adam state, and PPO settings.
|
|
67
|
+
Continue training by passing the loaded PPO to `learn` with a new transition budget.
|
|
68
|
+
Each `learn` call starts fresh episodes and a new transition counter.
|
|
69
|
+
`policy.save(path)` exports a policy for inference.
|
|
70
|
+
|
|
71
|
+
The full LunarLander trainer, evaluation, and PyTorch reference live in
|
|
72
|
+
`examples/lunar_lander`.
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
python -m examples.lunar_lander.train
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Pendulum uses three observations and one action. Its example rescales policy actions
|
|
79
|
+
from `[-1, 1]` to torque in `[-2, 2]`.
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
pip install -e . -r examples/pendulum/requirements.txt
|
|
83
|
+
DEV=CPU BEAM=0 python -m examples.pendulum.train --output runs/pendulum
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The output directory contains the configuration, episode returns, update times,
|
|
87
|
+
and a PPO checkpoint. Initial and final evaluations each use 100 fresh stochastic episodes.
|
|
88
|
+
|
|
89
|
+
Three independent four-environment CPU runs reached mean returns of -141.4, -162.2,
|
|
90
|
+
and -150.8 after 200,704 transitions each. Each final evaluation used the reloaded checkpoint.
|
|
91
|
+
Our acceptance cutoff is -200 over 100 fresh stochastic episodes per run; all three passed.
|
|
92
|
+
Gymnasium defines no solved threshold for Pendulum. We chose this cutoff after the first run.
|
|
93
|
+
Run records: [1](examples/pendulum/results/cpu.jsonl),
|
|
94
|
+
[2](examples/pendulum/results/cpu-2.jsonl), [3](examples/pendulum/results/cpu-3.jsonl).
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# tinybaselines
|
|
2
|
+
|
|
3
|
+
PPO in tinygrad. Continuous actions clipped to `[-1, 1]`.
|
|
4
|
+
|
|
5
|
+
Python 3.11 and clang.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
python3.11 -m venv .venv
|
|
9
|
+
source .venv/bin/activate
|
|
10
|
+
pip install -e . -r examples/lunar_lander/requirements.txt
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Train, save, and load.
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from statistics import mean
|
|
17
|
+
from tinybaselines import GaussianActorCritic, PPO, evaluate, learn
|
|
18
|
+
from examples.lunar_lander.config import OBS_SCALE
|
|
19
|
+
from examples.lunar_lander.train import make_env
|
|
20
|
+
|
|
21
|
+
env = make_env(4, asynchronous=False)
|
|
22
|
+
policy = GaussianActorCritic(state_dim=8, action_dim=2, hidden_dim=128,
|
|
23
|
+
actor_layers=4, critic_layers=4, observation_scale=OBS_SCALE)
|
|
24
|
+
ppo = PPO(policy, pi_lr=1e-3, vf_lr=1e-3, gamma=0.99, lamda=0.95,
|
|
25
|
+
K_epochs=20, eps_clip=0.2, batch_size=10_000, vf_coef=1.0, entropy_coef=0.001)
|
|
26
|
+
|
|
27
|
+
for step in learn(env, ppo, total_timesteps=2_000_000, rollout_steps=20_000):
|
|
28
|
+
print(step.timesteps, step.rollout_seconds, step.update_seconds)
|
|
29
|
+
|
|
30
|
+
ppo.save('ppo.safetensors')
|
|
31
|
+
ppo = PPO.load('ppo.safetensors')
|
|
32
|
+
policy = ppo.actor_critic
|
|
33
|
+
observations, _ = env.reset()
|
|
34
|
+
actions, raw_actions = policy.act(observations, deterministic=True)
|
|
35
|
+
env.close()
|
|
36
|
+
|
|
37
|
+
eval_env = make_env(4, asynchronous=False)
|
|
38
|
+
returns = evaluate(eval_env, policy, num_episodes=100)
|
|
39
|
+
eval_env.close()
|
|
40
|
+
print(mean(returns))
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`total_timesteps` and `rollout_steps` count transitions across all environments.
|
|
44
|
+
Use a positive multiple of `env.num_envs` for `rollout_steps`. Training stops
|
|
45
|
+
after the first full rollout that reaches the budget. Each step contains completed
|
|
46
|
+
`episode_returns` and collection/update times. The caller owns the environment.
|
|
47
|
+
Use a separate environment for evaluation during training.
|
|
48
|
+
`evaluate` resets its environment and returns complete episode returns. It samples
|
|
49
|
+
actions by default; `deterministic=True` uses clipped policy means.
|
|
50
|
+
`actions` go to the environment; `raw_actions` go into PPO.
|
|
51
|
+
The collector uses Gymnasium vector environments with same-step autoreset.
|
|
52
|
+
For time-limit bootstrapping, the collector reads pre-reset observations from `infos['final_obs']`.
|
|
53
|
+
|
|
54
|
+
PPO checkpoints contain the policy, observation scale, Adam state, and PPO settings.
|
|
55
|
+
Continue training by passing the loaded PPO to `learn` with a new transition budget.
|
|
56
|
+
Each `learn` call starts fresh episodes and a new transition counter.
|
|
57
|
+
`policy.save(path)` exports a policy for inference.
|
|
58
|
+
|
|
59
|
+
The full LunarLander trainer, evaluation, and PyTorch reference live in
|
|
60
|
+
`examples/lunar_lander`.
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
python -m examples.lunar_lander.train
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Pendulum uses three observations and one action. Its example rescales policy actions
|
|
67
|
+
from `[-1, 1]` to torque in `[-2, 2]`.
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
pip install -e . -r examples/pendulum/requirements.txt
|
|
71
|
+
DEV=CPU BEAM=0 python -m examples.pendulum.train --output runs/pendulum
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The output directory contains the configuration, episode returns, update times,
|
|
75
|
+
and a PPO checkpoint. Initial and final evaluations each use 100 fresh stochastic episodes.
|
|
76
|
+
|
|
77
|
+
Three independent four-environment CPU runs reached mean returns of -141.4, -162.2,
|
|
78
|
+
and -150.8 after 200,704 transitions each. Each final evaluation used the reloaded checkpoint.
|
|
79
|
+
Our acceptance cutoff is -200 over 100 fresh stochastic episodes per run; all three passed.
|
|
80
|
+
Gymnasium defines no solved threshold for Pendulum. We chose this cutoff after the first run.
|
|
81
|
+
Run records: [1](examples/pendulum/results/cpu.jsonl),
|
|
82
|
+
[2](examples/pendulum/results/cpu-2.jsonl), [3](examples/pendulum/results/cpu-3.jsonl).
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import gymnasium as gym
|
|
2
|
+
import numpy as np
|
|
3
|
+
import time
|
|
4
|
+
import torch
|
|
5
|
+
import torch.nn as nn
|
|
6
|
+
import torch.nn.functional as F
|
|
7
|
+
import torch.optim as optim
|
|
8
|
+
import os
|
|
9
|
+
import warnings
|
|
10
|
+
from tqdm import trange
|
|
11
|
+
from .config import ENV_NAME, OBS_SCALE
|
|
12
|
+
from tinybaselines.rollout import RolloutBatch, RolloutState
|
|
13
|
+
|
|
14
|
+
warnings.filterwarnings('ignore', message='pkg_resources is deprecated')
|
|
15
|
+
|
|
16
|
+
state_dim, action_dim = 8, 2
|
|
17
|
+
num_envs = int(os.getenv('NUM_ENVS', 4))
|
|
18
|
+
|
|
19
|
+
max_epochs, steps_per_epoch = 100, 20_000
|
|
20
|
+
log_interval, eval_interval = 5, 5
|
|
21
|
+
eval_episodes = 100
|
|
22
|
+
batch_size, K_epochs = 10_000, 20
|
|
23
|
+
hidden_dim = 128
|
|
24
|
+
actor_layers, critic_layers = 4, 4
|
|
25
|
+
pi_lr, vf_lr = 1e-3, 1e-3
|
|
26
|
+
gamma, gae_lambda, eps_clip = 0.99, 0.95, 0.2
|
|
27
|
+
vf_coef, entropy_coef = 1.0, 0.001
|
|
28
|
+
solved_threshold = 200
|
|
29
|
+
|
|
30
|
+
PLOT = bool(int(os.getenv('PLOT', '0')))
|
|
31
|
+
RENDER = bool(int(os.getenv('RENDER', '0')))
|
|
32
|
+
RENDER_EPISODES = int(os.getenv('RENDER_EPISODES', '3'))
|
|
33
|
+
METAL = bool(int(os.getenv('METAL', '0')))
|
|
34
|
+
|
|
35
|
+
device = torch.device('mps' if METAL and torch.backends.mps.is_available() else 'cuda' if torch.cuda.is_available() else 'cpu')
|
|
36
|
+
|
|
37
|
+
if PLOT:
|
|
38
|
+
import matplotlib.pyplot as plt
|
|
39
|
+
|
|
40
|
+
def make_env(n, render=False, asynchronous=True):
|
|
41
|
+
render_mode = 'human' if render else None
|
|
42
|
+
env_class = gym.vector.AsyncVectorEnv if asynchronous else gym.vector.SyncVectorEnv
|
|
43
|
+
return env_class(
|
|
44
|
+
[lambda: gym.make(ENV_NAME, render_mode=render_mode) for _ in range(n)],
|
|
45
|
+
autoreset_mode=gym.vector.AutoresetMode.SAME_STEP,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def update_plot(ax, returns, threshold):
|
|
49
|
+
ax.clear()
|
|
50
|
+
ax.plot(returns, alpha=0.3, label='Episode Returns')
|
|
51
|
+
if len(returns) >= 100:
|
|
52
|
+
ma = np.convolve(returns, np.ones(100)/100, mode='valid')
|
|
53
|
+
ax.plot(range(99, len(returns)), ma, label='100-ep MA', linewidth=2)
|
|
54
|
+
ax.axhline(threshold, color='red', linestyle='--', alpha=0.5, label=f'Solved ({threshold})')
|
|
55
|
+
ax.legend()
|
|
56
|
+
ax.set_xlabel('Episode')
|
|
57
|
+
ax.set_ylabel('Return')
|
|
58
|
+
plt.pause(0.01)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def track_episode_returns(done_mask, ep_returns, ep_rets, remaining=None):
|
|
62
|
+
for idx in np.where(done_mask)[0]:
|
|
63
|
+
if remaining is None or remaining[idx] > 0:
|
|
64
|
+
ep_returns.append(ep_rets[idx])
|
|
65
|
+
if remaining is not None:
|
|
66
|
+
remaining[idx] -= 1
|
|
67
|
+
ep_rets[idx] = 0.0
|
|
68
|
+
|
|
69
|
+
class ActorCritic(nn.Module):
|
|
70
|
+
def __init__(self, state_dim, action_dim, hidden_dim, actor_layers, critic_layers):
|
|
71
|
+
super(ActorCritic, self).__init__()
|
|
72
|
+
self.config = dict(state_dim=state_dim, action_dim=action_dim, hidden_dim=hidden_dim,
|
|
73
|
+
actor_layers=actor_layers, critic_layers=critic_layers)
|
|
74
|
+
self.register_buffer('observation_scale', torch.from_numpy(OBS_SCALE.copy()))
|
|
75
|
+
|
|
76
|
+
actor = [nn.Linear(state_dim, hidden_dim), nn.ReLU()]
|
|
77
|
+
for _ in range(actor_layers - 1):
|
|
78
|
+
actor.extend([nn.Linear(hidden_dim, hidden_dim), nn.ReLU()])
|
|
79
|
+
actor.append(nn.Linear(hidden_dim, action_dim))
|
|
80
|
+
self.actor = nn.Sequential(*actor)
|
|
81
|
+
# small mu init → near-zero thrust initially (slow descent > random crashes)
|
|
82
|
+
self.actor[-1].weight.data.mul_(0.01)
|
|
83
|
+
self.actor[-1].bias.data.zero_()
|
|
84
|
+
self.log_std = nn.Parameter(torch.full((action_dim,), -0.7))
|
|
85
|
+
|
|
86
|
+
critic = [nn.Linear(state_dim, hidden_dim), nn.ReLU()]
|
|
87
|
+
for _ in range(critic_layers - 1):
|
|
88
|
+
critic.extend([nn.Linear(hidden_dim, hidden_dim), nn.ReLU()])
|
|
89
|
+
critic.append(nn.Linear(hidden_dim, 1))
|
|
90
|
+
self.critic = nn.Sequential(*critic)
|
|
91
|
+
|
|
92
|
+
def forward(self, state):
|
|
93
|
+
action_mean = self.actor(state)
|
|
94
|
+
action_std = self.log_std.clamp(-5, 2).exp().expand_as(action_mean)
|
|
95
|
+
value = self.critic(state)
|
|
96
|
+
return action_mean, action_std, value
|
|
97
|
+
|
|
98
|
+
def preprocess(self, observations):
|
|
99
|
+
return torch.as_tensor(observations, device=self.observation_scale.device, dtype=torch.float32) * self.observation_scale
|
|
100
|
+
|
|
101
|
+
@torch.inference_mode()
|
|
102
|
+
def act(self, state, deterministic=False):
|
|
103
|
+
state_tensor = self.preprocess(state)
|
|
104
|
+
action_mean = self.actor(state_tensor)
|
|
105
|
+
action_std = self.log_std.clamp(-5, 2).exp().expand_as(action_mean)
|
|
106
|
+
if deterministic:
|
|
107
|
+
action = action_mean.clamp(-1, 1)
|
|
108
|
+
return action.cpu().numpy(), action_mean.cpu().numpy()
|
|
109
|
+
sample = torch.distributions.Normal(action_mean, action_std).sample()
|
|
110
|
+
action = sample.clamp(-1, 1)
|
|
111
|
+
return action.cpu().numpy(), sample.cpu().numpy()
|
|
112
|
+
|
|
113
|
+
def save(self, path):
|
|
114
|
+
torch.save({'config': self.config, 'state_dict': self.state_dict()}, path)
|
|
115
|
+
|
|
116
|
+
@classmethod
|
|
117
|
+
def load(cls, path):
|
|
118
|
+
checkpoint = torch.load(path, map_location=device, weights_only=True)
|
|
119
|
+
model = cls(**checkpoint['config']).to(device)
|
|
120
|
+
model.load_state_dict(checkpoint['state_dict'])
|
|
121
|
+
return model
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class PPO:
|
|
125
|
+
def __init__(self, actor_critic, pi_lr, vf_lr, gamma, lamda, K_epochs, eps_clip, batch_size, vf_coef, entropy_coef):
|
|
126
|
+
self.actor_critic = actor_critic
|
|
127
|
+
self.pi_optimizer = optim.Adam(list(actor_critic.actor.parameters()) + [actor_critic.log_std], lr=pi_lr)
|
|
128
|
+
self.vf_optimizer = optim.Adam(actor_critic.critic.parameters(), lr=vf_lr)
|
|
129
|
+
self.gamma, self.lamda, self.K_epochs = gamma, lamda, K_epochs
|
|
130
|
+
self.eps_clip, self.batch_size, self.vf_coef, self.entropy_coef = eps_clip, batch_size, vf_coef, entropy_coef
|
|
131
|
+
|
|
132
|
+
def compute_advantages(self, rewards, state_values, terminated, truncated, next_values):
|
|
133
|
+
T, N = rewards.shape
|
|
134
|
+
advantages, gae = torch.zeros_like(rewards), torch.zeros(N, device=rewards.device)
|
|
135
|
+
episode_ends = terminated | truncated
|
|
136
|
+
for t in reversed(range(T)):
|
|
137
|
+
delta = rewards[t] + self.gamma * next_values[t] * ~terminated[t] - state_values[t]
|
|
138
|
+
gae = delta + self.gamma * self.lamda * ~episode_ends[t] * gae
|
|
139
|
+
advantages[t] = gae
|
|
140
|
+
returns = advantages + state_values
|
|
141
|
+
advantages = (advantages - advantages.mean()) / (advantages.std(correction=0) + 1e-8)
|
|
142
|
+
return advantages.reshape(-1), returns.reshape(-1)
|
|
143
|
+
|
|
144
|
+
def compute_loss(self, batch_states, batch_actions, batch_logprobs, batch_advantages, batch_returns):
|
|
145
|
+
action_means, action_stds, state_values = self.actor_critic(batch_states)
|
|
146
|
+
dist = torch.distributions.Normal(action_means, action_stds)
|
|
147
|
+
action_logprobs = dist.log_prob(batch_actions).sum(-1)
|
|
148
|
+
ratios = torch.exp(action_logprobs - batch_logprobs)
|
|
149
|
+
actor_loss = -torch.min(ratios * batch_advantages, torch.clamp(ratios, 1-self.eps_clip, 1+self.eps_clip) * batch_advantages).mean()
|
|
150
|
+
critic_loss = F.mse_loss(state_values.squeeze(-1), batch_returns)
|
|
151
|
+
entropy = dist.entropy().sum(-1).mean()
|
|
152
|
+
|
|
153
|
+
return actor_loss + self.vf_coef * critic_loss - self.entropy_coef * entropy
|
|
154
|
+
|
|
155
|
+
def train_step(self, batch_states, batch_actions, batch_logprobs, batch_advantages, batch_returns):
|
|
156
|
+
self.pi_optimizer.zero_grad()
|
|
157
|
+
self.vf_optimizer.zero_grad()
|
|
158
|
+
loss = self.compute_loss(batch_states, batch_actions, batch_logprobs, batch_advantages, batch_returns)
|
|
159
|
+
loss.backward()
|
|
160
|
+
torch.nn.utils.clip_grad_norm_(list(self.actor_critic.actor.parameters()) + [self.actor_critic.log_std], max_norm=0.5)
|
|
161
|
+
torch.nn.utils.clip_grad_norm_(self.actor_critic.critic.parameters(), max_norm=0.5)
|
|
162
|
+
self.pi_optimizer.step()
|
|
163
|
+
self.vf_optimizer.step()
|
|
164
|
+
return loss.detach()
|
|
165
|
+
|
|
166
|
+
def prepare(self, batch):
|
|
167
|
+
T, N = batch.rewards.shape
|
|
168
|
+
obs_flat = self.actor_critic.preprocess(batch.observations.reshape(T * N, -1))
|
|
169
|
+
raw_act_flat = torch.from_numpy(batch.raw_actions.reshape(T * N, -1)).to(device=device, dtype=torch.float32)
|
|
170
|
+
rew_t = torch.from_numpy(batch.rewards).to(device=device, dtype=torch.float32)
|
|
171
|
+
terminated_t = torch.from_numpy(batch.terminated).to(device=device)
|
|
172
|
+
truncated_t = torch.from_numpy(batch.truncated).to(device=device)
|
|
173
|
+
|
|
174
|
+
with torch.no_grad():
|
|
175
|
+
mean, std, val = self.actor_critic(obs_flat)
|
|
176
|
+
dist = torch.distributions.Normal(mean, std)
|
|
177
|
+
old_logprobs = dist.log_prob(raw_act_flat).sum(-1)
|
|
178
|
+
old_values = val.squeeze(-1).view(T, N)
|
|
179
|
+
next_obs_t = self.actor_critic.preprocess(batch.next_observations.reshape(T * N, -1))
|
|
180
|
+
next_values = self.actor_critic.critic(next_obs_t).squeeze(-1).view(T, N)
|
|
181
|
+
advantages, returns = self.compute_advantages(rew_t, old_values, terminated_t, truncated_t, next_values)
|
|
182
|
+
|
|
183
|
+
return obs_flat, raw_act_flat, old_logprobs, advantages, returns
|
|
184
|
+
|
|
185
|
+
def update(self, batch):
|
|
186
|
+
obs_flat, raw_act_flat, old_logprobs, advantages, returns = self.prepare(batch)
|
|
187
|
+
num_samples = obs_flat.size(0)
|
|
188
|
+
for _ in range(self.K_epochs):
|
|
189
|
+
perm = torch.randperm(num_samples, device=device)
|
|
190
|
+
for start in range(0, num_samples, self.batch_size):
|
|
191
|
+
idx = perm[start:start + self.batch_size]
|
|
192
|
+
self.train_step(obs_flat[idx], raw_act_flat[idx], old_logprobs[idx], advantages[idx], returns[idx])
|
|
193
|
+
|
|
194
|
+
def rollout(env, actor_critic, num_steps=None, num_episodes=None, deterministic=False, state=None):
|
|
195
|
+
assert (num_steps is None) != (num_episodes is None), "Specify exactly one: num_steps or num_episodes"
|
|
196
|
+
if num_episodes is not None and num_episodes <= 0:
|
|
197
|
+
raise ValueError('num_episodes must be positive')
|
|
198
|
+
|
|
199
|
+
N = env.num_envs
|
|
200
|
+
if state is None:
|
|
201
|
+
state = RolloutState(env.reset()[0], np.zeros(N))
|
|
202
|
+
states = state.observations
|
|
203
|
+
ep_returns, ep_rets = [], state.episode_returns
|
|
204
|
+
|
|
205
|
+
collect = num_steps is not None
|
|
206
|
+
remaining = None if collect else (num_episodes + np.arange(N)) // N
|
|
207
|
+
if collect:
|
|
208
|
+
T = num_steps // N
|
|
209
|
+
obs = np.empty((T, N, state_dim), dtype=np.float32)
|
|
210
|
+
next_obs = np.empty_like(obs)
|
|
211
|
+
raw_act = np.empty((T, N, action_dim), dtype=np.float32)
|
|
212
|
+
rew = np.empty((T, N), dtype=np.float32)
|
|
213
|
+
terminations = np.empty((T, N), dtype=bool)
|
|
214
|
+
truncations = np.empty((T, N), dtype=bool)
|
|
215
|
+
|
|
216
|
+
t = 0
|
|
217
|
+
while True:
|
|
218
|
+
actions, raw_actions = actor_critic.act(states, deterministic=deterministic)
|
|
219
|
+
if collect:
|
|
220
|
+
obs[t], raw_act[t] = states, raw_actions
|
|
221
|
+
|
|
222
|
+
states, rewards, terminated, truncated, infos = env.step(actions)
|
|
223
|
+
d = np.logical_or(terminated, truncated)
|
|
224
|
+
if collect:
|
|
225
|
+
rew[t], terminations[t], truncations[t] = rewards, terminated, truncated
|
|
226
|
+
next_obs[t] = states
|
|
227
|
+
for idx in np.flatnonzero(d):
|
|
228
|
+
next_obs[t, idx] = infos['final_obs'][idx]
|
|
229
|
+
|
|
230
|
+
ep_rets += rewards
|
|
231
|
+
track_episode_returns(d, ep_returns, ep_rets, remaining)
|
|
232
|
+
t += 1
|
|
233
|
+
|
|
234
|
+
if (collect and t >= T) or (not collect and not remaining.any()):
|
|
235
|
+
break
|
|
236
|
+
|
|
237
|
+
state.observations = states
|
|
238
|
+
return (RolloutBatch(obs, raw_act, rew, terminations, truncations, next_obs), ep_returns) if collect else ep_returns
|
|
239
|
+
|
|
240
|
+
class TrainingContext:
|
|
241
|
+
def __init__(self):
|
|
242
|
+
self.ac = ActorCritic(state_dim, action_dim, hidden_dim, actor_layers, critic_layers).to(device)
|
|
243
|
+
self.ppo = PPO(self.ac, pi_lr, vf_lr, gamma, gae_lambda, K_epochs, eps_clip, batch_size, vf_coef, entropy_coef)
|
|
244
|
+
|
|
245
|
+
self.env = make_env(num_envs)
|
|
246
|
+
self.rollout_state = RolloutState(self.env.reset()[0], np.zeros(num_envs))
|
|
247
|
+
self.eval_env = make_env(min(num_envs, eval_episodes), asynchronous=False)
|
|
248
|
+
self.all_episode_returns = []
|
|
249
|
+
self.last_eval_stochastic = float('-inf')
|
|
250
|
+
self.pbar = trange(max_epochs, desc="Training", unit='epoch')
|
|
251
|
+
self.rollout_times = []
|
|
252
|
+
self.update_times = []
|
|
253
|
+
|
|
254
|
+
if PLOT:
|
|
255
|
+
plt.ion()
|
|
256
|
+
_, self.ax = plt.subplots()
|
|
257
|
+
else:
|
|
258
|
+
self.ax = None
|
|
259
|
+
|
|
260
|
+
def cleanup(self):
|
|
261
|
+
self.env.close()
|
|
262
|
+
self.eval_env.close()
|
|
263
|
+
self.pbar.close()
|
|
264
|
+
if PLOT:
|
|
265
|
+
plt.ioff()
|
|
266
|
+
plt.show()
|
|
267
|
+
|
|
268
|
+
def train_one_epoch(epoch, ctx):
|
|
269
|
+
t0 = time.perf_counter()
|
|
270
|
+
batch, ep_rets = rollout(
|
|
271
|
+
ctx.env, ctx.ac, num_steps=steps_per_epoch, state=ctx.rollout_state
|
|
272
|
+
)
|
|
273
|
+
t1 = time.perf_counter()
|
|
274
|
+
ctx.rollout_times.append(t1 - t0)
|
|
275
|
+
|
|
276
|
+
t0 = time.perf_counter()
|
|
277
|
+
ctx.ppo.update(batch)
|
|
278
|
+
t1 = time.perf_counter()
|
|
279
|
+
ctx.update_times.append(t1 - t0)
|
|
280
|
+
|
|
281
|
+
ctx.all_episode_returns.extend(ep_rets)
|
|
282
|
+
ctx.pbar.update(1)
|
|
283
|
+
|
|
284
|
+
if epoch % eval_interval == 0:
|
|
285
|
+
ctx.last_eval_stochastic = evaluate_policy(
|
|
286
|
+
ctx.ac, env=ctx.eval_env, deterministic=False
|
|
287
|
+
)
|
|
288
|
+
if RENDER:
|
|
289
|
+
evaluate_policy(ctx.ac, render=True, num_episodes=RENDER_EPISODES, deterministic=False)
|
|
290
|
+
|
|
291
|
+
if epoch % log_interval == 0:
|
|
292
|
+
s = ctx.ac.log_std.clamp(-5, 2).exp().detach().cpu().numpy()
|
|
293
|
+
rollout_ms = np.mean(ctx.rollout_times[-log_interval:]) * 1000
|
|
294
|
+
update_ms = np.mean(ctx.update_times[-log_interval:]) * 1000
|
|
295
|
+
total_ms = rollout_ms + update_ms
|
|
296
|
+
ctx.pbar.write(f"Epoch {epoch:3d} n_ep={len(ep_rets):3d} ret={np.mean(ep_rets):7.1f}±{np.std(ep_rets):5.1f} eval_stoch={ctx.last_eval_stochastic:6.1f} σ=[{s[0]:.2f} {s[1]:.2f}] ⏱ {total_ms:.0f}ms (rollout:{rollout_ms:.0f}ms update:{update_ms:.0f}ms)")
|
|
297
|
+
|
|
298
|
+
if PLOT and epoch % (log_interval * 2) == 0:
|
|
299
|
+
update_plot(ctx.ax, ctx.all_episode_returns, solved_threshold)
|
|
300
|
+
|
|
301
|
+
if ctx.last_eval_stochastic >= solved_threshold:
|
|
302
|
+
ctx.pbar.write(f"\n{'='*60}\nSOLVED at epoch {epoch}! eval_stoch={ctx.last_eval_stochastic:.1f} ≥ {solved_threshold}\n{'='*60}")
|
|
303
|
+
if RENDER:
|
|
304
|
+
evaluate_policy(ctx.ac, render=True, num_episodes=RENDER_EPISODES, deterministic=False)
|
|
305
|
+
return True
|
|
306
|
+
|
|
307
|
+
return False
|
|
308
|
+
|
|
309
|
+
def train():
|
|
310
|
+
ctx = TrainingContext()
|
|
311
|
+
for epoch in range(max_epochs):
|
|
312
|
+
if train_one_epoch(epoch, ctx):
|
|
313
|
+
break
|
|
314
|
+
ctx.cleanup()
|
|
315
|
+
|
|
316
|
+
def evaluate_policy(actor_critic, num_episodes=eval_episodes, render=False, env=None, deterministic=True):
|
|
317
|
+
close_env = env is None
|
|
318
|
+
if env is None:
|
|
319
|
+
env = make_env(1 if render else num_episodes, render)
|
|
320
|
+
ep_rets = rollout(env, actor_critic, num_episodes=num_episodes, deterministic=deterministic)
|
|
321
|
+
if close_env:
|
|
322
|
+
env.close()
|
|
323
|
+
return float(np.mean(ep_rets)) if ep_rets else 0.0
|
|
324
|
+
|
|
325
|
+
if __name__ == '__main__':
|
|
326
|
+
print(f"Using {device} device")
|
|
327
|
+
if METAL:
|
|
328
|
+
print(f"MPS available: {torch.backends.mps.is_available()}")
|
|
329
|
+
print(f"MPS built: {torch.backends.mps.is_built()}")
|
|
330
|
+
if device.type == 'mps':
|
|
331
|
+
print("✅ MPS device active")
|
|
332
|
+
else:
|
|
333
|
+
print("⚠️ METAL=1 but device is not MPS")
|
|
334
|
+
print(f"PyTorch threads: {torch.get_num_threads()}")
|
|
335
|
+
train()
|
tinybaselines-0.1.0a1/examples/lunar_lander/results/2026-10-26/phase-initial-tinygrad-thread4.jsonl
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"seconds": 0.614734499999031, "event": "config", "framework": "tinygrad", "environment": "LunarLanderContinuous-v3", "source_commit": "8f0b31a96b03e23f43b35dcf12e36302e0036e40", "device": "CPU", "dtype": "dtypes.float", "platform": "macOS-27.0.1-arm64-arm-64bit", "python": "3.11.14", "versions": {"gymnasium": "1.1.1", "numpy": "2.4.6", "tinygrad": "0.13.0"}, "backend_source": {"url": "https://github.com/tinygrad/tinygrad.git", "vcs_info": {"vcs": "git", "commit_id": "5b60a09ab0a7f20c3426e505251c48cac020d1fa", "requested_revision": "5b60a09ab0a7f20c3426e505251c48cac020d1fa"}}, "settings": {"num_envs": 4, "steps_per_epoch": 20000, "max_epochs": 3, "batch_size": 10000, "K_epochs": 20, "pi_lr": 0.001, "vf_lr": 0.001, "gamma": 0.99, "gae_lambda": 0.95, "eps_clip": 0.2, "vf_coef": 1.0, "entropy_coef": 0.001, "eval_interval": 5, "eval_episodes": 100, "solved_threshold": 200}, "architecture": {"state_dim": 8, "action_dim": 2, "hidden_dim": 128, "actor_layers": 4, "critic_layers": 4}, "beam": "1", "torch_threads": null}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{"seconds": 0.48239595799532253, "event": "config", "framework": "torch", "environment": "LunarLanderContinuous-v3", "source_commit": "8f0b31a96b03e23f43b35dcf12e36302e0036e40", "device": "cpu", "dtype": "torch.float32", "platform": "macOS-27.0.1-arm64-arm-64bit", "python": "3.11.14", "versions": {"gymnasium": "1.1.1", "numpy": "2.4.6", "torch": "2.8.0"}, "backend_source": {}, "settings": {"num_envs": 4, "steps_per_epoch": 20000, "max_epochs": 3, "batch_size": 10000, "K_epochs": 20, "pi_lr": 0.001, "vf_lr": 0.001, "gamma": 0.99, "gae_lambda": 0.95, "eps_clip": 0.2, "vf_coef": 1.0, "entropy_coef": 0.001, "eval_interval": 5, "eval_episodes": 100, "solved_threshold": 200}, "architecture": {"state_dim": 8, "action_dim": 2, "hidden_dim": 128, "actor_layers": 4, "critic_layers": 4}, "beam": "1", "torch_threads": 4}
|
|
2
|
+
{"seconds": 2.1622258749994216, "event": "epoch", "steps": 20000, "seconds_total": 1.679733083001338, "rollout_seconds": 0.8259355000045616, "update_seconds": 0.43762720800441457, "other_seconds": 0.41617037499236176}
|
|
3
|
+
{"seconds": 2.1622854999950505, "event": "evaluation", "steps": 20000, "score": -144.3682780688725}
|
|
4
|
+
{"seconds": 3.3910218749952037, "event": "epoch", "steps": 40000, "seconds_total": 1.2287172080032178, "rollout_seconds": 0.8063115420009126, "update_seconds": 0.4221108749989071, "other_seconds": 0.0002947910033981316}
|
|
5
|
+
{"seconds": 4.608377207994636, "event": "epoch", "steps": 60000, "seconds_total": 1.2172969999955967, "rollout_seconds": 0.7961072079997393, "update_seconds": 0.4209024999945541, "other_seconds": 0.00028729200130328536}
|
|
6
|
+
{"seconds": 5.081694374996005, "event": "final", "steps": 60000, "score": -104.08706254110945, "seconds_to_stop": 4.608431249995192, "stopped_on_threshold": false, "policy": "policy.pt"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"date": "2026-10-26",
|
|
3
|
+
"hardware": "Apple M4",
|
|
4
|
+
"purpose": "Locate wall time in the real training path. These are independent stochastic runs, not identical-input backend comparisons.",
|
|
5
|
+
"source_commit": "483d1da6b213611ff1dccd9520149edd4777671d",
|
|
6
|
+
"selected_runs": {
|
|
7
|
+
"torch": "phase-torch-thread4.jsonl",
|
|
8
|
+
"tinygrad": "phase-tinygrad-thread10.jsonl"
|
|
9
|
+
},
|
|
10
|
+
"run_order": [
|
|
11
|
+
"phase-initial-torch.jsonl",
|
|
12
|
+
"phase-initial-tinygrad-thread4.jsonl",
|
|
13
|
+
"phase-tinygrad-thread4-error.jsonl",
|
|
14
|
+
"phase-torch-thread4.jsonl",
|
|
15
|
+
"phase-tinygrad-thread10.jsonl"
|
|
16
|
+
],
|
|
17
|
+
"environment": {
|
|
18
|
+
"METAL": "0",
|
|
19
|
+
"DEV": "CPU",
|
|
20
|
+
"NUM_ENVS": "4",
|
|
21
|
+
"NUM_CPU_THREADS": "10",
|
|
22
|
+
"BEAM": "1",
|
|
23
|
+
"JIT": "1",
|
|
24
|
+
"XDG_CACHE_HOME": "$PWD/.venv/cache"
|
|
25
|
+
},
|
|
26
|
+
"requested_threads": {
|
|
27
|
+
"torch": 4,
|
|
28
|
+
"tinygrad": 10
|
|
29
|
+
},
|
|
30
|
+
"compiler_cache_directory_reused": true,
|
|
31
|
+
"epochs_per_completed_run": 3,
|
|
32
|
+
"warmup": "No epochs excluded from the records. Article table shows zero-based epoch 2; first epoch includes initial JIT calls.",
|
|
33
|
+
"timings": {
|
|
34
|
+
"seconds_total": "Wall time around train_one_epoch. Excludes backend import, environment construction, save/load and the final evaluation.",
|
|
35
|
+
"rollout_seconds": "Collection of 20000 transitions, including policy.act, environment step/IPC, and rollout buffer work.",
|
|
36
|
+
"update_seconds": "PPO.update, including preparation/GAE, shuffling/indexing, and 40 optimizer steps.",
|
|
37
|
+
"other_seconds": "seconds_total minus rollout_seconds and update_seconds. Includes scheduled evaluation, progress display and logging."
|
|
38
|
+
},
|
|
39
|
+
"evaluation": "100 fresh stochastic episodes at epoch 0 and after save/load. Three-epoch probes are not convergence runs.",
|
|
40
|
+
"initial_runs": "First Torch run and the first four-thread tinygrad failure used 8f0b31a. The initial failure has only a config record because error logging was not yet implemented.",
|
|
41
|
+
"failed_run": {
|
|
42
|
+
"file": "phase-tinygrad-thread4-error.jsonl",
|
|
43
|
+
"NUM_CPU_THREADS": "4",
|
|
44
|
+
"error_type": "KernelOptError",
|
|
45
|
+
"message": "too many threads",
|
|
46
|
+
"location": "Replaying cached beam-search options during PPO.prepare, before any epoch completed.",
|
|
47
|
+
"cache_manually_cleared": false
|
|
48
|
+
},
|
|
49
|
+
"reproduce": [
|
|
50
|
+
"METAL=0 DEV=CPU NUM_ENVS=4 NUM_CPU_THREADS=10 BEAM=1 JIT=1 XDG_CACHE_HOME=\"$PWD/.venv/cache\" .venv/bin/python -c 'from pathlib import Path; from examples.lunar_lander import reference; from examples.lunar_lander.run_learning import run; reference.max_epochs=3; run(\"torch\", Path(\"runs/torch-phases\"))'",
|
|
51
|
+
"METAL=0 DEV=CPU NUM_ENVS=4 NUM_CPU_THREADS=10 BEAM=1 JIT=1 XDG_CACHE_HOME=\"$PWD/.venv/cache\" .venv/bin/python -c 'from pathlib import Path; from examples.lunar_lander import train; from examples.lunar_lander.run_learning import run; train.max_epochs=3; run(\"tinygrad\", Path(\"runs/tinygrad-phases\"))'"
|
|
52
|
+
]
|
|
53
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{"seconds": 0.707170124995173, "event": "config", "framework": "tinygrad", "environment": "LunarLanderContinuous-v3", "source_commit": "483d1da6b213611ff1dccd9520149edd4777671d", "device": "CPU", "dtype": "dtypes.float", "platform": "macOS-27.0.1-arm64-arm-64bit", "python": "3.11.14", "versions": {"gymnasium": "1.1.1", "numpy": "2.4.6", "tinygrad": "0.13.0"}, "backend_source": {"url": "https://github.com/tinygrad/tinygrad.git", "vcs_info": {"vcs": "git", "commit_id": "5b60a09ab0a7f20c3426e505251c48cac020d1fa", "requested_revision": "5b60a09ab0a7f20c3426e505251c48cac020d1fa"}}, "settings": {"num_envs": 4, "steps_per_epoch": 20000, "max_epochs": 3, "batch_size": 10000, "K_epochs": 20, "pi_lr": 0.001, "vf_lr": 0.001, "gamma": 0.99, "gae_lambda": 0.95, "eps_clip": 0.2, "vf_coef": 1.0, "entropy_coef": 0.001, "eval_interval": 5, "eval_episodes": 100, "solved_threshold": 200}, "architecture": {"state_dim": 8, "action_dim": 2, "hidden_dim": 128, "actor_layers": 4, "critic_layers": 4}, "beam": "1", "torch_threads": null, "tinygrad_cpu_threads": 10}
|
|
2
|
+
{"seconds": 27.337733790998755, "event": "epoch", "steps": 20000, "seconds_total": 26.63045024999883, "rollout_seconds": 10.67004020800232, "update_seconds": 10.632532167001045, "other_seconds": 5.327877874995465}
|
|
3
|
+
{"seconds": 27.33781787499902, "event": "evaluation", "steps": 20000, "score": -148.26613410664652}
|
|
4
|
+
{"seconds": 41.51010841599782, "event": "epoch", "steps": 40000, "seconds_total": 14.172251832998882, "rollout_seconds": 9.591514917003224, "update_seconds": 4.580453624999791, "other_seconds": 0.0002832909958669916}
|
|
5
|
+
{"seconds": 55.548277791000146, "event": "epoch", "steps": 60000, "seconds_total": 14.038110916000733, "rollout_seconds": 9.761195333005162, "update_seconds": 4.276613417001499, "other_seconds": 0.00030216599407140166}
|
|
6
|
+
{"seconds": 62.28786079099518, "event": "final", "steps": 60000, "score": -90.02494137380951, "seconds_to_stop": 55.548339540997404, "stopped_on_threshold": false, "policy": "policy.safetensors"}
|