#@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 2021 The TF-Agents Authors.
End of explanation
"""


!pip install tf-agents

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import abc
import tensorflow as tf
import numpy as np

from tf_agents.environments import random_py_environment
from tf_agents.environments import tf_py_environment
from tf_agents.networks import encoding_network
from tf_agents.networks import network
from tf_agents.networks import utils
from tf_agents.specs import array_spec
from tf_agents.utils import common as common_utils
from tf_agents.utils import nest_utils

"""
Explanation: 网络
<table class="tfo-notebook-buttons" align="left">
  <td>     <a target="_blank" href="https://tensorflow.google.cn/agents/tutorials/8_networks_tutorial"><img src="https://tensorflow.google.cn/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/zh-cn/agents/tutorials/8_networks_tutorial.ipynb"><img src="https://tensorflow.google.cn/images/colab_logo_32px.png">在 Google Colab 中运行</a></td>
  <td>     <a target="_blank" href="https://github.com/tensorflow/docs-l10n/blob/master/site/zh-cn/agents/tutorials/8_networks_tutorial.ipynb"><img src="https://tensorflow.google.cn/images/GitHub-Mark-32px.png">在 Github 上查看源代码</a>
</td>
  <td>     <a href="https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/zh-cn/agents/tutorials/8_networks_tutorial.ipynb"><img src="https://tensorflow.google.cn/images/download_logo_32px.png">下载笔记本</a>   </td>
</table>

简介
在此 Colab 中，我们将介绍如何为您的代理定义自定义网络。网络可以帮助我们定义由代理训练的模型。在 TF-Agents 中，您会发现几种不同类型的网络，这些网络在代理间十分有用：
主要网络

QNetwork：在 Qlearning 中用于具有离散操作的环境，该网络会将观测值映射到每个可能操作的值估算。
CriticNetworks：在文献中也称为 ValueNetworks，它会学习估算某个版本的 Value 函数，Value 函数可将某种状态映射到策略预期回报的估算。这些网络可以估算代理当前处于何种状态。
ActorNetworks：学习从观测值到操作的映射。我们的策略通常使用这些网络来生成操作。
ActorDistributionNetworks：类似于 ActorNetworks，但它们会生成一个分布，随后策略可以对该分布进行采样以生成操作。

辅助网络

EncodingNetwork：允许用户轻松定义预处理层的映射以应用于网络的输入。
DynamicUnrollLayer：在按时间顺序应用时，自动在片段边界上重置网络状态。
ProjectionNetwork：像 CategoricalProjectionNetwork 或 NormalProjectionNetwork 这样的网络会获取输入并生成所需的参数，以生成分类分布或正态分布。

TF-Agents 中的所有示例均带有预配置的网络。但是，这些网络的设置不适合处理复杂的观测值。
如果您的环境公开了多个观测值/操作，并且需要自定义网络，那么本教程非常适合您！
设置
如果尚未安装 TF-Agents，请运行以下命令：
End of explanation
"""


class ActorNetwork(network.Network):

  def __init__(self,
               observation_spec,
               action_spec,
               preprocessing_layers=None,
               preprocessing_combiner=None,
               conv_layer_params=None,
               fc_layer_params=(75, 40),
               dropout_layer_params=None,
               activation_fn=tf.keras.activations.relu,
               enable_last_layer_zero_initializer=False,
               name='ActorNetwork'):
    super(ActorNetwork, self).__init__(
        input_tensor_spec=observation_spec, state_spec=(), name=name)

    # For simplicity we will only support a single action float output.
    self._action_spec = action_spec
    flat_action_spec = tf.nest.flatten(action_spec)
    if len(flat_action_spec) > 1:
      raise ValueError('Only a single action is supported by this network')
    self._single_action_spec = flat_action_spec[0]
    if self._single_action_spec.dtype not in [tf.float32, tf.float64]:
      raise ValueError('Only float actions are supported by this network.')

    kernel_initializer = tf.keras.initializers.VarianceScaling(
        scale=1. / 3., mode='fan_in', distribution='uniform')
    self._encoder = encoding_network.EncodingNetwork(
        observation_spec,
        preprocessing_layers=preprocessing_layers,
        preprocessing_combiner=preprocessing_combiner,
        conv_layer_params=conv_layer_params,
        fc_layer_params=fc_layer_params,
        dropout_layer_params=dropout_layer_params,
        activation_fn=activation_fn,
        kernel_initializer=kernel_initializer,
        batch_squash=False)

    initializer = tf.keras.initializers.RandomUniform(
        minval=-0.003, maxval=0.003)

    self._action_projection_layer = tf.keras.layers.Dense(
        flat_action_spec[0].shape.num_elements(),
        activation=tf.keras.activations.tanh,
        kernel_initializer=initializer,
        name='action')

  def call(self, observations, step_type=(), network_state=()):
    outer_rank = nest_utils.get_outer_rank(observations, self.input_tensor_spec)
    # We use batch_squash here in case the observations have a time sequence
    # compoment.
    batch_squash = utils.BatchSquash(outer_rank)
    observations = tf.nest.map_structure(batch_squash.flatten, observations)

    state, network_state = self._encoder(
        observations, step_type=step_type, network_state=network_state)
    actions = self._action_projection_layer(state)
    actions = common_utils.scale_to_spec(actions, self._single_action_spec)
    actions = batch_squash.unflatten(actions)
    return tf.nest.pack_sequence_as(self._action_spec, [actions]), network_state

"""
Explanation: 定义网络
Network API
在 TF-Agents 中，我们从 Keras 网络继承类。借助它，我们可以：

简化创建目标网络时所需的复制操作。
调用 network.variables() 时执行自动变量创建。
根据网络 input_specs 验证输入。

EncodingNetwork 如上所述，EncodingNetwork 让我们能够轻松定义预处理层的映射，以应用于网络输入来生成某种编码。
EncodingNetwork 由以下层（大部分是可选的）组成：

预处理层
预处理组合器
Conv2D
平展
密集

编码网络的特别之处在于应用了输入预处理。输入预处理可以通过 preprocessing_layers 和 preprocessing_combiner 层实现。每个层都可以指定为嵌套结构。如果 preprocessing_layers 嵌套比 input_tensor_spec 浅，则这些层将获得子嵌套。例如，如果：
input_tensor_spec = ([TensorSpec(3)] * 2, [TensorSpec(3)] * 5)
preprocessing_layers = (Layer1(), Layer2())
则预处理将调用：
preprocessed = [preprocessing_layers[0](observations[0]),
                preprocessing_layers[1](observations[1])]
不过，如果
preprocessing_layers = ([Layer1() for _ in range(2)],
                        [Layer2() for _ in range(5)])
则预处理将调用：
python
preprocessed = [
  layer(obs) for layer, obs in zip(flatten(preprocessing_layers),
                                    flatten(observations))
]
自定义网络
要创建自己的网络，只需重写 __init__ 和 call 方法。利用学到的 EncodingNetworks 相关知识，我们创建一个自定义网络 ActorNetwork 来获取包含图像和向量的观测值。
End of explanation
"""


action_spec = array_spec.BoundedArraySpec((3,), np.float32, minimum=0, maximum=10)
observation_spec =  {
    'image': array_spec.BoundedArraySpec((16, 16, 3), np.float32, minimum=0,
                                        maximum=255),
    'vector': array_spec.BoundedArraySpec((5,), np.float32, minimum=-100,
                                          maximum=100)}

random_env = random_py_environment.RandomPyEnvironment(observation_spec, action_spec=action_spec)

# Convert the environment to a TFEnv to generate tensors.
tf_env = tf_py_environment.TFPyEnvironment(random_env)

"""
Explanation: 我们创建一个 RandomPyEnvironment 来生成结构化观测值并验证我们的实现。
End of explanation
"""


preprocessing_layers = {
    'image': tf.keras.models.Sequential([tf.keras.layers.Conv2D(8, 4),
                                        tf.keras.layers.Flatten()]),
    'vector': tf.keras.layers.Dense(5)
    }
preprocessing_combiner = tf.keras.layers.Concatenate(axis=-1)
actor = ActorNetwork(tf_env.observation_spec(), 
                     tf_env.action_spec(),
                     preprocessing_layers=preprocessing_layers,
                     preprocessing_combiner=preprocessing_combiner)

"""
Explanation: 由于我们已经将观测值定义为字典，因此我们需要创建预处理层来处理这些观测值。
End of explanation
"""


time_step = tf_env.reset()
actor(time_step.observation, time_step.step_type)

"""
Explanation: 现在我们有了行动网络，接下来可以处理来自环境的观测值。
End of explanation
"""


