使用序列填充
有两个问题。您需要首先在文本序列上使用pad_sequences。而且SimpleRNN 中也没有这样的参数input_shape。试试下面的代码:
max_features = 20000 # Only consider the top 20k words
maxlen = 200 # Only consider the first 200 words of each movie review
batch_size = 1
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=max_features)
print(len(x_train), "Training sequences")
print(len(x_test), "Validation sequences")
x_train = tf.keras.preprocessing.sequence.pad_sequences(x_train, maxlen=maxlen)
x_test = tf.keras.preprocessing.sequence.pad_sequences(x_test, maxlen=maxlen)
model = Sequential()
model.add(Embedding(input_dim=max_features, output_dim=32))
model.add(SimpleRNN(units=32))
model.add(Dense(1, activation="sigmoid"))
model.compile(optimizer="rmsprop", loss="binary_crossentropy", metrics=["acc"])
history = model.fit(x_train, y_train, batch_size=batch_size,
epochs=10, validation_split=0.2)
Here是官方代码示例,希望对你有所帮助。
在嵌入层中使用带有掩码的序列填充
根据您的 cmets 和信息,似乎可以使用 可变长度 输入序列,请检查 this 和 this。但是,我可以说,在大多数情况下,从业者更愿意pad 序列长度一致;因为它很有说服力。选择非均匀或可变输入序列长度是一种特殊情况;类似于我们想要视觉模型的可变输入图像大小。
但是,我们将在此处添加有关 padding 的信息,以及我们如何通过 mask 输出训练时间的填充值,这在技术上似乎是可变长度的输入训练。希望能说服你。我们先来了解pad_sequences 是做什么的。通常在序列数据中,每个训练样本的长度不同是很常见的情况。让我们考虑以下输入:
raw_inputs = [
[711, 632, 71],
[73, 8, 3215, 55, 927],
[83, 91, 1, 645, 1253, 927],
]
这 3 个训练样本的长度不同,分别为 3、5 和 6。我们接下来要做的是通过添加一些值(通常是 0 或 -1)使它们的长度相等 - 无论是在序列的开头还是结尾。
tf.keras.preprocessing.sequence.pad_sequences(
raw_inputs, maxlen=6, dtype="int32", padding="pre", value=0.0
)
array([[ 0, 0, 0, 711, 632, 71],
[ 0, 73, 8, 3215, 55, 927],
[ 83, 91, 1, 645, 1253, 927]], dtype=int32)
我们可以设置padding = "post"在序列末尾设置pad值。但它建议在使用RNN 层时使用"post" 填充,以便能够使用层的CuDNN 实现。但是,仅供参考,您可能会注意到我们设置了maxlen = 6,这是最高的输入序列长度。但它不一定是最高的输入序列长度,因为如果数据集变得更大,它可能会变得计算量大。我们可以将其设置为5,假设我们的模型可以在这个长度内学习特征表示,它是一种超参数。这带来了另一个参数truncating。
tf.keras.preprocessing.sequence.pad_sequences(
raw_inputs, maxlen=5, dtype="int32", padding="pre", truncating="pre", value=0.0
)
array([[ 0, 0, 711, 632, 71],
[ 73, 8, 3215, 55, 927],
[ 91, 1, 645, 1253, 927]], dtype=int32
好的,现在我们有一个填充输入序列,所有输入都是统一长度的。现在,我们可以mask 在训练时取出那些额外的填充值。我们将告诉模型数据的某些部分是填充的,应该忽略这些。该机制是屏蔽。因此,这是一种告诉 sequence-processing 层输入中缺少某些时间步长的方法,因此在处理数据时应该跳过。在Keras模型中引入输入masks有三种方式:
- 添加
keras. layers.Masking layer。
- 使用
mask_zero=True 配置keras.layers.Embedding 层。
- 在调用支持此参数的图层时手动传递掩码参数(例如
RNN 图层)。
这里我们只通过配置Embedding层来展示。它有一个名为mask_zero 的参数,默认设置为False。如果我们将其设置为True,那么将跳过包含序列中索引的0。 False 条目表示应该在处理过程中忽略相应的时间步。
padd_input = tf.keras.preprocessing.sequence.pad_sequences(
raw_inputs, maxlen=6, dtype="int32", padding="pre", value=0.0
)
print(padd_input)
embedding = tf.keras.layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)
masked_output = embedding(padd_input)
print(masked_output._keras_mask)
[[ 0 0 0 711 632 71]
[ 0 73 8 3215 55 927]
[ 83 91 1 645 1253 927]]
tf.Tensor(
[[False False False True True True]
[False True True True True True]
[ True True True True True True]], shape=(3, 6), dtype=bool)
这是在 Embedding(Layer) 类中的计算方式。
def compute_mask(self, inputs, mask=None):
if not self.mask_zero:
return None
return tf.not_equal(inputs, 0)
这里有一个问题,如果我们将mask_zero 设置为True,则索引0 不能在词汇表中使用。根据文档
mask_zero:布尔值,输入值 0 是否是一个特殊的“填充”值,应该被屏蔽掉。这在使用可能采用可变长度输入的循环层时很有用。如果这是True,那么模型中的所有后续层都需要支持屏蔽,否则将引发异常。如果 mask_zero 设置为 True,则索引 0 不能在词汇表中使用(input_dim 应该等于词汇表的大小 + 1)。
所以,我们至少必须使用max_features + 1。 Here 是一个很好的解释。
这是使用这些代码的完整示例。
# get the data
(x_train, y_train), (_, _) = imdb.load_data(num_words=max_features)
print(x_train.shape)
# check highest sequence lenght
max_list_length = lambda list: max( [len(i) for i in list])
print(max_list_idx(x_train))
max_features = 20000 # Only consider the top 20k words
maxlen = 350 # Only consider the first 350 words out of `max_list_idx(x_train)`
batch_size = 512
print('Length ', len(x_train[0]), x_train[0])
print('Length ', len(x_train[1]), x_train[1])
print('Length ', len(x_train[2]), x_train[2])
# (1). padding with value 0 at the end of the sequence - padding="post", value=0.
# (2). truncate 'maxlen' words
# out of `max_list_idx(x_train)` at the end - maxlen=maxlen, truncating="post"
x_train = tf.keras.preprocessing.sequence.pad_sequences(x_train,
maxlen=maxlen, dtype="int32",
padding="post", truncating="post",
value=0.)
print('Length ', len(x_train[0]), x_train[0])
print('Length ', len(x_train[1]), x_train[1])
print('Length ', len(x_train[2]), x_train[2])
您的模型定义现在应该是
model = Sequential()
model.add(Embedding(
input_dim=max_features + 1,
output_dim=32,
mask_zero=True))
model.add(SimpleRNN(units=32))
model.add(Dense(1, activation="sigmoid"))
model.compile(optimizer="rmsprop", loss="binary_crossentropy", metrics=["acc"])
history = model.fit(x_train, y_train,
batch_size=256,
epochs=1, validation_split=0.2)
639ms/step - loss: 0.6774 - acc: 0.5640 - val_loss: 0.5034 - val_acc: 0.8036
参考文献