Простое обучение с подкреплением с Tensorflow, часть 0: Q-обучение с таблицами и нейронными сетями
Nuances of programmingПеревод статьи Arthur Juliani: "Simple Reinforcement Learning with Tensorflow Part 0: Q-Learning with Tables and Neural Networks"

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

В этом уроке мы попытаемся применить среду 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-обучения!
Статью перевел Владислав Семёнов.