使用循环网络的电影评论分类

Gok*_*tug 5 python deep-learning keras tensorflow recurrent-neural-network

据我所知和研究,数据集中的序列可以有不同的长度;我们不需要填充或截断它们,前提是训练过程中的每个批次都包含相同长度的序列。

为了实现和应用它,我决定将批量大小设置为 1,并在 IMDB 电影分类数据集上训练我的 RNN 模型。我添加了我在下面编写的代码。

import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras.datasets import imdb
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import SimpleRNN
from tensorflow.keras.layers import Embedding

max_features = 10000
batch_size = 1

(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=max_features)

model = Sequential()
model.add(Embedding(input_dim=10000, output_dim=32))
model.add(SimpleRNN(units=32, input_shape=(None, 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)
Run Code Online (Sandbox Code Playgroud)
acc = history.history["acc"]
loss = history.history["loss"]
val_acc = history.history["val_acc"]
val_loss = history.history["val_loss"]

epochs = range(len(acc) + 1)
plt.plot(epochs, acc, "bo", label="Training Acc")
plt.plot(epochs, val_acc, "b", label="Validation Acc")
plt.title("Training and Validation Accuracy")
plt.legend()
plt.figure()
plt.plot(epochs, loss, "bo", label="Training Loss")
plt.plot(epochs, val_loss, "b", label="Validation Loss")
plt.title("Training and Validation Loss")
plt.legend()
plt.show()
Run Code Online (Sandbox Code Playgroud)

我遇到的错误是由于输入numpy数组中的列表组件而无法将输入转换为张量格式。但是,当我更改它们时,我继续遇到类似的错误。

错误信息:

ValueError: Failed to convert a NumPy array to a Tensor (Unsupported object type list).
Run Code Online (Sandbox Code Playgroud)

我无法处理这个问题。在这一点上有人可以帮助我吗?

M.I*_*nat 2

带序列填充

有两个问题。您需要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)
Run Code Online (Sandbox Code Playgroud)

是官方的代码示例,也许对你有帮助。


在嵌入层中使用带有掩模的序列填充

根据您的评论和信息,似乎可以使用可变长度输入序列,也请检查。但我仍然可以说,在大多数情况下,从业者更喜欢pad统一长度的序列;因为它有说服力。选择非均匀或可变的输入序列长度是某种特殊情况;类似于我们想要视觉模型的可变输入图像大小。

但是,在这里我们将添加有关padding如何mask在训练时间中取出填充值的信息,这在技术上似乎是可变长度输入训练。希望这能说服你。我们先来了解一下pad_sequences做什么。通常在序列数据中,很常见的情况是每个训练样本的长度不同。让我们考虑以下输入:

raw_inputs = [
    [711, 632, 71],
    [73, 8, 3215, 55, 927],
    [83, 91, 1, 645, 1253, 927],
]
Run Code Online (Sandbox Code Playgroud)

这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)
Run Code Online (Sandbox Code Playgroud)

我们可以设置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
Run Code Online (Sandbox Code Playgroud)

好的,现在我们有了一个填充的输入序列,所有输入的长度都是统一的。现在,我们可以mask在训练时间内删除这些额外的填充值。我们将告诉模型数据的某些部分正在填充,并且这些部分应该被忽略。这种机制就是掩蔽。因此,这是一种告诉序列处理层输入中缺少某些时间步长的方法,因此在处理数据时应跳过这些时间步长。在模型中引入输入掩码有三种方法Keras

  • 添加一个keras. layers.Masking layer.
  • 配置一个keras.layers.Embedding图层mask_zero=True
  • 当调用支持此参数的图层(例如图层)时,手动传递掩码参数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)
Run Code Online (Sandbox Code Playgroud)

以下是它在类中的计算方式Embedding(Layer)

  def compute_mask(self, inputs, mask=None):
    if not self.mask_zero:
      return None

    return tf.not_equal(inputs, 0)
Run Code Online (Sandbox Code Playgroud)

这里有一个问题,如果我们设置mask_zeroTrue,结果索引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])
Run Code Online (Sandbox Code Playgroud)

您的模型定义现在应该是

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"))
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)

参考