腕ごとの機能を備えた多腕バンディットのチュートリアル

始めましょう

TensorFlow.org で見る Google Colabで実行 GitHub でソースを表示ノートをダウンロード

このチュートリアルは、機能 (ジャンル、リリース年、 ...)。

前提条件

読者は、TF-Agents の Bandit ライブラリにある程度精通しており、特に、このチュートリアルを読む前に、TF-Agents の Banditsチュートリアルを完了していることを前提としています。

アーム機能を備えた多腕バンディット

「古典的な」コンテキスト多腕バンディット設定では、エージェントはタイム ステップごとにコンテキスト ベクトル (別名観測) を受け取り、累積報酬を最大化するために、有限の番号付きアクション (アーム) のセットから選択する必要があります。

次に、エージェントが次に見る映画をユーザーに勧めるシナリオを考えてみましょう。決定を下すたびに、エージェントはユーザーに関するいくつかの情報 (視聴履歴、ジャンルの好みなど) と、選択する映画のリストをコンテキストとして受け取ります。

ユーザー情報をコンテキストとして使用することでこの問題を定式化することができ、アームはmovie_1, movie_2, ..., movie_Kになりますが、このアプローチには複数の欠点があります。

  • アクションの数は、システム内のすべてのムービーでなければならず、新しいムービーを追加するのは面倒です。
  • エージェントは、すべての映画のモデルを学習する必要があります。
  • 映画間の類似性は考慮されていません。

映画に番号を付ける代わりに、もっと直感的なことをすることができます: ジャンル、長さ、出演者、評価、年などを含む一連の特徴で映画を表すことができます.このアプローチの利点は多岐にわたります:

  • 映画全体の一般化。
  • エージェントは、ユーザーと映画の特徴による報酬をモデル化する 1 つの報酬関数のみを学習します。
  • システムから簡単に削除したり、新しい映画をシステムに導入したりできます。

この新しい設定では、アクションの数はすべての時間ステップで同じである必要はありません。

TF-Agents の腕ごとの盗賊

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]))

シンプルなアームごとの環境

他のチュートリアルで説明されている定常確率的環境には、アームごとの対応があります。

アームごとの環境を初期化するには、生成する関数を定義する必要があります

  • global and per-arm features : これらの関数には入力パラメーターがなく、呼び出されると単一の (グローバルまたはアームごとの) 特徴ベクトルが生成されます。
  • 報酬: この関数は、グローバルおよびアームごとの特徴ベクトルの連結をパラメータとして取り、報酬を生成します。基本的に、これはエージェントが「推測」する必要がある機能です。ここで、アームごとの場合、報酬関数はすべてのアームで同じであることに注意してください。これは、エージェントが各アームの報酬関数を個別に推定する必要がある古典的な盗賊のケースとは根本的な違いです。
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)

観測仕様は 2 つの要素を持つ辞書であることがわかります。

  • キー'global'を持つ 1 つ: これはグローバル コンテキスト パーツであり、形状がパラメータGLOBAL_DIM一致します。
  • キー'per_arm' : これはアームごとのコンテキストであり、その形状は[NUM_ACTIONS, PER_ARM_DIM]です。この部分は、タイム ステップのすべてのアームのアーム フィーチャのプレースホルダーです。

LinUCB エージェント

LinUCB エージェントは、同じ名前の Bandit アルゴリズムを実装します。これは、線形報酬関数のパラメーターを推定する一方で、推定値の周りの信頼楕円体も維持します。エージェントは、パラメーターが信頼楕円体内にあると仮定して、推定される予想報酬が最も高い腕を選択します。

エージェントの作成には、観察とアクションの仕様に関する知識が必要です。エージェントを定義するとき、ブール値パラメータaccepts_per_arm_featuresTrue設定し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]形状のテンソル[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フィールドには 1 つの腕の特徴ベクトルしかなく、それが選択された腕になることがchosen_arm_featuresます。 policy_info 、およびそれにchosen_arm_featuresは、トレーニング データの仕様の調査から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 エージェントを含む他のエージェントからも選択できるコードベースに実装されています。