Keras RNN,输入形状不正确,即使形状显示为正确



我正在尝试训练一个RNN来分类文本。在我的电脑上,我有一个大的文本文件,其中包含每个类别(总共 2 个)训练网络的所有短语,例如

短语 1
短语 2
短语 3

然后,我将其转换为 keras 数据集,使用

tf.data.TextLineDataset(
directory)

这没有附加标签到项目上,所以我使用了该功能

directory.map(lambda ex: labeler(ex,2))

它为所有项目添加了标签,留下了一个如下所示的数据集:

<MapDataset element_spec=(TensorSpec(shape=(), dtype=tf.string, name=None), TensorSpec(shape=(), dtype=tf.int64, name=None))>

然后使用.skip.take将其拆分为验证集和训练集。然后使用category1 = category1.concatenate(category2)将这两个类别合并为单个验证和单个训练数据集

然后,我创建了一个矢量化层,如下所示:

def vectorize_text(text, label):
text = tf.expand_dims(text, -1)
return vectorize_layer(text), label

并通过函数运行训练和验证集以矢量化所有短语。然后留下一个数据集,如下所示:

<MapDataset element_spec=(TensorSpec(shape=(None, 250), dtype=tf.int64, name=None), TensorSpec(shape=(), dtype=tf.int64, name=None))>

项目的示例是

(<tf.Tensor: shape=(1, 250), dtype=int64, numpy=array([[   1,   28,   12, 1199, 3445,   61,   31,  166,  163,   13,   28,
2,   97,   13,    6,  206,  625,  972,  344,    7, 2790,   11,
1, 1379, 3615,   24,    1,    2,   27,   21,    3,  435,    4,
16,    1,   15,   22,    1,    3,  127,    2,   13,   36,    8,
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,    0,    0,    0,    0,    0,    0,    0]], dtype=int64)>, <tf.Tensor: shape=(), dtype=int64, numpy=2>),

如您所见,该项目的形状为 1,250,并且还有另一个张量表示它所属的类别,在本例中为 #2。然后我通过我的模型输入它,那里的东西坏了。模型是这样的:

model = keras.Sequential()
model.add(keras.layers.LSTM(128, input_shape=(1,250,), activation="relu", return_sequences=True))
model.add(keras.layers.LSTM(128, activation="relu", return_sequences=True))
model.add(keras.layers.LSTM(32, activation="relu", return_sequences=False))
model.add(keras.layers.Dense(1,activation="relu"))
model.compile(optimizer=tf.keras.optimizers.Adam(0.01), loss='binary_crossentropy', metrics=['accuracy','Precision','Recall'])
model.fit(train_set,batch_size=32,epochs=1)

但是当我运行代码时,我得到了错误

ValueError: Input 0 of layer "sequential_54" is incompatible with the layer: expected shape=(None, 1, 250), found shape=(None, 250)

为了解决这个问题,我试图添加一个重塑层,但没有奏效。我也尝试使用np.expand_dims,但这也不能解决问题。有人有解决方案吗?此外,某些函数(如 train_set.shape)会给出错误,例如ConcatenateDataset object has no attribute shape

编辑:数据预处理
拆分和提取


def labeler(example, index):  #function to label items 
return example, tf.cast(index, tf.int64)
train_set_1 = tf.data.TextLineDataset( #get data 
"comments1.txt",
compression_type=None,
buffer_size=None,
num_parallel_reads=None,
name=None
)
#split data into val and training 
val_set_1 = train_set_1.skip(int(1200*8/10)) #1200 is the number of items so 80:20 split
train_set_1 = train_set_1.take(int(1200*8/10))
#label both sets 
labeled_train_1 = train_set_1.map(lambda ex: labeler(ex, 1)) #1 is the label
labeled_val_1 = val_set_1.map(lambda ex: labeler(ex, 1))
print(labeled_train_1)

print(train_set_1)
#repeat for set 2 
train_set_2 = tf.data.TextLineDataset(
"comments2.txt",
compression_type=None,
buffer_size=None,
num_parallel_reads=None,
name=None
)
val_set_2 = train_set_2.skip(int(1200*8/10))
train_set_2= train_set_2.take(int(1200*8/10))
labeled_train_2 = train_set_2.map(lambda ex: labeler(ex, 2))
labeled_val_2 = val_set_2.map(lambda ex: labeler(ex, 2))

矢 量化

#len(counter) is total number of words, max_length is 250
vectorize_layer = tf.keras.layers.TextVectorization( max_tokens=len(counter), output_mode='int', output_sequence_length=max_length)
vectorize_layer.adapt(train_set_1)
vectorize_layer.adapt(train_set_2)  

def vectorize_text(text, label): #this is where i can change the dimensions and vectorize the whole sequence 
text = tf.expand_dims(text, 0)
text = tf.expand_dims(text, -1)
return vectorize_layer(text), label
#actually vectorizing and combining all the text
train_1= labeled_train_1.map(vectorize_text)
train_2 = labeled_train_2.map(vectorize_text)
train_set = train_1.concatenate(train_2)
val_1 = labeled_val_1.map(vectorize_text)
val_2 = labeled_val_2.map(vectorize_text)
val_set = val_1.concatenate(val_2)
print(val_2)
print(list(val_2))

接下来它只是被输入到模型中.
Edit 2:
我发现一个笔记本在做与我的项目类似的事情,它似乎在神经网络中使用了嵌入层,所以我认为嵌入层可能会有所帮助。我已经尝试了它并使用了扩展调光的不同配置,但仍然没有解决方案,但该层可能很有用。

这就是我目前正在搞砸的:

model.add(keras.layers.Embedding(len(counter),250,input_length=1))
model.add(keras.layers.LSTM(128, activation="relu", return_sequences=True))
model.add(keras.layers.LSTM(128, activation="relu", return_sequences=True))
model.add(keras.layers.LSTM(32, activation="relu", return_sequences=False))
model.add(keras.layers.Dense(1,activation="relu"))

但我也尝试了 .
Edit 3len(counter),1,input_length=250:我设法将维度更改为 250,1 而不是 1,250,但我收到一条错误消息,对于 fit 循环,输入形状为 none,1,1。这似乎问题可能是输入既是标记化的词,这是一个大小为 250,1 的张量,也是答案又名数据集 1 或数据集 2,这是另一个张量,导致张量包含 2 个张量,这可能会给出大小为 none,1,1。

你只需要扩展你的dims,即使你可能错过了一个步骤,但我稍后会谈到这一点......修复应该是:

model.fit(tf.expand_dim(train_set, 1),batch_size=32,epochs=1)

现在,关于 dims:
RNN 期望单个元素的形状为(None, X),X 为正整数.
第一个None表示短语/序列的长度,因为它可能会有所不同,因此它使用None以避免手动修复它.
第二个维度,X表示序列中元素的"特征"。以天气预报为例,这些特征是风速,湿度等。

话虽如此,您的序列应该编码为(250, 1),因为您有"序列中的 250 个单词/元素",并且每个单词都有 1 个特征(对应于它的整数)。

鉴于此,在我看来,您应该使用以下方法:

model = keras.Sequential()
model.add(keras.layers.LSTM(128, input_shape=(250,1), activation="relu", return_sequences=True))
model.add(keras.layers.LSTM(128, activation="relu", return_sequences=True))
model.add(keras.layers.LSTM(32, activation="relu", return_sequences=False))
model.add(keras.layers.Dense(1,activation="relu"))
model.compile(optimizer=tf.keras.optimizers.Adam(0.01), loss='binary_crossentropy', metrics=['accuracy','Precision','Recall'])
model.fit(tf.expand_dim(train_set, -1),batch_size=32,epochs=1)

您可以在此页面的文档中看到以下内容:

调用参数
inputs:具有形状 [批处理、时间步长、特征] 的 3D 张量。

我找到了解决方案。问题是矢量化层的工作方式存在错误,这会导致它有时会返回一个空数组而不是一个填充的数组。因此,我不得不使用

def dataset_to_numpy(ds):
#Convert tensorflow dataset to numpy arrays
texts = []
labels = []
# Iterate over a dataset
for i, (text, label) in enumerate(tfds.as_numpy(ds)):
texts.append(text)
labels.append(label)
for i, txt in enumerate(texts):
if i < 3:
print(txt.shape, labels[i])
return texts, labels

我通过函数运行了 val 和训练集,然后使用了这个函数

to_del = [] 
for i in range(len(train_set[0])):
if train_set[0][i].shape != (250, 1):
print(i)
to_del.append(i)

哪个得到了要删除的项目。然后我删除了数组中的那些项目并运行了这段代码


train_set[0] = list(train_set[0])
train_set[1] = list(train_set[1])
train_set[0] = np.array([np.array(val) for val in train_set[0]])
train_set[1] = np.array([np.array(val) for val in train_set[1]])
train_set[1] = np.expand_dims(train_set[1],-1) 

它将列表转换为 NP 数组,并扩展了目标的维度。最后,我将train_set转换为x_train和y_train,并将其输入到它开始训练的网络。

最新更新