基于DEAP库的python进化算法-5.遗传算法求解TSP问题的改进

本文介绍了如何使用DEAP库改进遗传算法求解旅行商问题(TSP)。通过引入精英保留策略,结合局部搜索算法2-OPT,以及超参数调参,包括迭代步数、族群规模和变异概率的自适应调整,显著提升了算法的收敛速度和解的质量。实验结果显示,改进后的遗传算法在求解精度和效率上均优于原版和基准算法。
摘要由CSDN通过智能技术生成

前言

前面一节我们尝试用GA求解TSP问题,简单遗传算法总是不能很好收敛到一个较优的解,在用时和求解精度上都被贪心算法吊打。末尾我们总结了三个可能的改进方向,这一次我们沿着这三个方向试着改进简单遗传算法,以提升其性能。

一.精英保留策略

1.精英保留策略

在简单遗传算法中,我们总是用产生的育种后代族群完全取代父代族群。在真实的生物进化中,总是会有一部分后代由于不能适应环境而夭折,留下来的后代往往是相对来说更加优秀的个体。仿照这个原理,我们可以在SGA中加入自然选择,用精英保存策略(Eliteness preservation strategy)来筛选产生的育种后代并保留其中较为优秀的个体,这样可以使得算法更快收敛。
精英保留策略如下:

  • 在选择育种后代时,选择出比原始族群更大的一个育种族群,
  • 对该育种族群进行变异操作-交叉与突变
  • 对变异后的育种族群进行自然选择,只取其中n_pop个个体以保持族群规模。
    精英保留策略代码实现
    为了获取更大的自由度,此处不再调用DEAP内置的eaSimple函数,而是自行用注册的工具箱实现
'''
    精英保留策略
'''
from scipy.spatial import distance
import numpy as np
import matplotlib.pyplot as plt
from deap import creator,tools,base
import random
# 生成城市坐标
def genCity(n,lb = 100,ub = 999):
    return np.random.randint(low=lb,high=ub,size=(n,2))

# 生成城市距离矩阵
def cityDistance(cities):
    return distance.cdist(cities,cities,metric='euclidean')

# 补全路线
def completeRoute(individual):
    return individual + [individual[0]]  # 不要append

def routeDistance(route):
    '''
    :param route: 一条路线,一个序列
    :return: 路线长度
    '''
    if route[0] != route[-1]:
        route = completeRoute(route)
    # 存储路线长度
    routeDist = 0
    # i,j分别是相邻的坐标点
    for i, j in zip(route[0::], route[1::]):
        # 这里直接从CityDist变量中取值了
        routeDist += cityDist[i, j]
    return (routeDist),

# 精英保留策略一遗传算法
def GA_improved(cities,nCities,npop = 100,cxpb = 0.5,mutpb = 0.2,ngen = 200):
    global cityDist
    # 定义问题
    creator.create('FitnessMin',base.Fitness,weights=(-1.0,))   # 单目标优化最小值
    creator.create('Individual',list,fitness = creator.FitnessMin)

    # 定义个体编码
    toolbox = base.Toolbox()
    toolbox.register('Indices',np.random.permutation,range(nCities))    # 创建序列
    toolbox.register('Individual',tools.initIterate,creator.Individual,toolbox.Indices)

    # 创建族群
    toolbox.register('Population',tools.initRepeat,list,toolbox.Individual)
    pop = toolbox.Population(npop)

    # 注册所需工具
    cityDist = cityDistance(cities)
    toolbox.register('evaluate',routeDistance)
    toolbox.register('select',tools.selTournament,tournsize=2)
    toolbox.register('mate',tools.cxOrdered)
    toolbox.register('mutate',tools.mutShuffleIndexes,indpb=0.2)

    # 数据记录
    stats = tools.Statistics(key= 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)
    logbook = tools.Logbook()
    logbook.header = 'gen','nevals','avg','std','min','max'

    # 实现遗传算法
    for gen in range(1,ngen+1):
        # 配种选择
        offspring = toolbox.select(pop,2*npop)
        # 复制,否则在交叉和突变这样的原位操作中,会改变所有select出来的同个体副本
        offspring_copy = list(map(toolbox.clone,offspring))

        # 变异操作-交叉
        for child1,child2 in zip(offspring_copy[::2],offspring_copy[1::2]):
            if random.random() < cxpb:
                toolbox.mate(child1,child2)
                del child1.fitness.values
                del child2.fitness.values

        # 变异操作-突变
        for mutant in offspring_copy:
            if random.random() < mutpb:
                toolbox.mutate(mutant)
                del mutant.fitness.values

        # 对于被改变的个体,重新计算其适应度
        invalid_ind = [ind for ind in offspring_copy if not ind.fitness.valid]
        fitnesses = map(toolbox.evaluate,invalid_ind)

        for ind,fit in zip(invalid_ind,fitnesses):
            ind.fitness.values = fit

        # 环境选择-保留精英,保持种群规模
        pop = tools.selBest(offspring_copy,npop,fit_attr='fitness')

        # 记录数据
        # compile(sequence)# 将每个注册功能应用于输入序列数据,并将结果作为字典返回
        record = stats.compile(pop)
        logbook.record(gen=gen,nevals=len(invalid_ind),**record)

    return pop,logbook


# 可视化
def plotTour(logbookGA_improved):
    gen = logbookGA_improved.select('gen')
    min = logbookGA_improved.select('min')
    avg = logbookGA_improved.select('avg')
    plt.plot(gen,min,'r-',label='Minimum Fitness')
    plt.plot(gen,avg,'b-',label='Average Fitness')
    plt.xlabel('Iteration')
    plt.ylabel('Fitness')
    plt.legend(loc='upper right')
    plt.title('GA with eliteness preservation strategy iterations, Problem size:{nCities}',fontsize = 20)
    plt.tight_layout()


nCities = 30
# 随机生成30个城市坐标
cities = genCity(nCities)
resultPopGA_improved,logbookGA_improved = GA_improved(cities,nCities)
print(logbookGA_improved)
plotTour(logbookGA_improved)

改进后的GA迭代过程如下
在这里插入图片描述
相比于SGA的过程有很大改进
可以看到加入精英保存策略之后,遗传算法的收敛速度大大加快了,遗传算法寻优能力得到了提升。

2.精英策略结合局部搜索算法

遗传算法依靠较大的种群数和变异操作,能够在很大的空间内寻找最优解,它的一个重要优点时不需要问题的具体信息。但是正由于没有利用问题的具体信息,导致GA的局部搜索能力差,在解空间较大的优化问题中,往往需要“人海战术”,产生很大的族群来尝试寻找最优解。

在上一篇文章中我们已经知道2-OPT这种局部搜索方法能够通过uncross路径来改善解的质量,我们可以在变异操作之后,用2-OPT改善族群中的优秀个体,提高搜索效率。

代码实现如下

from scipy.spatial import distance
import numpy as np
import matplotlib.pyplot as plt
from deap import creator,tools,base
import random
# 生成城市坐标
def genCity(n,lb = 100,ub = 999):
    return np.random.randint(low=lb,high=ub,size=(n,2))

# 生成城市距离矩阵
def cityDistance(cities):
    return distance.cdist(cities,cities,metric='euclidean')

# 补全路线
def completeRoute(individual):
    return individual + [individual[0]]  # 不要append

def routeDistance(route):
    '''
    :param route: 一条路线,一个序列
    :return: 路线长度
    '''
    if route[0] != route[-1]:
        route = completeRoute(route)
    # 存储路线长度
    routeDist = 0
    # i,j分别是相邻的坐标点
    for i, j in zip(route[0::], route[1::]):
        # 这里直接从CityDist变量中取值了
        routeDist += cityDist[i, j]
    return (routeDist),

# 2-OPT优化算法
def opt(route,k=2):
    '''
    :param route: 路径
    :param k:
    :return: 返回优化后的路径及其路径长度
    '''
    nCities = len(route)
    optimizedRoute = route
    minDistance = routeDistance(route)
    for i in range(1,nCities-2):
        for j in range(i+k,nCities):
            if j - i <= 1:
                continue
            reversedRoute = route[:i] + route[i:j][::-1] + route[j:]
            reversedRouteDist = routeDistance(reversedRoute)
            # 如果翻转后的路径更优,则更新最优解
            if reversedRouteDist < minDistance:
                optimizedRoute = reversedRoute
                minDistance = reversedRouteDist

    return optimizedRoute,minDistance


# 精英保留策略一遗传算法
def GA_improved(cities,nCities,npop = 100,cxpb = 0.5,mutpb = 0.2,ngen = 200):
    global cityDist
    # 定义问题
    creator.create('FitnessMin',base.Fitness,weights=(-1.0,))   # 单目标优化最小值
    creator.create('Individual',list,fitness = creator.FitnessMin)

    # 定义个体编码
    toolbox = base.Toolbox()
    toolbox.register('Indices',np.random.permutation,range(nCities))    # 创建序列
    toolbox.register('Individual',tools.initIterate,creator.Individual,toolbox.Indices)

    # 创建族群
    toolbox.register('Population',tools.initRepeat,list,toolbox.Individual)
    pop = toolbox.Population(npop)

    # 注册所需工具
    cityDist = cityDistance(cities)
    toolbox.register('evaluate',routeDistance)
    toolbox.register('select',tools.selTournament,tournsize=2)
    toolbox.register('mate',tools.cxOrdered)
    toolbox.register('mutate',tools.mutShuffleIndexes,indpb=0.2)
    toolbox.register('localOpt',opt)    # 注册2-opt
    # 数据记录
    stats = tools.Statistics(key= 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)
    logbook = tools.Logbook()
    logbook.header = 'gen','nevals','avg','std','min','max'

    # 实现遗传算法
    for gen in range(1,ngen+1):
        # 配种选择
        offspring = toolbox.select(pop,2*npop)
        # 复制,否则在交叉和突变这样的原位操作中,会改变所有select出来的同个体副本
        offspring_copy = list(map(toolbox.clone,offspring))

        # 变异操作-交叉
        for child1,child2 in zip(offspring_copy[::2],offspring_copy[1::2]):
            if random.random() < cxpb:
                toolbox.mate(child1,child2)
                del child1.fitness.values
                del child2.fitness.values

        # 变异操作-突变
        for mutant in offspring_copy:
            if random.random() < mutpb:
                toolbox.mutate(mutant)
                del mutant.fitness.values

        # 对于被改变的个体,重新计算其适应度
        invalid_ind = [ind for ind in offspring_copy if not ind.fitness.valid]
        fitnesses = map(toolbox.evaluate,invalid_ind)
        for ind,fit in zip(invalid_ind,fitnesses):
            ind.fitness.values = fit

        # 环境选择-保留精英,保持种群规模
        pop = tools.selBest(offspring_copy,npop,fit_attr='fitness')

        # 对族群中的精英进行优化,也可以对全部个体进行优化
        nOpt = int(npop/10)
        pop_opt = tools.selBest(pop,nOpt)
        for ind in pop_opt:
            ind[:],ind.fitness.values = toolbox.localOpt(ind)

        # 记录数据
        # compile(sequence)# 将每个注册功能应用于输入序列数据,并将结果作为字典返回
        record = stats.compile(pop)
        logbook.record(gen=gen,nevals=len(invalid_ind),**record)

    return pop,logbook


# 可视化
def plot(logbookGA_improved):
    gen = logbookGA_improved.select('gen')
    min = logbookGA_improved.select('min')
    avg = logbookGA_improved.select('avg')
    fig = plt.figure()
    fig.add_subplot(111)
    plt.plot(gen,min,'r-',label='Minimum Fitness')
    plt.plot(gen,avg,'b-',label='Average Fitness')
    plt.xlabel('Iteration')
    plt.ylabel('Fitness')
    plt.legend(loc='upper right')
    plt.title('GA with eliteness preservation strategy iterations, Problem size:{%d}'%nCities,fontsize = 10)
    plt.tight_layout()
    plt.show()

#
# 路径可视化
def plotTour(tour, cities):
    start = tour[0:1]
    fig = plt.figure()
    fig.add_subplot(111)
    for i, j in zip(tour[0::], tour[1::]):
        plt.plot([cities[i, 0], cities[j, 0]], [cities[i, 1], cities[j, 1]],'bo-')
    plt.plot(cities[start, 0], cities[start, 1], 'rD-')
    plt.axis('scaled')
    plt.axis('off')
    plt.title('30cities_GA_opt_TSP')
    plt.show()

nCities = 30
# 随机生成30个城市坐标
cities = genCity(nCities)
resultPopGA_improved,logbookGA_improved = GA_improved(cities,nCities)
print(logbookGA_improved)
plot(logbookGA_improved)
# 最优路径
tour = tools.selBest(resultPopGA_improved,k=1)[0]
print('最优路径为:'+str(tour)+'\n'+'最优路径距离为:'+str(routeDistance(tour)))
tour = completeRoute(tour)
plotTour(tour,cities)

运行结果

最优路径为:[22, 29, 1, 17, 9, 13, 10, 28, 14, 2, 12, 11, 24, 16, 6, 0, 3, 27, 7, 18, 5, 25, 20, 23, 15, 8, 21, 26, 4, 19]
最优路径距离为:(3776.4456973287165,)

可视化
在这里插入图片描述

在这里插入图片描述
可以看到,在加入局部搜索之后,获得的解质量大幅提升,已经超过repNNTSP+2-OPT的水平,另外迭代收敛所需的次数也大大减少了。

二.超参数调参

1.遗传算法中的超参数以及其一般取值

一般遗传算法中的超参数有:
在这里插入图片描述
通常来说这些超参数是通过经验和反复试错获得的,并且超参数的选择和问题的类型紧密相关。

除了这些一般超参数以外,各项操作–育种选择、变异和环境选择的方法选择也可以视为一些额外的超参数。

2.超参数寻优

迭代步数–停止原则

GA的停止准则通常包含以下几种:

  • 当适应度函数收敛到某个特定值:一般在对问题有深刻了解的情况下或者问题有特定约束时才能使用;
  • 当最佳适应度在较长时间内不再改变:这是一个常用的准则,但是如果以此作为准则,有在局部最优提前结束的可能;
  • 当族群没有进化潜力时:这也是较为普遍采用的准则,一般来说可以通过计算族群的variance来确定族群的多样性是否还能支持进一步进化。当variance小于某个值时,可以判定已经收敛。
  • 当迭代步数达到预设值:这是为了防止计算时间无限拖延的一个安全设定。迭代步数达到预设值并不能保证遗传算法收敛到最优,因此通常只是作为一个最后保险。
    此处,不妨采用第三种准则,当种群适应度的标准差小于1*10^(-9)时,结束运算
#!usr/bin/env python
# -*- coding:utf-8 _*-
"""
@author: liujie
@software: PyCharm
@file: 迭代停止-方差.py
@time: 2020/11/24 19:29
"""
from scipy.spatial import distance
import numpy as np
import matplotlib.pyplot as plt
from deap import creator,tools,base
import random
# 生成城市坐标
def genCity(n,lb = 100,ub = 999):
    return np.random.randint(low=lb,high=ub,size=(n,2))

# 生成城市距离矩阵
def cityDistance(cities):
    return distance.cdist(cities,cities,metric='euclidean')

# 补全路线
def completeRoute(individual):
    return individual + [individual[0]]  # 不要append

def routeDistance(route):
    '''
    :param route: 一条路线,一个序列
    :return: 路线长度
    '''
    if route[0] != route[-1]:
        route = completeRoute(route)
    # 存储路线长度
    routeDist = 0
    # i,j分别是相邻的坐标点
    for i, j in zip(route[0::], route[1::]):
        # 这里直接从CityDist变量中取值了
        routeDist += cityDist[i, j]
    return (routeDist),

# 2-OPT优化算法
def opt(route,k=2):
    '''
    :param route: 路径
    :param k:
    :return: 返回优化后的路径及其路径长度
    '''
    nCities = len(route)
    optimizedRoute = route
    minDistance = routeDistance(route)
    for i in range(1,nCities-2):
        for j in range(i+k,nCities):
            if j - i <= 1:
                continue
            reversedRoute = route[:i] + route[i:j][::-1] + route[j:]
            reversedRouteDist = routeDistance(reversedRoute)
            # 如果翻转后的路径更优,则更新最优解
            if reversedRouteDist < minDistance:
                optimizedRoute = reversedRoute
                minDistance = reversedRouteDist

    return optimizedRoute,minDistance


# 精英保留策略一遗传算法
def GA_improved(cities,nCities,npop = 100,cxpb = 0.5,mutpb = 0.2,ngen = 200):
    global cityDist
    # 定义问题
    creator.create('FitnessMin',base.Fitness,weights=(-1.0,))   # 单目标优化最小值
    creator.create('Individual',list,fitness = creator.FitnessMin)

    # 定义个体编码
    toolbox = base.Toolbox()
    toolbox.register('Indices',np.random.permutation,range(nCities))    # 创建序列
    toolbox.register('Individual',tools.initIterate,creator.Individual,toolbox.Indices)

    # 创建族群
    toolbox.register('Population',tools.initRepeat,list,toolbox.Individual)
    pop = toolbox.Population(npop)

    # 注册所需工具
    cityDist = cityDistance(cities)
    toolbox.register('evaluate',routeDistance)
    toolbox.register('select',tools.selTournament,tournsize=2)
    toolbox.register('mate',tools.cxOrdered)
    toolbox.register('mutate',tools.mutShuffleIndexes,indpb=0.2)
    toolbox.register('localOpt',opt)    # 注册2-opt
    # 数据记录
    stat
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值