rlmodule 0.0.5__py3-none-any.whl

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.
@@ -0,0 +1,1003 @@
1
+ import torch
2
+ import torch.nn as nn
3
+
4
+ # todo consider get rid of this and just pass torch.nn stuff
5
+ def _get_activation_function(activation: str) -> nn.Module:
6
+ """Get the activation function
7
+
8
+ Supported activation functions:
9
+
10
+ - "elu"
11
+ - "leaky_relu"
12
+ - "relu"
13
+ - "selu"
14
+ - "sigmoid"
15
+ - "softmax"
16
+ - "softplus"
17
+ - "softsign"
18
+ - "tanh"
19
+
20
+ :param activation: activation function name.
21
+ If activation is an empty string, a placeholder will be returned (``torch.nn.Identity()``)
22
+ :type activation: str
23
+
24
+ :raises: ValueError if activation is not a valid activation function
25
+
26
+ :return: activation function
27
+ :rtype: nn.Module
28
+ """
29
+ if not activation:
30
+ return torch.nn.Identity()
31
+ elif activation == "relu":
32
+ return torch.nn.ReLU()
33
+ elif activation == "tanh":
34
+ return torch.nn.Tanh()
35
+ elif activation == "sigmoid":
36
+ return torch.nn.Sigmoid()
37
+ elif activation == "leaky_relu":
38
+ return torch.nn.LeakyReLU()
39
+ elif activation == "elu":
40
+ return torch.nn.ELU()
41
+ elif activation == "softplus":
42
+ return torch.nn.Softplus()
43
+ elif activation == "softsign":
44
+ return torch.nn.Softsign()
45
+ elif activation == "selu":
46
+ return torch.nn.SELU()
47
+ elif activation == "softmax":
48
+ return torch.nn.Softmax()
49
+ else:
50
+ raise ValueError(f"Unknown activation function: {activation}")
51
+
52
+
53
+
54
+ # TODO(ll) double function, already defined in model_instantiators.py
55
+ def contains_rnn_module(module: nn.Module, module_types):
56
+ for submodule in module.modules():
57
+ if isinstance(submodule, module_types):
58
+ return True
59
+ return False
60
+
61
+
62
+ # TODO check for input sizes, also check if you can't just pass inputs. instead of separate thingies separated in RLModel
63
+ def get_output_size(module, input_shape):
64
+ module.train(False)
65
+ if isinstance(input_shape, int):
66
+ input_shape = (input_shape,)
67
+ dummy_input = torch.zeros(1, *input_shape)
68
+ if contains_rnn_module(module, (nn.LSTM, nn.RNN, nn.GRU)):
69
+ # TODO(ll) module.num_layer * bidirectional?
70
+ dummy_hidden = torch.zeros(module.num_layers, 1, module.hidden_size)
71
+ return module(dummy_input, None, (dummy_hidden, dummy_hidden))[0].view(-1).shape[0]
72
+ else:
73
+ return module(dummy_input).view(-1).shape[0]
74
+
75
+
76
+ class MLP(nn.Module):
77
+ def __init__(self, params):
78
+ super().__init__()
79
+
80
+ input_size = params['input_size']
81
+ hidden_units = params['hidden_units']
82
+ activations = params['activations']
83
+
84
+ # input layer
85
+ layers = [
86
+ nn.Linear(input_size, hidden_units[0]),
87
+ _get_activation_function(activations[0]),
88
+ ]
89
+
90
+ # hidden layers
91
+ for i in range(len(hidden_units) - 1):
92
+ layers.append(nn.Linear(hidden_units[i], hidden_units[i + 1]))
93
+ layers.append(_get_activation_function(activations[i + 1]))
94
+
95
+ self.mlp = nn.Sequential(*layers)
96
+
97
+ def forward(self, input):
98
+ return self.mlp(input)
99
+
100
+
101
+ def example_MLP():
102
+ params = {'input_size': 517, 'hidden_units': [2048, 1024, 1024, 512], 'activations': ['elu', 'elu', 'elu', 'elu']}
103
+ return MLP(params)
104
+
105
+
106
+ def get_cnn_layer(params):
107
+ """
108
+ Create a CNN layer based on the provided parameters and activation function.
109
+
110
+ Args:
111
+ params (dict): Dictionary containing the parameters for the layer.
112
+ Expected keys are:
113
+ - 'type' (str): Type of the layer ('conv' for convolutional, 'pool' for pooling,
114
+ 'dense' for fully connected).
115
+ - 'in_channels' (int): Number of input channels (required for 'conv' type).
116
+ - 'out_channels' (int): Number of output channels (required for 'conv' type).
117
+ - 'kernel_size' (int or tuple): Size of the kernel (required for 'conv' or 'pool' type).
118
+ - 'stride' (int or tuple): Stride of the convolution or pooling operation
119
+ (required for 'conv' or 'pool' type).
120
+ - 'in_features' (int): Number of input features (required for 'dense' type).
121
+ - 'out_features' (int): Number of output features (required for 'dense' type).
122
+ - 'activation' (str): Activation function to use after the layer (only for 'conv' and 'dense' types).
123
+
124
+ Returns:
125
+ list: List containing the created layer(s). For 'conv' type, it includes the convolutional
126
+ layer followed by the activation function. For 'pool' type, it includes only the pooling layer.
127
+ For 'dense' type, it includes the fully connected layer followed by the activation function.
128
+
129
+ Raises:
130
+ ValueError: If the 'type' specified in params is not supported.
131
+ """
132
+ if params['type'] == 'conv':
133
+ return [
134
+ nn.Conv2d(
135
+ in_channels=params['in_channels'],
136
+ out_channels=params['out_channels'],
137
+ kernel_size=params['kernel_size'],
138
+ stride=params['stride'],
139
+ ),
140
+ _get_activation_function(params['activation']),
141
+ ]
142
+ elif params['type'] == 'pool':
143
+ return [
144
+ nn.MaxPool2d(
145
+ kernel_size=params['kernel_size'],
146
+ stride=params['stride'],
147
+ )
148
+ ]
149
+ elif params['type'] == 'dense':
150
+ return [
151
+ nn.Flatten(), # if there is 2D layer before need to be flatten to 1D.
152
+ nn.Linear(
153
+ in_features=params['in_features'],
154
+ out_features=params['out_features'],
155
+ ),
156
+ _get_activation_function(params['activation']),
157
+ ]
158
+ else:
159
+ raise ValueError(f"Unsupported layer type: {params['type']}")
160
+
161
+
162
+ class CNN(nn.Module):
163
+ def __init__(self, params):
164
+ super().__init__()
165
+
166
+ layers = sum([get_cnn_layer(layer_params) for layer_params in params['layers']], [])
167
+ self.cnn = nn.Sequential(*layers, nn.Flatten())
168
+
169
+ def forward(self, input):
170
+ return self.cnn(input)
171
+
172
+
173
+ def example_CNN():
174
+ params = {
175
+ 'input_shape': [1, 13, 13],
176
+ 'layers': [
177
+ {'type': 'conv', 'kernel_size': 3, 'stride': 2, 'in_channels': 1, 'out_channels': 32, 'activation': 'relu'},
178
+ {
179
+ 'type': 'conv',
180
+ 'kernel_size': 3,
181
+ 'stride': 1,
182
+ 'in_channels': 32,
183
+ 'out_channels': 64,
184
+ 'activation': 'relu',
185
+ },
186
+ ],
187
+ }
188
+
189
+ return CNN(params)
190
+
191
+
192
+ class TripleCnnAndMlp(nn.Module):
193
+ """
194
+ Split the observation space into parts and pass some parts through different CNNs.
195
+
196
+ ----------------------------------------------------------------------
197
+ | Network architecture (cnn shape 1x13x13) |
198
+ |--------------------------------------------------------------------|
199
+ | |
200
+ | 10 1x13x13 1x13x13 1x13x13 |
201
+ | | | | | |
202
+ | | CNN0 CNN1 CNN2 |
203
+ | | | | | |
204
+ | | Flatten Flatten Flatten |
205
+ | | | | | |
206
+ | |____|_________|_________| |
207
+ | | |
208
+ | Join to shape 10 + OutUnits0 + OutUnits1 + OutUnits2 |
209
+ | | |
210
+ | MLP: UNITS[0] |
211
+ | | |
212
+ | ... |
213
+ | | |
214
+ | MLP: UNITS[-1] |
215
+ | | |
216
+ | Output |
217
+ | |
218
+ ----------------------------------------------------------------------
219
+
220
+ """
221
+
222
+ def __init__(self, cnn_params, mlp_params):
223
+ super().__init__()
224
+ self.input_shape = cnn_params['input_shape']
225
+
226
+ self.prefix_length = 10
227
+ self.cnn_number = 3
228
+
229
+ self.cnns = nn.ModuleList([CNN(cnn_params) for _ in range(self.cnn_number)])
230
+
231
+ mlp_params['input_size'] = self.prefix_length + self.cnn_number * get_output_size(
232
+ self.cnns[0], self.input_shape
233
+ )
234
+
235
+ self.mlp = MLP(mlp_params)
236
+
237
+ def forward(self, input):
238
+ # Split the input
239
+ prefix = input[:, : self.prefix_length]
240
+
241
+ c, h, w = self.input_shape
242
+ cnn_input = input[:, self.prefix_length :].view(-1, self.cnn_number * c, h, w)
243
+
244
+ # Forward pass through CNNs
245
+ cnn_outputs = []
246
+ for i in range(self.cnn_number):
247
+ cnn_output = self.cnns[i](cnn_input[:, i : i + 1, :, :]).view(input.size(0), -1)
248
+ cnn_outputs.append(cnn_output)
249
+
250
+ # Concatenate the outputs
251
+ combined = torch.cat([prefix] + cnn_outputs, dim=1)
252
+
253
+ # Forward pass through MLP
254
+ output = self.mlp(combined)
255
+ return output
256
+
257
+
258
+ def triple_cnn_and_mlp_example():
259
+ cnn_params = {
260
+ 'input_shape': [1, 13, 13],
261
+ 'layers': [
262
+ {'type': 'conv', 'kernel_size': 3, 'stride': 2, 'in_channels': 1, 'out_channels': 32, 'activation': 'relu'},
263
+ {
264
+ 'type': 'conv',
265
+ 'kernel_size': 3,
266
+ 'stride': 1,
267
+ 'in_channels': 32,
268
+ 'out_channels': 64,
269
+ 'activation': 'relu',
270
+ },
271
+ ],
272
+ }
273
+
274
+ mlp_params = {'hidden_units': [1024, 1024, 512], 'activations': ['elu', 'elu', 'elu']}
275
+
276
+ return TripleCnnAndMlp(cnn_params, mlp_params)
277
+
278
+
279
+ class RnnBase(nn.Module):
280
+ def __init__(self, params):
281
+ super().__init__()
282
+
283
+ # parameters necessery for RNN, GRU and LSTM networks
284
+ self.num_envs = params['num_envs']
285
+ self.num_layers = params['num_layers']
286
+ self.hidden_size = params['hidden_size']
287
+ self.sequence_length = params['sequence_length']
288
+ if 'input_size' in params:
289
+ self.input_size = params['input_size']
290
+
291
+
292
+ class LSTM(RnnBase):
293
+
294
+ def __init__(self, params):
295
+ super().__init__(params)
296
+
297
+ self.lstm = nn.LSTM(
298
+ input_size=self.input_size,
299
+ hidden_size=self.hidden_size,
300
+ num_layers=self.num_layers,
301
+ batch_first=True,
302
+ )
303
+
304
+ def forward(self, states, terminated, rnn_inputs):
305
+
306
+ # Process rnn inputs
307
+ hidden_states, cell_states = rnn_inputs[0], rnn_inputs[1]
308
+
309
+ # Dimensions Explained
310
+ # ---------------------
311
+ # 'N' - Batch size
312
+ # 'L' - Sequence length
313
+ # 'Hin' - Input size (number of features per input at each time step)
314
+ # 'Hout' - Hidden size (number of features in the hidden state of the LSTM)
315
+ # 'HCell' - Cell state size (same as hidden size in LSTM context)
316
+ # 'D' - Number of directions (1 for unidirectional, 2 for bidirectional)
317
+ # 'num_layers' - Number of stacked LSTM layers
318
+
319
+ # training
320
+ if self.training:
321
+ # reshape (N * L, Hin) -> (N, L, Hin)
322
+ rnn_input = states.view(-1, self.sequence_length, states.shape[-1])
323
+
324
+ # reshape (D * num_layers, N * L, Hout) -> (D * num_layers, N, L, Hout)
325
+ hidden_states = hidden_states.view(self.num_layers, -1, self.sequence_length, hidden_states.shape[-1])
326
+
327
+ # reshape (D * num_layers, N * L, Hcell) -> (D * num_layers, N, L, Hcell)
328
+ cell_states = cell_states.view(self.num_layers, -1, self.sequence_length, cell_states.shape[-1])
329
+
330
+ # get the hidden/cell states corresponding to the first time step of the sequence for each batch.
331
+ hidden_states = hidden_states[:, :, 0, :].contiguous() # (D * num_layers, N, Hout)
332
+ cell_states = cell_states[:, :, 0, :].contiguous() # (D * num_layers, N, Hcell)
333
+
334
+ # reset the RNN state in the middle of a sequence
335
+ if terminated is not None and torch.any(terminated):
336
+ rnn_outputs = []
337
+ terminated = terminated.view(-1, self.sequence_length)
338
+ indexes = (
339
+ [0]
340
+ + (terminated[:, :-1].any(dim=0).nonzero(as_tuple=True)[0] + 1).tolist()
341
+ + [self.sequence_length]
342
+ )
343
+
344
+ for i in range(len(indexes) - 1):
345
+ i0, i1 = indexes[i], indexes[i + 1]
346
+ # Compute the RNN output for the segment of the sequence
347
+ rnn_output, (hidden_states, cell_states) = self.lstm(
348
+ rnn_input[:, i0:i1, :], (hidden_states, cell_states)
349
+ )
350
+ # Reset hidden and cell states where sequences are terminated
351
+ hidden_states[:, (terminated[:, i1 - 1]), :] = 0
352
+ cell_states[:, (terminated[:, i1 - 1]), :] = 0
353
+ rnn_outputs.append(rnn_output)
354
+
355
+ rnn_states = (hidden_states, cell_states)
356
+ rnn_output = torch.cat(rnn_outputs, dim=1)
357
+ # if no termination reset is needed, simply compute the RNN output
358
+ else:
359
+ rnn_output, rnn_states = self.lstm(rnn_input, (hidden_states, cell_states))
360
+ # rollout
361
+ else:
362
+ # reshape (N, Hin) -> (N, 1, Hin)
363
+ # This is done to add a sequence length dimension of 1 for processing one time step at a time
364
+ rnn_input = states.view(-1, 1, states.shape[-1])
365
+ rnn_output, rnn_states = self.lstm(rnn_input, (hidden_states, cell_states))
366
+
367
+ # reshape (N, L, D * Hout) -> (N * L, D * Hout)
368
+ rnn_output = torch.flatten(rnn_output, start_dim=0, end_dim=1)
369
+
370
+ return rnn_output, {'rnn': [rnn_states[0], rnn_states[1]]}
371
+
372
+
373
+ class RnnModule(RnnBase):
374
+
375
+ def __init__(self, params):
376
+ super().__init__(params)
377
+
378
+ def forward(self, states, terminated, rnn_inputs):
379
+
380
+ # Process rnn inputs
381
+ hidden_states = rnn_inputs[0]
382
+
383
+ # Dimensions Explained
384
+ # ---------------------
385
+ # 'N' - Batch size
386
+ # 'L' - Sequence length
387
+ # 'Hin' - Input size (number of features per input at each time step)
388
+ # 'Hout' - Hidden size (number of features in the hidden state of the RNN)
389
+ # 'D' - Number of directions (1 for unidirectional, 2 for bidirectional)
390
+ # 'num_layers' - Number of stacked RNN layers
391
+
392
+ # training
393
+ if self.training:
394
+ # reshape (N * L, Hin) -> (N, L, Hin)
395
+ rnn_input = states.view(-1, self.sequence_length, states.shape[-1])
396
+
397
+ # reshape (D * num_layers, N * L, Hout) -> (D * num_layers, N, L, Hout)
398
+ hidden_states = hidden_states.view(self.num_layers, -1, self.sequence_length, hidden_states.shape[-1])
399
+
400
+ # get the hidden states corresponding to the first time step of the sequence for each batch.
401
+ hidden_states = hidden_states[:, :, 0, :].contiguous() # (D * num_layers, N, Hout)
402
+
403
+ # reset the RNN state in the middle of a sequence
404
+ if terminated is not None and torch.any(terminated):
405
+ rnn_outputs = []
406
+ terminated = terminated.view(-1, self.sequence_length)
407
+ indexes = (
408
+ [0]
409
+ + (terminated[:, :-1].any(dim=0).nonzero(as_tuple=True)[0] + 1).tolist()
410
+ + [self.sequence_length]
411
+ )
412
+
413
+ for i in range(len(indexes) - 1):
414
+ i0, i1 = indexes[i], indexes[i + 1]
415
+ # Compute the RNN output for the segment of the sequence
416
+ rnn_output, hidden_states = self.rnn(rnn_input[:, i0:i1, :], hidden_states)
417
+ # Reset hidden states where sequences are terminated
418
+ hidden_states[:, (terminated[:, i1 - 1]), :] = 0
419
+ rnn_outputs.append(rnn_output)
420
+
421
+ rnn_output = torch.cat(rnn_outputs, dim=1)
422
+ # if no termination reset is needed, simply compute the RNN output
423
+ else:
424
+ rnn_output, hidden_states = self.rnn(rnn_input, hidden_states)
425
+ # rollout
426
+ else:
427
+ # reshape (N, Hin) -> (N, 1, Hin)
428
+ # This is done to add a sequence length dimension of 1 for processing one time step at a time
429
+ rnn_input = states.view(-1, 1, states.shape[-1])
430
+ rnn_output, hidden_states = self.rnn(rnn_input, hidden_states)
431
+
432
+ # reshape (N, L, D * Hout) -> (N * L, D * Hout)
433
+ rnn_output = torch.flatten(rnn_output, start_dim=0, end_dim=1)
434
+
435
+ return rnn_output, {'rnn': [hidden_states]}
436
+
437
+
438
+ class RNN(RnnModule):
439
+ def __init__(self, params):
440
+ super().__init__(params)
441
+
442
+ self.rnn = nn.RNN(
443
+ input_size=self.input_size,
444
+ hidden_size=self.hidden_size,
445
+ num_layers=self.num_layers,
446
+ batch_first=True,
447
+ )
448
+
449
+
450
+ class GRU(RnnModule):
451
+ def __init__(self, params):
452
+ super().__init__(params)
453
+
454
+ self.rnn = nn.GRU(
455
+ input_size=self.input_size,
456
+ hidden_size=self.hidden_size,
457
+ num_layers=self.num_layers,
458
+ batch_first=True,
459
+ )
460
+
461
+
462
+ def example_RNN():
463
+ params = {
464
+ 'num_envs': 2048,
465
+ 'input_size': 517,
466
+ 'num_layers': 1,
467
+ 'hidden_size': 512 + 256,
468
+ 'sequence_length': 128,
469
+ }
470
+ return RNN(params)
471
+
472
+
473
+ def example_GRU():
474
+ params = {
475
+ 'num_envs': 2048,
476
+ 'input_size': 517,
477
+ 'num_layers': 1,
478
+ 'hidden_size': 512 + 256,
479
+ 'sequence_length': 128,
480
+ }
481
+ return GRU(params)
482
+
483
+
484
+ def example_LSTM():
485
+ params = {
486
+ 'num_envs': 2048,
487
+ 'input_size': 517,
488
+ 'num_layers': 1,
489
+ 'hidden_size': 512 + 256,
490
+ 'sequence_length': 32,
491
+ }
492
+ return LSTM(params)
493
+
494
+
495
+ class RnnMlp(RnnBase):
496
+
497
+ def __init__(self, rnn_class, rnn_params, mlp_params):
498
+ super().__init__(rnn_params)
499
+
500
+ self.rnn = rnn_class(rnn_params)
501
+
502
+ mlp_params['input_size'] = self.hidden_size
503
+
504
+ self.mlp = MLP(mlp_params)
505
+
506
+ def forward(self, states, terminated, rnn_inputs):
507
+ rnn_output, output_dict = self.rnn(states, terminated, rnn_inputs)
508
+ return self.mlp(rnn_output), output_dict
509
+
510
+
511
+ def example_LstmMlp():
512
+ rnn_params = {
513
+ 'num_envs': 2048,
514
+ 'input_size': 517,
515
+ 'num_layers': 1,
516
+ 'hidden_size': 512 + 256,
517
+ 'sequence_length': 128,
518
+ }
519
+ mlp_params = {
520
+ 'hidden_units': [2048, 1024, 1024, 512],
521
+ 'activations': ['elu', 'elu', 'elu', 'elu'],
522
+ }
523
+
524
+ return RnnMlp(LSTM, rnn_params, mlp_params)
525
+
526
+
527
+ def example_GruMlp():
528
+ rnn_params = {
529
+ 'num_envs': 2048,
530
+ 'input_size': 517,
531
+ 'num_layers': 1,
532
+ 'hidden_size': 512 + 256,
533
+ 'sequence_length': 128,
534
+ }
535
+ mlp_params = {
536
+ 'hidden_units': [2048, 1024, 1024, 512],
537
+ 'activations': ['elu', 'elu', 'elu', 'elu'],
538
+ }
539
+
540
+ return RnnMlp(GRU, rnn_params, mlp_params)
541
+
542
+
543
+ def example_RnnMlp():
544
+ rnn_params = {
545
+ 'num_envs': 2048,
546
+ 'input_size': 517,
547
+ 'num_layers': 1,
548
+ 'hidden_size': 512 + 256,
549
+ 'sequence_length': 128,
550
+ }
551
+ mlp_params = {
552
+ 'hidden_units': [2048, 1024, 1024, 512],
553
+ 'activations': ['elu', 'elu', 'elu', 'elu'],
554
+ }
555
+
556
+ return RnnMlp(RNN, rnn_params, mlp_params)
557
+
558
+
559
+ class RnnMlpWithForwardedInput(RnnBase):
560
+
561
+ def __init__(self, rnn_class, rnn_params, mlp_params):
562
+ super().__init__(rnn_params)
563
+
564
+ self.rnn = rnn_class(rnn_params)
565
+
566
+ mlp_params['input_size'] = self.input_size + self.hidden_size
567
+
568
+ self.mlp = MLP(mlp_params)
569
+
570
+ def forward(self, states, terminated, rnn_inputs):
571
+ rnn_output, output_dict = self.rnn(states, terminated, rnn_inputs)
572
+ mlp_input = torch.cat((states, rnn_output), dim=1)
573
+
574
+ return self.mlp(mlp_input), output_dict
575
+
576
+
577
+ def example_LstmMlpWithForwardedInput():
578
+ rnn_params = {
579
+ 'num_envs': 2048,
580
+ 'input_size': 517,
581
+ 'num_layers': 1,
582
+ 'hidden_size': 512 + 256,
583
+ 'sequence_length': 128,
584
+ }
585
+ mlp_params = {
586
+ 'hidden_units': [2048, 1024, 1024, 512],
587
+ 'activations': ['elu', 'elu', 'elu', 'elu'],
588
+ }
589
+
590
+ return RnnMlpWithForwardedInput(LSTM, rnn_params, mlp_params)
591
+
592
+
593
+ def example_GruMlpWithForwardedInput():
594
+ rnn_params = {
595
+ 'num_envs': 2048,
596
+ 'input_size': 517,
597
+ 'num_layers': 1,
598
+ 'hidden_size': 512 + 256,
599
+ 'sequence_length': 128,
600
+ }
601
+ mlp_params = {
602
+ 'hidden_units': [2048, 1024, 1024, 512],
603
+ 'activations': ['elu', 'elu', 'elu', 'elu'],
604
+ }
605
+
606
+ return RnnMlpWithForwardedInput(GRU, rnn_params, mlp_params)
607
+
608
+
609
+ def example_RnnMlpWithForwardedInput():
610
+ rnn_params = {
611
+ 'num_envs': 2048,
612
+ 'input_size': 517,
613
+ 'num_layers': 1,
614
+ 'hidden_size': 512 + 256,
615
+ 'sequence_length': 128,
616
+ }
617
+ mlp_params = {
618
+ 'hidden_units': [2048, 1024, 1024, 512],
619
+ 'activations': ['elu', 'elu', 'elu', 'elu'],
620
+ }
621
+
622
+ return RnnMlpWithForwardedInput(RNN, rnn_params, mlp_params)
623
+
624
+
625
+ class CrazyNet(RnnBase):
626
+ def __init__(self, rnn_class, rnn_params, cnn_params, mlp_params):
627
+ super().__init__(rnn_params)
628
+
629
+ self.input_shape = cnn_params['input_shape']
630
+
631
+ self.prefix_length = 10
632
+ self.cnn_number = 3
633
+
634
+ self.cnns = nn.ModuleList([CNN(cnn_params) for _ in range(self.cnn_number)])
635
+
636
+ rnn_params['input_size'] = self.prefix_length + self.cnn_number * get_output_size(
637
+ self.cnns[0], self.input_shape
638
+ )
639
+
640
+ self.rnn = rnn_class(rnn_params)
641
+
642
+ mlp_params['input_size'] = self.rnn.hidden_size + self.rnn.input_size
643
+
644
+ self.mlp = MLP(mlp_params)
645
+
646
+ def forward(self, states, terminated, rnn_inputs):
647
+ # Split the input
648
+ prefix = states[:, : self.prefix_length]
649
+
650
+ c, h, w = self.input_shape
651
+ cnn_input = states[:, self.prefix_length :].view(-1, self.cnn_number * c, h, w)
652
+
653
+ # Forward pass through CNNs
654
+ cnn_outputs = []
655
+ for i in range(self.cnn_number):
656
+ cnn_output = self.cnns[i](cnn_input[:, i : i + 1, :, :]).view(states.size(0), -1)
657
+ cnn_outputs.append(cnn_output)
658
+
659
+ # Concatenate the outputs of prefix and CNNs
660
+ cnn_combined = torch.cat([prefix] + cnn_outputs, dim=1)
661
+
662
+ # Forward pass through RNN
663
+ rnn_output, output_dict = self.rnn(cnn_combined, terminated, rnn_inputs)
664
+
665
+ # Concatenate the outputs of RNN, CNNs and prefix
666
+ combined = torch.cat([cnn_combined, rnn_output], dim=1)
667
+
668
+ # Forward pass through MLP
669
+ output = self.mlp(combined)
670
+
671
+ return output, output_dict
672
+
673
+
674
+ def crazy_net_example():
675
+ rnn_params = {
676
+ 'num_envs': 2048,
677
+ 'num_layers': 1,
678
+ 'hidden_size': 512 + 256,
679
+ 'sequence_length': 128,
680
+ }
681
+
682
+ cnn_params = {
683
+ 'input_shape': [1, 13, 13],
684
+ 'layers': [
685
+ {'type': 'conv', 'kernel_size': 3, 'stride': 2, 'in_channels': 1, 'out_channels': 32, 'activation': 'relu'},
686
+ {
687
+ 'type': 'conv',
688
+ 'kernel_size': 3,
689
+ 'stride': 1,
690
+ 'in_channels': 32,
691
+ 'out_channels': 64,
692
+ 'activation': 'relu',
693
+ },
694
+ ],
695
+ }
696
+
697
+ mlp_params = {'hidden_units': [1024, 1024, 512], 'activations': ['elu', 'elu', 'elu']}
698
+
699
+ return CrazyNet(LSTM, rnn_params, cnn_params, mlp_params)
700
+
701
+
702
+ # def lstm_module(model_params, observation_space, action_space, device, double_pass):
703
+
704
+ # class LSTM(nn.Module):
705
+
706
+ # def __init__(self):
707
+ # super().__init__()
708
+
709
+ # # lstm
710
+ # self.num_envs = 2048
711
+ # self.num_layers = 1
712
+ # self.hidden_size = 256 + 512
713
+ # self.sequence_length = 128
714
+
715
+ # self.lstm = nn.LSTM(
716
+ # input_size=observation_space.shape[0],
717
+ # hidden_size=self.hidden_size,
718
+ # num_layers=self.num_layers,
719
+ # batch_first=True,
720
+ # )
721
+
722
+ # # mlp
723
+ # input_size = self.hidden_size
724
+
725
+ # mlp_params = model_params['mlp']
726
+ # self.net = DEPRECATEDMLP(input_size, mlp_params['units'], device, mlp_params['activation'])
727
+
728
+ # # TODO(ll) can inherit from LSTM ..without mlp
729
+ # def forward(self, inputs):
730
+
731
+ # # Process rnn inputs
732
+ # hidden_states, cell_states = rnn_inputs[0], rnn_inputs[1]
733
+
734
+ # # Dimensions Explained
735
+ # # ---------------------
736
+ # # 'N' - Batch size
737
+ # # 'L' - Sequence length
738
+ # # 'Hin' - Input size (number of features per input at each time step)
739
+ # # 'Hout' - Hidden size (number of features in the hidden state of the LSTM)
740
+ # # 'HCell' - Cell state size (same as hidden size in LSTM context)
741
+ # # 'D' - Number of directions (1 for unidirectional, 2 for bidirectional)
742
+ # # 'num_layers' - Number of stacked LSTM layers
743
+
744
+ # # training
745
+ # if self.training:
746
+ # # reshape (N * L, Hin) -> (N, L, Hin)
747
+ # rnn_input = states.view(-1, self.sequence_length, states.shape[-1])
748
+
749
+ # # reshape (D * num_layers, N * L, Hout) -> (D * num_layers, N, L, Hout)
750
+ # hidden_states = hidden_states.view(self.num_layers, -1, self.sequence_length, hidden_states.shape[-1])
751
+
752
+ # # reshape (D * num_layers, N * L, Hcell) -> (D * num_layers, N, L, Hcell)
753
+ # cell_states = cell_states.view(self.num_layers, -1, self.sequence_length, cell_states.shape[-1])
754
+
755
+ # # get the hidden/cell states corresponding to the first time step of the sequence for each batch.
756
+ # hidden_states = hidden_states[:, :, 0, :].contiguous() # (D * num_layers, N, Hout)
757
+ # cell_states = cell_states[:, :, 0, :].contiguous() # (D * num_layers, N, Hcell)
758
+
759
+ # # reset the RNN state in the middle of a sequence
760
+ # if terminated is not None and torch.any(terminated):
761
+ # rnn_outputs = []
762
+ # terminated = terminated.view(-1, self.sequence_length)
763
+ # indexes = (
764
+ # [0]
765
+ # + (terminated[:, :-1].any(dim=0).nonzero(as_tuple=True)[0] + 1).tolist()
766
+ # + [self.sequence_length]
767
+ # )
768
+
769
+ # for i in range(len(indexes) - 1):
770
+ # i0, i1 = indexes[i], indexes[i + 1]
771
+ # # Compute the RNN output for the segment of the sequence
772
+ # rnn_output, (hidden_states, cell_states) = self.lstm(
773
+ # rnn_input[:, i0:i1, :], (hidden_states, cell_states)
774
+ # )
775
+ # # Reset hidden and cell states where sequences are terminated
776
+ # hidden_states[:, (terminated[:, i1 - 1]), :] = 0
777
+ # cell_states[:, (terminated[:, i1 - 1]), :] = 0
778
+ # rnn_outputs.append(rnn_output)
779
+
780
+ # rnn_states = (hidden_states, cell_states)
781
+ # rnn_output = torch.cat(rnn_outputs, dim=1)
782
+ # # if no termination reset is needed, simply compute the RNN output
783
+ # else:
784
+ # rnn_output, rnn_states = self.lstm(rnn_input, (hidden_states, cell_states))
785
+ # # rollout
786
+ # else:
787
+ # # reshape (N, Hin) -> (N, 1, Hin)
788
+ # # This is done to add a sequence length dimension of 1 for processing one time step at a time
789
+ # rnn_input = states.view(-1, 1, states.shape[-1])
790
+ # rnn_output, rnn_states = self.lstm(rnn_input, (hidden_states, cell_states))
791
+
792
+ # # reshape (N, L, D * Hout) -> (N * L, D * Hout)
793
+ # rnn_output = torch.flatten(rnn_output, start_dim=0, end_dim=1)
794
+
795
+ # return self.net(rnn_output), {'rnn': [rnn_states[0], rnn_states[1]]}
796
+
797
+ # # TODO(ll) if not giving in params then just fast return.
798
+ # lstm = LSTM()
799
+ # return lstm
800
+
801
+
802
+ def get_models_shared(model_params, observation_space, action_space, device, double_pass):
803
+ # input_size = observation_space.shape[0]
804
+
805
+ # MLP -- change mlp in grouting PPO
806
+ # mlp_params = model_params['mlp']
807
+ # net = MLP(input_size, mlp_params['units'], device, mlp_params['activation'])
808
+
809
+ # LSTM -- change lstm in grouting PPO
810
+ # net = lstm_module(model_params, observation_space, action_space, device, double_pass)
811
+
812
+ # net = example_MLP()
813
+ # net = crazy_net_example()
814
+ # net = triple_cnn_and_mlp_example()
815
+ net = example_LstmMlp()
816
+ # net = example_LstmMlpWithForwardedInput()
817
+
818
+ # TODO --infere this inside of Layer?
819
+ net_output_size = get_output_size(net, observation_space.shape[0])
820
+
821
+ model = SharedRLModel(
822
+ observation_space=observation_space,
823
+ action_space=action_space,
824
+ input_shape=Shape.STATES,
825
+ device=device,
826
+ network=net,
827
+ output_layer={
828
+ 'policy': GaussianLayer(
829
+ input_size=net_output_size,
830
+ output_size=action_space.shape[0],
831
+ device=device,
832
+ output_shape=Shape.ACTIONS,
833
+ output_activation=nn.Tanh(),
834
+ clip_actions=False,
835
+ clip_log_std=True,
836
+ min_log_std=-1.2,
837
+ max_log_std=2,
838
+ initial_log_std=0.2,
839
+ ),
840
+ 'value': DeterministicLayer(
841
+ input_size=net_output_size,
842
+ output_size=1,
843
+ device=device,
844
+ output_shape=Shape.ONE,
845
+ output_activation=nn.Identity(),
846
+ ),
847
+ },
848
+ ).to(device)
849
+
850
+ return {'policy': model, 'value': model}
851
+
852
+
853
+ def get_models_separate(model_params, observation_space, action_space, device, double_pass):
854
+
855
+ net = example_MLP()
856
+ value_net = example_MLP()
857
+
858
+ # change for lstm/mlp
859
+ # net = lstm_module(model_params, observation_space, action_space, device, double_pass)
860
+ # value_net = lstm_module(model_params, observation_space, action_space, device, double_pass)
861
+
862
+ policy_model = RLModel(
863
+ observation_space=observation_space,
864
+ action_space=action_space,
865
+ input_shape=Shape.STATES,
866
+ device=device,
867
+ network=net,
868
+ output_layer=GaussianLayer(
869
+ input_size=get_output_size(net, observation_space.shape[0]),
870
+ output_size=action_space.shape[0],
871
+ device=device,
872
+ output_shape=Shape.ACTIONS,
873
+ output_activation=nn.Tanh(),
874
+ clip_actions=False,
875
+ clip_log_std=True,
876
+ min_log_std=-20,
877
+ max_log_std=2,
878
+ initial_log_std=0,
879
+ ),
880
+ ).to(device)
881
+
882
+ value_model = RLModel(
883
+ observation_space=observation_space,
884
+ action_space=action_space,
885
+ input_shape=Shape.STATES,
886
+ device=device,
887
+ network=value_net,
888
+ output_layer=DeterministicLayer(
889
+ input_size=get_output_size(value_net, observation_space.shape[0]),
890
+ output_size=1,
891
+ device=device,
892
+ output_shape=Shape.ONE,
893
+ output_activation=nn.Identity(),
894
+ ),
895
+ ).to(device)
896
+
897
+ return {'policy': policy_model, 'value': value_model}
898
+
899
+
900
+ # def get_models_separate_in(model_params, observation_space, action_space, device, double_pass):
901
+
902
+ # if 'mlp' in model_params:
903
+
904
+ # # if 'lstm' in model_params:
905
+ # # input_size = model_params['lstm']['hidden_size']
906
+ # # else:
907
+ # input_size = observation_space.shape[0] # TODO: is this approach too simplistic
908
+
909
+ # if 'cnn' in model_params:
910
+ # net = MixedCNN(model_params, input_size, device)
911
+ # else:
912
+ # mlp_params = model_params['mlp']
913
+ # net = MLP(input_size, mlp_params['units'], device, mlp_params['activation'])
914
+ # value_net = MLP(input_size, mlp_params['units'], device, mlp_params['activation'])
915
+ # else:
916
+ # raise ValueError('Invalid model type')
917
+
918
+ # # change for lstm/mlp
919
+ # # net = lstm_module(model_params, observation_space, action_space, device, double_pass)
920
+ # # value_net = lstm_module(model_params, observation_space, action_space, device, double_pass)
921
+
922
+ # policy_model = GaussianModel(
923
+ # observation_space=observation_space,
924
+ # action_space=action_space,
925
+ # network=net,
926
+ # device=device,
927
+ # clip_actions=False,
928
+ # clip_log_std=True,
929
+ # min_log_std=-20,
930
+ # max_log_std=2,
931
+ # initial_log_std=0,
932
+ # )
933
+
934
+ # value_model = DeterministicModel(
935
+ # observation_space=observation_space,
936
+ # action_space=action_space,
937
+ # network=value_net,
938
+ # device=device,
939
+ # clip_actions=False,
940
+ # )
941
+
942
+ # return {'policy': policy_model, 'value': value_model}
943
+
944
+
945
+ # def get_models(model_params, observation_space, action_space, device, double_pass):
946
+
947
+ # if 'mlp' in model_params:
948
+
949
+ # if 'lstm' in model_params:
950
+ # input_size = model_params['lstm']['hidden_size']
951
+ # else:
952
+ # input_size = observation_space.shape[0] # TODO: is this approach too simplistic
953
+
954
+ # if 'cnn' in model_params:
955
+ # net = MixedCNN(model_params, input_size, device)
956
+ # else:
957
+ # mlp_params = model_params['mlp']
958
+ # net = MLP(input_size, mlp_params['units'], device, mlp_params['activation'])
959
+ # else:
960
+ # raise ValueError('Invalid model type')
961
+
962
+ # shared_config = {
963
+ # 'clip_actions': False,
964
+ # 'clip_log_std': True,
965
+ # 'min_log_std': -20,
966
+ # 'max_log_std': 2,
967
+ # 'initial_log_std': 0,
968
+ # 'input_shape': Shape.STATES,
969
+ # 'output_scale': 1,
970
+ # 'hiddens': model_params['mlp']['units'],
971
+ # 'hidden_activation': [model_params['mlp']['activation'] for _ in range(len(model_params['mlp']['units']))],
972
+ # 'output_activation': None,
973
+ # }
974
+
975
+ # policy_config = shared_config.copy()
976
+ # value_config = shared_config.copy()
977
+
978
+ # policy_config['output_activation'] = 'tanh'
979
+ # policy_config['output_shape'] = Shape.ACTIONS
980
+ # value_config['output_shape'] = Shape.ONE
981
+
982
+ # if 'lstm' in model_params:
983
+ # model = shared_model_lstm(
984
+ # observation_space=observation_space,
985
+ # action_space=action_space,
986
+ # device=device,
987
+ # roles=['policy', 'value'],
988
+ # parameters=[policy_config, value_config],
989
+ # net=net,
990
+ # double_pass=double_pass,
991
+ # )
992
+ # else:
993
+ # model = shared_model(
994
+ # observation_space=observation_space,
995
+ # action_space=action_space,
996
+ # device=device,
997
+ # roles=['policy', 'value'],
998
+ # parameters=[policy_config, value_config],
999
+ # net=net,
1000
+ # double_pass=double_pass,
1001
+ # )
1002
+
1003
+ # return {'policy': model, 'value': model}