本文是演示了一个最简单的LSTM模型的构建,以0.1~0.9序列为例,做了一个预测的小实例,文章最后我新增了一个测试集,重新使用训练的模型进行一次预测,详细代码以及注释见文末。
源码地址:https://github.com/yangwohenmai/LSTM/tree/master/%E9%95%BF%E7%9F%AD%E6%9C%9F%E8%AE%B0%E5%BF%86(LSTM)/Keras%E4%B8%AD%E9%95%BF%E7%9F%AD%E6%9C%9F%E8%AE%B0%E5%BF%86%E6%A8%A1%E5%9E%8B%E7%9A%845%E6%AD%A5%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F
后续的3.2和3.3课程是一组课程的学习目录,以及作者自己新书的推荐,3.2章节会在后续学习中更新上来,3.3的内容大家对作者书记感兴趣的话可以进去看看。
3.2、https://machinelearningmastery.com/long-short-term-memory-recurrent-neural-networks-mini-course/
3.3、https://machinelearningmastery.com/lstms-with-python/
3.1的原文摘录如下:
在这篇文章中,您将发现在Keras中创建,训练和评估长期短期记忆(LSTM)回归神经网络的分步生命周期,以及如何使用训练有素的模型进行预测。
阅读这篇文章后,你会知道:
第一步是定义您的网络。
神经网络在Keras中被定义为层序列。这些图层的容器是Sequential类。
第一步是创建Sequential类的实例。然后,您可以创建图层并按照它们应连接的顺序添加它们。由存储器单元组成的LSTM循环层称为LSTM()。通常跟随LSTM层并用于输出预测的完全连接层称为Dense()。
例如,我们可以分两步完成:
model = Sequential()
model.add(LSTM(2))
model.add(Dense(1))
但是我们也可以通过创建一个图层数组并将其传递给Sequential的构造函数来一步完成。
layers = [LSTM(2), Dense(1)]
model = Sequential(layers)
网络中的第一层必须定义预期的输入数量。输入必须是三维的,包括样本,时间步和特征。
假设您的数据作为NumPy数组加载,您可以使用NumPy中的reshape()函数将2D数据集转换为3D数据集。如果您希望列成为一个功能的时间步长,您可以使用:
data = data.reshape((data.shape[0], 1, data.shape[1]))
如果您希望2D数据中的列成为具有一个时间步长的要素,则可以使用:
data = data.reshape((data.shape[0], 1, data.shape[1]))
您可以指定input_shape参数,该参数需要包含时间步数和要素数的元组。例如,如果我们有两个时间步长和一个特征用于单变量时间序列,每行有两个滞后观察值,则将指定如下:
model = Sequential()
model.add(LSTM(5, input_shape=(2,1)))
model.add(Dense(1))
可以通过将LSTM图层添加到Sequential模型来堆叠LSTM图层。重要的是,当堆叠LSTM层时,我们必须为每个输入输出序列而不是单个值,以便后续LSTM层可以具有所需的3D输入。我们可以通过将return_sequences参数设置为True来完成此操作。例如:
model = Sequential()
model.add(LSTM(5, input_shape=(2,1), return_sequences=True))
model.add(LSTM(5))
model.add(Dense(1))
将Sequential模型视为一个管道,其中您的原始数据最终被输入,而预测则来自另一个。
这在Keras中是一个有用的容器,因为传统上与图层相关的关注点也可以拆分并作为单独的图层添加,清楚地显示它们在从输入到预测的数据转换中的作用。将Sequential模型视为一个管道,其中您的原始数据最终被输入,而预测则来自另一个。
例如,可以提取转换来自层中每个神经元的求和信号的激活函数,并将其作为称为激活的层状对象添加到Sequential中。
model = Sequential()
model.add(LSTM(5, input_shape=(2,1)))
model.add(Dense(1))
model.add(Activation('sigmoid'))
激活函数的选择对于输出层是最重要的,因为它将定义预测将采用的格式。
例如,下面是一些常见的预测建模问题类型以及可以在输出层中使用的结构和标准激活函数:激活函数的选择对于输出层是最重要的,因为它将定义预测将采用的格式。
一旦我们定义了网络,我们就必须编译它。
编译是一个效率步骤。它将我们定义的简单图层序列转换为高效的矩阵变换系列,其格式应在GPU或CPU上执行,具体取决于Keras的配置方式。
将编译视为网络的预计算步骤。定义模型后始终需要它。
编译需要指定许多参数,专门用于培训您的网络。具体地,用于训练网络的优化算法和用于评估由优化算法最小化的网络的损失函数。
例如,下面是编译定义模型并指定随机梯度下降(sgd)优化算法和均值误差(mean_squared_error)损失函数的情况,用于回归类型问题。
model.compile(optimizer='sgd', loss='mean_squared_error')
或者,可以在作为编译步骤的参数提供之前创建和配置优化程序。
algorithm = SGD(lr=0.1, momentum=0.3)
model.compile(optimizer=algorithm, loss='mean_squared_error')
预测建模问题的类型对可以使用的损失函数的类型施加约束。
例如,下面是不同预测模型类型的一些标准损失函数:预测建模问题的类型对可以使用的损失函数的类型施加约束。
最常见的优化算法是随机梯度下降,但Keras还支持一套其他最先进的优化算法,这些算法在很少或没有配置的情况下都能很好地工作。
也许最常用的优化算法因为它们通常具有更好的性能:
最后,除了损失函数之外,您还可以指定在拟合模型时收集的度量标准。通常,要收集的最有用的附加度量标准是分类问题的准确性。要收集的度量标准由数组中的名称指定。
例如:
model.compile(optimizer='sgd', loss='mean_squared_error', metrics=['accuracy'])
一旦网络被编译,它就可以适合,这意味着在训练数据集上调整权重。
安装网络需要指定训练数据,包括输入模式矩阵X和匹配输出模式数组y。
使用反向传播算法训练网络,并根据编译模型时指定的优化算法和损失函数进行优化。
反向传播算法要求网络训练指定数量的时期或暴露于训练数据集。
每个纪元可以被划分为称为批次的输入 - 输出模式对的组。这定义了在一个纪元内更新权重之前网络所接触的模式数。它也是一种效率优化,确保一次不会将太多输入模式加载到内存中。
拟合网络的最小例子如下:
history = model.fit(X, y, batch_size=10, epochs=100)
适合后,将返回历史对象,该对象提供训练期间模型性能的摘要。这包括损失和编译模型时指定的任何其他指标,记录每个时期。
训练可能需要很长时间,从几秒到几小时到几天,具体取决于网络的大小和培训数据的大小。适合后,将返回历史对象,该对象提供训练期间模型性能的摘要。这包括损失和编译模型时指定的任何其他指标,记录每个时期。
默认情况下,每个纪元的命令行上都会显示一个进度条。这可能会给您带来太多噪音,或者可能会对您的环境造成问题,例如您使用的是交互式笔记本电脑或IDE。
通过将详细参数设置为2,可以减少每个时期显示的信息量。您可以通过将详细设置为1来关闭所有输出。例如:
history = model.fit(X, y, batch_size=10, epochs=100, verbose=0)
一旦网络被训练,就可以对其进行评估。
可以在训练数据上评估网络,但是这不会提供作为预测模型的网络性能的有用指示,因为它之前已经看到了所有这些数据。
我们可以在测试期间看不到的单独数据集上评估网络的性能。这将在未来对未见数据进行预测时提供网络性能的估计。
该模型评估所有测试模式的损失,以及编译模型时指定的任何其他指标,如分类准确性。返回评估指标列表。
例如,对于使用精度度量编制的模型,我们可以在新数据集上对其进行评估,如下所示:
loss, accuracy = model.evaluate(X, y)
与拟合网络一样,提供详细输出以了解评估模型的进度。我们可以通过将verbose参数设置为0来关闭它。
loss, accuracy = model.evaluate(X, y, verbose=0)
一旦我们对拟合模型的性能感到满意,我们就可以用它来预测新数据。
这就像使用一组新输入模式调用模型上的predict()函数一样简单。
例如:
predictions = model.predict(X)
预测将以网络输出层提供的格式返回。
在回归问题的情况下,这些预测可以是直接问题的格式,由线性激活函数提供。预测将以网络输出层提供的格式返回。
对于二元分类问题,预测可以是第一类的概率数组,其可以通过舍入转换为1或0。
对于多类分类问题,结果可以是概率数组的形式(假设一个热编码输出变量),可能需要使用argmax()NumPy函数将其转换为单个类输出预测。
或者,对于分类问题,我们可以使用predict_classes()函数,该函数会自动将uncrisp预测转换为清晰的整数类值。
predictions = model.predict_classes(X)
与拟合和评估网络一样,提供详细输出以给出预测模型进展的概念。我们可以通过将verbose参数设置为0来关闭它。
predictions = model.predict(X, verbose=0)
让我们将这一切与一个小例子结合起来。
这个例子将使用一个学习10个数字序列的简单问题。我们将向网络显示一个数字,例如0.0,并期望它预测为0.1。然后显示它0.1并期望它预测0.2,依此类推至0.9。
完整的代码清单如下。
# Example of LSTM to learn a sequence
from pandas import DataFrame
from pandas import concat
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
# create sequence
length = 10
sequence = [i/float(length) for i in range(length)]
print(sequence)
# create X/y pairs
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
df.dropna(inplace=True)
# convert to LSTM friendly format
values = df.values
X, y = values[:, 0], values[:, 1]
X = X.reshape(len(X), 1, 1)
# 1. define network
model = Sequential()
model.add(LSTM(10, input_shape=(1,1)))
model.add(Dense(1))
# 2. compile network
model.compile(optimizer='adam', loss='mean_squared_error')
# 3. fit network
history = model.fit(X, y, epochs=1000, batch_size=len(X), verbose=0)
# 4. evaluate network
loss = model.evaluate(X, y, verbose=0)
print(loss)
# 5. make predictions
predictions = model.predict(X, verbose=0)
print(predictions[:, 0])
运行此示例将生成以下输出,显示10个数字的原始输入序列,对整个序列进行预测时网络的均方误差丢失,以及每个输入模式的预测。
输出间隔开以便于阅读。运行此示例将生成以下输出,显示10个数字的原始输入序列,对整个序列进行预测时网络的均方误差丢失,以及每个输入模式的预测。
我们可以看到序列被很好地学习,特别是如果我们将预测舍入到第一个小数位。
1 2 3 4 5 6 |
[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
4.54527471447e-05
[ 0.11612834 0.20493418 0.29793766 0.39445466 0.49376178 0.59512401 0.69782174 0.80117452 0.90455914] |
本人又新增了一个小测试,原文是预测了0.1~0.9的数列,我将数列修改成0.5~1.4,仍然利用上述结果进行预测,测试预测值的准确率,效果如下:
[0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4]
1 2 3 4 5 6 |
[0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4]
[0.5920643 0.6938976 0.79730105 0.90165794 1.0063956 1.1109957 |
可以感觉到误差有所扩大,但对于这个预测结果还是可以接受的,说明模型仍有优化的空间,修改后的代码如下:
from pandas import DataFrame
from pandas import concat
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
# 创建一个0.1~0.9的序列
length = 10
sequence = [i/float(length) for i in range(length)]
print(sequence)
# 构建一个X->y的映射关系
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
df.dropna(inplace=True)
# 使用reshape方法,把序列转换为LSTM可识别的数组格式
values = df.values
X, y = values[:, 0], values[:, 1]
X = X.reshape(len(X), 1, 1)
# 1. 定义网络类型
model = Sequential()
model.add(LSTM(10, input_shape=(1,1)))
model.add(Dense(1))
# 2. 编译网络,设置损失参数
model.compile(optimizer='adam', loss='mean_squared_error')
# 3. 调用网络开始训练模型
history = model.fit(X, y, epochs=1000, batch_size=len(X), verbose=0)
# 4. 评估网络
loss = model.evaluate(X, y, verbose=0)
print(loss)
# 5. 利用训练好的模型,带入原始的X进行单步预测
predictions = model.predict(X, verbose=0)
print(predictions[:, 0])
# 创建一个0.1~0.9的序列
length = 10
sequence = [(i+5)/float(length) for i in range(length)]
print(sequence)
# 构建一个X->y的映射关系
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
df.dropna(inplace=True)
# 使用reshape方法,把序列转换为LSTM可识别的数组格式
values = df.values
X, y = values[:, 0], values[:, 1]
X = X.reshape(len(X), 1, 1)
predictions = model.predict(X, verbose=0)
print(predictions[:, 0])