掩码屏蔽
掩码屏蔽
模型需要学习应该忽略掉填充令牌。可以设置告诉模型去忽略掉填充令牌,以便它可以专注于实际很重要的数据。实际上这很容易:在创建嵌入(Embedding)层时只需添加mask_zero=True。这意味着所有下游层都将忽略填充令牌(其ID为0)
这种工作方式是Embedding层创建一个等于K.not_equal(inputs,0)(其中K=keras.backend)的掩码张量:它是一个布尔张量,其形状与输入相同,它在单词ID为0的任何地方为False,否则为True。只要时间维度被保留,该掩码张量就会由模型自动传播到所有后续层。每一层可能会以不同的方式来处理掩码,但通常它们只忽略被掩码的时间步长(即掩码为False的时间步长)。例如,当循环层遇到被掩码的时间步长时,它仅复制前一时间步长的输出。如果掩码一直以这种方式传播到输出,那么它也会被应用于损失,因此,被掩码的时间步长不会对损失造成影响(它们的损失是0)
LSTM和GRU层有一个基于Nvidia的cuDNN库优化了GPU的实现。但是,此实现不支持掩码。如果创建的模型使用了掩码,那么这些层会退回到(慢得多的)默认实现。优化的实现还需要使用多个超参数的默认值:activation、recurrent_activation、recurrent_dropout、unroll、use_bias和reset_after
能接收掩码的所有层都必须使用掩码,否则将引发异常,这包括所有循环层,以及TimeDistributed层和其他一些层。任何支持掩码的层都必须有一个等于True的supports_masking属性。如果想实现自己的支持掩码的自定义图层,则应在call()方法中添加mask参数(现在这样会使用该方法以某种方式使用mask)。另外,应该在构造函数中设置self.supports_masking=True。如果,层不是以Embedding层开始,则可以改用keras.layers.Masking层:它将掩码设置为K.any(K.not_equal(inputs,0),axis=-1),这意味着在最后一个维度都为0的时间步长中,后续层都会被屏蔽
使用掩码层和自动掩码传播最适合简单的Sequential模型。它不适用于更复杂的模型,当需要混合Conv1D和循环层时,在这种情况下需要使用函数API或子类API来显式地计算掩码并将其传递到适合的层。例如,一下模型与先前的模型相同,不同之处在于它是使用函数API构建并手动处理掩码的:
from tensorflow import keras
vocab_size = 10000
embed_size = 128
num_oov_buckets = 1000
K = keras.backend
inputs = keras.layers.Input(shape=[None])
mask = keras.layers.Lambda(lambda inputs: K.not_equal(inputs, 0))(inputs)
z = keras.layers.Embedding(vocab_size + num_oov_buckets, embed_size)(inputs)
z = keras.layers.GRU(128, return_sequences=True)(z, mask=mask)
z = keras.layers.GRU(128)(z, mask=mask)
outputs = keras.layers.Dense(1, activation='sigmoid')(z)
model = keras.Model(inputs=[inputs], outputs=[outputs])
经过几个轮次的训练之后,该模型将非常擅长判断评论是正面还是负面。如果使用TensorBoard()回调函数,可以在模型学习时使用TensorBoard把嵌入可视化