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

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

Мне потребовалось время, чтобы найти достойный первый вызов для DQN. В большинстве учебных пособий, которые я видел, реализованы сверточные сети DQN +, и я пытался создать агент, который превосходит игры Atari или Doom. Это казалось отвлечением: я не видел смысла тратить время на проектирование сетей обработки изображений для решения проблемы с подкреплением. Вот почему я выбрал крестики-нолики: они довольно просты, поэтому я могу тренировать их на своем MacBook, обработка изображений не требуется, так как я могу кодировать игру, и все же для победы требуется разработка стратегии.

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

1. Разбейте его на самые простые возможные задачи.

Как бы очевидно это ни звучало, мы все время от времени склонны забывать об этом. Если вы читали мое Введение в DQN, вы, возможно, помните, что я давал в качестве упражнения Hello World очень-очень простую игру: агент, который должен заполнить пустые ячейки (а если вы этого не сделали - не беспокойтесь, мы вернемся к этому очень скоро). Я придумал это упражнение не на пустом месте.

Когда я решил реализовать Tic-Tac-Toe, я сел и написал почти весь необходимый код сразу: класс Game, класс Player, класс Experience Replay, класс Q Network, реализация Double Deep Q Network - и запустил игру. Угадай, что? Он ничему не научился. Даже то, что является действительным или недействительным ходом, что является абсолютной основой любого действия.

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

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

2. Простые задачи просты только вам

Как уже упоминалось, простая задача, которую я дал агенту, заключалась в том, чтобы заполнить все свободные места на доске с 4 ячейками. Пустая ячейка обозначается «0», а заполненная - «1». Сколько игр вам нужно сыграть, чтобы понять логику? Наверное, не так уж и много. Но вы человек, а компьютер - нет.

Я думал, что нескольких сотен игр должно хватить, но оказалось, что моя реализация требовала около 2500 игр, чтобы справиться с этим. Оказалось, что 100–200 игр было настолько мало, что стоимость практически не изменилась. Что еще больше запутало, стоимость была действительно низкой - но, как я понял позже, это было из-за холодного старта сети и недостаточного количества тренировок (см. Мою реализацию). По сути, я не проводил достаточно тренингов для сети, чтобы чему-то научиться, потому что недооценил, насколько сложно агенту это изучить.

Итог - то, что нам кажется простым, поскольку людям может быть совсем непросто для компьютеров (и наоборот - сколько времени потребуется вам, чтобы вычислить 147x819?). Тренируйте модель дольше.

3. Как (не) определять следующее состояние

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

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

Переходы в нечетные состояния выполняются игроком X, а в четные состояния - игроком O. Именно так я добавил записи в буфер памяти модели: если s = 0 и игрок X выбрал левую -средняя ячейка, затем s '= 1. Затем игрок O получил состояние s = 1 и перешел на s = 2. Это были две отдельные записи, которые я добавил в память Experience Replay модели.

Но это означает, что игрок никогда не может проиграть, когда настала его очередь играть. Давайте посмотрим на s = 3: игрок O выбрал верхнюю среднюю ячейку и перешел к s = 4. Он еще не проиграл. Теперь ход игрока X, он делает очевидный выбор, переходит к s = 5 и побеждает. Только теперь игрок O знает, что он проиграл, и поэтому, чтобы в следующий раз лучше рассудить, отрицательное вознаграждение, которое игрок O получает при s = 5, должно распространить два состояния назад до значений Q s = 3. Теоретически это не должно быть проблемой из-за рекурсивности уравнения Беллмана и многократного обучения сети. Теоретически

На практике это не работает - я считаю, по двум причинам. Первый и более интуитивный: модель не видела риска при переходе от s = 3 к s = 4, потому что не знала не он выбирает, как перейти с s = 4. Насколько известно модели, он может просто выбрать другое действие в s = 4, не допуская проигрыша.

Как только я понял это, исправить стало легко - все, что мне нужно было сделать, это определить следующее состояние как следующее представление доски, которое увидит игрок - так что для игрока O, если s = 3 и действие s = 3 em> верхняя средняя ячейка, затем s '= 5, а не 4 - и теперь модель может узнать, что это действие приведет к проигрышу игры. .

А какова вторая причина? хорошо -

4. Помните, что сеть - это только приблизительное значение.

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

Это может быть хорошим моментом вернуться к моему решению примера« заполнить доску », который я описал выше. Если вы пройдетесь по алгоритмам Q-Table и Q-Network, которые я реализовал там, вы увидите, что я построил прогнозы для всех значений Q при их тестировании. Значения Q в таблице имеют смысл (несмотря на то, что иногда они немного отклоняются из-за того, что дороги менее заняты во время обучения), но это не относится к прогнозируемым значениям Q-Network. Они не имеют абсолютно ничего общего с вычисленными с помощью табличного алгоритма.

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

Я полагаю, что ключевой вывод здесь заключается в том, что, хотя их называют Deep Q-Network, значения Q - это не то, чему они на самом деле учатся. Непонятно, но это жизнь.

Заключительные слова

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