Как я учил змейку играть в себя с помощью Q-Network

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


Обучение с подкреплением


В машинном обучении RL(Reinforcement Learning) достаточно сильно отличается от других направлений. Отличие состоит в том, что классический ML алгоритм обучается уже на готовых данных, в то время как RL, так сказать, сам создает себе эти данные. Идея RL состоит в том, что помимо самого алгоритма, который называют агентом, существует среда(environment), в которую этот агент и помещается. На каждом этапе агент должен совершать какое-то действие(action), а среда отвечает на это наградой(reward) и своим состоянием(state), на основе которого агент и совершает действие.


DQN


Здесь должно быть объяснение того, как алгоритм работает, но я оставлю ссылку на то, где это объясняют умные люди.


Реализация змейки


После того, как мы разобрались c rl, надо создать среду, в которую будем помещать агента. К счастью, изобретать велосипед не требуется, тк такая компания как open-ai уже написала библиотеку gym, с помощью которой можно писать свои энвайронменты. В библиотеке их уже имеется в большом количестве. От простых atari игр до сложных 3d моделей. Но среди всего этого нет змейки. Поэтому приступим к ее созданию.


Я не буду описывать все моменты создания энвайронмента в gym, а покажу только основной класс, в котором требуется реализовать несколько функций.


import gym

class Env(gym.Env):
     def __init__(self):
          pass

     def step(self, action):
          """Функции подается выбранное агентом действие. Возвращает состояние после действия, награду и информацию об окончании эпизода"""

     def reset(self):
          """Сбрасывает среду к стартовому состоянию и возвращает стартовое состояние"""

     def render(self, mode='human'):
          """Рендерит среду"""

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


Состояние


В видео человек подавал змейке расстояние до стены, змейки и яблока в 8 направлениях. Те 24 числа. Я же решил уменьшить количество данных, но немного усложнить их. Во первых, я совмещу расстояние до стен с расстоянием до змейки. Проще говоря будем говорить ей расстояние до ближайшего объекта, который может убить при столкновении. Во вторых, направлений будет всего 3 и они будут зависеть от направления движения змейки. Например при старте змейка смотрит вверх, значит мы сообщим ей расстояние до верхней, левой и правой стенки. Но когда голова змейки повернется направо, то мы уже будем сообщать расстояние до правой, верхней и нижней стенки. Для пущей простоты приведу картинку.



С яблоком я тоже решил поиграться. Информацию о нем мы будем представлять в виде (x,y) координаты в системе координат, которая берет начало у головы змейки. Система координат также будет менять свою ориентацию за головой змейки. После картинки, думаю, точно должно стать понятно.



Награда


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


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


  • При каждом шаге награда равна -0.25.
  • При смерти -10.
  • При смерти до 15 шагов -100.
  • При съедании яблока sqrt(количество съеденных яблок) * 3.5.

А так же приведу примеры к чему приводит плохая система наград.


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

Ну и пример того, чему змейка научилась за 2000 эпизодов


Итог


Основным интересом при написании змейки было увидеть, как змейка обучится зная так мало о своей среде. И обучилась она неплохо, тк средний показатель съеденных яблок достиг 23, что, мне кажется, очень не дурно. Поэтому эксперимент можно считать удачным.


Исходный код

Средняя зарплата в IT

113 000 ₽/мес.
Средняя зарплата по всем IT-специализациям на основании 10 036 анкет, за 2-ое пол. 2020 года Узнать свою зарплату
Реклама
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее

Комментарии 12

    0
    При съедании яблока sqrt(количество съеденных яблок) * 3.5.
    а почему не простое линейное «количество съеденных яблок»?
      +1
      Изначально была линейная функция, но с нелинейной обучение идет чуть-чуть быстрее
      0
      А как называется обучение (и есть ли оно), когда даны не все характеристики окружения и агент сам дополняет описание?
        0

        Если я правильно понял вопрос, то это тоже будет обучением с подкреплением.

          0

          Может какие-то марковские процессы имеете ввиду?

            0
            Посмотрю, спасибо. Если бы я знал, как оно называется, я б задал более конкретный вопрос :)
            0

            https://en.wikipedia.org/wiki/Partially_observable_Markov_decision_process Вероятно вы имеете ввиду это

              0
              Сейчас читаю, но не могу понять: states должны быть известны заранее, или как-то могут вводиться новые по ходу выполнения модели?
                0
                states — заранее, а неизвестность вводится при помощи beliefs (читайте вторую половину статьи в википедии)
                  0
                  Читал, не понял (показалось, что belief размазывается по существующим состояниям и не может породить новое), потыкаю примеры тут, например.

                  Под новым состоянием я понимаю следующее:
                  пусть у нас есть робот, оснащённый камерой и манипулятором с динамометром, и задача классифицировать объекты. Часть объектов совпадает по внешнему виду, но различается по весу. У робота в базе есть описание объектов в виде визуальные дескрипторы+вес, но не для всех объектов. Нужно, чтобы робот мог добавить новый объект с описанием, если он не похож ни на какие предыдущие.
                    0
                    Нужно, чтобы робот мог добавить новый объект с описанием, если он не похож ни на какие предыдущие.

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

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое