5. 개발 및 확장
EvoX는 즉시 사용 가능한(out-of-the-box) 기능을 제공할 뿐만 아니라, 개발자와 고급 사용자가 사용자 정의 개발 및 확장된 통합을 수행할 수 있도록 풍부한 인터페이스 세트를 제공합니다. 이 장에서는 사용자 정의 알고리즘과 문제를 구현하는 방법, EvoX의 API를 활용하여 더 깊이 있게 제어하는 방법, 그리고 다른 도구와 EvoX를 통합하여 더 복잡한 애플리케이션을 구축하는 방법에 대해 자세히 설명합니다.
5.1 사용자 정의 모듈 개발
때로는 해결하려는 문제나 사용하려는 알고리즘이 EvoX의 표준 라이브러리에 포함되어 있지 않을 수 있습니다. 이러한 경우, EvoX가 제공하는 인터페이스를 사용하여 사용자 정의 모듈을 개발할 수 있습니다.
5.1.1 사용자 정의 문제 (MyProblem)
목적 함수가 evox.problems에 없다면, evox.core.Problem 기본 클래스를 상속받아(또는 필수 인터페이스를 준수하여) 직접 정의할 수 있습니다. 일반적인 문제 클래스는 솔루션 배치(pop)를 받아 해당하는 적합도/목적 함수 값을 반환하는 evaluate 함수를 구현해야 합니다. 병렬 계산을 활용하기 위해, EvoX는 evaluate가 **배치 입력(batch input)**을 지원하도록 요구합니다.
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)
예를 들어, 결정 벡터(decision vector)의 세제곱 합을 최소화하려면:
$$ \min f(x) = \sum_{i=1}^{n} x_i^3 $$
다음과 같이 MyProblem 클래스를 구현할 수 있습니다:
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
여기서 pop은 (population_size, dim) 형태(shape)의 텐서입니다. evaluate 함수는 적합도 값들의 1D 텐서를 반환합니다. 다목적 문제의 경우, 각 목적에 대한 별도의 키를 가진 딕셔너리를 반환할 수 있습니다.
내장된 문제처럼 사용자 정의 문제를 사용할 수 있습니다:
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 사용자 정의 알고리즘 (MyAlgorithm)
사용자 정의 알고리즘을 만드는 것은 초기화, 새로운 솔루션 생성, 선택 과정을 포함하므로 조금 더 복잡합니다. 새로운 알고리즘을 만들려면 evox.core.Algorithm을 상속받고 최소한 다음을 구현해야 합니다:
__init__: 초기화를 위해.step: 주요 진화 단계 로직.
아래는 EvoX에서 Particle Swarm Optimization (PSO) 알고리즘을 구현하는 예시입니다:
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)
알고리즘을 워크플로우에 통합하려면:
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 기타 사용자 정의 모듈
Monitor, Operator 또는 EvoX의 모든 모듈을 사용자 정의할 수도 있습니다. 예를 들어, 개체군 다양성을 기록하는 MyMonitor를 구현하거나 사용자 정의 교차/변이 전략을 위한 MyOperator를 생성할 수 있습니다. 어떤 메서드를 오버라이드해야 하는지 이해하려면 기존 기본 클래스와 예제를 참조하세요.
5.2 API 사용
EvoX는 API를 모듈로 구성하여 컴포넌트를 쉽게 확장하고 결합할 수 있도록 합니다.
5.2.1 알고리즘 및 문제
- 알고리즘:
evox.algorithms.so(단일 목적) 및evox.algorithms.mo(다목적)에서 찾을 수 있습니다.
from evox.algorithms.so import PSO
from evox.algorithms.mo import RVEA
- 문제:
evox.problems에서 찾을 수 있으며, 다음을 포함합니다:numerical– 고전적인 테스트 함수 (예: Ackley, Sphere).neuroevolution– Brax와 같은 RL 환경.hpo_wrapper– ML 훈련을 HPO 문제로 래핑.
예시: PyTorch MLP를 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",
...
)
예시: 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 워크플로우 및 도구
- 워크플로우: 기본적인 최적화 루프를 위한
evox.workflows.StdWorkflow. - 모니터: 성능 추적을 위한
EvalMonitor.
예시:
workflow = StdWorkflow(algorithm, problem, monitor)
compiled_step = torch.compile(workflow.step)
for i in range(10):
compiled_step()
print("Top fitness:", monitor.topk_fitness)
- 지표 (Metrics):
evox.metrics는 IGD, Hypervolume 등을 제공합니다.
from evox.metrics import igd
igd_value = igd(current_population, true_pareto_front)
- PyTorch 상호 운용성:
torch.nn,torch.Tensor등과 원활하게 통합됩니다.
5.3 다른 도구와의 통합
EvoX는 외부 도구와 쉽게 통합되도록 설계되었습니다.
5.3.1 머신러닝 통합
EvoX를 사용하여 하이퍼파라미터를 튜닝할 수 있습니다:
- 훈련/검증 과정을
Problem으로 래핑합니다. - CMA-ES와 같은 알고리즘을 사용합니다.
- 여러 실행에 걸쳐 하이퍼파라미터를 최적화합니다.
- 최적의 파라미터로 최종 모델을 훈련합니다.
5.3.2 강화학습 통합
EvoX를 사용하여 신경망 정책(policy)을 진화시킬 수 있습니다:
BraxProblem을 사용하여 RL 환경을 래핑합니다.ParamsAndVector를 사용하여 정책 네트워크를 평탄화(flatten)합니다.- GA나 CMA-ES와 같은 진화 알고리즘을 사용하여 최적화합니다.
- 최적화된 정책을 직접 배포하거나 RL로 미세 조정(fine-tune)합니다.
EvoX는 GPU/CPU 성능을 완전히 활용하기 위해 배치 환경 시뮬레이션을 지원합니다.
요약하자면, EvoX는 사용자 정의 알고리즘 구현, 모든 최적화 문제 래핑, ML 및 RL 도구와의 통합을 위해 강력하고 모듈화된 API와 개발자 친화적인 설계를 제공합니다. 이해도가 깊어짐에 따라, 이러한 인터페이스를 창의적으로 적용하여 맞춤형 최적화 솔루션을 구축할 수 있습니다.