遗传算法与深度学习实战——使用进化策略实现EvoLisa
import random
import numpy as np
from deap import algorithms
from deap import base
from deap import creator
from deap import tools
import os
import cv2
import urllib.request
import matplotlib.pyplot as plt
from IPython.display import clear_output
def load_target_image(image_url, color=True, size=None):
image_path = "target_image"
urllib.request.urlretrieve(image_url,image_path)
if color:
target = cv2.imread(image_path, cv2.IMREAD_COLOR)
# Switch from bgr to rgb
target = cv2.cvtColor(target, cv2.COLOR_BGR2RGB)
else:
target = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
if size:
# Only resizes image if it is needed!
target = cv2.resize(src=target, dsize=size, interpolation=cv2.INTER_AREA)
return target
def show_image(img_arr):
plt.figure(figsize=(10,10))
plt.axis("off")
plt.imshow(img_arr/255)
plt.show()
def show_results(history, img_arr, org):
plt.figure(figsize=(10,10))
plt.tight_layout()
plt.subplot(221)
plt.axis("off")
plt.imshow(img_arr/255)
plt.title('best of generation')
plt.subplot(222)
plt.axis("off")
plt.imshow(org/255)
plt.title('target image')
plt.subplot(212)
lh = len(history)
plt.xlim([lh-50, lh])
plt.plot(history)
plt.title('min fitness by generation')
plt.show()
polygons = 255 #@param {type:"slider", min:10, max:1000, step:1}
size = 32 #@param {type:"slider", min:16, max:1000, step:2}
target_image = "Mona Lisa" #@param ["Mona Lisa", "Stop Sign", "Landscape", "Celebrity", "Art", "Abstract"]
report_every_gen = 10 #@param {type:"slider", min:1, max:100, step:1}
number_generations = 10000 #@param {type:"slider", min:100, max:10000, step:10}
POLYGONS = polygons
SIZE = (size, size)
target_urls = { "Mona Lisa" : 'https://upload.wikimedia.org/wikipedia/commons/b/b7/Mona_Lisa_face_800x800px.jpg',
"Stop Sign" : 'https://images.uline.com/is/image//content/dam/images/H/H2500/H-2381.jpg',
"Landscape" : 'https://www.adorama.com/alc/wp-content/uploads/2018/11/landscape-photography-tips-yosemite-valley-feature.jpg',
"Celebrity" : 'https://s.abcnews.com/images/Entertainment/WireAP_91d6741d1954459f9993bd7a2f62b6bb_16x9_992.jpg',
"Art" : "http://www.indianruminations.com/wp-content/uploads/what-is-modern-art-definition-2.jpg",
"Abstract" : "https://scx2.b-cdn.net/gfx/news/2020/abstractart.jpg"
}
target_image_url = target_urls[target_image]
target = load_target_image(target_image_url, size=SIZE)
show_image(target)
print(target.shape)
#polygon genes
GENE_LENGTH = 10
NUM_GENES = POLYGONS * GENE_LENGTH
#create a sample invidiual
individual = np.random.uniform(0,1,NUM_GENES)
print(individual)
# [0.62249533 0.44090963 0.14777921 ... 0.57283261 0.9325435 0.25907929]
def extract_genes(genes, length):
for i in range(0, len(genes), length):
yield genes[i:i + length]
def render_individual(individual):
if isinstance(individual,list):
individual = np.array(individual)
canvas = np.zeros(SIZE+(3,))
radius_avg = (SIZE[0] + SIZE[1]) / 2 / 6
genes = extract_genes(individual, GENE_LENGTH)
for gene in genes:
try:
overlay = canvas.copy()
# alternative drawing methods circle or rectangle
# circle brush uses a GENE_LENGTH of 7
# center = (0, 1) [2]
# radius = (2) [3]
# color = (3,4,5) [6]
# alpha = (6) [7]
#cv2.circle(
# overlay,
# center=(int(gene[1] * SIZE[1]), int(gene[0] * SIZE[0])),
# radius=int(gene[2] * radius_avg),
# color=color,
# thickness=-1,
#)
# rectangle brush uses GENE_LENGTH = 8
# top left = (0, 1) [2]
# btm right = (2, 3) [4]
# color = (4, 5, 6) [7]
# alpha = (7) [8]
#cv2.rectangle(overlay, (x1, y1), (x2, y2), color, -1)
# polyline brush uses GENE_LENGTH = 10
# pts = (0, 1), (2, 3), (4, 5) [6]
# color = (6, 7, 8) [9]
# alpha = (9) [10]
x1 = int(gene[0] * SIZE[0])
x2 = int(gene[2] * SIZE[0])
x3 = int(gene[4] * SIZE[0])
y1 = int(gene[1] * SIZE[1])
y2 = int(gene[3] * SIZE[1])
y3 = int(gene[5] * SIZE[1])
color = (gene[6:-1] * 255).astype(int).tolist()
pts = np.array([[x1,y1],[x2,y2],[x3,y3]], np.int32)
pts = pts.reshape((-1, 1, 2))
pts = np.array([[x1,y1],[x2,y2],[x3,y3]])
cv2.fillPoly(overlay, [pts], color)
alpha = gene[-1]
canvas = cv2.addWeighted(overlay, alpha, canvas, 1 - alpha, 0)
except:
pass
return canvas
render = render_individual(individual)
show_image(render)
from skimage.metrics import structural_similarity as ss
#@title Fitness Function
def fitness_mse(render):
"""Calculates Mean Square Error Fitness for a render"""
error = (np.square(render - target)).mean(axis=None)
return error
def fitness_ss(render):
"""Calculated Structural Similiarity Fitness"""
index = ss(render, target, multichannel=True)
return 1-index
print(fitness_mse(render))
IND_SIZE = NUM_GENES
MIN_VALUE = -1
MAX_VALUE = 1
MIN_STRATEGY = 0.5
MAX_STRATEGY = 5
CXPB = .6
MUTPB = .3
creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
creator.create("Individual", list, typecode="d", fitness=creator.FitnessMin, strategy=None)
creator.create("Strategy", list, typecode="d")
def generateES(icls, scls, size, imin, imax, smin, smax):
ind = icls(random.uniform(imin, imax) for _ in range(size))
ind.strategy = scls(random.uniform(smin, smax) for _ in range(size))
return ind
def checkStrategy(minstrategy):
def decorator(func):
def wrappper(*args, **kargs):
children = func(*args, **kargs)
for child in children:
for i, s in enumerate(child.strategy):
if s < minstrategy:
child.strategy[i] = minstrategy
return children
return wrappper
return decorator
def uniform(low, up, size=None):
try:
return [random.uniform(a, b) for a, b in zip(low, up)]
except TypeError:
return [random.uniform(a, b) for a, b in zip([low] * size, [up] * size)]
def clamp(low, up, n):
return max(low, min(n, up))
def custom_blend(ind1, ind2, alpha):
for i, (x1, s1, x2, s2) in enumerate(zip(ind1, ind1.strategy,
ind2, ind2.strategy)):
# Blend the values
gamma = (1. + 2. * alpha) * random.random() - alpha
ind1[i] = clamp(0.0, 1.0, (1. - gamma) * x1 + gamma * x2)
ind2[i] = clamp(0.0, 1.0, gamma * x1 + (1. - gamma) * x2)
# Blend the strategies
gamma = (1. + 2. * alpha) * random.random() - alpha
ind1.strategy[i] = (1. - gamma) * s1 + gamma * s2
ind2.strategy[i] = gamma * s1 + (1. - gamma) * s2
return ind1, ind2
toolbox = base.Toolbox()
toolbox.register("individual", generateES, creator.Individual, creator.Strategy,
IND_SIZE, MIN_VALUE, MAX_VALUE, MIN_STRATEGY, MAX_STRATEGY)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
toolbox.register("mate", custom_blend, alpha=0.5)
toolbox.register("mutate", tools.mutESLogNormal, c=1.0, indpb=0.06)
toolbox.register("select", tools.selTournament, tournsize=5)
toolbox.decorate("mate", checkStrategy(MIN_STRATEGY))
toolbox.decorate("mutate", checkStrategy(MIN_STRATEGY))
def evaluate(individual):
render = render_individual(individual)
print('.', end='')
return fitness_mse(render), #using MSE for fitness
#toolbox.register("mutate", tools.mutGaussian, mu=0.0, sigma=.1, indpb=.25)
toolbox.register("evaluate", evaluate)
NGEN = number_generations
RGEN = report_every_gen
CXPB = .6
MUTPB = .3
MU, LAMBDA = 100, 250
pop = toolbox.population(n=MU)
hof = tools.HallOfFame(1)
stats = tools.Statistics(lambda ind: ind.fitness.values)
stats.register("avg", np.mean)
stats.register("std", np.std)
stats.register("min", np.min)
stats.register("max", np.max)
best = None
history = []
for g in range(NGEN):
pop, logbook = algorithms.eaMuCommaLambda(pop, toolbox, mu=MU, lambda_=LAMBDA,
cxpb=CXPB, mutpb=MUTPB, ngen=RGEN, stats=stats, halloffame=hof, verbose=False)
best = hof[0]
#pop, logbook = algorithms.eaSimple(pop, toolbox,
# cxpb=CXPB, mutpb=MUTPB, ngen=100, stats=stats, halloffame=hof, verbose=False)
#best = hof[0]
clear_output()
render = render_individual(best)
history.extend([clamp(0.0, 5000.0, l["min"]) for l in logbook])
show_results(history, render, target)
print(f"Gen ({(g+1)*RGEN}) : best fitness = {fitness_mse(render)}")