5. Desarrollo y Extension
EvoX no solo ofrece funcionalidad lista para usar, sino que tambien proporciona a los desarrolladores y usuarios avanzados un rico conjunto de interfaces para desarrollo personalizado e integracion extendida. Este capitulo detalla como implementar algoritmos y problemas personalizados, como utilizar las APIs de EvoX para un control mas profundo, y como integrar EvoX con otras herramientas para construir aplicaciones mas complejas.
5.1 Desarrollo de Modulos Personalizados
A veces el problema que esta resolviendo o el algoritmo que desea usar no esta incluido en la biblioteca estandar de EvoX. En tales casos, puede desarrollar modulos personalizados usando las interfaces que EvoX proporciona.
5.1.1 Problemas Personalizados (MyProblem)
Si su funcion objetivo no esta disponible en evox.problems, puede definir la suya propia heredando de la clase base evox.core.Problem (o conformandose a la interfaz requerida). Una clase de problema tipica necesita implementar una funcion evaluate, que recibe un lote de soluciones (pop) y devuelve los valores de aptitud/objetivo correspondientes. Para aprovechar el calculo paralelo, EvoX requiere que evaluate soporte entrada por lotes.
import torch
from abc import ABC
from typing import Any, Dict
from evox.core.module import ModuleBase
class Problem(ModuleBase, ABC):
def __init__(self):
super().__init__()
def evaluate(self, pop: torch.Tensor) -> torch.Tensor:
return torch.empty(0)
Por ejemplo, para minimizar la suma de cubos del vector de decision:
$$ \min f(x) = \sum_{i=1}^{n} x_i^3 $$
Puede implementar una clase MyProblem asi:
import torch
from evox.core import Problem
class MyProblem(Problem):
def __init__(self):
super().__init__()
def evaluate(self, pop: torch.Tensor):
fitness = torch.sum(pop**3, dim=1)
return fitness
Aqui, pop es un tensor de forma (population_size, dim). La funcion evaluate devuelve un tensor 1D de valores de aptitud. Para problemas multiobjetivo, puede devolver un diccionario con claves separadas para cada objetivo.
Puede usar su problema personalizado como uno incorporado:
import torch
from MyProblems import MyProblem
popsize = 10
dim = 2
initial_pop = torch.rand(popsize, dim)
problem = MyProblem()
initial_fitness = problem.evaluate(initial_pop)
5.1.2 Algoritmos Personalizados (MyAlgorithm)
Crear un algoritmo personalizado es mas complejo, ya que incluye inicializacion, generacion de nuevas soluciones y seleccion. Para crear un nuevo algoritmo, herede de evox.core.Algorithm e implemente al menos:
__init__: Para inicializacion.step: La logica principal del paso evolutivo.
A continuacion se muestra un ejemplo de implementacion del algoritmo de Optimizacion por Enjambre de Particulas (PSO) en EvoX:
import torch
from evox.core import Algorithm, Mutable, Parameter
from evox.utils import clamp
from evox.algorithms.so.pso_variants.utils import min_by
class PSO(Algorithm):
def __init__(
self,
pop_size: int,
lb: torch.Tensor,
ub: torch.Tensor,
w: float = 0.6,
phi_p: float = 2.5,
phi_g: float = 0.8,
device: torch.device | None = None,
):
super().__init__()
device = torch.get_default_device() if device is None else device
assert lb.shape == ub.shape and lb.ndim == 1
self.pop_size = pop_size
self.dim = lb.shape[0]
lb = lb[None, :].to(device)
ub = ub[None, :].to(device)
length = ub - lb
pop = length * torch.rand(self.pop_size, self.dim, device=device) + lb
velocity = 2 * length * torch.rand(self.pop_size, self.dim, device=device) - length
self.lb = lb
self.ub = ub
self.w = Parameter(w, device=device)
self.phi_p = Parameter(phi_p, device=device)
self.phi_g = Parameter(phi_g, device=device)
self.pop = Mutable(pop)
self.velocity = Mutable(velocity)
self.fit = Mutable(torch.full((self.pop_size,), torch.inf, device=device))
self.local_best_location = Mutable(pop.clone())
self.local_best_fit = Mutable(torch.full((self.pop_size,), torch.inf, device=device))
self.global_best_location = Mutable(pop[0])
self.global_best_fit = Mutable(torch.tensor(torch.inf, device=device))
def step(self):
compare = self.local_best_fit > self.fit
self.local_best_location = torch.where(compare[:, None], self.pop, self.local_best_location)
self.local_best_fit = torch.where(compare, self.fit, self.local_best_fit)
self.global_best_location, self.global_best_fit = min_by(
[self.global_best_location.unsqueeze(0), self.pop],
[self.global_best_fit.unsqueeze(0), self.fit],
)
rg = torch.rand(self.pop_size, self.dim, device=self.fit.device)
rp = torch.rand(self.pop_size, self.dim, device=self.fit.device)
velocity = (
self.w * self.velocity
+ self.phi_p * rp * (self.local_best_location - self.pop)
+ self.phi_g * rg * (self.global_best_location - self.pop)
)
pop = self.pop + velocity
self.pop = clamp(pop, self.lb, self.ub)
self.velocity = clamp(velocity, self.lb, self.ub)
self.fit = self.evaluate(self.pop)
def init_step(self):
self.fit = self.evaluate(self.pop)
self.local_best_fit = self.fit
self.global_best_fit = torch.min(self.fit)
Para integrar el algoritmo en un flujo de trabajo:
import torch
from MyProblems import MyProblem
from evox.workflows import EvalMonitor, StdWorkflow
from evox.algorithms import PSO
problem = MyProblem()
algorithm = PSO(
pop_size=100,
lb=torch.tensor([-10.0]),
ub=torch.tensor([10.0])
)
monitor = EvalMonitor()
workflow = StdWorkflow(algorithm, problem, monitor)
for i in range(10):
workflow.step()
5.1.3 Otros Modulos Personalizados
Tambien puede personalizar Monitor, Operator o cualquier modulo en EvoX. Por ejemplo, implementar un MyMonitor para registrar la diversidad de la poblacion o crear un MyOperator para estrategias personalizadas de cruce/mutacion. Consulte las clases base existentes y los ejemplos para entender que metodos sobreescribir.
5.2 Uso de la API
EvoX organiza sus APIs en modulos, facilitando la extension y combinacion de componentes.
5.2.1 Algoritmos y Problemas
- Algoritmos: Se encuentran en
evox.algorithms.so(objetivo unico) yevox.algorithms.mo(multiobjetivo).
from evox.algorithms.so import PSO
from evox.algorithms.mo import RVEA
- Problemas: Se encuentran en
evox.problems, incluyendo:numerical— funciones de prueba clasicas (por ejemplo, Ackley, Sphere).neuroevolution— entornos de RL como Brax.hpo_wrapper— envolver entrenamiento de ML en problemas HPO.
Ejemplo: Envolver un MLP de PyTorch con un entorno Brax:
import torch.nn as nn
from evox.problems.neuroevolution.brax import BraxProblem
class SimpleMLP(nn.Module):
...
policy = SimpleMLP().to(device)
problem = BraxProblem(
policy=policy,
env_name="swimmer",
...
)
Ejemplo: Envolver un proceso de optimizacion para HPO:
from evox.problems.hpo_wrapper import HPOProblemWrapper
...
hpo_problem = HPOProblemWrapper(
iterations=30,
num_instances=128,
workflow=inner_workflow,
copy_init_state=True
)
5.2.2 Flujos de Trabajo y Herramientas
- Flujos de Trabajo:
evox.workflows.StdWorkflowpara bucles de optimizacion basicos. - Monitores:
EvalMonitorpara rastrear el rendimiento.
Ejemplo:
workflow = StdWorkflow(algorithm, problem, monitor)
compiled_step = torch.compile(workflow.step)
for i in range(10):
compiled_step()
print("Top fitness:", monitor.topk_fitness)
- Metricas:
evox.metricsproporciona IGD, Hipervolumen, etc.
from evox.metrics import igd
igd_value = igd(current_population, true_pareto_front)
- Interoperabilidad con PyTorch: Integracion perfecta con
torch.nn,torch.Tensor, etc.
5.3 Integracion con Otras Herramientas
EvoX esta disenado para integrarse facilmente con herramientas externas.
5.3.1 Integracion con Aprendizaje Automatico
Use EvoX para ajustar hiperparametros:
- Envuelva el entrenamiento/validacion como un
Problem. - Use un algoritmo como CMA-ES.
- Optimice hiperparametros a lo largo de multiples ejecuciones.
- Entrene el modelo final con los mejores parametros.
5.3.2 Integracion con Aprendizaje por Refuerzo
Use EvoX para evolucionar politicas de redes neuronales:
- Envuelva el entorno de RL usando
BraxProblem. - Aplane la red de politicas usando
ParamsAndVector. - Optimice usando algoritmos evolutivos como GA o CMA-ES.
- Despliegue las politicas optimizadas directamente o ajuste con RL.
EvoX soporta simulacion de entornos por lotes para utilizar completamente la potencia de GPU/CPU.
En resumen, EvoX proporciona APIs potentes y modulares y un diseno amigable para desarrolladores para implementar algoritmos personalizados, envolver cualquier problema de optimizacion e integrarse con herramientas de ML y RL. A medida que profundice su comprension, puede aplicar creativamente estas interfaces para construir soluciones de optimizacion a medida.