Обучение с подкреплением Chrome Dino Dino: наивысший балл превышает 4000

искусственный интеллект Python Chrome игра

Обучение с подкреплением — очень популярное направление исследований в современной области искусственного интеллекта, и прогресс в игровых агентах особенно поразителен. Рави Мунде, магистрант Северо-восточного университета, недавно опубликовал статью, в которой рассказывается о процессе создания обучающего агента с подкреплением для «Dino Run». "Little Dinosaur Run" - это скрытая игра в браузере Chrome. Когда ваш браузер отключен от сети, этот маленький динозавр появится на вашем экране. В это время просто нажмите клавишу со стрелкой ↑. Запустите игру.

В статье DeepMind 2013 года «Игра в Atari с глубоким обучением с подкреплением» была представлена ​​новая модель глубокого обучения для обучения с подкреплением и продемонстрирована ее способность осваивать Atari, используя в качестве входных данных только необработанные пиксели. 2600 Способность управлять различными стратегиями управления для компьютерных игр. В этом уроке я реализую эту статью с помощью Keras. Сначала я расскажу об основах обучения с подкреплением, а затем углублюсь в код для практического понимания.

ИИ играет в "Little Dinosaur Run"

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

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

шаг:

  • Создайте двунаправленный интерфейс между браузером (JavaScript) и моделью (Python).

  • Получение и предварительная обработка изображений

  • Обучите модель

  • оценивать

Исходный код: https://github.com/Paperspace/DinoRunTutorial.git

Начинать

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

git clone https://github.com/Paperspace/DinoRunTutorial.git

Затем работайте на ноутбуке Jupyter.

Reinforcement Learning Dino Run.ipynb

Убедитесь, что вы сначала запустили init_cache() для инициализации структур файловой системы.

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

ребенок учится ходить

Для многих это может быть новым словарным запасом, но каждый из нас научился ходить, используя концепции обучения с подкреплением (RL), и наш мозг до сих пор работает таким образом. Система вознаграждения является основой любого алгоритма обучения с подкреплением. Если мы вернемся к аналогии с ходьбой ребенка, то положительной наградой могут быть аплодисменты родителей или получение конфеты, а отрицательной наградой — отсутствие конфеты. Затем ребенок сначала учится стоять, прежде чем начать ходить. В случае с искусственным интеллектом основная цель агента (в нашем случае маленького динозавра Дино) состоит в том, чтобы максимизировать конкретное численное вознаграждение, выполняя определенную последовательность действий в окружающей среде. Самая большая проблема в обучении с подкреплением заключается в том, что нет наблюдения (помеченных данных), чтобы направлять агента. Он должен исследовать и учиться самостоятельно. Агент сначала выполняет случайные действия, затем наблюдает за вознаграждением, производимым каждым действием, а затем учится предсказывать наилучшее возможное действие при столкновении с аналогичными состояниями окружающей среды.

Самая простая и чистая структура обучения с подкреплением

Q-обучение

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

Пример таблицы значений добротности

Мы используем глубокую нейронную сеть для решения этой проблемы с помощью метода регрессии, а затем выбираем действие с самым высоким предсказанным значением Q. Чтобы узнать больше о Q-обучении, ознакомьтесь с отличной статьей Тамбета Матиисена: https://ai.intel.com/demystifying-deep-reinforcement-learning/. Вы также можете увидеть мою предыдущую статью обо всех гиперпараметрах Q-обучения: https://medium.com/acing-ai/how-i-build-an-ai-to-play-dino-run-e37f37bdf153

настраивать

Сначала настройте среду, необходимую для тренировочного процесса.

1. Выберите виртуальную машину (ВМ): нам нужна полноценная среда рабочего стола, где мы можем делать снимки экрана и использовать их для обучения. Я выбрал зеркало Ubuntu Paperspace ML-in-a-box (MLIAB). Преимущество MLIAB в том, что он поставляется с предустановленной Anaconda и многими другими библиотеками машинного обучения.

ML-in-a-box (MLIAB)

2. Настройте и установите Keras и используйте GPU

Нам нужно установить версию Keras и TensorFlow для графического процессора. Виртуальная машина Paperspace предустановлена ​​с ними, но если нет, вы можете сделать следующее для установки:

pip install keraspip install tensorflow

Также убедитесь, что GPU распознан. Выполните следующий код Python, и вы должны увидеть доступные устройства GPU:

from keras import backend as KK.tensorflow_backend._get_available_gpus()

3. Установите зависимости

  • Selenium: pip установить селен

  • OpenCV: pip установить opencv-python

  • Загрузите Chromedriver: http://chromedriver.chromium.org

игровая структура

Вы можете начать игру, указав в браузере chrome://dino или просто отключив сетевой разъем. Другой способ — извлечь игру из библиотеки с открытым исходным кодом Chromium — если мы хотим изменить код игры.

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

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

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

Selenium и OpenCV обеспечивают наилучшую производительность для захвата экрана и предварительной обработки изображений соответственно, достигая частоты кадров 6-7 кадров в секунду.

Нам нужно всего 4 FPS, так что этого достаточно.

игровой модуль

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

class Game:    def __init__(self):        self._driver = webdriver.Chrome(executable_path = chrome_driver_path)        self._driver.set_window_position(x=-10,y=0)        self._driver.get(game_url)    def restart(self):        self._driver.execute_script("Runner.instance_.restart()")    def press_up(self):        self._driver.find_element_by_tag_name("body").send_keys(Keys.ARROW_UP)    def get_score(self):        score_array = self._driver.execute_script("return Runner.instance_.distanceMeter.digits")        score = ''.join(score_array).        return int(score)

модуль агента

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

class DinoAgent:    def __init__(self,game): #takes game as input for taking actions        self._game = game;         self.jump(); #to start the game, we need to jump once    def is_crashed(self):        return self._game.get_crashed()    def jump(self):        self._game.press_up()

модуль состояния игры

Чтобы отправить действие модулю и получить результирующее состояние переходов среды, вызванных этим действием, мы используем модуль game-state. Он упрощает этот процесс, получая и выполняя действия, определяя награды и возвращая наборы опыта.

class Game_sate:    def __init__(self,agent,game):        self._agent = agent        self._game = game    def get_state(self,actions):        score = self._game.get_score()         reward = 0.1 #survival reward        is_over = False #game over        if actions[1] == 1: #else do nothing            self._agent.jump()        image = grab_screen(self._game._driver) 
        if self._agent.is_crashed():            self._game.restart()            reward = -1            is_over = True        return image, reward, is_over #return the Experience tuple

Процесс обработки изображения

получить изображение

Есть много способов получить экран игры, например, используя библиотеки PIL и MSS Python, чтобы сделать снимок экрана всего экрана, а затем обрезать соответствующую область. Однако его самым большим недостатком является чувствительность к разрешению экрана и положению окна. К счастью, в игре используется HTML Canvas. Мы можем легко получить изображения в формате base64 с помощью JavaScript. Мы используем Selenium для запуска этого скрипта.

#javascript code to get the image data from canvasvar canvas = document.getElementsByClassName('runner-canvas')[0];var img_data = canvas.toDataURL()return img_data

Изображение извлечено из холста

def grab_screen(_driver = None):    image_b64 = _driver.execute_script(getbase64Script)    screen = np.array(Image.open(BytesIO(base64.b64decode(image_b64))))    image = process_img(screen)#processing image as required    return image

обрабатывать изображения

Результирующее необработанное изображение имеет разрешение примерно 600×150 с 3 каналами (RGB). Мы намерены использовать 4 последовательных снимка экрана в качестве одного входа в эту модель. Это сделало бы наш единственный вход размером до 600×150×3×4. Такие вычисления дороги, и не все функции полезны для игры. Поэтому мы используем библиотеку OpenCV для изменения размера, обрезки и обработки изображения. Окончательное входное изображение, полученное после обработки, имеет размер всего 80x80 пикселей и имеет только один канал (оттенки серого).

def process_img(image):    image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)    image = image[:300, :500]    return image

Обработка изображения

Архитектура модели

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

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

Слой максимального пула может значительно улучшить результаты обработки плотных наборов признаков.

Архитектура модели

Наш выходной слой состоит из двух нейронов, каждый из которых представляет максимальную прогнозируемую награду за каждое действие. Затем мы выбираем действие с наибольшей наградой (значение Q).

def buildmodel():    print("Now we build the model")    model = Sequential()    model.add(Conv2D(32, (8, 8), padding='same',strides=(4, 4),input_shape=(img_cols,img_rows,img_channels)))  #80*80*4    model.add(MaxPooling2D(pool_size=(2,2)))    model.add(Activation('relu'))    model.add(Conv2D(64, (4, 4),strides=(2, 2),  padding='same'))    model.add(MaxPooling2D(pool_size=(2,2)))    model.add(Activation('relu'))    model.add(Conv2D(64, (3, 3),strides=(1, 1),  padding='same'))    model.add(MaxPooling2D(pool_size=(2,2)))    model.add(Activation('relu'))    model.add(Flatten())    model.add(Dense(512))    model.add(Activation('relu'))    model.add(Dense(ACTIONS))    adam = Adam(lr=LEARNING_RATE)    model.compile(loss='mse',optimizer=adam)    print("We finish building the model")    return model

тренироваться

Вот что происходит на этапе обучения:

  • Начните без действий, получите начальное состояние (s_t)

  • Наблюдайте за процессом игры, выполняя этапы НАБЛЮДЕНИЯ.

  • Предсказать и выполнить действие

  • Храните впечатления в памяти воспроизведения

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

  • Если игра окончена, начните сначала

Эта часть кода немного длинная, но ее довольно просто понять.

def trainNetwork(model,game_state):    # store the previous observations in replay memory    D = deque() #experience replay memory    # get the first state by doing nothing    do_nothing = np.zeros(ACTIONS)    do_nothing[0] =1 #0 => do nothing,                     #1=> jump
    x_t, r_0, terminal = game_state.get_state(do_nothing) # get next step after performing the action    s_t = np.stack((x_t, x_t, x_t, x_t), axis=2).reshape(1,20,40,4) # stack 4 images to create placeholder input reshaped 1*20*40*4 
    OBSERVE = OBSERVATION    epsilon = INITIAL_EPSILON    t = 0    while (True): #endless running
        loss = 0        Q_sa = 0        action_index = 0        r_t = 0 #reward at t        a_t = np.zeros([ACTIONS]) # action at t
        q = model.predict(s_t)       #input a stack of 4 images, get the prediction        max_Q = np.argmax(q)         # chosing index with maximum q value        action_index = max_Q         a_t[action_index] = 1        # o=> do nothing, 1=> jump
        #run the selected action and observed next state and reward        x_t1, r_t, terminal = game_state.get_state(a_t)        x_t1 = x_t1.reshape(1, x_t1.shape[0], x_t1.shape[1], 1) #1x20x40x1        s_t1 = np.append(x_t1, s_t[:, :, :, :3], axis=3) # append the new image to input stack and remove the first one
        D.append((s_t, action_index, r_t, s_t1, terminal))# store the transition 
        #only train if done observing; sample a minibatch to train on        trainBatch(random.sample(D, BATCH)) if t > OBSERVE else 0        s_t = s_t1         t += 1

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

def trainBatch(minibatch):  for i in range(0, len(minibatch)):                loss = 0                inputs = np.zeros((BATCH, s_t.shape[1], s_t.shape[2], s_t.shape[3]))   #32, 20, 40, 4                targets = np.zeros((inputs.shape[0], ACTIONS))                         #32, 2                state_t = minibatch[i][0]    # 4D stack of images                action_t = minibatch[i][1]   #This is action index                reward_t = minibatch[i][2]   #reward at state_t due to action_t                state_t1 = minibatch[i][3]   #next state                terminal = minibatch[i][4]   #wheather the agent died or survided due the action                inputs[i:i + 1] = state_t                    targets[i] = model.predict(state_t)  # predicted q values                Q_sa = model.predict(state_t1)      #predict q values for next step
                if terminal:                    targets[i, action_t] = reward_t # if terminated, only equals reward                else:                    targets[i, action_t] = reward_t + GAMMA * np.max(Q_sa)
            loss += model.train_on_batch(inputs, targets)

результат

Используя эту архитектуру, мы должны получить хорошие результаты. Графический процессор может значительно улучшить результаты, что подтверждается увеличением среднего балла. На графике ниже показаны средние баллы с момента начала обучения. По окончании обучения средний балл за 10 игр оставался значительно выше 1000.

Средний балл за 10 игр

Наивысший зарегистрированный результат составил более 4000, что намного выше 250 у предыдущей модели (и намного выше уровня большинства людей!). На графике ниже показано изменение наивысшего балла игры на этапе обучения (каждые 10 игр выбирается один наивысший балл).

Наивысший результат за 10 игр

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

Рабочий код с дополнительными настройками здесь: http://https/github.com/Paperspace/DinoRunTutorial

Оригинальная ссылка: https://blog.paperspace.com/dino-run/