drlab 0.2.1__tar.gz → 0.2.3__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.
- {drlab-0.2.1/src/drlab.egg-info → drlab-0.2.3}/PKG-INFO +1 -1
- {drlab-0.2.1 → drlab-0.2.3}/pyproject.toml +1 -1
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/__init__.py +3 -1
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/controllers/__init__.py +4 -2
- drlab-0.2.3/src/drlab/controllers/warmup.py +42 -0
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/experiments/off_policy_experiment.py +46 -19
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/experiments/on_policy_experiment.py +1 -1
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/learners/off_policy/base.py +4 -4
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/learners/off_policy/dqn.py +6 -4
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/learners/off_policy/sac.py +15 -5
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/learners/on_policy/actor_critic.py +8 -8
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/learners/on_policy/base.py +5 -5
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/learners/on_policy/ppo.py +4 -4
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/learners/on_policy/reinforce.py +4 -4
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/replay/replay_buffer.py +18 -8
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/replay/transition_batch.py +6 -3
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/runners/runner.py +29 -16
- {drlab-0.2.1 → drlab-0.2.3/src/drlab.egg-info}/PKG-INFO +1 -1
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab.egg-info/SOURCES.txt +4 -2
- {drlab-0.2.1 → drlab-0.2.3}/tests/test_controllers.py +46 -0
- {drlab-0.2.1 → drlab-0.2.3}/tests/test_learners.py +56 -6
- drlab-0.2.3/tests/test_off_policy_experiment.py +155 -0
- {drlab-0.2.1 → drlab-0.2.3}/tests/test_public_api.py +2 -1
- {drlab-0.2.1 → drlab-0.2.3}/tests/test_replay.py +21 -9
- {drlab-0.2.1 → drlab-0.2.3}/tests/test_runner.py +23 -0
- {drlab-0.2.1 → drlab-0.2.3}/LICENSE +0 -0
- {drlab-0.2.1 → drlab-0.2.3}/README.md +0 -0
- {drlab-0.2.1 → drlab-0.2.3}/setup.cfg +0 -0
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/controllers/base.py +0 -0
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/controllers/e_greedy.py +0 -0
- /drlab-0.2.1/src/drlab/controllers/gaussian_controller.py → /drlab-0.2.3/src/drlab/controllers/gaussian.py +0 -0
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/controllers/greedy.py +0 -0
- /drlab-0.2.1/src/drlab/controllers/stochastic_controller.py → /drlab-0.2.3/src/drlab/controllers/stochastic.py +0 -0
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/experiments/__init__.py +0 -0
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/learners/__init__.py +0 -0
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/learners/off_policy/__init__.py +0 -0
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/learners/off_policy/configs.py +0 -0
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/learners/on_policy/__init__.py +0 -0
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/learners/on_policy/configs.py +0 -0
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/replay/__init__.py +0 -0
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab/runners/__init__.py +0 -0
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab.egg-info/dependency_links.txt +0 -0
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab.egg-info/requires.txt +0 -0
- {drlab-0.2.1 → drlab-0.2.3}/src/drlab.egg-info/top_level.txt +0 -0
|
@@ -6,6 +6,7 @@ from drlab.controllers import (
|
|
|
6
6
|
GaussianController,
|
|
7
7
|
GreedyController,
|
|
8
8
|
StochasticController,
|
|
9
|
+
WarmupController,
|
|
9
10
|
)
|
|
10
11
|
from drlab.experiments import (
|
|
11
12
|
OnPolicyExperiment,
|
|
@@ -34,7 +35,7 @@ from drlab.learners import (
|
|
|
34
35
|
from drlab.replay import ReplayBuffer, TransitionBatch
|
|
35
36
|
from drlab.runners import Runner
|
|
36
37
|
|
|
37
|
-
__version__ = "0.2.
|
|
38
|
+
__version__ = "0.2.3"
|
|
38
39
|
|
|
39
40
|
__all__ = [
|
|
40
41
|
"__version__",
|
|
@@ -68,4 +69,5 @@ __all__ = [
|
|
|
68
69
|
"StochasticController",
|
|
69
70
|
"TransitionBatch",
|
|
70
71
|
"ValueTargets",
|
|
72
|
+
"WarmupController",
|
|
71
73
|
]
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from .base import ContinuousActionController, Controller, DiscreteActionController
|
|
2
2
|
from .greedy import GreedyController
|
|
3
3
|
from .e_greedy import EpsilonGreedyController
|
|
4
|
-
from .
|
|
5
|
-
from .
|
|
4
|
+
from .stochastic import StochasticController
|
|
5
|
+
from .gaussian import GaussianController
|
|
6
|
+
from .warmup import WarmupController
|
|
6
7
|
|
|
7
8
|
__all__ = [
|
|
8
9
|
"ContinuousActionController",
|
|
@@ -12,4 +13,5 @@ __all__ = [
|
|
|
12
13
|
"GaussianController",
|
|
13
14
|
"GreedyController",
|
|
14
15
|
"StochasticController",
|
|
16
|
+
"WarmupController",
|
|
15
17
|
]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import torch as th
|
|
5
|
+
|
|
6
|
+
from .base import ContinuousActionController, Controller, DiscreteActionController
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class WarmupController(ContinuousActionController, DiscreteActionController):
|
|
10
|
+
def __init__(self, controller: Controller, action_space: Any, warmup_steps: int):
|
|
11
|
+
self.controller = controller
|
|
12
|
+
self.action_space = action_space
|
|
13
|
+
self.warmup_steps = warmup_steps
|
|
14
|
+
self.steps = 0
|
|
15
|
+
self.model = controller.model
|
|
16
|
+
|
|
17
|
+
if hasattr(controller, "num_actions"):
|
|
18
|
+
self.num_actions = controller.num_actions
|
|
19
|
+
if hasattr(controller, "action_dim"):
|
|
20
|
+
self.action_dim = controller.action_dim
|
|
21
|
+
|
|
22
|
+
if warmup_steps < 0:
|
|
23
|
+
raise ValueError("warmup_steps must be >= 0.")
|
|
24
|
+
|
|
25
|
+
def choose(
|
|
26
|
+
self,
|
|
27
|
+
obs: th.Tensor,
|
|
28
|
+
**kwargs,
|
|
29
|
+
) -> th.Tensor:
|
|
30
|
+
if self.steps >= self.warmup_steps:
|
|
31
|
+
return self.controller.choose(obs, **kwargs)
|
|
32
|
+
|
|
33
|
+
batch_size = obs.shape[0]
|
|
34
|
+
self.steps += batch_size
|
|
35
|
+
actions = [self.action_space.sample() for _ in range(batch_size)]
|
|
36
|
+
return th.as_tensor(np.asarray(actions), device=obs.device)
|
|
37
|
+
|
|
38
|
+
def probabilities(self, obs: th.Tensor, **kwargs) -> th.Tensor:
|
|
39
|
+
if self.steps >= self.warmup_steps:
|
|
40
|
+
return self.controller.probabilities(obs, **kwargs)
|
|
41
|
+
|
|
42
|
+
return th.ones((obs.shape[0], self.num_actions), device=obs.device) / self.num_actions
|
|
@@ -2,12 +2,12 @@ import numpy as np
|
|
|
2
2
|
from tqdm import tqdm
|
|
3
3
|
import gymnasium as gym
|
|
4
4
|
from dataclasses import dataclass
|
|
5
|
-
from typing import Callable
|
|
5
|
+
from typing import Callable
|
|
6
6
|
from torch.utils.tensorboard import SummaryWriter
|
|
7
7
|
|
|
8
8
|
from drlab.learners import OffPolicyLearner, SACLearner
|
|
9
9
|
from drlab.runners import Runner
|
|
10
|
-
from drlab.controllers import Controller
|
|
10
|
+
from drlab.controllers import Controller, WarmupController
|
|
11
11
|
from drlab.replay import TransitionBatch, ReplayBuffer
|
|
12
12
|
|
|
13
13
|
@dataclass
|
|
@@ -24,6 +24,8 @@ class OffPolicyExperimentConfig:
|
|
|
24
24
|
grad_repeats: int = 1
|
|
25
25
|
step_callback: Callable[[int], None] | None = None
|
|
26
26
|
step_callback_interval: int | None = None
|
|
27
|
+
learning_starts: int | None = None
|
|
28
|
+
warmup_steps: int = 0
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
class OffPolicyExperiment:
|
|
@@ -44,9 +46,22 @@ class OffPolicyExperiment:
|
|
|
44
46
|
self.run_steps = config.run_steps
|
|
45
47
|
self.step_callback = config.step_callback
|
|
46
48
|
self.step_callback_interval = config.step_callback_interval
|
|
49
|
+
self.steps = 0
|
|
50
|
+
self.learning_starts = (
|
|
51
|
+
config.batch_size if config.learning_starts is None else config.learning_starts
|
|
52
|
+
)
|
|
53
|
+
self.warmup_steps = config.warmup_steps
|
|
54
|
+
|
|
55
|
+
self._validate_config()
|
|
47
56
|
|
|
48
57
|
# Init drl components
|
|
49
58
|
continous_actions = isinstance(learner, SACLearner)
|
|
59
|
+
if self.warmup_steps > 0:
|
|
60
|
+
controller = WarmupController(
|
|
61
|
+
controller,
|
|
62
|
+
env.action_space,
|
|
63
|
+
warmup_steps=self.warmup_steps,
|
|
64
|
+
)
|
|
50
65
|
self.runner = Runner(
|
|
51
66
|
env,
|
|
52
67
|
controller,
|
|
@@ -73,9 +88,17 @@ class OffPolicyExperiment:
|
|
|
73
88
|
self.writer = SummaryWriter(log_dir=config.log_dir)
|
|
74
89
|
self.experiment_name = config.experiment_name
|
|
75
90
|
|
|
91
|
+
|
|
92
|
+
def _validate_config(self):
|
|
76
93
|
if self.step_callback is not None and self.step_callback_interval is None:
|
|
77
94
|
raise ValueError("step_callback_interval must be set when step_callback is provided.")
|
|
78
95
|
|
|
96
|
+
if self.learning_starts < self.batch_size:
|
|
97
|
+
raise ValueError("learning_starts must be greater than or equal to batch_size.")
|
|
98
|
+
|
|
99
|
+
if self.warmup_steps < 0:
|
|
100
|
+
raise ValueError("warmup_steps must be greater than or equal to 0.")
|
|
101
|
+
|
|
79
102
|
def _make_minibatch(self, batch: TransitionBatch, last_episode: TransitionBatch | None) -> TransitionBatch:
|
|
80
103
|
|
|
81
104
|
# No replay buffer
|
|
@@ -97,17 +120,22 @@ class OffPolicyExperiment:
|
|
|
97
120
|
rest = self.replay_buffer.sample(self.batch_size - ep_len)
|
|
98
121
|
return rest.cat(episode)
|
|
99
122
|
|
|
100
|
-
def _learn_from_batch(
|
|
123
|
+
def _learn_from_batch(
|
|
124
|
+
self,
|
|
125
|
+
batch: TransitionBatch,
|
|
126
|
+
last_episode: TransitionBatch | None,
|
|
127
|
+
) -> float:
|
|
101
128
|
# 1) Add batch to replay buffer
|
|
102
129
|
self.replay_buffer.add(
|
|
103
130
|
states=batch.states,
|
|
104
131
|
actions=batch.actions,
|
|
105
132
|
rewards=batch.rewards,
|
|
106
|
-
|
|
133
|
+
terminated=batch.terminated,
|
|
134
|
+
truncated=batch.truncated,
|
|
107
135
|
next_states=batch.next_states,
|
|
108
136
|
returns=batch.returns,
|
|
109
137
|
)
|
|
110
|
-
if self.
|
|
138
|
+
if self.steps < self.learning_starts:
|
|
111
139
|
return 0.0
|
|
112
140
|
|
|
113
141
|
# 2) train repeats
|
|
@@ -115,19 +143,19 @@ class OffPolicyExperiment:
|
|
|
115
143
|
for _ in range(self.grad_repeats):
|
|
116
144
|
mb = self._make_minibatch(batch, last_episode)
|
|
117
145
|
total_loss += self.learner.train(
|
|
118
|
-
mb.rewards, mb.
|
|
146
|
+
mb.rewards, mb.terminated, mb.states, mb.actions, mb.next_states
|
|
119
147
|
)
|
|
120
148
|
|
|
121
149
|
return total_loss / self.grad_repeats
|
|
122
150
|
|
|
123
151
|
def run(self):
|
|
124
|
-
|
|
125
|
-
steps = 0
|
|
152
|
+
|
|
153
|
+
self.steps = 0
|
|
126
154
|
if self.step_callback is not None:
|
|
127
|
-
self.step_callback(steps)
|
|
155
|
+
self.step_callback(self.steps)
|
|
128
156
|
next_callback_step = self.step_callback_interval
|
|
129
157
|
pbar = tqdm(total=self.max_steps, desc=self.experiment_name, dynamic_ncols=True, mininterval=1.0)
|
|
130
|
-
while steps < self.max_steps:
|
|
158
|
+
while self.steps < self.max_steps:
|
|
131
159
|
|
|
132
160
|
# 1. Run batch of transitions
|
|
133
161
|
batch, ep_returns, ep_lengths, last_episode = self.runner.run(
|
|
@@ -135,16 +163,20 @@ class OffPolicyExperiment:
|
|
|
135
163
|
as_numpy=True,
|
|
136
164
|
)
|
|
137
165
|
|
|
166
|
+
step_inc = batch.states.shape[0]
|
|
167
|
+
self.steps += step_inc
|
|
168
|
+
pbar.update(step_inc)
|
|
169
|
+
|
|
138
170
|
# 2. Learn from batch
|
|
139
171
|
loss = self._learn_from_batch(batch, last_episode)
|
|
140
172
|
|
|
141
173
|
# 3. Log results
|
|
142
|
-
self.writer.add_scalar("Loss", loss, steps)
|
|
174
|
+
self.writer.add_scalar("Loss", loss, self.steps)
|
|
143
175
|
if ep_returns:
|
|
144
176
|
mean_return = np.mean(ep_returns)
|
|
145
177
|
mean_length = np.mean(ep_lengths)
|
|
146
|
-
self.writer.add_scalar("Episode return", mean_return, steps)
|
|
147
|
-
self.writer.add_scalar("Episode Length", mean_length, steps)
|
|
178
|
+
self.writer.add_scalar("Episode return", mean_return, self.steps)
|
|
179
|
+
self.writer.add_scalar("Episode Length", mean_length, self.steps)
|
|
148
180
|
|
|
149
181
|
pbar.set_postfix({
|
|
150
182
|
"loss": f"{loss:.3f}",
|
|
@@ -152,12 +184,7 @@ class OffPolicyExperiment:
|
|
|
152
184
|
"len": f"{mean_length:.1f}",
|
|
153
185
|
}, refresh=False)
|
|
154
186
|
|
|
155
|
-
|
|
156
|
-
step_inc = batch.states.shape[0]
|
|
157
|
-
steps += step_inc
|
|
158
|
-
pbar.update(step_inc)
|
|
159
|
-
|
|
160
|
-
while self.step_callback is not None and steps >= next_callback_step:
|
|
187
|
+
while self.step_callback is not None and self.steps >= next_callback_step:
|
|
161
188
|
self.step_callback(next_callback_step)
|
|
162
189
|
next_callback_step += self.step_callback_interval
|
|
163
190
|
|
|
@@ -88,7 +88,7 @@ class OffPolicyLearner(ABC):
|
|
|
88
88
|
self,
|
|
89
89
|
model: th.nn.Module,
|
|
90
90
|
rewards: th.Tensor,
|
|
91
|
-
|
|
91
|
+
terminated: th.Tensor,
|
|
92
92
|
states: th.Tensor,
|
|
93
93
|
actions: th.Tensor,
|
|
94
94
|
next_states: th.Tensor,
|
|
@@ -102,7 +102,7 @@ class OffPolicyLearner(ABC):
|
|
|
102
102
|
value = regularizer(
|
|
103
103
|
model,
|
|
104
104
|
rewards,
|
|
105
|
-
|
|
105
|
+
terminated,
|
|
106
106
|
states,
|
|
107
107
|
actions,
|
|
108
108
|
next_states,
|
|
@@ -139,9 +139,9 @@ class OffPolicyLearner(ABC):
|
|
|
139
139
|
def train(
|
|
140
140
|
self,
|
|
141
141
|
rewards: th.Tensor,
|
|
142
|
-
|
|
142
|
+
terminated: th.Tensor,
|
|
143
143
|
states: th.Tensor,
|
|
144
144
|
actions: th.Tensor,
|
|
145
145
|
next_states: th.Tensor,
|
|
146
146
|
) -> float:
|
|
147
|
-
pass
|
|
147
|
+
pass
|
|
@@ -58,7 +58,7 @@ class DQNLearner(OffPolicyLearner):
|
|
|
58
58
|
def train(
|
|
59
59
|
self,
|
|
60
60
|
rewards: th.Tensor, # float32, [B,1]
|
|
61
|
-
|
|
61
|
+
terminated: th.Tensor, # bool or float(0/1), [B,1]
|
|
62
62
|
states: th.Tensor, # float32, [B, obs_dim] or [B,C,H,W]
|
|
63
63
|
actions: th.Tensor, # int64, [B,1]
|
|
64
64
|
next_states: th.Tensor, # float32, same as states
|
|
@@ -66,8 +66,10 @@ class DQNLearner(OffPolicyLearner):
|
|
|
66
66
|
self.model.train(True)
|
|
67
67
|
|
|
68
68
|
# Compute targets
|
|
69
|
-
|
|
70
|
-
targets = rewards + self.gamma * (
|
|
69
|
+
not_terminated = 1.0 - terminated.float()
|
|
70
|
+
targets = rewards + self.gamma * (
|
|
71
|
+
not_terminated * self.next_values(next_states)
|
|
72
|
+
)
|
|
71
73
|
pred = self.current_values(states, actions)
|
|
72
74
|
|
|
73
75
|
# Compute loss
|
|
@@ -75,7 +77,7 @@ class DQNLearner(OffPolicyLearner):
|
|
|
75
77
|
reg_loss = self.regularization_loss(
|
|
76
78
|
self.model,
|
|
77
79
|
rewards=rewards,
|
|
78
|
-
|
|
80
|
+
terminated=terminated,
|
|
79
81
|
states=states,
|
|
80
82
|
actions=actions,
|
|
81
83
|
next_states=next_states,
|
|
@@ -90,7 +90,7 @@ class SACLearner(OffPolicyLearner):
|
|
|
90
90
|
def train(
|
|
91
91
|
self,
|
|
92
92
|
rewards: th.Tensor, # float32, [B,1]
|
|
93
|
-
|
|
93
|
+
terminated: th.Tensor, # bool or float(0/1), [B,1]
|
|
94
94
|
states: th.Tensor, # float32, [B, obs_dim] or [B,C,H,W]
|
|
95
95
|
actions: th.Tensor, # float32, [B, action_dim]
|
|
96
96
|
next_states: th.Tensor, # float32, same as states
|
|
@@ -110,9 +110,9 @@ class SACLearner(OffPolicyLearner):
|
|
|
110
110
|
target_q2 = self.critic2_target(next_action_states)
|
|
111
111
|
target_q = th.min(target_q1, target_q2)
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
not_terminated = 1.0 - terminated.float()
|
|
114
114
|
targets = rewards + self.config.gamma * (
|
|
115
|
-
|
|
115
|
+
not_terminated * (target_q - self.alpha.detach() * next_log_probs)
|
|
116
116
|
)
|
|
117
117
|
|
|
118
118
|
action_states = th.cat([states, actions], dim=-1)
|
|
@@ -145,7 +145,16 @@ class SACLearner(OffPolicyLearner):
|
|
|
145
145
|
new_q2 = self.critic2(new_action_states)
|
|
146
146
|
new_q = th.min(new_q1, new_q2)
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
policy_loss = (self.alpha.detach() * log_probs - new_q).mean()
|
|
149
|
+
reg_loss = self.regularization_loss(
|
|
150
|
+
self.actor,
|
|
151
|
+
rewards=rewards,
|
|
152
|
+
terminated=terminated,
|
|
153
|
+
states=states,
|
|
154
|
+
actions=actions,
|
|
155
|
+
next_states=next_states,
|
|
156
|
+
)
|
|
157
|
+
actor_loss = policy_loss + reg_loss
|
|
149
158
|
self.optimize(
|
|
150
159
|
actor_loss,
|
|
151
160
|
optimizer=self.actor_optimizer,
|
|
@@ -176,9 +185,10 @@ class SACLearner(OffPolicyLearner):
|
|
|
176
185
|
|
|
177
186
|
loss = actor_loss + critic_loss + alpha_loss
|
|
178
187
|
self.last_losses = {
|
|
179
|
-
"actor": float(
|
|
188
|
+
"actor": float(policy_loss.item()),
|
|
180
189
|
"critic": float(critic_loss.item()),
|
|
181
190
|
"alpha": float(alpha_loss.item()),
|
|
191
|
+
"regularization": float(reg_loss.item()),
|
|
182
192
|
"total": float(loss.item()),
|
|
183
193
|
}
|
|
184
194
|
|
|
@@ -28,14 +28,14 @@ class ActorCriticLearner(OnPolicyLearner):
|
|
|
28
28
|
self,
|
|
29
29
|
returns: th.Tensor,
|
|
30
30
|
rewards: th.Tensor,
|
|
31
|
-
|
|
31
|
+
terminated: th.Tensor,
|
|
32
32
|
values: th.Tensor,
|
|
33
33
|
next_values: th.Tensor,
|
|
34
34
|
) -> th.Tensor:
|
|
35
35
|
if self.config.value_targets == ValueTargets.RETURNS:
|
|
36
36
|
targets = returns
|
|
37
37
|
elif self.config.value_targets == ValueTargets.TD:
|
|
38
|
-
targets = rewards + self.config.gamma * (~
|
|
38
|
+
targets = rewards + self.config.gamma * (~terminated * next_values)
|
|
39
39
|
else:
|
|
40
40
|
raise ValueError(f"Unknown value_targets: {self.config.value_targets}")
|
|
41
41
|
return self.config.value_criterion(values, targets)
|
|
@@ -44,13 +44,13 @@ class ActorCriticLearner(OnPolicyLearner):
|
|
|
44
44
|
self,
|
|
45
45
|
returns: th.Tensor,
|
|
46
46
|
rewards: th.Tensor,
|
|
47
|
-
|
|
47
|
+
terminated: th.Tensor,
|
|
48
48
|
values: th.Tensor,
|
|
49
49
|
next_values: th.Tensor
|
|
50
50
|
) -> th.Tensor:
|
|
51
51
|
advantages = None
|
|
52
52
|
if self.config.advantage_bootstrap:
|
|
53
|
-
advantages = rewards + self.config.gamma * (~
|
|
53
|
+
advantages = rewards + self.config.gamma * (~terminated * next_values)
|
|
54
54
|
else:
|
|
55
55
|
advantages = returns
|
|
56
56
|
if self.config.use_bias:
|
|
@@ -74,7 +74,7 @@ class ActorCriticLearner(OnPolicyLearner):
|
|
|
74
74
|
def train(
|
|
75
75
|
self,
|
|
76
76
|
rewards: th.Tensor,
|
|
77
|
-
|
|
77
|
+
terminated: th.Tensor,
|
|
78
78
|
states: th.Tensor,
|
|
79
79
|
actions: th.Tensor,
|
|
80
80
|
next_states: th.Tensor,
|
|
@@ -106,7 +106,7 @@ class ActorCriticLearner(OnPolicyLearner):
|
|
|
106
106
|
advantages = self._advantages(
|
|
107
107
|
returns=returns,
|
|
108
108
|
rewards=rewards,
|
|
109
|
-
|
|
109
|
+
terminated=terminated,
|
|
110
110
|
values=values,
|
|
111
111
|
next_values=next_values,
|
|
112
112
|
)
|
|
@@ -119,7 +119,7 @@ class ActorCriticLearner(OnPolicyLearner):
|
|
|
119
119
|
value_loss = self.config.value_lambda * self._value_loss(
|
|
120
120
|
returns=returns,
|
|
121
121
|
rewards=rewards,
|
|
122
|
-
|
|
122
|
+
terminated=terminated,
|
|
123
123
|
values=values,
|
|
124
124
|
next_values=next_values,
|
|
125
125
|
)
|
|
@@ -128,7 +128,7 @@ class ActorCriticLearner(OnPolicyLearner):
|
|
|
128
128
|
|
|
129
129
|
reg_loss = self._regularization_loss(
|
|
130
130
|
rewards=rewards,
|
|
131
|
-
|
|
131
|
+
terminated=terminated,
|
|
132
132
|
states=states,
|
|
133
133
|
actions=actions,
|
|
134
134
|
next_states=next_states,
|
|
@@ -50,7 +50,7 @@ class OnPolicyLearner:
|
|
|
50
50
|
def _regularization_loss(
|
|
51
51
|
self,
|
|
52
52
|
rewards: th.Tensor,
|
|
53
|
-
|
|
53
|
+
terminated: th.Tensor,
|
|
54
54
|
states: th.Tensor,
|
|
55
55
|
actions: th.Tensor,
|
|
56
56
|
next_states: th.Tensor,
|
|
@@ -59,7 +59,7 @@ class OnPolicyLearner:
|
|
|
59
59
|
return 0.0
|
|
60
60
|
|
|
61
61
|
return sum(
|
|
62
|
-
lam * reg(self.actor, rewards,
|
|
62
|
+
lam * reg(self.actor, rewards, terminated, states, actions, next_states)
|
|
63
63
|
for reg, lam in zip(self.config.regularizers, self.config.reg_lams)
|
|
64
64
|
)
|
|
65
65
|
|
|
@@ -67,7 +67,7 @@ class OnPolicyLearner:
|
|
|
67
67
|
self,
|
|
68
68
|
returns: th.Tensor,
|
|
69
69
|
rewards: th.Tensor,
|
|
70
|
-
|
|
70
|
+
terminated: th.Tensor,
|
|
71
71
|
values: th.Tensor,
|
|
72
72
|
next_values: th.Tensor,
|
|
73
73
|
) -> th.Tensor:
|
|
@@ -106,7 +106,7 @@ class OnPolicyLearner:
|
|
|
106
106
|
self,
|
|
107
107
|
returns: th.Tensor,
|
|
108
108
|
rewards: th.Tensor,
|
|
109
|
-
|
|
109
|
+
terminated: th.Tensor,
|
|
110
110
|
values: th.Tensor,
|
|
111
111
|
next_values: th.Tensor
|
|
112
112
|
) -> th.Tensor:
|
|
@@ -115,7 +115,7 @@ class OnPolicyLearner:
|
|
|
115
115
|
def train(
|
|
116
116
|
self,
|
|
117
117
|
rewards: th.Tensor, # float32, [B,1]
|
|
118
|
-
|
|
118
|
+
terminated: th.Tensor, # bool or float(0/1), [B,1]
|
|
119
119
|
states: th.Tensor, # float32, [B, obs_dim] or [B,C,H,W]
|
|
120
120
|
actions: th.Tensor, # int64, [B,1]
|
|
121
121
|
next_states: th.Tensor, # float32, same as states
|
|
@@ -38,7 +38,7 @@ class PPOLearner(ActorCriticLearner):
|
|
|
38
38
|
def train(
|
|
39
39
|
self,
|
|
40
40
|
rewards: th.Tensor,
|
|
41
|
-
|
|
41
|
+
terminated: th.Tensor,
|
|
42
42
|
states: th.Tensor,
|
|
43
43
|
actions: th.Tensor,
|
|
44
44
|
next_states: th.Tensor,
|
|
@@ -69,7 +69,7 @@ class PPOLearner(ActorCriticLearner):
|
|
|
69
69
|
advantages = self._advantages(
|
|
70
70
|
returns=returns,
|
|
71
71
|
rewards=rewards,
|
|
72
|
-
|
|
72
|
+
terminated=terminated,
|
|
73
73
|
values=old_values,
|
|
74
74
|
next_values=next_values,
|
|
75
75
|
)
|
|
@@ -96,7 +96,7 @@ class PPOLearner(ActorCriticLearner):
|
|
|
96
96
|
value_loss = self.config.value_lambda * self._value_loss(
|
|
97
97
|
returns=returns,
|
|
98
98
|
rewards=rewards,
|
|
99
|
-
|
|
99
|
+
terminated=terminated,
|
|
100
100
|
values=values,
|
|
101
101
|
next_values=next_values,
|
|
102
102
|
)
|
|
@@ -105,7 +105,7 @@ class PPOLearner(ActorCriticLearner):
|
|
|
105
105
|
|
|
106
106
|
reg_loss = self._regularization_loss(
|
|
107
107
|
rewards=rewards,
|
|
108
|
-
|
|
108
|
+
terminated=terminated,
|
|
109
109
|
states=states,
|
|
110
110
|
actions=actions,
|
|
111
111
|
next_states=next_states,
|
|
@@ -18,7 +18,7 @@ class ReinforceLearner(OnPolicyLearner):
|
|
|
18
18
|
self,
|
|
19
19
|
returns: th.Tensor,
|
|
20
20
|
rewards: th.Tensor,
|
|
21
|
-
|
|
21
|
+
terminated: th.Tensor,
|
|
22
22
|
values: th.Tensor | None,
|
|
23
23
|
next_values: th.Tensor | None,
|
|
24
24
|
) -> th.Tensor:
|
|
@@ -34,7 +34,7 @@ class ReinforceLearner(OnPolicyLearner):
|
|
|
34
34
|
def train(
|
|
35
35
|
self,
|
|
36
36
|
rewards: th.Tensor,
|
|
37
|
-
|
|
37
|
+
terminated: th.Tensor,
|
|
38
38
|
states: th.Tensor,
|
|
39
39
|
actions: th.Tensor,
|
|
40
40
|
next_states: th.Tensor,
|
|
@@ -50,7 +50,7 @@ class ReinforceLearner(OnPolicyLearner):
|
|
|
50
50
|
advantages = self._advantages(
|
|
51
51
|
returns=returns,
|
|
52
52
|
rewards=rewards,
|
|
53
|
-
|
|
53
|
+
terminated=terminated,
|
|
54
54
|
values=None,
|
|
55
55
|
next_values=None,
|
|
56
56
|
)
|
|
@@ -60,7 +60,7 @@ class ReinforceLearner(OnPolicyLearner):
|
|
|
60
60
|
entropy_loss = self._entropy_loss(logits)
|
|
61
61
|
reg_loss = self._regularization_loss(
|
|
62
62
|
rewards=rewards,
|
|
63
|
-
|
|
63
|
+
terminated=terminated,
|
|
64
64
|
states=states,
|
|
65
65
|
actions=actions,
|
|
66
66
|
next_states=next_states,
|
|
@@ -27,7 +27,8 @@ class ReplayBuffer:
|
|
|
27
27
|
self.actions = np.zeros((capacity, *action_shape), dtype=action_dtype)
|
|
28
28
|
|
|
29
29
|
self.rewards = np.zeros((capacity, 1), dtype=np.float32)
|
|
30
|
-
self.
|
|
30
|
+
self.terminated = np.zeros((capacity, 1), dtype=np.bool_)
|
|
31
|
+
self.truncated = np.zeros((capacity, 1), dtype=np.bool_)
|
|
31
32
|
self.returns = np.zeros((capacity, 1), dtype=np.float32)
|
|
32
33
|
|
|
33
34
|
self.ptr = 0
|
|
@@ -41,7 +42,8 @@ class ReplayBuffer:
|
|
|
41
42
|
states: np.ndarray, # [B, *obs_shape]
|
|
42
43
|
actions: np.ndarray, # [B] or [B,1]
|
|
43
44
|
rewards: np.ndarray, # [B] or [B,1]
|
|
44
|
-
|
|
45
|
+
terminated: np.ndarray, # [B] or [B,1] (bool)
|
|
46
|
+
truncated: np.ndarray, # [B] or [B,1] (bool)
|
|
45
47
|
next_states: np.ndarray, # [B, *obs_shape]
|
|
46
48
|
returns: np.ndarray # [B, 1]
|
|
47
49
|
|
|
@@ -56,7 +58,8 @@ class ReplayBuffer:
|
|
|
56
58
|
else:
|
|
57
59
|
actions = np.asarray(actions, dtype=np.int64).reshape(B, 1)
|
|
58
60
|
rewards = np.asarray(rewards, dtype=np.float32).reshape(B, 1)
|
|
59
|
-
|
|
61
|
+
terminated = np.asarray(terminated, dtype=np.bool_).reshape(B, 1)
|
|
62
|
+
truncated = np.asarray(truncated, dtype=np.bool_).reshape(B, 1)
|
|
60
63
|
returns = np.asarray(returns, dtype=np.float32).reshape(B, 1)
|
|
61
64
|
|
|
62
65
|
idx = (self.ptr + np.arange(B)) % self.capacity
|
|
@@ -65,7 +68,8 @@ class ReplayBuffer:
|
|
|
65
68
|
self.next_states[idx] = next_states
|
|
66
69
|
self.actions[idx] = actions
|
|
67
70
|
self.rewards[idx] = rewards
|
|
68
|
-
self.
|
|
71
|
+
self.terminated[idx] = terminated
|
|
72
|
+
self.truncated[idx] = truncated
|
|
69
73
|
self.returns[idx] = returns
|
|
70
74
|
|
|
71
75
|
self.ptr = (self.ptr + B) % self.capacity
|
|
@@ -76,9 +80,12 @@ class ReplayBuffer:
|
|
|
76
80
|
next_states = th.from_numpy(self.next_states[:self.size]).to(self.device)
|
|
77
81
|
actions = th.from_numpy(self.actions[:self.size]).to(self.device)
|
|
78
82
|
rewards = th.from_numpy(self.rewards[:self.size]).to(self.device)
|
|
79
|
-
|
|
83
|
+
terminated = th.from_numpy(self.terminated[:self.size]).to(self.device)
|
|
84
|
+
truncated = th.from_numpy(self.truncated[:self.size]).to(self.device)
|
|
80
85
|
returns = th.from_numpy(self.returns[:self.size]).to(self.device)
|
|
81
|
-
return TransitionBatch(
|
|
86
|
+
return TransitionBatch(
|
|
87
|
+
states, actions, rewards, terminated, truncated, next_states, returns
|
|
88
|
+
)
|
|
82
89
|
|
|
83
90
|
def sample(self, batch_size: int) -> TransitionBatch:
|
|
84
91
|
if self.size == 0:
|
|
@@ -90,7 +97,10 @@ class ReplayBuffer:
|
|
|
90
97
|
next_states = th.from_numpy(self.next_states[idx]).to(self.device)
|
|
91
98
|
actions = th.from_numpy(self.actions[idx]).to(self.device) # long already
|
|
92
99
|
rewards = th.from_numpy(self.rewards[idx]).to(self.device) # float32
|
|
93
|
-
|
|
100
|
+
terminated = th.from_numpy(self.terminated[idx]).to(self.device) # bool
|
|
101
|
+
truncated = th.from_numpy(self.truncated[idx]).to(self.device) # bool
|
|
94
102
|
returns = th.from_numpy(self.returns[idx]).to(self.device) # float32
|
|
95
103
|
|
|
96
|
-
return TransitionBatch(
|
|
104
|
+
return TransitionBatch(
|
|
105
|
+
states, actions, rewards, terminated, truncated, next_states, returns
|
|
106
|
+
)
|
|
@@ -8,7 +8,8 @@ class TransitionBatch:
|
|
|
8
8
|
states: th.Tensor | np.ndarray # [B, obs_dim]
|
|
9
9
|
actions: th.Tensor | np.ndarray # [B, 1] (int64/float32)
|
|
10
10
|
rewards: th.Tensor | np.ndarray # [B, 1] (float)
|
|
11
|
-
|
|
11
|
+
terminated: th.Tensor | np.ndarray # [B, 1] (bool), disables bootstrapping
|
|
12
|
+
truncated: th.Tensor | np.ndarray # [B, 1] (bool), ends an episode only
|
|
12
13
|
next_states: th.Tensor | np.ndarray # [B, obs_dim]
|
|
13
14
|
returns: th.Tensor | np.ndarray # [B, 1] (float)
|
|
14
15
|
|
|
@@ -24,7 +25,8 @@ class TransitionBatch:
|
|
|
24
25
|
states=move(self.states),
|
|
25
26
|
actions=move(self.actions),
|
|
26
27
|
rewards=move(self.rewards),
|
|
27
|
-
|
|
28
|
+
terminated=move(self.terminated),
|
|
29
|
+
truncated=move(self.truncated),
|
|
28
30
|
next_states=move(self.next_states),
|
|
29
31
|
returns=move(self.returns),
|
|
30
32
|
)
|
|
@@ -34,7 +36,8 @@ class TransitionBatch:
|
|
|
34
36
|
states=th.cat([self.states, other.states], dim=0),
|
|
35
37
|
actions=th.cat([self.actions, other.actions], dim=0),
|
|
36
38
|
rewards=th.cat([self.rewards, other.rewards], dim=0),
|
|
37
|
-
|
|
39
|
+
terminated=th.cat([self.terminated, other.terminated], dim=0),
|
|
40
|
+
truncated=th.cat([self.truncated, other.truncated], dim=0),
|
|
38
41
|
next_states=th.cat([self.next_states, other.next_states], dim=0),
|
|
39
42
|
returns=th.cat([self.returns, other.returns], dim=0),
|
|
40
43
|
)
|