<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Copyright 2020 The TensorFlow Authors.
Step1:  Actor-Critic 法による CartPole の実験
<table class="tfo-notebook-buttons" align="left">
  <td>     <a target="_blank" href="https
Step4:  モデル
アクターとクリティックは、アクションの確率とクリティック値をそれぞれ生成する 1 つのニューラルネットワークを使ってモデル化されます。このチュートリアルでは、モデルの定義にモデルのサブクラス化を使用します。
フォワードパス中、モデルは、状態を入力として取り、アクション確率とクリティック値 $V$ の両方を出力し、これによって状態に依存する価値関数を形成します。期待される戻り値を最大化するポリシー $\pi$ に基づいてアクションを選択するモデルをトレーニングするのが目標です。
Cartpole-v0 では、状態は 4 つの値で表現されます。カートの位置、カートの速度、ポールの角度、およびポールの速度です。エージェントは 2 つのアクションを取って、カートを左（0）と右（1）に押します。
詳細については、OpenAI Gym の CartPole-v0 ウィキページをご覧ください。
Step7:  トレーニング
エージェントをトレーニングするには、次の手順を実行します。
環境でエージェントを実行し、エピソードごとのトレーニングデータを収集します。
時間ステップごとに期待される戻り値を計算します。
アクターとクリティックを合わせたモデルの損失を計算します。
勾配を計算し、ネットワークパラメーターを更新します。
成功基準または最大エピソード数に達するまで、1～4 の手順を繰り返します。
1. トレーニングデータを収集する
アクタークリティックモデルのトレーニングでは、教師あり学習と同様にトレーニングデータが必要です。ただし、そのようなデータを収集するには、モデルを環境で「実行」する必要があります。
トレーニングデータは、エピソードごとに収集されます。次に、モデルの重みによってパラメーター化された現在のポリシーに基づいてアクションの確率とクリティック値を生成するために、時間ステップごとにモデルのフォワードパスが環境の状態で実行されます。
次のアクションはモデルが生成したアクション確率からサンプリングされます。これが環境に適用されると、次の状態と報酬が生成されます。
このプロセスは、run_episode 関数に実装されます。後で TensorFlow グラフにコンパイルしてトレーニングを加速化できるように、TensorFlow 演算が使用されています。可変長配列でテンソルをイテレーションできるように、tf.TensorArray が使用されていることに注意してください。
Step9:  2. 期待される戻り値を計算する
1 つのエピソード中に収集される各時間ステップ $t$ の報酬のシーケンス ${r_{t}}^{T}{t=1}$ は、期待される戻り値のシーケンス ${G{t}}^{T}_{t=1}$ に変換されます。ここで、報酬の合計は現在の時間ステップ $t$ ～ $T$ から取得され、各報酬は指数関数で気に減衰するディスカウント要因 $\gamma$ で乗算されます。
$$G_{t} = \sum^{T}{t'=t} \gamma^{t'-t}r{t'}$$
$\gamma\in(0,1)$ であるため、現在の時間ステップ以降の報酬には与えられる重みは、徐々に少なくなります。
直感的に、期待される戻り値は単に、現在の報酬が後の報酬より良いことを示しています。数学的に見れば、報酬の合計が収束することが保証されています。
トレーニングを安定化するには、生成される戻り値のシーケンスも標準化されます（つまり、平均と単位標準偏差がゼロ）。
Step11:  3. アクタークリティックの損失
ハイブリッドのアクタークリティックモデルが使用されているため、選択される損失関数は、以下のように、トレーニング用のアクター損失とクリティック損失の合計です。
$$L = L_{actor} + L_{critic}$$
アクター損失
アクター損失は、クリティックを状態依存のベースラインとしたポリシー勾配に基づき、単一サンプル（エピソード単位）の推定で計算されます。
$$L_{actor} = -\sum^{T}{t=1} log\pi{\theta}(a_{t} | s_{t})[G(s_{t}, a_{t})  - V^{\pi}{\theta}(s{t})]$$
上記は以下を意味します。
$T$
Step13:  4. パラメーターを更新するようにトレーニングステップを定義する
上記のすべてのステップは、エピソードごとに実行されるトレーニングステップに結合されます。損失関数に導くすべてのステップは、自動微分を可能にする tf.GradientTape コンテキストで実行されます。
このチュートリアルでは、Adam オプティマイザを使って勾配をモデルパラメーターに適用します。
ディスカウントされていない報酬の合計 episode_reward も、このステップで計算されます。この値は、成功基準が満たされるかどうかを評価するために、後で使用されます。
tf.function コンテキストは train_step 関数に適用することで、コーラブル TenSorFlow グラフにコンパイルできるようなります。そうすることで、トレーニングを 10 倍加速させることができます。
Step14:  5. トレーニングツールを実行する
トレーニングは、成功基準またはエピソードの最大数に達するまでトレーニングステップを実行することで、実行されます。
エピソード報酬の実行中の記録はキューに保持されます。100 トライアルに達したら、キューの左終端（テール）から最も古い報酬が削除され、キューの右（ヘッド）に最も新しい報酬が追加されます。実行中の報酬の合計も、計算の効率を得るために管理されます。
ランタイムによっては、トレーニングを 1 分未満で完了することもできます。
Step15:  視覚化
トレーニングが終わったら、モデルが環境でどのように実行するかを視覚化すると良いでしょう。以下のセルを実行すると、モデルの 1 エピソードの実行を視覚化する GIF アニメーションを生成することができます。Colab で環境の画像を正しくレンダリングするには、OpenAI Gym の追加パッケージをインストールする必要があることに注意してください。
<ASSISTANT_TASK:>
Python Code:
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Explanation: Copyright 2020 The TensorFlow Authors.
End of explanation
!pip install gym
!pip install pyglet
%%bash
# Install additional packages for visualization
sudo apt-get install -y xvfb python-opengl > /dev/null 2>&1
pip install pyvirtualdisplay > /dev/null 2>&1
pip install git+https://github.com/tensorflow/docs > /dev/null 2>&1
import collections
import gym
import numpy as np
import statistics
import tensorflow as tf
import tqdm
from matplotlib import pyplot as plt
from tensorflow.keras import layers
from typing import Any, List, Sequence, Tuple
# Create the environment
env = gym.make("CartPole-v0")
# Set seed for experiment reproducibility
seed = 42
env.seed(seed)
tf.random.set_seed(seed)
np.random.seed(seed)
# Small epsilon value for stabilizing division operations
eps = np.finfo(np.float32).eps.item()
Explanation: Actor-Critic 法による CartPole の実験
<table class="tfo-notebook-buttons" align="left">
  <td>     <a target="_blank" href="https://www.tensorflow.org/tutorials/reinforcement_learning/actor_critic">     <img src="https://www.tensorflow.org/images/tf_logo_32px.png">    TensorFlow.org で表示</a>   </td>
  <td>     <a target="_blank" href="https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/tutorials/reinforcement_learning/actor_critic.ipynb">     <img src="https://www.tensorflow.org/images/colab_logo_32px.png">    Google Colab で実行</a>   </td>
  <td>     <a target="_blank" href="https://github.com/tensorflow/docs-l10n/blob/master/site/ja/tutorials/reinforcement_learning/actor_critic.ipynb">     <img src="https://www.tensorflow.org/images/GitHub-Mark-32px.png">    GitHub でソースを表示</a>   </td>
  <td>     <a href="https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/tutorials/reinforcement_learning/actor_critic.ipynb"><img src="https://www.tensorflow.org/images/download_logo_32px.png">ノートブックをダウンロード</a>   </td>
</table>
このチュートリアルでは、強化学習のポリシー勾配メソッドを理解していることを前提に、TensorFlow を使用して Actor-Critic 法を実装し、Open AI Gym CartPole-V0 環境のエージェントをトレーニングする方法を示します。
Actor-Critic 法
Actor-Critic 法は、価値関数から独立してポリシー関数を表すTD（時間的差分）学習の手法です。
ポリシー関数（またはポリシー）は、ある特定の状態に基づいてエージェントが実行できるアクションの確率分布を返します。価値関数は、特定の状態で開始し、その後永久に特定のポリシーに従って動作するエージェントの期待される戻り値を決定します。
Actor-Critic 法では、ポリシーは状態に応じて一連の可能なアクションを提案する「アクター」と呼ばれます。推定される価値関数は「クリティック’と呼ばれ、特定のポリシーに基づいて「アクター」が実行するアクションを評価します。
このチュートリアルでは、アクターとクリティックは、2 つの出力を持つ 1 つのニューラルネットワークを使って表現されます。
CartPole-v0
CartPole-v0 環境では、ポールは摩擦の無いレール上を移動するカートに取り付けられています。ポールは直立状態で始まり、エージェントの目標は、カートに -1 または +1 の力を加えてポールが倒れないようにすることです。ポールが直立状態を維持する時間ステップごとに +1 の報酬が与えられます。エピソードは、（1）ポールが直立から 15 度以上に傾斜したとき、または（2）カートが中央から 2.4 ユニット以上移動したときに、終了します。
<center>
<pre data-md-type="custom_pre">&lt;figure&gt;
    &lt;image src="images/cartpole-v0.gif"&gt;
    &lt;figcaption&gt;
      Trained actor-critic model in Cartpole-v0 environment
    &lt;/figcaption&gt;
  &lt;/figure&gt;</pre>
</center>
この問題は、100 回の連続トライアルにおいて、エピソードの平均合計報酬が 195 に達すると「解決」とみなされます。
セットアップ
必要なパッケージをインポートし、グローバル設定を構成します。
End of explanation
class ActorCritic(tf.keras.Model):
  Combined actor-critic network.
  def __init__(
      self, 
      num_actions: int, 
      num_hidden_units: int):
    Initialize.
    super().__init__()
    self.common = layers.Dense(num_hidden_units, activation="relu")
    self.actor = layers.Dense(num_actions)
    self.critic = layers.Dense(1)
  def call(self, inputs: tf.Tensor) -> Tuple[tf.Tensor, tf.Tensor]:
    x = self.common(inputs)
    return self.actor(x), self.critic(x)
num_actions = env.action_space.n  # 2
num_hidden_units = 128
model = ActorCritic(num_actions, num_hidden_units)
Explanation: モデル
アクターとクリティックは、アクションの確率とクリティック値をそれぞれ生成する 1 つのニューラルネットワークを使ってモデル化されます。このチュートリアルでは、モデルの定義にモデルのサブクラス化を使用します。
フォワードパス中、モデルは、状態を入力として取り、アクション確率とクリティック値 $V$ の両方を出力し、これによって状態に依存する価値関数を形成します。期待される戻り値を最大化するポリシー $\pi$ に基づいてアクションを選択するモデルをトレーニングするのが目標です。
Cartpole-v0 では、状態は 4 つの値で表現されます。カートの位置、カートの速度、ポールの角度、およびポールの速度です。エージェントは 2 つのアクションを取って、カートを左（0）と右（1）に押します。
詳細については、OpenAI Gym の CartPole-v0 ウィキページをご覧ください。
End of explanation
# Wrap OpenAI Gym's `env.step` call as an operation in a TensorFlow function.
# This would allow it to be included in a callable TensorFlow graph.
def env_step(action: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
  Returns state, reward and done flag given an action.
  state, reward, done, _ = env.step(action)
  return (state.astype(np.float32), 
          np.array(reward, np.int32), 
          np.array(done, np.int32))
def tf_env_step(action: tf.Tensor) -> List[tf.Tensor]:
  return tf.numpy_function(env_step, [action], 
                           [tf.float32, tf.int32, tf.int32])
def run_episode(
    initial_state: tf.Tensor,  
    model: tf.keras.Model, 
    max_steps: int) -> Tuple[tf.Tensor, tf.Tensor, tf.Tensor]:
  Runs a single episode to collect training data.
  action_probs = tf.TensorArray(dtype=tf.float32, size=0, dynamic_size=True)
  values = tf.TensorArray(dtype=tf.float32, size=0, dynamic_size=True)
  rewards = tf.TensorArray(dtype=tf.int32, size=0, dynamic_size=True)
  initial_state_shape = initial_state.shape
  state = initial_state
  for t in tf.range(max_steps):
    # Convert state into a batched tensor (batch size = 1)
    state = tf.expand_dims(state, 0)
  
    # Run the model and to get action probabilities and critic value
    action_logits_t, value = model(state)
  
    # Sample next action from the action probability distribution
    action = tf.random.categorical(action_logits_t, 1)[0, 0]
    action_probs_t = tf.nn.softmax(action_logits_t)
    # Store critic values
    values = values.write(t, tf.squeeze(value))
    # Store log probability of the action chosen
    action_probs = action_probs.write(t, action_probs_t[0, action])
  
    # Apply action to the environment to get next state and reward
    state, reward, done = tf_env_step(action)
    state.set_shape(initial_state_shape)
  
    # Store reward
    rewards = rewards.write(t, reward)
    if tf.cast(done, tf.bool):
      break
  action_probs = action_probs.stack()
  values = values.stack()
  rewards = rewards.stack()
  
  return action_probs, values, rewards
Explanation: トレーニング
エージェントをトレーニングするには、次の手順を実行します。
環境でエージェントを実行し、エピソードごとのトレーニングデータを収集します。
時間ステップごとに期待される戻り値を計算します。
アクターとクリティックを合わせたモデルの損失を計算します。
勾配を計算し、ネットワークパラメーターを更新します。
成功基準または最大エピソード数に達するまで、1～4 の手順を繰り返します。
1. トレーニングデータを収集する
アクタークリティックモデルのトレーニングでは、教師あり学習と同様にトレーニングデータが必要です。ただし、そのようなデータを収集するには、モデルを環境で「実行」する必要があります。
トレーニングデータは、エピソードごとに収集されます。次に、モデルの重みによってパラメーター化された現在のポリシーに基づいてアクションの確率とクリティック値を生成するために、時間ステップごとにモデルのフォワードパスが環境の状態で実行されます。
次のアクションはモデルが生成したアクション確率からサンプリングされます。これが環境に適用されると、次の状態と報酬が生成されます。
このプロセスは、run_episode 関数に実装されます。後で TensorFlow グラフにコンパイルしてトレーニングを加速化できるように、TensorFlow 演算が使用されています。可変長配列でテンソルをイテレーションできるように、tf.TensorArray が使用されていることに注意してください。
End of explanation
def get_expected_return(
    rewards: tf.Tensor, 
    gamma: float, 
    standardize: bool = True) -> tf.Tensor:
  Compute expected returns per timestep.
  n = tf.shape(rewards)[0]
  returns = tf.TensorArray(dtype=tf.float32, size=n)
  # Start from the end of `rewards` and accumulate reward sums
  # into the `returns` array
  rewards = tf.cast(rewards[::-1], dtype=tf.float32)
  discounted_sum = tf.constant(0.0)
  discounted_sum_shape = discounted_sum.shape
  for i in tf.range(n):
    reward = rewards[i]
    discounted_sum = reward + gamma * discounted_sum
    discounted_sum.set_shape(discounted_sum_shape)
    returns = returns.write(i, discounted_sum)
  returns = returns.stack()[::-1]
  if standardize:
    returns = ((returns - tf.math.reduce_mean(returns)) / 
               (tf.math.reduce_std(returns) + eps))
  return returns
Explanation: 2. 期待される戻り値を計算する
1 つのエピソード中に収集される各時間ステップ $t$ の報酬のシーケンス ${r_{t}}^{T}{t=1}$ は、期待される戻り値のシーケンス ${G{t}}^{T}_{t=1}$ に変換されます。ここで、報酬の合計は現在の時間ステップ $t$ ～ $T$ から取得され、各報酬は指数関数で気に減衰するディスカウント要因 $\gamma$ で乗算されます。
$$G_{t} = \sum^{T}{t'=t} \gamma^{t'-t}r{t'}$$
$\gamma\in(0,1)$ であるため、現在の時間ステップ以降の報酬には与えられる重みは、徐々に少なくなります。
直感的に、期待される戻り値は単に、現在の報酬が後の報酬より良いことを示しています。数学的に見れば、報酬の合計が収束することが保証されています。
トレーニングを安定化するには、生成される戻り値のシーケンスも標準化されます（つまり、平均と単位標準偏差がゼロ）。
End of explanation
huber_loss = tf.keras.losses.Huber(reduction=tf.keras.losses.Reduction.SUM)
def compute_loss(
    action_probs: tf.Tensor,  
    values: tf.Tensor,  
    returns: tf.Tensor) -> tf.Tensor:
  Computes the combined actor-critic loss.
  advantage = returns - values
  action_log_probs = tf.math.log(action_probs)
  actor_loss = -tf.math.reduce_sum(action_log_probs * advantage)
  critic_loss = huber_loss(values, returns)
  return actor_loss + critic_loss
Explanation: 3. アクタークリティックの損失
ハイブリッドのアクタークリティックモデルが使用されているため、選択される損失関数は、以下のように、トレーニング用のアクター損失とクリティック損失の合計です。
$$L = L_{actor} + L_{critic}$$
アクター損失
アクター損失は、クリティックを状態依存のベースラインとしたポリシー勾配に基づき、単一サンプル（エピソード単位）の推定で計算されます。
$$L_{actor} = -\sum^{T}{t=1} log\pi{\theta}(a_{t} | s_{t})[G(s_{t}, a_{t})  - V^{\pi}{\theta}(s{t})]$$
上記は以下を意味します。
$T$: エピソードごとの時間ステップの数。エピソードごとにことなります。
$s_{t}$: 時間ステップ $t$ における状態。
$a_{t}$: 状態 $s$ の場合の時間ステップ $t$ で選択されたアクション。
$\pi_{\theta}$: $\theta$ でパラメーター化されたポリシー（アクター）。
$V^{\pi}_{\theta}$: $\theta$ でパラメーター化された価値関数（クリティック）。
$G = G_{t}$: 時間ステップ $t$ において特定の状態とアクションに対して期待される戻り値。
組み合わせの損失を最小限に抑えることでアクションがより高い報酬を生み出す確率を最大化しようとしているため、合計に負の項が追加されます。
<br>
アドバンテージ
$L_{actor}$ 式の $G - V$ の項はアドバンテージと呼ばれ、特定の状態において、あるアクションが、その状態のポリシー $\pi$ に従って選択されたランダムアクションと比べてどれくらい優れているかを示します。
ベースラインを除外することは可能ですが、除外した場合、トレーニング中のバリアンスが高くなってしまう可能性があります。また、ベースラインとしてクリティック $V$ を選択すると、できる限り $G$ に近くなるようにトレーニングされるため、バリアンスがより低くなります。
さらに、クリティックがなければ、アルゴリズムは特定の状態で実行されるアクションの確率を期待される戻り値に応じて高めようとするため、アクション間の相対的な確率が同じままである場合、結果はあまり変わりません。
たとえば、特定の状態における 2 つのアクションが同じ期待される戻り値を生成したとします。クリティックがなければ、アルゴリズムは客観的な $J$ に基づき、これらのアクションの確率をあげようとします。クリティックがあれば、アドバンテージがなく（$G - V = 0$）、そのためアクションの確率を上げることにアドバンテージがなく、アルゴリズムは勾配をゼロに設定します。
<br>
クリティックの損失
$V$ ができる限り $G$ に近づくようにトレーニングするには、次の損失関数で回帰問題としてセットアップできます。
$$L_{critic} = L_{\delta}(G, V^{\pi}_{\theta})$$
上記の $L_{\delta}$ は Huber 損失でこれは、二乗誤差損失よりもデータの外れ値に対する感度が低くなります。
End of explanation
optimizer = tf.keras.optimizers.Adam(learning_rate=0.01)
@tf.function
def train_step(
    initial_state: tf.Tensor, 
    model: tf.keras.Model, 
    optimizer: tf.keras.optimizers.Optimizer, 
    gamma: float, 
    max_steps_per_episode: int) -> tf.Tensor:
  Runs a model training step.
  with tf.GradientTape() as tape:
    # Run the model for one episode to collect training data
    action_probs, values, rewards = run_episode(
        initial_state, model, max_steps_per_episode) 
    # Calculate expected returns
    returns = get_expected_return(rewards, gamma)
    # Convert training data to appropriate TF tensor shapes
    action_probs, values, returns = [
        tf.expand_dims(x, 1) for x in [action_probs, values, returns]] 
    # Calculating loss values to update our network
    loss = compute_loss(action_probs, values, returns)
  # Compute the gradients from the loss
  grads = tape.gradient(loss, model.trainable_variables)
  # Apply the gradients to the model's parameters
  optimizer.apply_gradients(zip(grads, model.trainable_variables))
  episode_reward = tf.math.reduce_sum(rewards)
  return episode_reward
Explanation: 4. パラメーターを更新するようにトレーニングステップを定義する
上記のすべてのステップは、エピソードごとに実行されるトレーニングステップに結合されます。損失関数に導くすべてのステップは、自動微分を可能にする tf.GradientTape コンテキストで実行されます。
このチュートリアルでは、Adam オプティマイザを使って勾配をモデルパラメーターに適用します。
ディスカウントされていない報酬の合計 episode_reward も、このステップで計算されます。この値は、成功基準が満たされるかどうかを評価するために、後で使用されます。
tf.function コンテキストは train_step 関数に適用することで、コーラブル TenSorFlow グラフにコンパイルできるようなります。そうすることで、トレーニングを 10 倍加速させることができます。
End of explanation
%%time
min_episodes_criterion = 100
max_episodes = 10000
max_steps_per_episode = 1000
# Cartpole-v0 is considered solved if average reward is >= 195 over 100 
# consecutive trials
reward_threshold = 195
running_reward = 0
# Discount factor for future rewards
gamma = 0.99
# Keep last episodes reward
episodes_reward: collections.deque = collections.deque(maxlen=min_episodes_criterion)
with tqdm.trange(max_episodes) as t:
  for i in t:
    initial_state = tf.constant(env.reset(), dtype=tf.float32)
    episode_reward = int(train_step(
        initial_state, model, optimizer, gamma, max_steps_per_episode))
    
    episodes_reward.append(episode_reward)
    running_reward = statistics.mean(episodes_reward)
  
    t.set_description(f'Episode {i}')
    t.set_postfix(
        episode_reward=episode_reward, running_reward=running_reward)
  
    # Show average episode reward every 10 episodes
    if i % 10 == 0:
      pass # print(f'Episode {i}: average reward: {avg_reward}')
  
    if running_reward > reward_threshold and i >= min_episodes_criterion:  
        break
print(f'\nSolved at episode {i}: average reward: {running_reward:.2f}!')
Explanation: 5. トレーニングツールを実行する
トレーニングは、成功基準またはエピソードの最大数に達するまでトレーニングステップを実行することで、実行されます。
エピソード報酬の実行中の記録はキューに保持されます。100 トライアルに達したら、キューの左終端（テール）から最も古い報酬が削除され、キューの右（ヘッド）に最も新しい報酬が追加されます。実行中の報酬の合計も、計算の効率を得るために管理されます。
ランタイムによっては、トレーニングを 1 分未満で完了することもできます。
End of explanation
# Render an episode and save as a GIF file
from IPython import display as ipythondisplay
from PIL import Image
from pyvirtualdisplay import Display
display = Display(visible=0, size=(400, 300))
display.start()
def render_episode(env: gym.Env, model: tf.keras.Model, max_steps: int): 
  screen = env.render(mode='rgb_array')
  im = Image.fromarray(screen)
  images = [im]
  
  state = tf.constant(env.reset(), dtype=tf.float32)
  for i in range(1, max_steps + 1):
    state = tf.expand_dims(state, 0)
    action_probs, _ = model(state)
    action = np.argmax(np.squeeze(action_probs))
    state, _, done, _ = env.step(action)
    state = tf.constant(state, dtype=tf.float32)
    # Render screen every 10 steps
    if i % 10 == 0:
      screen = env.render(mode='rgb_array')
      images.append(Image.fromarray(screen))
  
    if done:
      break
  
  return images
# Save GIF image
images = render_episode(env, model, max_steps_per_episode)
image_file = 'cartpole-v0.gif'
# loop=0: loop forever, duration=1: play each frame for 1ms
images[0].save(
    image_file, save_all=True, append_images=images[1:], loop=0, duration=1)
import tensorflow_docs.vis.embed as embed
embed.embed_file(image_file)
Explanation: 視覚化
トレーニングが終わったら、モデルが環境でどのように実行するかを視覚化すると良いでしょう。以下のセルを実行すると、モデルの 1 エピソードの実行を視覚化する GIF アニメーションを生成することができます。Colab で環境の画像を正しくレンダリングするには、OpenAI Gym の追加パッケージをインストールする必要があることに注意してください。
End of explanation
<END_TASK>