Учебное пособие по многоруким бандитам с функциями Per-Arm

Начать

Посмотреть на TensorFlow.org Запустить в Google Colab Посмотреть исходный код на GitHub Скачать блокнот

Это руководство представляет собой пошаговое руководство по использованию библиотеки TF-Agents для решения проблем с контекстными бандитами, когда действия (оружие) имеют свои собственные функции, такие как список фильмов, представленных функциями (жанр, год выпуска, ...).

Предварительное условие

Предполагается, что читатель в некоторой степени знаком с библиотекой Bandit для TF-Agents, в частности, перед чтением этого учебника проработал учебник для Bandits in TF-Agents .

Многорукие бандиты с особенностями рук

В «классической» настройке «Контекстные многорукие бандиты» агент получает вектор контекста (также известный как наблюдение) на каждом временном шаге и должен выбирать из конечного набора пронумерованных действий (рук), чтобы максимизировать свою совокупную награду.

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

Мы могли бы попытаться сформулировать эту проблему movie_1, movie_2, ..., movie_K в качестве контекста информацию о пользователе, а руками - movie_1, movie_2, ..., movie_K , но у этого подхода есть несколько недостатков:

  • Количество действий должно соответствовать всем фильмам в системе, и добавлять новый фильм сложно.
  • Агент должен выучить модель для каждого фильма.
  • Сходство фильмов не принимается во внимание.

Вместо того, чтобы нумеровать фильмы, мы можем сделать что-то более интуитивно понятное: мы можем представить фильмы с набором характеристик, включая жанр, продолжительность, состав, рейтинг, год и т. Д. У этого подхода много преимуществ:

  • Обобщение фильмов.
  • Агент изучает только одну функцию вознаграждения, которая моделирует вознаграждение с помощью функций пользователя и фильма.
  • Легко удалить из системы или добавить в систему новые фильмы.

В этой новой настройке количество действий даже не обязательно должно быть одинаковым на каждом временном шаге.

Бандиты в составе агентов TF

Пакет TF-Agents Bandit разработан таким образом, чтобы его можно было использовать и для индивидуального использования. Существуют индивидуальные среды, а также большинство политик и агентов могут работать в индивидуальном режиме.

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

Монтаж

pip install tf-agents

Импорт

import functools
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf

from tf_agents.bandits.agents import lin_ucb_agent
from tf_agents.bandits.environments import stationary_stochastic_per_arm_py_environment as p_a_env
from tf_agents.bandits.metrics import tf_metrics as tf_bandit_metrics
from tf_agents.drivers import dynamic_step_driver
from tf_agents.environments import tf_py_environment
from tf_agents.replay_buffers import tf_uniform_replay_buffer
from tf_agents.specs import tensor_spec
from tf_agents.trajectories import time_step as ts

# Clear any leftover state from previous colabs run.
# (This is not necessary for normal programs.)
tf.compat.v1.reset_default_graph()

tf.compat.v1.enable_resource_variables()
tf.compat.v1.enable_v2_behavior()
nest = tf.compat.v2.nest

Параметры - не стесняйтесь играть

# The dimension of the global features.
GLOBAL_DIM = 40 
# The elements of the global feature will be integers in [-GLOBAL_BOUND, GLOBAL_BOUND).
GLOBAL_BOUND = 10 
# The dimension of the per-arm features.
PER_ARM_DIM = 50 
# The elements of the PER-ARM feature will be integers in [-PER_ARM_BOUND, PER_ARM_BOUND).
PER_ARM_BOUND = 6 
# The variance of the Gaussian distribution that generates the rewards.
VARIANCE = 100.0 
# The elements of the linear reward parameter will be integers in [-PARAM_BOUND, PARAM_BOUND).
PARAM_BOUND = 10 

NUM_ACTIONS = 70 
BATCH_SIZE = 20 

# Parameter for linear reward function acting on the
# concatenation of global and per-arm features.
reward_param = list(np.random.randint(
      -PARAM_BOUND, PARAM_BOUND, [GLOBAL_DIM + PER_ARM_DIM]))

Простая среда Per-Arm

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

Чтобы инициализировать среду для каждой руки, нужно определить функции, которые генерируют

  • глобальные и индивидуальные функции : эти функции не имеют входных параметров и при вызове генерируют один (глобальный или для каждой руки) вектор функций.
  • вознаграждения : эта функция принимает в качестве параметра конкатенацию глобального и индивидуального вектора функций и генерирует вознаграждение. По сути, это функция, которую агент должен «угадать». Здесь стоит отметить, что для каждой руки функция вознаграждения идентична для каждой руки. Это фундаментальное отличие от классического случая с бандитами, когда агент должен оценивать функции вознаграждения для каждой руки независимо.
def global_context_sampling_fn():
  """This function generates a single global observation vector."""
  return np.random.randint(
      -GLOBAL_BOUND, GLOBAL_BOUND, [GLOBAL_DIM]).astype(np.float32)

def per_arm_context_sampling_fn():
  """"This function generates a single per-arm observation vector."""
  return np.random.randint(
      -PER_ARM_BOUND, PER_ARM_BOUND, [PER_ARM_DIM]).astype(np.float32)

def linear_normal_reward_fn(x):
  """This function generates a reward from the concatenated global and per-arm observations."""
  mu = np.dot(x, reward_param)
  return np.random.normal(mu, VARIANCE)

Теперь мы готовы инициализировать нашу среду.

per_arm_py_env = p_a_env.StationaryStochasticPerArmPyEnvironment(
    global_context_sampling_fn,
    per_arm_context_sampling_fn,
    NUM_ACTIONS,
    linear_normal_reward_fn,
    batch_size=BATCH_SIZE
)
per_arm_tf_env = tf_py_environment.TFPyEnvironment(per_arm_py_env)

Ниже мы можем проверить, что производит эта среда.

print('observation spec: ', per_arm_tf_env.observation_spec())
print('\nAn observation: ', per_arm_tf_env.reset().observation)

action = tf.zeros(BATCH_SIZE, dtype=tf.int32)
time_step = per_arm_tf_env.step(action)
print('\nRewards after taking an action: ', time_step.reward)
observation spec:  {'global': TensorSpec(shape=(40,), dtype=tf.float32, name=None), 'per_arm': TensorSpec(shape=(70, 50), dtype=tf.float32, name=None)}

An observation:  {'global': <tf.Tensor: shape=(20, 40), dtype=float32, numpy=
array([[  8.,  -7.,  -1.,   7.,   4.,  -9.,  -7.,  -5.,   5.,   9.,  -2.,
          3.,  -3.,   2.,   6.,  -3.,   5.,  -6.,  -3.,   7.,  -1.,   4.,
          7.,   8.,  -9.,   6.,  -7.,  -2.,  -3.,   9.,  -6.,  -7.,  -2.,
          5.,   5.,   2.,  -5.,  -3.,  -2.,  -3.],
       [  4.,   2.,   8.,   1.,   2.,   7.,   2.,   1.,  -5.,  -1.,  -1.,
         -4.,  -6.,   1.,  -5.,  -1.,  -1.,  -6.,   1.,  -4.,   6.,  -5.,
          4.,  -5.,  -5.,   9.,   9.,  -9.,   5.,   0.,   6.,  -5.,   2.,
          7.,   8.,   9.,  -9.,   1.,   6.,  -1.],
       [  5.,  -5.,  -6.,   5.,   5.,   7.,  -9., -10.,  -4., -10.,  -8.,
         -2.,  -8.,  -6.,  -6.,   6.,   7.,   6.,  -9.,   8.,   7.,  -3.,
         -9.,   8.,   5.,  -6.,  -4.,   5.,   7.,   3.,  -3.,  -1.,  -5.,
          3.,   7.,  -1.,   1.,   7.,  -7.,  -7.],
       [  1.,   5.,   3.,  -8.,   1.,  -7.,  -4.,   7.,  -2.,   4.,  -3.,
         -8.,   8.,  -1.,   8.,  -8.,   0.,   0.,   2.,   3.,   3.,  -2.,
          0.,   6.,  -4.,   0.,  -5.,  -3.,  -3.,  -7.,   4.,  -8.,   7.,
         -1.,   4.,   1.,   7.,   2.,  -7.,   2.],
       [ -5.,   5., -10.,   5.,   2.,   2.,   1.,   4.,   5.,   9.,   2.,
         -4., -10.,   3.,   6.,   6.,   6.,  -1.,   7.,   2.,  -7.,  -4.,
         -3.,   6.,   1.,  -4., -10.,   3., -10.,  -1.,  -5.,   4.,   8.,
         -7.,   2.,  -1.,   0.,   9.,  -8.,  -2.],
       [  9.,   1.,   9.,   1.,   9.,   5.,   9.,  -3.,   1.,   6.,   8.,
          3.,   1.,   2.,  -3.,   5.,   1.,   8.,  -9.,   8.,  -4.,   1.,
          8., -10.,  -4.,   2.,  -7.,   5.,   7.,   2.,  -4.,  -8.,  -8.,
         -2.,   4.,  -8.,  -3.,  -8.,  -4.,  -8.],
       [  4.,   9.,  -3.,   1.,  -5.,   3.,  -7.,  -5.,  -9.,  -9., -10.,
          2.,  -2.,  -4.,   8.,   3.,   3.,  -6.,  -4.,  -2.,   5.,   0.,
         -4.,  -6.,  -9.,   1.,   9.,  -9.,  -4.,   5.,  -3.,   2.,   1.,
         -5.,   3.,   6.,  -2.,   1.,   5.,  -9.],
       [  6.,   2.,   7.,   0.,   1.,   7.,   4.,  -3.,   8.,   4.,   0.,
          3.,   9.,   7.,   6.,   3.,  -3.,  -5.,  -9.,  -4.,   8.,   9.,
         -9.,  -8.,  -2.,   6.,   8.,   7.,   8.,  -4.,   3.,   5.,   2.,
         -7., -10.,   0.,  -7., -10.,  -4.,   0.],
       [  2.,  -6.,  -5.,   0., -10.,  -6.,   9.,  -4.,  -6.,   7.,  -5.,
         -4.,   7.,  -1.,   9.,  -4.,  -2., -10.,  -5.,   7.,   6.,   1.,
          6.,   9.,  -8.,   7.,  -8.,   7.,   0.,   7.,  -5.,  -1.,  -7.,
          5.,   5.,   6.,  -8., -10.,   1.,  -8.],
       [  0.,  -7., -10.,  -4.,   0.,   1., -10.,   4.,   9.,   1.,  -1.,
         -1.,   0.,   2.,   8.,  -7.,   2.,  -2.,   5.,   1.,   1.,   2.,
         -3.,  -2.,   4.,   7.,   6.,  -4.,   7.,  -5.,  -4.,   1.,   9.,
         -8.,   5.,  -3.,  -7.,  -4.,  -3.,  -1.],
       [ -7.,  -5.,  -1.,  -1., -10.,   6.,  -6.,   7.,  -7.,  -4.,   3.,
         -3.,   2.,   1.,   5.,   9.,  -4.,  -3.,   0.,   2.,   4.,  -9.,
         -8.,  -5.,   5.,  -8.,   7.,   0.,   8.,   7.,  -2.,   9.,  -4.,
         -5.,  -7.,   7.,   6.,  -3.,  -9.,  -9.],
       [ -1.,  -1.,  -4.,  -3.,   8.,   4.,   5.,   7.,   2.,   9.,  -9.,
         -7.,   2.,   6.,   9.,   8.,  -1.,   2.,   5.,  -1.,  -2.,  -4.,
          8.,  -1.,  -9.,   9.,  -9.,   4.,  -4.,  -3.,  -3.,   9.,   3.,
          8.,  -1.,  -9.,   4.,  -6.,   2., -10.],
       [  2.,  -5.,   9.,  -8.,  -5.,   8.,   8.,   0.,  -3.,  -6.,   5.,
          3.,  -9.,  -8.,   9.,  -8.,  -2.,   2.,   7.,   6.,   0.,  -9.,
          2., -10.,   4.,  -9.,  -8., -10.,  -8.,  -9.,  -1.,   1.,   0.,
          0.,   6.,  -4.,  -8.,   4.,  -4.,  -2.],
       [  0.,   6.,   1.,   1.,   4.,   7.,  -8.,   0.,   3.,  -2.,   5.,
          9.,   1.,  -3.,   1.,  -5.,   1.,   7.,  -8., -10.,   1.,  -2.,
         -1.,  -3., -10.,   7.,  -3.,   9.,   4.,   2.,  -2.,   8., -10.,
         -4.,  -4.,  -6.,   6.,   4.,  -3., -10.],
       [ -9.,  -4.,  -6.,   6., -10.,   4.,  -3.,   5.,  -2.,  -1.,  -9.,
          0.,   0.,  -8.,  -8.,  -1.,  -5.,   0.,   4.,   4.,  -4.,  -8.,
          1.,  -6., -10.,  -3.,   4.,   8.,  -6.,   0.,  -2.,  -9.,   4.,
         -8.,  -5.,   2.,   7.,  -4.,  -7.,   2.],
       [  5.,  -6.,  -2.,  -6.,   6.,   7.,  -5.,   2.,  -1.,  -4.,   3.,
          8.,  -2.,   1.,   7.,  -9.,   4.,   7.,  -8.,   7.,  -2.,  -2.,
          3.,   5., -10.,  -9.,  -2.,  -8.,  -1.,  -7.,   0.,  -3.,  -1.,
          4.,   7.,   4.,   9.,  -6.,  -9.,  -8.],
       [  4.,  -6.,   4.,  -6.,  -7.,  -6.,   5.,  -3.,   6.,   1.,   8.,
          6.,  -9.,   1.,   0., -10.,  -1.,   4.,  -5.,   2.,  -5.,   5.,
         -7.,  -2.,   9.,   0.,  -5.,  -4.,  -5.,   0.,   8., -10.,   1.,
         -3.,  -6.,   9.,  -6.,   2.,  -8.,  -5.],
       [-10.,  -4.,   5.,   0.,   0.,  -2.,   8.,   3.,   5.,  -7.,  -7.,
         -8.,  -7.,  -4.,   0.,   6.,   1.,  -3.,  -1.,   0.,   1.,  -9.,
         -7.,   5.,  -7.,  -1., -10.,   6.,  -7., -10.,  -4.,  -3.,   2.,
         -7.,   9.,  -6.,  -7.,   6.,   3.,  -2.],
       [  5.,  -6.,   8.,   3.,  -7.,   6.,   9.,   4.,   3.,   7.,   8.,
          1.,  -5.,   8.,   2.,   4.,  -1., -10.,  -4.,   5.,  -1.,   6.,
         -7.,  -9., -10.,   3.,  -8.,   2.,  -5.,  -4.,   6.,   3.,  -2.,
          6.,   4.,  -5.,   0.,   9., -10.,  -9.],
       [ -4.,   7.,  -1.,  -4.,  -5.,  -4.,  -6.,   7.,   2.,  -9.,   2.,
          5.,   1.,   3.,  -9.,   0.,  -7.,   0.,  -8.,  -4.,   7.,   2.,
         -8.,  -8.,   4.,  -9.,  -1.,   0.,   8.,   2.,   7.,  -2.,   9.,
         -3.,   0.,  -8.,   3.,   1.,   3.,  -5.]], dtype=float32)>, 'per_arm': <tf.Tensor: shape=(20, 70, 50), dtype=float32, numpy=
array([[[-4.,  4.,  3., ..., -3.,  5.,  0.],
        [ 0.,  0., -3., ..., -6., -6.,  0.],
        [ 5.,  0.,  0., ..., -1., -6., -5.],
        ...,
        [-5., -4.,  5., ...,  1.,  3.,  0.],
        [ 3., -3., -5., ..., -4.,  0.,  1.],
        [ 4.,  3.,  2., ...,  3.,  5.,  4.]],

       [[ 0.,  5., -2., ...,  1., -4., -6.],
        [-5., -6.,  4., ...,  3., -2., -5.],
        [ 4.,  2.,  0., ...,  4., -4., -5.],
        ...,
        [-5.,  0., -3., ...,  0., -2.,  5.],
        [ 4.,  1., -4., ..., -1.,  5., -1.],
        [-5., -4., -6., ..., -4., -4.,  4.]],

       [[ 4.,  1.,  1., ...,  1.,  4.,  4.],
        [ 5., -5., -4., ..., -5., -5.,  3.],
        [-3.,  4., -1., ..., -4., -6.,  3.],
        ...,
        [-2., -4.,  3., ...,  0., -5.,  3.],
        [-1.,  1.,  4., ...,  3., -6., -3.],
        [-2., -1.,  2., ...,  2.,  2.,  3.]],

       ...,

       [[-4.,  4., -1., ..., -6., -5., -2.],
        [-4., -1., -1., ...,  3.,  0.,  3.],
        [-3., -5., -6., ...,  5., -4.,  0.],
        ...,
        [ 3.,  0., -1., ...,  1., -6., -5.],
        [-4.,  3.,  4., ...,  5., -6., -1.],
        [ 2.,  2., -3., ..., -6.,  1.,  4.]],

       [[-1., -2.,  0., ..., -2., -5., -2.],
        [-4.,  3., -3., ...,  4., -1.,  3.],
        [-4., -5., -2., ..., -5.,  5.,  1.],
        ...,
        [ 0.,  2., -1., ...,  5., -5.,  3.],
        [-3.,  5., -1., ..., -5., -4., -2.],
        [-2., -4.,  1., ...,  5.,  5., -4.]],

       [[-1., -1., -5., ...,  1.,  1., -6.],
        [-1.,  4.,  3., ...,  1., -3., -1.],
        [-4., -2.,  0., ..., -6., -4.,  0.],
        ...,
        [ 1.,  0.,  5., ...,  4.,  2., -3.],
        [ 4.,  0.,  3., ..., -5., -3.,  5.],
        [-2.,  4., -5., ...,  0., -2., -3.]]], dtype=float32)>}

Rewards after taking an action:  tf.Tensor(
[-162.36607   -41.31555   262.59555  -114.683365  369.00427   365.1768
 -251.67975   505.18155  -343.5173    401.40985   286.79205  -214.25279
  136.54169   259.2176   -414.68408   -14.064158  474.0389   -216.08818
  -22.4924    184.67035 ], shape=(20,), dtype=float32)

Мы видим, что спецификация наблюдения представляет собой словарь с двумя элементами:

  • Один с ключом 'global' : это часть глобального контекста, форма которой соответствует параметру GLOBAL_DIM .
  • Один с ключом 'per_arm' : это контекст для каждой руки, его форма [NUM_ACTIONS, PER_ARM_DIM] . Эта часть является заполнителем для функций руки для каждой руки на временном шаге.

Агент LinUCB

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

Создание агента требует знания наблюдения и спецификации действий. При определении агента мы устанавливаем для логического параметра accepts_per_arm_features значение True .

observation_spec = per_arm_tf_env.observation_spec()
time_step_spec = ts.time_step_spec(observation_spec)
action_spec = tensor_spec.BoundedTensorSpec(
    dtype=tf.int32, shape=(), minimum=0, maximum=NUM_ACTIONS - 1)

agent = lin_ucb_agent.LinearUCBAgent(time_step_spec=time_step_spec,
                                     action_spec=action_spec,
                                     accepts_per_arm_features=True)

Поток обучающих данных

В этом разделе дается краткое представление о том, как отдельные функции переходят от политики к обучению. Не стесняйтесь переходить к следующему разделу (Определение метрики сожаления) и возвращаться сюда позже, если интересно.

Во-первых, давайте посмотрим на спецификацию данных в агенте. Атрибут training_data_spec агента определяет, какие элементы и структуру должны иметь обучающие данные.

print('training data spec: ', agent.training_data_spec)
training data spec:  Trajectory(
{'action': BoundedTensorSpec(shape=(), dtype=tf.int32, name=None, minimum=array(0, dtype=int32), maximum=array(69, dtype=int32)),
 'discount': BoundedTensorSpec(shape=(), dtype=tf.float32, name='discount', minimum=array(0., dtype=float32), maximum=array(1., dtype=float32)),
 'next_step_type': TensorSpec(shape=(), dtype=tf.int32, name='step_type'),
 'observation': {'global': TensorSpec(shape=(40,), dtype=tf.float32, name=None)},
 'policy_info': PerArmPolicyInfo(log_probability=(), predicted_rewards_mean=(), predicted_rewards_optimistic=(), predicted_rewards_sampled=(), bandit_policy_type=(), chosen_arm_features=TensorSpec(shape=(50,), dtype=tf.float32, name=None)),
 'reward': TensorSpec(shape=(), dtype=tf.float32, name='reward'),
 'step_type': TensorSpec(shape=(), dtype=tf.int32, name='step_type')})

Если мы внимательно посмотрим на observation часть спецификации, мы увидим, что она не содержит отдельных функций!

print('observation spec in training: ', agent.training_data_spec.observation)
observation spec in training:  {'global': TensorSpec(shape=(40,), dtype=tf.float32, name=None)}

Что случилось с функциями рук? Чтобы ответить на этот вопрос, сначала отметим, что при обучении агент LinUCB не нуждается в функциях каждой руки для всех рук, ему нужны только функции выбранной руки. Следовательно, имеет смысл отбросить тензор формы [BATCH_SIZE, NUM_ACTIONS, PER_ARM_DIM] , поскольку это очень расточительно, особенно если количество действий велико.

Но все же, особенности выбранной руки должны быть где-то! С этой целью мы гарантируем, что политика LinUCB хранит характеристики выбранной руки в поле policy_info обучающих данных:

print('chosen arm features: ', agent.training_data_spec.policy_info.chosen_arm_features)
chosen arm features:  TensorSpec(shape=(50,), dtype=tf.float32, name=None)

Из фигуры chosen_arm_features поле chosen_arm_features имеет вектор признаков только одной руки, и это будет выбранная рука. Обратите внимание, что policy_info и вместе с ним chosen_arm_features являются частью обучающих данных, как мы видели при проверке спецификации обучающих данных, и поэтому они доступны во время обучения.

Определение метрики сожаления

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

def _all_rewards(observation, hidden_param):
  """Outputs rewards for all actions, given an observation."""
  hidden_param = tf.cast(hidden_param, dtype=tf.float32)
  global_obs = observation['global']
  per_arm_obs = observation['per_arm']
  num_actions = tf.shape(per_arm_obs)[1]
  tiled_global = tf.tile(
      tf.expand_dims(global_obs, axis=1), [1, num_actions, 1])
  concatenated = tf.concat([tiled_global, per_arm_obs], axis=-1)
  rewards = tf.linalg.matvec(concatenated, hidden_param)
  return rewards

def optimal_reward(observation):
  """Outputs the maximum expected reward for every element in the batch."""
  return tf.reduce_max(_all_rewards(observation, reward_param), axis=1)

regret_metric = tf_bandit_metrics.RegretMetric(optimal_reward)

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

num_iterations = 20 # @param
steps_per_loop = 1 # @param

replay_buffer = tf_uniform_replay_buffer.TFUniformReplayBuffer(
    data_spec=agent.policy.trajectory_spec,
    batch_size=BATCH_SIZE,
    max_length=steps_per_loop)

observers = [replay_buffer.add_batch, regret_metric]

driver = dynamic_step_driver.DynamicStepDriver(
    env=per_arm_tf_env,
    policy=agent.collect_policy,
    num_steps=steps_per_loop * BATCH_SIZE,
    observers=observers)

regret_values = []

for _ in range(num_iterations):
  driver.run()
  loss_info = agent.train(replay_buffer.gather_all())
  replay_buffer.clear()
  regret_values.append(regret_metric.result())
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tf_agents/drivers/dynamic_step_driver.py:206: calling while_loop_v2 (from tensorflow.python.ops.control_flow_ops) with back_prop=False is deprecated and will be removed in a future version.
Instructions for updating:
back_prop=False is deprecated. Consider using tf.stop_gradient instead.
Instead of:
results = tf.while_loop(c, b, vars, back_prop=False)
Use:
results = tf.nest.map_structure(tf.stop_gradient, tf.while_loop(c, b, vars))
WARNING:tensorflow:From <ipython-input-1-3fff329da73f>:21: ReplayBuffer.gather_all (from tf_agents.replay_buffers.replay_buffer) is deprecated and will be removed in a future version.
Instructions for updating:
Use `as_dataset(..., single_deterministic_pass=True)` instead.

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

plt.plot(regret_values)
plt.title('Regret of LinUCB on the Linear per-arm environment')
plt.xlabel('Number of Iterations')
_ = plt.ylabel('Average Regret')

PNG

Что дальше?

Приведенный выше пример реализован в нашей кодовой базе, где вы также можете выбирать из других агентов, включая агент Neural epsilon-Greedy .