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

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

В некотором смысле это можно было бы рассматривать как часть 3 моего глубокого обучения с подкреплением, но я думаю, что эта статья также может быть самостоятельной. Обратите внимание, что в отличие от этих предыдущих руководств, в этом посте будет использоваться PyTorch вместо Keras, в основном потому, что это то, на что я лично переключился, но также потому, что PyTorch действительно больше подходит для этого конкретного случая использования. Для частей с 0 по 2 см:



Настройка среды

Следующие элементы идентичны DQN (Mnih et al., 2015): (1) предварительная обработка данных, (2) сетевая архитектура и (3) стохастическая среда, которая запускает каждый эпизод с 30 случайными начальными блокировками. операции.

Отлично, поэтому предварительная обработка такая же, как и с DQN! Это очень удобно. Напоминаем, что это влечет за собой:

  • Мы должны использовать среду «Deterministic-v4» из тренажерного зала OpenAI, это те, которые автоматически пропускают 4 кадра, что кажется нормой в документах DQN.
  • Мы должны сделать состояние оттенками серого и изменить его размер до 64 на 64. Мы также должны убедиться, что значения пикселей находятся в диапазоне от 0 до 1, а не от 0 до 255.
  • Состояние должно состоять из предыдущих 4 кадров, которые мы видели.

Я выполнил предварительную обработку с помощью OpenCV, что дает нам:

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

С точки зрения эффекта, это трансформация, которую мы видим:

Становится:

Вот и все, теперь нам просто нужно сложить 4 из них, чтобы получить наше состояние.

Модель

Мы используем более крупную архитектуру DQN от Mnih et al. (2015), состоящий из 3 сверточных слоев с 32, 64 и 64 каналами, за которыми следует скрытый слой с 512 единицами. Сверточные слои используют фильтры 8 × 8, 4 × 4 и 3 × 3 с шагом 4, 2 и 1 соответственно. Все скрытые слои сопровождались нелинейностью выпрямителя (ReLU). Сеть содержит более 4 миллионов параметров.

Ничего такого, чего мы раньше не видели, давайте просто напишем об этом!

Хорошо, теперь мы можем перейти к интересным вещам:

Простое объяснение генетических алгоритмов

Генетический алгоритм (Holland, 1992; Eiben et al., 2003) развивает популяцию P из N индивидуумов (здесь векторы параметров нейронной сети θ, часто называемые генотипами). В каждом поколении каждый θi оценивается, получая оценку пригодности (также известную как награда) F (θi). Наш вариант GA выполняет отбор с усечением, при котором лучшие T особей становятся родителями следующего поколения. Для создания следующего поколения следующий процесс повторяется N-1 раз: родительский элемент выбирается равномерно случайным образом с заменой и изменяется путем применения аддитивного гауссовского шума к вектору параметров: θ '= θ + σε, где ε ∼ N (0 , I). Соответствующее значение σ было определено эмпирически для каждого эксперимента, как описано в таблице 3 дополнительной информации (SI). N-й человек является неизмененной копией лучшего человека из предыдущего поколения, метод, называемый элитарностью.

Мне это кажется довольно ясным, но я попытаюсь объяснить это заново: в основном вместо того, чтобы генерировать сеть случайным образом и изменять ее веса с помощью градиентного спуска, мы произвольно генерируем группу сетей, составляя «поколение», а затем оцениваем их. Конечно, некоторые из них будут работать немного лучше. Мы выбираем тот, который работает лучше всего, и оставляем его для следующего поколения. Затем среди нескольких лучших сетей мы создаем новое поколение, копируя их несколько раз и немного изменяя веса каждой копии. Поскольку каждый раз выбираются только лучшие сети, и поскольку мы постоянно меняем сети, кажется, что наша производительность должна продолжать расти, и это действительно часто происходит!

В нашем случае, поскольку мы фокусируемся на играх Atari, размер популяции составляет 5000, порог усечения равен 10, а параметр σ равен 0,005. Упомянутый ими параметр I просто означает, что он выбран из нормального распределения с единичной дисперсией (I - это единичная матрица).

Итак, когда мы остановимся на поколениях, спросите вы? Uber очень четко заявляет, что они останавливались после 1 миллиарда кадров, а не после фиксированного количества поколений, поэтому мы обязательно будем отслеживать количество кадров, которые мы видим, и останавливаться после того, как мы пройдем. 1000000000. Напоминаем, что существует разница между кадром и прямым проходом в нейронной сети, поскольку стандартно запускать 4 кадра для каждого прямого прохода (т. Е. Сеть выбирает действие только каждые 4 кадра). Если вы используете Deterministic-v4 в тренажерном зале OpenAI, этот пропуск кадров с 4 кадрами автоматически реализуется для вас, поэтому просто умножьте количество раз, которое вы вызвали .step, на 4 и остановитесь, когда это число достигнет миллиарда.

Модель сжатия

Одно интересное нововведение, которое Uber предлагает в этой статье, - это сжатие нейронных сетей.

Мы предлагаем новый метод компактного хранения больших векторов параметров, представляя каждый вектор параметров как начальное значение инициализации плюс список случайных начальных значений, которые производят серию мутаций, применяемых к θ. Это нововведение было необходимо для того, чтобы GA могли работать в масштабе глубоких нейронных сетей, поэтому мы называем его Deep GA. Преимущество этого метода также заключается в том, что он предлагает современный метод сжатия (раздел 5).

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

К счастью, реализовать это очень просто! Как упоминалось в статье, речь идет просто о запоминании случайных начальных чисел, используемых каждый раз, когда мы генерируем случайные числа, т.е. нам нужно помнить:

  • Семя, которое мы использовали для инициализации сети
  • Посевной материал, который мы использовали каждый раз, когда изменяли веса сети.

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

Способ установить случайное начальное число с помощью PyTorch - это просто torch.manual_seed, которому вы можете передать число. Чтобы убедиться, что случайные начальные числа всегда были разными, я передал число, сгенерированное Python random.randint, от 0 до 2 ^ 31 - 1 (почему вы спрашиваете это число? Ну, я не был уверен, какие числа допустимы как случайные начальные числа, но это гарантирует, что все, что я передаю, вписывается в 32-битное целое число со знаком, что, по моему мнению, почти наверняка приемлемо, в то время как большие числа могут быть неприемлемыми).

Обучение: используйте случайные SEEDS, а не случайные STATES.

PyTorch также имеет способ получить свое текущее случайное состояние с помощью torch.get_rng_state и установить его с помощью torch.set_rng_state. Сначала я использовал это, потому что существует намного больше возможных случайных состояний, чем случайных начальных чисел, поэтому мне это показалось более «случайным». Большая проблема заключается в том, что каждое случайное состояние уже имеет размер около 5 КБ (для сравнения, случайное начальное число составляет всего 4 байта, или в 1000 раз меньше), а это означало, что моя система быстро стала очень медленной из-за большого количества задействованных данных. Не делай той же ошибки, что и я!

Теперь, когда мы понимаем сжатие модели и ее эволюцию, мы можем завершить код нашей модели для обработки и того, и другого. Я решил создать отдельный класс под названием CompressedModel для представления сжатой модели, и между ними можно перемещаться вперед и назад. Это требует некоторого обновления модели, чтобы она поддерживала evolve функцию, которая принимает начальное значение и развивает его, и чтобы она могла принимать начальное значение при инициализации. Мы получаем:

Распределенные вычисления: очереди заданий и спотовые инстансы

Справедливое предупреждение: в этом разделе я предполагаю, что вы уже немного знаете об AWS или другом облачном провайдере, таком как Google Cloud или Microsoft Azure. Если вы этого не сделаете, возможно, стоит хотя бы взглянуть на какой-нибудь учебник по использованию AWS для глубокого обучения (например: https://towardsdatascience.com/how-to-set-up-a-deep-learning -environment-on-aws-with-keras-theano-b0f39e3d861c) , или, по крайней мере, просто знайте, что вам, вероятно, придется немного погуглить, потому что я пропущу некоторые важные базовые вещи.

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

Насколько масштабно мы говорим? Посмотрим, что об этом скажет Uber:

GA и ES работают быстрее, чем методы Q-обучения и градиента политики, когда доступны значительные распределенные вычисления (здесь 720 ядер ЦП на десятках машин).

720? Вот это да. Я склонен думать, что мое собственное оборудование достаточно современное и современное, но даже между моим настольным компьютером и ноутбуком у меня всего 8 ядер процессора ...

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

Очереди заданий

Дело в том, что у нас нет абсолютно никакого способа получить 720 ядер ЦП на одной машине. Более разумно мы можем получить 64 или немного больше. Это означает, что если мы хотим обрабатывать вещи так же, как Uber, нам понадобится несколько машин.

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

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

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

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

В частности, в .py файле должно быть следующее:

  • Код для модели нейронной сети
  • Код для сжатия / распаковки нейронной сети
  • Функция для оценки нейронной сети по сжатой модели и имени среды, которая возвращает как минимум оценку и количество используемых кадров.

Затем ваш главный код может добавить задание в очередь, используя job = rq.enqueue(...) с рассматриваемой функцией и аргументами, которые она должна принимать, и он может получить результат с помощью job.result, который будет None, если функция еще не вернулась. Вы должны убедиться, что ваш код устойчив к сбрасываемым заданиям: если вы не получаете результат по заданию в течение длительного времени, убедитесь, что он запрашивается повторно. Не забудьте передать сжатые нейронные сети в качестве аргументов enqueue, поскольку их нужно будет сериализовать и передать по сети, важно, чтобы они были небольшими. Собирая все это вместе, вот мой последний мастер-код.

Как только у вас есть все это, вы должны создать экземпляр AWS для своего мастера, достаточно небольшого, лично я использовал t2.medium. Вы можете установить на него любой Linux, который хотите, лично я использовал AMI Ubuntu 16.04 без глубокого обучения. Затем установите все зависимости для вашего агента, включая redis-server, rq, pytorch и т. Д. Как только это будет сделано, вы должны сделать следующее:

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

  • Узнайте частный IP-адрес своего главного компьютера (должен быть в веб-интерфейсе EC2) и создайте следующие два файла:
    Файл с именем redis.conf, содержащий bind <YOUR PRIVATE IP>
    A файл с именем settings.py, содержащий REDIS_HOST = '<YOUR PRIVATE IP'
  • Запустите redis server: nohup redis-server redis.conf&. Часть nohup гарантирует, что сервер будет продолжать работать, даже если ваше соединение прервется, а & в конце гарантирует, что он сразу же будет работать в фоновом режиме, чтобы вы могли делать другие вещи.
  • Запустите RQ worker с помощью rq worker -c settings.
  • Теперь проверьте, что это работает, запустив ваш мастер-код и убедившись, что задания действительно обрабатываются исполнителем.
  • Убейте воркера, вернитесь в интерфейс AWS, щелкните свой экземпляр и перейдите в «Действия -› Изображения - ›Создать образ». Это позволит вам создать AMI, содержащий все только что установленные вами пакеты, чтобы он легко запускать рабочих на других машинах!

Теперь мы можем перейти к созданию реальных рабочих.

Спотовые инстансы AWS

Как оказалось, получить доступ к большому количеству процессоров довольно просто: например, экземпляр c5.18xlarge на AWS содержит ошеломляющие 72 процессора, что составляет 1/10 от того, что нам нужно! Всего с 10 из этих экземпляров у нас будет точно такое же количество процессоров, что и Uber. Есть даже экземпляр с 96 процессорами (m5.24xlarge), но стоит отметить, что его процессоры немного медленнее, чем c5.18xlarge, и его стоимость в расчете на процессор немного выше из-за того, что он также имеет намного больше память, которая нам на самом деле не нужна (c5.18xlarge имеет 144 ГБ памяти, а m5.24xlarge - 384 ГБ, но, насколько я помню, я никогда не использовал более 5 ГБ на любой машине, которую я использовал ...).

Хорошо, так сколько это будет стоить? Что ж, один c5.18xlarge стоит 3,06 доллара в час в регионе Запад США (другие регионы должны быть примерно такими же), и Uber утверждает, что они смогли обучить свои сети примерно за час, используя эквивалент 10 c5. 18xlarge, поэтому обучение одной сети должно стоить нам 30,60 долларов при условии, что все пройдет гладко. На самом деле это не так уж и плохо! Но мы можем добиться большего, используя точечные экземпляры.

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

Кроме того, фактическая цена, которую вы платите, может быть менее предсказуемой, чем с обычными инстансами по требованию: спотовая цена инстансов колеблется по мере увеличения и уменьшения спроса, и вы всегда платите текущую спотовую цену, пока она ниже вашей максимальной ставки. . Итак, если инстанс торгуется по 1 доллару в час, а вы предложили 2 доллара, вы, вероятно, сначала заплатите 1 доллар, но со временем вы можете начать платить 1,50 доллара в час или 0,50 доллара и т. Д. К счастью, рынок экземпляров достаточно стабилен сейчас, когда это не вызывает большого беспокойства: если вы используете свои экземпляры только в течение нескольких часов, скорее всего, вы заплатите в пределах 5% или около того от цены, которую вы видели при первом создании спотового экземпляра.

В целом, описанная выше конструкция идеально подходит для спотовых инстансов, поскольку у вас может быть «главный» сервер, работающий на небольшом инстансе по требованию, и вы можете использовать любое количество спотовых инстансов с высокой ЦП. Если вы написали свой код правильно, он должен быть относительно невосприимчивым к гибели точечных экземпляров: он будет просто повторять выполнение заданий и не терять данные. На момент написания этой статьи спотовый инстанс c5.18xlarge стоит 1,08 доллара, что почти в 3 раза меньше, чем использование инстанса по требованию. Это означает, что оптимистично обучение одной игре должно стоить около 11 долларов. Обратите внимание, однако, что из-за того, что я тренировался в 3 играх (Frostbite, Breakout и Space Invaders), и из-за множества ошибок, которые я сделал на этом пути, весь этот эксперимент обошелся мне примерно в 115 долларов, поэтому постарайтесь быть особенно осторожными или убедитесь, что вы готовы потратить около 100 долларов, прежде чем начать это делать.

Итак, как создать спотовый экземпляр? Давай сделаем это сейчас! Напоминаем, что перед этим убедитесь, что вы достаточно уверены в своих настройках (т. Е. Создали AMI и подтвердили, что можете легко запустить систему на другом компьютере).

На панели EC2 щелкните «Спотовые запросы» справа, а затем кнопку «Запросить спотовые инстансы». Вы попадете на эту страницу:

Многие параметры можно оставить по умолчанию, но не забудьте сделать следующее:

  • Выберите общую емкость (10 даст вам то, что использует Uber, но я лично решил использовать 5 экземпляров и просто ждать 2 часа вместо 1. Вам нужно будет подключиться по ssh к каждой машине, чтобы меньшее число было более управляемым).
  • Выберите только что созданный AMI.
  • Удалите тип экземпляра c3.large из списка типов экземпляра и добавьте вместо него c5.18xlarge. Окно выбора типа экземпляра фактически покажет вам все доступные экземпляры и их цены.
    Возможно, другой тип экземпляра будет для вас более выгодным, когда вы посмотрите. Мой совет при выборе лучшего экземпляра - просто разделить текущую спотовую цену на количество процессоров, чтобы найти самый дешевый экземпляр на процессор. Однако обратите внимание, что экземпляры, имена которых начинаются с «c», оптимизированы для вычислений, что обычно означает, что их ЦП быстрее, чем в других экземплярах, даже если количество ядер равно.
  • Выберите ту же зону доступности, что и ваш мастер (возможно, не обязательно).
  • Выберите группу безопасности, которую вы ранее создали / отредактировали.

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

Как только это будет сделано, вы можете войти в различные спотовые экземпляры и запустить на них rq worker. Это сценарий, который я использовал для запуска 72 воркеров, каждый из которых записывал свой журнал в отдельный файл:

for i in {0..72}
do
nohup rq worker -c settings > $i&
done

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

Важное замечание по Intel MKL

Очень крутой недавней разработкой в ​​области численных вычислений является Intel MKL. Это чрезвычайно быстрая математическая библиотека, разработанная Intel, которая использует преимущества последних инструкций и многопоточность для очень быстрого выполнения численных вычислений. Он используется PyTorch для вычислений CPU и помогает уменьшить разницу между производительностью CPU и GPU для нейронных сетей (хотя графические процессоры по-прежнему быстрее).

К сожалению, MKL доставит нам проблемы! Это связано с тем, что он будет автоматически использовать несколько ядер ЦП для прямого прохода одной нейронной сети, но мы уже настроили все так, чтобы использовать одно ядро ​​ЦП на нейронную сеть. Я обнаружил, что это, похоже, замедляет работу для этой цели, поэтому я рекомендую отключить многопоточность в MKL. Это можно сделать, установив mkl-service в python (conda install mkl-service) и поместив следующие строки вверху рабочего файла перед импортом pytorch:

import mkl
mkl.set_num_threads(1)

Посмотрим на результат!

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

Итак, как мы оцениваем работу наших агентов? Что ж, Uber может кое-что сказать по этому поводу:

Достоверное сравнение наших результатов с результатами других алгоритмов чрезвычайно сложно, поскольку такие сравнения по своей сути являются яблоками и апельсинами по-разному. Одним из важных соображений является то, оцениваются ли агенты при случайных запусках (случайное количество бездействующих действий), что является режимом, в котором они обучаются, или запуски случайным образом выбираются из игры человека, что проверяет на обобщение (Nair et al., 2015 ). Поскольку у нас нет базы данных о запусках людей, с которых производится выборка, наши агенты оцениваются случайным образом. По возможности мы сравниваем наши результаты с результатами для других алгоритмов, для которых доступны такие результаты случайного запуска. Это верно для DQN и ES, но не верно для A3C, где нам пришлось включить результаты по запускам человека.

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

Тем не менее, похоже, что способ, которым мы должны оценивать нашего агента, заключается в простом использовании случайного числа бездействующих операций перед их запуском, именно так мы и проводили обучение в первую очередь. Количество невыполненных операций здесь не указано, но в других местах указано от 0 до 30, так что мы и сделаем это. Поскольку есть этот аспект случайности (не говоря уже о возможности генерации случайных чисел в отдельных играх Atari), я решил запустить игру 30 раз и отобразить лучший, худший и средний результат. Поехали!

Обморожение

Мы видим, что у нас есть агент, который очень хорошо работает для Frostbite: 4570 как лучший результат, что близко к 4801 баллу Uber, и, насколько мне известно, на данный момент это примерно современный уровень! Очень волнующе.

К сожалению, даже при случайных запусках агент совсем не устойчив! Средний балл составляет всего 170, а худший балл - 160! Мне непонятно, почему Uber не сообщил об этом: проблема в моей реализации? Что-то, что случилось с ними, но о чем они не сообщили? Проблема в их реализации? (если они, например, плохо реализовали случайный запуск, этого может быть не видно).

Похоже, это объясняет чрезвычайно большое расхождение между средним баллом и лучшим баллом по мере того, как я прохожу через поколения (график «средних» баллов Uber не использует это определение, поэтому я не могу сравнить их с тем, что я нашел: в определении Uber это медиана лучшего агента за несколько прогонов): дело не в том, что агенты очень чувствительны к небольшим изменениям своего веса, а в том, что они очень чувствительны к небольшим изменениям в их исходной точке!

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

  • Оценка каждой нейронной сети не в одном прогоне, а в нескольких, каждый из которых начинается с разного количества операций без операций.
  • Возвращение к эпсилону, жаждущему генетических алгоритмов: это кажется безумием, потому что похоже, что GA не «нуждается» в стратегии исследования, поэтому Uber не реализовал ее, но может оказаться, что исследование не просто помогает только агентам обучения с подкреплением. с нахождением новых состояний с высокой наградой, но также и со стремлением оказаться в непредвиденных ситуациях. Прямо сейчас кажется, что сети GA перестраиваются под очень специфическую последовательность действий из игры.
    Edit: после повторного рассмотрения этого, я считаю, что включение epsilon-greedy может быть полезным для улучшения производительности генетических алгоритмов для некоторых игр, возможно, это повредит Frostbite. Это потому, что в Frostbite легко убить себя одним действием (например, прыжком в неподходящий момент), поэтому случайный выбор действий может быть опасным. Напротив, в таких играх, как прорыв, обычно легко исправить эффекты случайных действий (просто отмените ход предыдущего действия). Это означает, что нам нужна лучшая стратегия исследования Frostbite.

Прорыв

О боже… GA делают ужасно на Breakout. Почему?

Я вижу несколько причин:

  • Вам нужно выполнить определенное действие, иначе прорыв не начнется (в частности, прорыв имеет 4 действия: ничего не делать, начать игру, двигаться влево и двигаться вправо).
    Это не проблема при следовании стратегия рандомизированного исследования, такая как epsilon-greedy, потому что действие будет выбрано в какой-то момент, но без какого-либо исследования вообще, это означает, что большая часть агентов просто ничего не будет делать вечно.
    Чтобы предотвратить это, Я даже постарался сократить максимальную продолжительность игры в первых нескольких поколениях и наказать агентов, которые все время ничего не делали. Однако очевидно, что это не сработало.
  • Очень немногие пиксели изменяются при разрыве, поэтому агенты продолжают выполнять одно и то же действие.
    В Frostbite почти 50% пикселей гарантированно изменятся, даже если агент ничего не делает. В Breakout изменится только крошечный шарик, ракетка и, возможно, один или два кирпича, и только если агент действительно что-то сделает. Для случайно инициализированной нейронной сети этого, вероятно, недостаточно, чтобы она решила выполнить другое действие.

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

Космические захватчики

Агент относительно неплохо справляется с космическими захватчиками, как и агент, которого мы обучили в DQNs Part 2, что далеко от современного уровня техники, но все же довольно прилично. Кроме того, он фактически работает одинаково независимо от начального случайного запрета операций.

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

Заключение

Я очень доволен результатами, опубликованными в статье Uber. Тот факт, что он так хорошо работает с Frostbite, довольно удивителен, и мне неясно, означает ли это, что традиционные алгоритмы RL особенно слабы или что GA на удивление сильна. Я думаю, однако, что существуют значительные проблемы с GA, на которые Uber не указывает, отчасти потому, что они не смогли оценить свои алгоритмы с использованием базы данных, запускаемой человеком (что, как я думаю, показало бы хрупкость системы). различные агенты), и было бы интересно попробовать их исправить. Я, со своей стороны, планирую попробовать включить в будущем эпсилон-жадное исследование, которое, как я думаю, поможет повысить надежность агентов, и, как указывает Uber, существует тонна литературы по генетическим алгоритмам, и они попробовали только самые основные возможные. Кто знает, куда нас заведут более продвинутые ГА.