Нейроэволюция — один из самых удобных алгоритмов машинного обучения, с которым можно создавать, настраивать и экспериментировать. В сегодняшней статье мы создадим наш собственный алгоритм нейроэволюции, чтобы научить популяцию автомобилей успешно перемещаться по гоночной трассе.

Мы будем использовать P5.js для графического движка, Tensorflow для обработки наших нейронных сетей и Javascript для объединения всего этого!

Вы можете ознакомиться с проектом, размещенным на Codesphere, здесь: https://38098-3000.codesphere.com/

Давайте прыгать прямо в!

Что такое нейроэволюция?

Нейроэволюция — это метод искусственного интеллекта, который имитирует эволюцию и генетическое воспроизводство для создания «интеллектуальных» моделей, чаще всего в форме искусственных нейронных сетей.

Нейроэволюция чаще всего осуществляется путем создания поколения агентов (в нашем случае автомобилей), которые имеют совершенно случайные веса и предубеждения. Это означает, что они будут эффективно принимать решения случайным образом и, следовательно, не продвинутся далеко.

Затем мы моделируем, как это поколение автомобилей справляется с задачей, которую мы хотим обучить (в данном случае это езда по гоночной трассе).

Затем мы присваиваем каждому автомобилю в поколении оценку того, насколько хорошо он сделан. Вот тут-то и начинается эволюция, потому что мы используем самые результативные автомобили для создания нового поколения.

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

Селекция — это процесс, посредством которого мы выбираем признаки из родительского поколения. Мутация — это процесс, посредством которого мы случайным образом генерируем признаки для нового поколения.

Нейроэволюция создаст новое поколение посредством отбора и мутации. Как и настоящая эволюция, это имеет тенденцию приводить к тому, что следующее поколение будет немного лучше, чем предыдущее.

Со временем наше количество автомобилей будет становиться все лучше и лучше, пока они не смогут работать на уровне, который нас устраивает.

Наша среда вождения

Я потратил время на предварительную сборку симулятора вождения с помощью P5.js, графической библиотеки Javascript и большого количества декартовой геометрии (Shoutout Desmos).

Если вы хотите, чтобы пустой симулятор вождения следовал за вами, все это содержится в двух файлах, index.html и car.js:

Если вы запустите приведенный выше стартовый код, у вас должно быть первое поколение автомобилей на стартовой линии. Поскольку мы еще не дали им возможности двигаться, все они будут стоять на месте.

Первым шагом в любом алгоритме нейроэволюции является определение входов и выходов, к которым имеет доступ ваш вид. В нашем случае каждый автомобиль может определить, насколько близко находится объект в каждом из 5 направлений (обозначено красными линиями). Он получит число от 1 до 20, представляющее, насколько близко находится объект. Если объекта нет, они также получат 20.

С точки зрения выхода каждая машина может принять четыре решения:

  • Ускорить
  • Тормоз
  • Поверните направо
  • Поверните налево

Конечно, если машина врежется в стену или препятствие, она погибнет.

Показатель пригодности автомобиля (насколько он хорош) определяется тем, сколько кругов он проехал (по 100 каждый) плюс расстояние по трассе (от 0 до 100). Например, если машина проедет 2 с половиной круга, прежде чем заглохнет, ее приспособленность будет равна 250.

Создание нашей нейронной сети с помощью Tensorflow

Теперь давайте дадим каждой машине возможность принимать решения о вождении. Мы будем снабжать каждый автомобильный объект искусственной нейронной сетью, которая принимает 5 целых чисел, представляющих близость к препятствию в каждом направлении. Тогда у нас будет один скрытый слой из 8 нейронов с активациями ReLU. Наконец, нейронная сеть выдаст 4 значения для каждого решения, которое она может принять:

  • Ускорить
  • Тормоз
  • Поверните направо
  • Поверните налево

Выходной слой будет использовать сигмовидную функцию активации, которая будет давать нам значения от 0 до 1 для каждого решения. Затем машина будет умножать каждое решение на максимальную скорость, с которой она может выполнять действие.

Например, предположим, что мы позволяем каждому автомобилю разгоняться с максимальным ускорением 0,50. Если он выводит 0,20 для первого нейрона, то мы добавим к скорости 0,2 * 0,5 (таким образом, 0,1).

Давайте сначала создадим модель в классе Car:

Обязательно вызовите указанную выше функцию в конструкторе класса Car.

Затем, прежде чем мы обновим положение каждого класса автомобиля в нашей функции рисования, давайте соберем входные данные и используем эту модель для принятия решений для автомобиля:

Теперь наши автомобили могут принимать решения, но имейте в виду, что веса и смещения наших нейронных сетей совершенно случайны. Таким образом, автомобили, скорее всего, будут заниматься ерундой:

На самом деле многие наши машины просто бесцельно крутятся кругами или вообще ничего не делают. Не о чем беспокоиться, поскольку все, что нам нужно, это случайно решить двигаться вперед, чтобы наша эволюция шла в правильном направлении!

Генетическая селекция и разведение новых поколений

Следующим шагом является разведение нового поколения. Мы будем использовать то, что известно как Пропорциональный фитнес, или Колесо рулетки, Выбор.

Этот алгоритм работает, рассматривая показатели пригодности каждого автомобиля в родительском поколении и случайным образом выбирая черты всех родителей, так что наиболее эффективные родители имеют наибольшую вероятность передачи своих черт.

Чтобы вычислить это, мы возьмем сумму оценок пригодности каждого автомобиля и используем функцию кумулятивного распределения, чтобы выбрать автомобиль. Если вам нужно более подробное объяснение математики здесь, свяжитесь с нами, и мы будем рады объяснить более подробно.

Кроме того, давайте создадим функцию, которая будет вычислять общую пригодность в конце поколения, а также вычислять наивысшую пригодность в поколении для целей сравнительного анализа (позже мы будем использовать метку, чтобы показать наивысший балл и текущее поколение).

Последняя вспомогательная функция, которая нам нужна, — это способ скопировать вес каждой машины, чтобы мы могли передать его новым поколениям:

Теперь давайте напишем нашу функцию для создания нового поколения.

Для каждого нового потомка мы будем перебирать вес в его модели и случайным образом назначать ему вес от родителя (используя наш алгоритм выбора).

Наконец, давайте запустим это новое поколение всякий раз, когда каждая машина неактивна, или по прошествии определенного времени.

Давайте создадим в нашем index.html целое число с именем frameCount, чтобы отслеживать, как долго длится поколение. Затем добавим следующий код в конец нашей функции рисования:

Теперь, если все реализовано правильно, мы можем начать видеть некоторый прогресс в течение пары поколений:

Но также невероятно вероятно, что вы можете увидеть что-то вроде этого:

Это связано с тем, что черты поколения полностью определяются чертами их родителей. Это означает, что если вы сможете создать особенно ужасное поколение, вы продолжите получать ужасных детей и застрянете в бесконечной петле.

Вот где в дело вступает мутация.

Генетическая мутация

Мутация — это когда определенному весу присваивается случайное значение. Вероятность мутации веса, а не выбора от родителя, является нашей частотой мутаций.

Мы возьмем нашу частоту мутаций из созданного нами текстового поля и обновим нашу новую функцию генерации следующим образом:

Это позволит поколениям иметь подстановочные черты и подняться над конкурентами.

Теперь мы должны увидеть значительный прогресс поколений:

Таким образом, мутация позволяет нашим машинам экспериментировать с новыми стратегиями, а отбор позволяет хорошим стратегиям распространяться среди населения.

Спустя всего 50 поколений у нас есть большинство наших автомобилей, способных преодолевать круги.

Окончательный код

Что дальше

Если вы действительно хотите бросить себе вызов, поиграйте с препятствиями. Мы специально сделали это так, чтобы вы могли очень легко пройти более сложную трассу.

Кроме того, есть всевозможные алгоритмы, которые вы можете использовать для мутации и отбора, играя с ними, вы сможете улучшить производительность алгоритмов нейроэволюции!

Это все от нас сегодня, и помните

Живая демонстрация (развернута на Codesphere): https://38098-3000.codesphere.com/

Есть вопросы? Оставьте их ниже, и мы будем рады помочь.

Как всегда удачного кодинга от команды Codesphere. Мы создаем Web IDE, работающую в облачной инфраструктуре, чтобы ваши возможности по созданию и развертыванию программного обеспечения никогда не ограничивались вашим локальным компьютером.