Обучение с подкреплением (RL) сейчас является горячей темой благодаря беспилотным автомобилям и (супер) человеческой производительности в таких играх, как Go или Dota. Однако подход к предмету может быть пугающим. Я давно хотел попробовать, но не знал, с чего начать. После пары начальных безрезультатных попыток я начал получать положительные результаты, получал удовольствие и многому научился в процессе. С правильными инструментами и ресурсами это на самом деле намного проще, чем кажется. Так что приходите, если вы заинтересованы в применении RL в захватывающей среде, такой как 3D-шутер!

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

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

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

  • OpenAI Spinning Up. Хорошо написанное руководство, которое отлично объясняет ключевые уравнения RL. Кроме того, вы найдете более подробную информацию об основных идеях, лежащих в основе популярных алгоритмов, таких как DDPG, TRPO, PPO и т. Д.
  • У Томаса Симонини есть отличная серия курсов по RL, в которых он подробно останавливается на фактической реализации алгоритмов RL. Он использует множество различных сред, в том числе ту, которую мы будем использовать сегодня.
  • Документация по стабильным базовым версиям и примеры - также отличный способ изучить RL. У них есть блокнот, который может служить хорошей отправной точкой для дальнейших экспериментов. Углубление в код может быть очень полезным, а хорошая структура и комментарии позволяют сделать это очень легко.
  • Курс Дэвида Сильвера по RL, это то место, куда вы хотите пойти для формального объяснения RL.
  • И последнее, но не менее важное: я нашел FastAI’s course одним из лучших ресурсов о глубоком обучении в целом. Каждая концепция проиллюстрирована вживую с кодом и демонстрациями, которые позволяют применить очень практичный подход к машинному обучению. Я люблю это.

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

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

  • Stable-baselines 3, PyTorch переписывает stable-baselines2. Это библиотека, предлагающая самую современную реализацию различных популярных алгоритмов RL.
  • VizDoom, версия Doom для RL, которая позволяет нам получать доступ к состоянию игры и выполнять действия программно.

Stable-baselines - это ответвление репозитория OpenAI’s baselines. У них есть исчерпывающая средняя статья, демонстрирующая многочисленные функции и причины, которые заставили их форк исходного кода. Это отличная библиотека, код хорошо документирован и прост в использовании. Большая часть стандартного кода абстрагирована, что означает, что его очень легко начать и запустить с помощью нескольких строк кода.

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

pip install stable-baselines3==0.10.0 vizdoom==1.1.8 torch==1.7.1 gym==0.17.3 tensorboard==2.4.0

Обратите внимание, что если вы используете компьютер с Windows, процесс установки VizDoom требует еще нескольких шагов, которые объясняются здесь. По сути, вам необходимо загрузить двоичные файлы, соответствующие используемой вами версии Python, разархивировать содержимое и вручную переместить папку vizdoom в пакеты вашего сайта.

Знакомство с VizDoom

Файловая структура

VizDoom имеет очень полное руководство на своей главной странице, а также красивый раздел примеров в их GitHub, чтобы помочь вам начать работу. Тем не менее, мы рассмотрим основные аспекты, которые вам нужно знать, чтобы начать работу как можно быстрее. Мы рассмотрим больше того, что может предложить VizDoom, в другой статье, где мы поработаем со сценариями Doom, чтобы повлиять на процесс обучения.

VizDoom работает со сценариями. Каждый сценарий описывает разные ситуации со своими правилами и наградами. Сценарий обычно состоит из двух файлов:

  1. Файл .cfg, который параметризует среду обучения, определяя, например, какие кнопки доступны нашему агенту или какой формат ввода на экране.
  2. Файл WAD, представляющий собой двоичный файл, содержащий карты и другие ресурсы, относящиеся к Doom.

Вот пример очень простого файла конфигурации:

# Resources selection
doom_scenario_path = basic.wad
doom_map = map01
# Episode finishes after 300 actions
episode_timeout = 300
# Rewards
living_reward = -1 
# Rendering options
screen_format = RGB24
screen_resolution = RES_320X240

# Available actions
available_buttons = 
   { 
      MOVE_LEFT 
      MOVE_RIGHT 
      ATTACK 
   }

mode = PLAYER
doom_skill = 5

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

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

Рабочий пример

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

Если вы выполните этот код, у вас должно получиться что-то вроде этого. Обратите внимание, что в вашей версии будут разные персонажи и текстуры, так как вы будете запускать игру с FreeDoom, который представляет собой бесплатный набор ресурсов для ZDoom. Это можно изменить, получив официальный файл doom IWAD.

Решение нашего первого сценария

Задача

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

Описание сценария

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

  • +101 к стрельбе по врагу.
  • -1 за временной шаг.
  • -5 за пропущенные выстрелы

И эпизод заканчивается через 300 временных шагов. Конфигурационный файл для этого сценария - это тот, который мы видели в примере. Итак, будут доступны три кнопки: двигаться влево, двигаться вправо и атаковать. Вы могли заметить, что в конфигурационном файле мы указали только вознаграждение за «временной шаг». Это потому, что другие награды определены в файле WAD. Файлы конфигурации позволяют определять только награды за жизнь и смерть.

Модель

Мы будем использовать проксимальную оптимизацию политики с политикой критики актеров. Если эти термины вам не понятны, ознакомьтесь с объяснением PPO в Spinning Up и интуитивной иллюстрацией Руди Гилмана методов актерской критики. Обучить агента PPO со стабильными базовыми показателями до смешного просто:

from stable_baselines3 import ppo
from stable_baselines3.common import policies

agent = ppo.PPO(policies.ActorCriticCnnPolicy, env)
agent.learn(total_timesteps=25000)

Единственная оговорка: стабильные базовые показатели предполагают наличие тренажерного зала. Тренажерный зал - это очень простой интерфейс для стандартизации взаимодействия между агентами RL и различными средами. Поэтому нам нужно будет написать адаптер, чтобы он работал с VizDoom.

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

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

Использование тренажерного зала:

from gym import spaces
observation_space = spaces.Box(
    low=0, 
    high=255, 
    shape=(h, w, c), 
    dtype=np.uint8)
action_space = spaces.Discrete(4)

Окружение

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

  • step(action: int) -> Tuple[np.ndarray, float, bool, Dict]
    Чей ввод представляет действие, выбранное нашим агентом. Он возвращает кортеж из 4 объектов: новое состояние, награду, полученную за выполнение действия, логический флаг, указывающий, закончился ли эпизод, и, наконец, dict, содержащий различную информацию.
  • reset() -> Frame:
    Самообъяснительный, сбрасывает внутренние переменные и возвращает новое начальное наблюдение.

Эквивалент функции step в VizDoom - make_action. Эта функция принимает необязательный второй аргумент, который представляет собой количество игровых тиков, для которых будет применено действие, и возвращает общую награду, полученную за выполнение действия (Doom запускается со скоростью 35 тиков в секунду). Как мы увидим позже, изменение значения тика на самом деле является одним из наиболее эффективных способов ускорить процесс обучения.

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

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

Наконец, мы можем передать наш экземпляр игры doom адаптеру и обернуть все это в VecEnv, объект, с которым stable-baselines умеет обращаться. Использование VecEnv немного необычно, поэтому, если вы заинтригованы, посмотрите блокнот, где я объясню, почему это хорошо.

Запуск учебного процесса

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

И вот так у нас есть агент RL, который учится играть в дум по простому сценарию. Stable-baselines также ведет журнал за вас, если вы предоставите модели путь с помощью параметра tensorboard_log. Просто запустите tensorboard, указав правильный путь, и вы сможете отслеживать прогресс, достигнутый вашим агентом RL в режиме реального времени!

tensorboard --logdir=logs/tensorboard

Это запустит сеанс Tensorboard, доступный по умолчанию на порте 6006. Посетите 127.0.0.1:6006 в своем веб-браузере. Через пару тысяч шагов вы должны увидеть, что ваш агент начинает действительно хорошо работать.

Идти дальше

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

Заглянув в код, вы заметите, что каждое действие, предпринимаемое нашей моделью, повторяется четыре раза. Это фиксируется параметром frame_skip. Причина в том, что повторение действий в течение нескольких кадров положительно влияет на обучение. Этот трюк довольно популярен и использовался, например, для игры в Atari Breakout с использованием DQN. В частности, авторы VizDoom также используют его в своей бумаге.

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

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

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