x-evolution 0.1.30__tar.gz → 0.1.32__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.
- {x_evolution-0.1.30 → x_evolution-0.1.32}/PKG-INFO +22 -9
- {x_evolution-0.1.30 → x_evolution-0.1.32}/README.md +20 -8
- {x_evolution-0.1.30 → x_evolution-0.1.32}/pyproject.toml +10 -1
- {x_evolution-0.1.30 → x_evolution-0.1.32}/x_evolution/x_evolution.py +38 -8
- x_evolution-0.1.30/.github/workflows/python-publish.yml +0 -36
- x_evolution-0.1.30/.github/workflows/test.yml +0 -21
- x_evolution-0.1.30/.gitlab-ci.yml +0 -27
- x_evolution-0.1.30/scripts/install-humanoid.sh +0 -2
- x_evolution-0.1.30/tests/test_evolution.py +0 -70
- x_evolution-0.1.30/train_ant_maze.py +0 -211
- x_evolution-0.1.30/train_humanoid.py +0 -215
- x_evolution-0.1.30/train_lunar.py +0 -141
- x_evolution-0.1.30/train_mnist.py +0 -91
- x_evolution-0.1.30/train_xor.py +0 -83
- {x_evolution-0.1.30 → x_evolution-0.1.32}/.gitignore +0 -0
- {x_evolution-0.1.30 → x_evolution-0.1.32}/LICENSE +0 -0
- {x_evolution-0.1.30 → x_evolution-0.1.32}/x_evolution/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: x-evolution
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.32
|
|
4
4
|
Summary: x-evolution
|
|
5
5
|
Project-URL: Homepage, https://pypi.org/project/x-evolution/
|
|
6
6
|
Project-URL: Repository, https://github.com/lucidrains/x-evolution
|
|
@@ -37,6 +37,7 @@ Requires-Python: >=3.9
|
|
|
37
37
|
Requires-Dist: accelerate
|
|
38
38
|
Requires-Dist: beartype
|
|
39
39
|
Requires-Dist: einops>=0.8.0
|
|
40
|
+
Requires-Dist: torch-einops-utils
|
|
40
41
|
Requires-Dist: torch>=2.4
|
|
41
42
|
Requires-Dist: x-mlps-pytorch>=0.2.0
|
|
42
43
|
Requires-Dist: x-transformers>=2.11.23
|
|
@@ -52,6 +53,8 @@ Description-Content-Type: text/markdown
|
|
|
52
53
|
|
|
53
54
|
Implementation of various evolutionary algorithms, starting with [evolutionary strategies](https://blog.otoro.net/2017/10/29/visual-evolution-strategies/)
|
|
54
55
|
|
|
56
|
+
[explained by @bycloud](https://www.youtube.com/watch?v=lLkE9w1NJs0)
|
|
57
|
+
|
|
55
58
|
## Install
|
|
56
59
|
|
|
57
60
|
```bash
|
|
@@ -95,19 +98,29 @@ evo_strat()
|
|
|
95
98
|
|
|
96
99
|
## Distributed
|
|
97
100
|
|
|
98
|
-
Using the CLI from 🤗
|
|
101
|
+
Using the CLI from 🤗
|
|
99
102
|
|
|
100
103
|
```shell
|
|
101
|
-
$ accelerate config
|
|
104
|
+
$ uv run accelerate config
|
|
102
105
|
```
|
|
103
106
|
|
|
104
107
|
Then
|
|
105
108
|
|
|
106
109
|
```shell
|
|
107
|
-
$ accelerate launch train.py
|
|
110
|
+
$ uv run accelerate launch train.py
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Or with `torchrun` (which can do multiple CPU processes for testing)
|
|
114
|
+
|
|
115
|
+
```shell
|
|
116
|
+
$ uv run torchrun --nproc_per_node=<N> train.py --cpu True
|
|
108
117
|
```
|
|
109
118
|
|
|
110
|
-
For
|
|
119
|
+
For a quick demo on LunarLander with 8 processes
|
|
120
|
+
|
|
121
|
+
```shell
|
|
122
|
+
$ uv run torchrun --nproc_per_node=8 train_lunar.py --cpu True --noise_population_size 100 --fitness_to_weighted_factor centered_rank
|
|
123
|
+
```
|
|
111
124
|
|
|
112
125
|
## Citations
|
|
113
126
|
|
|
@@ -124,25 +137,25 @@ For gymnasium simulations, first run `pip install '[examples]'`
|
|
|
124
137
|
|
|
125
138
|
```bibtex
|
|
126
139
|
@misc{sarkar2025evolutionstrategieshyperscale,
|
|
127
|
-
title = {Evolution Strategies at the Hyperscale},
|
|
140
|
+
title = {Evolution Strategies at the Hyperscale},
|
|
128
141
|
author = {Bidipta Sarkar and Mattie Fellows and Juan Agustin Duque and Alistair Letcher and Antonio León Villares and Anya Sims and Dylan Cope and Jarek Liesen and Lukas Seier and Theo Wolf and Uljad Berdica and Alexander David Goldie and Aaron Courville and Karin Sevegnani and Shimon Whiteson and Jakob Nicolaus Foerster},
|
|
129
142
|
year = {2025},
|
|
130
143
|
eprint = {2511.16652},
|
|
131
144
|
archivePrefix = {arXiv},
|
|
132
145
|
primaryClass = {cs.LG},
|
|
133
|
-
url = {https://arxiv.org/abs/2511.16652},
|
|
146
|
+
url = {https://arxiv.org/abs/2511.16652},
|
|
134
147
|
}
|
|
135
148
|
```
|
|
136
149
|
|
|
137
150
|
```bibtex
|
|
138
151
|
@misc{fortunato2019noisynetworksexploration,
|
|
139
|
-
title = {Noisy Networks for Exploration},
|
|
152
|
+
title = {Noisy Networks for Exploration},
|
|
140
153
|
author = {Meire Fortunato and Mohammad Gheshlaghi Azar and Bilal Piot and Jacob Menick and Ian Osband and Alex Graves and Vlad Mnih and Remi Munos and Demis Hassabis and Olivier Pietquin and Charles Blundell and Shane Legg},
|
|
141
154
|
year = {2019},
|
|
142
155
|
eprint = {1706.10295},
|
|
143
156
|
archivePrefix = {arXiv},
|
|
144
157
|
primaryClass = {cs.LG},
|
|
145
|
-
url = {https://arxiv.org/abs/1706.10295},
|
|
158
|
+
url = {https://arxiv.org/abs/1706.10295},
|
|
146
159
|
}
|
|
147
160
|
```
|
|
148
161
|
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Implementation of various evolutionary algorithms, starting with [evolutionary strategies](https://blog.otoro.net/2017/10/29/visual-evolution-strategies/)
|
|
4
4
|
|
|
5
|
+
[explained by @bycloud](https://www.youtube.com/watch?v=lLkE9w1NJs0)
|
|
6
|
+
|
|
5
7
|
## Install
|
|
6
8
|
|
|
7
9
|
```bash
|
|
@@ -45,19 +47,29 @@ evo_strat()
|
|
|
45
47
|
|
|
46
48
|
## Distributed
|
|
47
49
|
|
|
48
|
-
Using the CLI from 🤗
|
|
50
|
+
Using the CLI from 🤗
|
|
49
51
|
|
|
50
52
|
```shell
|
|
51
|
-
$ accelerate config
|
|
53
|
+
$ uv run accelerate config
|
|
52
54
|
```
|
|
53
55
|
|
|
54
56
|
Then
|
|
55
57
|
|
|
56
58
|
```shell
|
|
57
|
-
$ accelerate launch train.py
|
|
59
|
+
$ uv run accelerate launch train.py
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Or with `torchrun` (which can do multiple CPU processes for testing)
|
|
63
|
+
|
|
64
|
+
```shell
|
|
65
|
+
$ uv run torchrun --nproc_per_node=<N> train.py --cpu True
|
|
58
66
|
```
|
|
59
67
|
|
|
60
|
-
For
|
|
68
|
+
For a quick demo on LunarLander with 8 processes
|
|
69
|
+
|
|
70
|
+
```shell
|
|
71
|
+
$ uv run torchrun --nproc_per_node=8 train_lunar.py --cpu True --noise_population_size 100 --fitness_to_weighted_factor centered_rank
|
|
72
|
+
```
|
|
61
73
|
|
|
62
74
|
## Citations
|
|
63
75
|
|
|
@@ -74,25 +86,25 @@ For gymnasium simulations, first run `pip install '[examples]'`
|
|
|
74
86
|
|
|
75
87
|
```bibtex
|
|
76
88
|
@misc{sarkar2025evolutionstrategieshyperscale,
|
|
77
|
-
title = {Evolution Strategies at the Hyperscale},
|
|
89
|
+
title = {Evolution Strategies at the Hyperscale},
|
|
78
90
|
author = {Bidipta Sarkar and Mattie Fellows and Juan Agustin Duque and Alistair Letcher and Antonio León Villares and Anya Sims and Dylan Cope and Jarek Liesen and Lukas Seier and Theo Wolf and Uljad Berdica and Alexander David Goldie and Aaron Courville and Karin Sevegnani and Shimon Whiteson and Jakob Nicolaus Foerster},
|
|
79
91
|
year = {2025},
|
|
80
92
|
eprint = {2511.16652},
|
|
81
93
|
archivePrefix = {arXiv},
|
|
82
94
|
primaryClass = {cs.LG},
|
|
83
|
-
url = {https://arxiv.org/abs/2511.16652},
|
|
95
|
+
url = {https://arxiv.org/abs/2511.16652},
|
|
84
96
|
}
|
|
85
97
|
```
|
|
86
98
|
|
|
87
99
|
```bibtex
|
|
88
100
|
@misc{fortunato2019noisynetworksexploration,
|
|
89
|
-
title = {Noisy Networks for Exploration},
|
|
101
|
+
title = {Noisy Networks for Exploration},
|
|
90
102
|
author = {Meire Fortunato and Mohammad Gheshlaghi Azar and Bilal Piot and Jacob Menick and Ian Osband and Alex Graves and Vlad Mnih and Remi Munos and Demis Hassabis and Olivier Pietquin and Charles Blundell and Shane Legg},
|
|
91
103
|
year = {2019},
|
|
92
104
|
eprint = {1706.10295},
|
|
93
105
|
archivePrefix = {arXiv},
|
|
94
106
|
primaryClass = {cs.LG},
|
|
95
|
-
url = {https://arxiv.org/abs/1706.10295},
|
|
107
|
+
url = {https://arxiv.org/abs/1706.10295},
|
|
96
108
|
}
|
|
97
109
|
```
|
|
98
110
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "x-evolution"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.32"
|
|
4
4
|
description = "x-evolution"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Phil Wang", email = "lucidrains@gmail.com" }
|
|
@@ -27,6 +27,7 @@ dependencies = [
|
|
|
27
27
|
"beartype",
|
|
28
28
|
"einops>=0.8.0",
|
|
29
29
|
"torch>=2.4",
|
|
30
|
+
"torch-einops-utils",
|
|
30
31
|
"x-mlps-pytorch>=0.2.0",
|
|
31
32
|
"x-transformers>=2.11.23"
|
|
32
33
|
]
|
|
@@ -65,3 +66,11 @@ allow-direct-references = true
|
|
|
65
66
|
|
|
66
67
|
[tool.hatch.build.targets.wheel]
|
|
67
68
|
packages = ["x_evolution"]
|
|
69
|
+
|
|
70
|
+
[tool.hatch.build.targets.sdist]
|
|
71
|
+
include = [
|
|
72
|
+
"x_evolution",
|
|
73
|
+
"README.md",
|
|
74
|
+
"LICENSE",
|
|
75
|
+
"pyproject.toml",
|
|
76
|
+
]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from typing import Callable
|
|
2
|
+
from typing import Callable, Literal
|
|
3
3
|
|
|
4
4
|
from math import ceil
|
|
5
5
|
from pathlib import Path
|
|
@@ -27,6 +27,7 @@ from x_mlps_pytorch.noisable import (
|
|
|
27
27
|
)
|
|
28
28
|
|
|
29
29
|
from einops import pack
|
|
30
|
+
from torch_einops_utils import pack_with_inverse
|
|
30
31
|
|
|
31
32
|
# constants
|
|
32
33
|
|
|
@@ -46,15 +47,31 @@ def identity(t):
|
|
|
46
47
|
def divisible_by(num, den):
|
|
47
48
|
return (num % den) == 0
|
|
48
49
|
|
|
49
|
-
def normalize(t, eps = 1e-6):
|
|
50
|
-
return F.layer_norm(t, t.shape, eps = eps)
|
|
51
|
-
|
|
52
50
|
def accum_grad_(t, value):
|
|
53
51
|
if not exists(t.grad):
|
|
54
52
|
t.grad = value.clone()
|
|
55
53
|
else:
|
|
56
54
|
t.grad.add_(value)
|
|
57
55
|
|
|
56
|
+
# fitness weighting factors
|
|
57
|
+
|
|
58
|
+
def normalize(t, eps = 1e-6):
|
|
59
|
+
return F.layer_norm(t, t.shape, eps = eps)
|
|
60
|
+
|
|
61
|
+
def centered_rank_normalize(fitnesses):
|
|
62
|
+
dtype, device, n = fitnesses.dtype, fitnesses.device, fitnesses.numel()
|
|
63
|
+
flat_fitnesses, inverse = pack_with_inverse(fitnesses, '*')
|
|
64
|
+
ranks = flat_fitnesses.argsort().argsort().to(dtype = dtype)
|
|
65
|
+
ranks = (ranks / (n - 1)) - 0.5
|
|
66
|
+
return inverse(ranks)
|
|
67
|
+
|
|
68
|
+
FITNESS_WEIGHT_FACTORS = dict(
|
|
69
|
+
normalize = normalize,
|
|
70
|
+
centered_rank = centered_rank_normalize
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
FitnessWeightFactorType = Literal['normalize', 'centered_rank']
|
|
74
|
+
|
|
58
75
|
# class
|
|
59
76
|
|
|
60
77
|
class EvoStrategy(Module):
|
|
@@ -89,7 +106,7 @@ class EvoStrategy(Module):
|
|
|
89
106
|
sigma_scheduler_klass: type[LRScheduler] | None = None,
|
|
90
107
|
sigma_scheduler_kwargs: dict = dict(),
|
|
91
108
|
transform_fitness: Callable = identity,
|
|
92
|
-
fitness_to_weighted_factor: Callable[[Tensor], Tensor] = normalize,
|
|
109
|
+
fitness_to_weighted_factor: Callable[[Tensor], Tensor] | FitnessWeightFactorType = 'normalize',
|
|
93
110
|
checkpoint_every = None, # saving every number of generations
|
|
94
111
|
checkpoint_path = './checkpoints',
|
|
95
112
|
cpu = False,
|
|
@@ -97,6 +114,7 @@ class EvoStrategy(Module):
|
|
|
97
114
|
accelerator: Accelerator | None = None,
|
|
98
115
|
accelerate_kwargs: dict = dict(),
|
|
99
116
|
reject_generation_fitnesses_if: Callable[[Tensor], bool] | None = None,
|
|
117
|
+
on_result: Callable | None = None,
|
|
100
118
|
vectorized = False,
|
|
101
119
|
vector_size: int | None = None,
|
|
102
120
|
sync_on_init = True
|
|
@@ -106,6 +124,7 @@ class EvoStrategy(Module):
|
|
|
106
124
|
|
|
107
125
|
self.vectorized = vectorized
|
|
108
126
|
self.vector_size = vector_size
|
|
127
|
+
self.on_result = on_result
|
|
109
128
|
|
|
110
129
|
if not exists(accelerator):
|
|
111
130
|
accelerator = Accelerator(cpu = cpu, **accelerate_kwargs)
|
|
@@ -179,7 +198,7 @@ class EvoStrategy(Module):
|
|
|
179
198
|
# dtypes
|
|
180
199
|
|
|
181
200
|
self.param_dtypes = {name: param.dtype for name, param in named_parameters_dict.items()}
|
|
182
|
-
|
|
201
|
+
|
|
183
202
|
# hyperparameters
|
|
184
203
|
|
|
185
204
|
self.noise_population_size = noise_population_size
|
|
@@ -191,6 +210,11 @@ class EvoStrategy(Module):
|
|
|
191
210
|
|
|
192
211
|
self.transform_fitness = transform_fitness # a function that gets called before converting to weights for the weighted noise update - eventually get rank normalization
|
|
193
212
|
|
|
213
|
+
if isinstance(fitness_to_weighted_factor, str):
|
|
214
|
+
if fitness_to_weighted_factor not in FITNESS_WEIGHT_FACTORS:
|
|
215
|
+
raise ValueError(f'fitness_to_weighted_factor must be one of {list(FITNESS_WEIGHT_FACTORS.keys())}')
|
|
216
|
+
fitness_to_weighted_factor = FITNESS_WEIGHT_FACTORS[fitness_to_weighted_factor]
|
|
217
|
+
|
|
194
218
|
self.fitness_to_weighted_factor = fitness_to_weighted_factor
|
|
195
219
|
|
|
196
220
|
self.mirror_sampling = mirror_sampling # mirror / antithetical sampling - reducing variance by doing positive + negative of noise and subtracting
|
|
@@ -522,7 +546,10 @@ class EvoStrategy(Module):
|
|
|
522
546
|
|
|
523
547
|
if not self.mirror_sampling:
|
|
524
548
|
fitnesses.append(fitness)
|
|
525
|
-
|
|
549
|
+
|
|
550
|
+
if exists(self.on_result):
|
|
551
|
+
self.on_result(fitness, pbar)
|
|
552
|
+
|
|
526
553
|
continue
|
|
527
554
|
|
|
528
555
|
# handle mirror sampling
|
|
@@ -530,7 +557,10 @@ class EvoStrategy(Module):
|
|
|
530
557
|
fitness_mirrored = get_fitness(negate = True)
|
|
531
558
|
|
|
532
559
|
fitnesses.append([fitness, fitness_mirrored])
|
|
533
|
-
|
|
560
|
+
avg_fitness = (fitness + fitness_mirrored) / 2
|
|
561
|
+
|
|
562
|
+
if exists(self.on_result):
|
|
563
|
+
self.on_result(avg_fitness, pbar)
|
|
534
564
|
|
|
535
565
|
pbar.close()
|
|
536
566
|
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# This workflow will upload a Python Package using Twine when a release is created
|
|
2
|
-
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
|
|
3
|
-
|
|
4
|
-
# This workflow uses actions that are not certified by GitHub.
|
|
5
|
-
# They are provided by a third-party and are governed by
|
|
6
|
-
# separate terms of service, privacy policy, and support
|
|
7
|
-
# documentation.
|
|
8
|
-
|
|
9
|
-
name: Upload Python Package
|
|
10
|
-
|
|
11
|
-
on:
|
|
12
|
-
release:
|
|
13
|
-
types: [published]
|
|
14
|
-
|
|
15
|
-
jobs:
|
|
16
|
-
deploy:
|
|
17
|
-
|
|
18
|
-
runs-on: ubuntu-latest
|
|
19
|
-
|
|
20
|
-
steps:
|
|
21
|
-
- uses: actions/checkout@v2
|
|
22
|
-
- name: Set up Python
|
|
23
|
-
uses: actions/setup-python@v2
|
|
24
|
-
with:
|
|
25
|
-
python-version: '3.x'
|
|
26
|
-
- name: Install dependencies
|
|
27
|
-
run: |
|
|
28
|
-
python -m pip install --upgrade pip
|
|
29
|
-
pip install build
|
|
30
|
-
- name: Build package
|
|
31
|
-
run: python -m build
|
|
32
|
-
- name: Publish package
|
|
33
|
-
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
|
|
34
|
-
with:
|
|
35
|
-
user: __token__
|
|
36
|
-
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
name: Pytest
|
|
2
|
-
on: [push, pull_request]
|
|
3
|
-
|
|
4
|
-
jobs:
|
|
5
|
-
build:
|
|
6
|
-
|
|
7
|
-
runs-on: ubuntu-latest
|
|
8
|
-
|
|
9
|
-
steps:
|
|
10
|
-
- uses: actions/checkout@v4
|
|
11
|
-
- name: Set up Python 3.10
|
|
12
|
-
uses: actions/setup-python@v5
|
|
13
|
-
with:
|
|
14
|
-
python-version: "3.10"
|
|
15
|
-
- name: Install dependencies
|
|
16
|
-
run: |
|
|
17
|
-
python -m pip install --upgrade pip
|
|
18
|
-
python -m pip install -e .[test]
|
|
19
|
-
- name: Test with pytest
|
|
20
|
-
run: |
|
|
21
|
-
python -m pytest tests/
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
image: python:3.10
|
|
2
|
-
|
|
3
|
-
stages:
|
|
4
|
-
- test
|
|
5
|
-
- publish
|
|
6
|
-
|
|
7
|
-
test:
|
|
8
|
-
stage: test
|
|
9
|
-
before_script:
|
|
10
|
-
- python -m pip install --upgrade pip
|
|
11
|
-
- python -m pip install -e .[test]
|
|
12
|
-
script:
|
|
13
|
-
- python -m pytest tests/
|
|
14
|
-
only:
|
|
15
|
-
- pushes
|
|
16
|
-
- merge_requests
|
|
17
|
-
|
|
18
|
-
pypi:
|
|
19
|
-
stage: publish
|
|
20
|
-
before_script:
|
|
21
|
-
- python -m pip install --upgrade pip
|
|
22
|
-
- pip install build twine
|
|
23
|
-
script:
|
|
24
|
-
- python -m build
|
|
25
|
-
- TWINE_PASSWORD=$PYPI_API_TOKEN TWINE_USERNAME=__token__ python -m twine upload dist/*
|
|
26
|
-
only:
|
|
27
|
-
- tags
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
param = pytest.mark.parametrize
|
|
3
|
-
|
|
4
|
-
import torch
|
|
5
|
-
from x_mlps_pytorch import MLP
|
|
6
|
-
|
|
7
|
-
model = MLP(8, 16, 4)
|
|
8
|
-
|
|
9
|
-
@param('params_to_optimize', (None, [model.layers[1].weight]))
|
|
10
|
-
@param('use_optimizer', (False, True))
|
|
11
|
-
@param('noise_low_rank', (None, 1))
|
|
12
|
-
@param('mirror_sampling', (False, True))
|
|
13
|
-
@param('multi_models', (False, True))
|
|
14
|
-
@param('learned_sigma', (False, True))
|
|
15
|
-
@param('use_sigma_optimizer', (False, True))
|
|
16
|
-
def test_evo_strat(
|
|
17
|
-
params_to_optimize,
|
|
18
|
-
use_optimizer,
|
|
19
|
-
noise_low_rank,
|
|
20
|
-
mirror_sampling,
|
|
21
|
-
multi_models,
|
|
22
|
-
learned_sigma,
|
|
23
|
-
use_sigma_optimizer
|
|
24
|
-
):
|
|
25
|
-
from random import randrange
|
|
26
|
-
|
|
27
|
-
from x_evolution.x_evolution import EvoStrategy
|
|
28
|
-
|
|
29
|
-
to_optim = model
|
|
30
|
-
|
|
31
|
-
if multi_models:
|
|
32
|
-
to_optim = [model, MLP(8, 1)]
|
|
33
|
-
|
|
34
|
-
evo_strat = EvoStrategy(
|
|
35
|
-
to_optim,
|
|
36
|
-
environment = lambda model: float(randrange(100)),
|
|
37
|
-
num_generations = 2,
|
|
38
|
-
learned_noise_scale = learned_sigma,
|
|
39
|
-
params_to_optimize = params_to_optimize,
|
|
40
|
-
use_optimizer = use_optimizer,
|
|
41
|
-
noise_low_rank = noise_low_rank,
|
|
42
|
-
mirror_sampling = mirror_sampling,
|
|
43
|
-
use_sigma_optimizer = use_sigma_optimizer
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
evo_strat('evolve', 1)
|
|
47
|
-
evo_strat('more.evolve', 1)
|
|
48
|
-
|
|
49
|
-
fitnesses = evo_strat('more.evolve', 2, rollback_model_at_end = True)
|
|
50
|
-
|
|
51
|
-
@param('vector_size', (2, 4))
|
|
52
|
-
def test_evo_strat_vectorized(vector_size):
|
|
53
|
-
from x_evolution.x_evolution import EvoStrategy
|
|
54
|
-
|
|
55
|
-
model = MLP(8, 16, 4)
|
|
56
|
-
|
|
57
|
-
def environment(model):
|
|
58
|
-
# mock a vectorized environment returning multiple fitness scores
|
|
59
|
-
return torch.randn(vector_size)
|
|
60
|
-
|
|
61
|
-
evo_strat = EvoStrategy(
|
|
62
|
-
model,
|
|
63
|
-
environment = environment,
|
|
64
|
-
num_generations = 2,
|
|
65
|
-
vectorized = True,
|
|
66
|
-
vector_size = vector_size,
|
|
67
|
-
noise_population_size = 4
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
evo_strat()
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
# /// script
|
|
2
|
-
# dependencies = [
|
|
3
|
-
# "fire",
|
|
4
|
-
# "gymnasium[mujoco]>=1.0.0",
|
|
5
|
-
# "gymnasium-robotics",
|
|
6
|
-
# "moviepy",
|
|
7
|
-
# "tqdm",
|
|
8
|
-
# "x-evolution>=0.1.27",
|
|
9
|
-
# "x-mlps-pytorch"
|
|
10
|
-
# ]
|
|
11
|
-
# ///
|
|
12
|
-
|
|
13
|
-
import fire
|
|
14
|
-
from shutil import rmtree
|
|
15
|
-
import gymnasium as gym
|
|
16
|
-
import gymnasium_robotics
|
|
17
|
-
import numpy as np
|
|
18
|
-
from tqdm import tqdm
|
|
19
|
-
|
|
20
|
-
import torch
|
|
21
|
-
from torch import nn
|
|
22
|
-
from torch.nn import Module, GRU, Linear
|
|
23
|
-
import torch.nn.functional as F
|
|
24
|
-
|
|
25
|
-
# functions
|
|
26
|
-
|
|
27
|
-
def exists(v):
|
|
28
|
-
return v is not None
|
|
29
|
-
|
|
30
|
-
def softclamp(t, value):
|
|
31
|
-
return (t / value).tanh() * value
|
|
32
|
-
|
|
33
|
-
class AntMazeEnvironment(Module):
|
|
34
|
-
def __init__(
|
|
35
|
-
self,
|
|
36
|
-
env_id = 'AntMaze_UMazeDense-v5',
|
|
37
|
-
video_folder = './recordings_ant_maze',
|
|
38
|
-
render_every_eps = 100,
|
|
39
|
-
max_steps = 1000,
|
|
40
|
-
repeats = 1,
|
|
41
|
-
verbose = False
|
|
42
|
-
):
|
|
43
|
-
super().__init__()
|
|
44
|
-
|
|
45
|
-
self.verbose = verbose
|
|
46
|
-
|
|
47
|
-
env = gym.make(env_id, render_mode = 'rgb_array')
|
|
48
|
-
|
|
49
|
-
self.env = env
|
|
50
|
-
self.max_steps = max_steps
|
|
51
|
-
self.repeats = repeats
|
|
52
|
-
self.video_folder = video_folder
|
|
53
|
-
self.render_every_eps = render_every_eps
|
|
54
|
-
|
|
55
|
-
def pre_main_callback(self):
|
|
56
|
-
rmtree(self.video_folder, ignore_errors = True)
|
|
57
|
-
|
|
58
|
-
self.env = gym.wrappers.RecordVideo(
|
|
59
|
-
env = self.env,
|
|
60
|
-
video_folder = self.video_folder,
|
|
61
|
-
name_prefix = 'recording',
|
|
62
|
-
episode_trigger = lambda eps_num: (eps_num % self.render_every_eps) == 0,
|
|
63
|
-
disable_logger = True
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
def forward(self, model):
|
|
67
|
-
|
|
68
|
-
device = next(model.parameters()).device
|
|
69
|
-
|
|
70
|
-
seed = torch.randint(0, int(1e6), ())
|
|
71
|
-
|
|
72
|
-
num_envs = 1
|
|
73
|
-
cum_reward = torch.zeros(num_envs, device = device)
|
|
74
|
-
|
|
75
|
-
for _ in range(self.repeats):
|
|
76
|
-
obs, _ = self.env.reset(seed = seed.item())
|
|
77
|
-
|
|
78
|
-
step = 0
|
|
79
|
-
hiddens = None
|
|
80
|
-
|
|
81
|
-
dones = torch.zeros(num_envs, device = device, dtype = torch.bool)
|
|
82
|
-
|
|
83
|
-
while step < self.max_steps and not dones.all():
|
|
84
|
-
|
|
85
|
-
state = obs['observation']
|
|
86
|
-
goal = obs['desired_goal']
|
|
87
|
-
|
|
88
|
-
state_torch = torch.from_numpy(state).float().to(device)
|
|
89
|
-
goal_torch = torch.from_numpy(goal).float().to(device)
|
|
90
|
-
|
|
91
|
-
state_goal = torch.cat((state_torch, goal_torch), dim = -1)
|
|
92
|
-
|
|
93
|
-
action_logits, hiddens = model(state_goal, hiddens)
|
|
94
|
-
|
|
95
|
-
mean, log_var = action_logits.chunk(2, dim = -1)
|
|
96
|
-
|
|
97
|
-
std = (0.5 * softclamp(log_var, 5.)).exp()
|
|
98
|
-
sampled = mean + torch.randn_like(mean) * std
|
|
99
|
-
action = sampled.tanh()
|
|
100
|
-
|
|
101
|
-
next_obs, reward, truncated, terminated, info = self.env.step(action.detach().cpu().numpy())
|
|
102
|
-
|
|
103
|
-
reward_np = np.array(reward) if not isinstance(reward, np.ndarray) else reward
|
|
104
|
-
total_reward_base = torch.from_numpy(reward_np).float().to(device)
|
|
105
|
-
|
|
106
|
-
exploration_bonus = std.mean(dim = -1) * 0.01
|
|
107
|
-
penalize_extreme_actions = (mean.abs() > 1.).float().mean(dim = -1) * 0.01
|
|
108
|
-
|
|
109
|
-
total_reward = total_reward_base + exploration_bonus - penalize_extreme_actions
|
|
110
|
-
|
|
111
|
-
mask = (~dones).float()
|
|
112
|
-
cum_reward += total_reward * mask
|
|
113
|
-
|
|
114
|
-
dones_np = np.array(truncated | terminated) if not isinstance(truncated | terminated, np.ndarray) else (truncated | terminated)
|
|
115
|
-
dones |= torch.from_numpy(dones_np).to(device)
|
|
116
|
-
|
|
117
|
-
step += 1
|
|
118
|
-
obs = next_obs
|
|
119
|
-
|
|
120
|
-
return cum_reward.item() / self.repeats
|
|
121
|
-
|
|
122
|
-
# model
|
|
123
|
-
|
|
124
|
-
from x_mlps_pytorch.residual_normed_mlp import ResidualNormedMLP
|
|
125
|
-
|
|
126
|
-
class Model(Module):
|
|
127
|
-
|
|
128
|
-
def __init__(self, state_dim = 27 + 2, action_dim = 8):
|
|
129
|
-
super().__init__()
|
|
130
|
-
|
|
131
|
-
self.deep_mlp = ResidualNormedMLP(
|
|
132
|
-
dim_in = state_dim,
|
|
133
|
-
dim = 256,
|
|
134
|
-
depth = 8,
|
|
135
|
-
residual_every = 2
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
self.gru = GRU(256, 256, batch_first = True)
|
|
139
|
-
|
|
140
|
-
self.to_pred = Linear(256, action_dim * 2, bias = False)
|
|
141
|
-
|
|
142
|
-
def forward(self, state, hiddens = None):
|
|
143
|
-
|
|
144
|
-
x = self.deep_mlp(state)
|
|
145
|
-
|
|
146
|
-
is_batched = x.ndim > 1
|
|
147
|
-
|
|
148
|
-
if not is_batched:
|
|
149
|
-
x = x.unsqueeze(0)
|
|
150
|
-
|
|
151
|
-
x = x.unsqueeze(-2)
|
|
152
|
-
gru_out, hiddens = self.gru(x, hiddens)
|
|
153
|
-
x = x + gru_out
|
|
154
|
-
x = x.squeeze(-2)
|
|
155
|
-
|
|
156
|
-
if not is_batched:
|
|
157
|
-
x = x.squeeze(0)
|
|
158
|
-
|
|
159
|
-
return self.to_pred(x), hiddens
|
|
160
|
-
|
|
161
|
-
# evo strategy
|
|
162
|
-
|
|
163
|
-
from x_evolution import EvoStrategy
|
|
164
|
-
from torch.optim.lr_scheduler import CosineAnnealingLR
|
|
165
|
-
|
|
166
|
-
def main(
|
|
167
|
-
env_id = 'AntMaze_UMazeDense-v5',
|
|
168
|
-
num_generations = 50_000,
|
|
169
|
-
max_steps = 250,
|
|
170
|
-
noise_population_size = 150,
|
|
171
|
-
verbose = True
|
|
172
|
-
):
|
|
173
|
-
# Determine dims from temp env
|
|
174
|
-
temp_env = gym.make(env_id)
|
|
175
|
-
obs_space = temp_env.observation_space
|
|
176
|
-
action_space = temp_env.action_space
|
|
177
|
-
|
|
178
|
-
state_dim = obs_space['observation'].shape[0] + obs_space['desired_goal'].shape[0]
|
|
179
|
-
action_dim = action_space.shape[0]
|
|
180
|
-
temp_env.close()
|
|
181
|
-
|
|
182
|
-
model = Model(state_dim = state_dim, action_dim = action_dim)
|
|
183
|
-
|
|
184
|
-
evo_strat = EvoStrategy(
|
|
185
|
-
model,
|
|
186
|
-
environment = AntMazeEnvironment(
|
|
187
|
-
env_id = env_id,
|
|
188
|
-
max_steps = max_steps,
|
|
189
|
-
repeats = 1,
|
|
190
|
-
render_every_eps = 200,
|
|
191
|
-
verbose = verbose
|
|
192
|
-
),
|
|
193
|
-
num_generations = num_generations,
|
|
194
|
-
noise_population_size = noise_population_size,
|
|
195
|
-
noise_low_rank = 1,
|
|
196
|
-
noise_scale = 1e-2,
|
|
197
|
-
noise_scale_clamp_range = (5e-3, 2e-2),
|
|
198
|
-
learned_noise_scale = True,
|
|
199
|
-
use_sigma_optimizer = True,
|
|
200
|
-
learning_rate = 3e-4,
|
|
201
|
-
noise_scale_learning_rate = 1e-4,
|
|
202
|
-
use_scheduler = True,
|
|
203
|
-
scheduler_klass = CosineAnnealingLR,
|
|
204
|
-
scheduler_kwargs = dict(T_max = num_generations),
|
|
205
|
-
verbose = verbose
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
evo_strat()
|
|
209
|
-
|
|
210
|
-
if __name__ == '__main__':
|
|
211
|
-
fire.Fire(main)
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
# /// script
|
|
2
|
-
# dependencies = [
|
|
3
|
-
# "fire",
|
|
4
|
-
# "gymnasium[mujoco]>=1.0.0",
|
|
5
|
-
# "gymnasium[other]",
|
|
6
|
-
# "x-evolution>=0.0.20",
|
|
7
|
-
# "x-mlps-pytorch"
|
|
8
|
-
# ]
|
|
9
|
-
# ///
|
|
10
|
-
|
|
11
|
-
# import os
|
|
12
|
-
# os.environ["NCCL_P2P_DISABLE"] = "1"
|
|
13
|
-
# os.environ["NCCL_IB_DISABLE"] = "1"
|
|
14
|
-
# os.environ["MUJOCO_GL"] = "osmesa"
|
|
15
|
-
|
|
16
|
-
import fire
|
|
17
|
-
from shutil import rmtree
|
|
18
|
-
import gymnasium as gym
|
|
19
|
-
import numpy as np
|
|
20
|
-
|
|
21
|
-
import torch
|
|
22
|
-
from torch.nn import Module, GRU, Linear
|
|
23
|
-
import torch.nn.functional as F
|
|
24
|
-
|
|
25
|
-
# functions
|
|
26
|
-
|
|
27
|
-
def exists(v):
|
|
28
|
-
return v is not None
|
|
29
|
-
|
|
30
|
-
def softclamp(t, value):
|
|
31
|
-
return (t / value).tanh() * value
|
|
32
|
-
|
|
33
|
-
class HumanoidEnvironment(Module):
|
|
34
|
-
def __init__(
|
|
35
|
-
self,
|
|
36
|
-
video_folder = './recordings_humanoid',
|
|
37
|
-
render_every_eps = 100,
|
|
38
|
-
max_steps = 1000,
|
|
39
|
-
repeats = 1,
|
|
40
|
-
vectorized = False,
|
|
41
|
-
num_envs = 1
|
|
42
|
-
):
|
|
43
|
-
super().__init__()
|
|
44
|
-
|
|
45
|
-
self.vectorized = vectorized
|
|
46
|
-
self.num_envs = num_envs
|
|
47
|
-
|
|
48
|
-
if vectorized:
|
|
49
|
-
env = gym.make_vec('Humanoid-v5', num_envs = num_envs, render_mode = 'rgb_array')
|
|
50
|
-
else:
|
|
51
|
-
env = gym.make('Humanoid-v5', render_mode = 'rgb_array')
|
|
52
|
-
|
|
53
|
-
self.env = env
|
|
54
|
-
self.max_steps = max_steps
|
|
55
|
-
self.repeats = repeats
|
|
56
|
-
self.video_folder = video_folder
|
|
57
|
-
self.render_every_eps = render_every_eps
|
|
58
|
-
|
|
59
|
-
def pre_main_callback(self):
|
|
60
|
-
# the `pre_main_callback` on the environment passed in is called before the start of the evolutionary strategies loop
|
|
61
|
-
|
|
62
|
-
rmtree(self.video_folder, ignore_errors = True)
|
|
63
|
-
|
|
64
|
-
if not self.vectorized:
|
|
65
|
-
self.env = gym.wrappers.RecordVideo(
|
|
66
|
-
env = self.env,
|
|
67
|
-
video_folder = self.video_folder,
|
|
68
|
-
name_prefix = 'recording',
|
|
69
|
-
episode_trigger = lambda eps_num: (eps_num % self.render_every_eps) == 0,
|
|
70
|
-
disable_logger = True
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
def forward(self, model):
|
|
74
|
-
|
|
75
|
-
device = next(model.parameters()).device
|
|
76
|
-
|
|
77
|
-
seed = torch.randint(0, int(1e6), ())
|
|
78
|
-
|
|
79
|
-
num_envs = self.num_envs if self.vectorized else 1
|
|
80
|
-
cum_reward = torch.zeros(num_envs, device = device)
|
|
81
|
-
|
|
82
|
-
for _ in range(self.repeats):
|
|
83
|
-
state, _ = self.env.reset(seed = seed.item())
|
|
84
|
-
|
|
85
|
-
step = 0
|
|
86
|
-
hiddens = None
|
|
87
|
-
last_action = None
|
|
88
|
-
|
|
89
|
-
dones = torch.zeros(num_envs, device = device, dtype = torch.bool)
|
|
90
|
-
|
|
91
|
-
while step < self.max_steps and not dones.all():
|
|
92
|
-
|
|
93
|
-
state_torch = torch.from_numpy(state).float().to(device)
|
|
94
|
-
|
|
95
|
-
action_logits, hiddens = model(state_torch, hiddens)
|
|
96
|
-
|
|
97
|
-
mean, log_var = action_logits.chunk(2, dim = -1)
|
|
98
|
-
|
|
99
|
-
# sample and then bound and scale to -0.4 to 0.4
|
|
100
|
-
|
|
101
|
-
std = (0.5 * softclamp(log_var, 5.)).exp()
|
|
102
|
-
sampled = mean + torch.randn_like(mean) * std
|
|
103
|
-
action = sampled.tanh() * 0.4
|
|
104
|
-
|
|
105
|
-
next_state, reward, truncated, terminated, info = self.env.step(action.detach().cpu().numpy() if self.vectorized else action.item())
|
|
106
|
-
|
|
107
|
-
reward_np = np.array(reward) if not isinstance(reward, np.ndarray) else reward
|
|
108
|
-
total_reward_base = torch.from_numpy(reward_np).float().to(device)
|
|
109
|
-
|
|
110
|
-
# reward functions
|
|
111
|
-
|
|
112
|
-
# encouraged to move forward (1.0) and stay upright (> 1.2 meters)
|
|
113
|
-
|
|
114
|
-
z_pos = torch.from_numpy(next_state[..., 0]).float().to(device)
|
|
115
|
-
x_vel = torch.from_numpy(next_state[..., 5]).float().to(device)
|
|
116
|
-
|
|
117
|
-
reward_forward = x_vel
|
|
118
|
-
reward_upright = (z_pos > 1.2).float()
|
|
119
|
-
|
|
120
|
-
exploration_bonus = std.mean(dim = -1) * 0.05
|
|
121
|
-
penalize_extreme_actions = (mean.abs() > 1.).float().mean(dim = -1) * 0.05
|
|
122
|
-
|
|
123
|
-
penalize_action_change = 0.
|
|
124
|
-
if exists(last_action):
|
|
125
|
-
penalize_action_change = (last_action - action).abs().mean(dim = -1) * 0.1
|
|
126
|
-
|
|
127
|
-
total_reward = total_reward_base + reward_forward + reward_upright + exploration_bonus - penalize_extreme_actions - penalize_action_change
|
|
128
|
-
|
|
129
|
-
# only add reward if not done
|
|
130
|
-
|
|
131
|
-
mask = (~dones).float()
|
|
132
|
-
cum_reward += total_reward * mask
|
|
133
|
-
|
|
134
|
-
# update dones
|
|
135
|
-
|
|
136
|
-
dones_np = np.array(truncated | terminated) if not isinstance(truncated | terminated, np.ndarray) else (truncated | terminated)
|
|
137
|
-
dones |= torch.from_numpy(dones_np).to(device)
|
|
138
|
-
|
|
139
|
-
step += 1
|
|
140
|
-
|
|
141
|
-
state = next_state
|
|
142
|
-
last_action = action
|
|
143
|
-
|
|
144
|
-
if not self.vectorized:
|
|
145
|
-
return cum_reward.item() / self.repeats
|
|
146
|
-
|
|
147
|
-
return cum_reward / self.repeats
|
|
148
|
-
|
|
149
|
-
# evo strategy
|
|
150
|
-
|
|
151
|
-
from x_evolution import EvoStrategy
|
|
152
|
-
|
|
153
|
-
from x_mlps_pytorch.residual_normed_mlp import ResidualNormedMLP
|
|
154
|
-
|
|
155
|
-
class Model(Module):
|
|
156
|
-
|
|
157
|
-
def __init__(self):
|
|
158
|
-
super().__init__()
|
|
159
|
-
|
|
160
|
-
self.deep_mlp = ResidualNormedMLP(
|
|
161
|
-
dim_in = 348,
|
|
162
|
-
dim = 256,
|
|
163
|
-
depth = 8,
|
|
164
|
-
residual_every = 2
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
self.gru = GRU(256, 256, batch_first = True)
|
|
168
|
-
|
|
169
|
-
self.to_pred = Linear(256, 17 * 2, bias = False)
|
|
170
|
-
|
|
171
|
-
def forward(self, state, hiddens = None):
|
|
172
|
-
|
|
173
|
-
x = self.deep_mlp(state)
|
|
174
|
-
|
|
175
|
-
x = x.unsqueeze(-2)
|
|
176
|
-
gru_out, hiddens = self.gru(x, hiddens)
|
|
177
|
-
x = x + gru_out
|
|
178
|
-
x = x.squeeze(-2)
|
|
179
|
-
|
|
180
|
-
return self.to_pred(x), hiddens
|
|
181
|
-
|
|
182
|
-
from torch.optim.lr_scheduler import CosineAnnealingLR
|
|
183
|
-
|
|
184
|
-
def main(
|
|
185
|
-
vectorized = False,
|
|
186
|
-
num_envs = 8
|
|
187
|
-
):
|
|
188
|
-
evo_strat = EvoStrategy(
|
|
189
|
-
Model(),
|
|
190
|
-
environment = HumanoidEnvironment(
|
|
191
|
-
repeats = 1,
|
|
192
|
-
render_every_eps = 200,
|
|
193
|
-
vectorized = vectorized,
|
|
194
|
-
num_envs = num_envs
|
|
195
|
-
),
|
|
196
|
-
vectorized = vectorized,
|
|
197
|
-
vector_size = num_envs,
|
|
198
|
-
num_generations = 50_000,
|
|
199
|
-
noise_population_size = 200,
|
|
200
|
-
noise_low_rank = 1,
|
|
201
|
-
noise_scale = 1e-2,
|
|
202
|
-
noise_scale_clamp_range = (5e-3, 2e-2),
|
|
203
|
-
learned_noise_scale = True,
|
|
204
|
-
use_sigma_optimizer = True,
|
|
205
|
-
learning_rate = 1e-3,
|
|
206
|
-
noise_scale_learning_rate = 1e-4,
|
|
207
|
-
use_scheduler = True,
|
|
208
|
-
scheduler_klass = CosineAnnealingLR,
|
|
209
|
-
scheduler_kwargs = dict(T_max = 50_000)
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
evo_strat()
|
|
213
|
-
|
|
214
|
-
if __name__ == '__main__':
|
|
215
|
-
fire.Fire(main)
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
# /// script
|
|
2
|
-
# dependencies = [
|
|
3
|
-
# "fire",
|
|
4
|
-
# "gymnasium[box2d]>=1.0.0",
|
|
5
|
-
# "gymnasium[other]",
|
|
6
|
-
# "x-evolution>=0.0.20",
|
|
7
|
-
# "x-mlps-pytorch>=0.2.0"
|
|
8
|
-
# ]
|
|
9
|
-
# ///
|
|
10
|
-
|
|
11
|
-
import fire
|
|
12
|
-
from shutil import rmtree
|
|
13
|
-
import gymnasium as gym
|
|
14
|
-
import numpy as np
|
|
15
|
-
|
|
16
|
-
import torch
|
|
17
|
-
from torch.nn import Module
|
|
18
|
-
import torch.nn.functional as F
|
|
19
|
-
from x_mlps_pytorch.residual_normed_mlp import ResidualNormedMLP
|
|
20
|
-
from torch.optim.lr_scheduler import CosineAnnealingLR
|
|
21
|
-
|
|
22
|
-
class LunarEnvironment(Module):
|
|
23
|
-
def __init__(
|
|
24
|
-
self,
|
|
25
|
-
video_folder = './recordings',
|
|
26
|
-
render_every_eps = 500,
|
|
27
|
-
max_steps = 500,
|
|
28
|
-
repeats = 1,
|
|
29
|
-
vectorized = False,
|
|
30
|
-
num_envs = 1
|
|
31
|
-
):
|
|
32
|
-
super().__init__()
|
|
33
|
-
|
|
34
|
-
self.vectorized = vectorized
|
|
35
|
-
self.num_envs = num_envs
|
|
36
|
-
|
|
37
|
-
if vectorized:
|
|
38
|
-
env = gym.make_vec('LunarLander-v3', num_envs = num_envs, render_mode = 'rgb_array')
|
|
39
|
-
else:
|
|
40
|
-
env = gym.make('LunarLander-v3', render_mode = 'rgb_array')
|
|
41
|
-
|
|
42
|
-
self.env = env
|
|
43
|
-
self.max_steps = max_steps
|
|
44
|
-
self.repeats = repeats
|
|
45
|
-
self.video_folder = video_folder
|
|
46
|
-
self.render_every_eps = render_every_eps
|
|
47
|
-
|
|
48
|
-
def pre_main_callback(self):
|
|
49
|
-
# the `pre_main_callback` on the environment passed in is called before the start of the evolutionary strategies loop
|
|
50
|
-
|
|
51
|
-
rmtree(self.video_folder, ignore_errors = True)
|
|
52
|
-
|
|
53
|
-
if not self.vectorized:
|
|
54
|
-
self.env = gym.wrappers.RecordVideo(
|
|
55
|
-
env = self.env,
|
|
56
|
-
video_folder = self.video_folder,
|
|
57
|
-
name_prefix = 'recording',
|
|
58
|
-
episode_trigger = lambda eps_num: (eps_num % self.render_every_eps) == 0,
|
|
59
|
-
disable_logger = True
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
def forward(self, model):
|
|
63
|
-
|
|
64
|
-
device = next(model.parameters()).device
|
|
65
|
-
|
|
66
|
-
seed = torch.randint(0, int(1e6), ())
|
|
67
|
-
|
|
68
|
-
num_envs = self.num_envs if self.vectorized else 1
|
|
69
|
-
cum_reward = torch.zeros(num_envs, device = device)
|
|
70
|
-
|
|
71
|
-
for _ in range(self.repeats):
|
|
72
|
-
state, _ = self.env.reset(seed = seed.item())
|
|
73
|
-
|
|
74
|
-
step = 0
|
|
75
|
-
dones = torch.zeros(num_envs, device = device, dtype = torch.bool)
|
|
76
|
-
|
|
77
|
-
while step < self.max_steps and not dones.all():
|
|
78
|
-
|
|
79
|
-
state_torch = torch.from_numpy(state).to(device)
|
|
80
|
-
|
|
81
|
-
action_logits = model(state_torch)
|
|
82
|
-
|
|
83
|
-
action = F.gumbel_softmax(action_logits, hard = True).argmax(dim = -1)
|
|
84
|
-
|
|
85
|
-
next_state, reward, truncated, terminated, *_ = self.env.step(action.detach().cpu().numpy() if self.vectorized else action.item())
|
|
86
|
-
|
|
87
|
-
reward_np = np.array(reward) if not isinstance(reward, np.ndarray) else reward
|
|
88
|
-
total_reward = torch.from_numpy(reward_np).float().to(device)
|
|
89
|
-
|
|
90
|
-
mask = (~dones).float()
|
|
91
|
-
cum_reward += total_reward * mask
|
|
92
|
-
|
|
93
|
-
dones_np = np.array(truncated | terminated) if not isinstance(truncated | terminated, np.ndarray) else (truncated | terminated)
|
|
94
|
-
dones |= torch.from_numpy(dones_np).to(device)
|
|
95
|
-
|
|
96
|
-
step += 1
|
|
97
|
-
|
|
98
|
-
state = next_state
|
|
99
|
-
|
|
100
|
-
if not self.vectorized:
|
|
101
|
-
return cum_reward.item() / self.repeats
|
|
102
|
-
|
|
103
|
-
return cum_reward / self.repeats
|
|
104
|
-
|
|
105
|
-
# evo strategy
|
|
106
|
-
|
|
107
|
-
from x_evolution import EvoStrategy
|
|
108
|
-
|
|
109
|
-
def main(
|
|
110
|
-
vectorized = False,
|
|
111
|
-
num_envs = 8
|
|
112
|
-
):
|
|
113
|
-
actor = ResidualNormedMLP(dim_in = 8, dim = 24, depth = 2, residual_every = 1, dim_out = 4)
|
|
114
|
-
|
|
115
|
-
evo_strat = EvoStrategy(
|
|
116
|
-
actor,
|
|
117
|
-
environment = LunarEnvironment(
|
|
118
|
-
repeats = 2,
|
|
119
|
-
vectorized = vectorized,
|
|
120
|
-
num_envs = num_envs
|
|
121
|
-
),
|
|
122
|
-
vectorized = vectorized,
|
|
123
|
-
vector_size = num_envs,
|
|
124
|
-
num_generations = 50_000,
|
|
125
|
-
noise_population_size = 50,
|
|
126
|
-
noise_low_rank = 1,
|
|
127
|
-
noise_scale = 1e-2,
|
|
128
|
-
noise_scale_clamp_range = (5e-3, 2e-2),
|
|
129
|
-
learned_noise_scale = True,
|
|
130
|
-
use_sigma_optimizer = True,
|
|
131
|
-
learning_rate = 1e-3,
|
|
132
|
-
noise_scale_learning_rate = 1e-4,
|
|
133
|
-
use_scheduler = True,
|
|
134
|
-
scheduler_klass = CosineAnnealingLR,
|
|
135
|
-
scheduler_kwargs = dict(T_max = 50_000)
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
evo_strat()
|
|
139
|
-
|
|
140
|
-
if __name__ == '__main__':
|
|
141
|
-
fire.Fire(main)
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
# /// script
|
|
2
|
-
# dependencies = [
|
|
3
|
-
# "fire",
|
|
4
|
-
# "torchvision",
|
|
5
|
-
# "x-mlps-pytorch>=0.2.0",
|
|
6
|
-
# "x-evolution>=0.0.20"
|
|
7
|
-
# ]
|
|
8
|
-
# ///
|
|
9
|
-
|
|
10
|
-
import fire
|
|
11
|
-
import torch
|
|
12
|
-
from torch import nn
|
|
13
|
-
import torch.nn.functional as F
|
|
14
|
-
from torchvision import datasets, transforms
|
|
15
|
-
from torch.utils.data import DataLoader
|
|
16
|
-
|
|
17
|
-
# model
|
|
18
|
-
|
|
19
|
-
from x_mlps_pytorch.residual_normed_mlp import ResidualNormedMLP
|
|
20
|
-
|
|
21
|
-
model = nn.Sequential(
|
|
22
|
-
nn.Flatten(),
|
|
23
|
-
ResidualNormedMLP(dim_in = 784, dim = 512, depth = 8, residual_every = 2, dim_out = 10)
|
|
24
|
-
).half()
|
|
25
|
-
|
|
26
|
-
batch_size = 256
|
|
27
|
-
|
|
28
|
-
# data
|
|
29
|
-
|
|
30
|
-
dataset = datasets.MNIST('./data', train = True, download = True, transform = transforms.ToTensor())
|
|
31
|
-
|
|
32
|
-
# fitness as inverse of loss
|
|
33
|
-
|
|
34
|
-
def mnist_environment(
|
|
35
|
-
model,
|
|
36
|
-
num_envs = 1,
|
|
37
|
-
vectorized = False,
|
|
38
|
-
batch_size = 256
|
|
39
|
-
):
|
|
40
|
-
device = next(model.parameters()).device
|
|
41
|
-
|
|
42
|
-
iters = num_envs if vectorized else 1
|
|
43
|
-
|
|
44
|
-
losses = []
|
|
45
|
-
|
|
46
|
-
for _ in range(iters):
|
|
47
|
-
dataloader = DataLoader(dataset, batch_size = batch_size, shuffle = True)
|
|
48
|
-
data_iterator = iter(dataloader)
|
|
49
|
-
data, target = next(data_iterator)
|
|
50
|
-
|
|
51
|
-
data, target = data.to(device), target.to(device)
|
|
52
|
-
|
|
53
|
-
with torch.no_grad():
|
|
54
|
-
logits = model(data.half())
|
|
55
|
-
loss = F.cross_entropy(logits, target)
|
|
56
|
-
|
|
57
|
-
losses.append(-loss)
|
|
58
|
-
|
|
59
|
-
if not vectorized:
|
|
60
|
-
return losses[0]
|
|
61
|
-
|
|
62
|
-
return torch.stack(losses)
|
|
63
|
-
|
|
64
|
-
# evo
|
|
65
|
-
|
|
66
|
-
from x_evolution import EvoStrategy
|
|
67
|
-
|
|
68
|
-
def main(
|
|
69
|
-
vectorized = False,
|
|
70
|
-
num_envs = 8,
|
|
71
|
-
batch_size = 256
|
|
72
|
-
):
|
|
73
|
-
evo_strat = EvoStrategy(
|
|
74
|
-
model,
|
|
75
|
-
environment = lambda model: mnist_environment(model, num_envs = num_envs, vectorized = vectorized, batch_size = batch_size),
|
|
76
|
-
vectorized = vectorized,
|
|
77
|
-
vector_size = num_envs,
|
|
78
|
-
noise_population_size = 100,
|
|
79
|
-
noise_scale = 1e-2,
|
|
80
|
-
noise_scale_clamp_range = (8e-3, 2e-2),
|
|
81
|
-
noise_low_rank = 1,
|
|
82
|
-
num_generations = 10_000,
|
|
83
|
-
learning_rate = 1e-3,
|
|
84
|
-
learned_noise_scale = True,
|
|
85
|
-
noise_scale_learning_rate = 2e-5
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
evo_strat()
|
|
89
|
-
|
|
90
|
-
if __name__ == '__main__':
|
|
91
|
-
fire.Fire(main)
|
x_evolution-0.1.30/train_xor.py
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import fire
|
|
2
|
-
import torch
|
|
3
|
-
from torch import nn
|
|
4
|
-
import torch.nn.functional as F
|
|
5
|
-
from torch.optim.lr_scheduler import LambdaLR
|
|
6
|
-
|
|
7
|
-
# model
|
|
8
|
-
|
|
9
|
-
from torch import nn
|
|
10
|
-
|
|
11
|
-
model = nn.Sequential(
|
|
12
|
-
nn.Linear(2, 16),
|
|
13
|
-
nn.ReLU(),
|
|
14
|
-
nn.Linear(16, 2)
|
|
15
|
-
).half()
|
|
16
|
-
|
|
17
|
-
batch_size = 128
|
|
18
|
-
|
|
19
|
-
# fitness as inverse of loss
|
|
20
|
-
|
|
21
|
-
from x_evolution import EvoStrategy
|
|
22
|
-
|
|
23
|
-
def xor_environment(
|
|
24
|
-
model,
|
|
25
|
-
num_envs = 1,
|
|
26
|
-
vectorized = False,
|
|
27
|
-
batch_size = 128
|
|
28
|
-
):
|
|
29
|
-
device = next(model.parameters()).device
|
|
30
|
-
|
|
31
|
-
iters = num_envs if vectorized else 1
|
|
32
|
-
|
|
33
|
-
losses = []
|
|
34
|
-
|
|
35
|
-
for _ in range(iters):
|
|
36
|
-
data = torch.randint(0, 2, (batch_size, 2))
|
|
37
|
-
labels = data[:, 0] ^ data[:, 1]
|
|
38
|
-
|
|
39
|
-
data, labels = tuple(t.to(device) for t in (data, labels))
|
|
40
|
-
|
|
41
|
-
with torch.no_grad():
|
|
42
|
-
logits = model(data.half())
|
|
43
|
-
loss = F.cross_entropy(logits, labels)
|
|
44
|
-
|
|
45
|
-
losses.append(-loss)
|
|
46
|
-
|
|
47
|
-
if not vectorized:
|
|
48
|
-
return losses[0]
|
|
49
|
-
|
|
50
|
-
return torch.stack(losses)
|
|
51
|
-
|
|
52
|
-
# evo
|
|
53
|
-
|
|
54
|
-
def main(
|
|
55
|
-
vectorized = False,
|
|
56
|
-
num_envs = 8,
|
|
57
|
-
batch_size = 128
|
|
58
|
-
):
|
|
59
|
-
evo_strat = EvoStrategy(
|
|
60
|
-
model,
|
|
61
|
-
environment = lambda model: xor_environment(model, num_envs = num_envs, vectorized = vectorized, batch_size = batch_size),
|
|
62
|
-
vectorized = vectorized,
|
|
63
|
-
vector_size = num_envs,
|
|
64
|
-
noise_population_size = 100,
|
|
65
|
-
noise_low_rank = 1,
|
|
66
|
-
num_generations = 100_000,
|
|
67
|
-
learning_rate = 1e-1,
|
|
68
|
-
noise_scale = 1e-1,
|
|
69
|
-
noise_scale_clamp_range = (0.05, 0.2),
|
|
70
|
-
learned_noise_scale = True,
|
|
71
|
-
noise_scale_learning_rate = 5e-4,
|
|
72
|
-
use_scheduler = True,
|
|
73
|
-
scheduler_klass = LambdaLR,
|
|
74
|
-
scheduler_kwargs = dict(lr_lambda = lambda step: min(1., step / 10.)),
|
|
75
|
-
use_sigma_scheduler = True,
|
|
76
|
-
sigma_scheduler_klass = LambdaLR,
|
|
77
|
-
sigma_scheduler_kwargs = dict(lr_lambda = lambda step: min(1., step / 10.))
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
evo_strat()
|
|
81
|
-
|
|
82
|
-
if __name__ == '__main__':
|
|
83
|
-
fire.Fire(main)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|