Как я учил змейку играть в себя с помощью 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, что, мне кажется, очень не дурно. Поэтому эксперимент можно считать удачным.


Исходный код

Поддержать автора
Поделиться публикацией
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
                      Спасибо, аж стыдно за себя :) Тут лучше использовать иерархическую кластеризацию с дробление кластера, или, навскидку. какой-то другой метод посоветуете?

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

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