DEAP: сделать вероятность мутации зависимой от номера поколения

Я использую генетический алгоритм, реализованный с помощью библиотеки DEAP для Python. Чтобы избежать преждевременной конвергенции и заставить исследовать пространство признаков, я хотел бы, чтобы вероятность мутации была высокой в ​​течение первых поколений. Но чтобы предотвратить дрейф от экстремума после их выявления, я хотел бы, чтобы вероятность мутации была ниже в последних поколениях. Как сделать так, чтобы вероятность мутации уменьшалась в поколениях? Есть ли в DEAP встроенная функция для этого?

Когда я регистрирую функцию мутации, например

toolbox.register('mutate', tools.mutPolynomialBounded, eta=.6, low=[0,0], up=[1,1], indpb=0.1)

параметр indpb является числом с плавающей запятой. Как я могу сделать его функцией чего-то другого?


person usernumber    schedule 22.11.2019    source источник
comment
Я не совсем уверен, как DEAP мутирует, но я уверен, что он вызывается в вашей программе, например, deap.tools.mutGaussian(xx). Не могли бы вы использовать ступенчатое уменьшение на 1-prob для каждого шага генерации, чтобы определить, когда запускать шаг мутации?   -  person Jason Chia    schedule 22.11.2019


Ответы (1)


Похоже на задание для Callbackproxy, который оценивает аргументы функции каждый раз, когда они вызываются. Я добавил простой пример, где я изменил официальный DEAP n-queen пример, в котором частота мутаций установлена ​​на 2/N_GENS (произвольный выбор, чтобы подчеркнуть суть).

Обратите внимание, что Callbackproxy получает лямбду, поэтому вам нужно передать аргумент скорости мутации как функцию (используя либо полнофункциональную функцию, либо просто лямбду). В любом случае результат заключается в том, что каждый раз, когда оценивается параметр indpb, эта лямбда будет вызываться, и если лямбда содержит ссылку на счетчик генерации глобальной переменной, вы получите то, что хотите.

#    This file is part of DEAP.
#
#    DEAP is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Lesser General Public License as
#    published by the Free Software Foundation, either version 3 of
#    the License, or (at your option) any later version.
#
#    DEAP is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#    GNU Lesser General Public License for more details.
#
#    You should have received a copy of the GNU Lesser General Public
#    License along with DEAP. If not, see <http://www.gnu.org/licenses/>.

import random
from objproxies import CallbackProxy
import numpy

from deap import algorithms
from deap import base
from deap import creator
from deap import tools

# Problem parameter
NB_QUEENS = 20
N_EVALS = 0
N_GENS = 1

def evalNQueens(individual):
    global N_EVALS, N_GENS
    """Evaluation function for the n-queens problem.
    The problem is to determine a configuration of n queens
    on a nxn chessboard such that no queen can be taken by
    one another. In this version, each queens is assigned
    to one column, and only one queen can be on each line.
    The evaluation function therefore only counts the number
    of conflicts along the diagonals.
    """
    size = len(individual)
    # Count the number of conflicts with other queens.
    # The conflicts can only be diagonal, count on each diagonal line
    left_diagonal = [0] * (2 * size - 1)
    right_diagonal = [0] * (2 * size - 1)

    # Sum the number of queens on each diagonal:
    for i in range(size):
        left_diagonal[i + individual[i]] += 1
        right_diagonal[size - 1 - i + individual[i]] += 1

    # Count the number of conflicts on each diagonal
    sum_ = 0
    for i in range(2 * size - 1):
        if left_diagonal[i] > 1:
            sum_ += left_diagonal[i] - 1
        if right_diagonal[i] > 1:
            sum_ += right_diagonal[i] - 1

    N_EVALS += 1
    if N_EVALS % 300 == 0:
        N_GENS += 1
    return sum_,


creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
creator.create("Individual", list, fitness=creator.FitnessMin)

# Since there is only one queen per line,
# individual are represented by a permutation
toolbox = base.Toolbox()
toolbox.register("permutation", random.sample, range(NB_QUEENS), NB_QUEENS)

# Structure initializers
# An individual is a list that represents the position of each queen.
# Only the line is stored, the column is the index of the number in the list.
toolbox.register("individual", tools.initIterate, creator.Individual, toolbox.permutation)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)

toolbox.register("evaluate", evalNQueens)
toolbox.register("mate", tools.cxPartialyMatched)
toolbox.register("mutate", tools.mutShuffleIndexes, indpb=CallbackProxy(lambda: 2.0 / N_GENS))
toolbox.register("select", tools.selTournament, tournsize=3)





def main(seed=0):
    random.seed(seed)

    pop = toolbox.population(n=300)
    hof = tools.HallOfFame(1)
    stats = tools.Statistics(lambda ind: ind.fitness.values)
    stats.register("Avg", numpy.mean)
    stats.register("Std", numpy.std)
    stats.register("Min", numpy.min)
    stats.register("Max", numpy.max)

    algorithms.eaSimple(pop, toolbox, cxpb=0.5, mutpb=1, ngen=100, stats=stats,
                        halloffame=hof, verbose=True)

    return pop, stats, hof


if __name__ == "__main__":
    main()
person erap129    schedule 23.11.2019
comment
Откуда вы взяли, что на одно поколение приходится 300 оценок? - person usernumber; 27.11.2019
comment
хорошо, это хороший момент. Я предположил, что каждый человек оценивается в каждом поколении, и это не обязательно так. Лучшим вариантом было бы обновить глобальную переменную N_GENS прямо из цикла эволюции, но это потребует редактирования функции eaSimple или создания собственной. Лучшего способа пока не нашел. - person erap129; 29.11.2019
comment
eaSimple выводит количество оценок на экран при запуске. Может быть, есть способ получить доступ к этой информации и использовать ее? - person usernumber; 29.11.2019
comment
Вы можете переназначить sys.stdout файлу, а затем отслеживать строки, напечатанные в этом файле, хотя это звучит как настоящее излишество. Я бы просто скопировал функцию eaSimple и сделал необходимые адаптации. - person erap129; 29.11.2019