使用 DEAP 最小化 multi-objective 函数

Minimizing multi-objective function using DEAP

我正在尝试通过使用 DEAP 库最小化自定义函数来执行多重 objective 优化。虽然我在对几个 objectives(目标)进行最小化时得到了不错的结果,但对于超过 3 或 4 个,它无法收敛。通常它会将第一个 objective 最小化为 0,同时让其他 objective 跳来跳去(不最小化)。

我使用 sci-kit 库构建了一个元模型(岭回归)来描述一些模拟数据,因此我的模型基于系数和截距(包含在我的代码中)。新预测基于约 150 个均匀变化的输入。

有一个最小化 3 个目标的年份选项,以及一个最小化 8 个目标的月份选项。

我已经将我的 代码 作为要点包括在内,因为它非常大。 请找HERE.

问题: 任何人都知道剩余的 objective 未被最小化的原因可能是什么?我尝试过选择、变异和交叉过程,但还没有成功。或者它可能与模型本身有关?我也尝试过不同的重量来健身,但不知为何似乎并没有什么不同。

年度目标的结果:

每月目标的结果:

只是回答我自己的问题。

看来我在评估期间没有返回正确类型的值。

更改为差异的 RMSE 而不是目标和预测之间的绝对差异达到了目的:

def EvaluateObjective(individual):
    prediction = calculate(individual, for_sensitivity)
    prediction = [int(i) for i in prediction]

    # diff = []
    # for y in range(len(targets)):
    #     output = math.sqrt((targets[y] - prediction[y]) ** 2)
    #     #output = abs(targets[y] - prediction[y])
    #     diff.append(output)

    rmse = np.sqrt((sum((i - j)**2 for i, j in zip(prediction, targets)) / len(targets)))

    return (rmse,)

您为我一直苦苦挣扎的问题提供了解决方案。好方法,一个小技巧让我的程序也能运行!

我很确定一定有很多 deap 用户像我一样尝试使用两个以上的权重,比如 weights=(-1.0, -1.0, 1.0)。

我将post 3 个参数的简单示例(2 个参数最小化,1 个参数最大化。)

  • 例子是关于"How to load as many items as possible with conditions of maximum weight, maximum size"

  • 条件:

    1. 最小化权重总和。
    2. 最小化大小和。
    3. 最大化值的总和。
from numpy import array
import numpy
import random
from deap import base, creator, tools, algorithms

###  Multi-objective Optimization Problem  ###

IND_INIT_SIZE = 5

MAX_WEIGHT = 2000 # kg
MAX_SIZE = 1500 # m**3


# Create the item dictionary:

r = array([[213, 508,  22],  # 1st arg : weight / 2nd arg : size / 3rd arg : value
       [594, 354,  50],
       [275, 787,  43],
       [652, 218,  46],
       [728, 183,  43],
       [856, 308,  33],
       [727, 482,  45],
       [762, 683,  26],
       [707, 450,  19],
       [909, 309,  45],
       [979, 247,  42],
       [259, 705,  42],
       [260, 543,  14],
       [899, 825,  17],
       [446, 360,  35],
       [491, 818,  47],
       [647, 404,  17],
       [604, 623,  32],
       [900, 840,  45],
       [374, 127,  33]] )


NBR_ITEMS = r.shape[0]

items = {}
# Create random items and store them in the items' dictionary.
for i in range(NBR_ITEMS):
    items[i] = ( r[i][0] , r[i][1] , r[i][2] )


creator.create("Fitness", base.Fitness, weights=(-1.0, 1.0 ))  # Note here <- I used only two weights!  (at first, I tried weights=(-1.0 , -1.0, 1.0)) but it crashes. With deap, you cannot do such a thing.

creator.create("Individual", set, fitness=creator.Fitness)

toolbox = base.Toolbox()

# Attribute generator
toolbox.register("attr_item", random.randrange, NBR_ITEMS)

# Structure initializers
toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_item, n=IND_INIT_SIZE) #
toolbox.register("population", tools.initRepeat, list, toolbox.individual)


def evaluation(individual):
    weight = 0.0
    size =0.0
    value = 0.0

    # Maximize or Minimize Conditions
    for item in individual:
        weight += items[item][0]  # It must be minimized.
        size += items[item][1]  # It must be minimized.
        value += items[item][2]  # It must be maximized.

    # Limit Conditions
    if weight > MAX_WEIGHT or size > MAX_SIZE:
        return 10000, 0

    if value == 0:
        value = 0.0000001

    MinFitess_score = weight + size   # NOTE : Minimize weight, size
    MaxFitenss_score = value  # NOTE : Maximize weight, size

    return MinFitess_score , MaxFitenss_score,



def cxSet(ind1, ind2):
    """Apply a crossover operation on input sets. The first child is the
    intersection of the two sets, the second child is the difference of the
    two sets.
    """
    temp = set(ind1)  # Used in order to keep type
    ind1 &= ind2  # Intersection (inplace)
    ind2 ^= temp  # Symmetric Difference (inplace)
    return ind1, ind2


def mutSet(individual):
    """Mutation that pops or add an element."""
    if random.random() < 0.5:
        if len(individual) > 0:  # We cannot pop from an empty set
            individual.remove(random.choice(sorted(tuple(individual))))
    else:
        individual.add(random.randrange(NBR_ITEMS))
    return individual,  # NOTE comma(,) , if there's no comma, an error occurs.



toolbox.register("mate", cxSet)
toolbox.register("mutate", mutSet)
toolbox.register("select", tools.selNSGA2) # NSGA-2 applies to multi-objective problems such as knapsack problem
toolbox.register("evaluate", evaluation)


def main():
    ngen = 300  # a number of generation  < adjustable value >

    pop = toolbox.population(n= 300)
    hof = tools.ParetoFront() # a ParetoFront may be used to retrieve the best non dominated individuals of the evolution
    stats = tools.Statistics(lambda ind: ind.fitness.values)
    stats.register("avg", numpy.mean, axis=0)
    stats.register("std", numpy.std, axis=0)
    stats.register("min", numpy.min, axis=0)
    stats.register("max", numpy.max, axis=0)

    algorithms.eaSimple(pop, toolbox, 0.7, 0.2, ngen=ngen, stats=stats, halloffame=hof, verbose=True)

    return hof, pop


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

    print(hof) # non-dominated individuals' list  # the fittest value is placed on the most right side.

理想结果:

  1. 个人({1, 2, 19, 4}) 或
  2. 个人({1, 2, 19, 3})

因为他们的总分很相近。您将获得其中一个结果。