使用序列填充
有两个问题。您需要先使用pad_sequences文本序列。而且.中也没有这样的参数input_shape。SimpleRNN尝试使用以下代码:
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)
这是官方代码示例,它可能会对您有所帮助。
在嵌入层中使用带有掩码的序列填充
根据您的评论和信息,似乎可以使用可变长度输入序列,请检查这个和这个。但是,我仍然可以说,在大多数情况下,从业者更喜欢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"在序列结束时设置填充值。但它建议"post"在使用层时使用填充RNN,以便能够使用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在训练时取出那些额外的填充值。我们将告诉模型数据的某些部分是填充的,应该忽略这些。那个机制就是掩蔽。因此,这是一种告诉序列处理层输入中某些时间步长丢失的方法,因此在处理数据时应该跳过。在模型中引入输入掩码有三种方式Keras:
- 添加一个
keras. layers.Masking layer.
- 使用配置
keras.layers.Embedding图层mask_zero=True。
- 调用支持此参数的图层(例如
RNN图层)时,手动传递掩码参数。
这里我们将仅通过配置Embedding图层来展示。它有一个默认调用mask_zero和设置的参数。False如果我们设置它,True那么0将跳过序列中包含的索引。Falseentry 表示在处理过程中应该忽略相应的时间步长。
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至少要使用。这是一个很好的解释。
这是使用这些代码的完整示例。
# 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
参考