Архив проекта: ga_4.py
На предыдущем
занятии мы с вами реализовали простой ГА для решения задачи OneMax. Но за рамками
остается вопрос, как же ему все же удается находить решение задачи? На этом
занятии я попытаюсь ответить на него, для чего добавлю визуализацию
приспособленностей особей на каждой итерации (поколении).
Вначале у
статистки зарегистрируем еще один параметр:
stats.register("values", np.array)
который будет
хранить список значений функции приспособленности для каждого поколения, то
есть, на выходе мы получим двумерный список values.
Затем, прочитаем
все сформированные данные:
maxFitnessValues, meanFitnessValues, vals = logbook.select("max", "avg", "values")
и анимируем
изменение приспособленностей особей от поколения к поколению:
import time
plt.ion()
fig, ax = plt.subplots()
line, = ax.plot(vals[0], ' o', markersize=1)
ax.set_ylim(40, 110)
for v in vals:
line.set_ydata(v)
plt.draw()
plt.gcf().canvas.flush_events()
time.sleep(0.5)
plt.ioff()
plt.show()
Если эти строчки
программы вам не понятны, то подробнее о создании анимации в пакете matplotlib смотрите вот это
занятие:
https://youtu.be/YWt3SKpZwKM
После запуска
программы увидим анимацию из множества точек:
Каждая точка
здесь – это значение функции приспособленности отдельного индивида в текущем
поколении. От кадра к кадру происходит смена поколений и мы видим, что в целом,
популяция стремится к уровню 100 – максимальному значению приспособленности.
Так за счет чего
происходит такое улучшение? Рассмотрим подробнее процесс эволюции, заложенный в
ГА. Как считал Чарльз Дарвин, движущая сила эволюции – это естественный отбор.
В нашем алгоритме за это отвечает функция select:
toolbox.register("select", tools.selTournament, tournsize=3)
А сам отбор
происходит за счет турнира (состязания) трех случайно выбранных особей. Давайте
посмотрим, что именно дает только одна эта функция. Отключим кроссинговер и
мутации (поставим вероятности их возникновения, равные нулю):
population, logbook = algorithms.eaSimple(population, toolbox,
cxpb=0, #P_CROSSOVER,
mutpb=0, #P_MUTATION,
ngen=MAX_GENERATIONS,
stats=stats,
verbose=False)
И после
нескольких итераций, все особи окажутся одинаковыми, равные лучшему индивидууму
в начальной популяции:
Но дальше
процесс не пошел, популяция не стала улучшаться и достигать максимально
возможного уровня в 100 единиц. И это очевидно, так как хромосомы в начальной
популяции никак не меняются, здесь только отбрасываются худшие и не более того.
В результате, разнообразие особей утрачивается и все становятся идентичны друг
другу.
Также очевидно,
что мы можем добавлять разнообразие в хромосомы с помощью мутации генов. Для
этого вернем значение параметра:
Действительно,
это вносит разнообразие в популяцию, время от времени появляются лучшие особи и
оператор отбора, затем, подгоняет остальных особей вверх, делая популяцию
лучше. Однако, после 50 итераций (поколений) лучшего решения в 100 единиц так и
не было достигнуто:
Даже, если
увеличить вероятность мутации, все равно скорость сходимости алгоритма к
оптимальному решению оказывается ниже, чем с операцией кроссинговера
(скрещивания). Этот пример хорошо показывает, что генетический алгоритм – это
не просто направленный случайный поиск, когда мы случайным образом меняем
значение параметра, а затем, смотрим близость к решению. Здесь важную роль
играет оператор кроссинговера (скрещивания), когда части хромосом родителей
перемешиваются для получения потомков:
И это работает
гораздо эффективнее простой мутации. Представьте, что в двух разных частях
хромосом родителей уже сформировано оптимальное решение. Тогда достаточно
произвести обмен и будет получен потомок с наилучшим набором генов. Именно
благодаря этому операция кроссинговера дает заметное ускорение сходимости ГА к
оптимальному решению.
Давайте снова
включим его и убедимся в этом:
Может быть,
тогда мутация не играет особой роли и ее можно отключить и ГА с тем же успехом
будет находить наилучшее решение? Давайте проверим. Поставим вероятность
мутации ноль:
и после запуска
программы увидим следующий результат:
Видите, в
процессе скрещивания и отбора были потеряны особи, которые хранили нужные гены
для достижения оптимального решения. Мутация решает эту проблему. Она постоянно
вносит небольшое разнообразие в популяцию и случайно потерянная информация
(фрагменты хромосом) также случайно восстанавливается. Это гарантирует, при
большом числе итераций, существование достаточных данных для нахождения
оптимального решения.
Преимущества и недостатки ГА
Как у всех
алгоритм, ГА имеют свои преимущества и недостатки по сравнению с другими алгоритмами
оптимизации (назовем их классическими). Начнем с преимуществ:
- способны находить
глобальные оптимумы (с большей вероятностью, чем традиционные алгоритмы);
- можно применять
в задачах, которые сложно формализовать математически, либо даже совсем не
имеющие математического описания (функция приспособленности может быть выражена
на уровне алгоритма);
- устойчивы к шуму
(например, когда данные не могут быть точно измерены или основаны на
субъективном восприятии человека);
- можно
реализовать на уровне параллельных вычислений для ускорения работы алгоритма;
- возможность непрерывного
обучения (оптимизации) популяции при постоянном изменении окружающей среды (это
изменение сказывается на значениях функции принадлежности и популяции
необходимо постоянно подстраиваться под новые обстоятельства).
Недостатки генетических алгоритмов
К общим
недостаткам при реализации ГА можно отнести следующие моменты:
- специфичная
формализация исходной задачи на уровне генов и хромосом, а также операторов
отбора, скрещивания и мутации;
- определение
внешних параметров работы алгоритма (размер популяции, вероятности скрещивания
и мутации, способ отбора и кроссинговера и т.п.);
- относительно
большой объем вычислений (каждая особь в популяции, фактически, отдельный ход
оптимизации задачи);
- некоторые особи
в популяции могут быстро занять локальный оптимум и потянуть за собой всю
популяцию (будут продублированы оператором отбора много раз, и популяция будет
состоять, в основном, из их отпрысков), в результате другой, глобальный оптимум
может быть не обнаружен;
- генетический
алгоритм не гарантирует нахождение именно оптимального решения, нам приходится
довольствоваться тем лучшим решением, которое было найдено (однако, это
недостаток многих алгоритмов оптимизации).