В моем предыдущем посте Первый взгляд на обучение с подкреплением я попытался использовать обучение Deep Q для решения проблемы CartPole. В этом посте я продолжу изучать обучение Deep Q, но в контексте игр Atari.

В 2013 году в статье команды Deepmind Игра в Atari с глубоким обучением с подкреплением (Mnih et. Al.) Была исследована идея использования Deep Q обучения в играх Atari. Цель состояла в том, чтобы представить модель глубокого обучения, которая могла бы иметь дело с многомерными входными данными и достигать сверхчеловеческих тестов в различных играх Atari без каких-либо корректировок в базовой архитектуре модели. Прочитав статью, я захотел попробовать реализовать такую ​​модель и, надеюсь, смогу обучить агента играть в некоторые классические игры Atari, такие как Breakout или Pong. Я также буду воплощать идеи в следующей статье Контроль на уровне человека посредством глубокого обучения с подкреплением, опубликованной в британском журнале Nature. Кроме того, я также расскажу о модификации, которую мы можем добавить, под названием Double DQN.

Во-первых, мне нужно было выбрать среду для этой задачи. Когда я только начал этот проект, я хотел попробовать решить игру Теннис. Однако после пары часов обучения моей модели результаты оказались не лучше, чем случайная игра. Глядя на мои параметры обучения, я обучил своего агента только примерно 50 эпизодам, из которых для завершения каждого эпизода требовалось не более 200 кадров. Итого около 10 000 кадров. Глядя на статью Deepmind, они обучили своего агента более чем 10 миллионам кадров. Если на выполнение 10 000 кадров ушло около 1,5 часов, то на создание 10 миллионов кадров у меня ушло бы 2 месяца! В конце концов, я решил пойти с Понгом, одной из самых простых игр для изучения.

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

Для этого эксперимента я буду использовать библиотеку тренажерного зала OpenAI с готовыми средами. Обратите внимание, что в настоящее время единственной средой в пакете OpenAI atari-py является Тетрис, поэтому вам придется импортировать ПЗУ из другого места. Я буду включать файл в свой код в конце сообщения. Вот среда:

#initial environment
env = gym.make('PongNoFrameskip-v4')
#Atari preprocessing wrapper
env = gym.wrappers.AtariPreprocessing(env, noop_max=30, frame_skip=4, screen_size=84, terminal_on_life_loss=False, grayscale_obs=True, grayscale_newaxis=False, scale_obs=False)
#Frame stacking
env = gym.wrappers.FrameStack(env, 4)

В первой строке я инициализирую среду «PongNoFrameskip-v4». Формат, который принимает тренажерный зал, представляет собой объединенную строку [‘Game] [‘ NoFrameskip ’,‘ Deterministic ’, None] [‘ - v0 ’,‘ -v4 ’]. Вот краткое объяснение каждого термина:

  • «NoFrameskip»: каждый шаг среды - это один кадр.
  • «Детерминированный»: каждый шаг выполняет одно и то же действие для k кадров и возвращает k-й кадр. к = 4.
  • Нет: То же, что и «Детерминированный», но k выбирается из [2, 5].
  • «-V0»: вероятность повторения предыдущего действия на каждом шаге составляет 0,25.
  • «V4»: каждый шаг будет следовать за выполненным вами действием.

Во второй строке я применяю некоторую предварительную обработку, которая была подробно описана в статье Nature. Оболочка Atari следует рекомендациям Machado et al. (2018), «Пересмотр среды обучения аркад: протоколы оценки и открытые проблемы для общих агентов». Краткое объяснение каждого параметра:

  • NoopReset: получение начального состояния путем выполнения случайного количества бездействий (бездействия) при сбросе.
  • Пропуск кадров: по умолчанию 4
  • Максимальное объединение: принимает максимальное значение в пикселях из последних наблюдений, чтобы удалить эффект мерцания в некоторых играх Atari.
  • Сигнал завершения при потере жизни: по умолчанию отключен. Не рекомендуется Machado et al. (2018).
  • Измените размер изображения до квадратного: по умолчанию 84x84.
  • Наблюдение в оттенках серого: преобразование наблюдения в оттенки серого.
  • Масштабировать наблюдение: масштабирование наблюдения до [0, 1]

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

В третьей строке каждые k кадров складываются в одно наблюдение. Это даст модели представление о том, как разыгрывается действие, вместо того, чтобы просто вводить неподвижный кадр. Обратите внимание, что это не означает, что мы группируем каждые k кадров вместе, наблюдения суммируются для каждого кадра, поэтому они будут перекрываться. Наше измерение должно быть (4, 84, 84), где входными данными являются изображения размером 84x84, сложенные с последними 4 кадрами. В отношении предварительно обработанных наблюдений следует отметить то, что в статье Nature было решено сохранить игровой счет, а не вырезать его.

Внедрение Deep Q Learning

Мы будем использовать эпсилон-жадный алгоритм для выбора лучшего действия, где есть эпсилон-шанс выборки случайного действия из пространства действий. Вместо использования эпсилон-распада мы будем использовать линейный отжиг, чтобы уменьшить эпсилон с 1 до 0,1 за 1 миллион кадров в соответствии со спецификацией Deepmind.

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

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

Вход в нейронную сеть состоит из изображения 84 × 84 × 4, созданного картой предварительной обработки w. Первый скрытый слой сворачивает 32 фильтра 8 × 8 с шагом 4 с входным изображением и применяет нелинейность выпрямителя. Второй скрытый слой сворачивает 64 фильтра 4 × 4 с шагом 2, за которым снова следует нелинейность выпрямителя. За ним следует третий сверточный слой, который сворачивает 64 фильтра 3 × 3 с шагом 1, за которым следует выпрямитель. Последний скрытый слой полностью подключен и состоит из 512 выпрямительных блоков. Выходной слой - это полностью связанный линейный слой с одним выходом для каждого допустимого действия.

Моя реализация в Keras показана ниже:

model = Sequential(
    [
        Lambda(lambda tensor: tf.transpose(tensor, [0, 2, 3, 1]), output_shape=(84, 84, 4), input_shape=(4, 84, 84)),
        Conv2D(32, kernel_size=(8, 8), strides=4, activation="relu", input_shape=(4, 84, 84)),
        Conv2D(64, kernel_size=(4, 4), strides=2, activation="relu"),
        Flatten(),
        Dense(512, activation="relu"),
        Dense(env.action_space.n, activation="linear"),
    ]
)
rms = tf.keras.optimizers.RMSprop(learning_rate=0.00025, rho=0.95, epsilon=0.01)
model.compile(loss="mse", optimizer=rms)

В свою реализацию я включил слой Lambda, который преобразует входные данные формата NCWH (Batch, Channel, Width, Height) в NWHC. Это связано с тем, что, поскольку я обучаю свою модель с использованием ЦП (не удалось настроить графический процессор), формат NCWH не поддерживается. Если вы тренируетесь на GPU, вы можете игнорировать слой Lambda и использовать (4, 84, 84) в качестве входной формы для первого сверточного слоя. Спецификации оптимизаторов подробно описаны в статье Nature. Для получения дополнительной информации о RMSprop я нашел эту статью очень полезной.

Мы также будем использовать воспроизведение опыта и отслеживать прошлые переходы в массиве памяти. Параметры, описанные в статье, хранят в памяти до 1 миллиона переходов. Память будет предварительно заполнена 50000 переходами из случайного воспроизведения. Во время обновления мини-пакета будет равномерно выбрана партия из памяти, которая будет использоваться для обучения. В документе также упоминается более сложная стратегия, которая делает упор на переходах, на которых модель обучается больше всего, подобно поиску по приоритетам. Еще одна модификация подробно описана в статье:

Модификация предусматривает клонирование сети при каждом обновлении C и использует клонированную сеть в качестве целевой сети для целевого значения Q и обучает онлайн-сеть, а не использует одну и ту же сеть для обеих задач. Это сделано для снижения нестабильности в сети, поскольку каждый раз, когда мы обновляем сеть, цель также будет меняться. Использование целевой сети гарантирует, что цель не изменится для обновлений C. Как вы можете видеть ниже, дифференцированная функция потерь использует параметры модели θᵢ для текущего значения Q и параметры клонированной модели θᵢ⁻ для целевого значения Q.

Краткое описание алгоритма показано ниже:

Двойной DQN

Еще одна модификация, которую мы можем внести в этот алгоритм, называется Double Deep Q Learning. В статье Глубокое обучение с подкреплением с двойным Q-обучением (Хасселт и др., 2015) рассматривается проблема переоценки значений q. Хотя чрезмерно оптимистичные ценности не обязательно плохо, они могут негативно повлиять на итоговую политику, если значения действий не будут равномерно искажены.

Предлагаемое в статье изменение касается целевого значения во время обучения.

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

Приведенное выше уравнение - это то, как мы корректируем целевое уравнение DQN. Вместо использования оператора max для выбора наилучшего действия и одновременного получения значения действия, мы используем argmax для выбора действия, используя сеть Q с весами θₜ, а затем оцениваем это действие, используя сеть Q с другим набором весов. θ′ₜ. В статье объединена концепция обучения Double Q с DQN для создания простой модификации Double DQN, в которой мы можем использовать целевую сеть в качестве весов θ′ₜ и онлайн-сеть как веса θₜ.

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

Когда у нас есть несмещенная ошибка такая, что сумма разностей значений действий между истинной оптимальной сетью V и произвольной сетью Qₜ со среднеквадратичной ошибкой C ›0, мы можем получить точную нижнюю границу: √ (C / m-1), где m - количество действий. При двойном Q-обучении нижняя граница ошибки равна 0.

#action selection with online network
best_future_action = np.argmax(model.predict(np.expand_dims(new_state, axis=0)))
#action evaluation with target network
target = reward + discount_factor * target_model.predict(np.expand_dims(new_state, axis=0))[0][best_future_action]

Мои результаты

После реализации алгоритма я был готов обучать своего агента. Однако я сильно недооценил время тренировки. В статье Nature они обучили агента 50 миллионам кадров, что эквивалентно 38 дням игрового времени. Что касается меня, я решил использовать более простую игру Atari, для которой требуется более короткое время для схождения, что составляет около 750 000 кадров, согласно некоторым примерам, которые я видел. Даже с учетом сокращенного времени обучения обучение на моем локальном процессоре заняло бы дни, и у меня возникли проблемы с памятью. Я решил обучить свою модель 200000 кадров, при этом другие гиперпараметры масштабируются до количества обученных кадров.

Во-первых, давайте посмотрим, как мы поступили со случайной политикой. За 150 прохождений случайная политика имела средний балл -20,273, где балл рассчитывается как балл ЦП - балл агента, и игра заканчивается с 21 баллом. Как и ожидалось, случайная политика не очень хороша по сравнению с человеческим тестом -3.

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

Так почему моя модель не тренировалась? Пара гипотез, почему это происходило. Во-первых, я уменьшил все гиперпараметры и перешел с 10 миллионов обученных кадров на 200 000 кадров. Хотя все еще масштабируется, существует серьезная проблема. Количество кадров, необходимых для решения проблемы, осталось прежним. Поскольку я уменьшаю эпсилон в течение первых 20 000 кадров, изученная политика все еще так же хороша, как и случайная политика.

Во-вторых, это могло быть связано с редкостью вознаграждений в Pong. В среде CartPole мы вознаграждали агента за каждый временной шаг, на котором полюс находился в вертикальном положении. Такая обратная связь позволяла агенту быстро различать хорошие и плохие действия. Затем мы изменили вознаграждение, чтобы оно масштабировалось с учетом угла вехи для еще более точной обратной связи. Однако это другой случай для DQN. DQN был впервые предложен как общее решение для решения всех игровых сред Atari с учетом входного изображения. Таким образом, мы не можем назначать более точные награды, а система вознаграждения, используемая для Pong, - это либо -1, либо 1 за набранный балл. Это означает, что существует гораздо меньше наград, на которых агент учится в каждом прохождении, и есть множество кадров между каждой наградой, что усложняет агенту понимание будущей полезности действия.

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

Уф. По крайней мере, алгоритм действительно работает, и агент будет учиться в более простой среде. Сравнивая это с базовой реализацией DQN, подробно описанной в документе 2013 года, мы получили аналогичные результаты со средним баллом 476 за 10 эпизодов.

В целом, это был забавный опыт, и мне пришлось более подробно рассказать об алгоритме DQN, добавив целевые сети и наивную реализацию Double Q Learning. Есть еще улучшения, которые можно сделать, такие как приоритетное сканирование и дуэльные сети, но это будет изучено в будущем. Хотя DQN была первой моделью, использованной для решения игр Atari с вводом изображений, факт остается фактом: DQN имеет длительное время обучения и медленную скорость сходимости. В будущем я буду исследовать другие виды алгоритмов, такие как Deep Deterministic Policy Gradient (DDPG) для непрерывных действий, Proximal Policy Optimization, A3C и другие!

Мой код: https://github.com/chengxi600/RLStuff/blob/master/Q%20Learning/Atari_DQN.ipynb

Источники: