Может ли нейронная сеть изучить правила игры Конвея в жизнь? Почему и как?

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

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

Только пошутил, ну про последнее. Я не скептик в отношении ИИ, человек «приближается зима». Многие люди, занимающиеся машинным обучением, изучают разные подходы, если не в своей работе, то в свое личное время. Из известного нам ИИ можно извлечь огромную пользу, просто создавая на его основе системы и приложения; и сами методы машинного обучения продолжают совершенствоваться, часто фундаментально.

Хватит вафли. Давайте сделаем что-нибудь конкретное. Я изучаю, как искусственная жизнь - теория сложности - может помочь с ИИ. Фракталы. Теории инноваций и организационной структуры, о которых вы бы прочитали в HBR, если бы вы только удосужились прочитать HBR. Экология. Гайя, бабочки на горе Фудзи, облака в небе, миры в песчинке и золотое сечение в полевом цветке.

Перемотка назад

Я не пишу это для людей, которые не знакомы с «Игрой жизни» Конвея, но я быстро набросаю основные моменты: сетка ячеек, каждая живая или мертвая, каждая следует простым правилам с течением времени, удивительно сложная поведение возникает. Правила для каждой ячейки:

  • Если клетка живая и у нее есть ‹2 или› 3 живых соседа, на следующем временном шаге она умирает (как будто от одиночества в первом случае или перенаселения во втором).
  • Если клетка мертва и имеет ровно 3 живых соседей, на следующем временном шаге она оживет (как будто путем 3-полового размножения).

Вот пример того, что вы получаете от GoL.

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

Никто из тех, с кем я когда-либо беседовал, не предлагал реализовать GoL в виде свертки. Вот мой код для создания Игры жизни Конвея в Pytorch и отображения ее с помощью библиотеки изображений Python и OpenCV.

Самый важный момент - это свертка и нелинейность (вместе составляющие правила). Этот бит:

newboard = F.conv2d(board, weights, padding=1) \  .view(BOARD_HEIGHT,BOARD_WIDTH)
newboard = (newboard==12) | (newboard==3) | (newboard==13)
# ^^ a weird non-linearity

Вот рисунок, который, я надеюсь, иллюстрирует, как / почему работает свертка:

Может ли нейронная сеть изучить Игру Жизни?

Здесь у нас есть:

  • Неограниченные обучающие данные (сколько угодно игр, начиная с начальных случайных конфигураций)
  • Ядро свертки с весами, которые должны быть обучаемыми
  • Странная, недифференцируемая нелинейность

Чтобы градиентный спуск работал, я решил заменить нелинейность некоторыми полностью связанными слоями и функциями плавной активации. Как ни странно, я добился лучших результатов с SELU, который выглядит так:

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

Модель очень простая:

class GoL(torch.nn.Module):
    def __init__(self):
        super(GoL, self).__init__()
        self.conv1 = torch.nn.Conv2d(1, 1, (3, 3), padding=1)
        self.fc1 = torch.nn.Linear(BOARD_HEIGHT*BOARD_WIDTH, BOARD_HEIGHT*BOARD_WIDTH)
def forward(self, x):
    x = self.conv1(x)
    x = self.fc1(x.view(-1, BOARD_HEIGHT*BOARD_WIDTH))
    x = F.selu(x)
    return x.view(-1,1,BOARD_HEIGHT,BOARD_WIDTH)

Пара домашних дел:

  • Я использовал Адама в качестве оптимизатора.
  • Я «записал», может быть, 10 игр в качестве тренировочных данных. Мой ранний код работал только с batch_size=1, поскольку он считывал все кадры последовательно, по 2 за раз в цикле обучения. Впоследствии я преобразовал данные в пары (frame, next frame), чтобы они работали партиями. Перестановка кадров в загрузчике данных была важна, чтобы разные пакеты не просто брали градиент в совершенно разных направлениях.
  • Среднеквадратичная ошибка, основная рабочая лошадка функций потерь, работала хорошо. Теперь, в любой момент времени, большинство ячеек мертвы, и потеря от предсказания 0 будет… 0. Сеть, скорее всего, предпочтет и предпочла предсказывать мертвые ячейки. Таким образом, я увеличил потери от пропущенных переходов между состояниями на постоянный коэффициент.

Это сработало?

Да, результат получился похожим на Game of Life, с заметными общими конфигурациями, такими как блоки, ульи, шоры и жабы - даже некоторые примитивные планеры - или, по крайней мере, их приближения. Игра также имеет тенденцию развиваться до стабильного состояния, как настоящий GoL. Вот пара примеров:

Печальная правда в том, что мы получаем идеальный GoL с 4 простыми правилами, и я не думаю, что сеть научилась упрощаться таким образом. Скорее, он использует десятки тысяч параметров, чтобы имитировать то, что он видит, пиксель за пикселем, вместо того, чтобы изучать возникающие сложные явления, как это мог бы сделать человек, когда им представляются эти изображения.

Я пробовал несколько разных вещей, в частности, я попытался использовать 2 линейных слоя в качестве «странной нелинейности», где между ними образовалось узкое место (т.е. 100x100 пикселей, затем сжатие с 10k до 5k, а затем принудительное восстановление сети из сжатой версии). Интуитивно я думал, что сеть научится описывать «планер на 33,10; жаба на 40,80; … », Но от этого стало еще хуже.

Но с другой стороны… что именно здесь развилась сеть? Совершенно новый вид клеточных автоматов? Есть с чем поиграть.

Заключение

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

Нейронные сети с пиками - один из примеров этого, который лучше отражает, как мозг на самом деле работает на практике, благодаря «самоорганизованной критичности».

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