2024/8/25周报

摘要

多目标优化(Multi-Objective Optimization, MOO)是优化领域的一个分支,它处理的是同时优化多个相互冲突的目标函数的问题。在实际应用中,很少有决策问题只涉及单一目标,通常需要在多个目标之间找到平衡点。例如,在工程设计中可能需要同时考虑成本、性能和可靠性等因素。本周进一步学习一些多目标优化算法的常见类型。

Abstract

Multi-objective optimization (MOO) is a branch of optimization field, which deals with the problem of simultaneously optimizing multiple conflicting objective functions. In practical application, few decision-making problems only involve a single goal, and it is usually necessary to find a balance between multiple goals. For example, factors such as cost, performance and reliability may need to be considered simultaneously in engineering design. Learn more about some common types of multi-objective optimization algorithms this week.

多目标优化算法

多目标优化的基本概念

  1. 目标函数:每个目标函数衡量了某个特定方面的性能或质量。
  2. 解空间:所有可能的解决方案组成的集合。
  3. 可行解:满足所有约束条件的解。
  4. Pareto 优势:如果一个解在至少一个目标上优于另一个解,并且在其他目标上不劣于该解,则称这个解对另一个解具有 Pareto 优势。
  5. Pareto 最优解:没有其他解能够对该解产生 Pareto 优势,这样的解称为 Pareto 最优解。
  6. Pareto 前沿:所有 Pareto 最优解在目标空间中的投影构成的集合。

实现步骤

  1. 初始化种群:随机生成初始解集。
  2. 评估:计算每个个体的目标函数值。
  3. 选择:根据某种策略选择下一代的父代个体。
  4. 遗传操作:执行交叉和变异操作以产生新个体。
  5. 更新:根据 Pareto 优势更新种群。
  6. 终止条件:达到预定迭代次数或其他终止标准后停止。

多目标优化算法常见类型及其实现

多目标优化算法的目标是找到一组 Pareto 最优解,而不是单一的最佳解。常见的多目标优化算法包括但不限于以下几种:

NSGA-II (Non-dominated Sorting Genetic Algorithm II)

  • 使用快速非支配排序来评价种群中的个体。
  • 利用拥挤距离保持种群多样性。
  • 通过遗传操作(如交叉和变异)生成新的后代。

代码如下:

import random
import numpy as np
from deap import base, creator, tools, algorithms

# 定义问题和解决方案的类型
creator.create("FitnessMin", base.Fitness, weights=(-1.0, -1.0))
creator.create("Individual", list, fitness=creator.FitnessMin)

# 初始化工具箱
toolbox = base.Toolbox()

# 注册属性初始化器
toolbox.register("attr_float", random.random)
toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_float, n=2)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)

# 注册评估函数
def evaluateZDT1(individual):
    f1 = individual[0]
    g = 1 + 9 * sum(individual[1:]) / (len(individual) - 1)
    f2 = g * (1 - (f1 / g) ** 0.5)
    return f1, f2

toolbox.register("evaluate", evaluateZDT1)

# 注册遗传操作
toolbox.register("mate", tools.cxSimulatedBinaryBounded, eta=20.0, low=0, up=1)
toolbox.register("mutate", tools.mutPolynomialBounded, eta=20.0, low=0, up=1, indpb=1.0/2)
toolbox.register("select", tools.selNSGA2)

# 主程序
def main():
    # 初始化种群
    pop = toolbox.population(n=90)
    hof = tools.ParetoFront()
    stats = tools.Statistics(lambda ind: ind.fitness.values)
    stats.register("avg", np.mean, axis=0)
    stats.register("std", np.std, axis=0)
    stats.register("min", np.min, axis=0)
    stats.register("max", np.max, axis=0)

    # 进化过程
    pop, logbook = algorithms.eaMuPlusLambda(pop, toolbox, mu=len(pop), lambda_=len(pop),
                                             cxpb=0.9, mutpb=0.1, ngen=20,
                                             stats=stats, halloffame=hof, verbose=True)

    return pop, logbook, hof

if __name__ == "__main__":
    pop, log, hof = main()

这段代码的主要组成部分如下:

1.定义问题和解决方案的类型:
creator.create(“FitnessMin”, base.Fitness, weights=(-1.0, -1.0)) 定义了一个最小化多目标问题。
creator.create(“Individual”, list, fitness=creator.FitnessMin) 创建了一个列表类型的个体,其中包含一个FitnessMin实例作为其fitness属性。

2.注册属性初始化器:
toolbox.register(“attr_float”, random.random) 定义了一个随机浮点数生成器。
toolbox.register(“individual”, tools.initRepeat, creator.Individual, toolbox.attr_float, n=2) 注册了个体初始化器,每个个体有两个基因。
toolbox.register(“population”, tools.initRepeat, list, toolbox.individual) 注册了种群初始化器。

3.评估函数:
evaluateZDT1 是一个示例评估函数,用于评估ZDT1测试问题的个体。该函数返回两个目标值。

4.遗传操作:
cxSimulatedBinaryBounded 和 mutPolynomialBounded 分别用于执行模拟二进制交叉和多项式变异。
selNSGA2 用于执行快速非支配排序的选择操作。

5.主程序:
初始化种群和统计信息。
执行进化算法 eaMuPlusLambda。
输出最终的种群、日志和Pareto前沿。
结果如下:
在这里插入图片描述

SPEA2 (Strength Pareto Evolutionary Algorithm 2)

  • 引入个体适应度的概念,根据支配关系和密度估计进行排序。
  • 通过存档机制保持历史最优解。

代码如下:

import random
import numpy as np
from deap import base, creator, tools, algorithms

# 定义问题和解决方案的类型
creator.create("FitnessMin", base.Fitness, weights=(-1.0, -1.0))
creator.create("Individual", list, fitness=creator.FitnessMin)

# 初始化工具箱
toolbox = base.Toolbox()

# 注册属性初始化器
toolbox.register("attr_float", random.random)
toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_float, n=2)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)

# 注册评估函数
def evaluateZDT1(individual):
    f1 = individual[0]
    g = 1 + 9 * sum(individual[1:]) / (len(individual) - 1)
    f2 = g * (1 - (f1 / g) ** 0.5)
    return f1, f2

toolbox.register("evaluate", evaluateZDT1)

# 注册遗传操作
toolbox.register("mate", tools.cxSimulatedBinaryBounded, eta=20.0, low=0, up=1)
toolbox.register("mutate", tools.mutPolynomialBounded, eta=20.0, low=0, up=1, indpb=1.0/2)

# SPEA2的选择函数
def selSPEA2(individuals, k):
    if len(individuals) < k:
        return individuals[:]
    
    # 计算支配关系
    dominance = tools.SPEADomination()
    dominated_counts = [sum(dominance(i, j) for j in individuals) for i in individuals]

    # 计算邻近度
    k_nearest = tools.knn(20)
    distances = [k_nearest(i, individuals) for i in individuals]

    # 创建存档
    archive = tools.ParetoFront()
    archive.update(individuals)

    # 选择个体
    selected = []
    while len(selected) < k:
        candidates = [i for i in range(len(individuals)) if i not in selected]
        if not candidates:
            break
        
        # 选择支配数量最少的个体
        min_dominated_count = min(dominated_counts[i] for i in candidates)
        candidates = [i for i in candidates if dominated_counts[i] == min_dominated_count]

        # 如果有多个个体,则选择距离最大的个体
        if len(candidates) > 1:
            max_distance = max(distances[i] for i in candidates)
            candidates = [i for i in candidates if distances[i] == max_distance]

        selected.append(candidates[0])
        
        # 更新支配计数和邻近度
        for i in candidates:
            dominated_counts[i] = sum(dominance(i, j) for j in individuals if j not in selected)
            distances[i] = k_nearest(i, [j for j in individuals if j not in selected])

    return [individuals[i] for i in selected], archive

# 主程序
def main():
    # 初始化种群
    pop = toolbox.population(n=90)
    stats = tools.Statistics(lambda ind: ind.fitness.values)
    stats.register("avg", np.mean, axis=0)
    stats.register("std", np.std, axis=0)
    stats.register("min", np.min, axis=0)
    stats.register("max", np.max, axis=0)

    # 进化过程
    hof = tools.ParetoFront()
    logbook = tools.Logbook()
    record = stats.compile(pop)
    logbook.record(gen=0, evals=len(pop), **record)
    print(logbook.stream)

    for gen in range(1, 21):
        offspring = algorithms.varOr(pop, toolbox, lambda_=len(pop), cxpb=0.9, mutpb=0.1)
        invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
        fitnesses = toolbox.map(toolbox.evaluate, invalid_ind)
        for ind, fit in zip(invalid_ind, fitnesses):
            ind.fitness.values = fit

        # 选择操作
        pop, archive = selSPEA2(offspring + pop, len(pop))

        record = stats.compile(pop)
        logbook.record(gen=gen, evals=len(pop), **record)
        print(logbook.stream)

    return pop, logbook, hof

if __name__ == "__main__":
    pop, log, hof = main()

MOEA/D (Multi-Objective Evolutionary Algorithm based on Decomposition)

  • 将多目标问题分解为多个单目标子问题。
  • 每个个体负责优化一个子问题。
  • 使用局部搜索策略提高优化效率。

代码如下:

import random
import numpy as np
from deap import base, creator, tools, algorithms

# 定义问题和解决方案的类型
creator.create("FitnessMin", base.Fitness, weights=(-1.0, -1.0))
creator.create("Individual", list, fitness=creator.FitnessMin)

# 初始化工具箱
toolbox = base.Toolbox()

# 注册属性初始化器
toolbox.register("attr_float", random.random)
toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_float, n=2)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)

# 注册评估函数
def evaluateZDT1(individual):
    f1 = individual[0]
    g = 1 + 9 * sum(individual[1:]) / (len(individual) - 1)
    f2 = g * (1 - (f1 / g) ** 0.5)
    return f1, f2

toolbox.register("evaluate", evaluateZDT1)

# 注册遗传操作
toolbox.register("mate", tools.cxSimulatedBinaryBounded, eta=20.0, low=0, up=1)
toolbox.register("mutate", tools.mutPolynomialBounded, eta=20.0, low=0, up=1, indpb=1.0/2)

# 分解多目标问题
def decompose_objectives(objective_values, weight_vector):
    weighted_sum = sum(w * obj for w, obj in zip(weight_vector, objective_values))
    return weighted_sum

# MOEA/D的选择函数
def selMOEAD(individuals, k, weights):
    if len(individuals) < k:
        return individuals[:]
    
    # 计算每个个体对应的子问题权重
    for ind in individuals:
        ind.f_weights = weights[ind.id]

    # 创建存档
    archive = tools.ParetoFront()
    archive.update(individuals)

    # 选择个体
    selected = []
    while len(selected) < k:
        candidates = [i for i in range(len(individuals)) if i not in selected]
        if not candidates:
            break

        # 选择支配数量最少的个体
        min_dominated_count = min(ind.fitness.values[0] for ind in individuals if ind.id not in selected)
        candidates = [ind for ind in individuals if ind.fitness.values[0] == min_dominated_count and ind.id not in selected]

        # 如果有多个个体,则随机选择一个
        if len(candidates) > 1:
            selected.append(random.choice(candidates))
        else:
            selected.append(candidates[0].id)

        # 更新支配计数
        for i in candidates:
            i.fitness.values = decompose_objectives(i.fitness.values, i.f_weights)

    return [individuals[i] for i in selected], archive

# 主程序
def main():
    # 初始化种群
    pop = toolbox.population(n=90)
    weights = tools.uniform_reference_points(nobj=2, p=len(pop))
    for i, ind in enumerate(pop):
        ind.id = i
        ind.fitness.values = decompose_objectives(toolbox.evaluate(ind), weights[i])
    
    # 进化过程
    hof = tools.ParetoFront()
    logbook = tools.Logbook()
    record = tools.Statistics(lambda ind: ind.fitness.values)
    record.register("avg", np.mean, axis=0)
    record.register("std", np.std, axis=0)
    record.register("min", np.min, axis=0)
    record.register("max", np.max, axis=0)

    record = record.compile(pop)
    logbook.record(gen=0, evals=len(pop), **record)
    print(logbook.stream)

    for gen in range(1, 21):
        offspring = algorithms.varOr(pop, toolbox, lambda_=len(pop), cxpb=0.9, mutpb=0.1)
        invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
        fitnesses = toolbox.map(toolbox.evaluate, invalid_ind)
        for ind, fit in zip(invalid_ind, fitnesses):
            ind.fitness.values = decompose_objectives(fit, weights[ind.id])

        # 选择操作
        pop, archive = selMOEAD(offspring + pop, len(pop), weights)

        record = record.compile(pop)
        logbook.record(gen=gen, evals=len(pop), **record)
        print(logbook.stream)

    return pop, logbook, hof

if __name__ == "__main__":
    pop, log, hof = main()

ε-MOEA (Epsilon-Dominance Based Multi-Objective Evolutionary Algorithm)

  • 引入 ε-支配关系,允许一定程度的目标函数值差距。
  • 可以更好地控制 Pareto 前沿的分布。

代码如下:

import random
import numpy as np
from deap import base, creator, tools, algorithms

# 定义问题和解决方案的类型
creator.create("FitnessMin", base.Fitness, weights=(-1.0, -1.0))
creator.create("Individual", list, fitness=creator.FitnessMin)

# 初始化工具箱
toolbox = base.Toolbox()

# 注册属性初始化器
toolbox.register("attr_float", random.random)
toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_float, n=2)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)

# 注册评估函数
def evaluateZDT1(individual):
    f1 = individual[0]
    g = 1 + 9 * sum(individual[1:]) / (len(individual) - 1)
    f2 = g * (1 - (f1 / g) ** 0.5)
    return f1, f2

toolbox.register("evaluate", evaluateZDT1)

# 注册遗传操作
toolbox.register("mate", tools.cxSimulatedBinaryBounded, eta=20.0, low=0, up=1)
toolbox.register("mutate", tools.mutPolynomialBounded, eta=20.0, low=0, up=1, indpb=1.0/2)

# ε-支配关系
def epsilon_dominates(a, b, epsilons):
    dominates = True
    for ai, bi, eps in zip(a, b, epsilons):
        if ai > bi + eps:
            return False
        elif ai < bi - eps:
            dominates = False
    return dominates

# ε-MOEA的选择函数
def selEPSMOEA(individuals, k, epsilons):
    if len(individuals) < k:
        return individuals[:]
    
    # 创建存档
    archive = tools.ParetoFront()
    archive.update(individuals)

    # 选择个体
    selected = []
    while len(selected) < k:
        candidates = [i for i in range(len(individuals)) if i not in selected]
        if not candidates:
            break

        # 选择支配数量最少的个体
        min_dominated_count = min(sum(epsilon_dominates(ind.fitness.values, other.fitness.values, epsilons)
                                      for other in individuals if other.id != ind.id)
                                  for ind in individuals if ind.id not in selected)
        candidates = [ind for ind in individuals if ind.fitness.values[0] == min_dominated_count and ind.id not in selected]

        # 如果有多个个体,则随机选择一个
        if len(candidates) > 1:
            selected.append(random.choice(candidates))
        else:
            selected.append(candidates[0].id)

    return [individuals[i] for i in selected], archive

# 主程序
def main():
    # 初始化种群
    pop = toolbox.population(n=90)
    epsilons = [0.05, 0.05]  # 目标函数的ε值

    # 进化过程
    hof = tools.ParetoFront()
    logbook = tools.Logbook()
    record = tools.Statistics(lambda ind: ind.fitness.values)
    record.register("avg", np.mean, axis=0)
    record.register("std", np.std, axis=0)
    record.register("min", np.min, axis=0)
    record.register("max", np.max, axis=0)

    record = record.compile(pop)
    logbook.record(gen=0, evals=len(pop), **record)
    print(logbook.stream)

    for gen in range(1, 21):
        offspring = algorithms.varOr(pop, toolbox, lambda_=len(pop), cxpb=0.9, mutpb=0.1)
        invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
        fitnesses = toolbox.map(toolbox.evaluate, invalid_ind)
        for ind, fit in zip(invalid_ind, fitnesses):
            ind.fitness.values = fit

        # 选择操作
        pop, archive = selEPSMOEA(offspring + pop, len(pop), epsilons)

        record = record.compile(pop)
        logbook.record(gen=gen, evals=len(pop), **record)
        print(logbook.stream)

    return pop, logbook, hof

if __name__ == "__main__":
    pop, log, hof = main()

PESA-II (Performance-Scalarizing Evolutionary Algorithm)

  • 使用标量化方法将多目标问题转换为一系列单目标问题。
  • 通过动态调整权重向量来探索 Pareto 前沿的不同区域。

代码如下:

import random
import numpy as np
from deap import base, creator, tools, algorithms

# 定义问题和解决方案的类型
creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
creator.create("Individual", list, fitness=creator.FitnessMin)

# 初始化工具箱
toolbox = base.Toolbox()

# 注册属性初始化器
toolbox.register("attr_float", random.random)
toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_float, n=2)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)

# 注册评估函数
def evaluateZDT1(individual, weights):
    f1 = individual[0]
    g = 1 + 9 * sum(individual[1:]) / (len(individual) - 1)
    f2 = g * (1 - (f1 / g) ** 0.5)
    scalarized_value = weights[0]*f1 + weights[1]*f2
    return scalarized_value,

toolbox.register("evaluate", evaluateZDT1)

# 注册遗传操作
toolbox.register("mate", tools.cxSimulatedBinaryBounded, eta=20.0, low=0, up=1)
toolbox.register("mutate", tools.mutPolynomialBounded, eta=20.0, low=0, up=1, indpb=1.0/2)

# PESA-II的选择函数
def selPESA2(individuals, k, weights):
    if len(individuals) < k:
        return individuals[:]
    
    # 计算所有个体的适应度
    fitnesses = [toolbox.evaluate(ind, weights) for ind in individuals]
    
    # 按照适应度排序
    sorted_individuals = [ind for _, ind in sorted(zip(fitnesses, individuals))]
    
    # 选择前k个个体
    return sorted_individuals[:k]

# 主程序
def main():
    # 初始化种群
    pop = toolbox.population(n=90)
    num_weights = 2  # 两个目标
    num_weights_per_generation = 5  # 每代使用的权重数量
    
    # 进化过程
    hof = tools.ParetoFront()
    logbook = tools.Logbook()
    record = tools.Statistics(lambda ind: ind.fitness.values)
    record.register("avg", np.mean, axis=0)
    record.register("std", np.std, axis=0)
    record.register("min", np.min, axis=0)
    record.register("max", np.max, axis=0)

    record = record.compile(pop)
    logbook.record(gen=0, evals=len(pop), **record)
    print(logbook.stream)

    for gen in range(1, 21):
        # 生成权重向量
        weights = generate_weights(num_weights, num_weights_per_generation)
        
        offspring = algorithms.varOr(pop, toolbox, lambda_=len(pop), cxpb=0.9, mutpb=0.1)
        invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
        fitnesses = toolbox.map(lambda ind: toolbox.evaluate(ind, weights), invalid_ind)
        for ind, fit in zip(invalid_ind, fitnesses):
            ind.fitness.values = fit

        # 选择操作
        pop = selPESA2(offspring + pop, len(pop), weights)

        record = record.compile(pop)
        logbook.record(gen=gen, evals=len(pop), **record)
        print(logbook.stream)

    return pop, logbook, hof

# 生成权重向量
def generate_weights(num_objectives, num_weights):
    weights = []
    for _ in range(num_weights):
        w = [random.random() for _ in range(num_objectives)]
        w_sum = sum(w)
        w = [wi / w_sum for wi in w]  # 归一化
        weights.append(w)
    return weights

if __name__ == "__main__":
    pop, log, hof = main()

这些算法通常用于解决复杂问题,比如设计优化、资源分配等问题。每种算法都有其特点和适用场景,选择哪种算法取决于具体问题的需求。

一些常见的dos命令

“DOS命令”通常指的是在MS-DOS(Microsoft Disk Operating System)或者兼容的命令行界面(如Windows命令提示符cmd.exe)中使用的命令。

更换盘符: d:
查看当前目录下的文件及文件夹:dir
进入文件夹: cd 文件夹的名字
返回上一级目录:cd …
创建文件夹:mkdir/md 文件夹名
删除文件夹:rd 文件夹的名字
清屏:cls
退出: exit

总结

本周进一步学习了一些多目标优化算法的常见类型,下周将继续学习项目申报书中提到的动态优化GA模型等内容。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值