rlmodule 0.1.6.2__tar.gz → 0.1.6.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.
Files changed (43) hide show
  1. {rlmodule-0.1.6.2/rlmodule.egg-info → rlmodule-0.1.6.3}/PKG-INFO +2 -2
  2. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/README.md +1 -1
  3. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/pyproject.toml +1 -1
  4. rlmodule-0.1.6.3/rlmodule/skrl/torch/examples/gymnasium/cnn_experimental.py +152 -0
  5. {rlmodule-0.1.6.2/rlmodule/skrl/torch → rlmodule-0.1.6.3/rlmodule/skrl/torch/examples}/gymnasium/pendulum_ppo_mlp_separate_model.py +0 -3
  6. rlmodule-0.1.6.3/rlmodule/skrl/torch/network/__init__.py +29 -0
  7. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/source/build.py +4 -4
  8. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/source/network.py +51 -102
  9. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/source/network_cfg.py +16 -1
  10. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/source/output_layer.py +11 -11
  11. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/source/utils.py +29 -0
  12. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3/rlmodule.egg-info}/PKG-INFO +2 -2
  13. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule.egg-info/SOURCES.txt +11 -10
  14. rlmodule-0.1.6.2/rlmodule/skrl/torch/network/__init__.py +0 -20
  15. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/LICENSE +0 -0
  16. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/__init__.py +0 -0
  17. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/skrl/__init__.py +0 -0
  18. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/skrl/torch/__init__.py +0 -0
  19. {rlmodule-0.1.6.2/rlmodule/skrl/torch → rlmodule-0.1.6.3/rlmodule/skrl/torch/examples}/gymnasium/pendulum_ppo_mlp_shared_model.py +0 -0
  20. {rlmodule-0.1.6.2/rlmodule/skrl/torch → rlmodule-0.1.6.3/rlmodule/skrl/torch/examples}/gymnasium/pendulum_ppo_rnn_forwarded_input_shared_model.py +0 -0
  21. {rlmodule-0.1.6.2/rlmodule/skrl/torch → rlmodule-0.1.6.3/rlmodule/skrl/torch/examples}/gymnasium/pendulum_ppo_rnn_separate_model.py +0 -0
  22. {rlmodule-0.1.6.2/rlmodule/skrl/torch → rlmodule-0.1.6.3/rlmodule/skrl/torch/examples}/gymnasium/pendulum_ppo_rnn_shared_model.py +0 -0
  23. {rlmodule-0.1.6.2/rlmodule/skrl/torch → rlmodule-0.1.6.3/rlmodule/skrl/torch/examples}/gymnasium/pendulum_sac_mlp_separate_model.py +0 -0
  24. {rlmodule-0.1.6.2/rlmodule/skrl/torch → rlmodule-0.1.6.3/rlmodule/skrl/torch/examples}/gymnasium/skrl_no_module.py +0 -0
  25. {rlmodule-0.1.6.2/rlmodule/skrl/torch → rlmodule-0.1.6.3/rlmodule/skrl/torch/examples}/gymnasium/skrl_no_module_rnn.py +0 -0
  26. {rlmodule-0.1.6.2/rlmodule/skrl/torch → rlmodule-0.1.6.3/rlmodule/skrl/torch/examples}/gymnasium/skrl_sac.py +0 -0
  27. {rlmodule-0.1.6.2/rlmodule/skrl/torch → rlmodule-0.1.6.3/rlmodule/skrl/torch/examples}/gymnasium/temp.py +0 -0
  28. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/skrl/torch/output_layer/__init__.py +0 -0
  29. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/source/__init__.py +0 -0
  30. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/source/log_std.py +0 -0
  31. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/source/log_std_cfg.py +0 -0
  32. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/source/model.py +0 -0
  33. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/source/nvidia_utils/__init__.py +0 -0
  34. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/source/nvidia_utils/configclass.py +0 -0
  35. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/source/nvidia_utils/dict.py +0 -0
  36. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/source/nvidia_utils/string.py +0 -0
  37. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/source/output_layer_cfg.py +0 -0
  38. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/source/rlmodel.py +0 -0
  39. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule/source/rlmodel_cfg.py +0 -0
  40. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule.egg-info/dependency_links.txt +0 -0
  41. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule.egg-info/requires.txt +0 -0
  42. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/rlmodule.egg-info/top_level.txt +0 -0
  43. {rlmodule-0.1.6.2 → rlmodule-0.1.6.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rlmodule
3
- Version: 0.1.6.2
3
+ Version: 0.1.6.3
4
4
  Summary: Flexible reinforcement learning models instantiators library
5
5
  Author: Lopatovsky
6
6
  Author-email: lopatovsky@gmail.com
@@ -79,7 +79,7 @@ rm -rf runs
79
79
 
80
80
  ### Run chosen example
81
81
  ```
82
- python3 rlmodule/skrl/torch/gymnasium/pendulum_ppo_mlp_separate_model.py
82
+ python3 rlmodule/skrl/torch/examples/gymnasium/pendulum_ppo_mlp_separate_model.py
83
83
  ```
84
84
 
85
85
  Optional: observe run results in Tensorboard
@@ -46,7 +46,7 @@ rm -rf runs
46
46
 
47
47
  ### Run chosen example
48
48
  ```
49
- python3 rlmodule/skrl/torch/gymnasium/pendulum_ppo_mlp_separate_model.py
49
+ python3 rlmodule/skrl/torch/examples/gymnasium/pendulum_ppo_mlp_separate_model.py
50
50
  ```
51
51
 
52
52
  Optional: observe run results in Tensorboard
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "rlmodule"
3
- version = "0.1.6.2"
3
+ version = "0.1.6.3"
4
4
  description = "Flexible reinforcement learning models instantiators library"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.6"
@@ -0,0 +1,152 @@
1
+ from typing import Tuple
2
+
3
+ import os
4
+ from datetime import datetime
5
+ import gymnasium as gym
6
+
7
+ # import the skrl components to build the RL system
8
+ from skrl.agents.torch.ppo import PPO, PPO_DEFAULT_CONFIG, PPO_RNN
9
+ from skrl.envs.wrappers.torch.gymnasium_envs import GymnasiumWrapper
10
+ from skrl.memories.torch import RandomMemory
11
+ from skrl.resources.preprocessors.torch import RunningStandardScaler
12
+ from skrl.resources.schedulers.torch import KLAdaptiveRL
13
+ from skrl.trainers.torch import SequentialTrainer
14
+ from skrl.utils import set_seed
15
+
16
+ import numpy as np
17
+ import torch.nn as nn
18
+
19
+ from rlmodule.skrl.torch import SharedRLModelCfg, build_model
20
+ from rlmodule.skrl.torch.network import CnnCfg
21
+ from rlmodule.skrl.torch.output_layer import DeterministicLayerCfg, GaussianLayerCfg, ParameterLogStdCfg
22
+
23
+
24
+ # CNN is in an experimental phase. Some combination of inputs may not be yet supported
25
+
26
+
27
+ class DummyEnv(gym.Env):
28
+ """Dummy env producing random observations of given shape."""
29
+
30
+ def __init__(self):
31
+ super(DummyEnv, self).__init__()
32
+ # Define observation space:
33
+ self.observation_space = gym.spaces.Box(low=0, high=255, shape=(169,), dtype=np.uint8)
34
+ # Define action space: Continuous values between -1.0 and 1.0, with 6 action dimensions
35
+ self.action_space = gym.spaces.Box(low=-1.0, high=1.0, shape=(6,), dtype=np.float32)
36
+
37
+ def reset(self) -> Tuple[np.ndarray, dict]:
38
+ # Reset environment and return initial random observation
39
+ observation = self._get_random_observation()
40
+ return observation, {}
41
+
42
+ def step(self, action: np.ndarray) -> Tuple[np.ndarray, float, bool, bool, dict]:
43
+ # Create a new random observation
44
+ observation = self._get_random_observation()
45
+ reward = 0.0 # No meaningful reward in this dummy environment
46
+ terminated = False # This dummy environment never ends
47
+ truncated = False
48
+ info = {}
49
+ return observation, reward, terminated, truncated, info
50
+
51
+ def _get_random_observation(self) -> np.ndarray:
52
+ # Generate a random observation with values between 0 and 255
53
+ return np.random.randint(0, 256, 169, dtype=np.uint8)
54
+
55
+
56
+ def get_model(env):
57
+ """Instantiate the agent's models (function approximators)."""
58
+
59
+ net_cfg = CnnCfg(
60
+ input_states=(13, 13, 1),
61
+ layers=[
62
+ nn.Conv2d(in_channels=1, out_channels=64, kernel_size=3, stride=2),
63
+ nn.ReLU(),
64
+ nn.Flatten(),
65
+ ],
66
+ )
67
+
68
+ model = build_model(
69
+ SharedRLModelCfg(
70
+ network=net_cfg,
71
+ device=device,
72
+ policy_output_layer=GaussianLayerCfg(
73
+ output_size=env.action_space,
74
+ log_std=ParameterLogStdCfg(
75
+ min_log_std=-1.2,
76
+ max_log_std=2,
77
+ initial_log_std=0.0,
78
+ ),
79
+ ),
80
+ value_output_layer=DeterministicLayerCfg(),
81
+ )
82
+ )
83
+
84
+ print(model)
85
+ return {"policy": model, "value": model}
86
+
87
+
88
+ # set seed for reproducibility
89
+ seed = 42
90
+ set_seed(seed)
91
+
92
+ env = DummyEnv()
93
+ # env = gym.make_vec("ALE/Pong-v5", num_envs=4, vectorization_mode="sync")
94
+ env = GymnasiumWrapper(env)
95
+
96
+ device = env.device
97
+
98
+ # instantiate a memory as rollout buffer (any memory can be used for this)
99
+ memory = RandomMemory(memory_size=1024, num_envs=env.num_envs, device=device)
100
+
101
+ models = get_model(env)
102
+
103
+ # configure and instantiate the agent (visit its documentation to see all the options)
104
+ # https://skrl.readthedocs.io/en/latest/api/agents/ppo.html#configuration-and-hyperparameters
105
+ cfg = PPO_DEFAULT_CONFIG.copy()
106
+ cfg["rollouts"] = 1024 # memory_size
107
+ cfg["learning_epochs"] = 10
108
+ cfg["mini_batches"] = 32
109
+ cfg["discount_factor"] = 0.9
110
+ cfg["lambda"] = 0.95
111
+ cfg["learning_rate"] = 1e-3
112
+ cfg["learning_rate_scheduler"] = KLAdaptiveRL
113
+ cfg["learning_rate_scheduler_kwargs"] = {"kl_threshold": 0.008}
114
+ cfg["grad_norm_clip"] = 0.5
115
+ cfg["ratio_clip"] = 0.2
116
+ cfg["value_clip"] = 0.2
117
+ cfg["clip_predicted_values"] = False
118
+ cfg["entropy_loss_scale"] = 0.0
119
+ cfg["value_loss_scale"] = 0.5
120
+ cfg["kl_threshold"] = 0
121
+ cfg["state_preprocessor"] = RunningStandardScaler
122
+ cfg["state_preprocessor_kwargs"] = {"size": env.observation_space, "device": device}
123
+ cfg["value_preprocessor"] = RunningStandardScaler
124
+ cfg["value_preprocessor_kwargs"] = {"size": 1, "device": device}
125
+ # logging to TensorBoard and write checkpoints (in timesteps)
126
+ cfg["experiment"]["write_interval"] = 500
127
+ cfg["experiment"]["checkpoint_interval"] = 5000
128
+ cfg["experiment"]["directory"] = "runs/skrl/torch/"
129
+ cfg["experiment"]["experiment_name"] = (
130
+ os.path.splitext(os.path.basename(__file__))[0] + "_" + datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
131
+ )
132
+
133
+ params = {
134
+ "models": models,
135
+ "memory": memory,
136
+ "cfg": cfg,
137
+ "observation_space": env.observation_space,
138
+ "action_space": env.action_space,
139
+ "device": device,
140
+ }
141
+
142
+ if models["policy"].is_rnn:
143
+ agent = PPO_RNN(**params)
144
+ else:
145
+ agent = PPO(**params)
146
+
147
+ # configure and instantiate the RL trainer
148
+ cfg_trainer = {"timesteps": 100000, "headless": True}
149
+ trainer = SequentialTrainer(cfg=cfg_trainer, env=env, agents=[agent])
150
+
151
+ # start training
152
+ trainer.train()
@@ -27,9 +27,6 @@ def get_model(env):
27
27
  activation=nn.ReLU,
28
28
  )
29
29
 
30
- print(env.action_space)
31
- print(env.observation_space)
32
-
33
30
  policy_model = build_model(
34
31
  RLModelCfg(
35
32
  network=net_cfg,
@@ -0,0 +1,29 @@
1
+ __all__ = [
2
+ # modules:
3
+ "MLP",
4
+ "RNN",
5
+ "GRU",
6
+ "LSTM",
7
+ "RnnBase",
8
+ "RnnMlp",
9
+ "RnnMlpWithForwardedInput",
10
+ # configs:
11
+ "NetworkCfg",
12
+ "MlpCfg",
13
+ "RnnBaseCfg",
14
+ "RnnCfg",
15
+ "GruCfg",
16
+ "LstmCfg",
17
+ "RnnMlpCfg",
18
+ ]
19
+ from rlmodule.source.network import CNN, GRU, LSTM, MLP, RNN, RnnBase, RnnMlp, RnnMlpWithForwardedInput # noqa: F401
20
+ from rlmodule.source.network_cfg import ( # noqa: F401
21
+ CnnCfg,
22
+ GruCfg,
23
+ LstmCfg,
24
+ MlpCfg,
25
+ NetworkCfg,
26
+ RnnBaseCfg,
27
+ RnnCfg,
28
+ RnnMlpCfg,
29
+ )
@@ -10,9 +10,9 @@ def build_model(cfg: BaseRLCfg):
10
10
  actions_as_input = hasattr(network_cfg, "input_actions") and network_cfg.input_actions is not None
11
11
 
12
12
  # process input size
13
- network_cfg.input_states = get_space_size(network_cfg.input_states)
13
+ network_cfg.input_size = get_space_size(network_cfg.input_states)
14
14
  if actions_as_input:
15
- network_cfg.input_states += get_space_size(network_cfg.input_actions)
15
+ network_cfg.input_size += get_space_size(network_cfg.input_actions)
16
16
 
17
17
  # build base network of function approximator
18
18
  net = network_cfg.module(network_cfg)
@@ -21,10 +21,10 @@ def build_model(cfg: BaseRLCfg):
21
21
  net.input_actions = True
22
22
 
23
23
  # get output size to be used as output layer input
24
- network_output_size = get_output_size(net, network_cfg.input_states)
24
+ network_output_size = get_output_size(net, network_cfg.input_size)
25
25
 
26
26
  def build_output_layer(layer_cfg):
27
- return layer_cfg.class_type(device=cfg.device, input_states=network_output_size, cfg=layer_cfg)
27
+ return layer_cfg.class_type(device=cfg.device, input_size=network_output_size, cfg=layer_cfg)
28
28
 
29
29
  # build function approximator
30
30
  if isinstance(cfg, RLModelCfg):
@@ -1,6 +1,8 @@
1
1
  import torch
2
2
  import torch.nn as nn
3
3
 
4
+ from rlmodule.source.utils import get_cnn_shape
5
+
4
6
 
5
7
  class MLP(nn.Module):
6
8
  """Configurable multilayer perceptron module.
@@ -10,7 +12,7 @@ class MLP(nn.Module):
10
12
  Example of use:
11
13
 
12
14
  cfg = MlpCfg(
13
- input_states = 517,
15
+ input_size = 517,
14
16
  hidden_units = [2048, 1024, 1024, 512],
15
17
  activation = nn.ELU,
16
18
  )
@@ -22,7 +24,7 @@ class MLP(nn.Module):
22
24
 
23
25
  # input layer
24
26
  layers = [
25
- nn.Linear(cfg.input_states, cfg.hidden_units[0]),
27
+ nn.Linear(cfg.input_size, cfg.hidden_units[0]),
26
28
  cfg.activation(),
27
29
  ]
28
30
 
@@ -66,7 +68,7 @@ class LSTM(RnnBase):
66
68
  Example of use:
67
69
 
68
70
  cfg = LstmCfg(
69
- input_states = 517,
71
+ input_size = 517,
70
72
  num_envs = 2048,
71
73
  num_layers = 1,
72
74
  hidden_size = 512 + 256,
@@ -79,7 +81,7 @@ class LSTM(RnnBase):
79
81
  super().__init__(cfg)
80
82
 
81
83
  self.lstm = nn.LSTM(
82
- input_size=cfg.input_states,
84
+ input_size=cfg.input_size,
83
85
  hidden_size=cfg.hidden_size,
84
86
  num_layers=cfg.num_layers,
85
87
  batch_first=True,
@@ -235,7 +237,7 @@ class RNN(RnnModule):
235
237
  Example of use:
236
238
 
237
239
  cfg = RnnCfg(
238
- input_states = 517,
240
+ input_size = 517,
239
241
  num_envs = 2048,
240
242
  num_layers = 1,
241
243
  hidden_size = 512 + 256,
@@ -248,7 +250,7 @@ class RNN(RnnModule):
248
250
  super().__init__(cfg)
249
251
 
250
252
  self.rnn = nn.RNN(
251
- input_size=cfg.input_states,
253
+ input_size=cfg.input_size,
252
254
  hidden_size=cfg.hidden_size,
253
255
  num_layers=cfg.num_layers,
254
256
  batch_first=True,
@@ -266,7 +268,7 @@ class GRU(RnnModule):
266
268
  Example of use:
267
269
 
268
270
  cfg = GruCfg(
269
- input_states = 517,
271
+ input_size = 517,
270
272
  num_envs = 2048,
271
273
  num_layers = 1,
272
274
  hidden_size = 512 + 256,
@@ -279,7 +281,7 @@ class GRU(RnnModule):
279
281
  super().__init__(cfg)
280
282
 
281
283
  self.rnn = nn.GRU(
282
- input_size=cfg.input_states,
284
+ input_size=cfg.input_size,
283
285
  hidden_size=cfg.hidden_size,
284
286
  num_layers=cfg.num_layers,
285
287
  batch_first=True,
@@ -298,7 +300,7 @@ class RnnMlp(RnnBase):
298
300
  1) Example of use (with RNN module):
299
301
 
300
302
  cfg = RnnMlpCfg(
301
- input_states = 517,
303
+ input_size = 517,
302
304
  rnn = RnnCfg(
303
305
  num_envs = 2048,
304
306
  num_layers = 1,
@@ -315,7 +317,7 @@ class RnnMlp(RnnBase):
315
317
  2) Example of use (with GRU module):
316
318
 
317
319
  cfg = RnnMlpCfg(
318
- input_states = 517,
320
+ input_size = 517,
319
321
  rnn = GruCfg(
320
322
  num_envs = 2048,
321
323
  num_layers = 1,
@@ -332,7 +334,7 @@ class RnnMlp(RnnBase):
332
334
  3) Example of use (with LSTM module):
333
335
 
334
336
  cfg = RnnMlpCfg(
335
- input_states = 517,
337
+ input_size = 517,
336
338
  rnn = LstmCfg(
337
339
  num_envs = 2048,
338
340
  num_layers = 1,
@@ -349,9 +351,9 @@ class RnnMlp(RnnBase):
349
351
 
350
352
  def __init__(self, cfg):
351
353
  super().__init__(cfg.rnn)
352
- cfg.rnn.input_states = cfg.input_states
354
+ cfg.rnn.input_size = cfg.input_size
353
355
  self.rnn = cfg.rnn.module(cfg.rnn)
354
- cfg.mlp.input_states = self.hidden_size
356
+ cfg.mlp.input_size = self.hidden_size
355
357
  self.mlp = MLP(cfg.mlp)
356
358
 
357
359
  def forward(self, states, terminated, rnn_inputs):
@@ -372,7 +374,7 @@ class RnnMlpWithForwardedInput(RnnBase):
372
374
  1) Example of use (with RNN module):
373
375
 
374
376
  cfg = RnnMlpCfg(
375
- input_states = 517,
377
+ input_size = 517,
376
378
  module = RnnMlpWithForwardedInput,
377
379
  rnn = RnnCfg(
378
380
  num_envs = 2048,
@@ -390,7 +392,7 @@ class RnnMlpWithForwardedInput(RnnBase):
390
392
  2) Example of use (with GRU module):
391
393
 
392
394
  cfg = RnnMlpCfg(
393
- input_states = 517,
395
+ input_size = 517,
394
396
  module = RnnMlpWithForwardedInput,
395
397
  rnn = GruCfg(
396
398
  num_envs = 2048,
@@ -408,7 +410,7 @@ class RnnMlpWithForwardedInput(RnnBase):
408
410
  3) Example of use (with LSTM module):
409
411
 
410
412
  cfg = RnnMlpCfg(
411
- input_states = 517,
413
+ input_size = 517,
412
414
  module = RnnMlpWithForwardedInput,
413
415
  rnn = LstmCfg(
414
416
  num_envs = 2048,
@@ -430,9 +432,9 @@ class RnnMlpWithForwardedInput(RnnBase):
430
432
 
431
433
  def __init__(self, cfg):
432
434
  super().__init__(cfg.rnn)
433
- cfg.rnn.input_states = cfg.input_states
435
+ cfg.rnn.input_size = cfg.input_size
434
436
  self.rnn = cfg.rnn.module(cfg.rnn)
435
- cfg.mlp.input_states = cfg.input_states + self.hidden_size
437
+ cfg.mlp.input_size = cfg.input_size + self.hidden_size
436
438
  self.mlp = MLP(cfg.mlp)
437
439
 
438
440
  def forward(self, states, terminated, rnn_inputs):
@@ -442,93 +444,40 @@ class RnnMlpWithForwardedInput(RnnBase):
442
444
  return self.mlp(mlp_input), output_dict
443
445
 
444
446
 
445
- # CNN - coming soon
447
+ class CNN(nn.Module):
448
+ """Experimental implementation of Convolutional neural network.
449
+ Architecture is defined by providing modules_cfg.MlpCfg.
446
450
 
447
- # def get_cnn_layer(params):
448
- # """
449
- # Create a CNN layer based on the provided parameters and activation function.
450
-
451
- # Args:
452
- # params (dict): Dictionary containing the parameters for the layer.
453
- # Expected keys are:
454
- # - 'type' (str): Type of the layer ('conv' for convolutional, 'pool' for pooling,
455
- # 'dense' for fully connected).
456
- # - 'in_channels' (int): Number of input channels (required for 'conv' type).
457
- # - 'out_channels' (int): Number of output channels (required for 'conv' type).
458
- # - 'kernel_size' (int or tuple): Size of the kernel (required for 'conv' or 'pool' type).
459
- # - 'stride' (int or tuple): Stride of the convolution or pooling operation
460
- # (required for 'conv' or 'pool' type).
461
- # - 'in_features' (int): Number of input features (required for 'dense' type).
462
- # - 'out_features' (int): Number of output features (required for 'dense' type).
463
- # - 'activation' (str): Activation function to use after the layer (only for 'conv' and 'dense' types).
464
-
465
- # Returns:
466
- # list: List containing the created layer(s). For 'conv' type, it includes the convolutional
467
- # layer followed by the activation function. For 'pool' type, it includes only the pooling layer.
468
- # For 'dense' type, it includes the fully connected layer followed by the activation function.
469
-
470
- # Raises:
471
- # ValueError: If the 'type' specified in params is not supported.
472
- # """
473
- # if params['type'] == 'conv':
474
- # return [
475
- # nn.Conv2d(
476
- # in_channels=params['in_channels'],
477
- # out_channels=params['out_channels'],
478
- # kernel_size=params['kernel_size'],
479
- # stride=params['stride'],
480
- # ),
481
- # _get_activation_function(params['activation']),
482
- # ]
483
- # elif params['type'] == 'pool':
484
- # return [
485
- # nn.MaxPool2d(
486
- # kernel_size=params['kernel_size'],
487
- # stride=params['stride'],
488
- # )
489
- # ]
490
- # elif params['type'] == 'dense':
491
- # return [
492
- # nn.Flatten(), # if there is 2D layer before need to be flatten to 1D.
493
- # nn.Linear(
494
- # in_features=params['in_features'],
495
- # out_features=params['out_features'],
496
- # ),
497
- # _get_activation_function(params['activation']),
498
- # ]
499
- # else:
500
- # raise ValueError(f"Unsupported layer type: {params['type']}")
501
-
502
-
503
- # class CNN(nn.Module):
504
- # def __init__(self, params):
505
- # super().__init__()
451
+ Warning: CNN is in an experimental phase. Some combination of inputs may not be yet supported.
506
452
 
507
- # layers = sum([get_cnn_layer(layer_params) for layer_params in params['layers']], [])
508
- # self.cnn = nn.Sequential(*layers, nn.Flatten())
453
+ Example of use:
509
454
 
510
- # def forward(self, input):
511
- # return self.cnn(input)
455
+ cfg = CnnCfg(
456
+ input_size = (13, 13, 1) # input states in order (x,y,channels)
457
+ layers=[
458
+ nn.Conv2d(in_channels=1, out_channels=64, kernel_size=3, stride=2),
459
+ nn.ReLU(),
460
+ nn.Flatten()
461
+ ]
462
+ )
463
+ net = CNN(cfg)
464
+ """
512
465
 
466
+ def __init__(self, cfg):
467
+ super().__init__()
468
+ self._input_size = cfg.input_size
513
469
 
514
- # def example_CNN():
515
- # params = {
516
- # 'input_shape': [1, 13, 13],
517
- # 'layers': [
518
- # {'type': 'conv', 'kernel_size': 3, 'stride': 2, 'in_channels': 1,
519
- # 'out_channels': 32, 'activation': 'relu'},
520
- # {
521
- # 'type': 'conv',
522
- # 'kernel_size': 3,
523
- # 'stride': 1,
524
- # 'in_channels': 32,
525
- # 'out_channels': 64,
526
- # 'activation': 'relu',
527
- # },
528
- # ],
529
- # }
470
+ self._input_shape = get_cnn_shape(cfg.input_states)
530
471
 
531
- # return CNN(params)
472
+ modules = []
473
+ for layer in cfg.layers:
474
+ modules.append(layer)
475
+
476
+ self.cnn = nn.Sequential(*modules)
477
+
478
+ def forward(self, input):
479
+ # Cnn expects inputs in shape (batch, channels, x, y)
480
+ return self.cnn(input.view(-1, *self._input_shape).permute(0, 3, 1, 2))
532
481
 
533
482
 
534
483
  # class TripleCnnAndMlp(nn.Module):
@@ -570,7 +519,7 @@ class RnnMlpWithForwardedInput(RnnBase):
570
519
 
571
520
  # self.cnns = nn.ModuleList([CNN(cnn_params) for _ in range(self.cnn_number)])
572
521
 
573
- # mlp_params['input_states'] = self.prefix_length + self.cnn_number * get_output_size(
522
+ # mlp_params['input_size'] = self.prefix_length + self.cnn_number * get_output_size(
574
523
  # self.cnns[0], self.input_shape
575
524
  # )
576
525
 
@@ -630,13 +579,13 @@ class RnnMlpWithForwardedInput(RnnBase):
630
579
 
631
580
  # self.cnns = nn.ModuleList([CNN(cnn_params) for _ in range(self.cnn_number)])
632
581
 
633
- # rnn_params['input_states'] = self.prefix_length + self.cnn_number * get_output_size(
582
+ # rnn_params['input_size'] = self.prefix_length + self.cnn_number * get_output_size(
634
583
  # self.cnns[0], self.input_shape
635
584
  # )
636
585
 
637
586
  # self.rnn = rnn_class(rnn_params)
638
587
 
639
- # mlp_params['input_states'] = self.rnn.hidden_size + self.rnn.input_states
588
+ # mlp_params['input_size'] = self.rnn.hidden_size + self.rnn.input_size
640
589
 
641
590
  # self.mlp = MLP(mlp_params)
642
591
 
@@ -8,7 +8,7 @@ import gymnasium
8
8
  import torch.nn as nn
9
9
 
10
10
  from rlmodule import logger
11
- from rlmodule.source.network import GRU, LSTM, MLP, RNN, RnnBase, RnnMlp
11
+ from rlmodule.source.network import CNN, GRU, LSTM, MLP, RNN, RnnBase, RnnMlp
12
12
 
13
13
 
14
14
  # use isaac-lab native configclass if available to avoid it being declared twice
@@ -22,6 +22,8 @@ except ImportError:
22
22
  @configclass
23
23
  class NetworkCfg:
24
24
  module: Union[nn.Module, Callable[..., nn.Module]] = MISSING
25
+ input_size: int = None
26
+ """Size of all network inputs. This field should be automatically derived from input_states/input_actions."""
25
27
  input_states: Union[int, Sequence[int], gym.Space, gymnasium.Space] = None
26
28
  """Observations passed to the network as an input."""
27
29
  input_actions: Union[int, Sequence[int], gym.Space, gymnasium.Space] = None
@@ -65,3 +67,16 @@ class RnnMlpCfg(NetworkCfg):
65
67
 
66
68
  rnn: RnnBaseCfg = MISSING
67
69
  mlp: MlpCfg = MISSING
70
+
71
+
72
+ @configclass
73
+ class CnnCfg(NetworkCfg):
74
+ """Config for convolutional neural network.
75
+ For network to work properly the input states need to be specified as a triple in order (x,y,channels)
76
+ Warning: CNN is in an experimental phase. Some combination of inputs may not be yet supported.
77
+ """
78
+
79
+ module: type[CNN] = CNN
80
+
81
+ layers: Sequence[nn.Module] = MISSING
82
+ """Layers of convolutional neural network."""
@@ -11,7 +11,7 @@ from rlmodule.source.utils import get_space_size
11
11
 
12
12
 
13
13
  class OutputLayer(nn.Module):
14
- def __init__(self, device: Union[str, torch.device], input_states: int, cfg):
14
+ def __init__(self, device: Union[str, torch.device], input_size: int, cfg):
15
15
  """Base class for OutputLayer class hierarchy."""
16
16
  super().__init__()
17
17
  self.device = device
@@ -26,26 +26,26 @@ class OutputLayer(nn.Module):
26
26
  self._clip_actions_min = torch.tensor(cfg.output_size.low, device=self.device, dtype=torch.float32)
27
27
  self._clip_actions_max = torch.tensor(cfg.output_size.high, device=self.device, dtype=torch.float32)
28
28
 
29
- self._input_states = input_states
29
+ self._input_size = input_size
30
30
  self._output_size = get_space_size(cfg.output_size)
31
31
 
32
32
  self._output_scale = cfg.output_scale
33
33
 
34
34
 
35
35
  class GaussianLayer(OutputLayer):
36
- def __init__(self, device: Union[str, torch.device], input_states: int, cfg):
36
+ def __init__(self, device: Union[str, torch.device], input_size: int, cfg):
37
37
  """Gaussian output layer
38
38
 
39
39
  :param device: Device on which a tensor/array is or will be allocated
40
40
  :type device: str or torch.device
41
- :param input_states
41
+ :param input_size
42
42
  :type int
43
43
  :param cfg: Configuration of Gaussian output layer
44
44
  :type OutputLayerCfg
45
45
 
46
46
  :raises ValueError: If the reduction method is not valid
47
47
  """
48
- super().__init__(device, input_states, cfg)
48
+ super().__init__(device, input_size, cfg)
49
49
 
50
50
  self._cached_log_std = None
51
51
  self._num_samples = None
@@ -59,9 +59,9 @@ class GaussianLayer(OutputLayer):
59
59
  else torch.sum if cfg.reduction == "sum" else torch.prod if cfg.reduction == "prod" else None
60
60
  )
61
61
 
62
- self._net = nn.Sequential(nn.Linear(self._input_states, self._output_size), cfg.output_activation())
62
+ self._net = nn.Sequential(nn.Linear(self._input_size, self._output_size), cfg.output_activation())
63
63
 
64
- self._log_std = cfg.log_std.class_type(device, input_states, self._output_size, cfg.log_std)
64
+ self._log_std = cfg.log_std.class_type(device, input_size, self._output_size, cfg.log_std)
65
65
 
66
66
  def forward(self, input, taken_actions, outputs_dict):
67
67
  """Act stochastically in response to the state of the environment
@@ -185,20 +185,20 @@ class GaussianLayer(OutputLayer):
185
185
 
186
186
 
187
187
  class DeterministicLayer(OutputLayer):
188
- def __init__(self, device: Union[str, torch.device], input_states: int, cfg):
188
+ def __init__(self, device: Union[str, torch.device], input_size: int, cfg):
189
189
  """Deterministic output layer
190
190
 
191
191
  :param device: Device on which a tensor/array is or will be allocated
192
192
  :type device: str or torch.device
193
- :param input_states
193
+ :param input_size
194
194
  :type int
195
195
  :param cfg: Configuration of Gaussian output layer
196
196
  :type OutputLayerCfg
197
197
  """
198
198
 
199
- super().__init__(device, input_states, cfg)
199
+ super().__init__(device, input_size, cfg)
200
200
 
201
- self._net = nn.Sequential(nn.Linear(input_states, cfg.output_size), cfg.output_activation())
201
+ self._net = nn.Sequential(nn.Linear(input_size, cfg.output_size), cfg.output_activation())
202
202
 
203
203
  def forward(self, input, taken_actions, outputs_dict):
204
204
  """Act deterministically in response to the state of the environment
@@ -28,6 +28,35 @@ def get_output_size(module, input_shape):
28
28
  return module(dummy_input).view(-1).shape[0]
29
29
 
30
30
 
31
+ def get_cnn_shape(space: Union[int, Sequence[int], gym.Space, gymnasium.Space]):
32
+ """Experimental function to get shape of cnn input.
33
+
34
+ :param space: Space or shape from which to obtain the number of elements
35
+ :type space: int, sequence of int, gym.Space, or gymnasium.Space
36
+
37
+ :raises ValueError: If the space is not supported
38
+ :rtype: tuple
39
+
40
+ Example::
41
+ # from sequence of int
42
+ >>> model._get_space_size([2, 3])
43
+ (2, 3)
44
+
45
+ # Box space
46
+ >>> space = gym.spaces.Box(low=-1, high=1, shape=(2, 3))
47
+ >>> model._get_space_size(space)
48
+ (2, 3)
49
+
50
+ # Other types are currently not supported as a cnn input
51
+ """
52
+ if type(space) in [tuple, list]:
53
+ return tuple(space)
54
+ elif issubclass(type(space), gym.spaces.Box):
55
+ return space.shape
56
+ else:
57
+ raise ValueError(f"Space type {type(space)} is not supported as cnn input.")
58
+
59
+
31
60
  def get_space_size(
32
61
  space: Union[int, Sequence[int], gym.Space, gymnasium.Space], number_of_elements: bool = True
33
62
  ) -> int:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rlmodule
3
- Version: 0.1.6.2
3
+ Version: 0.1.6.3
4
4
  Summary: Flexible reinforcement learning models instantiators library
5
5
  Author: Lopatovsky
6
6
  Author-email: lopatovsky@gmail.com
@@ -79,7 +79,7 @@ rm -rf runs
79
79
 
80
80
  ### Run chosen example
81
81
  ```
82
- python3 rlmodule/skrl/torch/gymnasium/pendulum_ppo_mlp_separate_model.py
82
+ python3 rlmodule/skrl/torch/examples/gymnasium/pendulum_ppo_mlp_separate_model.py
83
83
  ```
84
84
 
85
85
  Optional: observe run results in Tensorboard
@@ -9,16 +9,17 @@ rlmodule.egg-info/requires.txt
9
9
  rlmodule.egg-info/top_level.txt
10
10
  rlmodule/skrl/__init__.py
11
11
  rlmodule/skrl/torch/__init__.py
12
- rlmodule/skrl/torch/gymnasium/pendulum_ppo_mlp_separate_model.py
13
- rlmodule/skrl/torch/gymnasium/pendulum_ppo_mlp_shared_model.py
14
- rlmodule/skrl/torch/gymnasium/pendulum_ppo_rnn_forwarded_input_shared_model.py
15
- rlmodule/skrl/torch/gymnasium/pendulum_ppo_rnn_separate_model.py
16
- rlmodule/skrl/torch/gymnasium/pendulum_ppo_rnn_shared_model.py
17
- rlmodule/skrl/torch/gymnasium/pendulum_sac_mlp_separate_model.py
18
- rlmodule/skrl/torch/gymnasium/skrl_no_module.py
19
- rlmodule/skrl/torch/gymnasium/skrl_no_module_rnn.py
20
- rlmodule/skrl/torch/gymnasium/skrl_sac.py
21
- rlmodule/skrl/torch/gymnasium/temp.py
12
+ rlmodule/skrl/torch/examples/gymnasium/cnn_experimental.py
13
+ rlmodule/skrl/torch/examples/gymnasium/pendulum_ppo_mlp_separate_model.py
14
+ rlmodule/skrl/torch/examples/gymnasium/pendulum_ppo_mlp_shared_model.py
15
+ rlmodule/skrl/torch/examples/gymnasium/pendulum_ppo_rnn_forwarded_input_shared_model.py
16
+ rlmodule/skrl/torch/examples/gymnasium/pendulum_ppo_rnn_separate_model.py
17
+ rlmodule/skrl/torch/examples/gymnasium/pendulum_ppo_rnn_shared_model.py
18
+ rlmodule/skrl/torch/examples/gymnasium/pendulum_sac_mlp_separate_model.py
19
+ rlmodule/skrl/torch/examples/gymnasium/skrl_no_module.py
20
+ rlmodule/skrl/torch/examples/gymnasium/skrl_no_module_rnn.py
21
+ rlmodule/skrl/torch/examples/gymnasium/skrl_sac.py
22
+ rlmodule/skrl/torch/examples/gymnasium/temp.py
22
23
  rlmodule/skrl/torch/network/__init__.py
23
24
  rlmodule/skrl/torch/output_layer/__init__.py
24
25
  rlmodule/source/__init__.py
@@ -1,20 +0,0 @@
1
- __all__ = [
2
- # modules:
3
- "MLP",
4
- "RNN",
5
- "GRU",
6
- "LSTM",
7
- "RnnBase",
8
- "RnnMlp",
9
- "RnnMlpWithForwardedInput",
10
- # configs:
11
- "NetworkCfg",
12
- "MlpCfg",
13
- "RnnBaseCfg",
14
- "RnnCfg",
15
- "GruCfg",
16
- "LstmCfg",
17
- "RnnMlpCfg",
18
- ]
19
- from rlmodule.source.network import GRU, LSTM, MLP, RNN, RnnBase, RnnMlp, RnnMlpWithForwardedInput # noqa: F401
20
- from rlmodule.source.network_cfg import GruCfg, LstmCfg, MlpCfg, NetworkCfg, RnnBaseCfg, RnnCfg, RnnMlpCfg # noqa: F401
File without changes
File without changes