{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Neuroevolution for machine learning" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "EvoX provides solutions for supervised learning tasks based on neuroevolution, with key modules including [`SupervisedLearningProblem`](#evox.problems.neuroevolution.supervised_learning.SupervisedLearningProblem) and [`ParamsAndVector`](#evox.utils.parameters_and_vector.ParamsAndVector). Taking the MNIST classification task as an example, this section illustrates the neuroevolution process for supervised learning by adopting the modules of EvoX." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Basic Setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Basic component imports and device configuration serve as the essential starting steps for the neuroevolution process.\n", "\n", "Here, to ensure the reproducibility of results, a random seed can be optionally set." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import torch\n", "import torch.nn as nn\n", "\n", "from evox.utils import ParamsAndVector\n", "from evox.core import Algorithm, Mutable, Parameter, jit_class\n", "from evox.problems.neuroevolution.supervised_learning import SupervisedLearningProblem\n", "from evox.algorithms import PSO\n", "from evox.workflows import EvalMonitor, StdWorkflow\n", "\n", "\n", "# Set device\n", "device = \"cuda:0\" if torch.cuda.is_available() else \"cpu\"\n", "\n", "# Set random seed\n", "seed = 0\n", "torch.manual_seed(seed)\n", "torch.cuda.manual_seed_all(seed)\n", "torch.backends.cudnn.benchmark = False\n", "torch.backends.cudnn.deterministic = True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this step, a sample convolutional neural network (CNN) model is directly defined upon the PyTorch framework and then loaded onto the device." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Total number of model parameters: 412\n" ] } ], "source": [ "class SampleCNN(nn.Module):\n", " def __init__(self):\n", " super(SampleCNN, self).__init__()\n", " self.features = nn.Sequential(\n", " nn.Conv2d(1, 3, kernel_size=3, padding=1),\n", " nn.ReLU(),\n", " nn.MaxPool2d(kernel_size=2, stride=2),\n", " nn.Conv2d(3, 3, kernel_size=3),\n", " nn.ReLU(),\n", " nn.MaxPool2d(kernel_size=2, stride=2),\n", " nn.Conv2d(3, 3, kernel_size=3),\n", " nn.ReLU(),\n", " nn.Conv2d(3, 3, kernel_size=3),\n", " nn.ReLU(),\n", " )\n", " self.classifier = nn.Sequential(nn.Flatten(), nn.Linear(12, 10))\n", "\n", " def forward(self, x):\n", " x = self.features(x)\n", " x = self.classifier(x)\n", " return x\n", "\n", "\n", "model = SampleCNN().to(device)\n", "\n", "total_params = sum(p.numel() for p in model.parameters())\n", "print(f\"Total number of model parameters: {total_params}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Setting dataset implies the selection of the task. The data loader now needs to be initialized based on PyTorch's built-in support.\n", "Here, the package `torchvision` must be installed in advance depending on your PyTorch version, if it is not already available.\n", "\n", "In case the MNIST dataset is not already present in the `data_root` directory, the `download=True` flag is set to ensure that the dataset will be automatically downloaded. Therefore, the setup may take some time during the first run." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "import os\n", "import torchvision\n", "\n", "\n", "data_root = \"./data\" # Choose a path to save dataset\n", "os.makedirs(data_root, exist_ok=True)\n", "train_dataset = torchvision.datasets.MNIST(\n", " root=data_root,\n", " train=True,\n", " download=True,\n", " transform=torchvision.transforms.ToTensor(),\n", ")\n", "test_dataset = torchvision.datasets.MNIST(\n", " root=data_root,\n", " train=False,\n", " download=True,\n", " transform=torchvision.transforms.ToTensor(),\n", ")\n", "\n", "\n", "BATCH_SIZE = 100\n", "train_loader = torch.utils.data.DataLoader(\n", " train_dataset,\n", " batch_size=BATCH_SIZE,\n", " shuffle=True,\n", " collate_fn=None,\n", ")\n", "test_loader = torch.utils.data.DataLoader(\n", " test_dataset,\n", " batch_size=BATCH_SIZE,\n", " shuffle=False,\n", " collate_fn=None,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To accelerate subsequent processes, all MNIST data are pre-loaded for faster execution. Below, three datasets are pre-loaded for different stages – gradient descent training, neuroevolution fine-tuning, and model testing.\n", "\n", "It should be noted that this is an optional operation that trades space for time. Its adoption depends on your GPU capacity, and it will always take some time to prepare." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# Used for gradient descent training process\n", "pre_gd_train_loader = tuple([(inputs.to(device), labels.to(device)) for inputs, labels in train_loader])\n", "\n", "# Used for neuroevolution fine-tuning process\n", "pre_ne_train_loader = tuple(\n", " [\n", " (\n", " inputs.to(device),\n", " labels.type(torch.float).unsqueeze(1).repeat(1, 10).to(device),\n", " )\n", " for inputs, labels in train_loader\n", " ]\n", ")\n", "\n", "# Used for model testing process\n", "pre_test_loader = tuple([(inputs.to(device), labels.to(device)) for inputs, labels in test_loader])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, a `model_test` function is pre-defined to simplify the evaluation of the model's prediction accuracy on the test dataset during subsequent stages." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "def model_test(model: nn.Module, data_loader: torch.utils.data.DataLoader, device: torch.device) -> float:\n", " model.eval()\n", " with torch.no_grad():\n", " total = 0\n", " correct = 0\n", " for inputs, labels in data_loader:\n", " inputs: torch.Tensor = inputs.to(device=device, non_blocking=True)\n", " labels: torch.Tensor = labels.to(device=device, non_blocking=True)\n", "\n", " logits = model(inputs)\n", " _, predicted = torch.max(logits.data, dim=1)\n", " total += labels.size(0)\n", " correct += (predicted == labels).sum().item()\n", " acc = 100 * correct / total\n", " return acc" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Gradient Descent Training (Optional)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The gradient descent based model training is performed first. In this example, this training is adopted to initialize the model, preparing it for subsequent neuroevolution processes. \n", "\n", "The model training process in PyTorch is compatible with neuroevolution in EvoX, making it convenient to reuse the same model implementation for further steps." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def model_train(\n", " model: nn.Module,\n", " data_loader: torch.utils.data.DataLoader,\n", " criterion: nn.Module,\n", " optimizer: torch.optim.Optimizer,\n", " max_epoch: int,\n", " device: torch.device,\n", " print_frequent: int = -1,\n", ") -> nn.Module:\n", " model.train()\n", " for epoch in range(max_epoch):\n", " running_loss = 0.0\n", " for step, (inputs, labels) in enumerate(data_loader, start=1):\n", " inputs: torch.Tensor = inputs.to(device=device, non_blocking=True)\n", " labels: torch.Tensor = labels.to(device=device, non_blocking=True)\n", "\n", " optimizer.zero_grad()\n", " logits = model(inputs)\n", " loss = criterion(logits, labels)\n", " loss.backward()\n", " optimizer.step()\n", "\n", " running_loss += loss.item()\n", " if print_frequent > 0 and step % print_frequent == 0:\n", " print(f\"[Epoch {epoch:2d}, step {step:4d}] running loss: {running_loss:.4f} \")\n", " running_loss = 0.0\n", " return model" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/opt/conda/envs/py_3.10/lib/python3.10/site-packages/torch/nn/modules/linear.py:125: UserWarning: Attempting to use hipBLASLt on an unsupported architecture! Overriding blas backend to hipblas (Triggered internally at ../aten/src/ATen/Context.cpp:296.)\n", " return F.linear(input, self.weight, self.bias)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "[Epoch 0, step 500] running loss: 394.9020 \n", "[Epoch 1, step 500] running loss: 231.2396 \n", "[Epoch 2, step 500] running loss: 206.0878 \n", "Accuracy after gradient descent training: 89.1500 %.\n" ] } ], "source": [ "model_train(\n", " model,\n", " data_loader=pre_gd_train_loader,\n", " criterion=nn.CrossEntropyLoss(),\n", " optimizer=torch.optim.Adam(model.parameters(), lr=1e-2),\n", " max_epoch=3,\n", " device=device,\n", " print_frequent=500,\n", ")\n", "\n", "gd_acc = model_test(model, pre_test_loader, device)\n", "print(f\"Accuracy after gradient descent training: {gd_acc:.4f} %.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Neuroevolution Fine-Tuning" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Based on the pre-trained model from the previous gradient descent process, neuroevolution is progressively applied to fine-tune the model.\n", "\n", "First, the [`ParamsAndVector`](#evox.utils.parameters_and_vector.ParamsAndVector) component is used to flatten the weights of the pre-trained model into a vector, which serves as the initial center individual for the subsequent neuroevolution process." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "adapter = ParamsAndVector(dummy_model=model)\n", "model_params = dict(model.named_parameters())\n", "pop_center = adapter.to_vector(model_params)\n", "lower_bound = pop_center - 0.01\n", "upper_bound = pop_center + 0.01" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> In case of algorithms specifically designed for neuroevolution, which can directly accept a dictionary of batched parameters as input, the usage of [`ParamsAndVector`](#evox.utils.parameters_and_vector.ParamsAndVector) can be unnecessary." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Additionally, a sample criterion is defined. Here, both the loss and accuracy of the individual model are selected and weighted to serve as the fitness function in the neuroevolution process. This step is customizable to suit the optimization direction." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "class AccuracyCriterion(nn.Module):\n", " def __init__(self, data_loader):\n", " super().__init__()\n", " data_loader = data_loader\n", "\n", " def forward(self, logits, labels):\n", " _, predicted = torch.max(logits, dim=1)\n", " correct = (predicted == labels[:, 0]).sum()\n", " fitness = -correct\n", " return fitness\n", "\n", "\n", "acc_criterion = AccuracyCriterion(pre_ne_train_loader)\n", "loss_criterion = nn.MSELoss()\n", "\n", "\n", "class WeightedCriterion(nn.Module):\n", " def __init__(self, loss_weight, loss_criterion, acc_weight, acc_criterion):\n", " super().__init__()\n", " self.loss_weight = loss_weight\n", " self.loss_criterion = loss_criterion\n", " self.acc_weight = acc_weight\n", " self.acc_criterion = acc_criterion\n", "\n", " def forward(self, logits, labels):\n", " weighted_loss = self.loss_weight * loss_criterion(logits, labels)\n", " weighted_acc = self.acc_weight * acc_criterion(logits, labels)\n", " return weighted_loss + weighted_acc\n", "\n", "\n", "weighted_criterion = WeightedCriterion(\n", " loss_weight=0.5,\n", " loss_criterion=loss_criterion,\n", " acc_weight=0.5,\n", " acc_criterion=acc_criterion,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "At the same time, similar to the gradient descent training and model testing processes, the neuroevolution fine-tuning process is also encapsulated into a function for convenient use in subsequent stages." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "import time\n", "\n", "\n", "def neuroevolution_process(\n", " workflow: StdWorkflow,\n", " adapter: ParamsAndVector,\n", " model: nn.Module,\n", " test_loader: torch.utils.data.DataLoader,\n", " device: torch.device,\n", " best_acc: float,\n", " max_generation: int = 2,\n", ") -> None:\n", " for index in range(max_generation):\n", " print(f\"In generation {index}:\")\n", " t = time.time()\n", " workflow.step()\n", " print(f\"\\tTime elapsed: {time.time() - t: .4f}(s).\")\n", "\n", " monitor = workflow.get_submodule(\"monitor\")\n", " print(f\"\\tTop fitness: {monitor.topk_fitness}\")\n", " best_params = adapter.to_params(monitor.topk_solutions[0])\n", " model.load_state_dict(best_params)\n", " acc = model_test(model, test_loader, device)\n", " if acc > best_acc:\n", " best_acc = acc\n", " print(f\"\\tBest accuracy: {best_acc:.4f} %.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Population-Based Neuroevolution Test" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this example, the population-based algorithm for neuroevolution is tested first, using Particle Swarm Optimization ([PSO](#evox.algorithms.so.pso_variants.pso.PSO)) as a representation. The configuration for neuroevolution is similar to that of other optimization tasks – we need to define the problem, algorithm, monitor, and workflow, along with their respective `setup()` functions to complete the initialization.\n", "\n", "A key point to note here is that the population size (`POP_SIZE` in this case) needs to be initialized in **both the problem and the algorithm** to avoid potential errors." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "POP_SIZE = 100\n", "vmapped_problem = SupervisedLearningProblem(\n", " model=model,\n", " data_loader=pre_ne_train_loader,\n", " criterion=weighted_criterion,\n", " pop_size=POP_SIZE,\n", " device=device,\n", ")\n", "vmapped_problem.setup()\n", "\n", "pop_algorithm = PSO(\n", " pop_size=POP_SIZE,\n", " lb=lower_bound,\n", " ub=upper_bound,\n", " device=device,\n", ")\n", "pop_algorithm.setup()\n", "\n", "monitor = EvalMonitor(\n", " topk=3,\n", " device=device,\n", ")\n", "monitor.setup()\n", "\n", "pop_workflow = StdWorkflow()\n", "pop_workflow.setup(\n", " algorithm=pop_algorithm,\n", " problem=vmapped_problem,\n", " solution_transform=adapter,\n", " monitor=monitor,\n", " device=device,\n", ")" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Upon gradient descent, the population-based neuroevolution process start. \n", "In generation 0:\n", "\tTime elapsed: 3.7248(s).\n", "\tTop fitness: tensor([0.8886, 1.4018, 1.6117], device='cuda:0')\n", "\tBest accuracy: 89.3000 %.\n", "In generation 1:\n", "\tTime elapsed: 4.2092(s).\n", "\tTop fitness: tensor([0.4963, 0.6073, 0.6423], device='cuda:0')\n", "\tBest accuracy: 89.3000 %.\n", "In generation 2:\n", "\tTime elapsed: 3.8874(s).\n", "\tTop fitness: tensor([0.4963, 0.6073, 0.6423], device='cuda:0')\n", "\tBest accuracy: 89.3000 %.\n", "In generation 3:\n", "\tTime elapsed: 3.5654(s).\n", "\tTop fitness: tensor([0.4963, 0.6073, 0.6423], device='cuda:0')\n", "\tBest accuracy: 89.3000 %.\n", "In generation 4:\n", "\tTime elapsed: 3.3940(s).\n", "\tTop fitness: tensor([0.4963, 0.6073, 0.6423], device='cuda:0')\n", "\tBest accuracy: 89.3000 %.\n", "In generation 5:\n", "\tTime elapsed: 3.4152(s).\n", "\tTop fitness: tensor([0.4963, 0.6073, 0.6423], device='cuda:0')\n", "\tBest accuracy: 89.3000 %.\n", "In generation 6:\n", "\tTime elapsed: 3.2818(s).\n", "\tTop fitness: tensor([0.4963, 0.6073, 0.6423], device='cuda:0')\n", "\tBest accuracy: 89.3000 %.\n", "In generation 7:\n", "\tTime elapsed: 3.0669(s).\n", "\tTop fitness: tensor([0.4963, 0.6073, 0.6423], device='cuda:0')\n", "\tBest accuracy: 89.3000 %.\n", "In generation 8:\n", "\tTime elapsed: 3.1275(s).\n", "\tTop fitness: tensor([0.4963, 0.6073, 0.6423], device='cuda:0')\n", "\tBest accuracy: 89.3000 %.\n", "In generation 9:\n", "\tTime elapsed: 3.1362(s).\n", "\tTop fitness: tensor([0.4963, 0.6073, 0.6423], device='cuda:0')\n", "\tBest accuracy: 89.3000 %.\n" ] } ], "source": [ "print(\"Upon gradient descent, the population-based neuroevolution process start. \")\n", "neuroevolution_process(\n", " workflow=pop_workflow,\n", " adapter=adapter,\n", " model=model,\n", " test_loader=pre_test_loader,\n", " device=device,\n", " best_acc=gd_acc,\n", " max_generation=10,\n", ")" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/html": [ " \n", " " ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.plotly.v1+json": { "config": { "plotlyServerURL": "https://plot.ly" }, "data": [ { "mode": "lines", "name": "Min", "showlegend": true, "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "y": [ 0.888567328453064, 0.49633949995040894, 5.6953816413879395, 5.732114791870117, 5.717602729797363, 5.696855068206787, 5.667969226837158, 5.670314311981201, 5.692254543304443, 5.723080158233643 ] }, { "mode": "lines", "name": "Max", "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "y": [ 5.421546459197998, 5.485681533813477, 6.85855770111084, 7.67168664932251, 7.467112064361572, 7.243370532989502, 7.342390537261963, 7.7736430168151855, 7.3163886070251465, 7.422550201416016 ] }, { "mode": "lines", "name": "Median", "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "y": [ 3.535249710083008, 2.637660026550293, 6.081429481506348, 6.325860977172852, 6.281477928161621, 6.274107456207275, 6.21946907043457, 6.316266059875488, 6.285235404968262, 6.317729949951172 ] }, { "mode": "lines", "name": "Average", "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "y": [ 3.4800467491149902, 2.7548327445983887, 6.116291046142578, 6.405825614929199, 6.3487348556518555, 6.346571445465088, 6.319439888000488, 6.396215915679932, 6.341086387634277, 6.361761569976807 ] } ], "frames": [ { "data": [ { "mode": "lines", "name": "Min", "showlegend": true, "type": "scatter", "x": [ 0 ], "y": [ 0.888567328453064 ] }, { "mode": "lines", "name": "Max", "type": "scatter", "x": [ 0 ], "y": [ 5.421546459197998 ] }, { "mode": "lines", "name": "Median", "type": "scatter", "x": [ 0 ], "y": [ 3.535249710083008 ] }, { "mode": "lines", "name": "Average", "type": "scatter", "x": [ 0 ], "y": [ 3.4800467491149902 ] } ], "name": "0" }, { "data": [ { "mode": "lines", "name": "Min", "showlegend": true, "type": "scatter", "x": [ 0, 1 ], "y": [ 0.888567328453064, 0.49633949995040894 ] }, { "mode": "lines", "name": "Max", "type": "scatter", "x": [ 0, 1 ], "y": [ 5.421546459197998, 5.485681533813477 ] }, { "mode": "lines", "name": "Median", "type": "scatter", "x": [ 0, 1 ], "y": [ 3.535249710083008, 2.637660026550293 ] }, { "mode": "lines", "name": "Average", "type": "scatter", "x": [ 0, 1 ], "y": [ 3.4800467491149902, 2.7548327445983887 ] } ], "name": "1" }, { "data": [ { "mode": "lines", "name": "Min", "showlegend": true, "type": "scatter", "x": [ 0, 1, 2 ], "y": [ 0.888567328453064, 0.49633949995040894, 5.6953816413879395 ] }, { "mode": "lines", "name": "Max", "type": "scatter", "x": [ 0, 1, 2 ], "y": [ 5.421546459197998, 5.485681533813477, 6.85855770111084 ] }, { "mode": "lines", "name": "Median", "type": "scatter", "x": [ 0, 1, 2 ], "y": [ 3.535249710083008, 2.637660026550293, 6.081429481506348 ] }, { "mode": "lines", "name": "Average", "type": "scatter", "x": [ 0, 1, 2 ], "y": [ 3.4800467491149902, 2.7548327445983887, 6.116291046142578 ] } ], "name": "2" }, { "data": [ { "mode": "lines", "name": "Min", "showlegend": true, "type": "scatter", "x": [ 0, 1, 2, 3 ], "y": [ 0.888567328453064, 0.49633949995040894, 5.6953816413879395, 5.732114791870117 ] }, { "mode": "lines", "name": "Max", "type": "scatter", "x": [ 0, 1, 2, 3 ], "y": [ 5.421546459197998, 5.485681533813477, 6.85855770111084, 7.67168664932251 ] }, { "mode": "lines", "name": "Median", "type": "scatter", "x": [ 0, 1, 2, 3 ], "y": [ 3.535249710083008, 2.637660026550293, 6.081429481506348, 6.325860977172852 ] }, { "mode": "lines", "name": "Average", "type": "scatter", "x": [ 0, 1, 2, 3 ], "y": [ 3.4800467491149902, 2.7548327445983887, 6.116291046142578, 6.405825614929199 ] } ], "name": "3" }, { "data": [ { "mode": "lines", "name": "Min", "showlegend": true, "type": "scatter", "x": [ 0, 1, 2, 3, 4 ], "y": [ 0.888567328453064, 0.49633949995040894, 5.6953816413879395, 5.732114791870117, 5.717602729797363 ] }, { "mode": "lines", "name": "Max", "type": "scatter", "x": [ 0, 1, 2, 3, 4 ], "y": [ 5.421546459197998, 5.485681533813477, 6.85855770111084, 7.67168664932251, 7.467112064361572 ] }, { "mode": "lines", "name": "Median", "type": "scatter", "x": [ 0, 1, 2, 3, 4 ], "y": [ 3.535249710083008, 2.637660026550293, 6.081429481506348, 6.325860977172852, 6.281477928161621 ] }, { "mode": "lines", "name": "Average", "type": "scatter", "x": [ 0, 1, 2, 3, 4 ], "y": [ 3.4800467491149902, 2.7548327445983887, 6.116291046142578, 6.405825614929199, 6.3487348556518555 ] } ], "name": "4" }, { "data": [ { "mode": "lines", "name": "Min", "showlegend": true, "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5 ], "y": [ 0.888567328453064, 0.49633949995040894, 5.6953816413879395, 5.732114791870117, 5.717602729797363, 5.696855068206787 ] }, { "mode": "lines", "name": "Max", "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5 ], "y": [ 5.421546459197998, 5.485681533813477, 6.85855770111084, 7.67168664932251, 7.467112064361572, 7.243370532989502 ] }, { "mode": "lines", "name": "Median", "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5 ], "y": [ 3.535249710083008, 2.637660026550293, 6.081429481506348, 6.325860977172852, 6.281477928161621, 6.274107456207275 ] }, { "mode": "lines", "name": "Average", "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5 ], "y": [ 3.4800467491149902, 2.7548327445983887, 6.116291046142578, 6.405825614929199, 6.3487348556518555, 6.346571445465088 ] } ], "name": "5" }, { "data": [ { "mode": "lines", "name": "Min", "showlegend": true, "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6 ], "y": [ 0.888567328453064, 0.49633949995040894, 5.6953816413879395, 5.732114791870117, 5.717602729797363, 5.696855068206787, 5.667969226837158 ] }, { "mode": "lines", "name": "Max", "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6 ], "y": [ 5.421546459197998, 5.485681533813477, 6.85855770111084, 7.67168664932251, 7.467112064361572, 7.243370532989502, 7.342390537261963 ] }, { "mode": "lines", "name": "Median", "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6 ], "y": [ 3.535249710083008, 2.637660026550293, 6.081429481506348, 6.325860977172852, 6.281477928161621, 6.274107456207275, 6.21946907043457 ] }, { "mode": "lines", "name": "Average", "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6 ], "y": [ 3.4800467491149902, 2.7548327445983887, 6.116291046142578, 6.405825614929199, 6.3487348556518555, 6.346571445465088, 6.319439888000488 ] } ], "name": "6" }, { "data": [ { "mode": "lines", "name": "Min", "showlegend": true, "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6, 7 ], "y": [ 0.888567328453064, 0.49633949995040894, 5.6953816413879395, 5.732114791870117, 5.717602729797363, 5.696855068206787, 5.667969226837158, 5.670314311981201 ] }, { "mode": "lines", "name": "Max", "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6, 7 ], "y": [ 5.421546459197998, 5.485681533813477, 6.85855770111084, 7.67168664932251, 7.467112064361572, 7.243370532989502, 7.342390537261963, 7.7736430168151855 ] }, { "mode": "lines", "name": "Median", "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6, 7 ], "y": [ 3.535249710083008, 2.637660026550293, 6.081429481506348, 6.325860977172852, 6.281477928161621, 6.274107456207275, 6.21946907043457, 6.316266059875488 ] }, { "mode": "lines", "name": "Average", "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6, 7 ], "y": [ 3.4800467491149902, 2.7548327445983887, 6.116291046142578, 6.405825614929199, 6.3487348556518555, 6.346571445465088, 6.319439888000488, 6.396215915679932 ] } ], "name": "7" }, { "data": [ { "mode": "lines", "name": "Min", "showlegend": true, "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ], "y": [ 0.888567328453064, 0.49633949995040894, 5.6953816413879395, 5.732114791870117, 5.717602729797363, 5.696855068206787, 5.667969226837158, 5.670314311981201, 5.692254543304443 ] }, { "mode": "lines", "name": "Max", "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ], "y": [ 5.421546459197998, 5.485681533813477, 6.85855770111084, 7.67168664932251, 7.467112064361572, 7.243370532989502, 7.342390537261963, 7.7736430168151855, 7.3163886070251465 ] }, { "mode": "lines", "name": "Median", "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ], "y": [ 3.535249710083008, 2.637660026550293, 6.081429481506348, 6.325860977172852, 6.281477928161621, 6.274107456207275, 6.21946907043457, 6.316266059875488, 6.285235404968262 ] }, { "mode": "lines", "name": "Average", "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ], "y": [ 3.4800467491149902, 2.7548327445983887, 6.116291046142578, 6.405825614929199, 6.3487348556518555, 6.346571445465088, 6.319439888000488, 6.396215915679932, 6.341086387634277 ] } ], "name": "8" }, { "data": [ { "mode": "lines", "name": "Min", "showlegend": true, "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "y": [ 0.888567328453064, 0.49633949995040894, 5.6953816413879395, 5.732114791870117, 5.717602729797363, 5.696855068206787, 5.667969226837158, 5.670314311981201, 5.692254543304443, 5.723080158233643 ] }, { "mode": "lines", "name": "Max", "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "y": [ 5.421546459197998, 5.485681533813477, 6.85855770111084, 7.67168664932251, 7.467112064361572, 7.243370532989502, 7.342390537261963, 7.7736430168151855, 7.3163886070251465, 7.422550201416016 ] }, { "mode": "lines", "name": "Median", "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "y": [ 3.535249710083008, 2.637660026550293, 6.081429481506348, 6.325860977172852, 6.281477928161621, 6.274107456207275, 6.21946907043457, 6.316266059875488, 6.285235404968262, 6.317729949951172 ] }, { "mode": "lines", "name": "Average", "type": "scatter", "x": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "y": [ 3.4800467491149902, 2.7548327445983887, 6.116291046142578, 6.405825614929199, 6.3487348556518555, 6.346571445465088, 6.319439888000488, 6.396215915679932, 6.341086387634277, 6.361761569976807 ] } ], "name": "9" } ], "layout": { "legend": { "x": 1, "xanchor": "auto", "y": 1 }, "margin": { "b": 0, "l": 0, "r": 0, "t": 0 }, "sliders": [ { "currentvalue": { "prefix": "Generation: " }, "len": 0.8, "pad": { "b": 1, "t": 10 }, "steps": [ { "args": [ [ "0" ], { "frame": { "duration": 200, "redraw": false }, "mode": "immediate", "transition": { "duration": 200 } } ], "label": "0", "method": "animate" }, { "args": [ [ "1" ], { "frame": { "duration": 200, "redraw": false }, "mode": "immediate", "transition": { "duration": 200 } } ], "label": "1", "method": "animate" }, { "args": [ [ "2" ], { "frame": { "duration": 200, "redraw": false }, "mode": "immediate", "transition": { "duration": 200 } } ], "label": "2", "method": "animate" }, { "args": [ [ "3" ], { "frame": { "duration": 200, "redraw": false }, "mode": "immediate", "transition": { "duration": 200 } } ], "label": "3", "method": "animate" }, { "args": [ [ "4" ], { "frame": { "duration": 200, "redraw": false }, "mode": "immediate", "transition": { "duration": 200 } } ], "label": "4", "method": "animate" }, { "args": [ [ "5" ], { "frame": { "duration": 200, "redraw": false }, "mode": "immediate", "transition": { "duration": 200 } } ], "label": "5", "method": "animate" }, { "args": [ [ "6" ], { "frame": { "duration": 200, "redraw": false }, "mode": "immediate", "transition": { "duration": 200 } } ], "label": "6", "method": "animate" }, { "args": [ [ "7" ], { "frame": { "duration": 200, "redraw": false }, "mode": "immediate", "transition": { "duration": 200 } } ], "label": "7", "method": "animate" }, { "args": [ [ "8" ], { "frame": { "duration": 200, "redraw": false }, "mode": "immediate", "transition": { "duration": 200 } } ], "label": "8", "method": "animate" }, { "args": [ [ "9" ], { "frame": { "duration": 200, "redraw": false }, "mode": "immediate", "transition": { "duration": 200 } } ], "label": "9", "method": "animate" } ], "x": 0.2, "xanchor": "left", "y": 0, "yanchor": "top" } ], "template": { "data": { "bar": [ { "error_x": { "color": "#2a3f5f" }, "error_y": { "color": "#2a3f5f" }, "marker": { "line": { "color": "#E5ECF6", "width": 0.5 }, "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "bar" } ], "barpolar": [ { "marker": { "line": { "color": "#E5ECF6", "width": 0.5 }, "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "barpolar" } ], "carpet": [ { "aaxis": { "endlinecolor": "#2a3f5f", "gridcolor": "white", "linecolor": "white", "minorgridcolor": "white", "startlinecolor": "#2a3f5f" }, "baxis": { "endlinecolor": "#2a3f5f", "gridcolor": "white", "linecolor": "white", "minorgridcolor": "white", "startlinecolor": "#2a3f5f" }, "type": "carpet" } ], "choropleth": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "choropleth" } ], "contour": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1, "#f0f921" ] ], "type": "contour" } ], "contourcarpet": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "contourcarpet" } ], "heatmap": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1, "#f0f921" ] ], "type": "heatmap" } ], "heatmapgl": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1, "#f0f921" ] ], "type": "heatmapgl" } ], "histogram": [ { "marker": { "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "histogram" } ], "histogram2d": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1, "#f0f921" ] ], "type": "histogram2d" } ], "histogram2dcontour": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1, "#f0f921" ] ], "type": "histogram2dcontour" } ], "mesh3d": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "mesh3d" } ], "parcoords": [ { "line": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "parcoords" } ], "pie": [ { "automargin": true, "type": "pie" } ], "scatter": [ { "fillpattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 }, "type": "scatter" } ], "scatter3d": [ { "line": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatter3d" } ], "scattercarpet": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattercarpet" } ], "scattergeo": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattergeo" } ], "scattergl": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattergl" } ], "scattermapbox": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattermapbox" } ], "scatterpolar": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterpolar" } ], "scatterpolargl": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterpolargl" } ], "scatterternary": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterternary" } ], "surface": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1, "#f0f921" ] ], "type": "surface" } ], "table": [ { "cells": { "fill": { "color": "#EBF0F8" }, "line": { "color": "white" } }, "header": { "fill": { "color": "#C8D4E3" }, "line": { "color": "white" } }, "type": "table" } ] }, "layout": { "annotationdefaults": { "arrowcolor": "#2a3f5f", "arrowhead": 0, "arrowwidth": 1 }, "autotypenumbers": "strict", "coloraxis": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "colorscale": { "diverging": [ [ 0, "#8e0152" ], [ 0.1, "#c51b7d" ], [ 0.2, "#de77ae" ], [ 0.3, "#f1b6da" ], [ 0.4, "#fde0ef" ], [ 0.5, "#f7f7f7" ], [ 0.6, "#e6f5d0" ], [ 0.7, "#b8e186" ], [ 0.8, "#7fbc41" ], [ 0.9, "#4d9221" ], [ 1, "#276419" ] ], "sequential": [ [ 0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1, "#f0f921" ] ], "sequentialminus": [ [ 0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1, "#f0f921" ] ] }, "colorway": [ "#636efa", "#EF553B", "#00cc96", "#ab63fa", "#FFA15A", "#19d3f3", "#FF6692", "#B6E880", "#FF97FF", "#FECB52" ], "font": { "color": "#2a3f5f" }, "geo": { "bgcolor": "white", "lakecolor": "white", "landcolor": "#E5ECF6", "showlakes": true, "showland": true, "subunitcolor": "white" }, "hoverlabel": { "align": "left" }, "hovermode": "closest", "mapbox": { "style": "light" }, "paper_bgcolor": "white", "plot_bgcolor": "#E5ECF6", "polar": { "angularaxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" }, "bgcolor": "#E5ECF6", "radialaxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" } }, "scene": { "xaxis": { "backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white" }, "yaxis": { "backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white" }, "zaxis": { "backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white" } }, "shapedefaults": { "line": { "color": "#2a3f5f" } }, "ternary": { "aaxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" }, "baxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" }, "bgcolor": "#E5ECF6", "caxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" } }, "title": { "x": 0.05 }, "xaxis": { "automargin": true, "gridcolor": "white", "linecolor": "white", "ticks": "", "title": { "standoff": 15 }, "zerolinecolor": "white", "zerolinewidth": 2 }, "yaxis": { "automargin": true, "gridcolor": "white", "linecolor": "white", "ticks": "", "title": { "standoff": 15 }, "zerolinecolor": "white", "zerolinewidth": 2 } } }, "updatemenus": [ { "buttons": [ { "args": [ null, { "frame": { "duration": 200, "redraw": false }, "fromcurrent": true } ], "label": "Play", "method": "animate" }, { "args": [ [ null ], { "frame": { "duration": 0, "redraw": false }, "mode": "immediate" } ], "label": "Pause", "method": "animate" } ], "direction": "left", "pad": { "r": 10, "t": 30 }, "type": "buttons", "x": 0.2, "xanchor": "right", "y": 0, "yanchor": "top" } ], "xaxis": { "autorange": false, "range": [ 0, 10 ] }, "yaxis": { "autorange": false, "range": [ 0.13247431516647334, 8.137508201599122 ] } } }, "text/html": [ "