Время прочтения: 5 мин.

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

Ген, геном, поколение – наборы нейронов и их связей, которые использует генетический алгоритм

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

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

Мутация – бывают двух видов: появляется новая связь между существующими, создается новый нейрон на месте уже существующей связи.

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

NEAT алгоритм (NeuroEvolutionofAugmentingTopology) – алгоритм для создания развивающихся искусственных нейронных сетей, созданный Кеном Стенли в 2002 году. Он изменяет весовые параметры и структуру сетей, чтобы найти баланс между пригодностью разработанных решений и их разнообразием. Он включает три основных метода: отслеживание генов с помощью маркеров истории, позволяющих пересечение топологий, эволюция видов и постепенное разнообразие топологий из простых исходных структур.

Мы будем исследовать NEAT алгоритм на примере обучения искусственного интеллекта играть в игру. Мой выбор пал на Dinosaur Game (доступен через Google chrome по ссылке chrome://dino). Цель этой игры состоит в том, чтобы игрок набрал как можно больше очков, уклоняясь от препятствий в виде кактусов и летающих птиц. Для упрощения встраивания алгоритма нейронных сетей напишем собственную версию игры.

Динозаврик умеет прыгать и пригибаться. Из препятствий на его пути будут кактусы разной высоты и летающие птицы, расположенные на разном расстоянии от земли. Логика игры проста. Динозаврик бежит на месте, а земля и различные препятствия двигаются навстречу к нему постепенно ускоряясь, если они сталкиваются, то динозаврик умирает и игра начинается заново.

Далее была разработана логика алгоритма искусственных нейронных сетей. Всего в поколении за раз используются 500 особей. Цель каждого динозаврика пробежать как можно дальше, счетчиком этого будут очки, отображаемые снизу слева. В каждом поколении динозаврики разделяются на небольшие группы – виды, по схожести развития и действий особей, например, одни динозаврики прыгают не переставая с самого начала, а вторые бегут, сразу пригнувшись. В процессе забега изучаются действия динозавриков, которые приводят к наилучшему результату. Когда все динозаврики умирают, они располагаются в рейтинге по убыванию набранных очков. На этом этапе происходит искусственный “естественный отбор”:

    speciate();//разделяем популяцию на виды
    calculateFitness();//вычисляем приспособленность каждого игрока
    sortSpecies();//сортируем виды для ранжирования в порядке пригодности, сначала лучшие
    if (massExtinctionEvent) { 
      massExtinction();
      massExtinctionEvent = false;
    }
    cullSpecies();//убиваем нижнюю половину каждого вида
    setBestPlayer();//сохраняем лучшего игрока этого поколения
    killStaleSpecies();//удаляем виды, которые не улучшились за последние 15 (ish) поколений
    killBadSpecies();//убиваем виды, которые настолько плохи, что не могут эволюционировать

Далее идет создание следующего поколения. Лучшие особи предыдущего поколения становятся родителями для следующего с небольшими мутациями.

  ArrayList<Player> children = new ArrayList<Player>(); //новое поколение
    println("Species:");               
    for (int j = 0; j < species.size(); j++) { //for каждого вида

      println("best unadjusted fitness:", species.get(j).bestFitness);
      for (int i = 0; i < species.get(j).players.size(); i++) {
        print("player " + i, "fitness: " +  species.get(j).players.get(i).fitness, "score " + species.get(j).players.get(i).score, ' ');
      }
      println();
      children.add(species.get(j).champ.clone()); //добавить чемпиона  без каких-либо мутаций

      int NoOfChildren = floor(species.get(j).averageFitness/averageSum * pop.size()) -1; //допустимое количество детей этого вида, примечание -1, потому что чемпион уже добавлен
      for (int i = 0; i< NoOfChildren; i++) { //получаем рассчитанное количество детей от этого вида
        children.add(species.get(j).giveMeBaby(innovationHistory));
      }
    }
    while (children.size() < pop.size()) { //если не хватает кол-во детей для популяции
      children.add(species.get(0).giveMeBaby(innovationHistory));//получить детей от лучших видов
    }
    pop.clear();
    pop = (ArrayList)children.clone(); //установить детей как текущее поколение
    gen+=1;
    for (int i = 0; i< pop.size(); i++) { //генерировать сети для каждого из детей
      pop.get(i).brain.generateNetwork();
    }
    
    populationLife = 0; 

После первого запуска динозаврики делали случайные действия, одни только прыгали, другие только пригибались, третьи делали и то и то. Спустя несколько поколений динозаврики научились перепрыгивать наиболее часто встречающиеся препятствия: кактусы и низко летящих птиц.

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

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

В итоге лучшие из лучших особей смогли преодолеть 10000 очков на огромной скорости, где человек уже не сможет среагировать на препятствия.

У искусственного интеллекта вырабатывается нечеловеческая реакция, и эта характеристика может очень сильно помочь человеку. После обучения такой нейронной сети ее можно встроить, например, в автопилот для автомобиля. Искусственный интеллект во время поездки сканирует все находящиеся вокруг препятствия, так же, как и динозаврик смотрит на кактусы, и выбирает наиболее лучшее и безопасное направление движения автомобиля. Для применения в Data science большую роль сыграет изменения весов связей и выявление наиболее важных признаков (или их совокупности) поиска информации, в случае динозаврика он большое внимание обращал на расстояние до препятствия, но ему было все равно на ширину препятствия и его собственную скорость.

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