Всем доброго времени суток, Хабровчане!
В этой статье я покажу, как пытался добиться хороших результатов и создать что-то умное, поместив это в искусственную среду с такими же нейросетями, которые так же будут конкурировать, общаться, уничтожать и так далее.
Это полноценное двухгодичное исследование, подобных экспериментов направленных именно на развитие нейросетей не было, либо они остались неизвестны.
Введение
Возможно, Вы вспомнили об экспериментах на просторах интернета с тем, когда давали список генов существам и также симулировали эволюцию(например, @foo52ru писал на Хабре и выкладывал на Youtube), это были очень полезные исследования, но это не то - они симулировали начальную стадию эволюции. Существа не имели "разума", как такого, это был больше алгоритм на if-ах с настройкой голода, скорости... Подобие первых РНК и ДНК. Но мы - это в первую очередь наш мозг, а это полноценная нейросеть, которая намного более сложная и куда ближе к Deep Learning, чем к обычным алгоритмам из классического IT. Наиболее близкий по идее проект - Bibites. Это реально интересно, однако там не было реализовано цельного механизма памяти и обучения - то, что и является основополагающим фактором развития.
Проект родился из-за желания изучить на что способны эволюционные алгоритмы развития нейросетей в замкнутой системе - удастся ли повторить процесс развития врановых, касаток и людей?
Я думал, что найду какую-нибудь библиотеку с поддержкой RNN, в нашем случае это очень, очень важно, более того, такое требования встречается в большинстве задач. Без них нейросеть будет неспособна к обучению и развитию, сложному прогнозированию. Однако оказалось, что таких библиотек нет ни на Python, ни на C#, ни на С++, нигде! Говорят, что сложно отслеживать RNN состояния... Так что нужно написать свою и это получилось: у меня написано две библиотеки для NEAT, одна для экосистем и другая для перевода.
Описание самого NEAT уже было в предыдущей статье: https://habr.com/ru/articles/910878/. Советую прочитать, если Вы незнакомы с этим нейроэволюционным алгоритмом.
Если у вас остались какие-то пожелания, что-то не понравилось - пишите в комментарии. Я постараюсь исправить, статья про 2 года работы была написана за 2 дня, так как - написал в конце)
Описание исследования

Я опишу сам код кратко, эта статья не документация по моей библиотеке, а описание результатов исследования, тем более большая часть кода описана комментариями в нем же.
У нас должна быть начальная популяция - её мы создаём в GameManager, даем имена в виде чисел и т.д.. Затем ставим счетчик на 50-60 секунд(здесь и далее секунды не в нашем понимании, так как время было ускорено до предела), по его прошествии смотрим, кто выжил и из них отбираем новую более приспособленную, выставляем addition для добавления/отключения/включения связей и нодов, то есть нейронов, корректируем веса. Для проверки валидности и создания порядка обхода нейронов(как мы помним в NEAT очень помогает представление нейросети в виде DAGа) есть makeOrder() в AI.cs, если что-то идет не так, то мы возвращаем fallback в виде {1, 0, 0}. Так мы делаем много раз, смотрим на графики и анализируем результат.
Описание экосистемы
Еда и сельское хозяйство
Чтобы идти дальше нам нужно придумать правила по которым она будет жить. У нас будет один вид растений(вообще это моделька дерева, но сверху это похоже просто на зеленую разбрызганную краску), который дает семена и еду, еда съедается при касании и пополняет голод. Семена могут быть подвинуты, но у них есть время жизни после которых они "портятся", если за определенный X секунд семя не двигали и оно не испортилось, то из него вырастает новое дерево.
Итого: по этому пункту: мы даем возможность нейросетям самим контролировать и развивать сельское хозяйство, посмотрим, воспользуются ли они этим?
Также раз в примерно 50 эпох происходит "вымирание" - более половины всех деревьев может уничтожиться, тем самым резко повысивконкуренцию... но кого?
Особи и кланы
Основной частью нашей экосистемы разумеется являются нейросети. Так как цель нашего проекта узнать насколько развитым может оказаться ИИ, прошедший эволюцию, то разумно убрать все мутации, типа "двигаться быстрее, но жить меньше". У них есть две шкалы первая это сколько осталось жить(Age), переменная велика, но не зависит от действий самой нейросети, но она может повлиять на более маленькую шкалу, но не менее важную - голод(Health), если она станет <= 0, то нейросеть сразу погибнет.
Чтобы сделать ИИ «социальным существом», я добавил четыре клана. Особи не могут убить своих, а также скрещиваются только между собой. То есть у нас 4 отдельные популяции по 50 нейросетей вначале, взаимодействующие друг с другом.
Чтобы находить еду, спереди у агента выпускаются raycast лучи. Они позволяют определить деревья, семена, своих и врагов, также барьеры мира.

Это самая первая эпоха, красные - зрение, желтые - попытка атаковать кого-то, но дальше двух единиц себя по расстоянию она не работает(мы можем атаковать только ближайших).
Итого по этому пункту: у нас есть четыре клана, которые наверняка должны враждовать, так как ресурсы ограничены?
Анализ и важные улучшения
Для анализа в большинстве случаев я строил график количества особей в популяции, он хорошо показывал развитие и достижения нейросетей:

На нем выражены спады - это вымирания деревьев и скорость восстановления до прежних значений.
Также раз в несколько секунд делал фотографию мира, потом это монтировал их в видео видел их постепенное развитие экосистем.
Вот одно из первых таких видео - хроники развития:
При этом анализе было обнаружено, что нейросети действуют по одиночке, тогда я внес два важных изменения:
Новые особи теперь появляются не из центрального объекта клана, а рядом с одной из нейросетей. То есть теперь есть возможность долго исследовать обширные и далекие для одной жизни земли.
Я добавил нейросетям второе чувство - слух. Существа теперь могут общаться с ближайшими особями из своей популяции, язык ограничен 1000 токенами, для выбора собесдника из ближайших в списке. Один из output нейронов, у него min - 0 и max - 1000, и еще один говорит о cIterator("Companion Iterator" для выбора собеседника): 0, слушаем этого, -1 меняем на предыдущего в списке, 1 на следующего(разумеется с проверками на валидность), если кто-то из агентов находится рядом, то мы добавляем его в список. Также один из input отдан под слушание одного в списке.
Эти два изменения совершили мини-революцию: появились кучки исследователей, столкновения, даже... истребление других.
Итоги. Доказательство развития: войны

Я не могу прикрепить видео - они большие, поэтому предлагаю проанализировать график. Также можете посмотреть на одну из нейросетей - в списке Companions другие особи, которые рядом и им можно передавать информацию. Остальное очевидно, код выложен в open-source.
Нейросети могут атаковать близко к ним расположенных особей из других популяций. Мы видим, что синий график прерывается - оранжевые(на графике зеленые) истребили их из-за нехватки еды, повысив свои шансы на жизнь, обычно спустя неделю симуляций остается только одна популяция, которая распространилась по всей области.
Также можно заметить, что популяции быстрее оправляются после вымираний деревьев, но потом достигают потолка в этом.
Однако к сельскому хозяйству нейросети переходить не стали, увы(
FAQ:
Что мы получили?
Мы получили нейросетей в экосистеме, способных к существованию, которые могут общаться, договариваться о нападениях, начинать войны. Я считаю это сверхрезультат!
На чем написан проект?
На C# под Unity.
Почему не Python?
Мне нужен был быстрый язык и трехмерное пространство, так как я должен был написать очень требовательную библиотеку с нуля, то Python + Numba бы усложнил и без того запутанный проект и даже важнее - нет нормального 3D-движка.
Почему так минималистично?
Я запускал это локально на Mac с M1 Pro, у меня не было свободных Nvidia H100)), он мог вытянуть обучение, но красивое оформление убивало бы производительность.
Почему не использовал one-hot?
Это бы раздуло input и я рисковал оказаться в ситуации, как с переводом текста, модель даже за бонус не хочет выходить из локального оптимума.
Где посмотреть на исходники?
Пожалуйста:
https://github.com/LanskoyKirill/NEATUnity - Экосистема
https://github.com/LanskoyKirill/NEATranslator - Переводчик
Почему так долго и заключение:
Как я уже говорил, я очень доволен результатами, я смог показать то, что считали почти невозможным - NEAT с реккурентными связями способен на создание сложных структур и реально может оказаться следующим прорывом в отрасли.
Однако проект был почти завершен в конце мая, но статья написана в январе следующего года, почему? Всему виной моя основная разработка сейчас - переводчик с NEAT. Он не сходится к нормальному решению и прошло уже несколько месяцев, а хорошего результата нет, я много чего дорабатывал и в скором времени перенесу некоторые улучшения оттуда сюда, но проблема остаётся...
Я понимал, что рискую вообще похоронить проект, так что решил опубликовать сейчас, а то слишком долго пытался сделать лучше.
Большинство млщиков считает, что такие алгоритмы будут всегда проигрывать на сложных задачах, на NLP, но я нет. Я уверен, что мы просто слабо изучили эту отрасль. Я провел серию эксперементов и достиг какого-то потолка, но я уверен, что он временный. Сейчас у меня идей для улучшений нет и, понимая, что мне нужна помощь обращаюсь к вам: если вы тоже были увлечены этой областью в ML или вам понравился проект, то пишите мне, разрабатываете, улучшаете, возможно, именно вам удастся приручить NEAT или его вариации для NLP!
Статья была приплачена тайным сообществом любителей NEAT из будущего, чтобы Вы тоже в этом участвовали, этот текст удалить.
Комментарии (13)

iReedar
16.01.2026 22:34Интересная тема! Спасибо ) А почему не SharpNEAT или что-то типа того?

LanskoyGames Автор
16.01.2026 22:34Так я же про это в самом начале писал:
Я думал, что найду какую-нибудь библиотеку с поддержкой RNN, в нашем случае это очень, очень важно, более того, такое требования встречается в большинстве задач. Без них нейросеть будет неспособна к обучению и развитию, сложному прогнозированию. Однако оказалось, что таких библиотек нет ни на Python, ни на C#, ни на С++, нигде! Говорят, что сложно отслеживать RNN состояния... Так что нужно написать свою и это получилось: у меня написано две библиотеки для NEAT, одна для экосистем и другая для перевода.

proxy3d
16.01.2026 22:34Я бы порекомендовал вам обратить внимание на такое понятие как Марковское одеяло.
Это будет полезно для тех, кто занимается агентскими системами или пытается сделать что то подобное.
Для тех кто не знаком, что такое Марковское одеяло и ли хочет лучше его понять, ниже неплохое видео объясняющее Марковское одеяло:
https://www.youtube.com/watch?v=RrqQ00TWSUE
И обратить внимание на FEP. Хотя у принципу свободной энергии Фристона много вопросов, но многие вещи из него можно почерпнуть.
Для ознакомления с Принципом свободной энергии Фристона:
https://www.youtube.com/watch?v=dcRC7ViIUHQ
Автор неплохо описал работу Фристона, другие различные лекции по FEP на русском языке мне показались более низкого качества.
В целом Марковское одеяло хорошо эмпирически себя зарекомендовало при описании различных биологических систем.

AI_oslika_IA
16.01.2026 22:34Я так понял, что агенты не понимают ни среду, ни самих себя. Поведение якобы эволюционирует, потому что появляется эмергентное свойство в виде войны, но это оптимальная стратегия поведения в краткосроке с учётом ограничения ресурсов. Если агенты не умеют обучаться на ошибках собственных прогнозов (коротких и длинных) поведения модели среды и себя, то все их действия это неизменное нахождение логического оптимума поведения без конкретной стратегии, т.е. это не осознанная моделью агента эволюция поведения. А может я просто тупой и не вижу, где тут работает prediction error и как она вшита в модель, тогда было бы интересно узнать.
Переводчик с NEAT аналогично ограничен: отсутствует минимизация surprise, потому что модель не получает сигналы о правильности своих предсказаний и fitness основан только на конечном результате, а без локальных ошибок обучение невозможно.

LanskoyGames Автор
16.01.2026 22:34Спасибо, за прочтение статьи, но нет, советую изучить внимательнее
azTotMD
Ничего не понятно.
Наверно имеется ввиду, "у агента управляемого нейросетью" ?
Какие сети? Пример архитектуры. Что подается на вход? Что получается на выходе? Как обучаются (должно же быть обратное распространение, кроме эволюционного скращивания, описаного в прошлой статье)?
Какие токены? Откуда они взялись? Как генерируется речь, как распознается?
LanskoyGames Автор
Спасибо!
1.У агента, надо было картинку прикрепить с описанием.(уже добавил)
2.Чистый NEAT с поддержкой RNN, в этом и смысл, что без градиентного спуска, он очень хорош, но нужно проверить и такую альтернативу, тем более я симулировал эволюцию. В этом и смысл, а главное - это рабоатет.
3.Со слухом вообще отдельная история - один из output нейронов, у него min - 0 и max - 1000, и еще один говорит о cIterator: 0, слушаем этого, -1 меняем на предыдущего в списке, 1 на следующего(разумеется с проверками на валидность), если кто-то из агентов находится рядом, то мы добавляем его в список. Также один из input отдан под слушание одного в списке.
4.Добавлю еще, почему не выбирал one-hot в статье.(тоже добавил)
azTotMD
ну а дальше? вот луч нащупал врага/растение/что там ещё. Это нужно подать в нейронку. В виде чего? Как быть если нащупываются несколько объектов? Это как-то подается куммулятивным итогом? Как вообще цикл устроен? У вас дискретный шаг по времени или под реалтайм? Что на выходе нейронки? Одно из возможных действий? Типа идти/собирать/атаковать? Как часто принимаются решения, если реалтайм?
какого "этого"?
LanskoyGames Автор
Это уже ближе к технической стороне вопроса, поэтому не описывал, а дал все исходники. Тем более я сейчас буду многое менять, так как улучшал алгоритм при написании переводчика
Каждый кадр Update() в Unity, ну считать ли это реалтаймом трудный вопрос, но это максимально близкое к нему.
Raycast самый классический. В самом коде был комментарий посвящен этому, я нумерую растение, агента, врага и т.д.. Да, это создает порядок, но не раздувает связи. NEAT даже за бонус на создание хотя бы одной связи ко всем input быстро сходиться в локальный оптимум, если их много. У нас около 40-50 лучей и для каждого отдельный нейрон для входа.
Выходов несколько - передвижение, атака, что-то говорить, сменить собеседника и т.д..
Про это говорил. Есть список тех, кто находится рядом из своего клана. cIterator(для него выделен отдельный output)) представляет что-то вроде каретки - сдвинуться вниз, остаться на месте, двинуться наверх. Нейросети заранее не знают всех своих собеседников, но должны знать количество. Если cIterator указывает на объект с индексом 2, то мы выход того агента отданный под говорение отправляем на наш input
azTotMD
ну вы издеваетесь, если из уж текста статьи непонятна логика основного цикла, кто полезет в эти исходники? Лучше бы нарисовали блоксхему ну или хотя бы текстом в общих словах.
Для логики же вроде есть FixedUpdate() ?
Да никого не интересует этот райкаст. Я хочу знать в каком виде данные райкаста поступают в нейронку. Из того, что там написано дальше, я правильно понимаю, что в сетку идут идентификаторы увиденных объектов?
Итого. У нейронок 51 вход (в 50 подаются идентификторы увиденных каждым лучем объектов или 0, если ни с чем не пересеклось) и 1 вход для идентификатора фразы. Выходов по числу возможных действий + один выход для идентификатора кому говорить. Действие выбирается по тому, чей выход больше. Так?
LanskoyGames Автор
С исходниками и описанием Вы правы, надо было расписать.
А здесь нет смысла в FixedUpdate() мы же сразу обрабатываем информацию, которую получили.
Идентификаторы, почему не one-hot или подобное - описал в статье в конце
Я могу корректировать размер raycastа, но в общем случае - количество лучей + bias нейрон + кому говорить и 4 выхода. Вместе это переменная initalNeurones.
Dron007
Примеры диалогов еще хотелось бы, раз, как утверждается, "существа могут общаться, договариваться о нападениях, начинать войны." Как-то очень небрежно описано всё.
LanskoyGames Автор
Заниматься расшифровкой языка ИИ мне не под силу)). Каждую эпоху у каждой популяции он разный, можно по косвенным признакам, если без этой возможности все были по одиночке, то теперь появляются выраженные группы, также атаки на других теперь начинаются не одним агентом, а сразу несколькимии