Keras LSTM教程

Keras LSTM教程,在本教程中,我将集中精力在Keras中创建LSTM网络,简要介绍LSTM的工作原理。在这个Keras LSTM教程中,我们将利用一个称为PTB语料库的大型文本数据集来实现序列到序列的文本预测模型。本教程中的所有代码都可以在此站点的Github存储库中找到。

循环神经网络

LSTM网络是一种循环神经网络。循环神经网络是一种神经网络,它试图对依赖于时间或顺序的行为(如语言、股价、电力需求等)进行建模。这是通过将神经网络层在t时刻的输出反馈给同一网络层在t + 1时刻的输入来实现的。它是这样的:
Keras LSTM教程
带节点的循环神经网络图

在训练和预测过程中,循环神经网络以编程方式“展开”,得到如下结果:
Keras LSTM教程

在这里,您可以看到,在每个时间步骤中,都提供了一个新单词——前一个F(即h_{t-1})的输出也在每个时间步骤中提供给网络。

vanilla 循环神经网络(由常规神经网络节点构造)的问题在于,当我们尝试对由大量其他单词分隔的单词或序列值之间的依赖关系建模时,我们体验到了梯度消失问题(有时也是梯度爆炸问题)。这是因为小梯度或权重(小于 1 的值)在多个时间步长中乘以多次,并且梯度以不相通方式收缩为零。这意味着这些早期图层的权重不会显著更改,因此网络不会学习长期依赖关系。

LSTM网络是解决这一问题的一种方法。

LSTM网络

如前所述,在这个Keras LSTM教程中,我们将构建一个用于文本预测的LSTM网络。LSTM网络是一个循环神经网络,它用LSTM细胞块来代替我们的标准神经网络层。这些单元格有不同的组成部分,称为输入门、遗忘门和输出门——这些将在稍后详细解释。以下是LSTM单元格的图形表示:
Keras LSTM教程
LSTM细胞图

首先请注意,在左侧,我们将新单词/序列值x_t连接到单元格h_{t-1}的前一个输出。这个组合输入的第一步是通过tanh层进行压缩。第二步是这个输入通过一个输入门。输入门是一层sigmoid激活节点,其输出乘以压扁的输入。这些输入门sigmoid可以“杀死”输入向量中不需要的任何元素。一个sigmoid函数输出0到1之间的值,因此可以将连接到这些节点的输入的权重训练为接近于零的输出值来“关闭”某些输入值(或者相反,接近于1的输出值来“传递”其他值)。

通过此单元格的数据流的下一步是内部状态/遗忘门循环。LSTM细胞有一个内部状态变量s_t。这个变量,滞后一个时间步长,即s_{t-1}被添加到输入数据中,创建一个有效的递归层。这个加法运算,而不是乘法运算,有助于降低梯度消失的风险。然而,这个递归循环是由遗忘门控制的——它的工作原理与输入门相同,但是它帮助网络学习哪些状态变量应该“记住”或“忘记”。

最后,我们有一个输出层tanh压扁函数,它的输出由一个输出门控制。这个门决定哪些值实际上可以作为单元格h_t的输出。

LSTM细胞的数学是这样的:
输入
首先,使用tanh激活函数将输入压缩到-1和1之间。这可以表示为:
Keras LSTM教程

其中U^gV^g分别为输入和以前的单元输出的权重,b^g为输入偏差。注意,指数g不是提高的幂,而是表示这些是输入权重和偏置值(与输入门、遗忘门、输出门等相反)。
然后将这个压缩后的输入按元素乘上输入门的输出,如上所述,输入门是一系列sigmoid激活节点:
Keras LSTM教程

LSTM单元输入部分的输出由:

g ∘ i

在这里∘操作符表示element-wise乘法。

忘记门和状态循环

遗忘门输出表示为:
Keras LSTM教程
前一状态和遗忘门的元素乘积的输出表示为s_{t-1} ∘ f。忘记门/状态循环阶段的输出为:
Keras LSTM教程

输出门
输出门表示为:
Keras LSTM教程
因此,经过tanh压扁后,单元格的最终输出为:
Keras LSTM教程

LSTM字嵌入和隐藏层大小

应该记住,在上面的所有数学中,我们处理的都是向量,即输入x_th_{t-1}不是单值标量,而是一定长度的向量。同样,所有的权值和偏置值分别是矩阵和向量。现在,你可能想知道,我们如何表示输入到神经网络的单词?答案是单词嵌入。在其他的教程中,我已经详细介绍了这一点,特别是在Python和TensorFlow中的Word2Vec word嵌入教程和Word2Vec Keras教程中。基本上,它涉及到取一个单词,并找到该单词的向量表示,它捕捉了该单词的一些含义。在Word2Vec中,这个意思通常是由上下文来量化的,也就是说,在向量空间中靠得很近的单词向量就是那些出现在句子中离同一个单词很近的单词。

向量这个词可以单独学习,就像在本教程中一样,也可以在您的Keras LSTM网络训练期间学习。在接下来的示例中,我们将设置所谓的嵌入层,将每个单词转换为有意义的单词向量。我们必须指定嵌入层的大小——这是每个单词所表示的向量的长度——通常在100-500之间。换句话说,如果嵌入层的大小为250,那么每个单词将由一个250长度的向量表示,i.e.[x_1,x_2,…,x_{250}]

LSTM隐藏层大小

我们通常将嵌入层输出的大小与LSTM单元中隐藏层的数量匹配起来。您可能想知道LSTM单元格中的隐藏层来自何处。在我的LSTM概览图中,我简单地显示了输入数据流经的“data rails”。然而,单元格中的每个sigmoid、tanh或隐藏状态层实际上是一组节点,其数量等于隐藏层的大小。因此,LSTM细胞中的每个“节点”实际上是一组正常的神经网络节点,就像密集连接的神经网络中的每一层一样。

Keras LSTM架构

本节将演示完整的LSTM体系结构,并展示我们在Keras中构建的网络体系结构。这将进一步阐明上面表达的一些思想,包括嵌入层和在网络中流动的张量大小。建议的架构如下:
Keras LSTM教程
Keras LSTM教程架构

文本数据的输入形状按如下顺序排列:(批量大小、时间步长、隐藏大小)。换句话说,对于每个批次样本和每个单词的时间步长,都有一个500长度的嵌入单词向量来表示输入单词。这些嵌入向量将作为整体模型学习的一部分来学习。然后将输入数据输入到两个LSTM单元的“stacked”层(长度为500隐藏大小)中——在上面的图中,LSTM网络显示为在所有时间步骤上展开。这些展开单元格的输出仍然是(批大小、时间步长、隐藏大小)。

然后将这个输出数据传递到一个名为TimeDistributed的Keras层,下面将对此进行更全面的解释。最后,输出层应用了一个softmax激活。将该输出与每个批次的训练y数据进行比较,然后在Keras中从那里执行误差和梯度反向传播。在本例中,训练y数据是输入x个提前一个时间步长的单词——换句话说,在每个时间步中,模型都试图预测序列中的下一个单词。然而,它在每个时间步长都这样做——因此输出层的时间步长与输入层的时间步长相同。这一点稍后将更加清楚。

构建Keras LSTM模型

在本节中,将逐步介绍并讨论用于创建上面所示Keras LSTM体系结构的每一行代码。但是,我只简要讨论文本预处理代码,它主要使用TensorFlow站点上的代码。Keras LSTM教程的完整代码可以在这个站点的Github存储库中找到,名为keras_lstm.py。注意,您首先必须下载Penn Tree Bank (PTB)数据集,该数据集将用作培训和验证语料库。您需要更改Github代码中的data_path变量,以匹配下载数据的位置。

文本预处理代码
为了使文本数据具有正确的形状,以便输入Keras LSTM模型,必须为语料库中的每个惟一单词分配一个惟一的整数索引。然后需要按顺序重新构建文本语料库,但不是按照文本单词的顺序使用整数标识符。代码中执行此操作的三个函数是read_words、build_vocab和file_to_word_ids。我不会详细讨论这些函数,但基本上,它们首先将给定的文本文件分割为单独的单词和基于句子的字符(即句子末尾)。然后,每个惟一的单词被标识并分配一个惟一的整数。最后,将原始文本文件转换为这些惟一整数的列表,其中每个单词都用其新的整数标识符替换。这允许文本数据在神经网络中使用。

我创建的运行这些函数的load_data函数如下所示:

def load_data():
    # get the data paths
    train_path = os.path.join(data_path, "ptb.train.txt")
    valid_path = os.path.join(data_path, "ptb.valid.txt")
    test_path = os.path.join(data_path, "ptb.test.txt")

    # build the complete vocabulary, then convert text data to list of integers
    word_to_id = build_vocab(train_path)
    train_data = file_to_word_ids(train_path, word_to_id)
    valid_data = file_to_word_ids(valid_path, word_to_id)
    test_data = file_to_word_ids(test_path, word_to_id)
    vocabulary = len(word_to_id)
    reversed_dictionary = dict(zip(word_to_id.values(), word_to_id.keys()))

    print(train_data[:5])
    print(word_to_id)
    print(vocabulary)
    print(" ".join([reversed_dictionary[x] for x in train_data[:10]]))
    return train_data, valid_data, test_data, vocabulary, reversed_dictionary

要调用这个函数,我们可以运行:

train_data, valid_data, test_data, vocabulary, reversed_dictionary = load_data()

这个函数的三个输出分别是来自数据集的训练数据、验证数据和测试数据,但是每个单词在列表中都表示为整数。一些信息在load_data()运行期间打印出来,其中之一是print(train_data[:5])——这将生成以下输出:

[9970, 9971, 9972, 9974, 9975]

正如您所看到的,训练数据按照预期由一组整数组成。

接下来,输出词汇表只是文本语料库的大小。当单词被合并到训练数据中时,并没有考虑每一个单独的单词——相反,在自然语言处理中,文本数据通常被限制在最常见的N个单词中。在本例中,N =词汇量= 10,000。

最后,reversed_dictionary是一个Python字典,其中键是单词的惟一整数标识符,关联值是文本中的单词。这允许我们从模型将生成的预测整数词向后工作,并将它们翻译回实际文本。例如,下面的代码将train_data中的整数转换回随后打印的文本:print(” “.join([reversed_dictionary[x] for x in train_data[100:110]]))。这段代码产生:

workers exposed to it more than N years ago researchers

这就是关于文本预处理所需要的所有解释,所以让我们继续设置输入数据生成器,它将把样本输入到Keras LSTM模型中。

创建Keras LSTM数据生成器

在训练神经网络时,我们通常以小批量的方式将数据输入它们,称为mini-batches or just “batches”。Keras有一些方便的函数,可以从预先提供的Python迭代器/生成器对象中自动提取训练数据并将其输入模型。其中一个Keras函数称为fit_generator。fit_generator的第一个参数是我们将要创建的Python迭代器函数,它将用于在培训过程中提取成批的数据。Keras中的这个函数将处理所有的数据提取、模型的输入、执行梯度步骤、记录指标(如精度)和执行回调(这些将在稍后讨论)。Python迭代器函数需要有如下形式:

while True:
    #do some things to create a batch of data (x, y)
   yield x, y

在本例中,我创建了一个生成器类,其中包含实现这种结构的方法。这个类的初始化是这样的:

class KerasBatchGenerator(object):

    def __init__(self, data, num_steps, batch_size, vocabulary, skip_step=5):
        self.data = data
        self.num_steps = num_steps
        self.batch_size = batch_size
        self.vocabulary = vocabulary
        # this will track the progress of the batches sequentially through the
        # data set - once the data reaches the end of the data set it will reset
        # back to zero
        self.current_idx = 0
        # skip_step is the number of words which will be skipped before the next
        # batch is skimmed from the data set
        self.skip_step = skip_step

这里,KerasBatchGenerator对象将我们的数据作为第一个参数。注意,这些数据可以是训练、验证或测试数据——可以在机器学习开发周期的不同阶段(训练、验证调优、测试)创建和使用同一个类的多个实例。提供的下一个参数称为num_steps——这是我们将输入到网络的时间分布式输入层的单词数量。换句话说,这是模型将从中学习的一组单词,用于预测后面的单词。参数batch_size非常容易理解,我们已经讨论了词汇表(在本例中它等于10,000)。最后,skip_steps是每批训练样本之间要跳过的单词数量。为了更清楚地说明这一点,考虑下面这句话:

“The cat sat on the mat, and ate his hat. Then he jumped up and spat”

如果将num_steps设置为5,那么作为给定示例的输入数据使用的数据将是“The cat sat on the”。在本例中,由于我们通过模型预测序列中的下一个单词,对于每个时间步长,匹配的输出y或目标数据将是“cat sat on the mat”。最后,skip_steps是在下一个数据批处理之前要跳过的单词数量。在本例中,如果是skip_steps=num_steps,那么下一批的下5个输入单词将是“mat and ate his hat”。希望这能说得通。

需要讨论类初始化中的最后一项。这是变量current_idx,初始值为零。这个变量需要通过完整的数据集跟踪数据的提取——一旦在训练中使用了完整的数据集,我们需要将current_idx重置为零,以便再次从数据集的开始使用数据。换句话说,它基本上是一个数据集位置指针。

好的,现在我们需要讨论将在fit_generator期间调用的生成器方法.

def generate(self):
    x = np.zeros((self.batch_size, self.num_steps))
    y = np.zeros((self.batch_size, self.num_steps, self.vocabulary))
    while True:
        for i in range(self.batch_size):
            if self.current_idx + self.num_steps >= len(self.data):
                # reset the index back to the start of the data set
                self.current_idx = 0
            x[i, :] = self.data[self.current_idx:self.current_idx + self.num_steps]
            temp_y = self.data[self.current_idx + 1:self.current_idx + self.num_steps + 1]
            # convert all of temp_y into a one hot representation
            y[i, :, :] = to_categorical(temp_y, num_classes=self.vocabulary)
            self.current_idx += self.skip_step
        yield x, y

在前几行中创建了x和y输出数组。变量x的大小很容易理解——它的第一个维度是我们在批处理中指定的样本数量。第二个维度是我们预测的单词数量。变量y的大小有点复杂。首先将批大小作为第一个维度,然后将时间步长作为第二个维度,如上所述。然而,y还有一个额外的三维空间,与我们的词汇表大小相同,在本例中为10,000。

原因是Keras LSTM网络的输出层将是一个标准的softmax层,它将为10,000个可能的单词分配一个概率。具有最高概率的单词将是预测单词——换句话说,Keras LSTM网络将从10,000个可能的类别中预测一个单词。因此,为了训练这个网络,我们需要为每个单词创建一个训练样本,每个单词的真单词位置都是1,其他9999个位置都是0。它将看起来像这样:(0,0,0,…,1,0,…,0,0)-这被称为一个热(one-hot)表示,或者,一个分类表示。因此,对于每个目标单词,需要有一个10000长度的向量,该向量中只有一个元素被设置为1。

好了,现在讲while True: yield x, y范型,这是前面讨论过的生成器。在第一行中,我们输入一个大小为batch_size的for循环,以填充批处理中的所有数据。接下来,需要测试一个条件,即是否需要重置current_idx指针。记住,对于每个训练样本,我们使用num_steps单词。因此,如果当前索引点加上num_steps大于数据集的长度,则需要将current_idx指针重置为零以重新开始数据集。

执行此检查后,输入数据将被使用到x数组中。所使用的数据索引非常容易理解——它是当前索引到current-index-plus-num_steps单词数量的当前索引。接下来,一个临时变量y填充的工作方式差不多,唯一的区别在于,起始点和结束点的数据消费提前1(例如+ 1)。如果这是困惑,请参阅“cat sat on the mat etc.”上面所讨论的例子。

最后一步是将每个示例中的每个目标单词转换为前面讨论过的一个 one-hot或分类表示。为此,可以使用Keras to_categorical函数。这个函数以一系列整数作为它的第一个参数,并向整数向量添加一个额外的维度——这个维度是每个整数的一个 one-hot表示。它的大小由传递给函数的第二个参数指定。假设我们有一系列形状(100,1)的整数,我们将它传递给to_categorical函数,并指定大小为10,000—返回的形状将是(100,10000)。例如,假设整数的序列/向量看起来像(0,1,2,3,…),to_categorical输出看起来像:
(1, 0, 0, 0, 0, ….)

(0, 1, 0, 0, 0, ….)

(0, 0, 1, 0, 0, ….)

and so on…

这里的“…”表示一大堆零,确保与每个整数关联的元素总数为10,000。希望这能说得通。

生成器函数的最后两行代码非常简单——首先,current_idx指针由前面讨论过的skip_step递增。最后一行生成一批x和y数据。

现在已经创建了生成器类,我们需要创建它的实例。如前所述,我们可以设置相同类的实例来对应于训练和验证数据。在代码中,如下所示:

train_data_generator = KerasBatchGenerator(train_data, num_steps, batch_size, vocabulary,
                                           skip_step=num_steps)
valid_data_generator = KerasBatchGenerator(valid_data, num_steps, batch_size, vocabulary,
                                           skip_step=num_steps)

现在Keras LSTM代码的输入数据已经设置好,可以运行了,现在是创建LSTM网络本身的时候了。

创建Keras LSTM结构

在本例中,将使用构建深度学习网络的顺序方法。这种构建网络的方法是在我的Keras教程中介绍的——用11行代码构建卷积神经网络。在Keras中构建网络的另一种方法是函数API,我在Word2Vec Keras教程中使用了它。基本上,顺序方法允许您轻松地将层堆叠到您的网络中,而不必过多担心流经模型的所有张量(及其形状)。然而,对于一些更复杂的层,您仍然需要保持头脑清醒,下面将对此进行讨论。在这个例子中,它看起来像这样:

model = Sequential()
model.add(Embedding(vocabulary, hidden_size, input_length=num_steps))
model.add(LSTM(hidden_size, return_sequences=True))
model.add(LSTM(hidden_size, return_sequences=True))
if use_dropout:
    model.add(Dropout(0.5))
model.add(TimeDistributed(Dense(vocabulary)))
model.add(Activation('softmax'))

第一步是使用sequence()构造函数创建Keras模型。网络中的第一层,正如前面显示的架构图所示,是一个单词嵌入层。这将把我们的单词(由数据中的整数引用)转换成有意义的嵌入向量。这个Embedding() 层将词汇表的大小作为它的第一个参数,然后将生成的嵌入向量的大小作为下一个参数。最后,由于这一层是网络的第一层,我们必须指定输入的“长度”,即每个样本中的步骤/单词的数量。

跟踪网络中的张量形状是值得的——在本例中,嵌入层的输入是(batch_size, num_steps),输出是(batch_size, num_steps, hidden_size)。注意,在顺序模型中,Keras始终将批处理大小保持为第一个维度。它从Keras拟合函数(即本例中的fit_generator)接收批处理大小,因此它很少(从不?)包含在序列模型层的定义中。

下一层是我们两个LSTM层中的第一个。要指定LSTM层,首先必须提供LSTM单元中隐藏层中的节点数,例如忘记门层中的单元数、tanh压缩输入层中的单元数等等。上面代码中指定的下一个参数是return_sequences=True参数。这样做的目的是确保LSTM单元始终返回展开的LSTM单元的所有输出。如果省略了这个参数,LSTM单元将只提供来自最后一个时间步骤的LSTM单元的输出。下图显示了我的意思:
Keras LSTM教程
Keras LSTM返回序列参数比较
从上图中可以看出,当return_sequences=False – h_t时,只有一个输出。但是,当return_sequences=True时,将返回LSTM单元格的所有展开输出h_0h_t。在这种情况下,我们想要后者的安排。为什么?在这个例子中,我们试着预测序列中的下一个单词。然而,如果我们试图训练模型,最好是能够比较LSTM细胞输出在每个时间步长序列中的下一个词——通过这种方式,我们获得了num_steps源来纠正模型中的错误(通过反向传播),而不是只针对每个示例获得一个错误。

因此,对于两个堆叠的LSTM层,我们都希望返回所有序列。每个LSTM层的输出形状是(batch_size、num_steps、hidden_size)。

Keras LSTM网络的下一层是dropout层,以防止过拟合。然后,有一个特殊的Keras层用于循环神经网络称为TimeDistributed。该函数为递归模型中的每个时间步添加一个独立的层。例如,如果我们在一个模型中有10个时间步长,一个TimeDistributed层在一个Dense层上运行,会产生10个独立的Dense层,每个时间步长对应一个。在Keras LSTM模型的最后一层,这些Dense层的激活被设置为softmax。

编译并运行Keras LSTM模型

Keras中的下一步是,一旦您完成了模型,就在模型上运行compile命令。它是这样的:

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['categorical_accuracy'])

在这个命令中,需要指定Keras用于训练模型的损失类型。在本例中,我们使用的是“categorical_crosssentropy”,它是交叉熵,适用于多个类或类别,其中只有一个为真。接下来,在本例中,将使用的优化器是Adam优化器——一个具有自适应步进的有效的“全面”优化器。最后,我们指定了一个度量标准—“categorical_accuracy”,它可以让我们看到在训练过程中准确率是如何提高的。

下一行代码涉及创建一个Keras回调——回调是Keras可以选择调用的某些函数,通常在训练周期结束后调用。在这个例子中使用的回调是一个模型检查点回调——这个回调在每个epoch之后保存模型,这对于您运行长期训练时非常方便。

checkpointer = ModelCheckpoint(filepath=data_path + '/model-{epoch:02d}.hdf5', verbose=1)

注意,模型检查点函数可以在模型的命名中包含epoch,这对于跟踪事情很有好处。

训练Keras LSTM模型的最后一步是调用前面提到的fit_generator函数。下面这行代码向您展示了如何做到这一点:

model.fit_generator(train_data_generator.generate(), len(train_data)//(batch_size*num_steps), num_epochs,
                        validation_data=valid_data_generator.generate(),
                        validation_steps=len(valid_data)//(batch_size*num_steps), callbacks=[checkpointer])

fit_generator的第一个参数是前面解释过的生成器函数。下一个参数是每个训练周期要运行的迭代数。给定的值len(train_data)//(batch_size*num_steps)确保在每个历元中运行整个数据集。同样,调用较小验证数据集的生成器,并使用相同的参数来表示要运行的迭代数。在每个历元结束时,验证数据将在模型中运行,并返回精度。最后,上面解释的模型检查点回调是通过fit_generator中的回调参数提供的。现在模型可以使用了!

在给出一些结果之前,需要做一些说明。首先,PTB数据集是一个严重的文本数据集,而不是用来演示LSTM模型有多好的玩具问题。因此,为了获得良好的结果,您可能必须在多个时点上运行模型,并且模型需要具有相当高的复杂性。因此,在CPU机器上运行它可能需要很长时间,如果您想尝试复制一些东西,我建议您在使用好的GPU的机器上运行它。如果您自己没有GPU机器,您可以创建一个Amazon EC2实例。另一种选择是使用谷歌Colaboratory,它提供了免费的GPU时间。我属于后者,并没有打算给亚马逊太多的钱来训练、优化学习参数等等。但是,我已经运行了40个epoch的模型,并得到了一些合理的初始结果。我对结果的模型参数如下:

num_steps=30

batch_size=20

hidden_size=500

经过40个epoch,训练数据集精度约为40%,验证集精度约为20-25%。这是您在运行训练期间将看到的输出类型:
Keras LSTM教程
Keras LSTM教程-示例训练输出

Keras LSTM结果

为了测试训练Keras LSTM模型,可以比较预测输出与实际的什么词序列是在训练和测试数据集。下面的代码是如何做到这一点,一个片段的比较是对预测模型的输出和训练数据集(此句可以用test_data数据)。

model = load_model(data_path + "\model-40.hdf5")
dummy_iters = 40
example_training_generator = KerasBatchGenerator(train_data, num_steps, 1, vocabulary,
                                                     skip_step=1)
print("Training data:")
for i in range(dummy_iters):
    dummy = next(example_training_generator.generate())
num_predict = 10
true_print_out = "Actual words: "
pred_print_out = "Predicted words: "
for i in range(num_predict):
    data = next(example_training_generator.generate())
    prediction = model.predict(data[0])
    predict_word = np.argmax(prediction[:, num_steps-1, :])
    true_print_out += reversed_dictionary[train_data[num_steps + dummy_iters + i]] + " "
    pred_print_out += reversed_dictionary[predict_word] + " "
print(true_print_out)
print(pred_print_out)

在上面的代码中,首先从训练数据重新加载模型(在上面的例子中,它是来自训练第40 epoch的检查点)。然后创建另一个KerasBatchGenerator类,如前所述——在本例中,使用的批处理长度为1,因为我们只想比较一个值文本数据的num_steps。然后从生成器中创建一个伪数据提取循环——这是为了控制在数据集中从哪里提取比较语句。第二个循环,从0到num_predict是有趣的地方。

首先,从生成器中提取一批数据,并将其传递给model.predict()方法。这将返回预测值为num_steps的单词——但是,每个单词都由一个分类输出或一个one hot输出表示。换句话说,每个单词都由一个包含10,000项的向量表示,其中大多数为零,只有一个元素等于1。这个“1”的索引是实际英语单词的整数表示。因此,要提取出现“1”的索引,可以使用np.argmax()函数。这个函数标识了一个向量中最大值所在的索引——在本例中,与所有的零相比,最大值为1,因此这是一个便于使用的函数。

一旦确定了索引,就可以使用在数据预处理期间构造的reverse_dictionary将其翻译成实际的英语单词。然后将这个英语单词添加到预测单词字符串中,最后返回实际单词和预测单词。

下面的输出是训练数据集经过10个epochs训练后的实际单词与预测单词的比较:
Keras LSTM教程
训练10个epochs阶段后训练数据集的比较

可以看出,虽然有些词语搭配,经过10个epochs的训练,搭配还是相当差的。顺便说一下,“”指的是数据集10000个长度的词汇表中没有包含的单词。或者,如果我们在训练了40个epochs后对比一下(同样,仅在训练数据集上):
Keras LSTM教程
训练40个阶段后训练数据集的比较

可以看出,训练集中实际单词与预测单词的匹配度相当好。
然而,当我们看测试数据集时,经过40个epochs的训练后的比赛并不那么好:
Keras LSTM教程
经过40个epochs训练阶段的测试数据集比较

尽管预测词和实际词之间没有完美的对应关系,但你可以看到它们之间有一个粗略的对应关系,预测的子句至少在语法上是有意义的。毕竟还不错。然而,为了训练一个Keras LSTM网络能够很好地在这个现实的、大的文本语料库上运行,需要更多的训练和优化。我将把它留给你们,读者,如果你们愿意,做进一步的实验。但是,当前的代码足以让您了解如何构建Keras LSTM网络,以及LSTM网络背后的理论。

我希望这个(大型)教程能够帮助您理解Keras LSTM网络,以及一般的LSTM网络。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程