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

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

0. 前言

我们已经学习了进化策略 (Evolutionary Strategies, ES) 的基本原理,并且尝试使用 ES 解决了函数逼近问题。函数逼近是一个很好的基准问题,但为了充分展示 ES 的作用,本节中,我们将重新思考 EvoLisa 问题,采用 ES 作为解决策略,以将 ES 和常规遗传算法进行对比。

1. 使用进化策略实现 EvoLisa

接下来,使用进化策略 (Evolutionary Strategies, ES) 通过复现 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)}")

2. 运行结果

下图显示了代码的运行结果,作为对比,图中还显示了使用经典遗传算法生成的结果。

代码运行结果

相关链接

遗传算法与深度学习实战(1)——进化深度学习
遗传算法与深度学习实战(4)——遗传算法(Genetic Algorithm)详解与实现
遗传算法与深度学习实战(5)——遗传算法中常用遗传算子
遗传算法与深度学习实战(6)——遗传算法框架DEAP
遗传算法与深度学习实战(7)——DEAP框架初体验
遗传算法与深度学习实战(10)——使用遗传算法重建图像
遗传算法与深度学习实战(14)——进化策略详解与实现

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

盼小辉丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值