Guía de referencia de pymoo del paquete de algoritmos de optimización multiobjetivo

Guía de referencia de pymoo del paquete de algoritmos de optimización multiobjetivo

1 Definición de función multiobjetivo

Sin perder generalidad, el problema de optimización se puede definir como:

En la fórmula:

  • xi x_iXyoParte IIi variablesa optimizar;
  • xi L x^L_iXiLxi U x^U_iXiUd.como sus límites inferior y superior ;
  • fm f_mFmes mmm funcionesobjetivas;
  • gj g_jgramojPara el jjj restriccionesde desigualdad;
  • hk h_khkpara el k-ésimok restriccionesde igualdad.

función objetivo fff debe satisfacer todas las restricciones de igualdad y desigualdad. Si una función objetivo específica esmaximizar(max ⁡ fi \max f_imáximoFyo), el problema se puede redefinir para minimizar su valor negativo ( min ⁡ − fi \min - f_imín.fyo)。

En los problemas de optimización es necesario considerar los siguientes aspectos:

1) tipo de variable

Las variables cubren el espacio de búsqueda Ω del problema de optimización . Diferentes tipos de variables, como continua, discreta/entera, binaria o permutación, definen las características del espacio de búsqueda. En algunos casos, los tipos de variables pueden incluso mezclarse, lo que añade mayor complejidad.

2) Número de variables

No sólo el tipo, sino también el número de variables (N) es fundamental. Puedes imaginar que resolver un problema con sólo diez variables es completamente diferente a resolver un problema con miles de variables. Para problemas de optimización a gran escala, donde incluso el cálculo de derivadas de segundo orden es muy costoso, el manejo eficiente de la memoria juega un papel más importante.

3) Cantidad objetivo

Algunos problemas de optimización tienen múltiples objetivos en conflicto (M>1) que deben optimizarse .

La optimización de objetivo único es solo un caso especial de M=1. En la optimización multiobjetivo, la solución gobierna la comparación de escalares en la optimización de un solo objetivo. Hay múltiples dimensiones en el espacio objetivo y el óptimo (la mayoría de las veces) consiste en un conjunto de soluciones no dominadas. Los algoritmos basados ​​en poblaciones se utilizan principalmente como solucionadores porque se debe obtener un conjunto de soluciones.

4) Restricciones

Hay dos tipos de restricciones en los problemas de optimización : restricciones de desigualdad (g) y restricciones de igualdad (h) .

Por muy acertada que sea una solución, se considera inviable si acaba violando una sola restricción .

Las restricciones tienen un fuerte impacto en la complejidad del problema. Por ejemplo, si solo son factibles unas pocas soluciones en el espacio de búsqueda, o si es necesario satisfacer una gran cantidad de restricciones (|J|+|K|). Para los algoritmos genéticos , satisfacer las restricciones de igualdad es todo un desafío. Por lo tanto, este problema debe resolverse de diferentes maneras, por ejemplo, asignando el espacio de búsqueda a un espacio de utilidad que siempre satisfaga las restricciones de igualdad , o personalizando la inyección de conocimiento de las restricciones de igualdad .

5) multimodal

En escenarios de aptitud multimodal, la optimización se vuelve más difícil debido a la existencia de un número pequeño o incluso grande de óptimos locales . Para encontrar una solución, siempre hay que preguntarse si el método explora un área suficiente del espacio de búsqueda para maximizar la probabilidad de obtener el óptimo global . Los espacios de búsqueda multimodal revelan rápidamente las limitaciones de la búsqueda local y pueden atascarse fácilmente .

6) Diferenciabilidad

Una función diferenciable significa que se pueden calcular la primera e incluso la segunda derivada . Las funciones diferenciables permiten el uso de métodos de optimización basados ​​en gradientes , que tienen ventajas significativas sobre los métodos sin gradientes. La mayoría de los algoritmos basados ​​en gradientes son puntuales y muy eficientes para escenarios de aptitud unimodal. En la práctica, sin embargo , las funciones a menudo no son diferenciables , o funciones más complejas requieren una búsqueda global en lugar de una búsqueda local . También se conoce como optimización de caja negra al campo de investigación que resuelve problemas sin conocer la optimización matemática .

7) Incertidumbre.

Generalmente se supone que las funciones objetivo y de restricción son deterministas . Sin embargo, si una o más funciones objetivas son inciertas, esto introduce ruido o también conocido como incertidumbre . Una técnica para tener en cuenta la aleatoriedad potencial es repetir la evaluación con diferentes semillas aleatorias y promediar los valores resultantes . Además, la desviación estándar derivada de múltiples evaluaciones se puede utilizar para determinar el rendimiento y la confiabilidad de una solución específica . En términos generales, los problemas de optimización con incertidumbre potencial se estudian en el campo de investigación de optimización estocástica.

2 Problema de optimización bioobjetivo

Problema de optimización bioobjetivo:

Entre las preguntas anteriores:

  • Hay dos funciones objetivo ( M = 2 M=2METRO=2 ), queminimiza f 1 ( x ) f_1(x)F1( x ) ,maximizar f 2 (x) f_2(x)F2( x )
  • Hay dos restricciones de desigualdad en el objetivo de optimización ( J ​​= 2 J=2j=2 ), dondeg 1 ( x ) g_1(x)gramo1( x ) esmenor o igual que,g 2 (x) g_2(x)gramo2( x ) esmayor o igual que.
  • El problema está definido por dos variables ( N = 2 N=2norte=2 ),x 1 x_1X1y x 2 x_2X2, ambos rangos en [ − 2 , 2 ] [-2,2][ -2 , _2 ]
  • Este problema de optimización no contiene restricciones de igualdad ( K = 0 K=0k=0 )。

Primero analice la ubicación de la solución óptima de Pareto.

f 1 (x) f_1(x)F1El valor mínimode ( x ) está en(0, 0) (0,0)( 0 ,0 ) ,f 2 ( x ) f_2(x)F2El valor máximode ( x ) está en(1, 0) (1,0)( 1 ,0 ) lugar. Como ambas funcionesson cuadráticas, la solución óptimaviene dada por una línea recta entre las dos soluciones óptimas. Esto significa que todas las soluciones óptimas de Pareto (ignorando las restricciones por ahora) tienenx 2 = 0 x_2=0X2=0x 1 ∈ ( 0 , 1 ) x1\in(0,1)x1 _( 0 ,1 )

La primera restricción depende sólo de x 1 x_1X1, y satisface x 1 ∈ ( 0.1 , 0.9 ) x_1\in(0.1,0.9)X1( 0 . 1 ,0 . 9 )x 1 ∈ ( 0.4 , 0.6 ) x_1 \in(0.4,0.6)X1( 0 , 4 ,0 . 6 ) satisface la segunda restriccióng 2 g_2gramo2. Esto significa que desde una perspectiva analítica, el conjunto óptimo de Pareto es:

PS = { ( x 1 , x 2 ) ∣ ( 0.1 ≤ x 1 ≤ 0.4 ) ∨ ( 0.6 ≤ x 1 ≤ 0.9 ) ∧ x 2 = 0 } PS=\left\{\left(x_{1}, x_{ 2}\right) \mid\left(0.1 \leq x_{1} \leq 0.4\right) \vee\left(0.6 \leq x_{1} \leq 0.9\right) \wedge x_{2}=0\ bien\}PD _={ ( x1,X2)( 0 . 1X10 . 4 )( 0 . 6X10 . 9 )X2=0 }

Vea el conjunto óptimo de Pareto (naranja) trazando la función:

import numpy as np

X1, X2 = np.meshgrid(np.linspace(-2, 2, 500), np.linspace(-2, 2, 500))

F1 = X1**2 + X2**2
F2 = (X1-1)**2 + X2**2
G = X1**2 - X1 + 3/16

G1 = 2 * (X1[0] - 0.1) * (X1[0] - 0.9)
G2 = 20 * (X1[0] - 0.4) * (X1[0] - 0.6)


import matplotlib.pyplot as plt
plt.rc('font', family='serif')

levels = [0.02, 0.1, 0.25, 0.5, 0.8]
plt.figure(figsize=(7, 5))
CS = plt.contour(X1, X2, F1, levels, colors='black', alpha=0.5)
CS.collections[0].set_label("$f_1(x)$")

CS = plt.contour(X1, X2, F2, levels, linestyles="dashed", colors='black', alpha=0.5)
CS.collections[0].set_label("$f_2(x)$")

plt.plot(X1[0], G1, linewidth=2.0, color="green", linestyle='dotted')
plt.plot(X1[0][G1<0], G1[G1<0], label="$g_1(x)$", linewidth=2.0, color="green")

plt.plot(X1[0], G2, linewidth=2.0, color="blue", linestyle='dotted')
plt.plot(X1[0][X1[0]>0.6], G2[X1[0]>0.6], label="$g_2(x)$",linewidth=2.0, color="blue")
plt.plot(X1[0][X1[0]<0.4], G2[X1[0]<0.4], linewidth=2.0, color="blue")

plt.plot(np.linspace(0.1,0.4,100), np.zeros(100),linewidth=3.0, color="orange")
plt.plot(np.linspace(0.6,0.9,100), np.zeros(100),linewidth=3.0, color="orange")

plt.xlim(-0.5, 1.5)
plt.ylim(-0.5, 1)
plt.xlabel("$x_1$")
plt.ylabel("$x_2$")

plt.legend(loc='upper center', bbox_to_anchor=(0.5, 1.12),
          ncol=4, fancybox=True, shadow=False)

plt.tight_layout()
plt.show()

A continuación, el frente de Pareto se deriva mapeando el conjunto de Pareto al espacio objetivo. Ecuación del frente de Pareto basada en f 2 f_2F2y depende de f 1 f_1F1Variables.

Sabemos que el valor óptimo es x 2 = 0 x_2=0X2=0 Esto significa que la función objetivo se puede simplificar af 1 ( x ) = 100 x 1 2 , f 2 ( x ) = − ( x 1 − 1 ) 2 f_1(x)=100 x^2_1, f_2(x) = -(x_1-1)^2F1( x )=1 0 0 x12,F2( x )=( x11 )2 . El primer objetivof 1 f_1F1Se puede convertir en x 1 = f 1 100 x_1=\sqrt{\frac{f_1}{100}}X1=1 0 0F1 Y luego coloque el segundo objetivo, el resultado es:

f 2 = − ( f 1 100 − 1 ) 2 f_{2}=-\left(\sqrt{\frac{f_{1}}{100}}-1\right)^{2}F2=(1 0 0F1 1 )2

La ecuación define la forma. A continuación necesitamos definir f 1 f_1F1todos los valores posibles de . Si x 1 x_1X1Sustituir el valor de f 1 f_1F1, obtendrás los puntos de interés [1, 16] [1,16][ 1 ,1 6 ] y[ 36 , 81 ] [36,81][ 3 6 ,8 1 ] . Por tanto, el frente de Pareto es:

import numpy as np
import matplotlib.pyplot as plt

plt.figure(figsize=(7, 5))

f2 = lambda f1: - ((f1/100) ** 0.5 - 1)**2
F1_a, F1_b = np.linspace(1, 16, 300), np.linspace(36, 81, 300)
F2_a, F2_b = f2(F1_a), f2(F1_b)

plt.rc('font', family='serif')
plt.plot(F1_a,F2_a, linewidth=2.0, color="green", label="Pareto-front")
plt.plot(F1_b,F2_b, linewidth=2.0, color="green")

plt.xlabel("$f_1$")
plt.ylabel("$f_2$")

plt.legend(loc='upper center', bbox_to_anchor=(0.5, 1.10),
          ncol=4, fancybox=True, shadow=False)

plt.tight_layout()
plt.show()

Se puede ver en la figura que se trata de un conjunto no dominado. Para este problema de optimización, el primer objetivo es minimizar, el segundo objetivo es maximizar y la mejor solución está en la esquina superior izquierda.

3 Utilice pymoo para optimizar el objetivo

La mayoría de los marcos de optimización se esfuerzan por minimizar o maximizar todos los objetivos y solo tienen restricciones ≤ o ≥ . En pymoo, cada función objetivo debe ser mínima y cada restricción debe proporcionarse en la forma ≤0 .

Por lo tanto, es necesario −1multiplicar un objetivo que se debe maximizar y luego minimizarlo. Esto lleva a minimizar − f 2 ( x ) −f_2(x)f2( x ) en lugar de maximizarf 2 ( x ) f_2(x)F2( x )

Además, las restricciones a la desigualdad deben expresarse como 小于零(≤0)restricciones. Por lo tanto, g 2 ( x ) g_2(x)gramo2( x )−1 parainvertirla relación de desigualdad.

Además, se recomienda normalizar las restricciones para que operen en la misma escala y darles la misma importancia. Para g 1 (x) g_1(x)gramo1( x ) , el coeficiente es2 ⋅ ( − 0.1 ) ⋅ ( − 0.9 ) = 0.18 2⋅(−0.1)⋅(−0.9)=0.182( - 0 , 1 )( - 0 , 9 )=0 . 1 8g 2 ( x ) g_2(x)gramo2( x ) , el coeficiente es20 ⋅ ( − 0.4 ) ⋅ ( − 0.6 ) = 4.8 20⋅(−0.4)⋅(−0.6)=4.82 0( - 0 , 4 )( - 0 , 6 )=4 . 8 .

Usando g 1 ( x ) g_1(x)gramo1( x ) yg 2 (x) g_2(x)gramo2( x ) se divide por su coeficiente correspondientepara normalizar las restricciones.

La función objetivo final es:

instalación de pymoo:

pip install -U pymoo

1) Definición del problema basada en elementos

ElementwiseProblemSe define un nuevo objetivo de Python heredado y se establecen las propiedades correctas, como el número de objetivos (n_obj) y el número de restricciones (n_constr) , así como el límite inferior (xl) y el límite superior (xu) .

La función responsable de la evaluación es _evaluate. La interfaz de la función es el parámetro xx.xfuera fuerafuera . _ _ Para esta implementación basada en elementos,xxx es unan_varmatriz NumPy unidimensional de longitud que representa la solución única que se va a calcular. fuera fuerao u t es un diccionario.

Los valores objetivo deben n_objescribirse como una lista de longitud de matriz NumPy out["F"]y las restricciones deben escribirse como una lista de n_constrlongitud out["G"].

import numpy as np
from pymoo.core.problem import ElementwiseProblem

class MyProblem(ElementwiseProblem):

    def __init__(self):
        super().__init__(n_var=2,
                         n_obj=2,
                         n_constr=2,
                         xl=np.array([-2,-2]),
                         xu=np.array([2,2]))

    def _evaluate(self, x, out, *args, **kwargs):
        f1 = 100 * (x[0]**2 + x[1]**2)
        f2 = (x[0]-1)**2 + x[1]**2

        g1 = 2*(x[0]-0.1) * (x[0]-0.9) / 0.18
        g2 = - 20*(x[0]-0.4) * (x[0]-0.6) / 4.8

        out["F"] = [f1, f2]
        out["G"] = [g1, g2]


problem = MyProblem()

Un problema se puede definir de varias maneras diferentes. Lo anterior demuestra una implementación basada en elementos, lo que significa que para cada solución xxx llama a _evaluar.

Otro enfoque es vectorizado, donde xxx representaun conjunto completo de solucioneso un enfoque funcional, posiblemente más pitónico, que proporciona cada objetivo y restricción en forma de función.

Para más detalles, consulte:

https://pymoo.org/problems/definition.html

2) Algoritmo de inicialización

Lista de algoritmos de pymoo:

algoritmo
1. Algoritmo genético GA , objetivo único
2. Evolución diferencial DE, objetivo único
3. Algoritmo genético de clave aleatoria sesgada BRKGA, objetivo único
4. NelderMead simplex, objetivo único
5. Búsqueda de patrones PatternSearch, objetivo único
6. CMAES, objetivo único
7. Estrategia de evolución ES, objetivo único
8. Estrategia de evolución de clasificación aleatoria SRES, objetivo único
9. Estrategia de evolución de clasificación aleatoria mejorada ISRES, objetivo único
10. NSGA-II, NSGA2, objetivos múltiples
11. R-NSGA-II, RNSGA2, multiobjetivo
12. NSGA-III, NSGA3, objetivos múltiples
13. U-NSGA-III, UNSGA3, objetivo único y objetivos múltiples
14. R-NSGA-III, RNSGA3, objetivo único y objetivo múltiple
15. Descomposición de objetivos múltiples MOEAD, objetivo único y objetivo múltiple
16. AGE-MOEA, objetivo único y objetivos múltiples
17. C-TAEA, objetivo único y objetivos múltiples

Para más detalles, consulte:

https://pymoo.org/algorithms/list.html#nb-algorithms-list

El algoritmo multiobjetivo NSGA-II se selecciona aquí para su explicación. Para la mayoría de los algoritmos, puede elegir los hiperparámetros predeterminados o crear su propia versión del algoritmo modificándolos.

Por ejemplo, para este problema relativamente simple, elija un tamaño de población de 40 ( pop_size=40) y solo 10 descendientes por generación ( ) n_offsprings=10)Habilite la verificación de duplicados ( eliminate_duplicate =True) para garantizar que los apareamientos produzcan descendientes que difieran tanto de sus propios valores espaciales de diseño como de los de la población existente. .

from pymoo.algorithms.moo.nsga2 import NSGA2
from pymoo.factory import get_sampling, get_crossover, get_mutation

algorithm = NSGA2(
    pop_size=40,
    n_offsprings=10,
    sampling=get_sampling("real_random"),
    crossover=get_crossover("real_sbx", prob=0.9, eta=15),
    mutation=get_mutation("real_pm", eta=20),
    eliminate_duplicates=True
)

3) Definir criterios de terminación

Además, es necesario definir criterios de terminación para iniciar el proceso de optimización. La forma más común de definir la terminación es limitar el número total de evaluaciones de funciones o simplemente limitar el número de iteraciones del algoritmo .

Además, algunos algoritmos han implementado sus propios algoritmos, como Nelder-Mead cuando el simplex degenera o CMA-ES cuando se utilizan bibliotecas de proveedores. Debido a la simplicidad del problema, se utilizó un algoritmo bastante pequeño de 40 iteraciones.

from pymoo.factory import get_termination
termination = get_termination("n_gen", 40)

Para obtener una lista y una explicación de los criterios de terminación, consulte:

https://pymoo.org/interface/termination.html

4) Optimización

Finalmente, el problema se resuelve con un algoritmo definido y una terminación. Las interfaces funcionales utilizan el enfoque de minimización.

De forma predeterminada, el algoritmo de ejecución de la función se minimiza y las copias profundas de los objetos se terminan para garantizar que no se modifiquen durante las llamadas a la función . Esto es importante para garantizar que las llamadas repetidas a funciones con la misma semilla aleatoria terminen con el mismo resultado. Cuando el algoritmo termina, la función de minimización devuelve un objeto Resultado.

from pymoo.optimize import minimize

res = minimize(problem,
               algorithm,
               termination,
               seed=1,
               save_history=True,
               verbose=True)

X = res.X
F = res.F

Cada fila en la figura anterior representa una iteración. Las dos primeras columnas son el contador de compilación actual y el número de cálculos hasta el momento. Para problemas restringidos, la tercera y cuarta columnas muestran la violación mínima de la restricción ( cv (min)) y la violación promedio de la restricción ( cv (avg)) en la población actual. El siguiente es el número de soluciones no dominadas ( n_nds) y otros dos indicadores que representan el movimiento en el espacio objetivo.

5) Visualización

import matplotlib.pyplot as plt
xl, xu = problem.bounds()
plt.figure(figsize=(7, 5))
plt.scatter(X[:, 0], X[:, 1], s=30, facecolors='none', edgecolors='r')
plt.xlim(xl[0], xu[0])
plt.ylim(xl[1], xu[1])
plt.title("Design Space")
plt.show()
F = res.F
xl, xu = problem.bounds()
plt.figure(figsize=(7, 5))
plt.scatter(F[:, 0], F[:, 1], s=30, facecolors='none', edgecolors='blue')
plt.title("Objective Space")
plt.show()

4 Toma de decisiones multicriterio

Después de obtener un conjunto de soluciones no dominadas, ¿cómo determina el tomador de decisiones este conjunto de soluciones en unas pocas o incluso una única solución? Este proceso de toma de decisiones para problemas multiobjetivo también se denomina toma de decisiones multicriterio (MCDM).

1) Normalización del valor de la función objetivo.

En la optimización multiobjetivo, se debe considerar la escala de la función objetivo :

fl = F.min(axis=0)
fu = F.max(axis=0)
print(f"Scale f1: [{
      
      fl[0]}, {
      
      fu[0]}]")
print(f"Scale f2: [{
      
      fl[1]}, {
      
      fu[1]}]")

# Scale f1: [1.3377795039158837, 74.97223429467643]
# Scale f2: [0.01809179532919018, 0.7831767823138299]

Se puede ver que el objetivo f 1 f_1F1y f 2 f_2F2Los límites superior e inferior de son muy diferentes y deben normalizarse.

Sin estandarización, estamos comparando naranjas y manzanas. El primer objetivo dominará cualquier cálculo de distancia en el espacio objetivo debido a su mayor tamaño . Tratar con objetivos de diferentes tamaños es una parte inherente de cualquier algoritmo multiobjetivo, por lo que se debe hacer lo mismo con el posprocesamiento.

Un enfoque común es utilizar los llamados puntos ideales y nadir para la especificación . Aquí se supone que se desconocen el punto ideal y el punto más bajo (también llamado punto límite) y el frente de Pareto. Entonces estos puntos se pueden aproximar como:

approx_ideal = F.min(axis=0)
approx_nadir = F.max(axis=0)
plt.figure(figsize=(7, 5))
plt.scatter(F[:, 0], F[:, 1], s=30, facecolors='none', edgecolors='blue')
plt.scatter(approx_ideal[0], approx_ideal[1], facecolors='none', edgecolors='red', marker="*", s=100, label="Ideal Point (Approx)")
plt.scatter(approx_nadir[0], approx_nadir[1], facecolors='none', edgecolors='black', marker="p", s=100, label="Nadir Point (Approx)")
plt.title("Objective Space")
plt.legend()
plt.show()

Los valores objetivo se normalizan por:

nF = (F - approx_ideal) / (approx_nadir - approx_ideal)

fl = nF.min(axis=0)
fu = nF.max(axis=0)
print(f"Scale f1: [{
      
      fl[0]}, {
      
      fu[0]}]")
print(f"Scale f2: [{
      
      fl[1]}, {
      
      fu[1]}]")

plt.figure(figsize=(7, 5))
plt.scatter(nF[:, 0], nF[:, 1], s=30, facecolors='none', edgecolors='blue')
plt.title("Objective Space")
plt.show()

# Scale f1: [0.0, 1.0]
# Scale f2: [0.0, 1.0]

2) Programación comprometida

Una forma de tomar decisiones es utilizar funciones de descomposición . Es necesario definir pesos que reflejen los deseos del usuario . Los pesos dados por el vector son sólo números positivos de punto flotante que suman 1 , con una longitud igual al número objetivo . Para un problema de doble objetivo, suponiendo que el primer objetivo no es tan importante como el segundo objetivo, establezca el peso:

weights = np.array([0.2, 0.8])

A continuación, elija el método de descomposición de la función de escalarización aumentada (ASF).

from pymoo.decomposition.asf import ASF
decomp = ASF()

Dado que el ASF debe minimizarse , se selecciona el valor de ASF más pequeño de todas las soluciones.

¿Por qué los pesos no se pasan directamente, sino 1/pesos? Para ASF existen diferentes fórmulas, una donde se dividen los valores y otra donde se multiplican los valores. En "pymoo", "dividir" no refleja los estándares del usuario. Por lo tanto, es necesario aplicar la función inversa.

i = decomp.do(nF, 1/weights).argmin()

Después de encontrar la solución ( iii ), el resultado se puede expresar en su proporción original:

print("Best regarding ASF: Point \ni = %s\nF = %s" % (i, F[i]))

plt.figure(figsize=(7, 5))
plt.scatter(F[:, 0], F[:, 1], s=30, facecolors='none', edgecolors='blue')
plt.scatter(F[i, 0], F[i, 1], marker="x", color="red", s=200)
plt.title("Objective Space")
plt.show()

# Best regarding ASF: Point
# i = 21
# F = [43.28059434  0.12244878]

Un beneficio de este enfoque es que se puede utilizar cualquier tipo de función de descomposición.

3) Pseudopesos

En un contexto de optimización multiobjetivo, un método simple para seleccionar soluciones de un conjunto de soluciones es el método del vector de pseudopeso. Calcular el iith respectivamente.Pseudopesoswi w_i de i funciones objetivowyo:

Esta ecuación calcula cada objetivo ii. La distancia normalizada de i a la peor solución . Tenga en cuenta que paralos conjuntos de Pareto no convexos, las pseudoponderaciones no corresponden a los resultados de optimización que utilizan sumas ponderadas. Sin embargo, paraun conjunto de Pareto convexo, los pseudopesos representan posiciones en el espacio objetivo.

from pymoo.mcdm.pseudo_weights import PseudoWeights
i = PseudoWeights(weights).do(nF)
print("Best regarding Pseudo Weights: Point \ni = %s\nF = %s" % (i, F[i]))

plt.figure(figsize=(7, 5))
plt.scatter(F[:, 0], F[:, 1], s=30, facecolors='none', edgecolors='blue')
plt.scatter(F[i, 0], F[i, 1], marker="x", color="red", s=200)
plt.title("Objective Space")
plt.show()

# Best regarding Pseudo Weights: Point
# i = 39
# F = [58.52211061  0.06005482]

5 Análisis de convergencia

El análisis de convergencia debe considerar dos casos: i) el conjunto de Pareto es desconocido, o ii) el conjunto de Pareto se ha derivado analíticamente o existe una aproximación razonable.

1) Visualización de convergencia

Para comprobar más a fondo qué tan bien los resultados coinciden con la optimización derivada analíticamente, los valores del espacio objetivo deben convertirse a un segundo objetivo f 2 f_2F2La definición original de maximización. Luego dibuje un diagrama de frente de Pareto para mostrar hasta qué punto puede converger el algoritmo.

from pymoo.util.misc import stack

class MyTestProblem(MyProblem):

    def _calc_pareto_front(self, flatten=True, *args, **kwargs):
        f2 = lambda f1: ((f1/100) ** 0.5 - 1)**2
        F1_a, F1_b = np.linspace(1, 16, 300), np.linspace(36, 81, 300)
        F2_a, F2_b = f2(F1_a), f2(F1_b)

        pf_a = np.column_stack([F1_a, F2_a])
        pf_b = np.column_stack([F1_b, F2_b])

        return stack(pf_a, pf_b, flatten=flatten)

    def _calc_pareto_set(self, *args, **kwargs):
        x1_a = np.linspace(0.1, 0.4, 50)
        x1_b = np.linspace(0.6, 0.9, 50)
        x2 = np.zeros(50)

        a, b = np.column_stack([x1_a, x2]), np.column_stack([x1_b, x2])
        return stack(a,b, flatten=flatten)

problem = MyTestProblem()

El frente de Pareto de problemas de prueba se puede implementar de las siguientes maneras:

pf_a, pf_b = problem.pareto_front(use_cache=False, flatten=False)
pf = problem.pareto_front(use_cache=False, flatten=True)
plt.figure(figsize=(7, 5))
plt.scatter(F[:, 0], F[:, 1], s=30, facecolors='none', edgecolors='b', label="Solutions")
plt.plot(pf_a[:, 0], pf_a[:, 1], alpha=0.5, linewidth=2.0, color="red", label="Pareto-front")
plt.plot(pf_b[:, 0], pf_b[:, 1], alpha=0.5, linewidth=2.0, color="red")
plt.title("Objective Space")
plt.legend()
plt.show()

Para dibujar un espacio de alta dimensión, consulte:

https://pymoo.org/visualization/index.html

Alternativamente, puedes usar save_history=Truepara verificar la convergencia:

from pymoo.optimize import minimize

res = minimize(problem,
               algorithm,
               ("n_gen", 40),
               seed=1,
               save_history=True,
               verbose=False)

X, F = res.opt.get("X", "F")

hist = res.history
print(len(hist)) # 40

n_evals = []             # corresponding number of function evaluations\
hist_F = []              # the objective space values in each generation
hist_cv = []             # constraint violation in each generation
hist_cv_avg = []         # average constraint violation in the whole population

for algo in hist:

    # store the number of function evaluations
    n_evals.append(algo.evaluator.n_eval)

    # retrieve the optimum from the algorithm
    opt = algo.opt

    # store the least contraint violation and the average in each population
    hist_cv.append(opt.get("CV").min())
    hist_cv_avg.append(algo.pop.get("CV").mean())

    # filter out only the feasible and append and objective space values
    feas = np.where(opt.get("feasible"))[0]
    hist_F.append(opt.get("F")[feas])

2) Se cumplen las restricciones

Primero, vea cuándo se encontró la primera solución funcional :

k = np.where(np.array(hist_cv) <= 0.0)[0].min()
print(f"At least one feasible solution in Generation {
      
      k} after {
      
      n_evals[k]} evaluations.")
# At least one feasible solution in Generation 0 after 40 evaluations.

# replace this line by `hist_cv` if you like to analyze the least feasible optimal solution and not the population
vals = hist_cv_avg

k = np.where(np.array(vals) <= 0.0)[0].min()
print(f"Whole population feasible in Generation {
      
      k} after {
      
      n_evals[k]} evaluations.")

plt.figure(figsize=(7, 5))
plt.plot(n_evals, vals,  color='black', lw=0.7, label="Avg. CV of Pop")
plt.scatter(n_evals, vals,  facecolor="none", edgecolor='black', marker="p")
plt.axvline(n_evals[k], color="red", label="All Feasible", linestyle="--")
plt.title("Convergence")
plt.xlabel("Function Evaluations")
plt.ylabel("Constraint Violation")
plt.legend()
plt.show()

# Whole population feasible in Generation 8 after 120 evaluations.

3) Colección de Pareto desconocida

Cuando se desconoce el frente de Pareto, es imposible saber si el algoritmo ha convergido al verdadero óptimo. Al menos no hay más información. Sin embargo, es posible ver cuándo el algoritmo ha logrado la mayor parte de su progreso durante el proceso de optimización y si el número de iteraciones debe ser menor o mayor. HypervolumeSe puede utilizar para comparar dos algoritmos.

Hipervolumen es un conjunto (concepto) basado en la normalización de límites, utilizado para indicadores de rendimiento de problemas multiobjetivo. Cumple con el criterio de Pareto y se basa en la capacidad entre puntos de referencia predefinidos y soluciones proporcionadas. Por lo tanto, el hipervolumen necesita definir un punto de referencia ref_point, que debe ser mayor que el valor máximo del frente de Pareto.

# 在多目标优化中,归一化是非常重要的
approx_ideal = F.min(axis=0)
approx_nadir = F.max(axis=0)

from pymoo.indicators.hv import Hypervolume

metric = Hypervolume(ref_point= np.array([1.1, 1.1]),
                     norm_ref_point=False,
                     zero_to_one=True,
                     ideal=approx_ideal,
                     nadir=approx_nadir)

hv = [metric.do(_F) for _F in hist_F]

plt.figure(figsize=(7, 5))
plt.plot(n_evals, hv,  color='black', lw=0.7, label="Avg. CV of Pop")
plt.scatter(n_evals, hv,  facecolor="none", edgecolor='black', marker="p")
plt.title("Convergence")
plt.xlabel("Function Evaluations")
plt.ylabel("Hypervolume")
plt.show()

A medida que aumenta el número de dimensiones, aumenta el costo computacional de Hypervolume. Puede calcular de manera eficiente el hipervolumen preciso para 2 y 3 objetivos. Para dimensiones superiores, algunos investigadores utilizan hypervolume approximation, que aún no está disponible en pymoo.

Recientemente se ha propuesto un método alternativo para analizar corridas cuando se desconoce el verdadero frente de Pareto running metric. Las métricas en ejecución muestran las diferencias en el espacio objetivo entre generaciones y visualizan mejoras utilizando la viabilidad del algoritmo. En pymoo, esta métrica se utiliza para determinar la terminación de un algoritmo de optimización multiobjetivo si no se define ningún criterio de terminación predeterminado.

Por ejemplo, a través del análisis, se puede encontrar que el algoritmo se ha mejorado significativamente desde la cuarta generación hasta la quinta generación.

from pymoo.util.running_metric import RunningMetric

running = RunningMetric(delta_gen=5,
                        n_plots=3,
                        only_if_n_plots=True,
                        key_press=False,
                        do_show=True)

for algorithm in res.history[:15]:
    running.notify(algorithm)

Trazar hasta que la población muestre convergencia, con solo ligeras mejoras aquí:

from pymoo.util.running_metric import RunningMetric

running = RunningMetric(delta_gen=10,
                        n_plots=4,
                        only_if_n_plots=True,
                        key_press=False,
                        do_show=True)

for algorithm in res.history:
    running.notify(algorithm)

4) El conjunto de Pareto es conocido o aproximado

Para problemas reales, se deben utilizar aproximaciones. Al ejecutar el algoritmo varias veces y extraer soluciones no dominadas de todos los conjuntos de soluciones , se puede obtener una solución aproximada. Si sólo hay una ejecución , otra opción es utilizar el conjunto resultante de soluciones no dominadas como aproximación . Sin embargo, los resultados sólo reflejan el progreso del algoritmo en su convergencia al conjunto final.

from pymoo.indicators.igd import IGD

metric = IGD(pf, zero_to_one=True)

igd = [metric.do(_F) for _F in hist_F]

plt.plot(n_evals, igd,  color='black', lw=0.7, label="Avg. CV of Pop")
plt.scatter(n_evals, igd,  facecolor="none", edgecolor='black', marker="p")
plt.axhline(10**-2, color="red", label="10^-2", linestyle="--")
plt.title("Convergence")
plt.xlabel("Function Evaluations")
plt.ylabel("IGD")
plt.yscale("log")
plt.legend()
plt.show()
from pymoo.indicators.igd_plus import IGDPlus

metric = IGDPlus(pf, zero_to_one=True)

igd = [metric.do(_F) for _F in hist_F]

plt.plot(n_evals, igd,  color='black', lw=0.7, label="Avg. CV of Pop")
plt.scatter(n_evals, igd,  facecolor="none", edgecolor='black', marker="p")
plt.axhline(10**-2, color="red", label="10^-2", linestyle="--")
plt.title("Convergence")
plt.xlabel("Function Evaluations")
plt.ylabel("IGD+")
plt.yscale("log")
plt.legend()
plt.show()

Para indicadores detallados, consulte:

https://pymoo.org/misc/indicators.html

referencia:

https://pymoo.org/index.html

Supongo que te gusta

Origin blog.csdn.net/mengjizhiyou/article/details/124647597
Recomendado
Clasificación