Непрерывные задачи о машине на склоне в Tensorflow и Theano (версия 2). Итоги

Непрерывная задача о машине на склоне в Tensorflow (версия 2)

Здравствуйте и вновь добро пожаловать на занятия по теме «Глубокое обучение с подкреплением: глубокое обучение на языке Python, часть 7».

В этой статье мы вновь используем метод градиента стратегии для непрерывной версии задачи о машине на склоне, но в этот раз выполним градиентный спуск в Theano и Tensorlow. И в конце подведем итоги.

Если вы не хотите попытаться написать код самостоятельно, соответствующий файл в репозитарии называется pg_tf.py в папке mountaincar.

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

Итак, вначале у нас идёт класс HiddenLayer, совершенно такой же, как ранее, поэтому сразу пойдём дальше.

# https://deeplearningcourses.com/c/deep-reinforcement-learning-in-python

# https://www.udemy.com/deep-reinforcement-learning-in-python

from __future__ import print_function, division

from builtins import range

# Note: you may need to update your version of future

# sudo pip install -U future

import gym

import os

import sys

import numpy as np

import tensorflow as tf

import matplotlib.pyplot as plt

from gym import wrappers

from datetime import datetime

from q_learning import plot_running_avg, FeatureTransformer, plot_cost_to_go

# so you can test different architectures

class HiddenLayer:

  def __init__(self, M1, M2, f=tf.nn.tanh, use_bias=True, zeros=False):

    if zeros:

      W = np.zeros((M1, M2), dtype=np.float32)

    else:

      W = tf.random_normal(shape=(M1, M2)) * np.sqrt(2. / M1, dtype=np.float32)

    self.W = tf.Variable(W)

    self.use_bias = use_bias

    if use_bias:

      self.b = tf.Variable(np.zeros(M2).astype(np.float32))

    self.f = f

  def forward(self, X):

    if self.use_bias:

      a = tf.matmul(X, self.W) + self.b

    else:

      a = tf.matmul(X, self.W)

    return self.f(a)

Следующим у нас идёт класс PolicyModel, моделирующий вероятность p(a|s). В качестве аргумента он принимает размеры скрытого слоя, хотя в нашем случае это будет просто пустой массив, так как мы будем использовать ядра радиальных базисных функций и линейной моделью поверху. После создания скрытых слоёв мы создаём конечные слои для вывода среднего значения и стандартного отклонения. Обратите внимание, что функцией активации для стандартного отклонения является softplus, чтобы гарантировать, что оно всегда будет положительным.

# approximates pi(a | s)

class PolicyModel:

  def __init__(self, D, ft, hidden_layer_sizes=[]):

    self.ft = ft

    ##### hidden layers #####

    M1 = D

    self.hidden_layers = []

    for M2 in hidden_layer_sizes:

      layer = HiddenLayer(M1, M2)

      self.hidden_layers.append(layer)

      M1 = M2

    # final layer mean

    self.mean_layer = HiddenLayer(M1, 1, lambda x: x, use_bias=False, zeros=True)

    # final layer variance

    self.stdv_layer = HiddenLayer(M1, 1, tf.nn.softplus, use_bias=False, zeros=False)

Далее мы создаём заполнители для данных: X, представляющее входящие состояния, actions – одномерный массив действий, которые предпринимаются в этих состояниях, и advantages – ещё один одномерный массив, который будет вычисляться извне. Затем мы подставляем X в скрытые слои, чтобы получить значение предпоследнего слоя, и подставляем его в конечные слои среднего значения и стандартного отклонения, чтобы получить параметры гауссового распределения действий. Кроме того, мы добавляем небольшое сглаживание, что является общеупотребимой практикой при работе с гауссовыми распределениями.

    # inputs and targets

    self.X = tf.placeholder(tf.float32, shape=(None, D), name=’X’)

    self.actions = tf.placeholder(tf.float32, shape=(None,), name=’actions’)

    self.advantages = tf.placeholder(tf.float32, shape=(None,), name=’advantages’)

    # get final hidden layer

    Z = self.X

    for layer in self.hidden_layers:

      Z = layer.forward(Z)

    # calculate output and cost

    mean = self.mean_layer.forward(Z)

    stdv = self.stdv_layer.forward(Z) + 1e-5 # smoothing

Далее, поскольку слои нейронной сети всегда работают с двухмерными данными, мы должны преобразовать средние значения и стандартные отклонения обратно в одномерные массивы. После этого можно создать объект нормального распределения с этими средними значениями и стандартными отклонениями, а затем сделать из него выборку. Полученные значения нужно «обрезать», чтобы они были между -1 и +1, поскольку среда требует от нас именно этого. Затем мы вычисляем функцию ошибок, которая на самом деле является отрицательным значением нашего целевого показателя стратегии, – ведь мы должны найти минимум, так как в Tensorflow отсутствуют функции нахождения максимума. Кроме того, обратите внимание, что мы делаем регуляризацию функции ошибок путём добавления константы, умноженной на энтропию гауссового распределения. Увеличивая энтропию, мы делаем большей дисперсию, то есть это нечто похожее на сглаживание дисперсии, виденное вами ранее.

    # make them 1-D

    mean = tf.reshape(mean, [-1])

    stdv = tf.reshape(stdv, [-1])

    norm = tf.contrib.distributions.Normal(mean, stdv)

    self.predict_op = tf.clip_by_value(norm.sample(), -1, 1)

    log_probs = norm.log_prob(self.actions)

    cost = -tf.reduce_sum(self.advantages * log_probs + 0.1*norm.entropy())

    self.train_op = tf.train.AdamOptimizer(1e-3).minimize(cost)

Затем идут обычные функции set_session и partial_fit; последняя выполняет одну итерацию градиентного спуска:

  def set_session(self, session):

    self.session = session

  def partial_fit(self, X, actions, advantages):

    X = np.atleast_2d(X)

    X = self.ft.transform(X)

    actions = np.atleast_1d(actions)

    advantages = np.atleast_1d(advantages)

    self.session.run(

      self.train_op,

      feed_dict={

        self.X: X,

        self.actions: actions,

        self.advantages: advantages,

      }

    )

После этого идут функции predict и sample_action, которые просто дают нам действие из текущей стратегии:

  def predict(self, X):

    X = np.atleast_2d(X)

    X = self.ft.transform(X)

    return self.session.run(self.predict_op, feed_dict={self.X: X})

  def sample_action(self, X):

    p = self.predict(X)[0]

    return p

Следующим идёт класс ValueModel. В отличие от программы со случайным восхождением по склону, нам нужно смоделировать ценность, так как она используется в вычислениях преимуществ, которые, в свою очередь, используются для вычисления целевого показателя стратегии. Это противоположно ситуации с восхождением на склон, где мы лишь параметризовати стратегию, а затем выбирали ту, которая даёт лучшее вознаграждение.

По структуре класс похож на PolicyModel: у нас есть ряд скрытых слоёв и конечный слой линейной регрессии для прогноза ценности – ничего особо нового:

# approximates V(s)

class ValueModel:

  def __init__(self, D, ft, hidden_layer_sizes=[]):

    self.ft = ft

    self.costs = []

    # create the graph

    self.layers = []

    M1 = D

    for M2 in hidden_layer_sizes:

      layer = HiddenLayer(M1, M2)

      self.layers.append(layer)

      M1 = M2

    # final layer

    layer = HiddenLayer(M1, 1, lambda x: x)

    self.layers.append(layer)

    # inputs and targets

    self.X = tf.placeholder(tf.float32, shape=(None, D), name=’X’)

    self.Y = tf.placeholder(tf.float32, shape=(None,), name=’Y’)

    # calculate output and cost

    Z = self.X

    for layer in self.layers:

      Z = layer.forward(Z)

    Y_hat = tf.reshape(Z, [-1]) # the output

    self.predict_op = Y_hat

В качестве функции ошибок используется квадрат ошибок, а минимизируем мы его с помощью Adam-оптимизатора:

    cost = tf.reduce_sum(tf.square(self.Y – Y_hat))

    self.cost = cost

    self.train_op = tf.train.AdamOptimizer(1e-1).minimize(cost)

Затем идут функции set_session, partial_fit и predict – очень схожие с теми, что в PolicyModel:

  def set_session(self, session):

    self.session = session

  def partial_fit(self, X, Y):

    X = np.atleast_2d(X)

    X = self.ft.transform(X)

    Y = np.atleast_1d(Y)

    self.session.run(self.train_op, feed_dict={self.X: X, self.Y: Y})

    cost = self.session.run(self.cost, feed_dict={self.X: X, self.Y: Y})

    self.costs.append(cost)

  def predict(self, X):

    X = np.atleast_2d(X)

    X = self.ft.transform(X)

    return self.session.run(self.predict_op, feed_dict={self.X: X})

Следующей идёт функция play_one_td. Она повторяется до тех пор, пока не будет получен сигнал done или не будет достигнуто предельное количество итераций. В зависимости от вашей версии Gym, это может быть 200 итераций, хотя я обнаружил, что этого слишком мало, чтобы действительно хорошо изучить среду. Так что если у вас более ранняя версия Gym, то, по всей видимости, это будет лучше. Внутри цикла мы выбираем действие из модели стратегии, выполняем его, получаем следующее состояние и затем обновляем обе наши модели с помощью одной итерации градиентного спуска:

def play_one_td(env, pmodel, vmodel, gamma):

  observation = env.reset()

  done = False

  totalreward = 0

  iters = 0

  while not done and iters < 2000:

    # if we reach 2000, just quit, don’t want this going forever

    # the 200 limit seems a bit early

    action = pmodel.sample_action(observation)

    prev_observation = observation

    observation, reward, done, info = env.step([action])

    totalreward += reward

    # update the models

    V_next = vmodel.predict(observation)

    G = reward + gamma*V_next

    advantage = G – vmodel.predict(prev_observation)

    pmodel.partial_fit(prev_observation, action, advantage)

    vmodel.partial_fit(prev_observation, G)

    iters += 1

  return totalreward, iters

Далее идёт наша функция main. Она использует тот же, что и ранее, преобразователь признаков с радиальными базисными функциями и 100 компонентов – в этой программе мы используем неглубокие сети, просто линейные слои поверх расширения признаков радиальных базисных функций. После этого мы отыгрываем 50 эпизодов и строим графики вознаграждений и сглаженных вознаграждений, хотя вы, конечно, можете попробовать разные размеры окна, поскольку отыгрывается не очень большое количество эпизодов. И наконец, мы строим график функции cost_to_go. Ожидается та же форма пиков, что и виденная ранее, хотя я нашёл эти установки гиперпараметров несколько нестабильными.

def main():

  env = gym.make(‘MountainCarContinuous-v0’)

  ft = FeatureTransformer(env, n_components=100)

  D = ft.dimensions

  pmodel = PolicyModel(D, ft, [])

  vmodel = ValueModel(D, ft, [])

  init = tf.global_variables_initializer()

  session = tf.InteractiveSession()

  session.run(init)

  pmodel.set_session(session)

  vmodel.set_session(session)

  gamma = 0.95

  if ‘monitor’ in sys.argv:

    filename = os.path.basename(__file__).split(‘.’)[0]

    monitor_dir = ‘./’ + filename + ‘_’ + str(datetime.now())

    env = wrappers.Monitor(env, monitor_dir)

  N = 50

  totalrewards = np.empty(N)

  costs = np.empty(N)

  for n in range(N):

    totalreward, num_steps = play_one_td(env, pmodel, vmodel, gamma)

    totalrewards[n] = totalreward

    if n % 1 == 0:

      print(“episode:”, n, “total reward: %.1f” % totalreward, “num steps: %d” % num_steps, “avg reward (last 100): %.1f” % totalrewards[max(0, n-100):(n+1)].mean())

  print(“avg reward for last 100 episodes:”, totalrewards[-100:].mean())

  plt.plot(totalrewards)

  plt.title(“Rewards”)

  plt.show()

  plot_running_avg(totalrewards)

  plot_cost_to_go(env, vmodel)

if __name__ == ‘__main__’:

  main()

Итак, запустим программу и посмотрим, что у нас получится.

Непрерывная задача о машине на склоне в Theano (версия 2)

Мы вновь используем метод градиента стратегии для непрерывной версии задачи о машине на склоне, но в этот раз выполним градиентный спуск. Если вы не хотите попытаться написать код самостоятельно, соответствующий файл в репозитарии называется pg_theano.py в папке mountaincar.

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

# https://deeplearningcourses.com/c/deep-reinforcement-learning-in-python

# https://www.udemy.com/deep-reinforcement-learning-in-python

from __future__ import print_function, division

from builtins import range

# Note: you may need to update your version of future

# sudo pip install -U future

import gym

import os

import sys

import numpy as np

import theano

import theano.tensor as T

import matplotlib.pyplot as plt

from gym import wrappers

from datetime import datetime

from q_learning import plot_running_avg, FeatureTransformer, plot_cost_to_go

Итак, вначале у нас идёт функция, помогающая выполнить Adam-оптимизацию в библиотеке Theano. В Tensorflow она уже встроена, поэтому прежде нам не требовалось писать для неё код вручную, однако в Theano надо. Она просто использует все настройки параметров по умолчанию, применяемые в Tensorflow. Инкапсуляция их прямо сейчас делает наш дальнейший код немного чище, поскольку мы используем Adam и для PolicyModel, и для ValueModel.

# helper for adam optimizer

# use tensorflow defaults

def adam(cost, params, lr0=1e-3, beta1=0.9, beta2=0.999, eps=1e-8):

  grads = T.grad(cost, params)

  updates = []

  time = theano.shared(0)

  new_time = time + 1

  updates.append((time, new_time))

  lr = lr0*T.sqrt(1 – beta2**new_time) / (1 – beta1**new_time)

  for p, g in zip(params, grads):

    m = theano.shared(p.get_value() * 0.)

    v = theano.shared(p.get_value() * 0.)

    new_m = beta1*m + (1 – beta1)*g

    new_v = beta2*v + (1 – beta2)*g*g

    new_p = p – lr*new_m / (T.sqrt(new_v) + eps)

    updates.append((m, new_m))

    updates.append((v, new_v))

    updates.append((p, new_p))

  return updates

Затем идёт класс HiddenLayer, совершенно такой же, как ранее, поэтому сразу пойдём дальше.

# so you can test different architectures

class HiddenLayer:

  def __init__(self, M1, M2, f=T.nnet.relu, use_bias=True, zeros=False):

    if zeros:

      W = np.zeros((M1, M2))

    else:

      W = np.random.randn(M1, M2) * np.sqrt(2. / M1)

    self.W = theano.shared(W)

    self.params = [self.W]

    self.use_bias = use_bias

    if use_bias:

      self.b = theano.shared(np.zeros(M2))

      self.params += [self.b]

    self.f = f

  def forward(self, X):

    if self.use_bias:

      a = X.dot(self.W) + self.b

    else:

      a = X.dot(self.W)

    return self.f(a)

После этого у нас идёт класс PolicyModel, моделирующий вероятность p(a|s). В качестве аргумента он принимает размеры скрытого слоя, хотя в нашем случае это будет просто пустой массив, так как мы будем использовать ядра радиальных базисных функций и линейной моделью поверху. После создания скрытых слоёв мы создаём конечные слои для вывода среднего значения и стандартного отклонения. Обратите внимание, что функцией активации для стандартного отклонения является softplus, чтобы гарантировать, что оно всегда будет положительным.

# approximates pi(a | s)

class PolicyModel:

  def __init__(self, D, ft, hidden_layer_sizes=[]):

    self.ft = ft

    ##### hidden layers #####

    M1 = D

    self.hidden_layers = []

    for M2 in hidden_layer_sizes:

      layer = HiddenLayer(M1, M2)

      self.hidden_layers.append(layer)

      M1 = M2

    # final layer mean

    self.mean_layer = HiddenLayer(M1, 1, lambda x: x, use_bias=False, zeros=True)

    # final layer variance

    self.var_layer = HiddenLayer(M1, 1, T.nnet.softplus, use_bias=False, zeros=False)

Далее мы создаём заполнители для данных: X, представляющее входящие состояния, actions – одномерный массив действий, которые предпринимаются в этих состояниях, и advantages – ещё один одномерный массив, который будет вычисляться извне. Затем мы подставляем X в скрытые слои, чтобы получить значение предпоследнего слоя, и подставляем его в конечные слои среднего значения и стандартного отклонения, чтобы получить параметры гауссового распределения действий. Кроме того, мы добавляем небольшое сглаживание, что является общеупотребимой практикой при работе с гауссовыми распределениями.

    # get all params for gradient later

    params = self.mean_layer.params + self.var_layer.params

    for layer in self.hidden_layers:

      params += layer.params

    # inputs and targets

    X = T.matrix(‘X’)

    actions = T.vector(‘actions’)

    advantages = T.vector(‘advantages’)

    target_value = T.vector(‘target_value’)

    # get final hidden layer

    Z = X

    for layer in self.hidden_layers:

      Z = layer.forward(Z)

Далее, поскольку слои нейронной сети всегда работают с двухмерными данными, мы должны преобразовать средние значения и стандартные отклонения обратно в одномерные массивы, воспользовавшись функцией flatten:

    mean = self.mean_layer.forward(Z).flatten()

    var = self.var_layer.forward(Z).flatten() + 1e-5 # smoothing

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

    # can’t find Theano log pdf, we will make it

    def log_pdf(actions, mean, var):

      k1 = T.log(2*np.pi*var)

      k2 = (actions – mean)**2 / var

      return -0.5*(k1 + k2)

    def entropy(var):

      return 0.5*T.log(2*np.pi*np.e*var)

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

    log_probs = log_pdf(actions, mean, var)

    cost = -T.sum(advantages * log_probs + 0.1*entropy(var))

    updates = adam(cost, params)

И наконец мы компилируем функции train_op и predict_op, чтобы использовать их позже:

    # compile functions

    self.train_op = theano.function(

      inputs=[X, actions, advantages],

      updates=updates,

      allow_input_downcast=True

    )

    # alternatively, we could create a RandomStream and sample from

    # the Gaussian using Theano code

    self.predict_op = theano.function(

      inputs=[X],

      outputs=[mean, var],

      allow_input_downcast=True

    )

Затем идёт обычная функция partial_fit, выполняющая одну итерацию градиентного спуска. За ней следуют функции predict и sample_action, которые просто дают нам действие из текущей стратегии. Обратите внимание, что наша функция predict на самом деле будет возвращать кортеж массивов. Первый элемент кортежа – это массив средних значений размерности 1, который индексируется как [0][0], а второй элемент – массив дисперсий такой же размерности, который индексируется как [1][0]. Это связано с тем, что мы будем выбирать только одно действие за раз. После этого мы генерируем выборку из гауссового распределения с этими средним значением и дисперсией. Для этого можно воспользоваться простым способом – сделать выборку из стандартного нормального распределения, отмасштабировав и сдвинув её на стандартное отклонение и среднее значение. Кроме того, мы «обрезаем» значения, чтобы они попадали в диапазон между -1 и +1, поскольку среда не принимает действия, находящиеся вне этого диапазона.

  def partial_fit(self, X, actions, advantages):

    X = np.atleast_2d(X)

    X = self.ft.transform(X)

    actions = np.atleast_1d(actions)

    advantages = np.atleast_1d(advantages)

    self.train_op(X, actions, advantages)

  def predict(self, X):

    X = np.atleast_2d(X)

    X = self.ft.transform(X)

    return self.predict_op(X)

  def sample_action(self, X):

    pred = self.predict(X)

    mu = pred[0][0]

    v = pred[1][0]

    a = np.random.randn()*np.sqrt(v) + mu

    return min(max(a, -1), 1)

Следующим идёт класс ValueModel. В отличие от программы со случайным восхождением по склону, нам нужно смоделировать ценность, так как она используется в вычислениях преимуществ, которые, в свою очередь, используются для вычисления целевого показателя стратегии. Это противоположно ситуации с восхождением на склон, где мы лишь параметризовати стратегию, а затем выбирали ту, которая даёт лучшее вознаграждение.

По структуре класс похож на PolicyModel: у нас есть ряд скрытых слоёв и конечный слой линейной регрессии для прогноза ценности – ничего особо нового:

# approximates V(s)

class ValueModel:

  def __init__(self, D, ft, hidden_layer_sizes=[]):

    self.ft = ft

    # create the graph

    self.layers = []

    M1 = D

    for M2 in hidden_layer_sizes:

      layer = HiddenLayer(M1, M2)

      self.layers.append(layer)

      M1 = M2

    # final layer

    layer = HiddenLayer(M1, 1, lambda x: x)

    self.layers.append(layer)

    # get all params for gradient later

    params = []

    for layer in self.layers:

      params += layer.params

    # inputs and targets

    X = T.matrix(‘X’)

    Y = T.vector(‘Y’)

В качестве функции ошибок используется квадрат ошибок, а минимизируем мы его с помощью Adam-оптимизатора:

    # calculate output and cost

    Z = X

    for layer in self.layers:

      Z = layer.forward(Z)

    Y_hat = T.flatten(Z)

    cost = T.sum((Y – Y_hat)**2)

    # specify update rule

    updates = adam(cost, params, lr0=1e-1)

    # compile functions

    self.train_op = theano.function(

      inputs=[X, Y],

      updates=updates,

      allow_input_downcast=True

    )

    self.predict_op = theano.function(

      inputs=[X],

      outputs=Y_hat,

      allow_input_downcast=True

    )

Затем идут функции partial_fit и predict – очень схожие с теми, что в PolicyModel:

  def partial_fit(self, X, Y):

    X = np.atleast_2d(X)

    X = self.ft.transform(X)

    Y = np.atleast_1d(Y)

    self.train_op(X, Y)

  def predict(self, X):

    X = np.atleast_2d(X)

    X = self.ft.transform(X)

    return self.predict_op(X)

Следующей идёт функция play_one_td. Она повторяется до тех пор, пока не будет получен сигнал done или не будет достигнуто предельное количество итераций. В зависимости от вашей версии Gym, это может быть 200 итераций, хотя я обнаружил, что этого слишком мало, чтобы действительно хорошо изучить среду. Так что если у вас более ранняя версия Gym, то, по всей видимости, это будет лучше. Внутри цикла мы выбираем действие из модели стратегии, выполняем его, получаем следующее состояние и затем обновляем обе наши модели с помощью одной итерации градиентного спуска:

def play_one_td(env, pmodel, vmodel, gamma, train=True):

  observation = env.reset()

  done = False

  totalreward = 0

  iters = 0

  while not done and iters < 2000:

    # if we reach 2000, just quit, don’t want this going forever

    # the 200 limit seems a bit early

    action = pmodel.sample_action(observation)

    prev_observation = observation

    observation, reward, done, info = env.step([action])

    totalreward += reward

    # update the models

    if train:

      V_next = vmodel.predict(observation)

      G = reward + gamma*V_next

      advantage = G – vmodel.predict(prev_observation)

      pmodel.partial_fit(prev_observation, action, advantage)

      vmodel.partial_fit(prev_observation, G)

    iters += 1

  return totalreward

Далее идёт наша функция main. Она использует тот же, что и ранее, преобразователь признаков с радиальными базисными функциями и 100 компонентов – в этой программе мы используем неглубокие сети, просто линейные слои поверх расширения признаков радиальных базисных функций. После этого мы отыгрываем 50 эпизодов и строим графики вознаграждений и сглаженных вознаграждений, хотя вы, конечно, можете попробовать разные размеры окна, поскольку отыгрывается не очень большое количество эпизодов. И наконец, мы строим график функции cost_to_go. Ожидается та же форма пиков, что и виденная ранее, хотя я нашёл эти установки гиперпараметров несколько нестабильными.

def main():

  env = gym.make(‘MountainCarContinuous-v0’)

  ft = FeatureTransformer(env, n_components=100)

  D = ft.dimensions

  pmodel = PolicyModel(D, ft)

  vmodel = ValueModel(D, ft)

  gamma = 0.99

  if ‘monitor’ in sys.argv:

    filename = os.path.basename(__file__).split(‘.’)[0]

    monitor_dir = ‘./’ + filename + ‘_’ + str(datetime.now())

    env = wrappers.Monitor(env, monitor_dir)

  N = 50

  totalrewards = np.empty(N)

  costs = np.empty(N)

  for n in range(N):

    totalreward = play_one_td(env, pmodel, vmodel, gamma)

    totalrewards[n] = totalreward

    if n % 1 == 0:

      print(“episode:”, n, “total reward: %.1f” % totalreward, “avg reward (last 100): %.1f” % totalrewards[max(0, n-100):(n+1)].mean())

  print(“avg reward for last 100 episodes:”, totalrewards[-100:].mean())

  plt.plot(totalrewards)

  plt.title(“Rewards”)

  plt.show()

  plot_running_avg(totalrewards)

  plot_cost_to_go(env, vmodel)

if __name__ == ‘__main__’:

  main()

Итак, запустим программу и посмотрим, что у нас получится.

Раздел градиента стратегии. Итоги

Подведём итоги всему тому, что в изучили в этой части курса, сосредоточенной на методе градиента стратегии.

Различие между методом градиента стратегии и другими заключается в том, что вместо моделирования лишь функции ценности мы создаём и модель для стратегии. Это позволяет естественным образом создавать вероятностную стратегию, а не выполнять что-то вроде эпсилон-жадного алгоритма, где нам приходится выбирать ε и где наши действия могут быть неоптимальными.

Мы начали эту часть с рассмотрения того, как параметризовать стратегию или, другими словами, создать функцию для моделирования стратегии с некоторыми изучаемыми параметрами. Затем мы задались вопросом, как можно оптимизировать эту стратегию. Для этого оказалось необходимым создать целевую функцию для стратегии. Справившись с этим, дальше всё было как обычно: обучение путём отыгрывания ряда эпизодов и выполнение градиентного спуска.

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

Андрей Никитенко
Андрей Никитенко
Задать вопрос эксперту
Понравилась статья? Поделить с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: