sneat


Namesneat JSON
Version 1.0.1 PyPI version JSON
download
home_pagehttps://github.com/bhark/sNEAT
SummarySimplified implementation of NEAT (Neuro-Evolution of Augmenting Topologies).
upload_time2024-06-05 11:49:24
maintainerNone
docs_urlNone
authorAdrian E. Bratlann
requires_pythonNone
licenseGPLv3
keywords
VCS
bugtrack_url
requirements box2d-py certifi cffi charset-normalizer cloudpickle contourpy cryptography cycler docutils Farama-Notifications fonttools gymnasium idna importlib_metadata jaraco.classes jaraco.context jaraco.functools jeepney keyring kiwisolver markdown-it-py matplotlib mdurl more-itertools networkx nh3 numpy packaging pillow pkginfo pycparser pygame Pygments pyparsing python-dateutil readme_renderer requests requests-toolbelt rfc3986 rich SecretStorage setuptools six swig tabulate tqdm twine typing_extensions urllib3 wheel zipp
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # sNEAT (Simplified NEAT)

![GitHub Tag](https://img.shields.io/github/v/tag/bhark/sneat) 
![PyPI - Version](https://img.shields.io/pypi/v/sneat)
![PyPI - License](https://img.shields.io/pypi/l/sneat)

A simplified implementation of [Neuro-evolution of Augmenting Topologies](https://nn.cs.utexas.edu/downloads/papers/stanley.ec02.pdf), a novel technique for neuro-evolution developed by Kenneth O. Stanley. 

## Why? 

Another implementation of NEAT in Python already exists (by CodeReclaimers, [here](https://github.com/CodeReclaimers/neat-python)), which is thorough and really nice work. However, as things stand, it faces some deeper issues and thus doesn't perform in accordance with the benchmarks provided in the original paper, and is no longer maintained. This solution has cut down to the bone in an attempt to simplify both usage and the codebase, and to achieve the expected results. 

## How? 

In the simplest case, all you need to begin training a neural network for any given problem is a fitness function. 

**1. Install the package**

`$ pip install sneat`

**2. Set up your fitness function**

Your fitness function should take a genome and output a fitness score based on how well that genome solves the task. 
Here's a simple example, training a neural network that will output the sum of its inputs:

```
def fitness_function(genome):
    inputs = list(np.random.randint(5, size=2))
    target = sum(inputs)

    # feed input to the genomes neural network, return its output
    output = genome.activate(inputs) 

    difference = (output - target) ** 2
    fitness = 1 / difference
    return fitness
```

**3. Magic**

There's a bunch of hyperparameters that can (should) be configured for your given problem, but again we'll take a simple approach and just use the default hyperparameters along with the default evolution loop:

```
from sneat import evolve

def fitness_function(genome):
    ...

winner = evolve(fitness_function)
```

...now watch the generations unfold and enjoy! If you let it run long enough, we might get to experience our very own doomsday. The `evolve` function outputs the winning genome when one of the following three conditions are fulfilled: 

- The fitness threshold is reached by the best genome in the population
- The maximum number of generations are reached
- The user cancels (`CTRL+C`)

## How, but more? 

A default configuration file is supplied, but you'll probably want to change some details (such as the number of input and output nodes in the networks). You can include as few or as many configuration elements as you want; those you don't provide will fall back to the defaults. 

Create a `config.ini` file in your working directory with the settings you want to change. Here's the default config file for inspiration: 

```
[NeuralNetwork]
num_inputs = 2
num_outputs = 1
input_activation = linear
output_activation = sigmoid
use_normalizer = False

[Population]
population_size = 150
compatibility_threshold = 3.0
min_species_size = 5
elite_size = 3
survival_threshold = 0.2

[MutationRates]
add_node=0.1
add_connection=0.2
toggle_connection=0.08
change_weight=0.65
change_activation=0.05
change_bias=0.05
remove_node=0.08

[Evolution]
max_generations = 100 # set to 0 to disable
max_fitness = 4 # set to 0 to disable
```

## More control

If you want to have more control over the whole loop (for custom reporting, for example), I'd suggest importing the `Population` class and working around that. This class has `.reproduce()`, which will perform selection, cross-over and mutation on all genomes based on their fitness values. Finally, it will properly speciate the new genomes and move on to the next generation. 

`Population.species` is a list containing all the species, which in turn offers `Species.genomes`. I'll let you figure out the rest - the code is pretty straight-forward. 

## Running the examples

Some examples are included in this repo, using [Gymnasium](https://gymnasium.farama.org/index.html#). To run them, first install the examples dependencies:

`pip install sneat[examples]`

You can then run one of the examples to begin training; fx:

`python examples/lunar_lander/lunar_lander.py`

The winning genome will be saved as `winner.pkl`. If you want to see it in action, run the example with the name of the saved genome as the first argument:

`python examples/lunar_lander/lunar_lander.py winner.pkl`

## Results

Naturally, results vary with each run. On average, it takes about 170 generations to solve [Lunar Lander](https://gymnasium.farama.org/environments/box2d/lunar_lander/). [Bipedal Walker](https://gymnasium.farama.org/environments/box2d/bipedal_walker/) takes about 800 generations. I've never been able to solve any of these environments with other open-source NEAT implementations. 

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/bhark/sNEAT",
    "name": "sneat",
    "maintainer": null,
    "docs_url": null,
    "requires_python": null,
    "maintainer_email": null,
    "keywords": null,
    "author": "Adrian E. Bratlann",
    "author_email": "aeb@tetrabit.coop",
    "download_url": "https://files.pythonhosted.org/packages/ce/0b/2704a25432e7bbed1dc1642e465a811eb6549c1a37baf583ec8a5072067d/sneat-1.0.1.tar.gz",
    "platform": null,
    "description": "# sNEAT (Simplified NEAT)\n\n![GitHub Tag](https://img.shields.io/github/v/tag/bhark/sneat) \n![PyPI - Version](https://img.shields.io/pypi/v/sneat)\n![PyPI - License](https://img.shields.io/pypi/l/sneat)\n\nA simplified implementation of [Neuro-evolution of Augmenting Topologies](https://nn.cs.utexas.edu/downloads/papers/stanley.ec02.pdf), a novel technique for neuro-evolution developed by Kenneth O. Stanley. \n\n## Why? \n\nAnother implementation of NEAT in Python already exists (by CodeReclaimers, [here](https://github.com/CodeReclaimers/neat-python)), which is thorough and really nice work. However, as things stand, it faces some deeper issues and thus doesn't perform in accordance with the benchmarks provided in the original paper, and is no longer maintained. This solution has cut down to the bone in an attempt to simplify both usage and the codebase, and to achieve the expected results. \n\n## How? \n\nIn the simplest case, all you need to begin training a neural network for any given problem is a fitness function. \n\n**1. Install the package**\n\n`$ pip install sneat`\n\n**2. Set up your fitness function**\n\nYour fitness function should take a genome and output a fitness score based on how well that genome solves the task. \nHere's a simple example, training a neural network that will output the sum of its inputs:\n\n```\ndef fitness_function(genome):\n    inputs = list(np.random.randint(5, size=2))\n    target = sum(inputs)\n\n    # feed input to the genomes neural network, return its output\n    output = genome.activate(inputs) \n\n    difference = (output - target) ** 2\n    fitness = 1 / difference\n    return fitness\n```\n\n**3. Magic**\n\nThere's a bunch of hyperparameters that can (should) be configured for your given problem, but again we'll take a simple approach and just use the default hyperparameters along with the default evolution loop:\n\n```\nfrom sneat import evolve\n\ndef fitness_function(genome):\n    ...\n\nwinner = evolve(fitness_function)\n```\n\n...now watch the generations unfold and enjoy! If you let it run long enough, we might get to experience our very own doomsday. The `evolve` function outputs the winning genome when one of the following three conditions are fulfilled: \n\n- The fitness threshold is reached by the best genome in the population\n- The maximum number of generations are reached\n- The user cancels (`CTRL+C`)\n\n## How, but more? \n\nA default configuration file is supplied, but you'll probably want to change some details (such as the number of input and output nodes in the networks). You can include as few or as many configuration elements as you want; those you don't provide will fall back to the defaults. \n\nCreate a `config.ini` file in your working directory with the settings you want to change. Here's the default config file for inspiration: \n\n```\n[NeuralNetwork]\nnum_inputs = 2\nnum_outputs = 1\ninput_activation = linear\noutput_activation = sigmoid\nuse_normalizer = False\n\n[Population]\npopulation_size = 150\ncompatibility_threshold = 3.0\nmin_species_size = 5\nelite_size = 3\nsurvival_threshold = 0.2\n\n[MutationRates]\nadd_node=0.1\nadd_connection=0.2\ntoggle_connection=0.08\nchange_weight=0.65\nchange_activation=0.05\nchange_bias=0.05\nremove_node=0.08\n\n[Evolution]\nmax_generations = 100 # set to 0 to disable\nmax_fitness = 4 # set to 0 to disable\n```\n\n## More control\n\nIf you want to have more control over the whole loop (for custom reporting, for example), I'd suggest importing the `Population` class and working around that. This class has `.reproduce()`, which will perform selection, cross-over and mutation on all genomes based on their fitness values. Finally, it will properly speciate the new genomes and move on to the next generation. \n\n`Population.species` is a list containing all the species, which in turn offers `Species.genomes`. I'll let you figure out the rest - the code is pretty straight-forward. \n\n## Running the examples\n\nSome examples are included in this repo, using [Gymnasium](https://gymnasium.farama.org/index.html#). To run them, first install the examples dependencies:\n\n`pip install sneat[examples]`\n\nYou can then run one of the examples to begin training; fx:\n\n`python examples/lunar_lander/lunar_lander.py`\n\nThe winning genome will be saved as `winner.pkl`. If you want to see it in action, run the example with the name of the saved genome as the first argument:\n\n`python examples/lunar_lander/lunar_lander.py winner.pkl`\n\n## Results\n\nNaturally, results vary with each run. On average, it takes about 170 generations to solve [Lunar Lander](https://gymnasium.farama.org/environments/box2d/lunar_lander/). [Bipedal Walker](https://gymnasium.farama.org/environments/box2d/bipedal_walker/) takes about 800 generations. I've never been able to solve any of these environments with other open-source NEAT implementations. \n",
    "bugtrack_url": null,
    "license": "GPLv3",
    "summary": "Simplified implementation of NEAT (Neuro-Evolution of Augmenting Topologies).",
    "version": "1.0.1",
    "project_urls": {
        "Homepage": "https://github.com/bhark/sNEAT"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "6fecd4d5f7991f0f893dfdf373390224f82f753fab1e4b640d515151c9936606",
                "md5": "ee9d50f8919e481968afa0728a2cfb29",
                "sha256": "96976ba41fd4ebd7ecbc1bb3e899cf6a58242c353da608e7b0295cbfa25832c3"
            },
            "downloads": -1,
            "filename": "sneat-1.0.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "ee9d50f8919e481968afa0728a2cfb29",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 25287,
            "upload_time": "2024-06-05T11:49:22",
            "upload_time_iso_8601": "2024-06-05T11:49:22.385594Z",
            "url": "https://files.pythonhosted.org/packages/6f/ec/d4d5f7991f0f893dfdf373390224f82f753fab1e4b640d515151c9936606/sneat-1.0.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ce0b2704a25432e7bbed1dc1642e465a811eb6549c1a37baf583ec8a5072067d",
                "md5": "aa8e9b40be0a8ed75c5c339c83ce56a6",
                "sha256": "2ed9b61f0fdf4c9a7c4775e30cf845e916ad40ae2b0855b9ab98d65fcfca013e"
            },
            "downloads": -1,
            "filename": "sneat-1.0.1.tar.gz",
            "has_sig": false,
            "md5_digest": "aa8e9b40be0a8ed75c5c339c83ce56a6",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 25663,
            "upload_time": "2024-06-05T11:49:24",
            "upload_time_iso_8601": "2024-06-05T11:49:24.049872Z",
            "url": "https://files.pythonhosted.org/packages/ce/0b/2704a25432e7bbed1dc1642e465a811eb6549c1a37baf583ec8a5072067d/sneat-1.0.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-06-05 11:49:24",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "bhark",
    "github_project": "sNEAT",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [
        {
            "name": "box2d-py",
            "specs": [
                [
                    "==",
                    "2.3.5"
                ]
            ]
        },
        {
            "name": "certifi",
            "specs": [
                [
                    "==",
                    "2024.2.2"
                ]
            ]
        },
        {
            "name": "cffi",
            "specs": [
                [
                    "==",
                    "1.16.0"
                ]
            ]
        },
        {
            "name": "charset-normalizer",
            "specs": [
                [
                    "==",
                    "3.3.2"
                ]
            ]
        },
        {
            "name": "cloudpickle",
            "specs": [
                [
                    "==",
                    "3.0.0"
                ]
            ]
        },
        {
            "name": "contourpy",
            "specs": [
                [
                    "==",
                    "1.2.1"
                ]
            ]
        },
        {
            "name": "cryptography",
            "specs": [
                [
                    "==",
                    "42.0.7"
                ]
            ]
        },
        {
            "name": "cycler",
            "specs": [
                [
                    "==",
                    "0.12.1"
                ]
            ]
        },
        {
            "name": "docutils",
            "specs": [
                [
                    "==",
                    "0.21.2"
                ]
            ]
        },
        {
            "name": "Farama-Notifications",
            "specs": [
                [
                    "==",
                    "0.0.4"
                ]
            ]
        },
        {
            "name": "fonttools",
            "specs": [
                [
                    "==",
                    "4.52.4"
                ]
            ]
        },
        {
            "name": "gymnasium",
            "specs": [
                [
                    "==",
                    "0.29.1"
                ]
            ]
        },
        {
            "name": "idna",
            "specs": [
                [
                    "==",
                    "3.7"
                ]
            ]
        },
        {
            "name": "importlib_metadata",
            "specs": [
                [
                    "==",
                    "7.1.0"
                ]
            ]
        },
        {
            "name": "jaraco.classes",
            "specs": [
                [
                    "==",
                    "3.4.0"
                ]
            ]
        },
        {
            "name": "jaraco.context",
            "specs": [
                [
                    "==",
                    "5.3.0"
                ]
            ]
        },
        {
            "name": "jaraco.functools",
            "specs": [
                [
                    "==",
                    "4.0.1"
                ]
            ]
        },
        {
            "name": "jeepney",
            "specs": [
                [
                    "==",
                    "0.8.0"
                ]
            ]
        },
        {
            "name": "keyring",
            "specs": [
                [
                    "==",
                    "25.2.1"
                ]
            ]
        },
        {
            "name": "kiwisolver",
            "specs": [
                [
                    "==",
                    "1.4.5"
                ]
            ]
        },
        {
            "name": "markdown-it-py",
            "specs": [
                [
                    "==",
                    "3.0.0"
                ]
            ]
        },
        {
            "name": "matplotlib",
            "specs": [
                [
                    "==",
                    "3.9.0"
                ]
            ]
        },
        {
            "name": "mdurl",
            "specs": [
                [
                    "==",
                    "0.1.2"
                ]
            ]
        },
        {
            "name": "more-itertools",
            "specs": [
                [
                    "==",
                    "10.2.0"
                ]
            ]
        },
        {
            "name": "networkx",
            "specs": [
                [
                    "==",
                    "3.3"
                ]
            ]
        },
        {
            "name": "nh3",
            "specs": [
                [
                    "==",
                    "0.2.17"
                ]
            ]
        },
        {
            "name": "numpy",
            "specs": [
                [
                    "==",
                    "1.26.4"
                ]
            ]
        },
        {
            "name": "packaging",
            "specs": [
                [
                    "==",
                    "24.0"
                ]
            ]
        },
        {
            "name": "pillow",
            "specs": [
                [
                    "==",
                    "10.3.0"
                ]
            ]
        },
        {
            "name": "pkginfo",
            "specs": [
                [
                    "==",
                    "1.10.0"
                ]
            ]
        },
        {
            "name": "pycparser",
            "specs": [
                [
                    "==",
                    "2.22"
                ]
            ]
        },
        {
            "name": "pygame",
            "specs": [
                [
                    "==",
                    "2.5.2"
                ]
            ]
        },
        {
            "name": "Pygments",
            "specs": [
                [
                    "==",
                    "2.18.0"
                ]
            ]
        },
        {
            "name": "pyparsing",
            "specs": [
                [
                    "==",
                    "3.1.2"
                ]
            ]
        },
        {
            "name": "python-dateutil",
            "specs": [
                [
                    "==",
                    "2.9.0.post0"
                ]
            ]
        },
        {
            "name": "readme_renderer",
            "specs": [
                [
                    "==",
                    "43.0"
                ]
            ]
        },
        {
            "name": "requests",
            "specs": [
                [
                    "==",
                    "2.32.3"
                ]
            ]
        },
        {
            "name": "requests-toolbelt",
            "specs": [
                [
                    "==",
                    "1.0.0"
                ]
            ]
        },
        {
            "name": "rfc3986",
            "specs": [
                [
                    "==",
                    "2.0.0"
                ]
            ]
        },
        {
            "name": "rich",
            "specs": [
                [
                    "==",
                    "13.7.1"
                ]
            ]
        },
        {
            "name": "SecretStorage",
            "specs": [
                [
                    "==",
                    "3.3.3"
                ]
            ]
        },
        {
            "name": "setuptools",
            "specs": [
                [
                    "==",
                    "70.0.0"
                ]
            ]
        },
        {
            "name": "six",
            "specs": [
                [
                    "==",
                    "1.16.0"
                ]
            ]
        },
        {
            "name": "swig",
            "specs": [
                [
                    "==",
                    "4.2.1"
                ]
            ]
        },
        {
            "name": "tabulate",
            "specs": [
                [
                    "==",
                    "0.9.0"
                ]
            ]
        },
        {
            "name": "tqdm",
            "specs": [
                [
                    "==",
                    "4.66.4"
                ]
            ]
        },
        {
            "name": "twine",
            "specs": [
                [
                    "==",
                    "5.1.0"
                ]
            ]
        },
        {
            "name": "typing_extensions",
            "specs": [
                [
                    "==",
                    "4.12.1"
                ]
            ]
        },
        {
            "name": "urllib3",
            "specs": [
                [
                    "==",
                    "2.2.1"
                ]
            ]
        },
        {
            "name": "wheel",
            "specs": [
                [
                    "==",
                    "0.43.0"
                ]
            ]
        },
        {
            "name": "zipp",
            "specs": [
                [
                    "==",
                    "3.19.0"
                ]
            ]
        }
    ],
    "lcname": "sneat"
}
        
Elapsed time: 0.31369s