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 estas resolviendo o el algoritmo que deseas usar no esta incluido en la biblioteca estandar de EvoX. En tales casos, puedes desarrollar modulos personalizados usando las interfaces que EvoX proporciona.
5.1.1 Problemas Personalizados (MyProblem)
Si tu funcion objetivo no esta disponible en evox.problems, puedes definir la tuya propia heredando de la clase base evox.core.Problem (o cumpliendo con 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 la computacion paralela, 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 $$
Puedes 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, puedes devolver un diccionario con claves separadas para cada objetivo.
Puedes usar tu problema personalizado como uno integrado:
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, hereda de evox.core.Algorithm e implementa al menos:
__init__: Para la 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 puedes 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. Consulta 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 de 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("Mejor aptitud:", 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 transparente 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
Usa EvoX para ajustar hiperparametros:
- Envuelve el entrenamiento/validacion como un
Problem. - Usa un algoritmo como CMA-ES.
- Optimiza hiperparametros a traves de multiples ejecuciones.
- Entrena el modelo final con los mejores parametros.
5.3.2 Integracion con Aprendizaje por Refuerzo
Usa EvoX para evolucionar politicas de redes neuronales:
- Envuelve el entorno de RL usando
BraxProblem. - Aplana la red de politicas usando
ParamsAndVector. - Optimiza usando algoritmos evolutivos como GA o CMA-ES.
- Despliega las politicas optimizadas directamente o ajustalas con RL.
EvoX soporta simulacion de entornos por lotes para aprovechar completamente la potencia de GPU/CPU.
En resumen, EvoX proporciona APIs poderosas 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 profundices tu comprension, puedes aplicar creativamente estas interfaces para construir soluciones de optimizacion a medida.