<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:  Keras 中的遮盖和填充
<table class="tfo-notebook-buttons" align="left">
  <td><a target="_blank" href="https
Step2:  简介
遮盖的作用是告知序列处理层输入中有某些时间步骤丢失，因此在处理数据时应将其跳过。
填充是遮盖的一种特殊形式，其中被遮盖的步骤位于序列的起点或开头。填充是出于将序列数据编码成连续批次的需要：为了使批次中的所有序列适合给定的标准长度，有必要填充或截断某些序列。
让我们仔细看看。
填充序列数据
在处理序列数据时，各个样本常常具有不同长度。请考虑以下示例（文本被切分为单词）：
[
  ["Hello", "world", "!"],
  ["How", "are", "you", "doing", "today"],
  ["The", "weather", "will", "be", "nice", "tomorrow"],
]
进行词汇查询后，数据可能会被向量化为整数，例如：
[
  [71, 1331, 4231]
  [73, 8, 3215, 55, 927],
  [83, 91, 1, 645, 1253, 927],
]
此数据是一个嵌套列表，其中各个样本的长度分别为 3、5 和 6。由于深度学习模型的输入数据必须为单一张量（例如在此例中形状为 (batch_size, 6, vocab_size)），短于最长条目的样本需要用占位符值进行填充（或者，也可以在填充短样本前截断长样本）。
Keras 提供了一个效用函数来截断和填充 Python 列表，使其具有相同长度：tf.keras.preprocessing.sequence.pad_sequences。
Step3:  遮盖
既然所有样本现在都具有了统一长度，那就必须告知模型，数据的某些部分实际上是填充，应该忽略。这种机制就是遮盖。
在 Keras 模型中引入输入掩码有三种方式：
添加一个 keras.layers.Masking 层。
使用 mask_zero=True 配置一个 keras.layers.Embedding 层。
在调用支持 mask 参数的层（如 RNN 层）时，手动传递此参数。
掩码生成层：Embedding 和 Masking
这些层将在后台创建一个掩码张量（形状为 (batch, sequence_length) 的二维张量），并将其附加到由 Masking 或 Embedding 层返回的张量输出上。
Step4:  您可以在输出结果中看到，该掩码是一个形状为 (batch_size, sequence_length) 的二维布尔张量，其中每个 False 条目表示对应的时间步骤应在处理时忽略。
函数式 API 和序列式 API 中的掩码传播
在使用函数式 API 或序列式 API 时，由 Embedding 或 Masking 层生成的掩码将通过网络传播给任何能够使用它们的层（如 RNN 层）。Keras 将自动提取与输入相对应的掩码，并将其传递给任何知道该掩码使用方法的层。
例如，在下面的序贯模型中，LSTM 层将自动接收掩码，这意味着它将忽略填充的值：
Step5:  对以下函数式 API 的情况也是如此：
Step6:  将掩码张量直接传递给层
能够处理掩码的层（如 LSTM 层）在其 __call__ 方法中有一个 mask 参数。
同时，生成掩码的层（如 Embedding）会公开一个 compute_mask(input, previous_mask) 方法，供您调用。
因此，您可以将掩码生成层的 compute_mask() 方法的输出传递给掩码使用层的 __call__ 方法，如下所示：
Step8:  在自定义层中支持遮盖
有时，您可能需要编写生成掩码的层（如 Embedding），或者需要修改当前掩码的层。
例如，任何生成与其输入具有不同时间维度的张量的层（如在时间维度上进行连接的 Concatenate 层）都需要修改当前掩码，这样下游层才能正确顾及被遮盖的时间步骤。
为此，您的层应实现 layer.compute_mask() 方法，该方法会根据输入和当前掩码生成新的掩码。
以下是需要修改当前掩码的 TemporalSplit 层的示例。
Step9:  下面是关于 CustomEmbedding 层的另一个示例，该层能够根据输入值生成掩码：
Step10:  在兼容层上选择启用掩码传播
大多数层都不会修改时间维度，因此无需修改当前掩码。但是，这些层可能仍希望能够将当前掩码不加更改地传播到下一层。这是一种可以选择启用的行为。默认情况下，自定义层将破坏当前掩码（因为框架无法确定传播该掩码是否安全）。
如果您有一个不会修改时间维度的自定义层，且您希望它能够传播当前的输入掩码，您应该在层构造函数中设置 self.supports_masking = True。在这种情况下，compute_mask() 的默认行为是仅传递当前掩码。
下面是被列入掩码传播白名单的层的示例：
Step11:  现在，您可以在掩码生成层（如 Embedding）和掩码使用层（如 LSTM）之间使用此自定义层，它会将掩码一路传递到掩码使用层。
Step12:  编写需要掩码信息的层
有些层是掩码使用者：他们会在 call 中接受 mask 参数，并使用该参数来决定是否跳过某些时间步骤。
要编写这样的层，您只需在 call 签名中添加一个 mask=None 参数。与输入关联的掩码只要可用就会被传递到您的层。
下面是一个简单示例：示例中的层在输入序列的时间维度（轴 1）上计算 Softmax，同时丢弃遮盖的时间步骤。
<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
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
Explanation: Keras 中的遮盖和填充
<table class="tfo-notebook-buttons" align="left">
  <td><a target="_blank" href="https://tensorflow.google.cn/guide/keras/masking_and_padding"><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/guide/keras/masking_and_padding.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/guide/keras/masking_and_padding.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/guide/keras/masking_and_padding.ipynb"><img src="https://tensorflow.google.cn/images/download_logo_32px.png" class="_active_edit_src">下载笔记本</a></td>
</table>
设置
End of explanation
raw_inputs = [
    [711, 632, 71],
    [73, 8, 3215, 55, 927],
    [83, 91, 1, 645, 1253, 927],
]
# By default, this will pad using 0s; it is configurable via the
# "value" parameter.
# Note that you could "pre" padding (at the beginning) or
# "post" padding (at the end).
# We recommend using "post" padding when working with RNN layers
# (in order to be able to use the
# CuDNN implementation of the layers).
padded_inputs = tf.keras.preprocessing.sequence.pad_sequences(
    raw_inputs, padding="post"
)
print(padded_inputs)
Explanation: 简介
遮盖的作用是告知序列处理层输入中有某些时间步骤丢失，因此在处理数据时应将其跳过。
填充是遮盖的一种特殊形式，其中被遮盖的步骤位于序列的起点或开头。填充是出于将序列数据编码成连续批次的需要：为了使批次中的所有序列适合给定的标准长度，有必要填充或截断某些序列。
让我们仔细看看。
填充序列数据
在处理序列数据时，各个样本常常具有不同长度。请考虑以下示例（文本被切分为单词）：
[
  ["Hello", "world", "!"],
  ["How", "are", "you", "doing", "today"],
  ["The", "weather", "will", "be", "nice", "tomorrow"],
]
进行词汇查询后，数据可能会被向量化为整数，例如：
[
  [71, 1331, 4231]
  [73, 8, 3215, 55, 927],
  [83, 91, 1, 645, 1253, 927],
]
此数据是一个嵌套列表，其中各个样本的长度分别为 3、5 和 6。由于深度学习模型的输入数据必须为单一张量（例如在此例中形状为 (batch_size, 6, vocab_size)），短于最长条目的样本需要用占位符值进行填充（或者，也可以在填充短样本前截断长样本）。
Keras 提供了一个效用函数来截断和填充 Python 列表，使其具有相同长度：tf.keras.preprocessing.sequence.pad_sequences。
End of explanation
embedding = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)
masked_output = embedding(padded_inputs)
print(masked_output._keras_mask)
masking_layer = layers.Masking()
# Simulate the embedding lookup by expanding the 2D input to 3D,
# with embedding dimension of 10.
unmasked_embedding = tf.cast(
    tf.tile(tf.expand_dims(padded_inputs, axis=-1), [1, 1, 10]), tf.float32
)
masked_embedding = masking_layer(unmasked_embedding)
print(masked_embedding._keras_mask)
Explanation: 遮盖
既然所有样本现在都具有了统一长度，那就必须告知模型，数据的某些部分实际上是填充，应该忽略。这种机制就是遮盖。
在 Keras 模型中引入输入掩码有三种方式：
添加一个 keras.layers.Masking 层。
使用 mask_zero=True 配置一个 keras.layers.Embedding 层。
在调用支持 mask 参数的层（如 RNN 层）时，手动传递此参数。
掩码生成层：Embedding 和 Masking
这些层将在后台创建一个掩码张量（形状为 (batch, sequence_length) 的二维张量），并将其附加到由 Masking 或 Embedding 层返回的张量输出上。
End of explanation
model = keras.Sequential(
    [layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True), layers.LSTM(32),]
)
Explanation: 您可以在输出结果中看到，该掩码是一个形状为 (batch_size, sequence_length) 的二维布尔张量，其中每个 False 条目表示对应的时间步骤应在处理时忽略。
函数式 API 和序列式 API 中的掩码传播
在使用函数式 API 或序列式 API 时，由 Embedding 或 Masking 层生成的掩码将通过网络传播给任何能够使用它们的层（如 RNN 层）。Keras 将自动提取与输入相对应的掩码，并将其传递给任何知道该掩码使用方法的层。
例如，在下面的序贯模型中，LSTM 层将自动接收掩码，这意味着它将忽略填充的值：
End of explanation
inputs = keras.Input(shape=(None,), dtype="int32")
x = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)(inputs)
outputs = layers.LSTM(32)(x)
model = keras.Model(inputs, outputs)
Explanation: 对以下函数式 API 的情况也是如此：
End of explanation
class MyLayer(layers.Layer):
    def __init__(self, **kwargs):
        super(MyLayer, self).__init__(**kwargs)
        self.embedding = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)
        self.lstm = layers.LSTM(32)
    def call(self, inputs):
        x = self.embedding(inputs)
        # Note that you could also prepare a `mask` tensor manually.
        # It only needs to be a boolean tensor
        # with the right shape, i.e. (batch_size, timesteps).
        mask = self.embedding.compute_mask(inputs)
        output = self.lstm(x, mask=mask)  # The layer will ignore the masked values
        return output
layer = MyLayer()
x = np.random.random((32, 10)) * 100
x = x.astype("int32")
layer(x)
Explanation: 将掩码张量直接传递给层
能够处理掩码的层（如 LSTM 层）在其 __call__ 方法中有一个 mask 参数。
同时，生成掩码的层（如 Embedding）会公开一个 compute_mask(input, previous_mask) 方法，供您调用。
因此，您可以将掩码生成层的 compute_mask() 方法的输出传递给掩码使用层的 __call__ 方法，如下所示：
End of explanation
class TemporalSplit(keras.layers.Layer):
    Split the input tensor into 2 tensors along the time dimension.
    def call(self, inputs):
        # Expect the input to be 3D and mask to be 2D, split the input tensor into 2
        # subtensors along the time axis (axis 1).
        return tf.split(inputs, 2, axis=1)
    def compute_mask(self, inputs, mask=None):
        # Also split the mask into 2 if it presents.
        if mask is None:
            return None
        return tf.split(mask, 2, axis=1)
first_half, second_half = TemporalSplit()(masked_embedding)
print(first_half._keras_mask)
print(second_half._keras_mask)
Explanation: 在自定义层中支持遮盖
有时，您可能需要编写生成掩码的层（如 Embedding），或者需要修改当前掩码的层。
例如，任何生成与其输入具有不同时间维度的张量的层（如在时间维度上进行连接的 Concatenate 层）都需要修改当前掩码，这样下游层才能正确顾及被遮盖的时间步骤。
为此，您的层应实现 layer.compute_mask() 方法，该方法会根据输入和当前掩码生成新的掩码。
以下是需要修改当前掩码的 TemporalSplit 层的示例。
End of explanation
class CustomEmbedding(keras.layers.Layer):
    def __init__(self, input_dim, output_dim, mask_zero=False, **kwargs):
        super(CustomEmbedding, self).__init__(**kwargs)
        self.input_dim = input_dim
        self.output_dim = output_dim
        self.mask_zero = mask_zero
    def build(self, input_shape):
        self.embeddings = self.add_weight(
            shape=(self.input_dim, self.output_dim),
            initializer="random_normal",
            dtype="float32",
        )
    def call(self, inputs):
        return tf.nn.embedding_lookup(self.embeddings, inputs)
    def compute_mask(self, inputs, mask=None):
        if not self.mask_zero:
            return None
        return tf.not_equal(inputs, 0)
layer = CustomEmbedding(10, 32, mask_zero=True)
x = np.random.random((3, 10)) * 9
x = x.astype("int32")
y = layer(x)
mask = layer.compute_mask(x)
print(mask)
Explanation: 下面是关于 CustomEmbedding 层的另一个示例，该层能够根据输入值生成掩码：
End of explanation
class MyActivation(keras.layers.Layer):
    def __init__(self, **kwargs):
        super(MyActivation, self).__init__(**kwargs)
        # Signal that the layer is safe for mask propagation
        self.supports_masking = True
    def call(self, inputs):
        return tf.nn.relu(inputs)
Explanation: 在兼容层上选择启用掩码传播
大多数层都不会修改时间维度，因此无需修改当前掩码。但是，这些层可能仍希望能够将当前掩码不加更改地传播到下一层。这是一种可以选择启用的行为。默认情况下，自定义层将破坏当前掩码（因为框架无法确定传播该掩码是否安全）。
如果您有一个不会修改时间维度的自定义层，且您希望它能够传播当前的输入掩码，您应该在层构造函数中设置 self.supports_masking = True。在这种情况下，compute_mask() 的默认行为是仅传递当前掩码。
下面是被列入掩码传播白名单的层的示例：
End of explanation
inputs = keras.Input(shape=(None,), dtype="int32")
x = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)(inputs)
x = MyActivation()(x)  # Will pass the mask along
print("Mask found:", x._keras_mask)
outputs = layers.LSTM(32)(x)  # Will receive the mask
model = keras.Model(inputs, outputs)
Explanation: 现在，您可以在掩码生成层（如 Embedding）和掩码使用层（如 LSTM）之间使用此自定义层，它会将掩码一路传递到掩码使用层。
End of explanation
class TemporalSoftmax(keras.layers.Layer):
    def call(self, inputs, mask=None):
        broadcast_float_mask = tf.expand_dims(tf.cast(mask, "float32"), -1)
        inputs_exp = tf.exp(inputs) * broadcast_float_mask
        inputs_sum = tf.reduce_sum(
            inputs_exp * broadcast_float_mask, axis=-1, keepdims=True
        )
        return inputs_exp / inputs_sum
inputs = keras.Input(shape=(None,), dtype="int32")
x = layers.Embedding(input_dim=10, output_dim=32, mask_zero=True)(inputs)
x = layers.Dense(1)(x)
outputs = TemporalSoftmax()(x)
model = keras.Model(inputs, outputs)
y = model(np.random.randint(0, 10, size=(32, 100)), np.random.random((32, 100, 1)))
Explanation: 编写需要掩码信息的层
有些层是掩码使用者：他们会在 call 中接受 mask 参数，并使用该参数来决定是否跳过某些时间步骤。
要编写这样的层，您只需在 call 签名中添加一个 mask=None 参数。与输入关联的掩码只要可用就会被传递到您的层。
下面是一个简单示例：示例中的层在输入序列的时间维度（轴 1）上计算 Softmax，同时丢弃遮盖的时间步骤。
End of explanation
<END_TASK>