我正在尝试训练一个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,并将其输入到它开始训练的网络。