dsh-eval-guard 0.7.3 → 0.7.4
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.
- package/ISSUES.md +5 -1
- package/README.md +4 -2
- package/assets/neural-pendulum/SPEC.md +4 -3
- package/assets/neural-pendulum/TASK.md +4 -3
- package/assets/neural-pendulum/rl-feasibility/README.md +60 -0
- package/assets/neural-pendulum/rl-feasibility/RESULTS.md +370 -0
- package/assets/neural-pendulum/rl-feasibility/evaluate.py +107 -0
- package/assets/neural-pendulum/rl-feasibility/finalize_run.py +168 -0
- package/assets/neural-pendulum/rl-feasibility/models.py +124 -0
- package/assets/neural-pendulum/rl-feasibility/obs.py +205 -0
- package/assets/neural-pendulum/rl-feasibility/plot_selected.py +55 -0
- package/assets/neural-pendulum/rl-feasibility/reference_controller.py +112 -0
- package/assets/neural-pendulum/rl-feasibility/results/curves_selected.png +0 -0
- package/assets/neural-pendulum/rl-feasibility/results/reference_controller.json +417 -0
- package/assets/neural-pendulum/rl-feasibility/results/summary.json +1599 -0
- package/assets/neural-pendulum/rl-feasibility/results/table.md +39 -0
- package/assets/neural-pendulum/rl-feasibility/sim.py +324 -0
- package/assets/neural-pendulum/rl-feasibility/summarize.py +175 -0
- package/assets/neural-pendulum/rl-feasibility/task.py +342 -0
- package/assets/neural-pendulum/rl-feasibility/test_contract.py +188 -0
- package/assets/neural-pendulum/rl-feasibility/train_ppo.py +403 -0
- package/assets/neural-pendulum/rl-feasibility/train_sac.py +406 -0
- package/assets/neural-pendulum/sim.cpp +13 -2
- package/assets/neural-pendulum/sim_eval +0 -0
- package/assets/neural-pendulum/workspace.zip +0 -0
- package/lib/index.js +5 -1
- package/native/dsh-eval-supervisor +0 -0
- package/native/supervisor.c +42 -10
- package/neural-pendulum-eval/sim_eval +0 -0
- package/package.json +2 -2
- package/eval/__pycache__/bomberman.cpython-310.pyc +0 -0
- package/eval/__pycache__/commit0.cpython-310.pyc +0 -0
- package/eval/__pycache__/common.cpython-310.pyc +0 -0
- package/eval/__pycache__/ctf.cpython-310.pyc +0 -0
- package/eval/__pycache__/featurebench.cpython-310.pyc +0 -0
- package/eval/__pycache__/land.cpython-310.pyc +0 -0
- package/eval/__pycache__/neural-cifar.cpython-310.pyc +0 -0
- package/eval/__pycache__/neural-cpp.cpython-310.pyc +0 -0
- package/eval/__pycache__/neural-fp32.cpython-310.pyc +0 -0
- package/eval/__pycache__/neural-pendulum.cpython-310.pyc +0 -0
- package/eval/__pycache__/swe-bench-pro.cpython-310.pyc +0 -0
- package/eval/__pycache__/tank.cpython-310.pyc +0 -0
- package/eval/__pycache__/warehouse.cpython-310.pyc +0 -0
- package/eval/legacy/__pycache__/bomberman.cpython-310.pyc +0 -0
- package/eval/legacy/__pycache__/commit0.cpython-310.pyc +0 -0
- package/eval/legacy/__pycache__/ctf.cpython-310.pyc +0 -0
- package/eval/legacy/__pycache__/featurebench.cpython-310.pyc +0 -0
- package/eval/legacy/__pycache__/land.cpython-310.pyc +0 -0
- package/eval/legacy/__pycache__/swe_bench_pro.cpython-310.pyc +0 -0
- package/eval/legacy/__pycache__/tank.cpython-310.pyc +0 -0
- package/eval/legacy/__pycache__/warehouse.cpython-310.pyc +0 -0
- package/neural-cifar-eval/__pycache__/cifar_cache.cpython-310.pyc +0 -0
- package/neural-cifar-eval/__pycache__/common.cpython-310.pyc +0 -0
- package/neural-cifar-eval/__pycache__/fetch_cifar.cpython-310.pyc +0 -0
- package/neural-cifar-eval/__pycache__/ops_eval.cpython-310.pyc +0 -0
- package/neural-cifar-eval/__pycache__/run_eval.cpython-310.pyc +0 -0
- package/neural-pendulum-eval/__pycache__/ops_eval.cpython-310.pyc +0 -0
- package/neural-pendulum-eval/__pycache__/run_eval.cpython-310.pyc +0 -0
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
"""PPO + 2x128 tanh MLP + sin/cos H=4 observation, angle-only simulator.
|
|
2
|
+
|
|
3
|
+
Experiments:
|
|
4
|
+
--init rand : every training reset draws theta_i ~ U(-.05,.05) and
|
|
5
|
+
omega_i ~ U(-.02,.02), with no fixed bias.
|
|
6
|
+
--init fixed : every training reset is exactly the evaluation state
|
|
7
|
+
theta = [0.013,-0.008,0.021], omega = 0.
|
|
8
|
+
|
|
9
|
+
The simulator returns only theta and raises EpisodeLengthError after 3000
|
|
10
|
+
steps. Everything else -- observation, reward, done, success, t_can, return
|
|
11
|
+
-- is built here and in task.py, never in the simulator.
|
|
12
|
+
|
|
13
|
+
TensorBoard scalars (tb/<exp>/):
|
|
14
|
+
train/episode_return, train/episode_length, train/t_can, train/t_can_max,
|
|
15
|
+
train/success_rate, train/ever_up_frac, train/first_up_time,
|
|
16
|
+
train/mean_step_reward, train/action_abs_mean, train/std, train/lr,
|
|
17
|
+
train/approx_kl, train/clip_frac,
|
|
18
|
+
loss/policy, loss/value, loss/entropy,
|
|
19
|
+
eval/t_can, eval/success, eval/ep_return, eval/ever_up, eval/final_ang_err,
|
|
20
|
+
eval/first_up_t, eval/up_frac, eval_final/*,
|
|
21
|
+
train/global_steps, time/elapsed_min
|
|
22
|
+
"""
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import argparse
|
|
26
|
+
import json
|
|
27
|
+
import os
|
|
28
|
+
import time
|
|
29
|
+
|
|
30
|
+
import numpy as np
|
|
31
|
+
import torch
|
|
32
|
+
import torch.nn as nn
|
|
33
|
+
from torch.utils.tensorboard import SummaryWriter
|
|
34
|
+
|
|
35
|
+
torch.set_num_threads(int(os.environ.get("TORCH_THREADS", "4")))
|
|
36
|
+
|
|
37
|
+
from evaluate import evaluate, evaluate_starts, make_ppo_policy
|
|
38
|
+
from models import ActorCritic
|
|
39
|
+
from obs import OBS_MODES, obs_spec
|
|
40
|
+
from sim import EPISODE_STEPS, Params, TorchVecSim
|
|
41
|
+
from task import (EVAL_THETA0, TORCH_REWARD_NOTE, TorchTask,
|
|
42
|
+
get_reward_preset, make_init, sample_train_init)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def collect(sim, task, ac, T, args, gen, ep_stats):
|
|
46
|
+
B = sim.n
|
|
47
|
+
obs = task.obs
|
|
48
|
+
obs_buf = torch.zeros(T, B, obs.shape[-1])
|
|
49
|
+
act_buf = torch.zeros(T, B, 2)
|
|
50
|
+
logp_buf = torch.zeros(T, B)
|
|
51
|
+
rew_buf = torch.zeros(T, B)
|
|
52
|
+
done_buf = torch.zeros(T, B)
|
|
53
|
+
val_buf = torch.zeros(T, B)
|
|
54
|
+
for t in range(T):
|
|
55
|
+
with torch.no_grad():
|
|
56
|
+
dist = ac.dist(obs)
|
|
57
|
+
a = dist.sample()
|
|
58
|
+
logp = dist.log_prob(a).sum(-1)
|
|
59
|
+
v = ac.value(obs)
|
|
60
|
+
theta = sim.step(a)
|
|
61
|
+
nobs, rew = task.step(theta, a)
|
|
62
|
+
done = sim.steps >= sim.episode_steps
|
|
63
|
+
obs_buf[t] = obs
|
|
64
|
+
act_buf[t] = a
|
|
65
|
+
logp_buf[t] = logp
|
|
66
|
+
rew_buf[t] = rew
|
|
67
|
+
done_buf[t] = done.float()
|
|
68
|
+
val_buf[t] = v
|
|
69
|
+
if bool(done.any()):
|
|
70
|
+
idx = done.nonzero().flatten()
|
|
71
|
+
summ = task.summary(idx)
|
|
72
|
+
ep_stats["return"] += summ["ep_return"].tolist()
|
|
73
|
+
ep_stats["t_can"] += summ["t_can"].tolist()
|
|
74
|
+
ep_stats["ever_up"] += summ["ever_up"].tolist()
|
|
75
|
+
ep_stats["first_up"] += summ["first_up_t"].tolist()
|
|
76
|
+
ep_stats["up_frac"] += summ["up_frac"].tolist()
|
|
77
|
+
ep_stats["length"] += summ["length"].tolist()
|
|
78
|
+
th, om = make_init(idx.numel(), args.init, gen, up_frac=args.up_frac)
|
|
79
|
+
reset_theta = sim.reset(th, om, idx)
|
|
80
|
+
task.reset(reset_theta, idx)
|
|
81
|
+
nobs = task.obs
|
|
82
|
+
obs = nobs
|
|
83
|
+
with torch.no_grad():
|
|
84
|
+
last_v = ac.value(obs)
|
|
85
|
+
return dict(obs=obs_buf, act=act_buf, logp=logp_buf, rew=rew_buf,
|
|
86
|
+
done=done_buf, val=val_buf, last_v=last_v, last_obs=obs)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def gae(rb, vb, db, last_v, gamma, lam):
|
|
90
|
+
T, B = rb.shape
|
|
91
|
+
adv = torch.zeros(T, B)
|
|
92
|
+
last = torch.zeros(B)
|
|
93
|
+
for t in reversed(range(T)):
|
|
94
|
+
next_v = last_v if t == T - 1 else vb[t + 1]
|
|
95
|
+
nonterm = 1.0 - db[t]
|
|
96
|
+
delta = rb[t] + gamma * next_v * nonterm - vb[t]
|
|
97
|
+
last = delta + gamma * lam * nonterm * last
|
|
98
|
+
adv[t] = last
|
|
99
|
+
return adv, adv + vb
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def ppo_update(ac, opt, data, args):
|
|
103
|
+
T, B = data["rew"].shape
|
|
104
|
+
obs = data["obs"].reshape(T * B, -1)
|
|
105
|
+
act = data["act"].reshape(T * B, ac.act_dim)
|
|
106
|
+
old_logp = data["logp"].reshape(T * B)
|
|
107
|
+
adv = data["adv"].reshape(T * B)
|
|
108
|
+
ret = data["ret"].reshape(T * B)
|
|
109
|
+
adv = (adv - adv.mean()) / (adv.std() + 1e-8)
|
|
110
|
+
n = obs.shape[0]
|
|
111
|
+
stats = dict(pg=[], v=[], ent=[], kl=[], cf=[])
|
|
112
|
+
stop = False
|
|
113
|
+
ep = 0
|
|
114
|
+
for ep in range(args.epochs):
|
|
115
|
+
idx = torch.randperm(n)
|
|
116
|
+
for s in range(0, n, args.minibatch):
|
|
117
|
+
mb = idx[s:s + args.minibatch]
|
|
118
|
+
dist = ac.dist(obs[mb])
|
|
119
|
+
logp = dist.log_prob(act[mb]).sum(-1)
|
|
120
|
+
ratio = (logp - old_logp[mb]).exp()
|
|
121
|
+
a = adv[mb]
|
|
122
|
+
pg = torch.max(-a * ratio,
|
|
123
|
+
-a * torch.clamp(ratio, 1 - args.clip, 1 + args.clip)).mean()
|
|
124
|
+
v = ac.value(obs[mb])
|
|
125
|
+
v_loss = 0.5 * ((v - ret[mb]) ** 2).mean()
|
|
126
|
+
ent = dist.entropy().sum(-1).mean()
|
|
127
|
+
loss = pg + args.vf_coef * v_loss - args.ent * ent
|
|
128
|
+
opt.zero_grad()
|
|
129
|
+
loss.backward()
|
|
130
|
+
nn.utils.clip_grad_norm_(ac.parameters(), 0.5)
|
|
131
|
+
opt.step()
|
|
132
|
+
with torch.no_grad():
|
|
133
|
+
kl = ((ratio - 1) - (logp - old_logp[mb])).mean().item()
|
|
134
|
+
cf = ((ratio - 1).abs() > args.clip).float().mean().item()
|
|
135
|
+
stats["pg"].append(pg.item()); stats["v"].append(v_loss.item())
|
|
136
|
+
stats["ent"].append(ent.item()); stats["kl"].append(kl)
|
|
137
|
+
stats["cf"].append(cf)
|
|
138
|
+
if kl > 2.0 * args.target_kl:
|
|
139
|
+
stop = True
|
|
140
|
+
break
|
|
141
|
+
if stop:
|
|
142
|
+
break
|
|
143
|
+
out = {k: float(np.mean(v)) for k, v in stats.items()}
|
|
144
|
+
out["epochs"] = ep + 1
|
|
145
|
+
return out
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def main():
|
|
149
|
+
ap = argparse.ArgumentParser()
|
|
150
|
+
ap.add_argument("--exp", type=str, required=True)
|
|
151
|
+
ap.add_argument("--seed", type=int, default=0)
|
|
152
|
+
ap.add_argument("--init", type=str, default="rand",
|
|
153
|
+
choices=["rand", "fixed", "mix"],
|
|
154
|
+
help="rand=down noise, fixed=eval state, "
|
|
155
|
+
"mix=curriculum with up_frac near upright")
|
|
156
|
+
ap.add_argument("--up-frac", type=float, default=0.5,
|
|
157
|
+
help="fraction of resets near upright when --init mix")
|
|
158
|
+
ap.add_argument("--obs", type=str, default="full",
|
|
159
|
+
choices=sorted(OBS_MODES),
|
|
160
|
+
help="observation mode (see obs.py)")
|
|
161
|
+
ap.add_argument("--reward", type=str, default="base",
|
|
162
|
+
choices=["base", "sparse", "energy"],
|
|
163
|
+
help="reward preset (see task.py REWARD_PRESETS)")
|
|
164
|
+
ap.add_argument("--envs", type=int, default=64)
|
|
165
|
+
ap.add_argument("--horizon", type=int, default=600)
|
|
166
|
+
ap.add_argument("--minutes", type=float, default=25.0)
|
|
167
|
+
ap.add_argument("--iters", type=int, default=10 ** 9)
|
|
168
|
+
ap.add_argument("--lr", type=float, default=3e-4)
|
|
169
|
+
ap.add_argument("--gamma", type=float, default=0.99)
|
|
170
|
+
ap.add_argument("--lam", type=float, default=0.95)
|
|
171
|
+
ap.add_argument("--clip", type=float, default=0.2)
|
|
172
|
+
ap.add_argument("--epochs", type=int, default=8)
|
|
173
|
+
ap.add_argument("--minibatch", type=int, default=2048)
|
|
174
|
+
ap.add_argument("--ent", type=float, default=0.003)
|
|
175
|
+
ap.add_argument("--vf-coef", type=float, default=0.5)
|
|
176
|
+
ap.add_argument("--target-kl", type=float, default=0.03)
|
|
177
|
+
ap.add_argument("--hidden", type=int, default=128)
|
|
178
|
+
ap.add_argument("--depth", type=int, default=2)
|
|
179
|
+
ap.add_argument("--episode-steps", type=int, default=EPISODE_STEPS)
|
|
180
|
+
ap.add_argument("--eval-every", type=int, default=5)
|
|
181
|
+
ap.add_argument("--tb-root", type=str, default="tb")
|
|
182
|
+
ap.add_argument("--ckpt-dir", type=str, default="checkpoints")
|
|
183
|
+
ap.add_argument("--out", type=str, default="results")
|
|
184
|
+
ap.add_argument("--tau-max", type=float, default=18.0)
|
|
185
|
+
ap.add_argument("--damping", type=float, default=0.01)
|
|
186
|
+
ap.add_argument("--log-every", type=int, default=1)
|
|
187
|
+
ap.add_argument("--robust-starts", type=int, default=8,
|
|
188
|
+
help="deterministic extra rollouts from the training "
|
|
189
|
+
"reset distribution at the end (0 disables)")
|
|
190
|
+
args = ap.parse_args()
|
|
191
|
+
|
|
192
|
+
torch.manual_seed(args.seed)
|
|
193
|
+
np.random.seed(args.seed)
|
|
194
|
+
for d in (args.tb_root, args.ckpt_dir, args.out):
|
|
195
|
+
os.makedirs(d, exist_ok=True)
|
|
196
|
+
exp = args.exp
|
|
197
|
+
writer = SummaryWriter(os.path.join(args.tb_root, exp))
|
|
198
|
+
p = Params(tau_max=args.tau_max, b=args.damping)
|
|
199
|
+
spec = obs_spec(args.obs)
|
|
200
|
+
obs_dim = spec["dim"]
|
|
201
|
+
reward_w = get_reward_preset(args.reward)
|
|
202
|
+
sim = TorchVecSim(p, n=args.envs, episode_steps=args.episode_steps)
|
|
203
|
+
task = TorchTask(p, n=args.envs, reward_w=reward_w, obs_mode=args.obs)
|
|
204
|
+
gen = torch.Generator().manual_seed(args.seed * 1000 + 7)
|
|
205
|
+
|
|
206
|
+
init_meta = dict(mode=args.init,
|
|
207
|
+
training=("theta_i ~ U(-0.05,0.05), omega_i ~ U(-0.02,0.02); no bias"
|
|
208
|
+
if args.init == "rand" else
|
|
209
|
+
("theta=[0.013,-0.008,0.021], omega=0 (same as eval)"
|
|
210
|
+
if args.init == "fixed" else
|
|
211
|
+
f"curriculum: {args.up_frac:.2f} near upright "
|
|
212
|
+
f"(pi + U(-0.05,0.05)), rest near down")),
|
|
213
|
+
eval="theta=[0.013,-0.008,0.021], omega=0 (fixed, no noise)")
|
|
214
|
+
meta = dict(algo="ppo", exp=exp, seed=args.seed, envs=args.envs,
|
|
215
|
+
horizon=args.horizon, obs=spec, obs_dim=obs_dim,
|
|
216
|
+
init=init_meta, reward=reward_w, reward_preset=args.reward,
|
|
217
|
+
reward_note=TORCH_REWARD_NOTE, args=vars(args))
|
|
218
|
+
writer.add_text("config", json.dumps(meta, indent=2), 0)
|
|
219
|
+
print(json.dumps(meta, indent=2), flush=True)
|
|
220
|
+
|
|
221
|
+
ac = ActorCritic(obs_dim, act_dim=2, hidden=args.hidden, depth=args.depth)
|
|
222
|
+
opt = torch.optim.Adam(ac.parameters(), lr=args.lr, eps=1e-5)
|
|
223
|
+
sched = torch.optim.lr_scheduler.LinearLR(opt, start_factor=1.0,
|
|
224
|
+
end_factor=0.2, total_iters=100000)
|
|
225
|
+
|
|
226
|
+
th, om = make_init(args.envs, args.init, gen, up_frac=args.up_frac)
|
|
227
|
+
theta = sim.reset(th, om)
|
|
228
|
+
task.reset(theta)
|
|
229
|
+
|
|
230
|
+
t_start = time.time()
|
|
231
|
+
global_steps = 0
|
|
232
|
+
it = 0
|
|
233
|
+
best_t_can = -1.0
|
|
234
|
+
best_eval = None
|
|
235
|
+
best_path = None
|
|
236
|
+
last_path = None
|
|
237
|
+
eval_curve = []
|
|
238
|
+
last = {}
|
|
239
|
+
ep_stats = {k: [] for k in ("return", "t_can", "ever_up", "first_up",
|
|
240
|
+
"up_frac", "length")}
|
|
241
|
+
|
|
242
|
+
def save_ckpt(path, ev):
|
|
243
|
+
torch.save(dict(model=ac.state_dict(), opt=opt.state_dict(),
|
|
244
|
+
obs_dim=obs_dim, obs=spec, init=init_meta,
|
|
245
|
+
reward=reward_w, reward_preset=args.reward,
|
|
246
|
+
args=vars(args),
|
|
247
|
+
iter=it, global_steps=global_steps, eval=ev,
|
|
248
|
+
algo="ppo"), os.path.join(args.ckpt_dir, path))
|
|
249
|
+
|
|
250
|
+
def log_eval(ev, tag=""):
|
|
251
|
+
nonlocal best_t_can, best_eval, best_path
|
|
252
|
+
eval_curve.append(dict(iter=it, global_steps=global_steps, **ev))
|
|
253
|
+
for k, v in ev.items():
|
|
254
|
+
if isinstance(v, (int, float)):
|
|
255
|
+
writer.add_scalar(f"eval/{k}", v, global_steps)
|
|
256
|
+
if (ev["t_can"] > best_t_can
|
|
257
|
+
or (ev["t_can"] == best_t_can and best_eval is not None
|
|
258
|
+
and ev["ep_return"] > best_eval["ep_return"])):
|
|
259
|
+
best_t_can = ev["t_can"]
|
|
260
|
+
best_eval = dict(ev)
|
|
261
|
+
best_path = exp + "_best.pt"
|
|
262
|
+
save_ckpt(best_path, ev)
|
|
263
|
+
|
|
264
|
+
while it < args.iters:
|
|
265
|
+
if (time.time() - t_start) / 60.0 > args.minutes:
|
|
266
|
+
print("time budget reached", flush=True)
|
|
267
|
+
break
|
|
268
|
+
data = collect(sim, task, ac, args.horizon, args, gen, ep_stats)
|
|
269
|
+
adv, ret = gae(data["rew"], data["val"], data["done"], data["last_v"],
|
|
270
|
+
args.gamma, args.lam)
|
|
271
|
+
data["adv"], data["ret"] = adv, ret
|
|
272
|
+
stats = ppo_update(ac, opt, data, args)
|
|
273
|
+
sched.step()
|
|
274
|
+
global_steps += args.horizon * args.envs
|
|
275
|
+
it += 1
|
|
276
|
+
last = stats
|
|
277
|
+
mean_train_ret = (float(np.mean(ep_stats["return"]))
|
|
278
|
+
if ep_stats["return"] else float("nan"))
|
|
279
|
+
|
|
280
|
+
if ep_stats["return"]:
|
|
281
|
+
writer.add_scalar("train/episode_return",
|
|
282
|
+
float(np.mean(ep_stats["return"])), global_steps)
|
|
283
|
+
writer.add_scalar("train/episode_length",
|
|
284
|
+
float(np.mean(ep_stats["length"])), global_steps)
|
|
285
|
+
writer.add_scalar("train/t_can", float(np.mean(ep_stats["t_can"])),
|
|
286
|
+
global_steps)
|
|
287
|
+
writer.add_scalar("train/t_can_max", float(np.max(ep_stats["t_can"])),
|
|
288
|
+
global_steps)
|
|
289
|
+
writer.add_scalar("train/success_rate",
|
|
290
|
+
float(np.mean(np.asarray(ep_stats["t_can"]) >= 10.0)),
|
|
291
|
+
global_steps)
|
|
292
|
+
writer.add_scalar("train/ever_up_frac",
|
|
293
|
+
float(np.mean(ep_stats["ever_up"])), global_steps)
|
|
294
|
+
writer.add_scalar("train/up_frac",
|
|
295
|
+
float(np.mean(ep_stats["up_frac"])), global_steps)
|
|
296
|
+
fu = [x for x in ep_stats["first_up"] if x > 0]
|
|
297
|
+
if fu:
|
|
298
|
+
writer.add_scalar("train/first_up_time", float(np.mean(fu)),
|
|
299
|
+
global_steps)
|
|
300
|
+
ep_stats = {k: [] for k in ep_stats}
|
|
301
|
+
writer.add_scalar("train/mean_step_reward", float(data["rew"].mean()), global_steps)
|
|
302
|
+
writer.add_scalar("train/action_abs_mean", float(data["act"].abs().mean()), global_steps)
|
|
303
|
+
writer.add_scalar("train/action_std", float(data["act"].std()), global_steps)
|
|
304
|
+
writer.add_scalar("train/std", float(ac.effective_log_std().exp().mean()),
|
|
305
|
+
global_steps)
|
|
306
|
+
writer.add_scalar("train/lr", opt.param_groups[0]["lr"], global_steps)
|
|
307
|
+
writer.add_scalar("train/approx_kl", stats["kl"], global_steps)
|
|
308
|
+
writer.add_scalar("train/clip_frac", stats["cf"], global_steps)
|
|
309
|
+
writer.add_scalar("train/global_steps", global_steps, global_steps)
|
|
310
|
+
writer.add_scalar("loss/policy", stats["pg"], global_steps)
|
|
311
|
+
writer.add_scalar("loss/value", stats["v"], global_steps)
|
|
312
|
+
writer.add_scalar("loss/entropy", stats["ent"], global_steps)
|
|
313
|
+
writer.add_scalar("time/elapsed_min", (time.time() - t_start) / 60.0,
|
|
314
|
+
global_steps)
|
|
315
|
+
|
|
316
|
+
if it % args.eval_every == 0 or it == 1:
|
|
317
|
+
ev = evaluate(make_ppo_policy(ac.actor), p, args.episode_steps,
|
|
318
|
+
reward_w=reward_w, obs_mode=args.obs)
|
|
319
|
+
log_eval(ev)
|
|
320
|
+
last_path = exp + "_last.pt"
|
|
321
|
+
save_ckpt(last_path, ev)
|
|
322
|
+
print(f"iter {it:4d} steps {global_steps/1e6:5.2f}M "
|
|
323
|
+
f"train_ret {mean_train_ret:8.1f} "
|
|
324
|
+
f"EVAL tc {ev['t_can']:6.2f} succ {ev['success']:.0f} "
|
|
325
|
+
f"everup {ev['ever_up']:.0f} angerr {ev['final_ang_err']:.3f} "
|
|
326
|
+
f"ret {ev['ep_return']:8.1f} pi {stats['pg']:+.3f} "
|
|
327
|
+
f"v {stats['v']:.2f} ent {stats['ent']:.2f} kl {stats['kl']:.3f} "
|
|
328
|
+
f"({time.time()-t_start:.0f}s)", flush=True)
|
|
329
|
+
elif it % args.log_every == 0:
|
|
330
|
+
print(f"iter {it:4d} steps {global_steps/1e6:5.2f}M "
|
|
331
|
+
f"train_ret {mean_train_ret:8.1f} "
|
|
332
|
+
f"pi {stats['pg']:+.3f} v {stats['v']:.2f} ent {stats['ent']:.2f} "
|
|
333
|
+
f"kl {stats['kl']:.3f} ({time.time()-t_start:.0f}s)", flush=True)
|
|
334
|
+
writer.flush()
|
|
335
|
+
|
|
336
|
+
ev_final = evaluate(make_ppo_policy(ac.actor), p, args.episode_steps,
|
|
337
|
+
reward_w=reward_w, obs_mode=args.obs)
|
|
338
|
+
eval_curve.append(dict(iter=it, global_steps=global_steps, **ev_final))
|
|
339
|
+
for k, v in ev_final.items():
|
|
340
|
+
if isinstance(v, (int, float)):
|
|
341
|
+
writer.add_scalar(f"eval_final/{k}", v, global_steps)
|
|
342
|
+
final_path = exp + "_final.pt"
|
|
343
|
+
save_ckpt(final_path, ev_final)
|
|
344
|
+
last_path = exp + "_last.pt"
|
|
345
|
+
save_ckpt(last_path, ev_final)
|
|
346
|
+
|
|
347
|
+
# Extra deterministic rollouts from the training reset distribution. This
|
|
348
|
+
# is NOT the mandated fixed-state eval; it checks generalisation.
|
|
349
|
+
rob = None
|
|
350
|
+
rob_best = None
|
|
351
|
+
if args.robust_starts > 0:
|
|
352
|
+
g = torch.Generator().manual_seed(args.seed * 1000 + 123)
|
|
353
|
+
th_r, om_r = sample_train_init(args.robust_starts, gen=g)
|
|
354
|
+
starts = list(zip(th_r.numpy(), om_r.numpy()))
|
|
355
|
+
rob = evaluate_starts(make_ppo_policy(ac.actor), p, starts,
|
|
356
|
+
args.episode_steps, reward_w=reward_w,
|
|
357
|
+
obs_mode=args.obs)
|
|
358
|
+
for k, v in rob.items():
|
|
359
|
+
if isinstance(v, (int, float)):
|
|
360
|
+
writer.add_scalar(f"robust/{k}", v, global_steps)
|
|
361
|
+
if best_path is not None:
|
|
362
|
+
ck = torch.load(os.path.join(args.ckpt_dir, best_path),
|
|
363
|
+
map_location="cpu")
|
|
364
|
+
ac_best = ActorCritic(obs_dim, act_dim=2, hidden=args.hidden,
|
|
365
|
+
depth=args.depth)
|
|
366
|
+
ac_best.load_state_dict(ck["model"])
|
|
367
|
+
rob_best = evaluate_starts(make_ppo_policy(ac_best.actor), p, starts,
|
|
368
|
+
args.episode_steps, reward_w=reward_w,
|
|
369
|
+
obs_mode=args.obs)
|
|
370
|
+
for k, v in rob_best.items():
|
|
371
|
+
if isinstance(v, (int, float)):
|
|
372
|
+
writer.add_scalar(f"robust_best/{k}", v, global_steps)
|
|
373
|
+
|
|
374
|
+
wall = time.time() - t_start
|
|
375
|
+
out = dict(algo="ppo", exp=exp, seed=args.seed, envs=args.envs,
|
|
376
|
+
horizon=args.horizon, obs=spec, obs_dim=obs_dim,
|
|
377
|
+
init=init_meta, reward=reward_w, reward_preset=args.reward,
|
|
378
|
+
reward_note=TORCH_REWARD_NOTE, args=vars(args),
|
|
379
|
+
minutes_budget=args.minutes, global_steps=global_steps,
|
|
380
|
+
iters=it, wall_time_s=wall,
|
|
381
|
+
steps_per_s=global_steps / max(wall, 1e-9),
|
|
382
|
+
final_eval=ev_final, best_eval=best_eval,
|
|
383
|
+
best_t_can=best_t_can, eval_curve=eval_curve,
|
|
384
|
+
robust=rob, robust_best=rob_best,
|
|
385
|
+
final_train=dict(policy_loss=last.get("pg"),
|
|
386
|
+
value_loss=last.get("v"),
|
|
387
|
+
entropy=last.get("ent"),
|
|
388
|
+
approx_kl=last.get("kl")),
|
|
389
|
+
checkpoints=dict(best=best_path, last=last_path,
|
|
390
|
+
final=final_path))
|
|
391
|
+
with open(os.path.join(args.out, exp + ".json"), "w") as f:
|
|
392
|
+
json.dump(out, f, indent=2)
|
|
393
|
+
writer.flush()
|
|
394
|
+
writer.close()
|
|
395
|
+
print("DONE " + json.dumps(dict(
|
|
396
|
+
exp=exp, init=args.init, seed=args.seed, global_steps=global_steps,
|
|
397
|
+
wall_s=round(wall, 1), steps_per_s=round(out["steps_per_s"], 1),
|
|
398
|
+
eval_t_can=ev_final["t_can"], best_t_can=best_t_can,
|
|
399
|
+
eval_return=ev_final["ep_return"])), flush=True)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
if __name__ == "__main__":
|
|
403
|
+
main()
|