evolutionary-policy-optimization 0.4.2__tar.gz → 0.5.0__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.
- {evolutionary_policy_optimization-0.4.2 → evolutionary_policy_optimization-0.5.0}/PKG-INFO +2 -2
- {evolutionary_policy_optimization-0.4.2 → evolutionary_policy_optimization-0.5.0}/evolutionary_policy_optimization/epo.py +190 -59
- {evolutionary_policy_optimization-0.4.2 → evolutionary_policy_optimization-0.5.0}/pyproject.toml +2 -2
- {evolutionary_policy_optimization-0.4.2 → evolutionary_policy_optimization-0.5.0}/.gitignore +0 -0
- {evolutionary_policy_optimization-0.4.2 → evolutionary_policy_optimization-0.5.0}/LICENSE +0 -0
- {evolutionary_policy_optimization-0.4.2 → evolutionary_policy_optimization-0.5.0}/README.md +0 -0
- {evolutionary_policy_optimization-0.4.2 → evolutionary_policy_optimization-0.5.0}/evolutionary_policy_optimization/__init__.py +0 -0
- {evolutionary_policy_optimization-0.4.2 → evolutionary_policy_optimization-0.5.0}/evolutionary_policy_optimization/distributed.py +0 -0
- {evolutionary_policy_optimization-0.4.2 → evolutionary_policy_optimization-0.5.0}/evolutionary_policy_optimization/env_wrappers.py +0 -0
- {evolutionary_policy_optimization-0.4.2 → evolutionary_policy_optimization-0.5.0}/evolutionary_policy_optimization/experimental.py +0 -0
- {evolutionary_policy_optimization-0.4.2 → evolutionary_policy_optimization-0.5.0}/evolutionary_policy_optimization/mock_env.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: evolutionary-policy-optimization
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: EPO - Pytorch
|
|
5
5
|
Project-URL: Homepage, https://pypi.org/project/evolutionary-policy-optimization/
|
|
6
6
|
Project-URL: Repository, https://github.com/lucidrains/evolutionary-policy-optimization
|
|
@@ -44,7 +44,7 @@ Requires-Dist: mean-conc-beta>=0.2.7
|
|
|
44
44
|
Requires-Dist: torch>=2.2
|
|
45
45
|
Requires-Dist: tqdm
|
|
46
46
|
Requires-Dist: x-mlps-pytorch>=0.3.0
|
|
47
|
-
Requires-Dist: x-ppo>=0.0.
|
|
47
|
+
Requires-Dist: x-ppo>=0.0.21
|
|
48
48
|
Provides-Extra: examples
|
|
49
49
|
Requires-Dist: fire; extra == 'examples'
|
|
50
50
|
Requires-Dist: gymnasium[box2d]>=1.0.0; extra == 'examples'
|
|
@@ -15,7 +15,6 @@ import torch
|
|
|
15
15
|
import torch.distributed as dist
|
|
16
16
|
import torch.nn.functional as F
|
|
17
17
|
from accelerate import Accelerator
|
|
18
|
-
from torch.optim import AdamW
|
|
19
18
|
from assoc_scan import AssocScan
|
|
20
19
|
from einops import einsum, rearrange, reduce, repeat
|
|
21
20
|
from einops.layers.torch import Rearrange
|
|
@@ -25,6 +24,7 @@ from mean_conc_beta import Beta
|
|
|
25
24
|
from torch import Tensor, cat, from_numpy, is_tensor, nn, stack, tensor
|
|
26
25
|
from torch.distributions import Categorical, Distribution
|
|
27
26
|
from torch.nn import Linear, Module, ModuleList
|
|
27
|
+
from torch.optim import AdamW
|
|
28
28
|
from torch.utils._pytree import tree_map
|
|
29
29
|
from torch.utils.data import DataLoader, TensorDataset
|
|
30
30
|
from tqdm import tqdm
|
|
@@ -438,20 +438,32 @@ class MLP(Module):
|
|
|
438
438
|
depth,
|
|
439
439
|
dim_latent = 0,
|
|
440
440
|
latent_mapping_network_depth = 2,
|
|
441
|
-
expansion_factor = 2
|
|
441
|
+
expansion_factor = 2.,
|
|
442
|
+
latent_conditioning = 'film', # film | concat (direct z injection, x-ppo style)
|
|
442
443
|
):
|
|
443
444
|
super().__init__()
|
|
444
445
|
dim_latent = default(dim_latent, 0)
|
|
445
446
|
|
|
446
447
|
self.dim_latent = dim_latent
|
|
448
|
+
self.latent_conditioning = latent_conditioning
|
|
447
449
|
|
|
448
450
|
self.needs_latent = dim_latent > 0
|
|
449
451
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
452
|
+
assert latent_conditioning in ('film', 'concat')
|
|
453
|
+
|
|
454
|
+
self.encode_latent = None
|
|
455
|
+
|
|
456
|
+
if self.needs_latent and latent_conditioning == 'film':
|
|
457
|
+
self.encode_latent = nn.Sequential(
|
|
458
|
+
LatentMappingNetwork(dim_latent, depth = latent_mapping_network_depth),
|
|
459
|
+
Linear(dim_latent, dim * 2),
|
|
460
|
+
nn.SiLU()
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
self.latent_proj = None
|
|
464
|
+
|
|
465
|
+
if self.needs_latent and latent_conditioning == 'concat':
|
|
466
|
+
self.latent_proj = nn.Linear(dim_latent, dim, bias = False)
|
|
455
467
|
|
|
456
468
|
dim_hidden = int(dim * expansion_factor)
|
|
457
469
|
|
|
@@ -464,7 +476,7 @@ class MLP(Module):
|
|
|
464
476
|
|
|
465
477
|
film = None
|
|
466
478
|
|
|
467
|
-
if self.needs_latent:
|
|
479
|
+
if self.needs_latent and latent_conditioning == 'film':
|
|
468
480
|
film = FiLM(dim * 2, dim)
|
|
469
481
|
|
|
470
482
|
lime = DynamicLIMe(dim, num_layers = ind + 1) if not is_first else None
|
|
@@ -498,16 +510,17 @@ class MLP(Module):
|
|
|
498
510
|
assert xnor(self.needs_latent, exists(latent))
|
|
499
511
|
|
|
500
512
|
if exists(latent):
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
latent = self.encode_latent(latent)
|
|
513
|
+
if self.latent_conditioning == 'film':
|
|
514
|
+
latent = self.encode_latent(latent)
|
|
505
515
|
|
|
506
516
|
if latent.ndim == 1:
|
|
507
517
|
latent = repeat(latent, 'd -> b d', b = batch)
|
|
508
518
|
|
|
509
519
|
assert latent.shape[0] == x.shape[0], f'received state with batch size {x.shape[0]} but latent ids received had batch size {latent.shape[0]}'
|
|
510
520
|
|
|
521
|
+
if self.latent_conditioning == 'concat':
|
|
522
|
+
x = x + self.latent_proj(latent)
|
|
523
|
+
|
|
511
524
|
# layers
|
|
512
525
|
|
|
513
526
|
prev_layer_inputs = [x]
|
|
@@ -528,43 +541,66 @@ class MLP(Module):
|
|
|
528
541
|
|
|
529
542
|
return self.final_lime(x, prev_layer_inputs)
|
|
530
543
|
|
|
531
|
-
# discriminator for predicting latent code from states
|
|
544
|
+
# discriminator for predicting latent code from states, transitions, and optionally actions (DIAYN / DADS)
|
|
532
545
|
|
|
533
546
|
class DiversityDiscr(Module):
|
|
534
547
|
def __init__(
|
|
535
548
|
self,
|
|
536
549
|
dim_state,
|
|
537
|
-
num_latents,
|
|
550
|
+
num_latents = None,
|
|
538
551
|
dim = 64,
|
|
539
552
|
depth = 2,
|
|
540
|
-
time_gap = 1
|
|
553
|
+
time_gap = 1,
|
|
554
|
+
dim_latent = None, # continuous latent -> regression head, DADS style
|
|
555
|
+
dim_action = None, # condition on action q(z | s, a, s')
|
|
556
|
+
action_is_continuous = False,
|
|
541
557
|
):
|
|
542
558
|
super().__init__()
|
|
543
|
-
|
|
559
|
+
|
|
560
|
+
assert exists(num_latents) ^ exists(dim_latent), 'give either num_latents (discrete) or dim_latent (continuous)'
|
|
561
|
+
|
|
544
562
|
self.state_transition = time_gap > 0
|
|
563
|
+
self.has_action = exists(dim_action)
|
|
564
|
+
self.action_is_continuous = action_is_continuous
|
|
545
565
|
|
|
546
566
|
self.state_proj = nn.Linear(dim_state, dim)
|
|
547
567
|
|
|
568
|
+
self.action_proj = None
|
|
569
|
+
|
|
570
|
+
if self.has_action:
|
|
571
|
+
self.action_proj = nn.Linear(dim_action, dim) if action_is_continuous else nn.Embedding(dim_action, dim)
|
|
572
|
+
|
|
573
|
+
dim_in = dim * (2 if self.state_transition else 1) + (dim if self.has_action else 0)
|
|
574
|
+
dim_out = default(dim_latent, num_latents)
|
|
575
|
+
|
|
548
576
|
self.net = AttnResidualNormedMLP(
|
|
549
577
|
dim = dim,
|
|
550
578
|
depth = depth,
|
|
551
|
-
dim_in =
|
|
552
|
-
dim_out =
|
|
579
|
+
dim_in = dim_in,
|
|
580
|
+
dim_out = dim_out
|
|
553
581
|
)
|
|
554
582
|
|
|
555
583
|
def reset_parameters(self):
|
|
556
584
|
for module in self.modules():
|
|
557
|
-
if isinstance(module, (nn.Linear, nn.LayerNorm, nn.RMSNorm)):
|
|
585
|
+
if isinstance(module, (nn.Linear, nn.LayerNorm, nn.RMSNorm, nn.Embedding)):
|
|
558
586
|
module.reset_parameters()
|
|
559
587
|
|
|
560
|
-
def forward(self, state, next_state = None):
|
|
588
|
+
def forward(self, state, next_state = None, action = None):
|
|
561
589
|
state_embed = self.state_proj(state)
|
|
562
590
|
|
|
563
|
-
|
|
564
|
-
|
|
591
|
+
inputs = [state_embed]
|
|
592
|
+
|
|
593
|
+
if self.state_transition:
|
|
594
|
+
assert exists(next_state), 'discriminator was built with transitions but no next_state was given'
|
|
595
|
+
inputs.append(self.state_proj(next_state))
|
|
596
|
+
|
|
597
|
+
assert not self.has_action or exists(action), 'discriminator was built with actions but none were given'
|
|
565
598
|
|
|
566
|
-
|
|
567
|
-
|
|
599
|
+
if self.has_action:
|
|
600
|
+
action_inp = action if self.action_is_continuous else action.long()
|
|
601
|
+
inputs.append(self.action_proj(action_inp))
|
|
602
|
+
|
|
603
|
+
return self.net(inputs[0] if len(inputs) == 1 else tuple(inputs))
|
|
568
604
|
|
|
569
605
|
# action distributions - the actor always returns a `Distribution`, either
|
|
570
606
|
# categorical (discrete) or beta mean-conc (continuous). beta is defined over
|
|
@@ -661,6 +697,7 @@ class Actor(Module):
|
|
|
661
697
|
dim_latent = 0,
|
|
662
698
|
action_is_continuous = False, # continuous control - beta policy
|
|
663
699
|
beta_kwargs: dict = dict(),
|
|
700
|
+
latent_conditioning = 'film',
|
|
664
701
|
):
|
|
665
702
|
super().__init__()
|
|
666
703
|
|
|
@@ -676,7 +713,7 @@ class Actor(Module):
|
|
|
676
713
|
nn.SiLU()
|
|
677
714
|
)
|
|
678
715
|
|
|
679
|
-
self.mlp = MLP(dim = dim, depth = mlp_depth, dim_latent = dim_latent)
|
|
716
|
+
self.mlp = MLP(dim = dim, depth = mlp_depth, dim_latent = dim_latent, latent_conditioning = latent_conditioning)
|
|
680
717
|
|
|
681
718
|
if self.beta_actions:
|
|
682
719
|
# beta head - (raw mean, raw concentration) per action dim
|
|
@@ -733,7 +770,8 @@ class Critic(Module):
|
|
|
733
770
|
min_value = 0.,
|
|
734
771
|
max_value = 500.,
|
|
735
772
|
num_bins = 250
|
|
736
|
-
)
|
|
773
|
+
),
|
|
774
|
+
latent_conditioning = 'film',
|
|
737
775
|
):
|
|
738
776
|
super().__init__()
|
|
739
777
|
|
|
@@ -746,7 +784,7 @@ class Critic(Module):
|
|
|
746
784
|
nn.SiLU()
|
|
747
785
|
)
|
|
748
786
|
|
|
749
|
-
self.mlp = MLP(dim = dim, depth = mlp_depth, dim_latent = dim_latent)
|
|
787
|
+
self.mlp = MLP(dim = dim, depth = mlp_depth, dim_latent = dim_latent, latent_conditioning = latent_conditioning)
|
|
750
788
|
|
|
751
789
|
self.final_norm = nn.RMSNorm(dim)
|
|
752
790
|
|
|
@@ -1253,6 +1291,8 @@ class Agent(Module):
|
|
|
1253
1291
|
accelerate_kwargs: dict = dict(),
|
|
1254
1292
|
accelerator = None,
|
|
1255
1293
|
quiet: bool = False,
|
|
1294
|
+
latent_mode = 'gene_pool', # gene_pool (evolved latents) | fixed (sample z ~ N(0, I))
|
|
1295
|
+
num_latents = 1, # fixed mode: latent samples per rollout cycle
|
|
1256
1296
|
):
|
|
1257
1297
|
super().__init__()
|
|
1258
1298
|
|
|
@@ -1290,14 +1330,28 @@ class Agent(Module):
|
|
|
1290
1330
|
**ema_kwargs
|
|
1291
1331
|
) if use_critic_ema else None
|
|
1292
1332
|
|
|
1333
|
+
assert latent_mode in ('gene_pool', 'fixed')
|
|
1334
|
+
|
|
1335
|
+
self.latent_mode = latent_mode
|
|
1293
1336
|
self.latent_gene_pool = latent_gene_pool
|
|
1294
|
-
self.num_latents = latent_gene_pool.num_latents if exists(latent_gene_pool) else 1
|
|
1295
1337
|
self.has_latent_genes = exists(latent_gene_pool)
|
|
1338
|
+
self.has_fixed_latents = latent_mode == 'fixed'
|
|
1339
|
+
self.has_latents = self.has_latent_genes or self.has_fixed_latents
|
|
1340
|
+
self.dim_latent = default(actor.dim_latent, 0)
|
|
1296
1341
|
|
|
1342
|
+
assert not (self.has_latent_genes and self.has_fixed_latents)
|
|
1297
1343
|
assert actor.dim_latent == critic.dim_latent
|
|
1298
1344
|
|
|
1299
1345
|
if self.has_latent_genes:
|
|
1300
1346
|
assert latent_gene_pool.dim_latent == actor.dim_latent
|
|
1347
|
+
elif self.has_fixed_latents:
|
|
1348
|
+
assert self.dim_latent > 0, 'fixed latent mode needs dim_latent > 0'
|
|
1349
|
+
|
|
1350
|
+
self.num_latents = (
|
|
1351
|
+
latent_gene_pool.num_latents if self.has_latent_genes
|
|
1352
|
+
else num_latents if self.has_fixed_latents
|
|
1353
|
+
else 1
|
|
1354
|
+
)
|
|
1301
1355
|
|
|
1302
1356
|
# gae function
|
|
1303
1357
|
|
|
@@ -1333,13 +1387,27 @@ class Agent(Module):
|
|
|
1333
1387
|
self.use_diversity_discr = use_diversity_discr
|
|
1334
1388
|
self.diversity_time_gap = diversity_time_gap
|
|
1335
1389
|
|
|
1390
|
+
self.diversity_discr_loss_fn = F.mse_loss if self.has_fixed_latents else F.cross_entropy
|
|
1391
|
+
|
|
1336
1392
|
if use_diversity_discr:
|
|
1337
|
-
assert
|
|
1393
|
+
assert self.has_latents, 'latent gene pool or fixed latents must be present to use DIAYN / DADS'
|
|
1394
|
+
|
|
1395
|
+
if self.has_fixed_latents:
|
|
1396
|
+
assert diversity_time_gap in (0, 1), 'fixed latents support DIAYN (0) or DADS (1) transitions'
|
|
1397
|
+
|
|
1338
1398
|
dim_state = actor.init_layer[0].in_features
|
|
1339
1399
|
|
|
1400
|
+
diversity_discr_kwargs = dict(diversity_discr_kwargs)
|
|
1401
|
+
|
|
1402
|
+
if self.has_fixed_latents:
|
|
1403
|
+
diversity_discr_kwargs['dim_latent'] = self.dim_latent
|
|
1404
|
+
diversity_discr_kwargs['dim_action'] = actor.num_actions
|
|
1405
|
+
diversity_discr_kwargs['action_is_continuous'] = actor.beta_actions
|
|
1406
|
+
else:
|
|
1407
|
+
diversity_discr_kwargs['num_latents'] = latent_gene_pool.num_latents
|
|
1408
|
+
|
|
1340
1409
|
self.diversity_discr = DiversityDiscr(
|
|
1341
1410
|
dim_state = dim_state,
|
|
1342
|
-
num_latents = latent_gene_pool.num_latents,
|
|
1343
1411
|
time_gap = diversity_time_gap,
|
|
1344
1412
|
**diversity_discr_kwargs
|
|
1345
1413
|
)
|
|
@@ -1350,7 +1418,6 @@ class Agent(Module):
|
|
|
1350
1418
|
self.diversity_discr_optim = None
|
|
1351
1419
|
|
|
1352
1420
|
self.clip_value = clip_value
|
|
1353
|
-
self.critic_loss_kwargs = critic_loss_kwargs
|
|
1354
1421
|
|
|
1355
1422
|
# self-predictive representations (SPR)
|
|
1356
1423
|
|
|
@@ -1373,6 +1440,7 @@ class Agent(Module):
|
|
|
1373
1440
|
|
|
1374
1441
|
self.register_buffer('has_diversity_discr_warmed_up', tensor(False))
|
|
1375
1442
|
self.register_buffer('zero', tensor(0.))
|
|
1443
|
+
self.last_diversity_discr_loss = 0.
|
|
1376
1444
|
|
|
1377
1445
|
# shrink and perturb every
|
|
1378
1446
|
|
|
@@ -1382,7 +1450,7 @@ class Agent(Module):
|
|
|
1382
1450
|
|
|
1383
1451
|
# promotes latents to be farther apart for diversity maintenance
|
|
1384
1452
|
|
|
1385
|
-
self.has_diversity_loss = diversity_aux_loss_weight > 0.
|
|
1453
|
+
self.has_diversity_loss = diversity_aux_loss_weight > 0. and self.has_latent_genes
|
|
1386
1454
|
self.diversity_aux_loss_weight = diversity_aux_loss_weight
|
|
1387
1455
|
|
|
1388
1456
|
# wrap with accelerate
|
|
@@ -1469,6 +1537,9 @@ class Agent(Module):
|
|
|
1469
1537
|
actor = unwrap(self.actor).state_dict(),
|
|
1470
1538
|
critic = unwrap(self.critic).state_dict(),
|
|
1471
1539
|
critic_ema = self.critic_ema.state_dict() if self.use_critic_ema else None,
|
|
1540
|
+
latent_mode = self.latent_mode,
|
|
1541
|
+
num_latents = self.num_latents,
|
|
1542
|
+
dim_latent = self.dim_latent,
|
|
1472
1543
|
latents = unwrap(self.latent_gene_pool).state_dict() if self.has_latent_genes else None,
|
|
1473
1544
|
diversity_discr = unwrap(self.diversity_discr).state_dict() if self.use_diversity_discr else None,
|
|
1474
1545
|
diversity_time_gap = self.diversity_time_gap,
|
|
@@ -1492,6 +1563,9 @@ class Agent(Module):
|
|
|
1492
1563
|
|
|
1493
1564
|
pkg = torch.load(str(path), weights_only = True)
|
|
1494
1565
|
|
|
1566
|
+
if 'latent_mode' in pkg:
|
|
1567
|
+
assert pkg['latent_mode'] == self.latent_mode, 'checkpoint latent_mode does not match the agent'
|
|
1568
|
+
|
|
1495
1569
|
unwrap(self.actor).load_state_dict(pkg['actor'])
|
|
1496
1570
|
unwrap(self.critic).load_state_dict(pkg['critic'])
|
|
1497
1571
|
|
|
@@ -1499,6 +1573,7 @@ class Agent(Module):
|
|
|
1499
1573
|
self.critic_ema.load_state_dict(pkg['critic_ema'])
|
|
1500
1574
|
|
|
1501
1575
|
if 'latents' in pkg and exists(pkg['latents']):
|
|
1576
|
+
assert self.has_latent_genes, 'checkpoint carries a latent gene pool but agent is in fixed latent mode'
|
|
1502
1577
|
unwrap(self.latent_gene_pool).load_state_dict(pkg['latents'])
|
|
1503
1578
|
|
|
1504
1579
|
if self.use_diversity_discr and 'diversity_discr' in pkg and exists(pkg['diversity_discr']):
|
|
@@ -1541,6 +1616,8 @@ class Agent(Module):
|
|
|
1541
1616
|
if not exists(latent) and exists(latent_id) and exists(self.latent_gene_pool):
|
|
1542
1617
|
latent = maybe_unwrap(self.latent_gene_pool)(latent_id = latent_id)
|
|
1543
1618
|
|
|
1619
|
+
assert not self.has_fixed_latents or exists(latent), 'fixed latent mode needs a latent sampled from N(0, I)'
|
|
1620
|
+
|
|
1544
1621
|
return maybe_unwrap(self.actor)(state, latent, temperature = temperature)
|
|
1545
1622
|
|
|
1546
1623
|
@move_input_tensors_to_device
|
|
@@ -1587,6 +1664,8 @@ class Agent(Module):
|
|
|
1587
1664
|
if not exists(latent) and exists(latent_id) and exists(self.latent_gene_pool):
|
|
1588
1665
|
latent = maybe_unwrap(self.latent_gene_pool)(latent_id = latent_id)
|
|
1589
1666
|
|
|
1667
|
+
assert not self.has_fixed_latents or exists(latent), 'fixed latent mode needs a latent sampled from N(0, I)'
|
|
1668
|
+
|
|
1590
1669
|
critic_forward = maybe_unwrap(self.critic)
|
|
1591
1670
|
|
|
1592
1671
|
if use_ema_if_available and self.use_critic_ema:
|
|
@@ -1634,7 +1713,7 @@ class Agent(Module):
|
|
|
1634
1713
|
episode_ids,
|
|
1635
1714
|
states,
|
|
1636
1715
|
next_states,
|
|
1637
|
-
|
|
1716
|
+
latent_refs,
|
|
1638
1717
|
actions,
|
|
1639
1718
|
log_probs,
|
|
1640
1719
|
rewards,
|
|
@@ -1656,7 +1735,7 @@ class Agent(Module):
|
|
|
1656
1735
|
|
|
1657
1736
|
valid_episode = episode_ids >= 0
|
|
1658
1737
|
|
|
1659
|
-
fields = tuple(t[valid_episode] for t in (advantages, states, next_states,
|
|
1738
|
+
fields = tuple(t[valid_episode] for t in (advantages, states, next_states, latent_refs, actions, log_probs, values, dones))
|
|
1660
1739
|
|
|
1661
1740
|
if self.wrap_with_accelerate or self.use_dataloader:
|
|
1662
1741
|
dataloader = DataLoader(TensorDataset(*fields), batch_size = self.batch_size, shuffle = True)
|
|
@@ -1671,12 +1750,15 @@ class Agent(Module):
|
|
|
1671
1750
|
self.actor.train()
|
|
1672
1751
|
self.critic.train()
|
|
1673
1752
|
|
|
1753
|
+
if self.use_diversity_discr:
|
|
1754
|
+
self.diversity_discr.train()
|
|
1755
|
+
|
|
1674
1756
|
for _ in tqdm(range(epochs), desc = 'learning actor/critic epoch', disable = self.quiet or not self.is_main_process):
|
|
1675
1757
|
for (
|
|
1676
1758
|
advantages,
|
|
1677
1759
|
states,
|
|
1678
1760
|
next_states,
|
|
1679
|
-
|
|
1761
|
+
latent_refs,
|
|
1680
1762
|
actions,
|
|
1681
1763
|
log_probs,
|
|
1682
1764
|
old_values,
|
|
@@ -1684,11 +1766,13 @@ class Agent(Module):
|
|
|
1684
1766
|
) in dataloader:
|
|
1685
1767
|
|
|
1686
1768
|
if self.has_latent_genes:
|
|
1687
|
-
latents = self.latent_gene_pool(latent_id =
|
|
1769
|
+
latents = self.latent_gene_pool(latent_id = latent_refs)
|
|
1688
1770
|
|
|
1689
1771
|
orig_latents = latents
|
|
1690
1772
|
latents = latents.detach()
|
|
1691
1773
|
latents.requires_grad_()
|
|
1774
|
+
elif self.has_fixed_latents:
|
|
1775
|
+
latents = latent_refs
|
|
1692
1776
|
else:
|
|
1693
1777
|
latents = None
|
|
1694
1778
|
|
|
@@ -1757,8 +1841,10 @@ class Agent(Module):
|
|
|
1757
1841
|
|
|
1758
1842
|
if self.use_diversity_discr:
|
|
1759
1843
|
discr_inp = (states,) if self.diversity_time_gap == 0 else (states, next_states)
|
|
1760
|
-
|
|
1761
|
-
diversity_discr_loss =
|
|
1844
|
+
diversity_discr_pred = self.diversity_discr(*discr_inp, action = actions)
|
|
1845
|
+
diversity_discr_loss = self.diversity_discr_loss_fn(diversity_discr_pred, latent_refs)
|
|
1846
|
+
|
|
1847
|
+
self.last_diversity_discr_loss = float(diversity_discr_loss.detach())
|
|
1762
1848
|
|
|
1763
1849
|
diversity_discr_loss.backward()
|
|
1764
1850
|
|
|
@@ -1905,6 +1991,7 @@ def create_agent(
|
|
|
1905
1991
|
use_critic_ema = True,
|
|
1906
1992
|
use_state_norm = False,
|
|
1907
1993
|
action_is_continuous = False, # continuous control - beta policy
|
|
1994
|
+
latent_mode = 'gene_pool', # gene_pool (evolved latents) | fixed (sample z ~ N(0, I))
|
|
1908
1995
|
diversity_time_gap = 1,
|
|
1909
1996
|
latent_gene_pool_kwargs: dict = dict(),
|
|
1910
1997
|
actor_kwargs: dict = dict(),
|
|
@@ -1912,9 +1999,13 @@ def create_agent(
|
|
|
1912
1999
|
**kwargs
|
|
1913
2000
|
) -> Agent:
|
|
1914
2001
|
|
|
1915
|
-
|
|
2002
|
+
assert latent_mode in ('gene_pool', 'fixed')
|
|
2003
|
+
|
|
2004
|
+
has_latent_genes = latent_mode == 'gene_pool' and num_latents > 1
|
|
1916
2005
|
|
|
1917
|
-
if
|
|
2006
|
+
if latent_mode == 'fixed':
|
|
2007
|
+
assert exists(dim_latent) and dim_latent > 0, 'fixed latent mode needs dim_latent > 0'
|
|
2008
|
+
elif not has_latent_genes:
|
|
1918
2009
|
dim_latent = None
|
|
1919
2010
|
|
|
1920
2011
|
latent_gene_pool = LatentGenePool(
|
|
@@ -1925,6 +2016,14 @@ def create_agent(
|
|
|
1925
2016
|
|
|
1926
2017
|
state_norm = StateNorm(dim = dim_state) if use_state_norm else None
|
|
1927
2018
|
|
|
2019
|
+
actor_kwargs = dict(actor_kwargs)
|
|
2020
|
+
critic_kwargs = dict(critic_kwargs)
|
|
2021
|
+
|
|
2022
|
+
latent_conditioning = 'concat' if latent_mode == 'fixed' else 'film'
|
|
2023
|
+
|
|
2024
|
+
actor_kwargs.setdefault('latent_conditioning', latent_conditioning)
|
|
2025
|
+
critic_kwargs.setdefault('latent_conditioning', latent_conditioning)
|
|
2026
|
+
|
|
1928
2027
|
actor = Actor(
|
|
1929
2028
|
num_actions = actor_num_actions,
|
|
1930
2029
|
dim_state = dim_state,
|
|
@@ -1950,6 +2049,8 @@ def create_agent(
|
|
|
1950
2049
|
critic = critic,
|
|
1951
2050
|
state_norm = state_norm,
|
|
1952
2051
|
latent_gene_pool = latent_gene_pool,
|
|
2052
|
+
latent_mode = latent_mode,
|
|
2053
|
+
num_latents = num_latents,
|
|
1953
2054
|
use_critic_ema = use_critic_ema,
|
|
1954
2055
|
diversity_time_gap = diversity_time_gap,
|
|
1955
2056
|
**kwargs
|
|
@@ -1960,6 +2061,8 @@ def create_agent(
|
|
|
1960
2061
|
# EPO - which is just PPO with natural selection of a population of latent variables conditioning the agent
|
|
1961
2062
|
# the tricky part is that the latent ids for each episode / trajectory needs to be tracked
|
|
1962
2063
|
|
|
2064
|
+
# latent_gene_id is a pool index in gene_pool mode, or the sampled z in fixed mode
|
|
2065
|
+
|
|
1963
2066
|
Memory = namedtuple('Memory', [
|
|
1964
2067
|
'episode_id',
|
|
1965
2068
|
'state',
|
|
@@ -2048,7 +2151,7 @@ class EPO(Module):
|
|
|
2048
2151
|
self.action_sample_temperature = action_sample_temperature
|
|
2049
2152
|
self.diversity_reward_weight = diversity_reward_weight
|
|
2050
2153
|
|
|
2051
|
-
self.num_latents = agent.
|
|
2154
|
+
self.num_latents = agent.num_latents
|
|
2052
2155
|
self.episodes_per_latent = episodes_per_latent
|
|
2053
2156
|
self.max_episode_length = max_episode_length
|
|
2054
2157
|
self.fix_environ_across_latents = fix_environ_across_latents
|
|
@@ -2144,7 +2247,12 @@ class EPO(Module):
|
|
|
2144
2247
|
|
|
2145
2248
|
latent_id, episode_id, maybe_seed = rollout
|
|
2146
2249
|
|
|
2147
|
-
|
|
2250
|
+
if self.agent.has_latent_genes:
|
|
2251
|
+
latent = self.agent.unwrapped_latent_gene_pool(latent_id = latent_id)
|
|
2252
|
+
elif self.agent.has_fixed_latents:
|
|
2253
|
+
latent = torch.randn(self.agent.dim_latent, device = self.device)
|
|
2254
|
+
else:
|
|
2255
|
+
latent = None
|
|
2148
2256
|
|
|
2149
2257
|
seed = maybe_seed if fix_environ_across_latents else None
|
|
2150
2258
|
state, _ = interface_torch_numpy(env.reset_one, device = self.device)(i, seed = seed)
|
|
@@ -2156,6 +2264,12 @@ class EPO(Module):
|
|
|
2156
2264
|
|
|
2157
2265
|
pbar = tqdm(total = num_episodes, desc = 'rollout', disable = self.agent.quiet or not self.agent.is_main_process)
|
|
2158
2266
|
|
|
2267
|
+
# some vec envs return the full batch even when only `active` rows were stepped
|
|
2268
|
+
|
|
2269
|
+
def active_rows(x, active):
|
|
2270
|
+
x = np.asarray(x)
|
|
2271
|
+
return x[active] if x.shape[0] != len(active) else x
|
|
2272
|
+
|
|
2159
2273
|
# each iteration, batch all active slots through the actor and critic,
|
|
2160
2274
|
# then step the env - slots that finish are refilled and the batch
|
|
2161
2275
|
# stays full until all episodes have been rolled out
|
|
@@ -2164,17 +2278,38 @@ class EPO(Module):
|
|
|
2164
2278
|
active = [i for i, slot in enumerate(slots) if exists(slot)]
|
|
2165
2279
|
|
|
2166
2280
|
obs = stack([slots[i].state for i in active])
|
|
2167
|
-
latents = stack([slots[i].latent for i in active]) if self.agent.
|
|
2281
|
+
latents = stack([slots[i].latent for i in active]) if self.agent.has_latents else None
|
|
2168
2282
|
|
|
2169
2283
|
actions, log_probs = self.agent.get_actor_actions(obs, latent = latents, sample = True, temperature = self.action_sample_temperature, use_unwrapped_model = True)
|
|
2170
2284
|
values = self.agent.get_critic_values(obs, latent = latents, use_ema_if_available = True, use_unwrapped_model = True)
|
|
2171
2285
|
|
|
2172
2286
|
next_obs, rewards, terminated, truncated = env.step_batch(actions.cpu().numpy(), worker_ids = active)[:4]
|
|
2173
2287
|
|
|
2174
|
-
next_states = from_numpy(np.ascontiguousarray(next_obs)).float().to(self.device)
|
|
2175
|
-
rewards = from_numpy(
|
|
2176
|
-
terminated = from_numpy(
|
|
2177
|
-
truncated = from_numpy(
|
|
2288
|
+
next_states = from_numpy(np.ascontiguousarray(active_rows(next_obs, active))).float().to(self.device)
|
|
2289
|
+
rewards = from_numpy(active_rows(rewards, active)).float().to(self.device)
|
|
2290
|
+
terminated = from_numpy(active_rows(terminated, active)).to(self.device)
|
|
2291
|
+
truncated = from_numpy(active_rows(truncated, active)).to(self.device)
|
|
2292
|
+
|
|
2293
|
+
# DADS / DIAYN intrinsic reward, batched over every active env
|
|
2294
|
+
|
|
2295
|
+
diversity_rewards = None
|
|
2296
|
+
time_gap = self.agent.diversity_time_gap
|
|
2297
|
+
|
|
2298
|
+
if self.agent.use_diversity_discr and self.agent.has_diversity_discr_warmed_up.item() and time_gap <= 1:
|
|
2299
|
+
with torch.no_grad():
|
|
2300
|
+
discr = self.agent.unwrap_model(self.agent.diversity_discr)
|
|
2301
|
+
|
|
2302
|
+
if self.agent.has_fixed_latents:
|
|
2303
|
+
# -mse(q(z | s, a, s'), z) - Sharma et al. https://arxiv.org/abs/1907.01657
|
|
2304
|
+
|
|
2305
|
+
pred = discr(obs, action = actions) if time_gap == 0 else discr(obs, next_states, action = actions)
|
|
2306
|
+
diversity_rewards = -F.mse_loss(pred, latents, reduction = 'none').mean(dim = -1)
|
|
2307
|
+
else:
|
|
2308
|
+
logits = discr(obs) if time_gap == 0 else discr(obs, next_states)
|
|
2309
|
+
latent_log_probs = logits.log_softmax(dim = -1)
|
|
2310
|
+
|
|
2311
|
+
latent_ids = tensor([slots[i].latent_id for i in active], device = self.device)
|
|
2312
|
+
diversity_rewards = latent_log_probs.gather(1, latent_ids[:, None]).squeeze(1) + math.log(self.agent.num_latents)
|
|
2178
2313
|
|
|
2179
2314
|
for k, worker in enumerate(active):
|
|
2180
2315
|
slot = slots[worker]
|
|
@@ -2187,19 +2322,16 @@ class EPO(Module):
|
|
|
2187
2322
|
|
|
2188
2323
|
# maybe diversity reward for the latent
|
|
2189
2324
|
|
|
2190
|
-
if
|
|
2325
|
+
if exists(diversity_rewards):
|
|
2326
|
+
reward = reward + self.diversity_reward_weight * diversity_rewards[k].item()
|
|
2327
|
+
elif self.agent.use_diversity_discr and self.agent.has_diversity_discr_warmed_up.item():
|
|
2328
|
+
# only the discrete > 1 time-gap case falls back to per-transition
|
|
2329
|
+
|
|
2191
2330
|
with torch.no_grad():
|
|
2192
2331
|
discr = self.agent.unwrap_model(self.agent.diversity_discr)
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
if time_gap == 0:
|
|
2196
|
-
logits = discr(rearrange(state, '... -> 1 ...'))
|
|
2197
|
-
elif time_gap == 1 or len(slot.memories) < (time_gap - 1):
|
|
2198
|
-
logits = discr(rearrange(state, '... -> 1 ...'), rearrange(next_state, '... -> 1 ...'))
|
|
2199
|
-
else:
|
|
2200
|
-
past_state = slot.memories[-(time_gap - 1)].state
|
|
2201
|
-
logits = discr(rearrange(past_state, '... -> 1 ...'), rearrange(next_state, '... -> 1 ...'))
|
|
2332
|
+
past_state = slot.memories[-(time_gap - 1)].state if len(slot.memories) >= (time_gap - 1) else state
|
|
2202
2333
|
|
|
2334
|
+
logits = discr(rearrange(past_state, '... -> 1 ...'), rearrange(next_state, '... -> 1 ...'))
|
|
2203
2335
|
latent_log_probs = logits.log_softmax(dim = -1)
|
|
2204
2336
|
diversity_reward = latent_log_probs[0, latent_id] + math.log(self.agent.num_latents)
|
|
2205
2337
|
|
|
@@ -2211,7 +2343,7 @@ class EPO(Module):
|
|
|
2211
2343
|
tensor(episode_id),
|
|
2212
2344
|
state,
|
|
2213
2345
|
next_state,
|
|
2214
|
-
tensor(latent_id),
|
|
2346
|
+
latent.detach().cpu() if self.agent.has_fixed_latents else tensor(latent_id),
|
|
2215
2347
|
actions[k],
|
|
2216
2348
|
log_probs[k],
|
|
2217
2349
|
reward,
|
|
@@ -2224,7 +2356,6 @@ class EPO(Module):
|
|
|
2224
2356
|
slot.memories.append(memory)
|
|
2225
2357
|
|
|
2226
2358
|
if self.agent.use_diversity_discr and self.agent.diversity_time_gap > 1:
|
|
2227
|
-
time_gap = self.agent.diversity_time_gap
|
|
2228
2359
|
if len(slot.memories) >= time_gap:
|
|
2229
2360
|
slot.memories[-time_gap] = slot.memories[-time_gap]._replace(next_state = next_state)
|
|
2230
2361
|
|
{evolutionary_policy_optimization-0.4.2 → evolutionary_policy_optimization-0.5.0}/pyproject.toml
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "evolutionary-policy-optimization"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.5.0"
|
|
4
4
|
description = "EPO - Pytorch"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Phil Wang", email = "lucidrains@gmail.com" }
|
|
@@ -34,7 +34,7 @@ dependencies = [
|
|
|
34
34
|
'torch>=2.2',
|
|
35
35
|
'tqdm',
|
|
36
36
|
"x-mlps-pytorch>=0.3.0",
|
|
37
|
-
"x-ppo>=0.0.
|
|
37
|
+
"x-ppo>=0.0.21",
|
|
38
38
|
]
|
|
39
39
|
|
|
40
40
|
[project.urls]
|
{evolutionary_policy_optimization-0.4.2 → evolutionary_policy_optimization-0.5.0}/.gitignore
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|