Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Ejemplo de Un Algoritmo Genético

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 22

Índice

Enunciado del Trabajo Práctico 2

Descripción de la metodología 4

Descripción Herramientas 6

Forma de trabajo abordada 7

Código 8

Análisis de gráficas 15
Parte 1: funcionamiento del algoritmo con/sin elitismo. 15
Parte 2: gráficas con modificaciones de parámetros 18

Conclusión 21
Hacer un programa que utilice un Algoritmo Genético Canónico para buscar un
máximo de la función:
f(x) = (x/coef)​2​ en el dominio [0 , 2​30​ -1]
donde coef = 2​30​ -1

Teniendo en cuenta los siguientes datos:


–Probabilidad de Crossover = 0,75
–Probabilidad de Mutación = 0,05
–Población Inicial: 10 individuos
–Ciclos del programa: 20
–Método de Selección: Ruleta
–Método de Crossover: 1 Punto
–Método de Mutación: invertida

Opción A:
● El programa debe mostrar, finalmente, el Cromosoma correspondiente al
valor máximo, el valor máximo, mínimo y promedio obtenido de cada
población.

● Mostrar la impresión de las tablas de mínimos, promedios y máximos para


20, 100 y 200 corridas.

● Deben presentarse las gráficas de los valores Máximos, Mínimos y


Promedios de la función objetivo por cada generación luego de correr el
algoritmo genético 20, 100 y 200 iteraciones (una gráfica por cada conjunto
de iteraciones)

● Realizar comparaciones de las salidas corriendo el mismo programa en


distintos ciclos de corridas y además realizar todos los cambios que

Página 1
considere oportunos en los parámetros de entrada de manera de enriquecer
sus conclusiones.

Opción B:

Se entiende por elite a un grupo pequeño que por algún motivo, característica,
facultad o privilegio es superior o mejor en comparación al grueso de una población
determinada; con cualidades o prerrogativas de las que la gran mayoría no
disfrutan.
Un algoritmo genético, desde el punto de vista de la optimización, es un método
poblacional de búsqueda dirigida basada en probabilidad. Bajo una condición
bastante débil, que el algoritmo mantenga elitismo, es decir, guarde siempre al
mejor elemento de la población sin hacerle ningún cambio, se puede demostrar que
el algoritmo converge en probabilidad al óptimo. En otras palabras, al aumentar el
número de iteraciones, la probabilidad de tener el óptimo en la población tiende a
uno.
Luego el método más utilizado para mejorar la convergencia de los algoritmos
genéticos es el elitismo.
Este método consiste básicamente para nuestro trabajo en realizar la etapa de
selección de la siguiente manera:
* Se realiza un muestreo en una élite de “ere” miembros es decir para nuestro
ejercicio se seleccionan dos cromosomas que poseen el mejor fitness de entre los
mejores de la población inicial y se incorporan directamente a la población siguiente,
sin pasar por la población intermedia.
*El proceso se repite para cada población que se va generando hasta completar el
número de veces que se ejecutará el algoritmo genético. Se solicita la ejecución de
100 iteraciones.
Para esta segunda parte del trabajo se deberá utilizar elitismo, mostrar nuevamente
las salidas por pantalla y las gráficas solicitadas en la PARTE A pero en este caso
considerando la aplicación de elitismo.

Resolver el ejercicio realizando 100 iteraciones del algoritmo.

Página 2
Descripción de la metodología
def f(​x​)

Es la función matemática que estamos estudiando para obtener sus máximos y


mínimos.

def fitness(​funcion, total​)

Esta función devuelve el valor del fitness de un cromosoma, es decir, qué tan bien
aplica dicho cromosoma en la función dada. El parámetro ​funcion ​refiere al
cromosoma aplicado a la función f(x), y ​total ​es el parámetro que indica la sumatoria
de todos los cromosomas aplicados a la función f(x) de la población.

def ruleta()

Dentro de esta función se crea un arreglo de longitud variable (dado que al utilizar
los números redondeados la suma de todos los fitness no siempre dan como
resultado el mismo entero) asignando en cada posición un único cromosoma
repetido la cantidad de veces que indique su fitness.
Luego con un número aleatorio, de 0 a la longitud del arreglo de la ruleta,
obtenemos el cromosoma elegido.

def poblacion_inicial()

Esta función genera la primer población con la que se empezará a trabajar


evolutivamente. Lo que hará es generar una matriz de 30 “0” y “1” ubicados
aleatoriamente para cada individuo de la población y los unirá para crear un “string”

Página 3
de 30 genes para cada entidad, guardandolos en el arreglo llamado
str_cromosomas​. Luego convertirá los “string” a sus enteros correspondientes en
el sistema decimal, para guardarlos en el arreglo ​int_cromosomas ​y poder
trabajarlos matemáticamente luego.

def elitismo()

Esta función hace que los dos mejores cromosomas, juzgándolos por su fitness,
pasen directamente a la próxima población sin ser modificados. Esto lo hace
buscando entre todos los cromosomas los dos de mayor fitness y guardándolos en
las dos últimas posiciones de la siguiente población.

def crossover()

La función crossover tiene como responsabilidad determinar si se va a realizar el


intercambio de bloques de genes entre dos padres, denominado como “crossover”.
Esto lo logra generando un número aleatorio entre 0 y 1 y comparándolo con la
probabilidad de crossover establecida. Si el número random que genera es menor a
la probabilidad que suceda, tomamos el string del cromosoma (número binario de 30
genes) y lo cortamos en una posición aleatoria para combinarlos entre sí.
Luego los guarda en el arreglo principal.

def mutación()

Primero valida si se realizará una mutación para el cromosoma con el que esté
trabajando, comparando probabilidades de la misma forma que la función anterior.
Si se da, toma un valor cualquiera de 0 a 30 para ubicarse en dicha posición y
cambiar el valor del gen. Es decir, si el gen es 0 cambiarlo a 1 o viceversa.

Página 4
Descripción Herramientas
Elegimos utilizar Python porque nos pareció un lenguaje muy potente en
cuanto a funciones matemáticas y su implementación. Además de que lo usamos
como excusa para acercarnos un poco más a este lenguaje, y aprender cómo
utilizarlo.
Como IDE decidimos utilizar PyCharm, ya que uno de nosotros estaba un
tanto familiarizado con este entorno.
Si bien al comienzo fue un gran desafío programar, aprender el lenguaje y la
metodología de los algoritmos genéticos logramos completar la tarea asignada con
resultados gratificantes.
Utilizamos las siguientes 3 bibliotecas que ya vienen junto con PyCharm:
- random
- matplotlib.pyplot
- numpy
- pandas
- os

random ​nos permitió generar números aleatorios para poder hacer probabilidades,
dentro de la ruleta, el crossover, la mutación.

matplotlib.pyplot​, la utilizamos para graficar el máximo, mínimo y el promedio de


cada población. Esta biblioteca fue determinante a la hora de elegir el lenguaje.

numpy ​nos ayudó a generar arreglos de números que utilizamos para hacer el eje x
de las gráficas.

pandas​, la utilizamos para poder escribir la tabla de todas las poblaciones en excel.

os​ nos permite acceder a funciones del sistema operativo, en nuestro caso buscar
el archivo de Excel y abrirlo.

Página 5
Forma de trabajo abordada

El trabajo lo hicimos a distancia, como la situación lo requería, utilizando la


aplicación Discord™ pudimos comunicarnos y así compartir pantalla en tiempo real.
Esto nos ayudó a realizar un continuo brainstorming de manera ordenada y así no
pisar nuestro propio código. Decidimos también que uno solo fuera el que escriba el
código mientras los demás aportan ideas para implementar en el algoritmo, en
paralelo con un proceso de investigación y aprendizaje sobre el lenguaje Python.
Esto nos resultó muy eficiente ya que pudimos dividir las responsabilidades sin
dividir el trabajo, y así realizamos un único código siguiendo un único formato.
Para hacer el informe nos valimos de Google Drive, con Google Docs, para
que todos tengan acceso al informe y así todos pueden ir editando el archivo a
tiempo real.

Página 6
Código
import random
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import os

# Constantes
tam_poblacion = 10
corridas = 0
coef = (2 ** 30) - 1
str_cromosomas = [""] * tam_poblacion
int_cromosomas = [0] * tam_poblacion
array_fitness = [0] * tam_poblacion
chances_crossover = 0.75
chances_mutacion = 0.05

# Funciones

# Pregunta si hace elitismo o no


def valida_elitismo():
opcion_elite = ''

while opcion_elite.lower() != 'n' and opcion_elite.lower() != 's':


opcion_elite = input("ELITISMO s/n: ")

if opcion_elite.lower() == 's':
return True
else:
return False

Página 7
# Hace la función matemática
def f(x):
return (x / coef) ** 2

# Calcula fitness
def fitness(funcion, total):
return funcion / total

# Genera la ruleta
def ruleta():
base = 0
cant_casilleros = 0

for i in range(0, tam_poblacion):


casilleros = round(array_fitness[i] * 100)
cant_casilleros = cant_casilleros + casilleros
roulette = [0] * cant_casilleros

for i in range(0, tam_poblacion):


casilleros = round(array_fitness[i] * 100)

for j in range(base, base + casilleros):


roulette[j] = i
base = base + casilleros

bolilla = random.randint(0, cant_casilleros - 1)


return roulette[bolilla]

# Genera la población inicial


def poblacion_inicial():
col = 30

Página 8
fila = tam_poblacion
matriz_cromosomas = [[0 for j in range(col)] for i in range(fila)]
for f in range(0, fila):
for c in range(0, col):
matriz_cromosomas[f][c] = str(random.randint(0, 1))
for f in range(0, fila):
str_cromosomas[f] = ""
for c in range(0, col):
str_cromosomas[f] = str_cromosomas[f] + matriz_cromosomas[f][c]
for f in range(0, fila):
int_cromosomas[f] = int(str_cromosomas[f], 2)

# Hace el crossover
def crossover():
for i in range(0, n, 2):
cros = random.random()

if cros < chances_crossover:


pos_corte = random.randint(0, 30)
cromosoma1 = nueva_poblacion[i]
cromosoma2 = nueva_poblacion[i + 1]
hijo1 = cromosoma1[0: pos_corte] + cromosoma2[pos_corte: len(cromosoma2)]
hijo2 = cromosoma2[0: pos_corte] + cromosoma1[pos_corte: len(cromosoma1)]
str_cromosomas[i] = hijo1
str_cromosomas[i + 1] = hijo2
int_cromosomas[i] = int(hijo1, 2)
int_cromosomas[i + 1] = int(hijo2, 2)

# Hace mutación
def mutacion():
for i in range(0, n):
muta = random.random()

Página 9
if muta < chances_mutacion:
pos_mutacion = random.randint(0, 29)
cromosoma1 = str_cromosomas[i]

if cromosoma1[pos_mutacion] == "1":
cromosoma1 = cromosoma1[:pos_mutacion] + "0" + cromosoma1[pos_mutacion +
1:]

else:
cromosoma1 = cromosoma1[:pos_mutacion] + "1" + cromosoma1[pos_mutacion +
1:]

str_cromosomas[i] = cromosoma1
int_cromosomas[i] = int(cromosoma1, 2)

# Hace elitismo
def elitismo():
busca1 = 0
valor1 = 0
busca2 = 0
valor2 = 0
for i in range(0, tam_poblacion):
if busca1 < f(int_cromosomas[i]):
busca1 = f(int_cromosomas[i])
valor1 = i
for i in range(0, tam_poblacion):
​ if i!= valor1 and busca2 < f(int_cromosomas[i]):
busca2 = f(int_cromosomas[i])
valor2 = i
nueva_poblacion[8] = str_cromosomas[valor1]
nueva_poblacion[9] = str_cromosomas[valor2]
str_cromosomas[8] = nueva_poblacion[8]
str_cromosomas[9] = nueva_poblacion[9]
int_cromosomas[8] = int(nueva_poblacion[8], 2)
int_cromosomas[9] = int(nueva_poblacion[9], 2)

Página 10
# Main
continuar = True
while continuar:
mvp_fitness = [0] * 3
mvp_fitness[1] = 0

elite = valida_elitismo()

corridas = int(input("Ingrese la cantidad de CORRIDAS: "))

x = np.linspace(0, corridas, corridas) # Eje x para graficar


array_maximos = [0] * corridas # Guarda los maximos de cada corrida
array_minimos = [0] * corridas # Guarda los minimos de cada corrida
array_promedios = [0] * corridas # Guarda los promedios de cada corrida
nueva_poblacion = [""] * tam_poblacion
datos = [0] * corridas

poblacion_inicial()

for i in range(0, corridas):


tabla = [0] * 4
acum_promedio = 0
mayor = 0
menor = coef
acum_fitness = 0

for j in range(0, tam_poblacion):


acum_fitness = acum_fitness + f(int_cromosomas[j]) # Calcula acum para fitness

for j in range(0, tam_poblacion):


array_fitness[j] = fitness(f(int_cromosomas[j]),
acum_fitness) # Evalua y guarda el fitness de cada individuo
acum_promedio = acum_promedio + f(int_cromosomas[j]) # Calcula promedio con
la funcion

Página 11
for j in range(0, tam_poblacion):
if mvp_fitness[1] < f(int_cromosomas[j]):
mvp_fitness[0] = i + 1
mvp_fitness[1] = f(int_cromosomas[j])
mvp_fitness[2] = str_cromosomas[j]

if mayor < int_cromosomas[j]:


mayor = int_cromosomas[j]

if menor > int_cromosomas[j]:


menor = int_cromosomas[j]

array_maximos[i] = f(mayor)
array_minimos[i] = f(menor)
array_promedios[i] = acum_promedio / 10

tabla[0] = f(menor)
tabla[1] = f(mayor)
tabla[2] = bin(mayor)
tabla[3] = acum_promedio/10

datos[i] = tabla

if elite:
elitismo()
n = tam_poblacion - 2
else:
n = tam_poblacion

for j in range(0, n):


nueva_poblacion[j] = str_cromosomas[ruleta()]

crossover()

mutacion()

Página 12
t = pd.DataFrame(data=datos, columns=["Mínimo", "Máximo", "Cromosoma Máx.",
"Promedio"])
t1 = pd.DataFrame(data=mvp_fitness, index=["Población", "Número", "Cromosoma"],
columns=["MVP"])
outfile = "Tabla.xlsx"
writer = pd.ExcelWriter(outfile, engine="xlsxwriter")
t.to_excel(writer, sheet_name="Tabla")
t1.to_excel(writer, sheet_name="MVP")
writer.save()

# excelTabla = "ruta del usuario donde se ubica el archivo de Excel"


# os.startfile(excelTabla) # Abre el archivo de Excel

plt.plot(x, array_maximos, 'r-', label='Maximo')


plt.plot(x, array_minimos, 'b-', label='Minimo')
plt.plot(x, array_promedios, 'g-', label='Promedio')
plt.xlabel('Corridas')
plt.ylabel('f(x)', multialignment='center')
plt.legend()
plt.suptitle("Gráfica de " + str(corridas) + " corridas")
plt.title("El maximo alcanzado fue " + str(mvp_fitness[1]), fontsize=10)
plt.show()

continuar_aux = ''
while continuar_aux.lower() != 'n' and continuar_aux.lower() != 's':

continuar_aux = input("Desea continuar? s/n ")

if continuar_aux.lower() == 's':
continuar = True
else:
continuar = False

Página 13
Análisis de gráficas

Parte 1: funcionamiento del algoritmo con/sin elitismo.


En esta primera parte del análisis, se presentan las gráficas del algoritmo con
20, 100 y 200 iteraciones respectivamente y sin la aplicación de elitismo, para luego
compararlas con una gráfica de 100 iteraciones que sí posea elitismo.

Figura 1. Gráfica de ejecución del algoritmo sin elitismo y con 20 corridas.

Página 14
Figura 2. Gráfica de ejecución del algoritmo sin elitismo y con 100 corridas.

Figura 3. Gráfica de ejecución del algoritmo sin elitismo y con 200 corridas.

En estas gráficas se pueden apreciar distintas cuestiones:


● La mutación puede afectar a cualquier cromosoma de la población. Esto
significa que incluso los individuos más aptos pueden verse afectados por
una modificación en alguno de sus genes, dando así lugar a otro individuo
mejor, o peor. Esto se puede visualizar en los picos de la gráfica: los azules
corresponden a individuos que pasaron a ser valores mínimos, y los rojos
corresponden a individuos que mejoraron. Sin embargo, los máximos pueden
empeorar dado este fenómeno.
● El tiempo de convergencia a un valor cercano al ideal es extenso, y depende
en gran medida de la población inicial del algoritmo.

Página 15
Figura 4. Gráfica de ejecución del algoritmo con elitismo y con 100 iteraciones.

Si ahora vemos esta gráfica donde se aplica elitismo, podemos visualizar


que la mutación no afecta a los valores máximos directamente, puesto que éstos no
se ven afectados. Sin embargo, puede ocurrir que un individuo semejante y no
elitista sufra una mutación y se genere un cromosoma más apto que los elitistas,
dando lugar a un crecimiento en la gráfica de máximos.
Esto nos permite demostrar que aplicando elitismo, los máximos no sólo
perduran en el tiempo sino que tienden a mejorar, llevando a una convergencia
óptima.

Tablas correspondientes:

● Figura 1: ​Tabla 20 corridas


● Figura 2: ​Tabla 100 corridas
● Figura 3: ​Tabla 200 corridas
● Figura 4: ​Tabla 100 corridas

Página 16
Parte 2: gráficas con modificaciones de parámetros
En esta segunda parte, el análisis se centrará en cómo cambia el
funcionamiento del algoritmo al modificar el parámetro de la mutación. Fue realizado
en las siguientes formas y variaciones:
● Gráficas SIN MUTACIONES
○ Gráfica SIN ELITISMO

Figura 5. Gráficas de ejecución del algoritmo sin mutación y sin elitismo.

Este es un perfecto ejemplo de lo que ocurre con frecuencia cuando


corremos el algoritmo sin mutaciones ni elitismo. Los cromosomas convergen
rápidamente hacia un valor y se estancan allí. A su vez, la convergencia del
algoritmo hacia su supuesto valor óptimo también es muy dependiente de los
valores de la población inicial, arribando a un valor similar o incluso peor que los
valores máximos iniciales: en estos ejemplos podemos ver cómo en vez de crecer,
los valores son peores. El estancamiento prematuro es obra de la falta de variedad
de los cromosomas, mientras que la carencia de mejora de la población inicial
corresponde a la inexistencia de elitismo, lo cual implica que se realizarán
crossovers entre cromosomas con alto fitness y cromosomas con bajo fitness
indistintamente.
Sin importar la cantidad de corridas que tengamos con estos parámetros
siempre llegamos a los mismos resultados.

Página 17
○ Gráfica CON ELITISMO

Figura 6.​ ​Gráficas de ejecución del algoritmo sin mutación y con elitismo.

A diferencia de los resultados sin elitismo, vemos como ahora los máximos se
mantienen, dado que en cada población nueva generada existirán ciertos
cromosomas con características elitistas que no se verán sometidos al crossover,
sino que por el contrario se incorporarán a la población siguiente directamente. Esto
implica que los valores máximos sólo pueden mantenerse o crecer. Sin embargo, la
falta de mutaciones en el algoritmo hace que existan altas probabilidades de que
dichos valores se estanquen rápidamente, dada la invariancia de estos
cromosomas.
● Gráficas CON MUTACIÓN
○ Gráfica SIN ELITISMO

Figura 7.​ ​Gráficas de ejecución del algoritmo con mutación y sin elitismo.

Página 18
En la figura 7 podemos apreciar que la aplicación del método de mutación
mejora notablemente el funcionamiento del algoritmo, brindando un mayor alcance
de posibilidades en la búsqueda de individuos y evitando, en líneas generales, el
estancamiento, logrando una convergencia más atenuada a lo largo de las corridas.
Sin embargo, el hecho de no aplicar elitismo implica correr con el riesgo de perder,
durante la selección, ciertos individuos que eran altamente aptos.

○ Gráfica CON ELITISMO

Figura 8.​ ​Gráficas de ejecución del algoritmo con mutación y con elitismo.

Finalmente, nos encontramos con estas gráficas, donde existe mutación y


elitismo. Analizando su comportamiento con los criterios anteriores, podemos
interpretar lo siguiente:
➢ La convergencia es gradual
➢ La convergencia es óptima
➢ La mutación contribuye a la aparición de nuevos individuos candidatos a ser
óptimos.
➢ El elitismo asegura la persistencia de valores máximos a lo largo del tiempo.
➢ El elitismo permite, en conjunto con la mutación, una convergencia más
homogénea. Esto es, una población final con valores máximos y mínimos
semejantes.

Página 19
Conclusión
Podríamos decir que la construcción de un algoritmo genético es necesaria
para comprender el funcionamiento de esta metodología de optimización de tareas.
Sin embargo, consideramos que no es suficiente. El posterior análisis de datos de
su comportamiento es fundamental, puesto que nos permitirá detectar cuestiones
teóricas que son determinantes en el algoritmo.
En primer lugar, la importancia de la existencia de mutaciones en el
algoritmo, sobre todo a medida que el número de poblaciones generadas aumenta.
El algoritmo genético debe, no sólo seleccionar de entre lo mejor que ha
encontrado, sino procurar encontrar mejores individuos. En esto, la mutación es un
elemento clave, dado que permite optimizar la exploración y el sondeo de la mayor
cantidad de individuos posibles.
Por otro lado, el algoritmo no debe perder de vista a los mejores individuos
encontrados: si bien las posibilidades de selección de éstos es alta, no siempre es
total, por lo que también resulta conveniente asegurar la continuidad de estos
individuos en el tiempo.
En el análisis de las gráficas consideramos también la aplicación (o no) del
método elitista en el algoritmo. Esto nos permitió advertir los beneficios que este
fenómeno trae al momento de buscar una convergencia óptima: población tras
población, los individuos más aptos van perdurando en el tiempo siempre y cuando
no se encuentre ningún individuo mejor. Caso contrario, perderán el carácter de
elitistas, viéndose forzados a competir junto al resto de los individuos menos aptos
nuevamente y, consecuentemente, brindando a éstos últimos la posibilidad de
mejorar con mayor rapidez en caso de darse el crossover.
Podríamos decir, en principio, que los dos objetivos principales de los
algoritmos genéticos son dos: la exploración y la explotación. Ahora bien, a fin de
lograr ambos objetivos, tendríamos que combinar los métodos hasta ahora
mencionados: la mutación y el elitismo. Y efectivamente, esta acción nos permite no
sólo la convergencia a un máximo óptimo sino también la vasta exploración de

Página 20
múltiples individuos existentes en el caso estudiado, asegurando así que los valores
finales hallados sean lo más cercanos posible al valor ideal.

Página 21

También podría gustarte