Machine Learning Mastery 深度学习时间序列教程(十六)
如何用 Keras 为时间序列预测调整 LSTM 超参数
原文:
machinelearningmastery.com/tune-lstm-hyperparameters-keras-time-series-forecasting/
配置神经网络很困难,因为没有关于如何做到这一点的好理论。
您必须系统地从探索的动态和客观结果点探索不同的配置,以尝试理解给定预测性建模问题的发生情况。
在本教程中,您将了解如何在时间序列预测问题上探索如何配置 LSTM 网络。
完成本教程后,您将了解:
- 如何调整和解释训练时期数的结果。
- 如何调整和解释训练批次大小的结果。
- 如何调整和解释神经元数量的结果。
让我们开始吧。
如何用 Keras 调整 LSTM 超参数用于时间序列预测 照片由 David Saddler 保留,保留一些权利。
教程概述
本教程分为 6 个部分;他们是:
- 洗发水销售数据集
- 实验测试线束
- 调整时代数量
- 调整批量大小
- 调整神经元数量
- 结果摘要
环境
本教程假定您已安装 Python SciPy 环境。您可以在此示例中使用 Python 2 或 3。
本教程假设您安装了 TensorFlow 或 Theano 后端的 Keras v2.0 或更高版本。
本教程还假设您安装了 scikit-learn,Pandas,NumPy 和 Matplotlib。
如果您在设置 Python 环境时需要帮助,请参阅以下帖子:
洗发水销售数据集
该数据集描述了 3 年期间每月洗发水的销售数量。
单位是销售计数,有 36 个观察。原始数据集归功于 Makridakis,Wheelwright 和 Hyndman(1998)。
下面的示例加载并创建已加载数据集的图。
# load and plot dataset
from pandas import read_csv
from pandas import datetime
from matplotlib import pyplot
# load dataset
def parser(x):
return datetime.strptime('190'+x, '%Y-%m')
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# summarize first few rows
print(series.head())
# line plot
series.plot()
pyplot.show()
运行该示例将数据集作为 Pandas Series 加载并打印前 5 行。
Month
1901-01-01 266.0
1901-02-01 145.9
1901-03-01 183.1
1901-04-01 119.3
1901-05-01 180.3
Name: Sales, dtype: float64
然后创建该系列的线图,显示明显的增加趋势。
洗发水销售数据集的线图
接下来,我们将了解实验中使用的 LSTM 配置和测试工具。
实验测试线束
本节介绍本教程中使用的测试工具。
数据拆分
我们将 Shampoo Sales 数据集分为两部分:训练和测试集。
前两年的数据将用于训练数据集,剩余的一年数据将用于测试集。
将使用训练数据集开发模型,并对测试数据集做出预测。
测试数据集的持久性预测(朴素预测)实现了每月洗发水销售 136.761 的错误。这在测试集上提供了较低的可接受表现限制。
模型评估
将使用滚动预测场景,也称为前进模型验证。
测试数据集的每个时间步骤将一次一个地走。将使用模型对时间步长做出预测,然后将获取测试集的实际预期值,并使其可用于下一时间步的预测模型。
这模仿了一个真实世界的场景,每个月都会有新的洗发水销售观察结果,并用于下个月的预测。
这将通过训练和测试数据集的结构进行模拟。我们将以一次性方法进行所有预测。
将收集关于测试数据集的所有预测,并计算错误分数以总结模型的技能。将使用均方根误差(RMSE),因为它会对大错误进行处罚,并产生与预测数据相同的分数,即每月洗发水销售额。
数据准备
在我们将 LSTM 模型拟合到数据集之前,我们必须转换数据。
在拟合模型和做出预测之前,对数据集执行以下三个数据变换。
- 转换时间序列数据,使其静止不动。具体而言,滞后= 1 差分以消除数据中的增加趋势。
- 将时间序列转换为监督学习问题。具体而言,将数据组织成输入和输出模式,其中前一时间步的观察被用作预测当前时间步的观察的输入
- 将观察结果转换为具有特定比例。具体而言,要将数据重缩放为-1 到 1 之间的值,以满足 LSTM 模型的默认双曲正切激活函数。
这些变换在预测时反转,在计算和误差分数之前将它们恢复到原始比例。
实验运行
每个实验场景将运行 10 次。
其原因在于,每次训练给定配置时,LSTM 网络的随机初始条件可能导致非常不同的结果。
诊断方法将用于研究模型配置。这是创建和研究模型技能随时间变化的线图(称为时期的训练迭代),以深入了解给定配置如何执行以及如何调整以获得更好的表现。
在每个时期结束时,将在训练和测试数据集上评估模型,并保存 RMSE 分数。
打印每个方案结束时的训练和测试 RMSE 分数,以指示进度。
一系列训练和测试 RMSE 得分在运行结束时绘制为线图。训练得分为蓝色,考试成绩为橙色。
让我们深入研究结果。
调整时代数量
我们将看调整的第一个 LSTM 参数是训练时期的数量。
该模型将使用批量大小为 4 和单个神经元。我们将探讨针对不同数量的训练时期训练此配置的效果。
500 个时代的诊断
下面列出了此诊断的完整代码清单。
代码的评论相当好,应该很容易理解。此代码将成为本教程中所有未来实验的基础,并且仅列出每个后续实验中所做的更改。
from pandas import DataFrame
from pandas import Series
from pandas import concat
from pandas import read_csv
from pandas import datetime
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from math import sqrt
import matplotlib
# be able to save images on server
matplotlib.use('Agg')
from matplotlib import pyplot
import numpy
# date-time parsing function for loading the dataset
def parser(x):
return datetime.strptime('190'+x, '%Y-%m')
# frame a sequence as a supervised learning problem
def timeseries_to_supervised(data, lag=1):
df = DataFrame(data)
columns = [df.shift(i) for i in range(1, lag+1)]
columns.append(df)
df = concat(columns, axis=1)
df = df.drop(0)
return df
# create a differenced series
def difference(dataset, interval=1):
diff = list()
for i in range(interval, len(dataset)):
value = dataset[i] - dataset[i - interval]
diff.append(value)
return Series(diff)
# scale train and test data to [-1, 1]
def scale(train, test):
# fit scaler
scaler = MinMaxScaler(feature_range=(-1, 1))
scaler = scaler.fit(train)
# transform train
train = train.reshape(train.shape[0], train.shape[1])
train_scaled = scaler.transform(train)
# transform test
test = test.reshape(test.shape[0], test.shape[1])
test_scaled = scaler.transform(test)
return scaler, train_scaled, test_scaled
# inverse scaling for a forecasted value
def invert_scale(scaler, X, yhat):
new_row = [x for x in X] + [yhat]
array = numpy.array(new_row)
array = array.reshape(1, len(array))
inverted = scaler.inverse_transform(array)
return inverted[0, -1]
# evaluate the model on a dataset, returns RMSE in transformed units
def evaluate(model, raw_data, scaled_dataset, scaler, offset, batch_size):
# separate
X, y = scaled_dataset[:,0:-1], scaled_dataset[:,-1]
# reshape
reshaped = X.reshape(len(X), 1, 1)
# forecast dataset
output = model.predict(reshaped, batch_size=batch_size)
# invert data transforms on forecast
predictions = list()
for i in range(len(output)):
yhat = output[i,0]
# invert scaling
yhat = invert_scale(scaler, X[i], yhat)
# invert differencing
yhat = yhat + raw_data[i]
# store forecast
predictions.append(yhat)
# report performance
rmse = sqrt(mean_squared_error(raw_data[1:], predictions))
return rmse
# fit an LSTM network to training data
def fit_lstm(train, test, raw, scaler, batch_size, nb_epoch, neurons):
X, y = train[:, 0:-1], train[:, -1]
X = X.reshape(X.shape[0], 1, X.shape[1])
# prepare model
model = Sequential()
model.add(LSTM(neurons, batch_input_shape=(batch_size, X.shape[1], X.shape[2]), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
# fit model
train_rmse, test_rmse = list(), list()
for i in range(nb_epoch):
model.fit(X, y, epochs=1, batch_size=batch_size, verbose=0, shuffle=False)
model.reset_states()
# evaluate model on train data
raw_train = raw[-(len(train)+len(test)+1):-len(test)]
train_rmse.append(evaluate(model, raw_train, train, scaler, 0, batch_size))
model.reset_states()
# evaluate model on test data
raw_test = raw[-(len(test)+1):]
test_rmse.append(evaluate(model, raw_test, test, scaler, 0, batch_size))
model.reset_states()
history = DataFrame()
history['train'], history['test'] = train_rmse, test_rmse
return history
# run diagnostic experiments
def run():
# load dataset
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# transform data to be stationary
raw_values = series.values
diff_values = difference(raw_values, 1)
# transform data to be supervised learning
supervised = timeseries_to_supervised(diff_values, 1)
supervised_values = supervised.values
# split data into train and test-sets
train, test = supervised_values[0:-12], supervised_values[-12:]
# transform the scale of the data
scaler, train_scaled, test_scaled = scale(train, test)
# fit and evaluate model
train_trimmed = train_scaled[2:, :]
# config
repeats = 10
n_batch = 4
n_epochs = 500
n_neurons = 1
# run diagnostic tests
for i in range(repeats):
history = fit_lstm(train_trimmed, test_scaled, raw_values, scaler, n_batch, n_epochs, n_neurons)
pyplot.plot(history['train'], color='blue')
pyplot.plot(history['test'], color='orange')
print('%d) TrainRMSE=%f, TestRMSE=%f' % (i, history['train'].iloc[-1], history['test'].iloc[-1]))
pyplot.savefig('epochs_diagnostic.png')
# entry point
run()
运行实验在 10 次实验运行的每一次结束时打印训练的 RMSE 和测试集。
0) TrainRMSE=63.495594, TestRMSE=113.472643
1) TrainRMSE=60.446307, TestRMSE=100.147470
2) TrainRMSE=59.879681, TestRMSE=95.112331
3) TrainRMSE=66.115269, TestRMSE=106.444401
4) TrainRMSE=61.878702, TestRMSE=86.572920
5) TrainRMSE=73.519382, TestRMSE=103.551694
6) TrainRMSE=64.407033, TestRMSE=98.849227
7) TrainRMSE=72.684834, TestRMSE=98.499976
8) TrainRMSE=77.593773, TestRMSE=124.404747
9) TrainRMSE=71.749335, TestRMSE=126.396615
还创建了在每个训练时期之后训练和测试集上的一系列 RMSE 得分的线图。
500 个时期的诊断结果
结果清楚地表明 RMSE 在几乎所有实验运行的训练时期都呈下降趋势。
这是一个好兆头,因为它表明模型正在学习问题并具有一些预测技巧。实际上,所有最终测试分数都低于简单持久性模型(朴素预测)的误差,该模型在此问题上达到了 136.761 的 RMSE。
结果表明,更多的训练时期将导致更熟练的模型。
让我们尝试将时期数从 500 增加到 1000。
1000 个时期的诊断
在本节中,我们使用相同的实验设置,并使模型适合 1000 个训练时期。
具体地,n_epochs参数在run()函数中被设置为1000。
n_epochs = 1000
运行该示例为最后一个时期的训练和测试集打印 RMSE。
0) TrainRMSE=69.242394, TestRMSE=90.832025
1) TrainRMSE=65.445810, TestRMSE=113.013681
2) TrainRMSE=57.949335, TestRMSE=103.727228
3) TrainRMSE=61.808586, TestRMSE=89.071392
4) TrainRMSE=68.127167, TestRMSE=88.122807
5) TrainRMSE=61.030678, TestRMSE=93.526607
6) TrainRMSE=61.144466, TestRMSE=97.963895
7) TrainRMSE=59.922150, TestRMSE=94.291120
8) TrainRMSE=60.170052, TestRMSE=90.076229
9) TrainRMSE=62.232470, TestRMSE=98.174839
还创建了每个时期的测试和训练 RMSE 得分的线图。
1000 个时期的诊断结果
我们可以看到模型误差的下降趋势确实继续并且似乎变慢。
训练和测试案例的线条变得更加横向,但仍然普遍呈下降趋势,尽管变化率较低。测试误差的一些示例显示可能的拐点大约 600 个时期并且可能显示出上升趋势。
值得进一步延长时代。我们对测试集中的平均表现持续改进感兴趣,这可能会持续下去。
让我们尝试将时期数从 1000 增加到 2000。
2000 年的诊断
在本节中,我们使用相同的实验设置,并使模型适合 2000 个训练时期。
具体地,在run()函数中将n_epochs参数设置为 2000。
n_epochs = 2000
运行该示例为最后一个时期的训练和测试集打印 RMSE。
0) TrainRMSE=67.292970, TestRMSE=83.096856
1) TrainRMSE=55.098951, TestRMSE=104.211509
2) TrainRMSE=69.237206, TestRMSE=117.392007
3) TrainRMSE=61.319941, TestRMSE=115.868142
4) TrainRMSE=60.147575, TestRMSE=87.793270
5) TrainRMSE=59.424241, TestRMSE=99.000790
6) TrainRMSE=66.990082, TestRMSE=80.490660
7) TrainRMSE=56.467012, TestRMSE=97.799062
8) TrainRMSE=60.386380, TestRMSE=103.810569
9) TrainRMSE=58.250862, TestRMSE=86.212094
还创建了每个时期的测试和训练 RMSE 得分的线图。
2000 年的诊断结果
正如人们可能已经猜到的那样,在训练和测试数据集的额外 1000 个时期内,误差的下降趋势仍在继续。
值得注意的是,大约一半的案例一直持续减少到运行结束,而其余案件则显示出增长趋势的迹象。
增长的趋势是过拟合的迹象。这是模型过拟合训练数据集的代价,代价是测试数据集的表现更差。通过对训练数据集的持续改进以及随后的测试数据集中的拐点和恶化技能的改进来举例说明。不到一半的运行显示了测试数据集中此类模式的开始。
然而,测试数据集的最终时期结果非常好。如果有机会我们可以通过更长时间的训练获得进一步的收益,我们必须探索它。
让我们尝试将 2000 年到 4000 年的时期数量加倍。
4000 个时代的诊断
在本节中,我们使用相同的实验设置,并使模型适合超过 4000 个训练时期。
具体地,在run()函数中将n_epochs参数设置为 4000。
n_epochs = 4000
运行该示例为最后一个时期的训练和测试集打印 RMSE。
0) TrainRMSE=58.889277, TestRMSE=99.121765
1) TrainRMSE=56.839065, TestRMSE=95.144846
2) TrainRMSE=58.522271, TestRMSE=87.671309
3) TrainRMSE=53.873962, TestRMSE=113.920076
4) TrainRMSE=66.386299, TestRMSE=77.523432
5) TrainRMSE=58.996230, TestRMSE=136.367014
6) TrainRMSE=55.725800, TestRMSE=113.206607
7) TrainRMSE=57.334604, TestRMSE=90.814642
8) TrainRMSE=54.593069, TestRMSE=105.724825
9) TrainRMSE=56.678498, TestRMSE=83.082262
还创建了每个时期的测试和训练 RMSE 得分的线图。
4000 个时期的诊断结果
类似的模式仍在继续。
即使在 4000 个时代,也存在改善表现的总趋势。有一种严重过拟合的情况,其中测试误差急剧上升。
同样,大多数运行以“良好”(优于持久性)最终测试错误结束。
结果摘要
上面的诊断运行有助于探索模型的动态行为,但缺乏客观和可比较的平均表现。
我们可以通过重复相同的实验并计算和比较每个配置的摘要统计量来解决这个问题。在这种情况下,30 个运行完成了迭代值 500,1000,2000,4000 和 6000。
我们的想法是在大量运行中使用汇总统计量比较配置,并确切地了解哪些配置可能在平均情况下表现更好。
完整的代码示例如下所示。
from pandas import DataFrame
from pandas import Series
from pandas import concat
from pandas import read_csv
from pandas import datetime
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from math import sqrt
import matplotlib
# be able to save images on server
matplotlib.use('Agg')
from matplotlib import pyplot
import numpy
# date-time parsing function for loading the dataset
def parser(x):
return datetime.strptime('190'+x, '%Y-%m')
# frame a sequence as a supervised learning problem
def timeseries_to_supervised(data, lag=1):
df = DataFrame(data)
columns = [df.shift(i) for i in range(1, lag+1)]
columns.append(df)
df = concat(columns, axis=1)
df = df.drop(0)
return df
# create a differenced series
def difference(dataset, interval=1):
diff = list()
for i in range(interval, len(dataset)):
value = dataset[i] - dataset[i - interval]
diff.append(value)
return Series(diff)
# invert differenced value
def inverse_difference(history, yhat, interval=1):
return yhat + history[-interval]
# scale train and test data to [-1, 1]
def scale(train, test):
# fit scaler
scaler = MinMaxScaler(feature_range=(-1, 1))
scaler = scaler.fit(train)
# transform train
train = train.reshape(train.shape[0], train.shape[1])
train_scaled = scaler.transform(train)
# transform test
test = test.reshape(test.shape[0], test.shape[1])
test_scaled = scaler.transform(test)
return scaler, train_scaled, test_scaled
# inverse scaling for a forecasted value
def invert_scale(scaler, X, yhat):
new_row = [x for x in X] + [yhat]
array = numpy.array(new_row)
array = array.reshape(1, len(array))
inverted = scaler.inverse_transform(array)
return inverted[0, -1]
# fit an LSTM network to training data
def fit_lstm(train, batch_size, nb_epoch, neurons):
X, y = train[:, 0:-1], train[:, -1]
X = X.reshape(X.shape[0], 1, X.shape[1])
model = Sequential()
model.add(LSTM(neurons, batch_input_shape=(batch_size, X.shape[1], X.shape[2]), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
for i in range(nb_epoch):
model.fit(X, y, epochs=1, batch_size=batch_size, verbose=0, shuffle=False)
model.reset_states()
return model
# run a repeated experiment
def experiment(repeats, series, epochs):
# transform data to be stationary
raw_values = series.values
diff_values = difference(raw_values, 1)
# transform data to be supervised learning
supervised = timeseries_to_supervised(diff_values, 1)
supervised_values = supervised.values
# split data into train and test-sets
train, test = supervised_values[0:-12], supervised_values[-12:]
# transform the scale of the data
scaler, train_scaled, test_scaled = scale(train, test)
# run experiment
error_scores = list()
for r in range(repeats):
# fit the model
batch_size = 4
train_trimmed = train_scaled[2:, :]
lstm_model = fit_lstm(train_trimmed, batch_size, epochs, 1)
# forecast the entire training dataset to build up state for forecasting
train_reshaped = train_trimmed[:, 0].reshape(len(train_trimmed), 1, 1)
lstm_model.predict(train_reshaped, batch_size=batch_size)
# forecast test dataset
test_reshaped = test_scaled[:,0:-1]
test_reshaped = test_reshaped.reshape(len(test_reshaped), 1, 1)
output = lstm_model.predict(test_reshaped, batch_size=batch_size)
predictions = list()
for i in range(len(output)):
yhat = output[i,0]
X = test_scaled[i, 0:-1]
# invert scaling
yhat = invert_scale(scaler, X, yhat)
# invert differencing
yhat = inverse_difference(raw_values, yhat, len(test_scaled)+1-i)
# store forecast
predictions.append(yhat)
# report performance
rmse = sqrt(mean_squared_error(raw_values[-12:], predictions))
print('%d) Test RMSE: %.3f' % (r+1, rmse))
error_scores.append(rmse)
return error_scores
# load dataset
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# experiment
repeats = 30
results = DataFrame()
# vary training epochs
epochs = [500, 1000, 2000, 4000, 6000]
for e in epochs:
results[str(e)] = experiment(repeats, series, e)
# summarize results
print(results.describe())
# save boxplot
results.boxplot()
pyplot.savefig('boxplot_epochs.png')
首先运行代码打印 5 个配置中每个配置的摘要统计量。值得注意的是,这包括每个结果群体的 RMSE 得分的平均值和标准差。
均值给出了配置的平均预期表现的概念,而标准偏差给出了方差的概念。最小和最大 RMSE 分数还可以了解可能预期的最佳和最差情况示例的范围。
仅查看平均 RMSE 分数,结果表明配置为 1000 的迭代可能更好。结果还表明可能需要进一步调查 1000 至 2000 年的时代价值。
500 1000 2000 4000 6000
count 30.000000 30.000000 30.000000 30.000000 30.000000
mean 109.439203 104.566259 107.882390 116.339792 127.618305
std 14.874031 19.097098 22.083335 21.590424 24.866763
min 87.747708 81.621783 75.327883 77.399968 90.512409
25% 96.484568 87.686776 86.753694 102.127451 105.861881
50% 110.891939 98.942264 116.264027 121.898248 125.273050
75% 121.067498 119.248849 125.518589 130.107772 150.832313
max 138.879278 139.928055 146.840997 157.026562 166.111151
分布也显示在盒子和须状图上。这有助于了解分布如何直接比较。
绿线显示中位数,方框显示第 25 和第 75 百分位数,或中间 50%的数据。该比较还表明,将时期设置为 1000 的选择优于测试的替代方案。它还表明,在 2000 年或 4000 年的时期内可以实现最佳表现,但代价是平均表现更差。
框和晶须图总结时代结果
接下来,我们将看看批量大小的影响。
调整批量大小
批量大小控制更新网络权重的频率。
重要的是在 Keras 中,批量大小必须是测试大小和训练数据集的一个因子。
在上一节探索训练时期的数量时,批量大小固定为 4,它完全分为测试数据集(大小为 12)和测试数据集的截断版本(大小为 20)。
在本节中,我们将探讨改变批量大小的效果。我们将训练时期的数量保持在 1000。
1000 个时期的诊断和 4 的批量大小
作为提醒,上一节在第二个实验中评估了批量大小为 4,其中一些时期为 1000。
结果显示出错误的下降趋势,大多数运行一直持续到最后的训练时期。
1000 个时期的诊断结果
1000 个时期的诊断和 2 的批量大小
在本节中,我们将批量大小从 4 减半。
对run()函数中的n_batch参数进行了此更改;例如:
n_batch = 2
运行该示例显示了与批量大小为 4 相同的总体趋势,可能在最后一个时期具有更高的 RMSE。
这些运行可能会显示出更快稳定 RMES 的行为,而不是看似继续下行趋势。
下面列出了每次运行的最终暴露的 RSME 分数。
0) TrainRMSE=63.510219, TestRMSE=115.855819
1) TrainRMSE=58.336003, TestRMSE=97.954374
2) TrainRMSE=69.163685, TestRMSE=96.721446
3) TrainRMSE=65.201764, TestRMSE=110.104828
4) TrainRMSE=62.146057, TestRMSE=112.153553
5) TrainRMSE=58.253952, TestRMSE=98.442715
6) TrainRMSE=67.306530, TestRMSE=108.132021
7) TrainRMSE=63.545292, TestRMSE=102.821356
8) TrainRMSE=61.693847, TestRMSE=99.859398
9) TrainRMSE=58.348250, TestRMSE=99.682159
还创建了每个时期的测试和训练 RMSE 得分的线图。
1000 个时期和批量大小为 2 的诊断结果
让我们再试一次批量。
1000 个时期的诊断和 1 的批量大小
批量大小为 1 在技术上执行在线学习。
这是在每个训练模式之后更新网络的地方。这可以与批量学习形成对比,其中权重仅在每个时期结束时更新。
我们可以在run()函数中更改n_batch参数;例如:
n_batch = 1
同样,运行该示例将打印每次运行的最后一个时期的 RMSE 分数。
0) TrainRMSE=60.349798, TestRMSE=100.182293
1) TrainRMSE=62.624106, TestRMSE=95.716070
2) TrainRMSE=64.091859, TestRMSE=98.598958
3) TrainRMSE=59.929993, TestRMSE=96.139427
4) TrainRMSE=59.890593, TestRMSE=94.173619
5) TrainRMSE=55.944968, TestRMSE=106.644275
6) TrainRMSE=60.570245, TestRMSE=99.981562
7) TrainRMSE=56.704995, TestRMSE=111.404182
8) TrainRMSE=59.909065, TestRMSE=90.238473
9) TrainRMSE=60.863807, TestRMSE=105.331214
还创建了每个时期的测试和训练 RMSE 得分的线图。
该图表明测试 RMSE 随时间变化的可变性更大,并且可能是训练 RMSE 比较大的批量大小更快稳定。测试 RMSE 的可变性增加是可以预期的,因为对网络进行的大量更改会给每次更新提供如此少的反馈。
该图还表明,如果配置提供更多的训练时期,RMSE 的下降趋势可能会继续。
1000 个时期和批量大小为 1 的诊断结果
结果摘要
与训练时期一样,我们可以客观地比较给定不同批量大小的网络表现。
每个配置运行 30 次,并根据最终结果计算汇总统计量。
...
# run a repeated experiment
def experiment(repeats, series, batch_size):
# transform data to be stationary
raw_values = series.values
diff_values = difference(raw_values, 1)
# transform data to be supervised learning
supervised = timeseries_to_supervised(diff_values, 1)
supervised_values = supervised.values
# split data into train and test-sets
train, test = supervised_values[0:-12], supervised_values[-12:]
# transform the scale of the data
scaler, train_scaled, test_scaled = scale(train, test)
# run experiment
error_scores = list()
for r in range(repeats):
# fit the model
train_trimmed = train_scaled[2:, :]
lstm_model = fit_lstm(train_trimmed, batch_size, 1000, 1)
# forecast the entire training dataset to build up state for forecasting
train_reshaped = train_trimmed[:, 0].reshape(len(train_trimmed), 1, 1)
lstm_model.predict(train_reshaped, batch_size=batch_size)
# forecast test dataset
test_reshaped = test_scaled[:,0:-1]
test_reshaped = test_reshaped.reshape(len(test_reshaped), 1, 1)
output = lstm_model.predict(test_reshaped, batch_size=batch_size)
predictions = list()
for i in range(len(output)):
yhat = output[i,0]
X = test_scaled[i, 0:-1]
# invert scaling
yhat = invert_scale(scaler, X, yhat)
# invert differencing
yhat = inverse_difference(raw_values, yhat, len(test_scaled)+1-i)
# store forecast
predictions.append(yhat)
# report performance
rmse = sqrt(mean_squared_error(raw_values[-12:], predictions))
print('%d) Test RMSE: %.3f' % (r+1, rmse))
error_scores.append(rmse)
return error_scores
# load dataset
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# experiment
repeats = 30
results = DataFrame()
# vary training batches
batches = [1, 2, 4]
for b in batches:
results[str(b)] = experiment(repeats, series, b)
# summarize results
print(results.describe())
# save boxplot
results.boxplot()
pyplot.savefig('boxplot_batches.png')
仅从平均表现来看,结果表明较低的 RMSE,批量大小为 1.正如前一节所述,随着更多的训练时期,这可能会得到进一步改善。
1 2 4
count 30.000000 30.000000 30.000000
mean 98.697017 102.642594 100.320203
std 12.227885 9.144163 15.957767
min 85.172215 85.072441 83.636365
25% 92.023175 96.834628 87.671461
50% 95.981688 101.139527 91.628144
75% 102.009268 110.171802 114.660192
max 147.688818 120.038036 135.290829
还创建了数据的框和胡须图,以帮助以图形方式比较分布。该图显示了作为绿线的中值表现,其中批量大小 4 显示最大可变性和最低中值 RMSE。
调整神经网络是平均表现和该表现的可变性的折衷,理想的结果是具有低可变性的低平均误差,这意味着它通常是良好且可再现的。
框和晶须图总结批量大小结果
调整神经元数量
在本节中,我们将研究改变网络中神经元数量的影响。
神经元的数量会影响网络的学习能力。通常,更多的神经元能够以更长的训练时间为代价从问题中学习更多的结构。更多的学习能力也会产生可能过拟合训练数据的问题。
我们将使用批量大小为 4 和 1000 的训练时期。
1000 个时期和 1 个神经元的诊断
我们将从 1 个神经元开始。
提醒一下,这是从时代实验中测试的第二个配置。
1000 个时期的诊断结果
1000 个时期和 2 个神经元的诊断
我们可以将神经元的数量从 1 增加到 2.这有望提高网络的学习能力。
我们可以通过改变run()函数中的n_neurons变量来实现。
n_neurons = 2
运行此配置将打印每次运行的最后一个时期的 RMSE 分数。
结果表明一般表现良好,但不是很好。
0) TrainRMSE=59.466223, TestRMSE=95.554547
1) TrainRMSE=58.752515, TestRMSE=101.908449
2) TrainRMSE=58.061139, TestRMSE=86.589039
3) TrainRMSE=55.883708, TestRMSE=94.747927
4) TrainRMSE=58.700290, TestRMSE=86.393213
5) TrainRMSE=60.564511, TestRMSE=101.956549
6) TrainRMSE=63.160916, TestRMSE=98.925108
7) TrainRMSE=60.148595, TestRMSE=95.082825
8) TrainRMSE=63.029242, TestRMSE=89.285092
9) TrainRMSE=57.794717, TestRMSE=91.425071
还创建了每个时期的测试和训练 RMSE 得分的线图。
这更有说服力。它显示测试 RMSE 快速下降到约 500-750 迭代,其中拐点显示测试 RMSE 在所有运行中几乎全面上升。同时,训练数据集显示持续减少到最后的时期。
这些是训练数据集过拟合的良好迹象。
1000 个时期和 2 个神经元的诊断结果
让我们看看这种趋势是否会持续更多的神经元。
1000 个时期和 3 个神经元的诊断
本节查看相同配置,神经元数量增加到 3。
我们可以通过在run()函数中设置n_neurons变量来实现。
n_neurons = 3
运行此配置将打印每次运行的最后一个时期的 RMSE 分数。
结果与上一节类似;我们没有看到 2 或 3 个神经元的最终时期测试分数之间有太大的差异。最终的训练得分看起来似乎低于 3 个神经元,可能表现出过拟合的加速。
训练数据集中的拐点似乎比 2 个神经元实验更早发生,可能在 300-400 时代。
这些神经元数量的增加可能受益于减慢学习速度的额外变化。例如使用正常化方法如丢失,减少批量大小,并减少到训练时期的数量。
0) TrainRMSE=55.686242, TestRMSE=90.955555
1) TrainRMSE=55.198617, TestRMSE=124.989622
2) TrainRMSE=55.767668, TestRMSE=104.751183
3) TrainRMSE=60.716046, TestRMSE=93.566307
4) TrainRMSE=57.703663, TestRMSE=110.813226
5) TrainRMSE=56.874231, TestRMSE=98.588524
6) TrainRMSE=57.206756, TestRMSE=94.386134
7) TrainRMSE=55.770377, TestRMSE=124.949862
8) TrainRMSE=56.876467, TestRMSE=95.059656
9) TrainRMSE=57.067810, TestRMSE=94.123620
还创建了每个时期的测试和训练 RMSE 得分的线图。
1000 个时期和 3 个神经元的诊断结果
结果摘要
同样,我们可以客观地比较增加神经元数量的影响,同时保持所有其他网络配置的固定。
在本节中,我们重复每个实验 30 次,并将平均测试 RMSE 表现与 1 到 5 的神经元数量进行比较。
...
# run a repeated experiment
def experiment(repeats, series, neurons):
# transform data to be stationary
raw_values = series.values
diff_values = difference(raw_values, 1)
# transform data to be supervised learning
supervised = timeseries_to_supervised(diff_values, 1)
supervised_values = supervised.values
# split data into train and test-sets
train, test = supervised_values[0:-12], supervised_values[-12:]
# transform the scale of the data
scaler, train_scaled, test_scaled = scale(train, test)
# run experiment
error_scores = list()
for r in range(repeats):
# fit the model
batch_size = 4
train_trimmed = train_scaled[2:, :]
lstm_model = fit_lstm(train_trimmed, batch_size, 1000, neurons)
# forecast the entire training dataset to build up state for forecasting
train_reshaped = train_trimmed[:, 0].reshape(len(train_trimmed), 1, 1)
lstm_model.predict(train_reshaped, batch_size=batch_size)
# forecast test dataset
test_reshaped = test_scaled[:,0:-1]
test_reshaped = test_reshaped.reshape(len(test_reshaped), 1, 1)
output = lstm_model.predict(test_reshaped, batch_size=batch_size)
predictions = list()
for i in range(len(output)):
yhat = output[i,0]
X = test_scaled[i, 0:-1]
# invert scaling
yhat = invert_scale(scaler, X, yhat)
# invert differencing
yhat = inverse_difference(raw_values, yhat, len(test_scaled)+1-i)
# store forecast
predictions.append(yhat)
# report performance
rmse = sqrt(mean_squared_error(raw_values[-12:], predictions))
print('%d) Test RMSE: %.3f' % (r+1, rmse))
error_scores.append(rmse)
return error_scores
# load dataset
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# experiment
repeats = 30
results = DataFrame()
# vary neurons
neurons = [1, 2, 3, 4, 5]
for n in neurons:
results[str(n)] = experiment(repeats, series, n)
# summarize results
print(results.describe())
# save boxplot
results.boxplot()
pyplot.savefig('boxplot_neurons.png')
运行实验会打印每个配置的摘要统计量。
仅从平均表现来看,结果表明具有 1 个神经元的网络配置具有超过 1000 个时期的最佳表现,批量大小为 4.此配置还显示最紧密的方差。
1 2 3 4 5
count 30.000000 30.000000 30.000000 30.000000 30.000000
mean 98.344696 103.268147 102.726894 112.453766 122.843032
std 13.538599 14.720989 12.905631 16.296657 25.586013
min 81.764721 87.731385 77.545899 85.632492 85.955093
25% 88.524334 94.040807 95.152752 102.477366 104.192588
50% 93.543948 100.330678 103.622600 110.906970 117.022724
75% 102.944050 105.087384 110.235754 118.653850 133.343669
max 132.934054 152.588092 130.551521 162.889845 184.678185
盒子和须状图显示中值测试集表现的明显趋势,其中神经元的增加导致测试 RMSE 的相应增加。
框和晶须图总结神经元结果
所有结果摘要
我们在本教程的 Shampoo Sales 数据集上完成了很多 LSTM 实验。
通常,似乎配置有 1 个神经元,批量大小为 4 并且训练 1000 个迭代的有状态 LSTM 可能是一个很好的配置。
结果还表明,批量大小为 1 并且适合更多时期的这种配置可能值得进一步探索。
调整神经网络是一项困难的实证研究,LSTM 也不例外。
本教程展示了配置行为随时间推移的诊断研究的好处,以及测试 RMSE 的客观研究。
然而,总会有更多的研究可以进行。下一节列出了一些想法。
扩展
本节列出了本教程中对实验进行扩展的一些想法。
如果您探索其中任何一项,请在评论中报告您的结果;我很想看看你想出了什么。
- dropout。使用正则化方法减慢学习速度,例如在重复的 LSTM 连接上丢失。
- 层。通过在每层中添加更多层和不同数量的神经元来探索额外的分层学习能力。
- 正规化。探索权重正则化(如 L1 和 L2)如何用于减慢某些配置上网络的学习和过拟合。
- 优化算法。探索替代优化算法的使用,例如经典的梯度下降,以查看加速或减慢学习的特定配置是否可以带来好处。
- 损失函数。探索替代损失函数的使用,看看它们是否可用于提升表现。
- 功能和时间步。探索使用滞后观察作为输入特征和特征的输入时间步骤,以查看它们作为输入的存在是否可以改善模型的学习和/或预测能力。
- 批量大。探索大于 4 的批量大小,可能需要进一步操纵训练和测试数据集的大小。
摘要
在本教程中,您了解了如何系统地研究 LSTM 网络的配置以进行时间序列预测。
具体来说,你学到了:
- 如何设计用于评估模型配置的系统测试工具。
- 如何使用模型诊断随着时间的推移,以及客观预测误差来解释模型行为。
- 如何探索和解释训练时期,批量大小和神经元数量的影响。
您对调整 LSTM 或本教程有任何疑问吗? 在下面的评论中提出您的问题,我会尽力回答。
如何在时间序列预测训练期间更新 LSTM 网络
原文:
machinelearningmastery.com/update-lstm-networks-training-time-series-forecasting/
使用神经网络模型进行时间序列预测的好处是可以在新数据可用时更新权重。
在本教程中,您将了解如何使用新数据更新长期短期记忆(LSTM)循环神经网络以进行时间序列预测。
完成本教程后,您将了解:
- 如何用新数据更新 LSTM 神经网络。
- 如何开发测试工具来评估不同的更新方案。
- 如何解释使用新数据更新 LSTM 网络的结果。
让我们开始吧。
- 2017 年 4 月更新:添加了缺少的 update_model()函数。
如何在时间序列预测训练期间更新 LSTM 网络 照片由 Esteban Alvarez ,保留一些权利。
教程概述
本教程分为 9 个部分。他们是:
- 洗发水销售数据集
- 实验测试线束
- 实验:没有更新
- 实验:2 更新时期
- 实验:5 更新时期
- 实验:10 个更新时期
- 实验:20 个更新时期
- 实验:50 更新时期
- 结果比较
环境
本教程假定您已安装 Python SciPy 环境。您可以在此示例中使用 Python 2 或 3。
本教程假设您安装了 TensorFlow 或 Theano 后端的 Keras v2.0 或更高版本。
本教程还假设您安装了 scikit-learn,Pandas,NumPy 和 Matplotlib。
如果您在设置 Python 环境时需要帮助,请参阅以下帖子:
洗发水销售数据集
该数据集描述了 3 年期间每月洗发水的销售数量。
单位是销售计数,有 36 个观察。原始数据集归功于 Makridakis,Wheelwright 和 Hyndman(1998)。
下面的示例加载并创建已加载数据集的图。
# load and plot dataset
from pandas import read_csv
from pandas import datetime
from matplotlib import pyplot
# load dataset
def parser(x):
return datetime.strptime('190'+x, '%Y-%m')
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# summarize first few rows
print(series.head())
# line plot
series.plot()
pyplot.show()
运行该示例将数据集作为 Pandas Series 加载并打印前 5 行。
Month
1901-01-01 266.0
1901-02-01 145.9
1901-03-01 183.1
1901-04-01 119.3
1901-05-01 180.3
Name: Sales, dtype: float64
然后创建该系列的线图,显示明显的增加趋势。
洗发水销售数据集的线图
接下来,我们将了解实验中使用的 LSTM 配置和测试工具。
实验测试线束
本节介绍本教程中使用的测试工具。
数据拆分
我们将 Shampoo Sales 数据集分为两部分:训练和测试集。
前两年的数据将用于训练数据集,剩余的一年数据将用于测试集。
将使用训练数据集开发模型,并对测试数据集做出预测。
测试数据集的持久性预测(朴素预测)实现了每月洗发水销售 136.761 的错误。这为测试集提供了可接受的表现下限。
模型评估
将使用滚动预测场景,也称为前进模型验证。
测试数据集的每个时间步骤将一次一个地走。将使用模型对时间步长做出预测,然后将获取测试集的实际预期值,并使其可用于下一时间步的预测模型。
这模仿了一个真实世界的场景,每个月都会有新的洗发水销售观察结果,并用于下个月的预测。
这将通过训练和测试数据集的结构进行模拟。
将收集关于测试数据集的所有预测,并计算错误分数以总结模型的技能。将使用均方根误差(RMSE),因为它会对大错误进行处罚,并产生与预测数据相同的分数,即每月洗发水销售额。
数据准备
在我们将 LSTM 模型拟合到数据集之前,我们必须转换数据。
在拟合模型和做出预测之前,对数据集执行以下三个数据变换。
- 转换时间序列数据,使其静止。具体而言,滞后= 1 差分以消除数据中的增加趋势。
- 将时间序列转换为监督学习问题。具体而言,将数据组织成输入和输出模式,其中前一时间步的观察被用作预测当前时间步的观察的输入
- 将观察结果转换为具有特定比例。具体而言,要将数据重缩放为-1 到 1 之间的值,以满足 LSTM 模型的默认双曲正切激活函数。
在计算错误分数之前,这些变换在预测中反转以将它们返回到其原始比例。
LSTM 模型
我们将使用 LSTM 模型,其中 1 个神经元适合 500 个时期。
批量大小为 1 是必需的,因为我们将使用前向验证并对最后 12 个月的每个数据进行一步预测。
批量大小为 1 意味着该模型将使用在线训练(而不是批量训练或小批量训练)。因此,预计模型拟合将具有一些变化。
理想情况下,将使用更多的训练时期(例如 1000 或 1500),但这被截断为 500 以保持运行时间合理。
使用有效的 ADAM 优化算法和均方误差损失函数来拟合模型。
实验运行
每个实验场景将运行 10 次。
其原因在于,每次训练给定配置时,LSTM 网络的随机初始条件可导致非常不同的表现。
让我们深入研究实验。
实验:没有更新
在第一个实验中,我们将评估一次训练的 LSTM 并重复使用以对每个时间步做出预测。
我们将其称为'_ 无更新模型 '或' 固定模型 _',因为一旦模型首次适合训练数据,将不会进行更新。这提供了一个表现基准,我们期望实验能够对模型进行适度更新,从而超越表现。
完整的代码清单如下。
from pandas import DataFrame
from pandas import Series
from pandas import concat
from pandas import read_csv
from pandas import datetime
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from math import sqrt
import matplotlib
import numpy
from numpy import concatenate
# date-time parsing function for loading the dataset
def parser(x):
return datetime.strptime('190'+x, '%Y-%m')
# frame a sequence as a supervised learning problem
def timeseries_to_supervised(data, lag=1):
df = DataFrame(data)
columns = [df.shift(i) for i in range(1, lag+1)]
columns.append(df)
df = concat(columns, axis=1)
df = df.drop(0)
return df
# create a differenced series
def difference(dataset, interval=1):
diff = list()
for i in range(interval, len(dataset)):
value = dataset[i] - dataset[i - interval]
diff.append(value)
return Series(diff)
# invert differenced value
def inverse_difference(history, yhat, interval=1):
return yhat + history[-interval]
# scale train and test data to [-1, 1]
def scale(train, test):
# fit scaler
scaler = MinMaxScaler(feature_range=(-1, 1))
scaler = scaler.fit(train)
# transform train
train = train.reshape(train.shape[0], train.shape[1])
train_scaled = scaler.transform(train)
# transform test
test = test.reshape(test.shape[0], test.shape[1])
test_scaled = scaler.transform(test)
return scaler, train_scaled, test_scaled
# inverse scaling for a forecasted value
def invert_scale(scaler, X, yhat):
new_row = [x for x in X] + [yhat]
array = numpy.array(new_row)
array = array.reshape(1, len(array))
inverted = scaler.inverse_transform(array)
return inverted[0, -1]
# fit an LSTM network to training data
def fit_lstm(train, batch_size, nb_epoch, neurons):
X, y = train[:, 0:-1], train[:, -1]
X = X.reshape(X.shape[0], 1, X.shape[1])
model = Sequential()
model.add(LSTM(neurons, batch_input_shape=(batch_size, X.shape[1], X.shape[2]), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
for i in range(nb_epoch):
model.fit(X, y, epochs=1, batch_size=batch_size, verbose=0, shuffle=False)
model.reset_states()
return model
# make a one-step forecast
def forecast_lstm(model, batch_size, X):
X = X.reshape(1, 1, len(X))
yhat = model.predict(X, batch_size=batch_size)
return yhat[0,0]
# run a repeated experiment
def experiment(repeats, series):
# transform data to be stationary
raw_values = series.values
diff_values = difference(raw_values, 1)
# transform data to be supervised learning
supervised = timeseries_to_supervised(diff_values, 1)
supervised_values = supervised.values
# split data into train and test-sets
train, test = supervised_values[0:-12], supervised_values[-12:]
# transform the scale of the data
scaler, train_scaled, test_scaled = scale(train, test)
# run experiment
error_scores = list()
for r in range(repeats):
# fit the base model
lstm_model = fit_lstm(train_scaled, 1, 500, 1)
# forecast test dataset
predictions = list()
for i in range(len(test_scaled)):
# predict
X, y = test_scaled[i, 0:-1], test_scaled[i, -1]
yhat = forecast_lstm(lstm_model, 1, X)
# invert scaling
yhat = invert_scale(scaler, X, yhat)
# invert differencing
yhat = inverse_difference(raw_values, yhat, len(test_scaled)+1-i)
# store forecast
predictions.append(yhat)
# report performance
rmse = sqrt(mean_squared_error(raw_values[-12:], predictions))
print('%d) Test RMSE: %.3f' % (r+1, rmse))
error_scores.append(rmse)
return error_scores
# execute the experiment
def run():
# load dataset
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# experiment
repeats = 10
results = DataFrame()
# run experiment
results['results'] = experiment(repeats, series)
# summarize results
print(results.describe())
# save results
results.to_csv('experiment_fixed.csv', index=False)
# entry point
run()
运行该示例使用前向验证存储在测试数据集上计算的 RMSE 分数。它们存储在名为experiment_fixed.csv的文件中,以供以后分析。打印分数摘要,如下所示。
结果表明,平均表现优于持久性模型,显示测试 RMSE 为 109.565465,而持久性的洗发水销售额为 136.761。
results
count 10.000000
mean 109.565465
std 14.329646
min 95.357198
25% 99.870983
50% 104.864387
75% 113.553952
max 138.261929
接下来,我们将开始查看在前向验证期间对模型进行更新的配置。
实验:2 更新时期
在本实验中,我们将模型拟合到所有训练数据上,然后在前向验证期间的每个预测之后更新模型。
然后,将用于在测试数据集中引出预测的每个测试模式添加到训练数据集中,并更新模型。
在这种情况下,该模型在进行下一次预测之前适合额外的 2 个训练时期。
使用与第一个实验中使用的相同的代码清单。代码清单的更改如下所示。
# Update LSTM model
def update_model(model, train, batch_size, updates):
X, y = train[:, 0:-1], train[:, -1]
X = X.reshape(X.shape[0], 1, X.shape[1])
for i in range(updates):
model.fit(X, y, nb_epoch=1, batch_size=batch_size, verbose=0, shuffle=False)
model.reset_states()
# run a repeated experiment
def experiment(repeats, series, updates):
# transform data to be stationary
raw_values = series.values
diff_values = difference(raw_values, 1)
# transform data to be supervised learning
supervised = timeseries_to_supervised(diff_values, 1)
supervised_values = supervised.values
# split data into train and test-sets
train, test = supervised_values[0:-12], supervised_values[-12:]
# transform the scale of the data
scaler, train_scaled, test_scaled = scale(train, test)
# run experiment
error_scores = list()
for r in range(repeats):
# fit the base model
lstm_model = fit_lstm(train_scaled, 1, 500, 1)
# forecast test dataset
train_copy = numpy.copy(train_scaled)
predictions = list()
for i in range(len(test_scaled)):
# update model
if i > 0:
update_model(lstm_model, train_copy, 1, updates)
# predict
X, y = test_scaled[i, 0:-1], test_scaled[i, -1]
yhat = forecast_lstm(lstm_model, 1, X)
# invert scaling
yhat = invert_scale(scaler, X, yhat)
# invert differencing
yhat = inverse_difference(raw_values, yhat, len(test_scaled)+1-i)
# store forecast
predictions.append(yhat)
# add to training set
train_copy = concatenate((train_copy, test_scaled[i,:].reshape(1, -1)))
# report performance
rmse = sqrt(mean_squared_error(raw_values[-12:], predictions))
print('%d) Test RMSE: %.3f' % (r+1, rmse))
error_scores.append(rmse)
return error_scores
# execute the experiment
def run():
# load dataset
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# experiment
repeats = 10
results = DataFrame()
# run experiment
updates = 2
results['results'] = experiment(repeats, series, updates)
# summarize results
print(results.describe())
# save results
results.to_csv('experiment_update_2.csv', index=False)
# entry point
run()
运行实验将最终测试 RMSE 分数保存在“experiment_update_2.csv”中,并打印结果的摘要统计量,如下所示。
results
count 10.000000
mean 99.566270
std 10.511337
min 87.771671
25% 93.925243
50% 97.903038
75% 101.213058
max 124.748746
实验:5 更新时期
该实验重复上述更新实验,并在将每个测试模式添加到训练数据集之后将模型训练另外 5 个时期。
运行实验将最终测试 RMSE 分数保存在“experiment_update_5.csv”中,并打印结果的摘要统计量,如下所示。
results
count 10.000000
mean 101.094469
std 9.422711
min 91.642706
25% 94.593701
50% 98.954743
75% 104.998420
max 123.651985
实验:10 个更新时期
该实验重复上述更新实验,并在将每个测试模式添加到训练数据集之后将模型训练另外 10 个时期。
运行实验将最终测试 RMSE 分数保存在“experiment_update_10.csv”中,并打印结果的摘要统计量,如下所示。
results
count 10.000000
mean 108.806418
std 21.707665
min 92.161703
25% 94.872009
50% 99.652295
75% 112.607260
max 159.921749
实验:20 个更新时期
该实验重复上述更新实验,并在将每个测试模式添加到训练数据集之后将模型训练另外 20 个时期。
运行实验将最终测试 RMSE 分数保存在“experiment_update_20.csv”中,并打印结果的摘要统计量,如下所示。
results
count 10.000000
mean 112.070895
std 16.631902
min 96.822760
25% 101.790705
50% 103.380896
75% 119.479211
max 140.828410
实验:50 更新时期
该实验重复上述更新实验,并在将每个测试模式添加到训练数据集之后将模型训练另外 50 个时期。
运行实验将最终测试 RMSE 分数保存在“experiment_update_50.csv”中,并打印结果的摘要统计量,如下所示。
results
count 10.000000
mean 110.721971
std 22.788192
min 93.362982
25% 96.833140
50% 98.411940
75% 123.793652
max 161.463289
结果比较
在本节中,我们比较了之前实验中保存的结果。
我们加载每个保存的结果,使用描述性统计量汇总结果,并使用 box 和 whisker 图比较结果。
完整的代码清单如下。
from pandas import DataFrame
from pandas import read_csv
from matplotlib import pyplot
# load results into a dataframe
filenames = ['experiment_fixed.csv',
'experiment_update_2.csv', 'experiment_update_5.csv',
'experiment_update_10.csv', 'experiment_update_20.csv',
'experiment_update_50.csv']
results = DataFrame()
for name in filenames:
results[name[11:-4]] = read_csv(name, header=0)
# describe all results
print(results.describe())
# box and whisker plot
results.boxplot()
pyplot.show()
首先运行该示例计算并打印每个实验结果的描述性统计量。
如果我们看一下平均表现,我们可以看到固定模型提供了良好的表现基线,但我们发现适度数量的更新时期(20 和 50)平均会产生更差的测试集 RMSE。
我们看到少数更新时期导致更好的整体测试集表现,特别是 2 个时期,接着是 5 个时期。这是令人鼓舞的。
fixed update_2 update_5 update_10 update_20 update_50
count 10.000000 10.000000 10.000000 10.000000 10.000000 10.000000
mean 109.565465 99.566270 101.094469 108.806418 112.070895 110.721971
std 14.329646 10.511337 9.422711 21.707665 16.631902 22.788192
min 95.357198 87.771671 91.642706 92.161703 96.822760 93.362982
25% 99.870983 93.925243 94.593701 94.872009 101.790705 96.833140
50% 104.864387 97.903038 98.954743 99.652295 103.380896 98.411940
75% 113.553952 101.213058 104.998420 112.607260 119.479211 123.793652
max 138.261929 124.748746 123.651985 159.921749 140.828410 161.463289
还创建了一个盒子和须状图,用于比较每个实验的测试 RMSE 结果的分布。
该图突出显示了每个实验的中位数(绿线)以及中间 50%的数据(框)。该图描述了与平均表现相同的故事,表明少数训练时期(2 或 5 个时期)导致最佳的整体测试 RMSE。
该图显示测试 RMSE 上升,因为更新次数增加到 20 个时期,然后再次下降 50 个时期。这可能是改进模型(11 * 50 时期)或少量重复(10)的人工制品的重要进一步训练的标志。
Box 和 Whisker Plots 比较更新时期的数量
重要的是要指出这些结果特定于模型配置和此数据集。
虽然这些实验确实为您自己的预测性建模问题执行类似的实验提供了框架,但很难将这些结果概括为超出此具体示例。
扩展
本节列出了本节中有关实验扩展的建议。
- 统计显着性检验。我们可以计算成对统计显着性检验,例如学生 t 检验,以查看结果群体中均值之间的差异是否具有统计学意义。
- 更多重复。我们可以将重复次数从 10 增加到 30,100 或更多,以使结果更加稳健。
- 更多时代。基础 LSTM 模型仅适用于 500 个具有在线训练的时期,并且相信额外的训练时期将导致更准确的基线模型。减少了时期的数量以减少实验运行时间。
- 与更多 Epochs 比较。更新模型的实验结果应直接与固定模型的实验进行比较,固定模型使用相同数量的总体时期来查看是否将额外的测试模式添加到训练数据集中会产生明显的差异。例如,可以将每个测试模式的 2 个更新时期与针对 500 +(12-1)* 2)或 522 个时期训练的固定模型进行比较,更新模型 5 与适合 500 +(12-1)的固定模型进行比较)* 5)或 555 个时代,依此类推。
- 全新模型。在将每个测试模式添加到训练数据集后,添加一个适合新模型的实验。这是尝试过的,但延长的运行时间阻止了在完成本教程之前收集结果。预计这将提供与更新和固定模型的有趣比较点。
你有没有探索过这些扩展? 在评论中报告您的结果;我很想听听你发现了什么。
摘要
在本教程中,您了解了如何更新 LSTM 网络,因为新数据可用于 Python 中的时间序列预测。
具体来说,你学到了:
- 如何设计一套系统的实验来探索更新 LSTM 模型的效果。
- 如何在新数据可用时更新 LSTM 模型。
- 对 LSTM 模型的更新可以产生更有效的预测模型,但是需要仔细校准预测问题。
你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。
如何为时间序列预测使用 LSTM 网络的丢弃法
原文:
machinelearningmastery.com/use-dropout-lstm-networks-time-series-forecasting/
长短期记忆(LSTM)模型是一种能够学习观察序列的循环神经网络。
这可能使它们成为一个非常适合时间序列预测的网络。
LSTM 的一个问题是他们可以轻松地过度训练训练数据,降低他们的预测技巧。
Dropout 是一种正规化方法,在训练网络时,LSTM 单元的输入和重复连接在概率上被排除在激活和权重更新之外。这具有减少过拟合和改善模型表现的效果。
在本教程中,您将了解如何在 LSTM 网络和设计实验中使用 dropout 来测试其对时间序列预测的有效性。
完成本教程后,您将了解:
- 如何设计一个强大的测试工具来评估 LSTM 网络的时间序列预测。
- 如何使用 LSTM 使用输入权重丢失来设计,执行和解释结果。
- 如何设计,执行和解释使用 LSTM 重复丢失重量的结果。
让我们开始吧。
如何使用 LSTM 网络的 Dropout 进行时间序列预测 照片来自 Jonas Bengtsson,保留一些权利。
教程概述
本教程分为 5 个部分。他们是:
- 洗发水销售数据集
- 实验测试线束
- 输入 dropout
- 经常性 dropout
- 审查结果
环境
本教程假定您已安装 Python SciPy 环境。您可以在此示例中使用 Python 2 或 3。
本教程假设您安装了 TensorFlow 或 Theano 后端的 Keras v2.0 或更高版本。
本教程还假设您安装了 scikit-learn,Pandas,NumPy 和 Matplotlib。
接下来,让我们看看标准时间序列预测问题,我们可以将其用作此实验的上下文。
如果您在设置 Python 环境时需要帮助,请参阅以下帖子:
洗发水销售数据集
该数据集描述了 3 年期间每月洗发水的销售数量。
单位是销售计数,有 36 个观察。原始数据集归功于 Makridakis,Wheelwright 和 Hyndman(1998)。
下面的示例加载并创建已加载数据集的图。
# load and plot dataset
from pandas import read_csv
from pandas import datetime
from matplotlib import pyplot
# load dataset
def parser(x):
return datetime.strptime('190'+x, '%Y-%m')
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# summarize first few rows
print(series.head())
# line plot
series.plot()
pyplot.show()
运行该示例将数据集作为 Pandas Series 加载并打印前 5 行。
Month
1901-01-01 266.0
1901-02-01 145.9
1901-03-01 183.1
1901-04-01 119.3
1901-05-01 180.3
Name: Sales, dtype: float64
然后创建该系列的线图,显示明显的增加趋势。
洗发水销售数据集的线图
接下来,我们将看一下实验中使用的模型配置和测试工具。
实验测试线束
本节介绍本教程中使用的测试工具。
数据拆分
我们将 Shampoo Sales 数据集分为两部分:训练和测试集。
前两年的数据将用于训练数据集,剩余的一年数据将用于测试集。
将使用训练数据集开发模型,并对测试数据集做出预测。
测试数据集的持久性预测(朴素预测)实现了每月洗发水销售 136.761 的错误。这在测试集上提供了较低的可接受表现限制。
模型评估
将使用滚动预测场景,也称为前进模型验证。
测试数据集的每个时间步骤将一次一个地走。将使用模型对时间步长做出预测,然后将获取测试集的实际预期值,并使其可用于下一时间步的预测模型。
这模仿了一个真实世界的场景,每个月都会有新的洗发水销售观察结果,并用于下个月的预测。
这将通过训练和测试数据集的结构进行模拟。
将收集关于测试数据集的所有预测,并计算错误分数以总结模型的技能。将使用均方根误差(RMSE),因为它会对大错误进行处罚,并产生与预测数据相同的分数,即每月洗发水销售额。
数据准备
在我们将模型拟合到数据集之前,我们必须转换数据。
在拟合模型和做出预测之前,对数据集执行以下三个数据变换。
- 转换时间序列数据,使其静止。具体而言,滞后= 1 差分以消除数据中的增加趋势。
- 将时间序列转换为监督学习问题。具体而言,将数据组织成输入和输出模式,其中前一时间步的观察被用作预测当前时间步的观察的输入
- 将观察结果转换为具有特定比例。具体而言,将数据重缩放为-1 到 1 之间的值。
这些变换在预测时反转,在计算和误差分数之前将它们恢复到原始比例。
LSTM 模型
我们将使用基础状态 LSTM 模型,其中 1 个神经元适合 1000 个时期。
批量大小为 1 是必需的,因为我们将使用前向验证并对最后 12 个月的测试数据进行一步预测。
批量大小为 1 意味着该模型将使用在线训练(而不是批量训练或小批量训练)。因此,预计模型拟合将具有一些变化。
理想情况下,将使用更多的训练时期(例如 1500),但这被截断为 1000 以保持运行时间合理。
使用有效的 ADAM 优化算法和均方误差损失函数来拟合模型。
实验运行
每个实验场景将运行 30 次,并且测试集上的 RMSE 得分将从每次运行结束时记录。
让我们深入研究实验。
基线 LSTM 模型
让我们从基线 LSTM 模型开始。
此问题的基线 LSTM 模型具有以下配置:
- 滞后输入:1
- 时代:1000
- LSTM 隐藏层中的单位:3
- 批量大小:4
- 重复:3
完整的代码清单如下。
此代码清单将用作所有后续实验的基础,只有后续部分中提供的此代码清单的更改。
from pandas import DataFrame
from pandas import Series
from pandas import concat
from pandas import read_csv
from pandas import datetime
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from math import sqrt
import matplotlib
# be able to save images on server
matplotlib.use('Agg')
from matplotlib import pyplot
import numpy
# date-time parsing function for loading the dataset
def parser(x):
return datetime.strptime('190'+x, '%Y-%m')
# frame a sequence as a supervised learning problem
def timeseries_to_supervised(data, lag=1):
df = DataFrame(data)
columns = [df.shift(i) for i in range(1, lag+1)]
columns.append(df)
df = concat(columns, axis=1)
return df
# create a differenced series
def difference(dataset, interval=1):
diff = list()
for i in range(interval, len(dataset)):
value = dataset[i] - dataset[i - interval]
diff.append(value)
return Series(diff)
# invert differenced value
def inverse_difference(history, yhat, interval=1):
return yhat + history[-interval]
# scale train and test data to [-1, 1]
def scale(train, test):
# fit scaler
scaler = MinMaxScaler(feature_range=(-1, 1))
scaler = scaler.fit(train)
# transform train
train = train.reshape(train.shape[0], train.shape[1])
train_scaled = scaler.transform(train)
# transform test
test = test.reshape(test.shape[0], test.shape[1])
test_scaled = scaler.transform(test)
return scaler, train_scaled, test_scaled
# inverse scaling for a forecasted value
def invert_scale(scaler, X, yhat):
new_row = [x for x in X] + [yhat]
array = numpy.array(new_row)
array = array.reshape(1, len(array))
inverted = scaler.inverse_transform(array)
return inverted[0, -1]
# fit an LSTM network to training data
def fit_lstm(train, n_batch, nb_epoch, n_neurons):
X, y = train[:, 0:-1], train[:, -1]
X = X.reshape(X.shape[0], 1, X.shape[1])
model = Sequential()
model.add(LSTM(n_neurons, batch_input_shape=(n_batch, X.shape[1], X.shape[2]), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
for i in range(nb_epoch):
model.fit(X, y, epochs=1, batch_size=n_batch, verbose=0, shuffle=False)
model.reset_states()
return model
# run a repeated experiment
def experiment(series, n_lag, n_repeats, n_epochs, n_batch, n_neurons):
# transform data to be stationary
raw_values = series.values
diff_values = difference(raw_values, 1)
# transform data to be supervised learning
supervised = timeseries_to_supervised(diff_values, n_lag)
supervised_values = supervised.values[n_lag:,:]
# split data into train and test-sets
train, test = supervised_values[0:-12], supervised_values[-12:]
# transform the scale of the data
scaler, train_scaled, test_scaled = scale(train, test)
# run experiment
error_scores = list()
for r in range(n_repeats):
# fit the model
train_trimmed = train_scaled[2:, :]
lstm_model = fit_lstm(train_trimmed, n_batch, n_epochs, n_neurons)
# forecast test dataset
test_reshaped = test_scaled[:,0:-1]
test_reshaped = test_reshaped.reshape(len(test_reshaped), 1, 1)
output = lstm_model.predict(test_reshaped, batch_size=n_batch)
predictions = list()
for i in range(len(output)):
yhat = output[i,0]
X = test_scaled[i, 0:-1]
# invert scaling
yhat = invert_scale(scaler, X, yhat)
# invert differencing
yhat = inverse_difference(raw_values, yhat, len(test_scaled)+1-i)
# store forecast
predictions.append(yhat)
# report performance
rmse = sqrt(mean_squared_error(raw_values[-12:], predictions))
print('%d) Test RMSE: %.3f' % (r+1, rmse))
error_scores.append(rmse)
return error_scores
# configure the experiment
def run():
# load dataset
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# configure the experiment
n_lag = 1
n_repeats = 30
n_epochs = 1000
n_batch = 4
n_neurons = 3
# run the experiment
results = DataFrame()
results['results'] = experiment(series, n_lag, n_repeats, n_epochs, n_batch, n_neurons)
# summarize results
print(results.describe())
# save boxplot
results.boxplot()
pyplot.savefig('experiment_baseline.png')
# entry point
run()
运行实验将打印所有重复测试 RMSE 的摘要统计量。
我们可以看到,平均而言,这种模型配置实现了约 92 个月洗发水销售的测试 RMSE,标准偏差为 5。
results
count 30.000000
mean 92.842537
std 5.748456
min 81.205979
25% 89.514367
50% 92.030003
75% 96.926145
max 105.247117
还会根据测试 RMSE 结果的分布创建一个盒子和胡须图并保存到文件中。
该图清楚地描述了结果的传播,突出了中间 50%的值(框)和中位数(绿线)。
洗发水销售数据集中基线表现的盒子和晶须图
网络配置需要考虑的另一个角度是模型适应时的行为方式。
我们可以在每个训练时期之后评估训练和测试数据集上的模型,以了解配置是否过拟合或不适合问题。
我们将在每组实验的最佳结果上使用此诊断方法。将运行总共 10 次重复的配置,并且在线图上绘制每个训练迭代之后的训练和测试 RMSE 得分。
在这种情况下,我们将在适用于 1000 个时期的 LSTM 上使用此诊断。
完整的诊断代码清单如下。
与前面的代码清单一样,下面的代码将用作本教程中所有诊断的基础,并且后续部分中仅提供对此列表的更改。
from pandas import DataFrame
from pandas import Series
from pandas import concat
from pandas import read_csv
from pandas import datetime
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from math import sqrt
import matplotlib
# be able to save images on server
matplotlib.use('Agg')
from matplotlib import pyplot
import numpy
# date-time parsing function for loading the dataset
def parser(x):
return datetime.strptime('190'+x, '%Y-%m')
# frame a sequence as a supervised learning problem
def timeseries_to_supervised(data, lag=1):
df = DataFrame(data)
columns = [df.shift(i) for i in range(1, lag+1)]
columns.append(df)
df = concat(columns, axis=1)
return df
# create a differenced series
def difference(dataset, interval=1):
diff = list()
for i in range(interval, len(dataset)):
value = dataset[i] - dataset[i - interval]
diff.append(value)
return Series(diff)
# scale train and test data to [-1, 1]
def scale(train, test):
# fit scaler
scaler = MinMaxScaler(feature_range=(-1, 1))
scaler = scaler.fit(train)
# transform train
train = train.reshape(train.shape[0], train.shape[1])
train_scaled = scaler.transform(train)
# transform test
test = test.reshape(test.shape[0], test.shape[1])
test_scaled = scaler.transform(test)
return scaler, train_scaled, test_scaled
# inverse scaling for a forecasted value
def invert_scale(scaler, X, yhat):
new_row = [x for x in X] + [yhat]
array = numpy.array(new_row)
array = array.reshape(1, len(array))
inverted = scaler.inverse_transform(array)
return inverted[0, -1]
# evaluate the model on a dataset, returns RMSE in transformed units
def evaluate(model, raw_data, scaled_dataset, scaler, offset, batch_size):
# separate
X, y = scaled_dataset[:,0:-1], scaled_dataset[:,-1]
# reshape
reshaped = X.reshape(len(X), 1, 1)
# forecast dataset
output = model.predict(reshaped, batch_size=batch_size)
# invert data transforms on forecast
predictions = list()
for i in range(len(output)):
yhat = output[i,0]
# invert scaling
yhat = invert_scale(scaler, X[i], yhat)
# invert differencing
yhat = yhat + raw_data[i]
# store forecast
predictions.append(yhat)
# report performance
rmse = sqrt(mean_squared_error(raw_data[1:], predictions))
# reset model state
model.reset_states()
return rmse
# fit an LSTM network to training data
def fit_lstm(train, test, raw, scaler, batch_size, nb_epoch, neurons):
X, y = train[:, 0:-1], train[:, -1]
X = X.reshape(X.shape[0], 1, X.shape[1])
# prepare model
model = Sequential()
model.add(LSTM(neurons, batch_input_shape=(batch_size, X.shape[1], X.shape[2]), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
# fit model
train_rmse, test_rmse = list(), list()
for i in range(nb_epoch):
model.fit(X, y, epochs=1, batch_size=batch_size, verbose=0, shuffle=False)
model.reset_states()
# evaluate model on train data
raw_train = raw[-(len(train)+len(test)+1):-len(test)]
train_rmse.append(evaluate(model, raw_train, train, scaler, 0, batch_size))
# evaluate model on test data
raw_test = raw[-(len(test)+1):]
test_rmse.append(evaluate(model, raw_test, test, scaler, 0, batch_size))
history = DataFrame()
history['train'], history['test'] = train_rmse, test_rmse
return history
# run diagnostic experiments
def run():
# config
n_lag = 1
n_repeats = 10
n_epochs = 1000
n_batch = 4
n_neurons = 3
# load dataset
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# transform data to be stationary
raw_values = series.values
diff_values = difference(raw_values, 1)
# transform data to be supervised learning
supervised = timeseries_to_supervised(diff_values, n_lag)
supervised_values = supervised.values[n_lag:,:]
# split data into train and test-sets
train, test = supervised_values[0:-12], supervised_values[-12:]
# transform the scale of the data
scaler, train_scaled, test_scaled = scale(train, test)
# fit and evaluate model
train_trimmed = train_scaled[2:, :]
# run diagnostic tests
for i in range(n_repeats):
history = fit_lstm(train_trimmed, test_scaled, raw_values, scaler, n_batch, n_epochs, n_neurons)
pyplot.plot(history['train'], color='blue')
pyplot.plot(history['test'], color='orange')
print('%d) TrainRMSE=%f, TestRMSE=%f' % (i+1, history['train'].iloc[-1], history['test'].iloc[-1]))
pyplot.savefig('diagnostic_baseline.png')
# entry point
run()
运行诊断程序打印最终训练并测试每次运行的 RMSE。更有趣的是创建的最终线图。
线图显示了每个训练时期之后的训练 RMSE(蓝色)和测试 RMSE(橙色)。
在这种情况下,诊断图显示训练和测试 RMSE 稳定下降到大约 400-500 个时期,此后似乎可能发生一些过拟合。这表现为训练 RMSE 的持续下降和测试 RMSE 的增加。
洗发水销售数据集基线模型的诊断线图
输入 dropout
Dropout 可以应用于 LSTM 节点内的输入连接。
输入的丢失意味着对于给定的概率,每个 LSTM 块的输入连接上的数据将从节点激活和权重更新中排除。
在 Keras 中,在创建 LSTM 层时使用dropout参数指定。丢失值是 0(无丢失)和 1(无连接)之间的百分比。
在这个实验中,我们将比较没有 dropout 率和 20%,40%和 60%的输入 dropout 率。
下面列出了更新的 fit_lstm(),_ 实验()_ 和run()函数,用于将输入丢失与 LSTM 一起使用。
# fit an LSTM network to training data
def fit_lstm(train, n_batch, nb_epoch, n_neurons, dropout):
X, y = train[:, 0:-1], train[:, -1]
X = X.reshape(X.shape[0], 1, X.shape[1])
model = Sequential()
model.add(LSTM(n_neurons, batch_input_shape=(n_batch, X.shape[1], X.shape[2]), stateful=True, dropout=dropout))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
for i in range(nb_epoch):
model.fit(X, y, epochs=1, batch_size=n_batch, verbose=0, shuffle=False)
model.reset_states()
return model
# run a repeated experiment
def experiment(series, n_lag, n_repeats, n_epochs, n_batch, n_neurons, dropout):
# transform data to be stationary
raw_values = series.values
diff_values = difference(raw_values, 1)
# transform data to be supervised learning
supervised = timeseries_to_supervised(diff_values, n_lag)
supervised_values = supervised.values[n_lag:,:]
# split data into train and test-sets
train, test = supervised_values[0:-12], supervised_values[-12:]
# transform the scale of the data
scaler, train_scaled, test_scaled = scale(train, test)
# run experiment
error_scores = list()
for r in range(n_repeats):
# fit the model
train_trimmed = train_scaled[2:, :]
lstm_model = fit_lstm(train_trimmed, n_batch, n_epochs, n_neurons, dropout)
# forecast test dataset
test_reshaped = test_scaled[:,0:-1]
test_reshaped = test_reshaped.reshape(len(test_reshaped), 1, 1)
output = lstm_model.predict(test_reshaped, batch_size=n_batch)
predictions = list()
for i in range(len(output)):
yhat = output[i,0]
X = test_scaled[i, 0:-1]
# invert scaling
yhat = invert_scale(scaler, X, yhat)
# invert differencing
yhat = inverse_difference(raw_values, yhat, len(test_scaled)+1-i)
# store forecast
predictions.append(yhat)
# report performance
rmse = sqrt(mean_squared_error(raw_values[-12:], predictions))
print('%d) Test RMSE: %.3f' % (r+1, rmse))
error_scores.append(rmse)
return error_scores
# configure the experiment
def run():
# load dataset
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# configure the experiment
n_lag = 1
n_repeats = 30
n_epochs = 1000
n_batch = 4
n_neurons = 3
n_dropout = [0.0, 0.2, 0.4, 0.6]
# run the experiment
results = DataFrame()
for dropout in n_dropout:
results[str(dropout)] = experiment(series, n_lag, n_repeats, n_epochs, n_batch, n_neurons, dropout)
# summarize results
print(results.describe())
# save boxplot
results.boxplot()
pyplot.savefig('experiment_dropout_input.png')
运行此实验会打印每个已评估配置的描述性统计量。
结果表明,平均输入 dropout 率为 40%会带来更好的表现,但 dropout 率为 20%,40%和 60%的平均结果之间的差异非常小。所有人似乎都胜过 dropout。
0.0 0.2 0.4 0.6
count 30.000000 30.000000 30.000000 30.000000
mean 97.578280 89.448450 88.957421 89.810789
std 7.927639 5.807239 4.070037 3.467317
min 84.749785 81.315336 80.662878 84.300135
25% 92.520968 84.712064 85.885858 87.766818
50% 97.324110 88.109654 88.790068 89.585945
75% 101.258252 93.642621 91.515127 91.109452
max 123.578235 104.528209 96.687333 99.660331
还会创建一个框和胡须图来比较每个配置的结果分布。
该图显示结果的扩散随输入 dropout 的增加而减少。该图还表明输入丢失率为 20%可能略低于中值测试 RMSE。
结果确实鼓励对所选 LSTM 配置使用一些输入丢失,可能设置为 40%。
洗发水销售数据集中输入 dropout 表现的盒子和晶须图
我们可以查看 40%的输入丢失如何影响模型的动态,同时适合训练数据。
下面的代码总结了fit_lstm()和run()函数与诊断脚本基线版本的更新。
# fit an LSTM network to training data
def fit_lstm(train, test, raw, scaler, batch_size, nb_epoch, neurons, dropout):
X, y = train[:, 0:-1], train[:, -1]
X = X.reshape(X.shape[0], 1, X.shape[1])
# prepare model
model = Sequential()
model.add(LSTM(neurons, batch_input_shape=(batch_size, X.shape[1], X.shape[2]), stateful=True, dropout=dropout))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
# fit model
train_rmse, test_rmse = list(), list()
for i in range(nb_epoch):
model.fit(X, y, epochs=1, batch_size=batch_size, verbose=0, shuffle=False)
model.reset_states()
# evaluate model on train data
raw_train = raw[-(len(train)+len(test)+1):-len(test)]
train_rmse.append(evaluate(model, raw_train, train, scaler, 0, batch_size))
# evaluate model on test data
raw_test = raw[-(len(test)+1):]
test_rmse.append(evaluate(model, raw_test, test, scaler, 0, batch_size))
history = DataFrame()
history['train'], history['test'] = train_rmse, test_rmse
return history
# run diagnostic experiments
def run():
# config
n_lag = 1
n_repeats = 10
n_epochs = 1000
n_batch = 4
n_neurons = 3
dropout = 0.4
# load dataset
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# transform data to be stationary
raw_values = series.values
diff_values = difference(raw_values, 1)
# transform data to be supervised learning
supervised = timeseries_to_supervised(diff_values, n_lag)
supervised_values = supervised.values[n_lag:,:]
# split data into train and test-sets
train, test = supervised_values[0:-12], supervised_values[-12:]
# transform the scale of the data
scaler, train_scaled, test_scaled = scale(train, test)
# fit and evaluate model
train_trimmed = train_scaled[2:, :]
# run diagnostic tests
for i in range(n_repeats):
history = fit_lstm(train_trimmed, test_scaled, raw_values, scaler, n_batch, n_epochs, n_neurons, dropout)
pyplot.plot(history['train'], color='blue')
pyplot.plot(history['test'], color='orange')
print('%d) TrainRMSE=%f, TestRMSE=%f' % (i+1, history['train'].iloc[-1], history['test'].iloc[-1]))
pyplot.savefig('diagnostic_dropout_input.png')
运行更新的诊断会在每个训练时期之后创建训练图并测试模型的 RMSE 表现以及输入丢失。
结果显示在训练上明显增加了凸起并测试了 RMSE 轨迹,这在测试 RMSE 分数上更为明显。
我们还可以看到过拟合的症状已经通过测试 RMSE 在整个 1000 个时期内持续下降来解决,这可能表明需要额外的训练时期来利用这种行为。
洗发水销售数据集中输入 dropout 表现的诊断线图
经常性 dropout
丢失也可以应用于 LSTM 单元上的循环输入信号。
在 Keras 中,这是通过在定义 LSTM 层时设置recurrent_dropout参数来实现的。
在这个实验中,我们将比较没有 dropout 率与 20%,40%和 60%的复发 dropout 率。
下面列出了更新的 fit_lstm(),_ 实验()_ 和run()函数,用于将输入丢失与 LSTM 一起使用。
# fit an LSTM network to training data
def fit_lstm(train, n_batch, nb_epoch, n_neurons, dropout):
X, y = train[:, 0:-1], train[:, -1]
X = X.reshape(X.shape[0], 1, X.shape[1])
model = Sequential()
model.add(LSTM(n_neurons, batch_input_shape=(n_batch, X.shape[1], X.shape[2]), stateful=True, recurrent_dropout=dropout))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
for i in range(nb_epoch):
model.fit(X, y, epochs=1, batch_size=n_batch, verbose=0, shuffle=False)
model.reset_states()
return model
# run a repeated experiment
def experiment(series, n_lag, n_repeats, n_epochs, n_batch, n_neurons, dropout):
# transform data to be stationary
raw_values = series.values
diff_values = difference(raw_values, 1)
# transform data to be supervised learning
supervised = timeseries_to_supervised(diff_values, n_lag)
supervised_values = supervised.values[n_lag:,:]
# split data into train and test-sets
train, test = supervised_values[0:-12], supervised_values[-12:]
# transform the scale of the data
scaler, train_scaled, test_scaled = scale(train, test)
# run experiment
error_scores = list()
for r in range(n_repeats):
# fit the model
train_trimmed = train_scaled[2:, :]
lstm_model = fit_lstm(train_trimmed, n_batch, n_epochs, n_neurons, dropout)
# forecast test dataset
test_reshaped = test_scaled[:,0:-1]
test_reshaped = test_reshaped.reshape(len(test_reshaped), 1, 1)
output = lstm_model.predict(test_reshaped, batch_size=n_batch)
predictions = list()
for i in range(len(output)):
yhat = output[i,0]
X = test_scaled[i, 0:-1]
# invert scaling
yhat = invert_scale(scaler, X, yhat)
# invert differencing
yhat = inverse_difference(raw_values, yhat, len(test_scaled)+1-i)
# store forecast
predictions.append(yhat)
# report performance
rmse = sqrt(mean_squared_error(raw_values[-12:], predictions))
print('%d) Test RMSE: %.3f' % (r+1, rmse))
error_scores.append(rmse)
return error_scores
# configure the experiment
def run():
# load dataset
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# configure the experiment
n_lag = 1
n_repeats = 30
n_epochs = 1000
n_batch = 4
n_neurons = 3
n_dropout = [0.0, 0.2, 0.4, 0.6]
# run the experiment
results = DataFrame()
for dropout in n_dropout:
results[str(dropout)] = experiment(series, n_lag, n_repeats, n_epochs, n_batch, n_neurons, dropout)
# summarize results
print(results.describe())
# save boxplot
results.boxplot()
pyplot.savefig('experiment_dropout_recurrent.png')
运行此实验会打印每个已评估配置的描述性统计量。
平均结果表明,平均复发性 dropout 率为 20%或 40%是首选,但总体而言,结果并不比基线好多少。
0.0 0.2 0.4 0.6
count 30.000000 30.000000 30.000000 30.000000
mean 95.743719 93.658016 93.706112 97.354599
std 9.222134 7.318882 5.591550 5.626212
min 80.144342 83.668154 84.585629 87.215540
25% 88.336066 87.071944 89.859503 93.940016
50% 96.703481 92.522428 92.698024 97.119864
75% 101.902782 100.554822 96.252689 100.915336
max 113.400863 106.222955 104.347850 114.160922
还会创建一个框和胡须图来比较每个配置的结果分布。
该图显示了更紧密的分布,反复 dropout 率为 40%,相比之下,20%和基线,可能使这种配置更可取。该图还强调,当使用反复丢失时,分布中的最小(最佳)测试 RMSE 似乎已受到影响,从而提供更差的表现。
洗发水销售数据集中反复 dropout 表现的盒子和晶须图
我们可以查看 40%的经常性 dropout 率如何影响模型的动态,同时适合训练数据。
下面的代码总结了fit_lstm()和run()函数与诊断脚本基线版本的更新。
# fit an LSTM network to training data
def fit_lstm(train, test, raw, scaler, batch_size, nb_epoch, neurons, dropout):
X, y = train[:, 0:-1], train[:, -1]
X = X.reshape(X.shape[0], 1, X.shape[1])
# prepare model
model = Sequential()
model.add(LSTM(neurons, batch_input_shape=(batch_size, X.shape[1], X.shape[2]), stateful=True, recurrent_dropout=dropout))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
# fit model
train_rmse, test_rmse = list(), list()
for i in range(nb_epoch):
model.fit(X, y, epochs=1, batch_size=batch_size, verbose=0, shuffle=False)
model.reset_states()
# evaluate model on train data
raw_train = raw[-(len(train)+len(test)+1):-len(test)]
train_rmse.append(evaluate(model, raw_train, train, scaler, 0, batch_size))
# evaluate model on test data
raw_test = raw[-(len(test)+1):]
test_rmse.append(evaluate(model, raw_test, test, scaler, 0, batch_size))
history = DataFrame()
history['train'], history['test'] = train_rmse, test_rmse
return history
# run diagnostic experiments
def run():
# config
n_lag = 1
n_repeats = 10
n_epochs = 1000
n_batch = 4
n_neurons = 3
dropout = 0.4
# load dataset
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# transform data to be stationary
raw_values = series.values
diff_values = difference(raw_values, 1)
# transform data to be supervised learning
supervised = timeseries_to_supervised(diff_values, n_lag)
supervised_values = supervised.values[n_lag:,:]
# split data into train and test-sets
train, test = supervised_values[0:-12], supervised_values[-12:]
# transform the scale of the data
scaler, train_scaled, test_scaled = scale(train, test)
# fit and evaluate model
train_trimmed = train_scaled[2:, :]
# run diagnostic tests
for i in range(n_repeats):
history = fit_lstm(train_trimmed, test_scaled, raw_values, scaler, n_batch, n_epochs, n_neurons, dropout)
pyplot.plot(history['train'], color='blue')
pyplot.plot(history['test'], color='orange')
print('%d) TrainRMSE=%f, TestRMSE=%f' % (i+1, history['train'].iloc[-1], history['test'].iloc[-1]))
pyplot.savefig('diagnostic_dropout_recurrent.png')
运行更新的诊断会在每个训练时期之后创建训练图并测试模型的 RMSE 表现以及输入丢失。
该图显示了测试 RMSE 迹线上增加的凸起,对训练 RMSE 迹线几乎没有影响。该图还表明,在大约 500 个时期之后,如果不是测试 RMSE 的增加趋势,则该平台也是如此。
至少在这个 LSTM 配置和这个问题上,可能反复发生的丢失可能不会增加太多价值。
洗发水销售数据集中经常性 dropout 表现的诊断线图
扩展
本节列出了在完成本教程后您可能希望考虑进一步实验的一些想法。
- 输入层丢失。可能值得探讨在输入层上使用压差以及它如何影响 LSTM 的表现和过拟合。
- 组合输入和循环。可能值得探索输入和重复丢失的组合,以查看是否可以提供任何额外的好处。
- 其他正则化方法。使用 LSTM 网络探索其他正则化方法可能是值得的,例如各种输入,循环和偏置权重正则化函数。
进一步阅读
有关在 Keras 中使用 MLP 模型退出的更多信息,请参阅帖子:
以下是一些关于 LSTM 网络 dropout 的论文,您可能会发现这些论文对于进一步阅读非常有用。
摘要
在本教程中,您了解了如何将 dropout 与 LSTM 一起用于时间序列预测。
具体来说,你学到了:
- 如何设计一个强大的测试工具来评估 LSTM 网络的时间序列预测。
- 如何在 LSTM 上配置输入权重丢失以进行时间序列预测。
- 如何在 LSTM 上配置循环重量丢失以进行时间序列预测。
您对使用 LSTM 网络的丢失有任何疑问吗? 在下面的评论中提出您的问题,我会尽力回答。
如何为时间序列预测使用 LSTM 网络中的特征
原文:
machinelearningmastery.com/use-features-lstm-networks-time-series-forecasting/
Keras 中的长短期记忆(LSTM)网络支持多种输入功能。
这就提出了一个问题,即单变量时间序列的滞后观测是否可以用作 LSTM 的特征,以及这是否会改善预测表现。
在本教程中,我们将研究使用滞后观察作为 Python 中 LSTM 模型的特征。
完成本教程后,您将了解:
- 如何开发测试工具以系统地评估 LSTM 功能以进行时间序列预测。
- 使用不同数量的滞后观测值作为 LSTM 模型的输入特征的影响。
- 对 LSTM 模型使用不同数量的滞后观察和匹配数量的神经元的影响。
让我们开始吧。
如何使用 LSTM 网络中的功能进行时间序列预测 Tom Hodgkinson 的照片,保留一些权利。
教程概述
本教程分为 4 个部分。他们是:
- 洗发水销售数据集
- 实验测试线束
- 使用 Timesteps 的实验
- 时间步和神经元的实验
环境
本教程假定您已安装 Python SciPy 环境。您可以在此示例中使用 Python 2 或 3。
本教程假设您安装了 TensorFlow 或 Theano 后端的 Keras v2.0 或更高版本。
本教程还假设您安装了 scikit-learn,Pandas,NumPy 和 Matplotlib。
如果您在设置 Python 环境时需要帮助,请参阅以下帖子:
洗发水销售数据集
该数据集描述了 3 年期间每月洗发水的销售数量。
单位是销售计数,有 36 个观察。原始数据集归功于 Makridakis,Wheelwright 和 Hyndman(1998)。
下面的示例加载并创建已加载数据集的图。
# load and plot dataset
from pandas import read_csv
from pandas import datetime
from matplotlib import pyplot
# load dataset
def parser(x):
return datetime.strptime('190'+x, '%Y-%m')
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# summarize first few rows
print(series.head())
# line plot
series.plot()
pyplot.show()
运行该示例将数据集作为 Pandas Series 加载并打印前 5 行。
Month
1901-01-01 266.0
1901-02-01 145.9
1901-03-01 183.1
1901-04-01 119.3
1901-05-01 180.3
Name: Sales, dtype: float64
然后创建该系列的线图,显示明显的增加趋势。
洗发水销售数据集的线图
接下来,我们将了解实验中使用的 LSTM 配置和测试工具。
实验测试线束
本节介绍本教程中使用的测试工具。
数据拆分
我们将 Shampoo Sales 数据集分为两部分:训练和测试集。
前两年的数据将用于训练数据集,剩余的一年数据将用于测试集。
将使用训练数据集开发模型,并对测试数据集做出预测。
测试数据集的持久性预测(朴素预测)实现了每月洗发水销售 136.761 的错误。这在测试集上提供了较低的可接受表现限制。
模型评估
将使用滚动预测场景,也称为前进模型验证。
测试数据集的每个时间步骤将一次一个地走。将使用模型对时间步长做出预测,然后将获取测试集的实际预期值,并使其可用于下一时间步的预测模型。
这模仿了一个真实世界的场景,每个月都会有新的洗发水销售观察结果,并用于下个月的预测。
这将通过训练和测试数据集的结构进行模拟。
将收集关于测试数据集的所有预测,并计算错误分数以总结模型的技能。将使用均方根误差(RMSE),因为它会对大错误进行处罚,并产生与预测数据相同的分数,即每月洗发水销售额。
数据准备
在我们将 LSTM 模型拟合到数据集之前,我们必须转换数据。
在拟合模型和做出预测之前,对数据集执行以下三个数据变换。
- 转换时间序列数据,使其静止。具体而言,滞后= 1 差分以消除数据中的增加趋势。
- 将时间序列转换为监督学习问题。具体而言,将数据组织成输入和输出模式,其中前一时间步的观察被用作预测当前时间步的观察的输入
- 将观察结果转换为具有特定比例。具体而言,要将数据重缩放为-1 到 1 之间的值,以满足 LSTM 模型的默认双曲正切激活函数。
这些变换在预测时反转,在计算和误差分数之前将它们恢复到原始比例。
LSTM 模型
我们将使用基础状态 LSTM 模型,其中 1 个神经元适合 500 个时期。
批量大小为 1 是必需的,因为我们将使用前向验证并对最后 12 个月的测试数据进行一步预测。
批量大小为 1 意味着该模型将使用在线训练(而不是批量训练或小批量训练)。因此,预计模型拟合将具有一些变化。
理想情况下,将使用更多的训练时期(例如 1000 或 1500),但这被截断为 500 以保持运行时间合理。
使用有效的 ADAM 优化算法和均方误差损失函数来拟合模型。
实验运行
每个实验场景将运行 10 次。
其原因在于,每次训练给定配置时,LSTM 网络的随机初始条件可能导致非常不同的结果。
让我们深入研究实验。
具有特征的实验
我们将进行 5 次实验;每个将使用不同数量的滞后观察作为 1 至 5 的特征。
使用有状态 LSTM 时,具有 1 输入要素的表示将是默认表示。设计使用 2 到 5 个功能。希望是滞后观测的附加背景可以改善预测模型的表现。
在训练模型之前,单变量时间序列被转换为监督学习问题。指定数量的特征定义用于预测下一次观察的输入变量(X)的数量(y)。因此,对于表示中使用的每个要素,必须从数据集的开头删除许多行。这是因为没有先前的观察结果可用作数据集中第一个值的特征。
下面提供了测试 1 输入功能的完整代码清单。
对于 5 个实验中的每一个,run()函数中的特征参数从 1 到 5 变化。此外,结果在实验结束时保存到文件中,并且还必须针对每个不同的实验运行更改该文件名,例如,experiment_features_1.csv,experiment_features_2.csv等
from pandas import DataFrame
from pandas import Series
from pandas import concat
from pandas import read_csv
from pandas import datetime
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from math import sqrt
import matplotlib
import numpy
from numpy import concatenate
# date-time parsing function for loading the dataset
def parser(x):
return datetime.strptime('190'+x, '%Y-%m')
# frame a sequence as a supervised learning problem
def timeseries_to_supervised(data, lag=1):
df = DataFrame(data)
columns = [df.shift(i) for i in range(1, lag+1)]
columns.append(df)
df = concat(columns, axis=1)
return df
# create a differenced series
def difference(dataset, interval=1):
diff = list()
for i in range(interval, len(dataset)):
value = dataset[i] - dataset[i - interval]
diff.append(value)
return Series(diff)
# invert differenced value
def inverse_difference(history, yhat, interval=1):
return yhat + history[-interval]
# scale train and test data to [-1, 1]
def scale(train, test):
# fit scaler
scaler = MinMaxScaler(feature_range=(-1, 1))
scaler = scaler.fit(train)
# transform train
train = train.reshape(train.shape[0], train.shape[1])
train_scaled = scaler.transform(train)
# transform test
test = test.reshape(test.shape[0], test.shape[1])
test_scaled = scaler.transform(test)
return scaler, train_scaled, test_scaled
# inverse scaling for a forecasted value
def invert_scale(scaler, X, yhat):
new_row = [x for x in X] + [yhat]
array = numpy.array(new_row)
array = array.reshape(1, len(array))
inverted = scaler.inverse_transform(array)
return inverted[0, -1]
# fit an LSTM network to training data
def fit_lstm(train, batch_size, nb_epoch, neurons):
X, y = train[:, 0:-1], train[:, -1]
X = X.reshape(X.shape[0], 1, X.shape[1])
model = Sequential()
model.add(LSTM(neurons, batch_input_shape=(batch_size, X.shape[1], X.shape[2]), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
for i in range(nb_epoch):
model.fit(X, y, epochs=1, batch_size=batch_size, verbose=0, shuffle=False)
model.reset_states()
return model
# make a one-step forecast
def forecast_lstm(model, batch_size, X):
X = X.reshape(1, 1, len(X))
yhat = model.predict(X, batch_size=batch_size)
return yhat[0,0]
# run a repeated experiment
def experiment(repeats, series, features):
# transform data to be stationary
raw_values = series.values
diff_values = difference(raw_values, 1)
# transform data to be supervised learning
supervised = timeseries_to_supervised(diff_values, features)
supervised_values = supervised.values[features:,:]
# split data into train and test-sets
train, test = supervised_values[0:-12, :], supervised_values[-12:, :]
# transform the scale of the data
scaler, train_scaled, test_scaled = scale(train, test)
# run experiment
error_scores = list()
for r in range(repeats):
# fit the base model
lstm_model = fit_lstm(train_scaled, 1, 500, 1)
# forecast test dataset
predictions = list()
for i in range(len(test_scaled)):
# predict
X, y = test_scaled[i, 0:-1], test_scaled[i, -1]
yhat = forecast_lstm(lstm_model, 1, X)
# invert scaling
yhat = invert_scale(scaler, X, yhat)
# invert differencing
yhat = inverse_difference(raw_values, yhat, len(test_scaled)+1-i)
# store forecast
predictions.append(yhat)
# report performance
rmse = sqrt(mean_squared_error(raw_values[-12:], predictions))
print('%d) Test RMSE: %.3f' % (r+1, rmse))
error_scores.append(rmse)
return error_scores
# execute the experiment
def run():
# load dataset
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# experiment
repeats = 10
results = DataFrame()
# run experiment
features = 1
results['results'] = experiment(repeats, series, features)
# summarize results
print(results.describe())
# save results
results.to_csv('experiment_features_1.csv', index=False)
# entry point
run()
针对 5 种不同数量的特征运行 5 个不同的实验。
如果有足够的内存和 CPU 资源,可以并行运行它们。这些实验不需要 GPU 资源,运行应该在几分钟到几十分钟内完成。
运行实验后,您应该有 5 个包含结果的文件,如下所示:
- experiment_features_1.csv
- experiment_features_2.csv
- experiment_features_3.csv
- experiment_features_4.csv
- experiment_features_5.csv
我们可以编写一些代码来加载和汇总这些结果。
具体而言,查看每次运行的描述性统计量并使用方框和胡须图比较每次运行的结果非常有用。
下面列出了总结结果的代码。
from pandas import DataFrame
from pandas import read_csv
from matplotlib import pyplot
# load results into a dataframe
filenames = ['experiment_features_1.csv', 'experiment_features_2.csv',
'experiment_features_3.csv','experiment_features_4.csv','experiment_features_5.csv']
results = DataFrame()
for name in filenames:
results[name[11:-4]] = read_csv(name, header=0)
# describe all results
print(results.describe())
# box and whisker plot
results.boxplot()
pyplot.show()
首先运行代码会为每组结果打印描述性统计量。
我们可以从单独的平均表现中看出,使用单个功能的默认值可以获得最佳表现。在查看中位数测试 RMSE(第 50 百分位数)时也会显示这一点。
features_1 features_2 features_3 features_4 features_5
count 10.000000 10.000000 10.000000 10.000000 10.000000
mean 104.588249 126.597800 118.268251 107.694178 116.414887
std 10.205840 18.639757 14.359983 8.683271 18.806281
min 89.046814 93.857991 103.900339 93.702085 98.245871
25% 97.850827 120.296634 107.664087 102.992045 105.660897
50% 103.713285 133.582095 116.123790 106.116922 112.950460
75% 111.441655 134.362198 121.794533 111.498255 117.926664
max 122.341580 149.807155 152.412861 123.006088 164.598542
还创建了比较结果分布的盒子和胡须图。
该情节与描述性统计量相同。随着功能数量的增加,测试 RMSE 似乎跃升了 2 个功能并且趋势向上。
测试 RMSE 的盒子和晶须图与输入特征的数量
至少在使用数据集和 LSTM 配置的情况下,没有观察到随着特征的增加而减少的误差的期望。
这就提出了一个问题,即网络的容量是否是一个限制因素。我们将在下一节中看到这一点。
特征和神经元的实验
LSTM 网络中的神经元(也称为单元)的数量定义了其学习能力。
在先前的实验中,可能使用一个神经元限制了网络的学习能力,使得它不能有效地使用滞后观察作为特征。
我们可以重复上述实验,并随着特征的增加增加 LSTM 中神经元的数量,看看它是否会导致表现的提高。
这可以通过更改实验函数中的行来实现:
lstm_model = fit_lstm(train_scaled, 1, 500, 1, features)
至
lstm_model = fit_lstm(train_scaled, 1, 500, features, features)
此外,我们可以通过在文件名中添加“_neurons”后缀来保持写入文件的结果与第一个实验的结果分开,例如,更改:
results.to_csv('experiment_features_1.csv', index=False)
至
results.to_csv('experiment_features_1_neurons.csv', index=False)
用这些变化重复相同的 5 个实验。
运行这些实验后,您应该有 5 个结果文件。
- experiment_features_1_neurons.csv
- experiment_features_2_neurons.csv
- experiment_features_3_neurons.csv
- experiment_features_4_neurons.csv
- experiment_features_5_neurons.csv
与前一个实验一样,我们可以加载结果,计算描述性统计量,并创建一个盒子和须状图。完整的代码清单如下。
from pandas import DataFrame
from pandas import read_csv
from matplotlib import pyplot
# load results into a dataframe
filenames = ['experiment_features_1_neurons.csv', 'experiment_features_2_neurons.csv',
'experiment_features_3_neurons.csv','experiment_features_4_neurons.csv','experiment_features_5_neurons.csv']
results = DataFrame()
for name in filenames:
results[name[11:-12]] = read_csv(name, header=0)
# describe all results
print(results.describe())
# box and whisker plot
results.boxplot()
pyplot.show()
运行代码首先打印 5 个实验中的每一个的描述性统计量。
结果用一个神经元 LSTM 对第一组实验说明了不同的故事。当神经元数量和特征数量设置为 1 时,平均测试 RMSE 显得最低,然后随着神经元和特征的增加,误差增加。
features_1 features_2 features_3 features_4 features_5
count 10.000000 10.000000 10.000000 10.000000 10.000000
mean 106.219189 138.411111 127.687128 154.281694 175.951500
std 16.100488 29.700981 21.411766 30.526294 44.839217
min 91.073598 92.641030 103.503546 94.063639 117.017109
25% 97.263723 125.748973 108.972440 134.805621 142.146601
50% 99.036766 133.639168 128.627349 162.295657 182.406707
75% 110.625302 146.896608 134.012859 176.969980 197.913894
max 146.638148 206.760081 170.899267 188.911768 250.685187
创建框和胡须图以比较分布。
随着神经元数量和输入特征的增加,扩散和中位表现的趋势几乎表明测试 RMSE 呈线性增加。
线性趋势可能表明增加的网络容量没有足够的时间来拟合数据。也许还需要增加时代数量。
测试 RMSE 的盒子和晶须图与神经元和输入特征的数量
特征和神经元的实验更多时代
在本节中,我们重复上述实验,以增加具有特征数量的神经元数量,但将训练时期的数量从 500 增加到 1000。
这可以通过更改实验函数中的行来实现:
lstm_model = fit_lstm(train_scaled, 1, 500, features, features)
至
lstm_model = fit_lstm(train_scaled, 1, 1000, features, features)
此外,我们可以通过在文件名中添加“1000”后缀来保持写入文件的结果与上一次实验的结果分开,例如,更改:
results.to_csv('experiment_features_1_neurons.csv', index=False)
至
results.to_csv('experiment_features_1_neurons1000.csv', index=False)
用这些变化重复相同的 5 个实验。
运行这些实验后,您应该有 5 个结果文件。
- experiment_features_1_neurons1000.csv
- experiment_features_2_neurons1000.csv
- experiment_features_3_neurons1000.csv
- experiment_features_4_neurons1000.csv
- experiment_features_5_neurons1000.csv
与前一个实验一样,我们可以加载结果,计算描述性统计量,并创建一个盒子和须状图。完整的代码清单如下。
from pandas import DataFrame
from pandas import read_csv
from matplotlib import pyplot
# load results into a dataframe
filenames = ['experiment_features_1_neurons1000.csv', 'experiment_features_2_neurons1000.csv',
'experiment_features_3_neurons1000.csv','experiment_features_4_neurons1000.csv','experiment_features_5_neurons1000.csv']
results = DataFrame()
for name in filenames:
results[name[11:-16]] = read_csv(name, header=0)
# describe all results
print(results.describe())
# box and whisker plot
results.boxplot()
pyplot.show()
运行代码首先打印 5 个实验中的每一个的描述性统计量。
结果与前一个实验的故事非常相似,训练时期数量减少了一半。平均而言,具有 1 个输入特征和 1 个神经元的模型优于其他配置。
features_1 features_2 features_3 features_4 features_5
count 10.000000 10.000000 10.000000 10.000000 10.000000
mean 109.262674 158.295172 120.340623 149.741882 201.992209
std 13.850525 32.288109 45.219564 53.121113 82.986691
min 95.927393 111.936394 83.983325 111.017837 78.040385
25% 98.754253 130.875314 95.198556 122.287208 148.840499
50% 103.990988 167.915523 110.256517 129.552084 188.498836
75% 116.055435 180.679252 122.158321 154.283676 234.519359
max 133.270446 204.260072 242.186747 288.907803 335.595974
还创建了一个盒子和胡须图来比较分布。在情节中,我们看到了与描述性统计中明确相同的趋势。
至少在这个问题和选择的 LSTM 配置中,我们没有看到增加输入功能数量的任何明显好处。
测试 RMSE 的盒子和晶须图与神经元和输入特征的数量以及 1000 个时期
扩展
本节列出了您可能考虑探索的一些进一步调查的领域。
- 诊断运行图。对于给定的实验,在多次运行的情况下查看训练和测试 RMSE 的图可能是有帮助的。这可能有助于梳理过拟合或过拟合是否正在发生,反过来又是解决它的方法。
- 增加重复次数。使用 10 次重复导致相对少量的测试 RMSE 结果。将重复增加至 30 或 100(或甚至更高)可能导致更稳定的结果。
你有没有探索过这些扩展? 在下面的评论中分享您的发现;我很想听听你发现了什么。
摘要
在本教程中,您了解了如何使用滞后观察作为 LSTM 网络中的输入要素进行调查。
具体来说,你学到了:
- 如何开发一个强大的测试工具来尝试使用 LSTM 进行输入表示。
- 如何使用滞后观测作为 LSTM 时间序列预测的输入特征。
- 如何通过增加输入功能来增加网络的学习能力。
您发现“_ 使用滞后观察作为输入功能可以提高模型技能 _”并未降低所选问题和 LSTM 配置的测试 RMSE。
你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。