7. Exemples pratiques

7. Exemples pratiques

Ce chapitre présente plusieurs exemples complets et pratiques pour démontrer comment appliquer les connaissances des chapitres précédents. Nous construirons un projet d’optimisation à partir de zéro et montrerons comment EvoX peut être intégré avec d’autres outils. Ces exemples couvrent une gamme de types de problèmes pour vous aider à appliquer EvoX dans des scénarios réels.


Exemple 1 : Optimisation mono-objectif

Problème : Optimiser la fonction classique de Rastrigin :

f(\mathbf{x}) = 10 d + \sum_{i=1}^{d}[x_i^2 - 10 \cos{(2\pi x_i)}],

où $\mathbf{x} \in \mathbb{R}^d$ et $d$ est la dimensionnalité. L’optimum global est 0 à l’origine. La fonction est hautement multimodale, ce qui la rend idéale pour tester les algorithmes d’optimisation globale. Voici un graphique de la fonction de Rastrigin

:alt: Un graphique de la fonction de Rastrigin
:figwidth: 70%
:align: center

Fonction de Rastrigin

Dans cet exemple, nous utiliserons l’algorithme d’Optimisation par Essaim de Particules (PSO) pour optimiser la fonction de Rastrigin en 10 dimensions.

Étape 1 : Configuration

En supposant que vous avez configuré votre environnement EvoX comme expliqué au Chapitre 2.

Étape 2 : Configuration du workflow

Créez un script Python opt_rastrigin_10.py :

import torch
from evox.algorithms.so.pso_variants import PSO
from evox.problems.numerical.basic import Rastrigin
from evox.workflows import StdWorkflow, EvalMonitor

Définissez l’algorithme PSO :

dim = 10
algo = PSO(
    pop_size=50,
    lb=-32 * torch.ones(dim),
    ub=32 * torch.ones(dim)
)

Configurez le problème et le workflow :

prob = Rastrigin()
monitor = EvalMonitor()
workflow = StdWorkflow(
    algorithm=algo,
    problem=prob,
    monitor=monitor
)

Étape 3 : Exécuter l’optimisation

workflow.init_step()
for iter in range(501):
    workflow.step()
    if iter % 100 == 0:
        current_best_fitness = monitor.get_best_fitness().item()
        print(f"Iter {iter}, Best Fitness: {current_best_fitness}")

print(f"Final Best Solution: {monitor.get_best_solution()}")

Exemple de sortie :

Iter 0, Best Fitness: 1398.625
Iter 100, Best Fitness: 11.608497619628906
Iter 200, Best Fitness: 2.5700759887695312
Iter 300, Best Fitness: 1.9909820556640625
Iter 400, Best Fitness: 1.9899139404296875
Iter 500, Best Fitness: 0.9976348876953125
Final Best Solution: tensor([...])

L’algorithme PSO trouve une solution quasi-optimale proche de l’origine, comme attendu.


Exemple 2 : Optimisation multi-objectif

Problème : Minimiser deux objectifs :

f_1(x) = x^2, \quad
f_2(x) = (x - 2)^2

Le front de Pareto se situe entre $x = 0$ (optimal pour $f_1$) et $x = 2$ (optimal pour $f_2$).

Étape 1 : Configuration de l’environnement

Assurez-vous d’avoir EvoX installé avec le support NSGA-II.

Étape 2 : Définir le problème personnalisé

EvoX dispose de nombreux problèmes de test multi-objectif intégrés, mais pour cet exemple, nous définirons un problème personnalisé pour optimiser les deux objectifs :

import torch
import numpy as np
import matplotlib.pyplot as plt

from evox.algorithms import NSGA2
from evox.workflows import StdWorkflow, EvalMonitor
# Importer les classes de base evox, voir le Chapitre 5 pour les détails
from evox.core import Problem

class TwoObjectiveProblem(Problem):
    def __init__(
        self,
        d: int = 1,
        m: int = 2,
    ):
        super().__init__()
        self.d = d
        self.m = m

    def evaluate(self, X: torch.Tensor) -> torch.Tensor:
        x = X[:, 0]
        f_1 = x ** 2
        f_2 = (x - 2) ** 2
        return torch.stack([f_1, f_2], dim=1)

    # Optionnel : Définir la fonction du front de Pareto
    def pf(self) -> torch.Tensor:
        pass

Étape 3 : Définir l’algorithme et le workflow

from evox.algorithms import NSGA2
from evox.workflows import StdWorkflow, EvalMonitor

prob = TwoObjectiveProblem()
torch.set_default_device("cuda:0")

algo = NSGA2(
    pop_size=50,
    n_objs=2,
    lb=-5 * torch.ones(1),
    ub=5 * torch.ones(1),
    device=torch.device("cuda"),
)

monitor = EvalMonitor()
workflow = StdWorkflow(algo, prob, monitor)

Étape 4 : Optimisation et visualisation

workflow.init_step()
for i in range(100):
    workflow.step()

data = algo.fit.cpu().numpy()

import numpy as np
import matplotlib.pyplot as plt

x_vals = np.linspace(0, 2, 400)
pf_f1 = x_vals ** 2
pf_f2 = (x_vals - 2) ** 2

plt.figure(figsize=(8, 6))
plt.scatter(data[:, 0], data[:, 1], c='blue', label='Optimized Population', alpha=0.7)
plt.plot(pf_f1, pf_f2, 'r-', linewidth=2, label='Pareto Front')
plt.xlabel("f1")
plt.ylabel("f2")
plt.title("NSGA-II on Bi-objective Problem")
plt.legend()
plt.grid(True)
plt.show()

Nous pouvons visualiser les résultats en utilisant Matplotlib. Les points bleus représentent la population optimisée, tandis que la ligne rouge montre le front de Pareto.

:alt: Un graphique de la population NSGA-II
:figwidth: 70%
:align: center

Un graphique de la population NSGA-II après optimisation

Dans Jupyter Notebook, vous pouvez utiliser les capacités de tracé intégrées d’EvoX pour visualiser le processus d’optimisation et surveiller comment la population évolue au fil des générations.

monitor.plot()

Exemple 3 : Optimisation d’hyperparamètres (HPO)

Problème : Ajuster C et max_iter d’un classifieur de régression logistique sur le jeu de données du cancer du sein pour maximiser la précision de validation.

Étape 1 : Charger les données et le modèle

import torch
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from evox.core import Problem

X, y = load_breast_cancer(return_X_y=True)
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)
scaler = StandardScaler().fit(X_train)
X_train = scaler.transform(X_train)
X_val = scaler.transform(X_val)

Étape 2 : Définir le problème

class HyperParamOptProblem(Problem):
    def __init__(self):
        super().__init__()

    def evaluate(self, pop):
        pop = pop.detach().cpu().numpy()
        objs = []
        for C_val, max_iter_val in pop:
            C_val = float(max(1e-3, C_val))
            max_iter_val = int(max(50, max_iter_val))
            model = LogisticRegression(C=C_val, max_iter=max_iter_val, solver='liblinear')
            model.fit(X_train, y_train)
            acc = model.score(X_val, y_val)
            objs.append(1 - acc)  # error rate
        return torch.tensor(objs)

Étape 3 : Configuration du workflow

from evox.algorithms.so.es_variants import CMAES
from evox.workflows import EvalMonitor, StdWorkflow

prob = HyperParamOptProblem()
init_params = torch.tensor([1.0, 100.0])
print("Initial error rate:", prob.evaluate(init_params.unsqueeze(0)).item())

algo = CMAES(
    mean_init=init_params,
    sigma=1.0,
)

monitor = EvalMonitor()
workflow = StdWorkflow(algo, prob, monitor)

Étape 4 : Optimisation

workflow.init_step()
for _ in range(100):
    workflow.step()

best_params = monitor.get_best_solution()
best_error = prob.evaluate(best_params.unsqueeze(0)).item()
print("Optimized error rate:", best_error)

Exemple de sortie :

Initial error rate: 0.0263
Optimized error rate: 0.0088

Avec seulement quelques lignes de code, EvoX automatise le fastidieux processus d’essai-erreur du réglage des hyperparamètres.


Ces exemples pratiques illustrent comment EvoX peut être efficacement appliqué dans divers domaines, des fonctions de test mathématiques aux workflows d’apprentissage automatique. Une fois que vous êtes à l’aise avec la structure de base — Algorithme + Problème + Moniteur + Workflow — vous pouvez adapter EvoX à presque n’importe quelle tâche d’optimisation.