Простое обучение с подкреплением с Tensorflow, часть 0: Q-обучение с таблицами и нейронными сетями

Простое обучение с подкреплением с Tensorflow, часть 0: Q-обучение с таблицами и нейронными сетями

Nuances of programming

Перевод статьи Arthur Juliani: "Simple Reinforcement Learning with Tensorflow Part 0: Q-Learning with Tables and Neural Networks"

Мы будем изучать на различных примерах среду OpenAI FrozenLake. Наша версия немного менее фотореалистична.

В этом уроке, первом из серии статей «Обучение с подкреплением» (или «обучение по алгоритму типа "кнут и пряник"») мы будем изучать семейство RL-алгоритмов, называемых Q-алгоритмами обучения. Этот класс алгоритмов несколько отличается от алгоритмов, которые будут рассмотрены нами далее в следующих частях серии своей простотой (см. части 1-3). Давайте однако отложим рассмотрение сложных и громоздких нейронных сетей на потом, а пока, для начала, рассмотрим реализацию простой версии модели таблицы поиска, а затем покажем, как реализовать ее эквивалент с помощью нейронной сети в среде Tensorflow. Поскольку мы собираемся начать с основных принципов, мы назвали эту часть серии статей нулевой. Надеемся, она поможет создать общее представление и приобрести интуитивное понимание того как на самом деле происходит Q-обучение, а также заложить основу успешного продолжения занятий, целью которых, в конечном счете - является освоение градиентных методов и Q-обучения. Эти методы используются при создании сложных RL-алгоритмов (заметим, что если вас больше интересуют стратегические сети, или вы уже разбираетесь в Q-обучении, рекомендуем перейти к изучению этих руководств).

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

Табличные методы средств форматирования таблиц

Правила средства FrozenLake.

В этом уроке мы попытаемся применить среду FrozenLake из тренажера OpenAI. Если кто-то сталкивается с этим тренажером впервые, заметим, что тренажер OpenAI предлагает простой способ проведения экспериментов с искусственными обучающимися роботами на основе компьютерных стимуляций и игрового типа. Среда FrozenLake состоит из сети блоков размером 4x4, среди которых начальный (стартовый) блок, конечный (целевой) блок, блок безопасной заморозки или блок с ямой (дырой). Целью является обучение робота перемещению из начального блока в конечный (блок цели или блок назначения) так, чтобы по пути он не попал в яму. В любой момент времени робот имеет возможность выбрать любое направление движения: переместиться вверх, вниз, влево или вправо. Сложность заключается в том, что на робота оказывает влияние сильная помеха - ветер, который случайным образом может смещать ("сдувать") его в совершенно другой блок, совершенно не тот, который робот выбрал для совершения следующего шага. Таким образом, реализовать каждый раз идеальное перемещение становится невозможным, однако научиться избегать отверстий и достигать желаемой цели остается вполне выполнимой задачей. Награда на каждом шаге игры равна нулю, за исключением шага, на котором робот достигает конечной цели и получает вознаграждение, равное единице. Таким образом, нам понадобится разработать алгоритм, который будет изучать ожидаемые долгосрочные награды. Именно для решения этой задачи предназначен метод Q-обучения.

В своем простейшем варианте реализации Q-обучение представляет собой таблицу значений для каждого состояния (строки) и действий (столбцы), которые возможны в моделируемой среде. Для каждой ячейки таблицы мы оцениваем стоимость, чтобы определить насколько хорошим является запланированное действие исходя из имеющегося в настоящий момент состояния. В среде FrozenLake, таким образом, задается 16 возможных состояний (по одному для каждого блока) и 4 возможных действия (четыре возможных направления движения робота), что в результате описывается таблицей из 16 значений Q. Работа алгоритма начинается с инициализации таблицы – в первый момент таблица описывает однородные состояния (все нули), а затем, отслеживая вознаграждения, получаемые при совершении действий, значения в таблице обновляются.

Обновления значений Q-таблицы описываются с помощью так называемого уравнения Беллмана, которое подразумевает, что ожидаемое долгосрочное вознаграждение для каждого действия записывается как комбинация вознаграждения за совершаемое в текущий момент действие и вознаграждение за наилучшее из возможных действий, совершаемых в будущем. Таким образом, Q-таблица используется для оценки вознаграждений за будущие действия! Данное уравнение записывается следующим образом:

Согласно этому уравнению значение Q для комбинации из данного состояния и будущего действия (a) представляет собой текущее вознаграждение (r), учитывающее максимальную величину (γ) будущей награды, получаемой в следующем положении (s), которое сможет занять робот. Поправочная переменная позволяет нам оценить возможную величину будущего вознаграждения на основе имеющейся в текущий момент величины вознаграждения. В таком алгоритме обновления колебания значений таблицы постепенно уменьшаются, принимая более точные величины оценки ожидаемой награды за предпринимаемые роботом шаги. Посмотрите пример пошагового разбора алгоритма Q-таблицы, реализованного в среде FrozenLake на языке Python:

Обучение с помощью Q-таблицы

In [ ]:

import gym
import numpy as np

Загрузка окружения

In [ ]:

env = gym.make('FrozenLake-v0')

Реализация обучения с помощью Q-таблицы

In [ ]:

#Initialize table with all zeros
Q = np.zeros([env.observation_space.n,env.action_space.n])
# Set learning parameters
lr = .8
y = .95
num_episodes = 2000
#create lists to contain total rewards and steps per episode
#jList = []
rList = []
for i in range(num_episodes):
    #Reset environment and get first new observation
    s = env.reset()
    rAll = 0
    d = False
    j = 0
    #The Q-Table learning algorithm
    while j < 99:
        j+=1
        #Choose an action by greedily (with noise) picking from Q table
        a = np.argmax(Q[s,:] + np.random.randn(1,env.action_space.n)*(1./(i+1)))
        #Get new state and reward from environment
        s1,r,d,_ = env.step(a)
        #Update Q-Table with new knowledge
        Q[s,a] = Q[s,a] + lr*(r + y*np.max(Q[s1,:]) - Q[s,a])
        rAll += r
        s = s1
        if d == True:
            break
    #jList.append(j)
    rList.append(rAll)

In [ ]:

print "Score over time: " +  str(sum(rList)/num_episodes)

In [ ]:

print "Final Q-Table Values"
print Q

(cм. Q-Table Learning-Clean.ipynb на GitHub)

(Выражаем благодарность Praneet D за поиск оптимальных гипер-параметров для данного метода)

Q-обучение с нейронными сетями

Возможно у вас возник следующий вопрос: да, таблицы действительно великолепны, но ведь они не масштабируемы, не так ли? Да, легко составить таблицу размером 16x4 для простейшего, описанного нами мира, в котором действует агент, но количество возможных состояний в любой современной игре или в реальной среде бесконечно больше. Для решения действительно интересных задач таблицы просто не будут работать. Вместо этого нам нужно каким-то образом описать наши состояния и получить Q-значения для оценки действий без использования таблицы – именно таким образом мы и приходим к нейронным сетям. Рассматривая приближения функции оценки, мы можем учитывать любое количество возможных состояний, которые мы записываем в виде вектора и учимся сопоставлять их с Q-значениями.

В случае рассмотренного нами примера с FrozenLake мы воспользуемся однослойной сетью, в которой входное состояние задается вектором размера (1x16), а на выходе получается вектор из 4-х Q-значений – по одному значению для каждого возможного действия. Такая простейшая сеть описывается таблицей, содержащей веса сети предыдущих состояний. Главное ее отличие заключается в том, что мы можем легко расширить сеть Tensorflow, добавляя новые уровни, состоящие из функций активации и задавая различные входные значения, что было бы совершенно невозможно осуществить с помощью обычной таблицы. Метод обновления состояний здесь также немного отличается. Вместо прямого обновления значений в нашей таблице, в случае нейронной сети мы будем использовать алгоритм обратного распространения ошибки и функцию потерь. Наша функция потерь будет представлять собой потери в виде суммы квадратов, в которой вычисляется разность между текущими прогнозируемыми Q-значениями, вычисленными целевыми значениями и градиентами прохождения по сети. В этом случае Q-цель для выбранного действия эквивалентна Q-значению, вычисленному в уравнении (1) выше.

Ниже приведен пошаговый разбор реализации нашей простейшей Q-сети в Tensorflow:

Обучение с помощью Q-сети

In [1]:

import gym
import numpy as np
import random
import tensorflow as tf
import matplotlib.pyplot as plt
%matplotlib inline

Загрузка окружения

In [2]:

env = gym.make('FrozenLake-v0')
INFO:gym.envs.registration:Making new env: FrozenLake-v0
[2016-08-25 11:47:05,546] Making new env: FrozenLake-v0

Метод Q-сети

Реализация самой сети

In [381]:

tf.reset_default_graph()

In [382]:

#These lines establish the feed-forward part of the network used to choose actions
inputs1 = tf.placeholder(shape=[1,16],dtype=tf.float32)
W = tf.Variable(tf.random_uniform([16,4],0,0.01))
Qout = tf.matmul(inputs1,W)
predict = tf.argmax(Qout,1)

#Below we obtain the loss by taking the sum of squares difference between the target and prediction Q values.
nextQ = tf.placeholder(shape=[1,4],dtype=tf.float32)
loss = tf.reduce_sum(tf.square(nextQ - Qout))
trainer = tf.train.GradientDescentOptimizer(learning_rate=0.1)
updateModel = trainer.minimize(loss)

Обучение сети

In [383]:

init = tf.initialize_all_variables()

# Set learning parameters
y = .99
e = 0.1
num_episodes = 2000
#create lists to contain total rewards and steps per episode
jList = []
rList = []
with tf.Session() as sess:
    sess.run(init)
    for i in range(num_episodes):
        #Reset environment and get first new observation
        s = env.reset()
        rAll = 0
        d = False
        j = 0
        #The Q-Network
        while j < 99:
            j+=1
            #Choose an action by greedily (with e chance of random action) from the Q-network
            a,allQ = sess.run([predict,Qout],feed_dict={inputs1:np.identity(16)[s:s+1]})
            if np.random.rand(1) < e:
                a[0] = env.action_space.sample()
            #Get new state and reward from environment
            s1,r,d,_ = env.step(a[0])
            #Obtain the Q' values by feeding the new state through our network
            Q1 = sess.run(Qout,feed_dict={inputs1:np.identity(16)[s1:s1+1]})
            #Obtain maxQ' and set our target value for chosen action.
            maxQ1 = np.max(Q1)
            targetQ = allQ
            targetQ[0,a[0]] = r + y*maxQ1
            #Train our network using target and predicted Q values
            _,W1 = sess.run([updateModel,W],feed_dict={inputs1:np.identity(16)[s:s+1],nextQ:targetQ})
            rAll += r
            s = s1
            if d == True:
                #Reduce chance of random action as we train the model.
                e = 1./((i/50) + 10)
                break
        jList.append(j)
        rList.append(rAll)
print "Percent of succesful episodes: " + str(sum(rList)/num_episodes) + "%"
Percent of succesful episodes: 0.352%

Некоторая статистика по производительности сети

Мы можем заметить, что сеть становится значительно лучше показывает себя при значениях отмеченных около величины 750.

In [384]:

plt.plot(rList)

Out[384]:

[<matplotlib.lines.Line2D at 0x11a81e610>]

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

In [385]:

plt.plot(jList)

Out[385]:

[<matplotlib.lines.Line2D at 0x119bd5dd0>]

In [ ]:

(cм. Q-Net Learning Clean.ipynb на GitHub)

Поскольку сеть должна обучаться решению задачи FrozenLake, кажется, что она не столь эффективна, как Q-таблица. Однако нейронные сети обладают большей гибкостью и масштабируемостью по отношению к размерам решаемых задач, хотя при этом важно отметить, что такая масшабируемость и гибкость достигается за счет устойчивости, если говорить о Q-обучении. Существует ряд возможных расширений нашей простейшей Q-сети, которые позволяют повысить эффективность и получить более надежные результаты обучения. Например, имеется два частных приема, называемых «Повторное воспроизведение опыта» и «Замерзающая целевая сеть». Подобные усовершенствования и другие настройки стали ключевыми при создании Atari Deep Q-сетей для компьютерных игр, и мы далее изучим эти приемы. Дополнительные сведения о теории Q-обучения вы можете найти в этой замечательной статье Тэмбет Матиисен. Надеюсь, этот учебник сможет быть полезен тем, кто интересуется реализацией простых алгоритмов Q-обучения!

Статью перевел Владислав Семёнов.

Report Page