在Keras中开发LSTM

1 定义模型

第一步是定义你的网络,keras中的神经网络被定义为一系列的层,这些层的容器是Sequential类。第一步是创建Sequential类的实例,然后可以创建相应的层,并按照它们应该连接的顺序添加它们。由存储单元组成的LSTM循环层被称为LSTM()。一个全连接层经常跟着一个LSTM层,用于输出一个预测的层被称为Dense()。

例如,我们可以定义一个具有两个存储单元的LSTM隐藏层,连接着带有一个神经元的一个全连接输出层。代码如下:

model = Sequential()

model.add(LSTM(2))

model.add(Dense(1))

但是我们也可以通过创建一个层的数组,并将其传递给Sequential类的构造函数来完成这一步。代码如下:

layers = [LSTM(2), Dense(1)]

model = Sequential(layers)

网络中的第一个隐藏层必须定义期望输出的数量,例如输入层的形状。输入必须是三维的,由样本、时间步长和特征组成。

样本:它们是你数据中的行,一个样本可以是一个序列。

时间步长:这些是对特征的过去观察值,例如滞后的变量。

特征:这些是数据中的列

假设你的数据被加载为了一个numpy数组,你可以使用numpy中的reshape()函数将1D或者2D的数据集转换为3D数据集。假设我们在numpy数组中有两个列的输入数据(X),我们可以把这两个列当做两个时间步长来对待,将其改写如下:

data = date.reshape((data.shape[0], date.shape[1], 1))

如果你让你的2D数据中的列成为一个时间步长的特征,你可以变形,如下:

data = data.reshape((data.shape[0], 1, data.shape[1]))

你可以指定输入形状参数,该参数期望是一个包含时间步长和特征数量的元组。例如,如果我们有一个单变量序列的两个时间步长和一个特征,每行具有两个之后的观测值,则具体如下:

model = Sequential()

model.add(LSTM(5, input_shape=(2,1)))

model.add(Dense(1))

样本的数量不必是指定的。模型假定一个或者多个样本,留给你定义的只有时间步长以及特征。把Sequential模型想像成为一个管道,一段输入原始数据,另外一段输出预测结果。在keras中这是一个很有用的容器,它传统上是和一层相联系的,可以拆分并添加一个单独的层。这样可以清楚的表明它们在数据从输入到预测的转换中的作用。例如激活函数,即变换一个从来自于一层的每一个神经元的求和的信号,可以被提取出来,并作为一个像层一样的对象,叫做Activation,添加到Sequential中。

model = Sequential()

model.add(LSTM(5, input_shape=(2,1)))

model.add(Dense(1))

model.add(Activation('sigmoid'))

激活函数的选择对于输出层来说是最重要的,因为它将决定预测所采用的格式。例如,下面是一些常见的预测建模问题类型和机构以及在输出层中使用的标准激活函数。

回归:线性激活函数,神经元的数量和输出的数量相匹配,这是用于全连接层的默认激活函数。

二分类(2类):逻辑激活函数,或者sigmoid,一个神经元的输出层。

多分类(大于2类):softmax激活函数,假设是one hot编码输出模式,每个类值一个输出神经元。

2 编译模型

一旦开发了我们的网络,我们就必须编译它。编译是一个有效的步骤。它将我们所定义的简单层序列转换成一系列高效的矩阵变换格式,以便在GPU或者CPU上执行,这取决于keras是如何配置的。将编译看做是一个网格的预计算步骤,一个模型建立之后总是需要编译的。

编译需要多个参数被指定,尤其是那些需要调整来训练你网络的。具体来说,用于网络训练的优化函数以及用于评价网络的损失函数,它被优化函数最小化了。

例如,下面是编译指定梯度下降(sgd)和均方差(mse)的损失函数模型的情况,用于解决回归类型的问题。

model.compile(optimizer='sgd', loss='mse')

或者在提供编译不走的参数之前创建和配置optimizer

algorithm = SGD(lr=0.1, momentum=0.3)

model.compile(optimizer=algorithm, loss='mse')

预测建模问题的类型可对所使用的损失函数进行约束。例如,下面是不同的预测模型类型的一些标准损失函数:

回归:均方误差,或者mean squared error,简称mse

二分类(2类):对数损失,也叫作交叉熵或者binary crossentropy

多分类(大于2类):多分类的对数损失,categorical crossentropy

最常见的优化算法是经典的梯度下降算法,但是keras还支持一套其他扩展的经典优化算法,这种算法表现很好,配置很少。由于它们通常的性能更好,也许最常用的优化算法是:

Stochastic Gradient Descent,或者sgd

Adam,或者Adam

RmSprop,或者rmsprop

最后,除了损失函数外,你也可以指定性能指标(Metrics)来收集拟合你模型时候的信息。总的来说,最有用的附加性能指标(Metrics)来收集的是分类问题的准确性(例如‘accuracy’或者简称‘acc’)。用来收集的性能指标(Metrics)可以通过性能指标数组中的名称或者损失函数的名字来指定。例如:

model.compile(optimizer='sgd', loss='mean_squared_error', metrics=['accuracy'])

3 拟合模型

一旦网络被编译,它就可以拟合,这意味着适应训练数据集上的权重。拟合网络需要指定训练数据集,包括输入模式的性能指标X以及和输出类型匹配的数组y。网络采用基于时间的反向传播算法进行训练,并根据优化算法和损失函数特性对模型进行优化。

反向传播算法要求对网络进行训练数据集中的所有序列进行特定数量的周期或者暴露数据集中的所有序列每个周期可以被分为成组的输入输出模式对,这被成为批次。这定义了网络在一个周期内更新权重之前所暴露的模式的数量。他也是一种有效的优化,确保在一段时间内没有太多的输入模式被加载到存储器中。

Epoch:遍历训练数据集中的所有样本,并更新网络权重。LSTM可以训练几十、几百或者数千个周期。

Batch:通过训练数据集中的样本子集,然后更新网络权值。一个周期是由一个或者多个批次组成的。 

下面是batch size一些常用的配置:

batch_size=1:在每个样本之后更新权重,并且该过程被称为随机梯度下降。

batch_size=32:通常的值是32,64和128,调整来符合预期的效率和模型更新速率。如果批大小不是一个周期中一个样本数量的因素,那么在周期的结尾,则在结束时运行剩余样本的一个额外的批的大小。

batch_size=n:其中,n是训练数据集的样本数。权重在每个周期中被更新,这个过程被称为批梯度下降。

batch_size为32的小批量梯度下降法是LSTM的一个常见配置。拟合网络的一个例子如下:

model.fit(X, y, batch_size=32, epochs=100)

一旦拟合,返回一个历史对象,该对象在训练期间提供对模型姓名的总结。当编译模型的时候,这包含损失和任何的额外的性能指标被指定,并在每个周期被记录。这些性能指标可以被记录、绘制和分析,以了解网络在训练数据集上是过拟合还是欠拟合。

根据网络规模和训练数据的大小,训练可能会花费很长的时间,从秒到数天。默认情况下,进度条显示在每个epoch的命令行上。这可能会对你造成很多的噪音,或者对你的环境造成问题,比如你在一个交互式的笔记本或IDE上。你可以通过将verbose参数设置为2来将显示的信息量减少到仅损失的每个周期。你可以通过设置verbose为0。例如:

history = model.fit(X, y, batch_size=10, epochs=100, verbose=0)

4 评价模型

一旦网络被训练,它就可以被评估。网络可以对训练数据进行评估,但是这不会作为预测模型提供网络性能的有用指示,因为它以前见过所有这些数据。我们可以在一个单独的数据集上评估网络的性能,在测试期间看不见。在未来对于看不见的数据,这将提供网络性能的估计。

该模型评估了所有测试模式的损失,以及模型编译时的任何其他度量,如分类精度。返回一个评估的度量表。如,对于使用精度度量编译模型,我们可以在新的数据集上对其进行评估如下

loss, accuracy = model.evaluate(X, y)

与网络一样,提出verbose输出的概念是为了评价模型的进度,我们可以通过将参数设置为0来关闭这个。

5 预测模型

一旦对我们的拟合模型的性能感到满意,我们可以用它来预测新的数据。这就在带有新输入模式数组的模型上调用predict()函数一样简单。例如:

predictions = model.predict(X)

预测将以网络的输出层提供的格式进行返回。在回归问题的情况下,这些预测可以直接和线性激活函数提供问题的格式一样。

对于一个二分类问题,预测可以是第一类的概率数组,它可以通过舍入转换为1或0,。对于一个多分类问题,结果可能是一个概率数组的形式(假设是一个one hot编码输出变量),他可能需要使用numpy的argmax函数转换成单类输出预测。另外,对于分类问题,我们可以使用predict_classes()函数来自动将不清晰的预测转换为清晰的整数类值。

predictions = model.predict_classes(X)

此处也可以添加verbose=0

6 LSTM状态管理

每个LSTM存储单元都保持着积累的内部状态。这种内部状态可能需要在网络训练和预测时对序列问题进行详细的管理。默认情况下,网络中的所有LSTM存储单元的内部状态在每个批次之后被重置,例如,当网络权重被更新时,这意味着批次大小的配置在三个时期上施加了张力。

  • 学习的正确性,或更新前处理多少个样本;

  • 学习的速度,或者权重的更新频率;

  • 内部状态,或内部状态重置频率。

通过顶一个LSTM层作为状态,Keras提供了将内部状态更新重置的很灵活的方式。这可以通过将LSTM层上的状态参数设置为true来实现。当使用状态LSTM层时,还必须通过设置输入形状参数来确定网络中输入形状的批大小(batch size),并且批处理大小必须是训练数据集中样本数量的一个因素。

批输入(batch input)形状参数需要一个定义为批处理大小、时间步长和特征的三维数组。

例如,我们可以定义一个状态LSTM,在训练数据集上训练100个样本,10个批次大小(batch size),1个特征的5个时间步长,如下:

model.add(LSTM(2, stateful=True, batch_input_shape=(10, 5, 1)))

状态LSTM不会在每个批次结束时重置内部状态。相反可以通过调用reset_states()函数对何时重置内部状态进行细粒度控制。例如,我们可能希望在每个周期结束时重置内部状态,我们可以这样做:

for i in range(1000):

    model.fit(X, y, epochs=1, batch_input_shape=(10, 5, 1))

    model.reset_states()

在进行预测时也必须使用相同状态的LSTM中的相同批次大小。

predictions = model.predict(X, batch_size=10)

 LSTM层的内部状态也在评估网络和进行预测时积累。因此,如果使用的是状态LSTM,则必须在验证数据集或者预测之后对网络进行重置状态。

在默认情况下,一个周期的状态被洗牌,在使用神经网络的多层感知机进行工作的时候是个很好的做法。如果您尝试在样本间保存状态,那么训练数据集中的样本顺序可能是重要的并且必须保留。这可以通过设置shuffle参数为false来完成,例如:

for i in range(1000):

    model.fit(X, y, epochs=1, shuffle=Flase, batch_input_shape=(10, 5, 1))

    model.reset_states() 

为了使这个更具体,下面是管理状态的3个常见的例子:

*在每个序列结束的时候进行预测,并且序列是独立的。状态应该在每个序列之后重置,通过将批次大小设置为1

*长序列被分割成多个子序列(每个样本具有许多的时间步长)。状态应该在网络暴露于整个序列之后通过LSTM状态化,关闭子序列的洗牌(shuffing),并在每个周期(epoch)之后重置状态。

*一个非常长的序列被分成多个子序列(每个样本具有很多时间步长)。训练质量比长期内部状态更重要,使用128个样本的批次大小,然后更新网络权重和状态重置。

7 准备数据的例子

7.1 单个输入样本的LSTM的例子

考虑一个具有多个时间步长和一个特征的序列。例如,这可以是10个值的序列。
0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0

from numpy import array
data = array([0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0])
#将这个一维数组变成三维数组,每个时间步长上有一个样本、10个时间步长和1个特征。在数组上调用时,reshape()函数采用一个参数,它是数组的新的形状。我们不能传递任何一组数字,变换必须要均匀的重新排列数组中的数据。
data = data.reshape((1,10,1))

#这个数据通过设置input_shape=(10, 1),现在已经准备好被用于输入(x)到LSTM模型中。
model = Sequential()
model.add(LSTM(32, input_type=(10, 1)))
7.2 多输入特征的LSTM例子

考虑到你的模型可能有多个并行序列作为输入的情况。例如,下面是有10个值的两个并行的序列:
series 1: 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0 series 2: 1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1

我们可以将这些数据定义为2列10行的数据:
from numpy import array
data = array([ [0.1, 1.0], [0.2, 0.9], [0.3, 0.8], [0.4, 0.7], [0.5, 0.6], [0.6, 0.5], [0.7, 0.4], [0.8, 0.3], [0.9, 0.2], [1.0, 0.1]])

这些数据可以被化为具有10个时间步长和2个特征的一个样本,它可以变形为3D数组如下。
data = data.reshape(1, 10, 2)

7.3 LSTM输入的建议

本章节列出了一些帮助您准备LSTM输入数据的提示:

LSTM输入层必须是3D的;

3个输入维度的含义是:样本、时间步长和特征。

LSTM输入层在输入隐藏层上由输入形状参数决定。

输入形状参数采用两个值的元组,以减少时间步长和特征的数量。

假设样本的数量是1个或者更多。

NumPy数组中的 reshape()函数可以用来将1D或者2D数据变换为3D的。

reshape()函数将元组作为新的形状的参数。
发布了352 篇原创文章 · 获赞 31 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/strawqqhat/article/details/97157165