5

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

为了实现和应用它,我决定将批量大小设置为 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)
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()

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

错误信息:

ValueError: Failed to convert a NumPy array to a Tensor (Unsupported object type list).

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

4

2 回答 2

2

使用序列填充

有两个问题。您需要先使用pad_sequences文本序列。而且.中也没有这样的参数input_shapeSimpleRNN尝试使用以下代码:

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_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])

您的模型定义现在应该是

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

参考

于 2021-03-26T09:26:23.340 回答
0

没有序列填充

序列建模中输入序列的可变长度不必填充。TensorFlow中,沿某个轴具有可变数量元素的张量称为不规则张量,我们将tf.ragged.RaggedTensor其用于不规则数据。例如:

# variable length input sequences 
ragged_list = [
    [0, 1, 2, 3],
    [4, 5],
    [6, 7, 8],
    [9]]

# convert to ragged tensor that handle such variable length inputs 
tf.ragged.constant(ragged_list).shape
shape: [4, None]

因此,我们可以在序列建模中使用参差不齐的输入数据,并且我们不再需要填充序列以获得统一的输入长度。


数据集

import tensorflow as tf 
import warnings, numpy as np 
warnings.filterwarnings("ignore", category=np.VisibleDeprecationWarning) 

# maxlen = 200 # No maximum length but whatever 
batch_size = 256
max_features = 20000  # Only consider the top 20k words

(x_train, y_train), (x_test, y_test) = \
              tf.keras.datasets.imdb.load_data(num_words=max_features)
print(len(x_train), "Training sequences")
print(len(x_test), "Validation sequences")

25000 Training sequences
25000 Validation sequences
# quick check 
x_train[:3]

array([list([1, 14, 22, 16, 43, 53, ....]),
       list([....]),
       list([...]),

转换为衣衫褴褛

现在,我们将其转换为处理可变大小序列的参差不齐的张量。

x_train = tf.ragged.constant(x_train)
x_test  = tf.ragged.constant(x_test)
# quick check 

x_train[:3]
<tf.RaggedTensor [[1, 14, 22, 16, 43, 53, ...] [...] [...]]

x_train.shape, x_test.shape
(TensorShape([25000, None]), TensorShape([25000, None]))

模型

# Input for variable-length sequences of integers
inputs = tf.keras.Input(shape=(None,), dtype="int32")
# Embed each integer in a 128-dimensional vector
x = tf.keras.layers.Embedding(max_features, 128)(inputs)
x = tf.keras.layers.SimpleRNN(units=32)(x)
# Add a classifier
outputs = tf.keras.layers.Dense(1, activation="sigmoid")(x)
model = tf.keras.Model(inputs, outputs)
model.summary()
Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_2 (InputLayer)         [(None, None)]            0         
_________________________________________________________________
embedding_1 (Embedding)      (None, None, 128)         2560000   
_________________________________________________________________
simple_rnn (SimpleRNN)       (None, 32)                5152      
_________________________________________________________________
dense (Dense)                (None, 1)                 33        
=================================================================
Total params: 2,565,185
Trainable params: 2,565,185
Non-trainable params: 0
_________________________________________________________________

编译和训练

model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["acc"])
model.fit(x_train, y_train, batch_size=batch_size, verbose=2, 
          epochs=10, validation_data=(x_test, y_test))
Epoch 1/10
113s 1s/step - loss: 0.6273 - acc: 0.6295 - val_loss: 0.4188 - val_acc: 0.8206
Epoch 2/10
109s 1s/step - loss: 0.4895 - acc: 0.8041 - val_loss: 0.4703 - val_acc: 0.8040
Epoch 3/10
109s 1s/step - loss: 0.3513 - acc: 0.8661 - val_loss: 0.3996 - val_acc: 0.8337
Epoch 4/10
110s 1s/step - loss: 0.2450 - acc: 0.9105 - val_loss: 0.3945 - val_acc: 0.8420
Epoch 5/10
109s 1s/step - loss: 0.1437 - acc: 0.9559 - val_loss: 0.4085 - val_acc: 0.8422
Epoch 6/10
109s 1s/step - loss: 0.0767 - acc: 0.9807 - val_loss: 0.4310 - val_acc: 0.8429
Epoch 7/10
109s 1s/step - loss: 0.0380 - acc: 0.9932 - val_loss: 0.4784 - val_acc: 0.8437
Epoch 8/10
110s 1s/step - loss: 0.0288 - acc: 0.9946 - val_loss: 0.5039 - val_acc: 0.8564
Epoch 9/10
110s 1s/step - loss: 0.0957 - acc: 0.9615 - val_loss: 0.5687 - val_acc: 0.8575
Epoch 10/10
109s 1s/step - loss: 0.1008 - acc: 0.9637 - val_loss: 0.5166 - val_acc: 0.8550
于 2021-04-12T18:18:57.210 回答