遗传算法与深度学习实战——使用进化策略实现EvoLisa

时间:2024-09-29 14:58:53
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)}")