[Python笔记] Keras-LSTM学习笔记

LSTM网络是一种循环神经网络。循环神经网络是一种神经网络,它试图对依赖于时间或顺序的行为(如语言、股价、电力需求等)进行建模。这是通过将神经网络层在t时刻的输出反馈给同一网络层在t + 1时刻的输入来实现的。它是这样的:
vanilla 循环神经网络(由常规神经网络节点构造)的问题在于,当我们尝试对由大量其他单词分隔的单词或序列值之间的依赖关系建模时,我们体验到了梯度消失问题(有时也是梯度爆炸问题)。这是因为小梯度或权重(小于 1 的值)在多个时间步长中乘以多次,并且梯度以不相通方式收缩为零。这意味着这些早期图层的权重不会显著更改,因此网络不会学习长期依赖关系。

1.结构

LSTM网络是一个循环神经网络,它用LSTM细胞块来代替我们的标准神经网络层。这些单元格有不同的组成部分,称为输入门、遗忘门和输出门——这些将在稍后详细解释。以下是LSTM单元格的图形表示:
在这里插入图片描述
首先,在左侧,我们将新单词/序列值xt连接到单元格ht−1的前一个输出。这个组合输入的第一步是通过tanh层进行压缩。第二步是这个输入通过一个输入门。输入门是一层sigmoid激活节点,其输出乘以压扁的输入。这些输入门sigmoid可以“杀死”输入向量中不需要的任何元素。一个sigmoid函数输出0到1之间的值,因此可以将连接到这些节点的输入的权重训练为接近于零的输出值来“关闭”某些输入值(或者相反,接近于1的输出值来“传递”其他值)。
通过此单元格的数据流的下一步是内部状态/遗忘门循环。LSTM细胞有一个内部状态变量st。这个变量,滞后一个时间步长,即st−1被添加到输入数据中,创建一个有效的递归层。这个加法运算,而不是乘法运算,有助于降低梯度消失的风险。 然而,这个递归循环是由遗忘门控制的——它的工作原理与输入门相同,但是它帮助网络学习哪些状态变量应该“记住”或“忘记”。
最后,我们有一个输出层tanh压扁函数,它的输出由一个输出门控制。这个门决定哪些值实际上可以作为单元格ht的输出。

2. Embedding

我们将设置所谓的嵌入层,将每个单词转换为有意义的单词向量。我们必须指定嵌入层的大小——这是每个单词所表示的向量的长度——通常在100-500之间。换句话说,如果嵌入层的大小为250,那么每个单词将由一个250长度的向量表示,i.e.[x1 ,x2 ,…,x250 ],那么LSTM单元中有250个隐藏层。
**我们通常将嵌入层输出的大小与LSTM单元中隐藏层的数量匹配起来。**您可能想知道LSTM单元格中的隐藏层来自何处。在我的LSTM概览图中,我简单地显示了输入数据流经的“data rails”。然而,单元格中的每个sigmoid、tanh或隐藏状态层实际上是一组节点,其数量等于隐藏层的大小。因此,LSTM细胞中的每个“节点”实际上是一组正常的神经网络节点,就像密集连接的神经网络中的每一层一样。

3.Keras LSTM

步骤:generator - model - fit - evaluate

generator

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

model

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始终将batch大小保持为第一个维度。它从Keras拟合函数(即本例中的fit_generator)接收batch大小,因此它很少包含在序列模型层的定义中,一般设置为函数接受batch参数。

下一层是两个LSTM层中的第一个。要指定LSTM层,首先必须提供LSTM单元中隐藏层中的节点数,例如忘却门层中的单元数、tanh压缩输入层中的单元数等等。
代码中指定的下一个参数是return_sequences=True参数。这样做的目的是确保LSTM单元始终返回展开的LSTM单元的所有输出。如果省略了这个参数,LSTM单元将只提供来自最后一个时间步骤的LSTM单元的输出(最后一层LSTM)。当return_sequences=False时,只有一个输出。但是,当return_sequences=True时,将返回LSTM单元格的所有展开输出h0… ht。在这种情况下,后者是适当的。为什么? 在这个例子中,我们试着预测序列中的下一个单词(数值)。然而,如果我们试图训练模型,最好是能够比较LSTM细胞输出在每个时间步长序列中的下一个词——通过这种方式,我们获得了num_steps纠正模型中的误差(BP),而不是只针对每个示例返回一个误差。

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

30s上手Keras

Keras 的核心数据结构是 model,一种组织网络层的方式。最简单的模型是 Sequential 顺序模型,它由多个网络层线性堆叠。对于更复杂的结构,你应该使用 Keras 函数式 API,它允许构建任意的神经网络图。
可以简单地使用 .add() 来堆叠模型。在完成了模型的构建后, 可以使用 .compile() 来配置学习过程。

from keras.models import Sequential
from keras.layers import Dense
model = Sequential()
model.add(Dense(units=64, activation='relu', input_dim=100))
model.add(Dense(units=10, activation='softmax'))
model.compile(loss='categorical_crossentropy',
              optimizer='sgd',
              metrics=['accuracy'])
# 编译模型时必须指明损失函数和优化器,如果你需要的话,也可以自己定制损失函数。Keras的一个核心理念就是简明易用同时,保证用户对Keras的绝对控制力度,用户可以根据自己的需要定制自己的模型、网络层,甚至修改源代码
#model.compile(loss=keras.losses.categorical_crossentropy,optimizer=keras.optimizers.SGD(lr=0.01, momentum=0.9, nesterov=True))
  1. Sequential()
    模型需要知道输入数据的shape,因此,Sequential的第一层需要接受一个关于输入数据shape的参数,后面的各个层则可以自动的推导出中间数据的shape,因此不需要为每个层都指定这个参数。有几种方法来为第一层指定输入数据的shape:
  • 传递一个input_shape的关键字参数给第一层,input_shape是一个tuple类型的数据,其中也可以填入None,** 如果填入None则表示此位置可能是任何正整数 ** 。数据的batch大小不应包含在其中。

  • 有些2D层,如Dense,支持通过指定其输入维度input_dim来隐含的指定输入数据shape,是一个Int类型的数据。一些3D的时域层支持通过参数input_dim和input_length来指定输入shape。

  • 如果你需要为输入指定一个固定大小的batch_size(常用于stateful RNN网络),可以传递batch_size参数到一个层中,例如你想指定输入张量的batch大小是32,数据shape是(6,8),则你需要传递batch_size=32和input_shape=(6,8)。

  1. Complie()
    compile接收三个参数:
  • 优化器optimizer:该参数可指定为已预定义的优化器名,如rmsprop、adagrad,或一个Optimizer类的对象,详情见optimizers

  • 损失函数loss:该参数为模型试图最小化的目标函数,它可为预定义的损失函数名,如categorical_crossentropy、mse,也可以为一个损失函数。详情见losses

  • 指标列表metrics:对分类问题,我们一般将该列表设置为metrics=[‘accuracy’]。指标可以是一个预定义指标的名字,也可以是一个用户定制的函数.指标函数应该返回单个张量,或一个完成metric_name - > metric_value映射的字典.请参考性能评估

Keras以Numpy数组作为输入数据和标签的数据类型。训练模型一般使用fit函数。现在,你可以批量地在训练数据上进行迭代了:

# x_train 和 y_train 是 Numpy 数组 -- 就像在 Scikit-Learn API 中一样。
model.fit(x_train, y_train, epochs=5, batch_size=32)
# 只需一行代码就能评估模型性能:
loss_and_metrics = model.evaluate(x_test, y_test, batch_size=128)
# 或者对新的数据生成预测:
classes = model.predict(x_test, batch_size=128)

对于Dense(), 注意定义第一层的时候需要制定数据输入的形状,即input_dim,这样才能让数据正常喂进网络!

Embedding层只能作为模型的第一层,单词嵌入是使用密集的矢量表示来表示单词和文档的一类方法。

Flatten层用来将输入“压平”,即把多维的输入一维化,常用在从卷积层到全连接层的过渡。Flatten不影响batch的大小。

常用方法

  1. fit_generator
fit_generator(self, generator, steps_per_epoch, epochs=1, 
						verbose=1, callbacks=None, 
						validation_data=None, 
						validation_steps=None, 
						class_weight=None, max_q_size=10, 
						workers=1, pickle_safe=False, 
						initial_epoch=0)
  • generator:生成器函数,生成器的输出应该为:
    一个形如(inputs,targets)的tuple
    一个形如(inputs, targets,sample_weight)的tuple。所有的返回值都应该包含相同数目的样本。生成器将无限在数据集上循环。

  • steps_per_epoch:整数,当生成器返回steps_per_epoch次数据时计一个epoch结束,执行下一个epoch。

  • validation_data:具有以下三种形式之一
    生成验证集的生成器
    一个形如(inputs,targets)的tuple
    一个形如(inputs,targets,sample_weights)的tuple

  1. Dense()
    定义第一层的时候需要制定数据输入的形状,即input_dim
  • units:该层有几个神经元
  • activation:该层使用的激活函数
  • use_bias:是否添加偏置项
keras.layers.Dense(units, 
				  activation=None, 
				  use_bias=True, 
				  kernel_initializer='glorot_uniform', 
				  bias_initializer='zeros', 
				  kernel_regularizer=None, 
				  bias_regularizer=None, 
			      activity_regularizer=None, 
				  kernel_constraint=None, 
				  bias_constraint=None)

  1. Dropout()
  • Hinton-2012-Improving neural networks by preventing co-adaptation of feature detectors提出:在每次训练前向传导的时候,让某个神经元的激活值以一定的概率p停止工作(Bernoulli以概率p,随机生成一个0\1向量),可以让一个神经元的出现不应该依赖于另外一个神经元,提高网络的泛化能力,称之为dropout。
  • 如果采用dropout,训练时间大大延长,但是对测试阶段没影响。
  • dropout-rescale:通常为了提高测试的性能(减少测试时的运算时间),可以将缩放的工作转移到训练阶段,而测试阶段与不使用dropout时相同。将前向传播dropout时保留下来的神经元的权重除p(期望: p*a+(1-p)*0=pa,测试时不用再缩小权重),这一改动仅会影响训练过程,而并不影响测试过程。
  • 我们在LSTM循环层和全连接层之间使用Dropout正则化。还有一种方法可以将Dropout与LSTM之类的循环层一起使用。LSTM可以将相同的Dropout掩码用于所有的输入中。这个方法也可用于跨样本时间步长的循环输入连接。这种使用递归模型进行Dropout正则化则称为变分循环神经网络(Variational RNN)。
model.add(Dropout(0.5))
  1. Embedding
    嵌入层被定义为网络的第一个隐藏层。它必须指定3个参数:

input_dim:这是文本数据中词汇的取值可能数。例如,如果您的数据是整数编码为0-9之间的值,那么词汇的大小就是10个单词;
output_dim:这是嵌入单词的向量空间的大小。它为每个单词定义了这个层的输出向量的大小。例如,它可能是32或100甚至更大,可以视为具体问题的超参数;
input_length:这是输入序列的长度,就像您为Keras模型的任何输入层所定义的一样,也就是一次输入带有的词汇个数。例如,如果您的所有输入文档都由1000个字组成,那么input_length就是1000。

例:embedding层将(1, 20)的一个输入sample(最长为20个单词的句子,其中每个单词表示为一个int数字),嵌入为一个(1, 20, 8)的向量,即将每个单词embed为一个8维的向量,而整个embedding层的参数就由神经网络学习得到,数据经过embedding层之后就方便地转换为了可以由CNN或者RNN进一步处理的格式。

  1. Callback()
    keras.callbacks.Callback()
    params: 字典:verbosity, batch size, number of epochs…
    model: keras.models.Model 的实例,指代被训练模型。
    被回调函数作为参数的 logs 字典,它会含有于当前批量或训练轮相关数据的键。目前,Sequential 模型类的 .fit() 方法会在传入到回调函数的 logs 里面包含以下的数据:
  • on_epoch_end: 包括 acc 和 loss 的日志, 也可以选择性的包括 val_loss(如果在 fit 中启用验证),和 val_acc(如果启用验证和监测精确值。
  • on_batch_begin: 包括 size 的日志,在当前批量内的样本数量。
  • on_batch_end: 包括 loss 的日志,也可以选择性的包括 acc(如果启用监测精确值。
    可自定义Callback,例如绘制神经网络训练iteration_loss曲线,参考:使用keras绘制实时的loss与acc曲线
发布了12 篇原创文章 · 获赞 75 · 访问量 2万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章