Machine Learning Mastery 深度学习时间序列教程(二)
用于时间序列预测的多层感知机网络的探索性配置
当开始使用神经网络的新预测性建模项目时,可能会很困难。
有很多配置,并没有明确的想法从哪里开始。
系统化很重要。您可以打破糟糕的假设并快速磨练有效的配置和可能需要进一步调查的区域。
在本教程中,您将了解如何使用多层感知机(MLP)神经网络的探索性配置来为时间序列预测找到良好的首先模型。
完成本教程后,您将了解:
- 如何设计一个强大的实验测试工具来评估 MLP 模型的时间序列预测。
- 针对不同时期,神经元和滞后配置的系统实验设计。
- 如何解释结果并使用诊断来了解有关良好表现模型的更多信息。
让我们开始吧。
- 2017 年 7 月更新:更改了创建模型的功能,使其更具描述性。
用于时间序列预测的多层感知机网络的探索性配置 照片由 Lachlan Donald 拍摄,保留一些权利。
教程概述
本教程分为 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
然后创建该系列的线图,显示明显的增加趋势。
洗发水销售数据集的线图
接下来,我们将看一下实验中使用的模型配置和测试工具。
实验测试线束
本节介绍本教程中使用的测试工具。
数据拆分
我们将 Shampoo Sales 数据集分为两部分:训练和测试集。
前两年的数据将用于训练数据集,剩余的一年数据将用于测试集。
将使用训练数据集开发模型,并对测试数据集做出预测。
测试数据集的持久性预测(朴素预测)实现了每月洗发水销售 136.761 的错误。这在测试集上提供了较低的可接受表现限制。
模型评估
将使用滚动预测场景,也称为前进模型验证。
测试数据集的每个时间步骤将一次一个地走。将使用模型对时间步长做出预测,然后将获取测试集的实际预期值,并使其可用于下一时间步的预测模型。
这模仿了一个真实世界的场景,每个月都会有新的洗发水销售观察结果,并用于下个月的预测。
这将通过训练和测试数据集的结构进行模拟。
将收集关于测试数据集的所有预测,并计算错误分数以总结模型的技能。将使用均方根误差(RMSE),因为它会对大错误进行处罚,并产生与预测数据相同的分数,即每月洗发水销售额。
数据准备
在我们将 MLP 模型拟合到数据集之前,我们必须转换数据。
在拟合模型和做出预测之前,对数据集执行以下三个数据变换。
- 转换时间序列数据,使其静止。具体而言,滞后= 1 差分以消除数据中的增加趋势。
- 将时间序列转换为监督学习问题。具体而言,将数据组织成输入和输出模式,其中前一时间步的观察被用作预测当前时间步的观察的输入
- 将观察结果转换为具有特定比例。具体而言,将数据重缩放为-1 到 1 之间的值。
这些变换在预测时反转,在计算和误差分数之前将它们恢复到原始比例。
MLP 模型
我们将使用具有 1 个神经元隐藏层的基础 MLP 模型,对隐藏神经元的整流线性激活函数,以及输出神经元上的线性激活函数。
在可能的情况下使用批量大小为 4,并且截断训练数据以确保模式的数量可被 4 整除。在某些情况下,使用批量大小为 2。
通常,训练数据集在每个批次或每个时期之后进行混洗,这有助于将训练数据集拟合到分类和回归问题上。所有实验都关闭了改组,因为它似乎可以带来更好的表现。需要更多的研究来确认时间序列预测的结果。
使用有效的 ADAM 优化算法和均方误差损失函数来拟合模型。
实验运行
每个实验场景将运行 30 次,并且测试集上的 RMSE 得分将从每次运行结束时记录。
让我们深入研究实验。
不同的训练时代
在第一个实验中,我们将研究改变隐藏层中具有一个隐藏层和一个神经元的简单 MLP 的训练时期的数量。
我们将使用批量大小为 4 并评估训练时期 50,100,500,1000 和 2000。
完整的代码清单如下。
此代码清单将用作所有后续实验的基础,后续部分中仅提供对此代码的更改。
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 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 MLP network to training data
def fit_model(train, batch_size, nb_epoch, neurons):
X, y = train[:, 0:-1], train[:, -1]
model = Sequential()
model.add(Dense(neurons, activation='relu', input_dim=X.shape[1]))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
model.fit(X, y, epochs=nb_epoch, batch_size=batch_size, verbose=0, shuffle=False)
return model
# run a repeated experiment
def experiment(repeats, series, epochs, lag, 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, lag)
supervised_values = supervised.values[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(repeats):
# fit the model
batch_size = 4
train_trimmed = train_scaled[2:, :]
model = fit_model(train_trimmed, batch_size, epochs, neurons)
# forecast test dataset
test_reshaped = test_scaled[:,0:-1]
output = 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()
lag = 1
neurons = 1
# vary training epochs
epochs = [50, 100, 500, 1000, 2000]
for e in epochs:
results[str(e)] = experiment(repeats, series, e, lag, neurons)
# summarize results
print(results.describe())
# save boxplot
results.boxplot()
pyplot.savefig('boxplot_epochs.png')
运行实验在每次实验运行结束时打印测试集 RMSE。
在所有运行结束时,提供了一个摘要统计表,每个统计量对应一行,每列有一个配置。
总结统计表明,平均 1000 个训练时期导致更好的表现,随着训练时期的增加,误差总体下降趋势。
50 100 500 1000 2000
count 30.000000 30.000000 30.000000 30.000000 30.000000
mean 129.660167 129.388944 111.444027 103.821703 107.500301
std 30.926344 28.499592 23.181317 22.138705 24.780781
min 94.598957 94.184903 89.506815 86.511801 86.452041
25% 105.198414 105.722736 90.679930 90.058655 86.457260
50% 129.705407 127.449491 93.508245 90.118331 90.074494
75% 141.420145 149.625816 136.157299 135.510850 135.741340
max 198.716220 198.704352 141.226816 139.994388 142.097747
还创建了每个配置的测试 RMSE 分数分布的盒子和须状图,并保存到文件中。
该图强调了每个配置在测试 RMSE 分数(框)中显示相同的一般分布,中位数(绿线)随着训练时期的增加而向下趋势。
结果证实,配置的 MLP 训练 1000 是这个问题的一个很好的起点。
用于洗发水销售数据集的时间序列预测的变化训练时期的盒子和晶须图
网络配置需要考虑的另一个角度是模型适应时的行为方式。
我们可以在每个训练时期之后评估训练和测试数据集上的模型,以了解配置是否过拟合或不适合问题。
我们将在每组实验的最佳结果上使用此诊断方法。将运行总共 10 次重复的配置,并且在线图上绘制每个训练迭代之后的训练和测试 RMSE 得分。
在这种情况下,我们将在 MLP 适合 1000 个时期使用此诊断。
完整的诊断代码清单如下。
与前面的代码清单一样,下面的代码清单将用作本教程中所有诊断的基础,并且后续部分中仅提供对此清单的更改。
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 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]
# forecast dataset
output = model.predict(X, 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 MLP network to training data
def fit(train, test, raw, scaler, batch_size, nb_epoch, neurons):
X, y = train[:, 0:-1], train[:, -1]
# prepare model
model = Sequential()
model.add(Dense(neurons, activation='relu', input_dim=X.shape[1]))
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)
# 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
repeats = 10
n_batch = 4
n_epochs = 1000
n_neurons = 1
n_lag = 1
# 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(repeats):
history = fit(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('diagnostic_epochs.png')
# entry point
run()
运行诊断程序打印最终训练并测试每次运行的 RMSE。更有趣的是创建的最终线图。
线图显示了每个训练时期之后的训练 RMSE(蓝色)和测试 RMSE(橙色)。
在这种情况下,诊断图显示在大约 400 个训练时期之后训练和测试 RMSE 几乎没有差异。训练和测试表现均接近平坦线。
这种快速平衡表明模型正在达到容量,并且可以从滞后观察或额外神经元方面的更多信息中受益。
训练诊断线图和洗发水销售数据集 1000 个时期的测试表现
改变隐藏层神经元
在本节中,我们将研究改变单个隐藏层中神经元的数量。
增加神经元的数量会增加网络的学习能力,从而存在过拟合训练数据的风险。
我们将探索将神经元的数量从 1 增加到 5 并使网络适合 1000 个时期。
下面列出了实验脚本的不同之处。
# 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()
lag = 1
epochs = 1000
# vary neurons
neurons = [1, 2, 3, 4, 5]
for n in neurons:
results[str(n)] = experiment(repeats, series, epochs, lag, n)
# summarize results
print(results.describe())
# save boxplot
results.boxplot()
pyplot.savefig('boxplot_neurons.png')
运行实验将打印每个配置的摘要统计量。
从平均表现来看,它表明测试 RMSE 随着单个隐藏层中神经元数量的增加而减少。
最好的结果似乎是 3 个神经元。
1 2 3 4 5
count 30.000000 30.000000 30.000000 30.000000 30.000000
mean 105.107026 102.836520 92.675912 94.889952 96.577617
std 23.130824 20.102353 10.266732 9.751318 6.421356
min 86.565630 84.199871 83.388967 84.385293 87.208454
25% 88.035396 89.386670 87.643954 89.154866 89.961809
50% 90.084895 91.488484 90.670565 91.204303 96.717739
75% 136.145248 104.416518 93.117926 100.228730 101.969331
max 143.428154 140.923087 136.883946 135.891663 106.797563
还创建了一个盒子和须状图,以总结和比较结果的分布。
该图证实了 3 个神经元与其他配置相比表现良好的建议,并且还表明结果的扩散也较小。这可能表明配置更稳定。
用于洗发水销售数据集的时间序列预测的变异隐藏神经元的盒子和晶须图
同样,我们可以通过回顾适用于 1000 个时期的 3 个神经元的所选配置的诊断来更深入地潜水。
诊断脚本的更改仅限于run()功能,如下所示。
# run diagnostic experiments
def run():
# config
repeats = 10
n_batch = 4
n_epochs = 1000
n_neurons = 3
n_lag = 1
# 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(repeats):
history = fit(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('diagnostic_neurons.png')
运行诊断脚本为每个训练时期提供了训练和测试 RMSE 的线图。
这种诊断方法表明模型技能可能已经过时,可能大约有 400 个时代。该图还提出了过拟合的可能情况,其中测试 RMSE 在过去 500 个训练时期略有增加,但训练 RMSE 没有强烈增加。
训练诊断线图和 3 个隐藏神经元在洗发水销售数据集上的测试表现
具有滞后的变隐藏层神经元
在本节中,我们将考虑将滞后观测值作为输入增加,同时增加网络容量。
增加的滞后观察将自动缩放输入神经元的数量。例如,作为输入的 3 个滞后观察将导致 3 个输入神经元。
添加的输入将需要网络中的额外容量。因此,我们还将使用滞后观察的数量作为输入来缩放一个隐藏层中的神经元的数量。
我们将使用奇数个滞后观察作为 1,3,5 和 7 的输入,并分别使用相同数量的神经元。
输入数量的改变影响在将时间序列数据转换为监督学习问题期间的训练模式的总数。因此,对于本节中的所有实验,批量大小从 4 减少到 2。
在每次实验运行中总共使用 1000 个训练时期。
基础实验脚本的更改仅限于 _ 实验()_ 功能和实验运行,如下所示。
# run a repeated experiment
def experiment(repeats, series, epochs, lag, 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, lag)
supervised_values = supervised.values[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(repeats):
# fit the model
batch_size = 2
model = fit_model(train_scaled, batch_size, epochs, neurons)
# forecast test dataset
test_reshaped = test_scaled[:,0:-1]
output = 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()
epochs = 1000
# vary neurons
neurons = [1, 3, 5, 7]
for n in neurons:
results[str(n)] = experiment(repeats, series, epochs, n, n)
# summarize results
print(results.describe())
# save boxplot
results.boxplot()
pyplot.savefig('boxplot_neurons_lag.png')
运行实验会使用每个配置的描述性统计量汇总结果。
结果表明滞后输入变量的所有增加随隐藏神经元的增加而降低表现。
值得注意的是 1 个神经元和 1 个输入配置,与上一节的结果相比,产生了类似的均值和标准差。
表现的降低可能与较小的批量大小有关,并且 1-神经元/ 1 滞后情况的结果不足以解释这一点。
1 3 5 7
count 30.000000 30.000000 30.000000 30.000000
mean 105.465038 109.447044 158.894730 147.024776
std 20.827644 15.312300 43.177520 22.717514
min 89.909627 77.426294 88.515319 95.801699
25% 92.187690 102.233491 125.008917 132.335683
50% 92.587411 109.506480 166.438582 145.078842
75% 135.386125 118.635143 189.457325 166.329000
max 139.941789 144.700754 232.962778 186.185471
还创建了结果分布的盒子和须状图,允许比较配置。
有趣的是,与其他配置相比,使用 3 个神经元和 3 个输入变量显示更紧密的传播。这类似于上一节中所见的 3 个神经元和 1 个输入变量的观察结果。
用于洗发水销售数据集的时间序列预测的变化滞后特征和隐藏神经元的盒子和晶须图
我们还可以使用诊断来梳理模型动态在拟合模型时可能发生的变化。
3-lag / 3-neurons 的结果很有意思,我们将进一步研究它们。
诊断脚本的更改仅限于run()功能。
# run diagnostic experiments
def run():
# config
repeats = 10
n_batch = 2
n_epochs = 1000
n_neurons = 3
n_lag = 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(repeats):
history = fit(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('diagnostic_neurons_lag.png')
运行诊断脚本会创建一个线图,显示在每个训练时期之后 10 次实验运行的训练和测试 RMSE。
结果表明在前 500 个时期内学习良好,并且可能在剩余的时期过拟合,测试 RMSE 显示出增加的趋势,并且训练 RMSE 显示出下降趋势。
训练诊断线图和 3 个隐藏神经元的测试表现和洗发水销售数据集的滞后特征
审查结果
我们在本教程中介绍了很多内容。让我们来复习。
- 时代。我们研究了模型技能如何随着训练时期的变化而变化,并发现 1000 可能是一个很好的起点。
- 神经元。我们研究了隐藏层中神经元数量的变化,发现 3 个神经元可能是一个很好的配置。
- 滞后输入。我们考虑将滞后观察的数量作为输入变化,同时增加隐藏层中神经元的数量,并发现结果通常变得更糟,但是隐藏层中的 3 个神经元显示出兴趣。与其他实验相比,差的结果可能与批量大小从 4 变为 2 有关。
结果表明,在隐藏层中使用 1 个滞后输入,3 个神经元,并且适合作为首次切割模型配置的 1000 个时期。
这可以通过多种方式得到改善;下一节列出了一些想法。
扩展
本节列出了您可能想要探索的扩展和后续实验。
- Shuffle vs No Shuffle 。没有使用洗牌,这是不正常的。在拟合时间序列预测模型时,开发一个实验来比较改组与训练集的无改组。
- 归一化方法。数据重缩放为-1 到 1,这是 tanh 激活函数的典型值,未在模型配置中使用。探索其他重新缩放,例如 0-1 规范化和标准化以及对模型表现的影响。
- 多层。探索使用多个隐藏层来增加网络容量,以了解更复杂的多步模式。
- 特色工程。探索使用其他功能,例如错误时间序列,甚至每个观察的日期时间元素。
另外,看看帖子:
你尝试过这些扩展吗? 在以下评论中发布您的结果。
摘要
在本教程中,您了解了如何使用系统实验来探索多层感知机在时间序列预测中的配置,并开发出第一个切割模型。
具体来说,你学到了:
- 如何开发一个强大的测试工具来评估时间序列预测的 MLP 模型。
- 如何系统地评估训练时期,隐藏层神经元和滞后输入。
- 如何使用诊断来帮助解释结果并建议后续实验。
您对本教程有任何疑问吗? 在下面的评论中提出您的问题,我会尽力回答。
比较时间序列预测的的经典和机器学习方法的结果
通常报告机器学习和深度学习方法是所有预测性建模问题的关键解决方案。
最近一项重要的研究评估并比较了许多经典和现代机器学习和深度学习方法在 1000 多个单变量时间序列预测问题的大量不同组合中的表现。
这项研究的结果表明,简单的经典方法,如线性方法和指数平滑,优于复杂和复杂的方法,如决策树,多层感知机(MLP)和长期短期记忆(LSTM)网络模型。
这些发现突出了评估经典方法并将其结果用作评估任何机器学习和时间序列预测的深度学习方法的基线的要求,以证明其增加的复杂性增加了预测技能。
在这篇文章中,您将发现这项最新研究的重要发现,评估和比较经典和现代机器学习方法在大量不同时间序列预测数据集上的表现。
阅读这篇文章后,你会知道:
- 像 ETS 和 ARIMA 这样的经典方法胜过机器学习和深度学习方法,可以对单变量数据集进行一步预测。
- 像 Theta 和 ARIMA 这样的经典方法胜过机器学习和深度学习方法,可以对单变量数据集进行多步预测。
- 机器学习和深度学习方法还没有兑现他们对单变量时间序列预测的承诺,还有很多工作要做。
让我们开始吧。
研究结果比较时间序列预测的经典和机器学习方法 Lyndon Hatherall 的照片,保留一些权利。
概观
Spyros Makridakis ,等。发表于 2018 年的一项研究题为“统计和机器学习预测方法:关注和前进方向。”
在这篇文章中,我们将仔细研究 Makridakis 等人的研究。仔细评估和比较经典时间序列预测方法与现代机器学习方法的表现。
这篇文章分为七个部分;他们是:
- 学习动机
- 时间序列数据集
- 时间序列预测方法
- 数据准备
- 一步预测结果
- 多步预测结果
- 成果
学习动机
该研究的目的是清楚地展示一套不同的机器学习方法的能力,与传统的时间序列预测方法相比,它可以收集大量不同的单变量时间序列预测问题。
该研究是对越来越多的论文的回应,并声称机器学习和深度学习方法为时间序列预测提供了优异的结果,几乎没有客观证据。
数以百计的论文提出了新的 ML 算法,提出了方法上的进步和准确率的改进。然而,关于它们作为标准预测工具的相对表现,可获得有限的客观证据。
- 统计和机器学习预测方法:关注点和前进方向,2018。
作者清楚地提出了大量索赔的三个问题;他们是:
- 他们的结论基于一些甚至一个时间序列,提出了关于结果的统计显着性及其概括性的问题。
- 对这些方法进行短期预测视野评估,通常是一步到位,不考虑中期和长期预测。
- 没有使用基准来比较 ML 方法与其他方法的准确率。
作为回应,该研究包括八种经典方法和 10 种机器学习方法,使用一个步骤和多步骤预测,在 1,045 个月度时间序列的集合中进行评估。
虽然不是确定的,但结果旨在客观和稳健。
时间序列数据集
研究中使用的时间序列数据集来自 M3 竞赛中使用的时间序列数据集。
M3 竞赛是一系列竞赛中的第三项,旨在准确发现在实时系列预测问题中哪些算法在实践中表现良好。比赛结果发表在 2000 年题为“ M3 竞赛:结果,结论和影响”的论文中。
比赛中使用的数据集来自各行各业,并且具有从小时到年度的各种不同时间间隔。
M3-Competition 的 3003 系列是在配额基础上选择的,包括各种类型的时间序列数据(微观,行业,宏观等)和连续观测之间的不同时间间隔(年度,季度等)。
下表摘自论文,提供了竞赛中使用的 3,003 个数据集的摘要。
M3 竞赛中使用的数据集,行业和时间间隔表 取自“M3 竞赛:结果,结论和影响”。
竞争的结果是更简单的时间序列预测方法优于更复杂的方法,包括神经网络模型。
这项研究,前两次 M-Competitions 和许多其他实证研究已经证明,除了最简单的疑问之外,精心设计的理论结构或更复杂的方法并不一定能提高样本后预测的准确率,而不是简单的方法,尽管它们可以更好地适应统计模型到可用的历史数据。
- M3-竞争:结果,结论和影响,2000。
我们在本文中回顾的最近一项评估机器学习方法的研究选择了 1,045 个时间序列的子集,每月间隔与 M3 竞赛中使用的间隔相比。
...使用 M3 竞赛中使用的 1045 个月度时间序列的大部分,在多个预测视野中评估此类表现。
- 统计和机器学习预测方法:关注点和前进方向,2018。
时间序列预测方法
该研究评估了八种经典(或简单)方法和 10 种机器学习方法的表现。
......八种传统统计方法和八种流行的 ML 方法,以及近两年来最受欢迎的两种方法。
- 统计和机器学习预测方法:关注点和前进方向,2018。
评估的八种经典方法如下:
- 朴素 2,实际上是一个随季节调整的随机游走模型。
- 简单的指数平滑。
- 霍尔特。
- 阻尼指数平滑。
- SES,Holt 和 Damped 的平均值。
- Theta 方法。
- ARIMA,自动。
- ETS,自动。
共有八种机器学习方法用于重现和比较 2010 年论文“时间序列预测的机器学习模型的经验比较”中提供的结果。
他们是:
- 多层感知机(MLP)
- 贝叶斯神经网络(BNN)
- 径向基函数(RBF)
- 广义循环神经网络(GRNN),也称为核回归
- K-最近邻回归(KNN)
- CART 回归树(CART)
- 支持向量回归(SVR)
- 高斯过程(GP)
另外两个'_ 现代 _'神经网络算法也被添加到列表中,因为它们的采用率最近有所上升;他们是:
- 循环神经网络(RNN)
- 长短期记忆(LSTM)
数据准备
再次,基于 2010 年论文“时间序列预测的机器学习模型的经验比较”中描述的方法,使用了谨慎的数据准备方法。
在那篇论文中,每个时间序列都是使用幂变换进行调整,去季节化和去趋势化。
[...]在计算 18 个预测之前,他们对系列进行了预处理,以实现平均值和方差的平稳性。这是使用对数转换,然后去季节化和最终缩放来完成的,同时还考虑了去除趋势分量的第一个差异。
- 统计和机器学习预测方法:关注点和前进方向,2018。
受这些操作的启发,对一步预测的 MLP 应用了五种不同数据转换的变体,并对它们的结果进行了比较。五个转变是:
- 原始数据。
- Box-Cox 功率变换。
- 延长数据的时间。
- 去除数据。
- 所有三个变换(权力,去季节化,趋势)。
一般来说,人们发现最好的方法是应用功率变换并对数据进行去季节化,并且可能也会对系列产生不利影响。
根据 sMAPE 的最佳组合是 7 号(Box-Cox 转换,deseasonalization),而根据 MASE 的最佳组合是 10 号(Box-Cox 转换,deseasonalization 和 detrending)
- 统计和机器学习预测方法:关注点和前进方向,2018。
一步预测结果
所有模型均使用一步时间序列预测进行评估。
具体而言,最后 18 个时间步骤用作测试集,并且模型适合于所有剩余的观察。对测试集中的 18 个观测值中的每个观测值进行单独的一步预测,可能使用前向验证方法,其中使用真实观测值作为输入以进行每个预测。
预测模型是使用前 n - 18 个观测值开发的,其中 n 是序列的长度。然后,生成了 18 个预测,并且与开发预测模型时未使用的实际值进行了评估。
- 统计和机器学习预测方法:关注点和前进方向,2018。
回顾结果,发现 MLP 和 BNN 从所有机器学习方法中获得最佳表现。
结果表明,MLP 和 BNN 的表现优于其余的 ML 方法。
- 统计和机器学习预测方法:关注点和前进方向,2018。
令人惊讶的结果是发现 RNN 和 LSTM 表现不佳。
应该指出的是,RNN 是不太准确的 ML 方法之一,这表明研究进展并不一定能保证预测表现的提高。这个结论也适用于 LSTM 的表现,LSTM 是另一种流行且更先进的 ML 方法,它也不能提高预测准确率。
- 统计和机器学习预测方法:关注点和前进方向,2018。
比较所有方法的表现,发现机器学习方法都是通过简单的经典方法进行的,其中 ETS 和 ARIMA 模型的整体表现最佳。
这一发现证实了之前类似研究和竞赛的结果。
条形图比较模型表现(sMAPE)的一步预测 取自“统计和机器学习预测方法:关注和前进方向”。
多步预测结果
多步骤预测涉及在最后一次已知观察之前预测多个步骤。
针对机器学习方法评估了多步预测的三种方法;他们是:
- 迭代预测
- 直接预测
- 多神经网络预测
发现经典方法再次优于机器学习方法。
在这种情况下,发现诸如 Theta,ARIMA 和指数平滑(梳状)的组合的方法实现了最佳表现。
简而言之,统计模型在所有预测视野中似乎总体上优于 ML 方法,根据所检查的误差指标,Theta,Comb 和 ARIMA 在竞争者中占主导地位。
- 统计和机器学习预测方法:关注点和前进方向,2018。
成果
该研究提供了重要的支持证据,即经典方法可能主导单变量时间序列预测,至少在评估的预测问题类型上如此。
该研究表明,对于单步和多步预测的单变量时间序列预测,机器学习和深度学习方法的表现更差,计算成本增加。
这些发现强烈鼓励使用经典方法,如 ETS,ARIMA 等,作为探索更精细方法之前的第一步,并要求将这些简单方法的结果用作表现的基线,以便更精细的方法必须清楚,以证明其使用的合理性。
它还强调了不仅要考虑仔细使用数据准备方法,而且需要主动测试针对给定问题的多种不同数据准备方案组合,以便发现哪种方法最有效,即使在经典方法的情况下也是如此。
机器学习和深度学习方法仍然可以在特定的单变量时间序列问题上获得更好的表现,并且应该进行评估。
该研究没有考虑更复杂的时间序列问题,例如那些数据集:
- 复杂的不规则时间结构
- 缺少观察
- 噪音很大。
- 多个变量之间复杂的相互关系。
该研究总结了一个诚实的困惑,为什么机器学习方法在实践中表现如此糟糕,因为它们在其他人工智能领域表现出色。
最有趣的问题和最大的挑战是找出其表现不佳的原因,目的是提高准确率并发挥其巨大潜力。人工智能学习算法已经彻底改变了各种领域的广泛应用,并且没有理由用 ML 方法在预测中无法实现同样的目标。因此,我们必须找到如何应用以提高他们更准确的预测能力。
- 统计和机器学习预测方法:关注点和前进方向,2018。
作者对 LSTM 和 RNN 进行了评论,这些评论通常被认为是一般的序列预测问题的深度学习方法,在这种情况下,它们在实践中表现明显不佳。
[...]人们会期望更高级的 NN 类型的 RNN 和 LSTM 比 ARIMA 和其他统计方法更准确。
- 统计和机器学习预测方法:关注点和前进方向,2018。
他们评论说 LSTM 似乎更适合拟合或过拟合训练数据集而不是预测它。
另一个有趣的例子可能是 LSTM 的情况与较简单的 NN(如 RNN 和 MLP)相比,报告更好的模型拟合但更差的预测准确率
- 统计和机器学习预测方法:关注点和前进方向,2018。
有工作要做,机器学习方法和深度学习方法比传统的统计方法更有学习时间序列数据的承诺,甚至通过自动特征学习直接对原始观察这样做。
鉴于他们的学习能力,ML 方法应该比简单的基准测试更好,比如指数平滑。接受问题是设计可行解决方案的第一步,我们希望 AI 和 ML 领域的人员能够接受实证研究结果并努力提高其方法的预测准确率。
- 统计和机器学习预测方法:关注点和前进方向,2018。
进一步阅读
如果您希望深入了解,本节将提供有关该主题的更多资源。
- Makridakis 比赛,维基百科
- M3-竞争:结果,结论和影响,2000。
- M4 竞赛:结果,发现,结论和前进方向,2018。
- 统计和机器学习预测方法:关注点和前进方向,2018。
- 时间序列预测机器学习模型的实证比较,2010。
摘要
在这篇文章中,您发现了最近一项研究的重要发现,该研究评估和比较了经典和现代机器学习方法在大量不同时间序列预测数据集上的表现。
具体来说,你学到了:
- 像 ETS 和 ARIMA 这样的经典方法胜过机器学习和深度学习方法,可以对单变量数据集进行一步预测。
- 像 Theta 和 ARIMA 这样的经典方法胜过机器学习和深度学习方法,可以对单变量数据集进行多步预测。
- 机器学习和深度学习方法尚未兑现其对单变量时间序列预测的承诺,还有很多工作要做。
你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。
如何通过深度学习快速获得时间序列预测的结果
原文:
machinelearningmastery.com/get-good-results-fast-deep-learning-time-series-forecasting/
3
您的预测性建模问题设计实验和管理复杂性的策略。
新的时间序列预测项目很难开始。
鉴于多年的数据,适合深度学习模型可能需要数天或数周。你是如何开始的?
对于一些从业者来说,这可能会导致项目一开始就瘫痪甚至拖延。在其他情况下,它可能导致陷入只尝试和使用以前工作的陷阱,而不是真正探索问题。
在这篇文章中,您将发现在将多层神经网络和长短期记忆(LSTM)循环神经网络模型等深度学习方法应用于时间序列预测问题时可以使用的实用策略。
这篇文章中的策略并非万无一失,但是我在处理大型时间序列数据集时发现了很难学到的经验法则。
阅读这篇文章后,你会知道:
- 一种平衡思想探索和利用对你的问题起作用的策略。
- 一种快速学习和利用数据扩展思路的策略,以确认它们能够解决更广泛的问题。
- 一种策略,可以解决问题框架的复杂性以及所选深度学习模型的复杂性。
让我们开始吧。
1.勘探和开发战略
在搜索能够很好地解决问题的模型时,平衡探索和利用非常重要。
我建议两种不同的方法应该串联使用:
- 诊断。
- 网格搜索。
诊断
诊断涉及使用一组超参数执行运行,并在每个训练时期的训练和测试数据集上生成模型技能的痕迹。
这些图提供了对过度学习或学习不足以及特定超参数集的潜力的深入了解。
它们是健全性检查或种子,用于深入研究可以探索的参数范围,并防止您浪费时间,使其具有比合理要求更多的时代,或者太大的网络。
以下是来自模型运行的诊断图的示例,其显示了训练和验证 RMSE。
示例诊断线图比较训练时期的训练和测试损失
网格搜索
基于对诊断结果的学习,网格搜索提供了针对特定模型超参数的一组值的扫描,例如神经元的数量,批量大小等。
它们允许您以分段方式系统地拨入特定的超参数值。
交织方法
我建议交错诊断运行和网格搜索运行。
您可以通过诊断检查您的假设,并通过网格搜索结果从有前途的想法中获得最佳效果。
我强烈建议你测试一下你对模型的每一个假设。这包括简单的事情,如数据缩放,权重初始化,甚至激活函数,损失函数等的选择。
与下面的数据处理策略一起使用,您将快速构建一个有关预测问题的有效和无效的映射。
下面是模型批量大小的网格搜索结果示例,显示每个实验重复 30 次的结果分布。
示例框和晶须图比较不同模型参数值的模型技巧
2.处理数据大小的策略
我建议首先使用较小的数据样本来测试想法并慢慢增加数据量,以查看在小样本上学到的东西是否包含更大的样本。
例如,如果您有多年的每小时测量,则可以按如下方式拆分数据:
- 1 周样本。
- 1 个月的样本。
- 1 年样本。
- 所有数据。
另一种方法是,您可以在整个数据集中拟合和探索模型,其中每个模型可能需要数天才能适应,这反过来意味着您的学习速度会大幅降低。
这种方法的好处是,您可以在几分钟内快速测试多次重复(例如统计上显着)的想法,然后将这些有前途的想法扩展到越来越多的数据。
一般来说,有了良好框架的监督学习问题,学习的确会随着数据而扩展。然而,存在这样的风险:在不同的数据规模上问题存在很大差异,并且结果不成立。您可以使用更简单的模型来检查这一点,这些模型可以更快地进行训练,并在早期就弄清楚这是否是一个问题。
最后,当您将模型扩展到更多数据时,您还可以减少实验的重复次数,以帮助加快结果的周转时间。
3.模型复杂性策略
与数据大小一样,模型的复杂性是另一个必须管理并可以扩展的问题。
我们可以从监督学习问题的框架和模型本身来看待这一点。
模型框架复杂性
例如,我们可以假设包括外生变量的时间序列预测问题(例如,多输入序列或多变量输入)。
我们可以扩展问题的复杂性,看看在一个复杂程度(例如,单变量输入)中工作的是复杂的复杂程度(多变量输入)。
例如,您可以通过以下方式处理模型复杂性:
- 单变量输入,单步输出。
- 单变量输入,多步输出。
- 多变量输入,单步输出。
- 多变量输入,多步输出。
这也可以扩展到多变量预测。
在每个步骤中,目标是证明增加复杂性可以提升模型的技能。
例如:
- 神经网络模型能否胜过持久性预测模型?
- 神经网络模型能否胜过线性预测模型?
- 外生输入变量可以通过单变量输入提升模型的技能吗?
- 直接多步骤预测能否比递归单步预测更具技巧性?
如果这些问题无法克服或轻易克服,它可以帮助您快速解决问题框架和所选模型。
模型能力的复杂性
当使用更复杂的神经网络模型(如 LSTM)时,可以使用相同的方法。
例如:
- 将问题建模为输入到输出的映射(例如,没有内部状态或 BPTT)。
- 将问题建模为仅在输入序列中具有内部状态的映射问题(无 BPTT)。
- 将问题建模为具有内部状态和 BPTT 的映射问题。
在每个步骤中,增加的模型复杂性必须证明技能处于或高于先前的复杂程度。换句话说,增加的模型复杂性必须通过模型技能或能力的相应增加来证明。
例如:
- LSTM 能否胜过带窗口的 MLP?
- 具有内部状态且没有 BPTT 的 LSTM 能否优于 LSTM,其中状态在每个样本后重置?
- BPTT 超过输入序列的 LSTM 能否优于每个时间步后更新的 LSTM 吗?
进一步阅读
如果您要深入了解,本节将提供有关该主题的更多资源。
摘要
在本教程中,您了解了如何克服在深度学习项目开始时可能出现的瘫痪。
具体来说,您了解了如何系统地分解可用于快速获得结果的复杂性和策略:
- 一种平衡思想探索和利用对你的问题起作用的策略。
- 一种快速学习和利用数据扩展思路的策略,以确认它们能够解决更广泛的问题。
- 一种策略,可以解决问题框架的复杂性以及所选深度学习模型的复杂性。
你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。
如何利用 Python 处理序列预测问题中的缺失时间步长
原文:
machinelearningmastery.com/handle-missing-timesteps-sequence-prediction-problems-python/
通常缺少来自序列数据的观察结果。
数据可能已损坏或不可用,但根据定义,您的数据也可能具有可变长度序列。具有较少时间步长的那些序列可被认为具有缺失值。
在本教程中,您将了解如何使用 Keras 深度学习库处理 Python 中序列预测问题缺失值的数据。
完成本教程后,您将了解:
- 如何删除包含缺少时间步长的行。
- 如何标记丢失的时间步骤并强制网络了解其含义。
- 如何屏蔽缺失的时间步长并将其从模型中的计算中排除。
让我们开始吧。
线性代数的温和介绍 Steve Corey 的照片,保留一些权利。
概观
本节分为 3 部分;他们是:
- 回波序列预测问题
- 处理缺失的序列数据
- 学习缺少序列值
环境
本教程假定您已安装 Python SciPy 环境。您可以在此示例中使用 Python 2 或 3。
本教程假设您使用 TensorFlow(v1.1.0 +)或 Theano(v0.9 +)后端安装了 Keras(v2.0.4 +)。
本教程还假设您安装了 scikit-learn,Pandas,NumPy 和 Matplotlib。
如果您在设置 Python 环境时需要帮助,请参阅以下帖子:
回波序列预测问题
打印问题是一个人为的序列预测问题,其目标是在固定的先前时间步长处记住和预测观察,称为滞后观察。
例如,最简单的情况是预测从前一个时间步的观察结果,即打印它。例如:
Time 1: Input 45
Time 2: Input 23, Output 45
Time 3: Input 73, Output 23
...
问题是,我们如何处理时间步 1?
我们可以在 Python 中实现打印序列预测问题。
这涉及两个步骤:随机序列的生成和随机序列到有监督学习问题的转换。
生成随机序列
我们可以使用随机模块中的 random()函数生成 0 到 1 之间的随机值序列。
我们可以将它放在一个名为 generate_sequence()的函数中,该函数将为所需的时间步长生成一系列随机浮点值。
此功能如下所列。
# generate a sequence of random values
def generate_sequence(n_timesteps):
return [random() for _ in range(n_timesteps)]
框架作为监督学习
在使用神经网络时,必须将序列框定为监督学习问题。
这意味着序列需要分为输入和输出对。
该问题可以被构造为基于当前和先前时间步的函数做出预测。
或者更正式地说:
y(t) = f(X(t), X(t-1))
其中 y(t)是当前时间步长的期望输出,f()是我们寻求用神经网络逼近的函数,X(t)和 X(t-1)是当前和之前的观测值时间步长。
输出可以等于先前的观察值,例如,y(t)= X(t-1),但是它可以很容易地是 y(t)= X(t)。我们针对这个问题进行训练的模型并不知道真正的表述,必须学习这种关系。
这模拟了真实的序列预测问题,其中我们将模型指定为一组固定的顺序时间步长的函数,但我们不知道从过去的观察到期望的输出值的实际函数关系。
我们可以将这个打印问题的框架实现为 python 中的监督学习问题。
Pandas shift()函数可用于创建序列的移位版本,可用于表示先前时间步的观测值。这可以与原始序列连接以提供 X(t-1)和 X(t)输入值。
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
然后我们可以将 Pandas DataFrame 中的值作为输入序列(X),并使用第一列作为输出序列(y)。
# specify input and output data
X, y = values, values[:, 0]
综上所述,我们可以定义一个函数,它将时间步数作为参数,并返回名为 generate_data()的序列学习的 X,y 数据。
# generate data for the lstm
def generate_data(n_timesteps):
# generate sequence
sequence = generate_sequence(n_timesteps)
sequence = array(sequence)
# create lag
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
values = df.values
# specify input and output data
X, y = values, values[:, 0]
return X, y
序列问题演示
我们可以将 generate_sequence()和 generate_data()代码绑定到一个工作示例中。
下面列出了完整的示例。
from random import random
from numpy import array
from pandas import concat
from pandas import DataFrame
# generate a sequence of random values
def generate_sequence(n_timesteps):
return [random() for _ in range(n_timesteps)]
# generate data for the lstm
def generate_data(n_timesteps):
# generate sequence
sequence = generate_sequence(n_timesteps)
sequence = array(sequence)
# create lag
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
values = df.values
# specify input and output data
X, y = values, values[:, 0]
return X, y
# generate sequence
n_timesteps = 10
X, y = generate_data(n_timesteps)
# print sequence
for i in range(n_timesteps):
print(X[i], '=>', y[i])
运行此示例会生成一个序列,将其转换为监督表示,并打印每个 X,Y 对。
[ nan 0.18961404] => nan
[ 0.18961404 0.25956078] => 0.189614044109
[ 0.25956078 0.30322084] => 0.259560776929
[ 0.30322084 0.72581287] => 0.303220844801
[ 0.72581287 0.02916655] => 0.725812865047
[ 0.02916655 0.88711086] => 0.0291665472554
[ 0.88711086 0.34267107] => 0.88711086298
[ 0.34267107 0.3844453 ] => 0.342671068373
[ 0.3844453 0.89759621] => 0.384445299683
[ 0.89759621 0.95278264] => 0.897596208691
我们可以看到第一行有 NaN 值。
这是因为我们没有事先观察序列中的第一个值。我们必须用一些东西填补这个空间。
但我们无法使用 NaN 输入拟合模型。
处理缺失的序列数据
处理缺失的序列数据有两种主要方法。
它们将删除缺少数据的行,并使用其他值填充缺少的时间步。
有关处理缺失数据的更常用方法,请参阅帖子:
处理缺失序列数据的最佳方法取决于您的问题和您选择的网络配置。我建议探索每种方法,看看哪种方法效果最好。
删除缺失的序列数据
在我们打印前一个时间步骤中的观察的情况下,第一行数据不包含任何有用的信息。
也就是说,在上面的例子中,给定输入:
[ nan 0.18961404]
和输出:
nan
没有任何有意义的东西可以学习或预测。
这里最好的情况是删除这一行。
我们可以通过删除包含 NaN 值的所有行,在序列的制定过程中将其作为监督学习问题。具体来说,可以在将数据拆分为 X 和 y 分量之前调用 dropna()函数。
完整示例如下:
from random import random
from numpy import array
from pandas import concat
from pandas import DataFrame
# generate a sequence of random values
def generate_sequence(n_timesteps):
return [random() for _ in range(n_timesteps)]
# generate data for the lstm
def generate_data(n_timesteps):
# generate sequence
sequence = generate_sequence(n_timesteps)
sequence = array(sequence)
# create lag
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
# remove rows with missing values
df.dropna(inplace=True)
values = df.values
# specify input and output data
X, y = values, values[:, 0]
return X, y
# generate sequence
n_timesteps = 10
X, y = generate_data(n_timesteps)
# print sequence
for i in range(len(X)):
print(X[i], '=>', y[i])
运行该示例会导致 9 X,y 对而不是 10 对,并删除第一行。
[ 0.60619475 0.24408238] => 0.606194746194
[ 0.24408238 0.44873712] => 0.244082383195
[ 0.44873712 0.92939547] => 0.448737123424
[ 0.92939547 0.74481645] => 0.929395472523
[ 0.74481645 0.69891311] => 0.744816453809
[ 0.69891311 0.8420314 ] => 0.69891310578
[ 0.8420314 0.58627624] => 0.842031399202
[ 0.58627624 0.48125348] => 0.586276240292
[ 0.48125348 0.75057094] => 0.481253484036
替换缺失的序列数据
在打印问题被配置为在当前时间步骤打印观察的情况下,第一行将包含有意义的信息。
例如,我们可以将 y 的定义从值[:,0]更改为值[:,1]并重新运行演示以生成此问题的示例,如下所示:
[ nan 0.50513289] => 0.505132894821
[ 0.50513289 0.22879667] => 0.228796667421
[ 0.22879667 0.66980995] => 0.669809946421
[ 0.66980995 0.10445146] => 0.104451463568
[ 0.10445146 0.70642423] => 0.70642422679
[ 0.70642423 0.10198636] => 0.101986362328
[ 0.10198636 0.49648033] => 0.496480332278
[ 0.49648033 0.06201137] => 0.0620113728356
[ 0.06201137 0.40653087] => 0.406530870804
[ 0.40653087 0.63299264] => 0.632992635565
我们可以看到第一行给出了输入:
[ nan 0.50513289]
和输出:
0.505132894821
这可以从输入中学到。
问题是,我们仍然需要处理 NaN 值。
我们可以用输入中不会自然出现的特定值(例如-1)替换所有 NaN 值,而不是删除具有 NaN 值的行。为此,我们可以使用 fillna()Pandas 函数。
完整示例如下:
from random import random
from numpy import array
from pandas import concat
from pandas import DataFrame
# generate a sequence of random values
def generate_sequence(n_timesteps):
return [random() for _ in range(n_timesteps)]
# generate data for the lstm
def generate_data(n_timesteps):
# generate sequence
sequence = generate_sequence(n_timesteps)
sequence = array(sequence)
# create lag
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
# replace missing values with -1
df.fillna(-1, inplace=True)
values = df.values
# specify input and output data
X, y = values, values[:, 1]
return X, y
# generate sequence
n_timesteps = 10
X, y = generate_data(n_timesteps)
# print sequence
for i in range(len(X)):
print(X[i], '=>', y[i])
运行该示例,我们可以看到第一行的第一列中的 NaN 值被替换为-1 值。
[-1\. 0.94641256] => 0.946412559807
[ 0.94641256 0.11958645] => 0.119586451733
[ 0.11958645 0.50597771] => 0.505977714614
[ 0.50597771 0.92496641] => 0.924966407025
[ 0.92496641 0.15011979] => 0.150119790096
[ 0.15011979 0.69387197] => 0.693871974256
[ 0.69387197 0.9194518 ] => 0.919451802966
[ 0.9194518 0.78690337] => 0.786903370269
[ 0.78690337 0.17017999] => 0.170179993691
[ 0.17017999 0.82286572] => 0.822865722747
学习缺少序列值
在学习具有标记缺失值的序列预测问题时,有两个主要选项。
该问题可以按原样建模,我们可以鼓励模型了解特定值意味着“缺失”。或者,可以屏蔽特殊缺失值并从预测计算中明确排除。
我们将通过两个输入来看看这两个案例的人为“回应当前观察”问题。
学习缺失的价值观
我们可以为预测问题开发 LSTM。
输入由 2 个时间步长和 1 个特征定义。在第一隐藏层中定义具有 5 个存储器单元的小 LSTM,并且具有线性激活功能的单个输出层。
使用均方误差丢失函数和具有默认配置的高效 ADAM 优化算法,网络将适合。
# define model
model = Sequential()
model.add(LSTM(5, input_shape=(2, 1)))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
为了确保模型学习问题的广义解,即始终将输入作为输出返回(y(t)== X(t)),我们将在每个时期生成一个新的随机序列。该网络将适合 500 个时期,并且将在每个序列中的每个样本之后执行更新(batch_size = 1)。
# fit model
for i in range(500):
X, y = generate_data(n_timesteps)
model.fit(X, y, epochs=1, batch_size=1, verbose=2)
一旦拟合,将生成另一个随机序列,并将来自模型的预测与预期值进行比较。这将提供模型技能的具体概念。
# evaluate model on new data
X, y = generate_data(n_timesteps)
yhat = model.predict(X)
for i in range(len(X)):
print('Expected', y[i,0], 'Predicted', yhat[i,0])
将所有这些结合在一起,下面提供了完整的代码清单。
from random import random
from numpy import array
from pandas import concat
from pandas import DataFrame
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
# generate a sequence of random values
def generate_sequence(n_timesteps):
return [random() for _ in range(n_timesteps)]
# generate data for the lstm
def generate_data(n_timesteps):
# generate sequence
sequence = generate_sequence(n_timesteps)
sequence = array(sequence)
# create lag
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
# replace missing values with -1
df.fillna(-1, inplace=True)
values = df.values
# specify input and output data
X, y = values, values[:, 1]
# reshape
X = X.reshape(len(X), 2, 1)
y = y.reshape(len(y), 1)
return X, y
n_timesteps = 10
# define model
model = Sequential()
model.add(LSTM(5, input_shape=(2, 1)))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
# fit model
for i in range(500):
X, y = generate_data(n_timesteps)
model.fit(X, y, epochs=1, batch_size=1, verbose=2)
# evaluate model on new data
X, y = generate_data(n_timesteps)
yhat = model.predict(X)
for i in range(len(X)):
print('Expected', y[i,0], 'Predicted', yhat[i,0])
运行该示例将打印每个时期的损失,并在运行结束时比较一个序列的预期输出与预测输出。
回顾最终预测,我们可以看到网络已经了解了问题并预测了“足够好”的输出,即使存在缺失值。
...
Epoch 1/1
0s - loss: 1.5992e-04
Epoch 1/1
0s - loss: 1.3409e-04
Epoch 1/1
0s - loss: 1.1581e-04
Epoch 1/1
0s - loss: 2.6176e-04
Epoch 1/1
0s - loss: 8.8303e-05
Expected 0.390784174343 Predicted 0.394238
Expected 0.688580469278 Predicted 0.690463
Expected 0.347155799665 Predicted 0.329972
Expected 0.345075533266 Predicted 0.333037
Expected 0.456591840482 Predicted 0.450145
Expected 0.842125610156 Predicted 0.839923
Expected 0.354087132135 Predicted 0.342418
Expected 0.601406667694 Predicted 0.60228
Expected 0.368929815424 Predicted 0.351224
Expected 0.716420996314 Predicted 0.719275
您可以进一步尝试此示例,并将给定序列的 t-1 观察值的 50%标记为-1,并查看它如何影响模型的技能随时间的变化。
掩盖缺失的价值观
可以从网络中的所有计算中屏蔽标记的缺失输入值。
我们可以通过使用 Masking 层作为网络的第一层来实现。
定义层时,我们可以指定要屏蔽的输入中的哪个值。如果时间步长的所有要素都包含蒙版值,则整个时间步长将从计算中排除。
这为完全排除行并强制网络了解标记缺失值的影响提供了一个中间立场。
由于 Masking 层是网络中的第一个,因此必须指定输入的预期形状,如下所示:
model.add(Masking(mask_value=-1, input_shape=(2, 1)))
我们可以将所有这些结合起来并重新运行示例。完整的代码清单如下。
from random import random
from numpy import array
from pandas import concat
from pandas import DataFrame
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
from keras.layers import Masking
# generate a sequence of random values
def generate_sequence(n_timesteps):
return [random() for _ in range(n_timesteps)]
# generate data for the lstm
def generate_data(n_timesteps):
# generate sequence
sequence = generate_sequence(n_timesteps)
sequence = array(sequence)
# create lag
df = DataFrame(sequence)
df = concat([df.shift(1), df], axis=1)
# replace missing values with -1
df.fillna(-1, inplace=True)
values = df.values
# specify input and output data
X, y = values, values[:, 1]
# reshape
X = X.reshape(len(X), 2, 1)
y = y.reshape(len(y), 1)
return X, y
n_timesteps = 10
# define model
model = Sequential()
model.add(Masking(mask_value=-1, input_shape=(2, 1)))
model.add(LSTM(5))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
# fit model
for i in range(500):
X, y = generate_data(n_timesteps)
model.fit(X, y, epochs=1, batch_size=1, verbose=2)
# evaluate model on new data
X, y = generate_data(n_timesteps)
yhat = model.predict(X)
for i in range(len(X)):
print('Expected', y[i,0], 'Predicted', yhat[i,0])
同样,每个时期打印损失,并将预测与最终序列的预期值进行比较。
同样,预测看起来足够小到几位小数。
...
Epoch 1/1
0s - loss: 1.0252e-04
Epoch 1/1
0s - loss: 6.5545e-05
Epoch 1/1
0s - loss: 3.0831e-05
Epoch 1/1
0s - loss: 1.8548e-04
Epoch 1/1
0s - loss: 7.4286e-05
Expected 0.550889403319 Predicted 0.538004
Expected 0.24252028132 Predicted 0.243288
Expected 0.718869927574 Predicted 0.724669
Expected 0.355185878917 Predicted 0.347479
Expected 0.240554707978 Predicted 0.242719
Expected 0.769765554707 Predicted 0.776608
Expected 0.660782450416 Predicted 0.656321
Expected 0.692962017672 Predicted 0.694851
Expected 0.0485233839401 Predicted 0.0722362
Expected 0.35192019185 Predicted 0.339201
选择哪种方法?
这些一次性实验不足以评估在简单回波序列预测问题上最有效的方法。
他们提供的模板可以用于您自己的问题。
我鼓励您探索在序列预测问题中处理缺失值的 3 种不同方法。他们是:
- 删除缺少值的行。
- 标记并学习缺失值。
- 掩盖和学习没有遗漏的价值观。
尝试针对序列预测问题的每种方法,并对看起来效果最好的方法进行加倍研究。
摘要
如果序列具有可变长度,则通常在序列预测问题中具有缺失值。
在本教程中,您了解了如何使用 Keras 处理 Python 中序列预测问题中的缺失数据。
具体来说,你学到了:
- 如何删除包含缺失值的行。
- 如何标记缺失值并强制模型了解其含义。
- 如何屏蔽缺失值以将其从模型中的计算中排除。
您对处理丢失的序列数据有任何疑问吗? 在评论中提出您的问题,我会尽力回答。
如何建立预测大气每日污染的概率预测模型
空气污染的特点是地面臭氧浓度。
根据风速和温度等气象测量结果,可以预测明天地面臭氧是否会达到足够高的水平,以发布公共空气污染预警。
这是用于时间序列分类数据集的标准机器学习数据集的基础,简称为“_ 臭氧预测问题 _”。该数据集描述了休斯顿地区七年来的气象观测以及臭氧水平是否高于临界空气污染水平。
在本教程中,您将了解如何探索这些数据并开发概率预测模型,以预测德克萨斯州休斯顿的空气污染。
完成本教程后,您将了解:
- 如何加载和准备臭氧日标准机器学习预测性建模问题。
- 如何开发一个朴素的预测模型,并使用 Brier 技能分数评估预测。
- 如何使用决策树集合开发技巧模型,并通过成功模型的超参数调整进一步提高技能。
让我们开始吧。
如何建立预测空气每日污染的概率预测模型 照片由 paramita ,保留一些权利。
教程概述
本教程分为五个部分;他们是:
- 臭氧预测问题
- 加载和检查数据
- 朴素预测模型
- 集合树预测模型
- 调整梯度提升
臭氧预测问题
空气污染的特征在于地面臭氧的高度测量,通常被称为“不良臭氧”,以区别于高层大气中的臭氧。
臭氧预测问题是时间序列分类预测问题,其涉及预测第二天是否将是高空气每日污染(臭氧日)。气象组织可以利用臭氧日的预测来警告公众,使他们能够采取预防措施。
该数据集最初由 Kun Zhang 等人研究。他们在 2006 年的论文“预测偏差随机臭氧天数:分析和解决方案”,然后在他们的后续报告中再次提到“预测偏差随机臭氧天:分析,解决方案及其他 “。
这是一个具有挑战性的问题,因为高臭氧水平的物理机制是(或没有)被完全理解,这意味着预测不能像其他气象预报一样基于物理模拟,如温度和降雨量。
该数据集被用作开发预测模型的基础,该模型使用一系列可能与预测臭氧水平相关或可能不相关的变量,此外还有少数已知与所涉及的实际化学过程相关的变量。
然而,环境科学家普遍认为,目前从未探索过的大量其他特征在建立高度精确的臭氧预测模型中非常有用。然而,鲜为人知的是这些特征究竟是什么以及它们如何在臭氧形成中实际相互作用。 [...]今天的环境科学都没有知道如何使用它们。这为数据挖掘提供了绝佳的机会
- 预测偏差随机臭氧天数:分析和解决方案,2006 年。
在随后的一天预测高水平的地面臭氧是一个具有挑战性的问题,已知其具有随机性。这意味着预计会出现预测错误。因此,希望概率地对预测问题进行建模并预测臭氧日的概率,或者在前一天或几天给出观察结果。
该数据集包含七年的每日气象变量观测值(1998-2004 或 2,536 天)以及是否有臭氧日,在美国德克萨斯州的休斯顿,加尔维斯顿和布拉佐里亚地区进行。
每天共观察到 72 个变量,其中许多被认为与预测问题相关,其中 10 个已根据物理学确认为相关。
[...]这 72 个特征中只有大约 10 个特征已经被环境科学家证实是有用和相关的,并且关于其他 60 个特征的相关性,既没有经验也没有理论信息。然而,空气质量控制科学家长期以来一直在猜测这些特征中的一些可能是有用的,但是无法发展理论或使用模拟来证明其相关性。
- 预测偏差随机臭氧天数:分析和解决方案,2006 年。
有 24 个变量跟踪每小时风速,另外 24 个变量跟踪一天中每小时的温度。数据集的两个版本可用于度量的不同平均周期,特别是 1 小时和 8 小时。
似乎缺少并可能有用的是每天观察到的臭氧,而不是二氧化硫臭氧日/非臭氧日。参数模型中使用的其他度量也不可用。
有趣的是,基于“开发臭氧预测计划指南”,1999 年 EPA 指南中的描述,使用参数臭氧预测模型作为基线。该文件还描述了验证臭氧预报系统的标准方法。
总之,这是一个具有挑战性的预测问题,因为:
- 存在大量变量,其重要性通常是未知的。
- 输入变量及其相互关系可能会随时间而变化。
- 对于需要处理的许多变量缺少观察结果。
- 非臭氧日(非事件)远远超过臭氧日(事件),使得这些类别高度不平衡。
加载和检查数据
该数据集可从 UCI 机器学习库获得。
我们只会查看本教程中的 8 小时数据。下载“eighthr.data”并将其放在当前的工作目录中。
检查数据文件,我们可以看到不同尺度的观察结果。
1/1/1998,0.8,1.8,2.4,2.1,2,2.1,1.5,1.7,1.9,2.3,3.7,5.5,5.1,5.4,5.4,4.7,4.3,3.5,3.5,2.9,3.2,3.2,2.8,2.6,5.5,3.1,5.2,6.1,6.1,6.1,6.1,5.6,5.2,5.4,7.2,10.6,14.5,17.2,18.3,18.9,19.1,18.9,18.3,17.3,16.8,16.1,15.4,14.9,14.8,15,19.1,12.5,6.7,0.11,3.83,0.14,1612,-2.3,0.3,7.18,0.12,3178.5,-15.5,0.15,10.67,-1.56,5795,-12.1,17.9,10330,-55,0,0.
1/2/1998,2.8,3.2,3.3,2.7,3.3,3.2,2.9,2.8,3.1,3.4,4.2,4.5,4.5,4.3,5.5,5.1,3.8,3,2.6,3,2.2,2.3,2.5,2.8,5.5,3.4,15.1,15.3,15.6,15.6,15.9,16.2,16.2,16.2,16.6,17.8,19.4,20.6,21.2,21.8,22.4,22.1,20.8,19.1,18.1,17.2,16.5,16.1,16,16.2,22.4,17.8,9,0.25,-0.41,9.53,1594.5,-2.2,0.96,8.24,7.3,3172,-14.5,0.48,8.39,3.84,5805,14.05,29,10275,-55,0,0.
1/3/1998,2.9,2.8,2.6,2.1,2.2,2.5,2.5,2.7,2.2,2.5,3.1,4,4.4,4.6,5.6,5.4,5.2,4.4,3.5,2.7,2.9,3.9,4.1,4.6,5.6,3.5,16.6,16.7,16.7,16.8,16.8,16.8,16.9,16.9,17.1,17.6,19.1,21.3,21.8,22,22.1,22.2,21.3,19.8,18.6,18,18,18.2,18.3,18.4,22.2,18.7,9,0.56,0.89,10.17,1568.5,0.9,0.54,3.8,4.42,3160,-15.9,0.6,6.94,9.8,5790,17.9,41.3,10235,-40,0,0.
...
浏览文件,例如到 2003 年初,我们可以看到缺少的观察值标有“?”值。
...
12/29/2002,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,11.7,0.09,5.59,3.79,1578,5.7,0.04,1.8,4.8,3181.5,-13,0.02,0.38,2.78,5835,-31.1,18.9,10250,-25,0.03,0.
12/30/2002,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,10.3,0.43,3.88,9.21,1525.5,1.8,0.87,9.17,9.96,3123,-11.3,0.03,11.23,10.79,5780,17,30.2,10175,-75,1.68,0.
12/31/2002,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,8.5,0.96,6.05,11.18,1433,-0.85,0.91,7.02,6.63,3014,-16.2,0.05,15.77,24.38,5625,31.15,48.75,10075,-100,0.05,0.
1/1/2003,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,7.2,5.7,4.5,4,3.6,3.3,3.1,3.2,6.7,11.1,13.8,15.8,17.2,18.6,20,21.1,21.5,20.4,19.1,17.8,17.4,16.9,16.6,14.9,21.5,12.6,6.4,0.6,12.91,-10.17,1421.5,1.95,0.55,11.97,-7.78,3006.5,-14.1,0.44,20.42,-13.31,5640,2.9,30.5,10095,35,0,0.
...
首先,我们可以使用 read_csv()函数将数据作为 Pandas DataFrame 加载。没有数据头,我们可以解析第一列中的日期并将它们用作索引;下面列出了完整的示例。
# load and summarize
from pandas import read_csv
from matplotlib import pyplot
# load dataset
data = read_csv('eighthr.data', header=None, index_col=0, parse_dates=True, squeeze=True)
print(data.shape)
# summarize class counts
counts = data.groupby(73).size()
for i in range(len(counts)):
percent = counts[i] / data.shape[0] * 100
print('Class=%d, total=%d, percentage=%.3f' % (i, counts[i], percent))
运行该示例确认有 2,534 天的数据和 73 个变量。
我们还可以看到阶级不平衡的性质,其中 93%以上的日子是非臭氧日,约 6%是臭氧日。
(2534, 73)
Class=0, total=2374, percentage=93.686
Class=1, total=160, percentage=6.314
我们还可以在七年内创建输出变量的线图,以了解臭氧天数是否发生在一年中的任何特定时间。
# load and plot output variable
from pandas import read_csv
from matplotlib import pyplot
# load dataset
data = read_csv('eighthr.data', header=None, index_col=0, parse_dates=True, squeeze=True)
# plot the output variable
pyplot.plot(data.index, data.values[:,-1])
pyplot.show()
运行该示例将创建七年内输出变量的线图。
我们可以看到,每年中期都有臭氧天集群:北半球夏季或温暖的月份。
输出变量的线图超过 7 年
通过简要回顾一下观察结果,我们可以得到一些关于如何准备数据的想法:
- 缺少数据需要处理。
- 最简单的框架是根据今天的观察结果预测明天的臭氧日。
- 温度可能与季节或一年中的时间相关,可能是一个有用的预测指标。
- 数据变量可能需要缩放(标准化),甚至可能需要标准化,具体取决于所选的算法。
- 预测概率将提供比预测类值更多的细微差别。
- 也许我们可以使用五年(约 72%)来训练模型并在剩余的两年内测试它(约 28%)
我们可以执行一些最小的数据准备。
下面的示例加载数据集,用 0.0 替换缺失的观测值,将数据构建为监督学习问题(明天根据今天的观察结果预测臭氧),并根据大量天数将数据分成训练和测试集。两年。
您可以探索替换缺失值的替代方法,例如输入平均值。此外,2004 年是一个闰年,因此将数据分成训练和测试集并不是一个干净的 5 - 2 年分裂,但是对于本教程来说足够接近。
# load and prepare
from pandas import read_csv
from matplotlib import pyplot
from numpy import array
from numpy import hstack
from numpy import savetxt
# load dataset
data = read_csv('eighthr.data', header=None, index_col=0, parse_dates=True, squeeze=True)
values = data.values
# replace missing observations with 0
values[values=='?'] = 0.0
# frame as supervised learning
supervised = list()
for i in range(len(values) - 1):
X, y = values[i, :-1], values[i + 1, -1]
row = hstack((X,y))
supervised.append(row)
supervised = array(supervised)
# split into train-test
split = 365 * 2
train, test = supervised[:-split,:], supervised[-split:,:]
train, test = train.astype('float32'), test.astype('float32')
print(train.shape, test.shape)
# save prepared datasets
savetxt('train.csv', train, delimiter=',')
savetxt('test.csv', test, delimiter=',')
运行该示例将训练和测试集保存为 CSV 文件,并汇总两个数据集的形状。
(1803, 73) (730, 73)
朴素预测模型
一个朴素的模型可以预测每天臭氧日的概率。
这是一种朴素的方法,因为它不使用除事件基本速率之外的任何信息。在气象预报的验证中,这被称为气候预报。
我们可以从训练数据集中估计臭氧日的概率,如下所示。
# load datasets
train = loadtxt('train.csv', delimiter=',')
test = loadtxt('test.csv', delimiter=',')
# estimate naive probabilistic forecast
naive = sum(train[:,-1]) / train.shape[0]
然后,我们可以预测测试数据集中每天臭氧日的初始概率。
# forecast the test dataset
yhat = [naive for _ in range(len(test))]
一旦我们有了预测,我们就可以对其进行评估。
评估概率预测的有用措施是 Brier 评分。该分数可以被认为是来自预期概率(0%或 1%)的预测概率(例如 5%)的均方误差。它是测试数据集中每天发生的错误的平均值。
我们感兴趣的是最小化 Brier 分数,较小的值更好,例如更小的错误。
我们可以使用 scikit-learn 库中的 brier_score_loss()函数来评估预测的 Brier 分数。
# evaluate forecast
testy = test[:, -1]
bs = brier_score_loss(testy, yhat)
print('Brier Score: %.6f' % bs)
对于熟练的模型,它必须具有比朴素预测的分数更好的分数。
我们可以通过计算基于朴素预测标准化 Brier 分数(BS)的 Brier 技能分数(BSS)来证明这一点。
我们预计朴素预报的计算 BSS 将为 0.0。展望未来,我们有兴趣最大化此分数,例如较大的 BSS 分数更好。
# calculate brier skill score
bs_ref = bs
bss = (bs - bs_ref) / (0 - bs_ref)
print('Brier Skill Score: %.6f' % bss)
下面列出了幼稚预测的完整示例。
# naive prediction method
from sklearn.metrics import brier_score_loss
from numpy import loadtxt
# load datasets
train = loadtxt('train.csv', delimiter=',')
test = loadtxt('test.csv', delimiter=',')
# estimate naive probabilistic forecast
naive = sum(train[:,-1]) / train.shape[0]
print(naive)
# forecast the test dataset
yhat = [naive for _ in range(len(test))]
# evaluate forecast
testy = test[:, -1]
bs = brier_score_loss(testy, yhat)
print('Brier Score: %.6f' % bs)
# calculate brier skill score
bs_ref = bs
bss = (bs - bs_ref) / (0 - bs_ref)
print('Brier Skill Score: %.6f' % bss)
运行这个例子,我们可以看到臭氧日的朴素概率甚至约为 7.2%。
使用基本费率作为预测会导致 Brier 技能为 0.039,预期 Brier 技能得分为 0.0(忽略该符号)。
0.07265668330560178
Brier Score: 0.039232
Brier Skill Score: -0.000000
我们现在准备探索一些机器学习方法,看看我们是否可以为此预测添加技能。
请注意,原始论文使用精确度和直接召回来评估方法的技巧,这是一种用于方法之间直接比较的令人惊讶的方法。
也许您可以探索的替代措施是 ROC 曲线下的面积(ROC AUC)。绘制最终模型的 ROC 曲线将允许模型的操作者选择阈值,该阈值提供真正的正(命中)和误报(误报)速率之间的期望平衡水平。
集合树预测模型
原始论文报告了袋装决策树的一些成功。
尽管我们对归纳学习器的选择是非穷尽的,但本文已经表明,归纳学习可以作为臭氧水平预测的一种选择方法,而基于集合的概率树提供比现有方法更好的预测(更高的召回率和精确度)。
- 预测偏差随机臭氧天数:分析和解决方案,2006 年。
出于以下几个原因,这并不奇怪:
- 袋装决策树不需要任何数据缩放。
- Bagged 决策树自动执行一种功能部分,忽略不相关的功能。
- 袋装决策树预测合理校准的概率(例如,与 SVM 不同)。
这表明在测试机器学习算法时,这是一个很好的起点。
我们可以通过现场检查 scikit-learn 库中标准集合树方法样本的表现来快速入门,其默认配置和树数设置为 100。
具体来说,方法:
- 袋装决策树(BaggingClassifier)
- 额外树木(ExtraTreesClassifier)
- 随机梯度提升(GradientBoostingClassifier)
- 随机森林(RandomForestClassifier)
首先,我们必须将训练和测试数据集分成输入(X)和输出(y)组件,以便我们可以拟合 sklearn 模型。
# load datasets
train = loadtxt('train.csv', delimiter=',')
test = loadtxt('test.csv', delimiter=',')
# split into inputs/outputs
trainX, trainy, testX, testy = train[:,:-1],train[:,-1],test[:,:-1],test[:,-1]
我们还需要 Brier 分数进行朴素的预测,以便我们能够正确计算新模型的 Brier 技能分数。
# estimate naive probabilistic forecast
naive = sum(train[:,-1]) / train.shape[0]
# forecast the test dataset
yhat = [naive for _ in range(len(test))]
# calculate naive bs
bs_ref = brier_score_loss(testy, yhat)
我们可以一般地评估单个 scikit-learn 模型的技能。
下面定义名为evaluate_once()的函数,该函数适合并评估给定的已定义和配置的 scikit-learn 模型并返回 Brier 技能分数(BSS)。
# evaluate a sklearn model
def evaluate_once(bs_ref, template, trainX, trainy, testX, testy):
# fit model
model = clone(template)
model.fit(trainX, trainy)
# predict probabilities for 0 and 1
probs = model.predict_proba(testX)
# keep the probabilities for class=1 only
yhat = probs[:, 1]
# calculate brier score
bs = brier_score_loss(testy, yhat)
# calculate brier skill score
bss = (bs - bs_ref) / (0 - bs_ref)
return bss
集合树是一种随机机器学习方法。
这意味着当相同模型的相同配置在相同数据上训练时,它们将做出不同的预测。为了纠正这个问题,我们可以多次评估给定模型,例如 10 次,并计算每次运行的平均技能。
下面的函数将评估给定模型 10 次,打印平均 BSS 分数,并返回分数的总体用于分析。
# evaluate an sklearn model n times
def evaluate(bs_ref, model, trainX, trainy, testX, testy, n=10):
scores = [evaluate_once(bs_ref, model, trainX, trainy, testX, testy) for _ in range(n)]
print('>%s, bss=%.6f' % (type(model), mean(scores)))
return scores
我们现在准备评估一套集合决策树算法。
下面列出了完整的示例。
# evaluate ensemble tree methods
from numpy import loadtxt
from numpy import mean
from matplotlib import pyplot
from sklearn.base import clone
from sklearn.metrics import brier_score_loss
from sklearn.ensemble import BaggingClassifier
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.ensemble import RandomForestClassifier
# evaluate a sklearn model
def evaluate_once(bs_ref, template, trainX, trainy, testX, testy):
# fit model
model = clone(template)
model.fit(trainX, trainy)
# predict probabilities for 0 and 1
probs = model.predict_proba(testX)
# keep the probabilities for class=1 only
yhat = probs[:, 1]
# calculate brier score
bs = brier_score_loss(testy, yhat)
# calculate brier skill score
bss = (bs - bs_ref) / (0 - bs_ref)
return bss
# evaluate an sklearn model n times
def evaluate(bs_ref, model, trainX, trainy, testX, testy, n=10):
scores = [evaluate_once(bs_ref, model, trainX, trainy, testX, testy) for _ in range(n)]
print('>%s, bss=%.6f' % (type(model), mean(scores)))
return scores
# load datasets
train = loadtxt('train.csv', delimiter=',')
test = loadtxt('test.csv', delimiter=',')
# split into inputs/outputs
trainX, trainy, testX, testy = train[:,:-1],train[:,-1],test[:,:-1],test[:,-1]
# estimate naive probabilistic forecast
naive = sum(train[:,-1]) / train.shape[0]
# forecast the test dataset
yhat = [naive for _ in range(len(test))]
# calculate naive bs
bs_ref = brier_score_loss(testy, yhat)
# evaluate a suite of ensemble tree methods
scores, names = list(), list()
n_trees=100
# bagging
model = BaggingClassifier(n_estimators=n_trees)
avg_bss = evaluate(bs_ref, model, trainX, trainy, testX, testy)
scores.append(avg_bss)
names.append('bagging')
# extra
model = ExtraTreesClassifier(n_estimators=n_trees)
avg_bss = evaluate(bs_ref, model, trainX, trainy, testX, testy)
scores.append(avg_bss)
names.append('extra')
# gbm
model = GradientBoostingClassifier(n_estimators=n_trees)
avg_bss = evaluate(bs_ref, model, trainX, trainy, testX, testy)
scores.append(avg_bss)
names.append('gbm')
# rf
model = RandomForestClassifier(n_estimators=n_trees)
avg_bss = evaluate(bs_ref, model, trainX, trainy, testX, testy)
scores.append(avg_bss)
names.append('rf')
# plot results
pyplot.boxplot(scores, labels=names)
pyplot.show()
运行该示例总结了 10 次运行中平均每个模型的平均 BSS。
鉴于算法的随机性,您的具体结果可能会有所不同,但趋势应该相同。
从平均 BSS 分数来看,它表明额外的树木,随机梯度增强和随机森林模型是最熟练的。
><class 'sklearn.ensemble.bagging.BaggingClassifier'>, bss=0.069762
><class 'sklearn.ensemble.forest.ExtraTreesClassifier'>, bss=0.103291
><class 'sklearn.ensemble.gradient_boosting.GradientBoostingClassifier'>, bss=0.119803
><class 'sklearn.ensemble.forest.RandomForestClassifier'>, bss=0.102736
绘制每个模型的分数的盒子和须状图。
他们所有跑步的所有模型都显示出朴素预测的技巧(正分数),这是非常令人鼓舞的。
额外树木,随机梯度提升和随机森林的 BSS 分数的分布看起来都令人鼓舞。
测试集上的集合决策树 BSS 分数的框和胡须图
调整梯度提升
鉴于随机梯度增强看起来很有希望,有必要探讨是否可以通过某些参数调整进一步提升模型的表现。
有许多参数可以调整模型,但调整模型的一些好的启发式方法包括:
- 降低学习率(
learning_rate),同时增加决策树的数量(n_estimators)。 - 增加决策树的最大深度(
max_depth),同时减少可用于拟合树的样本数(_ 样本 _)。
我们可以根据这些原则检查一些参数,而不是网格搜索值。如果您有时间和计算资源,可以自己探索这些参数的网格搜索。
我们将比较 GBM 模型的四种配置:
- 基线:如前一节测试的那样(
learning_rate= 0.1,n_estimators= 100,_ 子样本 _ = 1.0,max_depth= 3) - lr ,学习率较低且树木较多(
learning_rate= 0.01,n_estimators= 500,_ 子样本 _ = 1.0,max_depth= 3) - 深度,最大树深度增加,数据集采样量减少(
learning_rate= 0.1,n_estimators= 100,_ 子样本 _ = 0.7,max_depth=) - 所有,所有修改。
下面列出了完整的示例。
# tune the gbm configuration
from numpy import loadtxt
from numpy import mean
from matplotlib import pyplot
from sklearn.base import clone
from sklearn.metrics import brier_score_loss
from sklearn.ensemble import BaggingClassifier
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.ensemble import RandomForestClassifier
# evaluate a sklearn model
def evaluate_once(bs_ref, template, trainX, trainy, testX, testy):
# fit model
model = clone(template)
model.fit(trainX, trainy)
# predict probabilities for 0 and 1
probs = model.predict_proba(testX)
# keep the probabilities for class=1 only
yhat = probs[:, 1]
# calculate brier score
bs = brier_score_loss(testy, yhat)
# calculate brier skill score
bss = (bs - bs_ref) / (0 - bs_ref)
return bss
# evaluate an sklearn model n times
def evaluate(bs_ref, model, trainX, trainy, testX, testy, n=10):
scores = [evaluate_once(bs_ref, model, trainX, trainy, testX, testy) for _ in range(n)]
print('>%s, bss=%.6f' % (type(model), mean(scores)))
return scores
# load datasets
train = loadtxt('train.csv', delimiter=',')
test = loadtxt('test.csv', delimiter=',')
# split into inputs/outputs
trainX, trainy, testX, testy = train[:,:-1],train[:,-1],test[:,:-1],test[:,-1]
# estimate naive probabilistic forecast
naive = sum(train[:,-1]) / train.shape[0]
# forecast the test dataset
yhat = [naive for _ in range(len(test))]
# calculate naive bs
bs_ref = brier_score_loss(testy, yhat)
# evaluate a suite of ensemble tree methods
scores, names = list(), list()
# base
model = GradientBoostingClassifier(learning_rate=0.1, n_estimators=100, subsample=1.0, max_depth=3)
avg_bss = evaluate(bs_ref, model, trainX, trainy, testX, testy)
scores.append(avg_bss)
names.append('base')
# learning rate
model = GradientBoostingClassifier(learning_rate=0.01, n_estimators=500, subsample=1.0, max_depth=3)
avg_bss = evaluate(bs_ref, model, trainX, trainy, testX, testy)
scores.append(avg_bss)
names.append('lr')
# depth
model = GradientBoostingClassifier(learning_rate=0.1, n_estimators=100, subsample=0.7, max_depth=7)
avg_bss = evaluate(bs_ref, model, trainX, trainy, testX, testy)
scores.append(avg_bss)
names.append('depth')
# all
model = GradientBoostingClassifier(learning_rate=0.01, n_estimators=500, subsample=0.7, max_depth=7)
avg_bss = evaluate(bs_ref, model, trainX, trainy, testX, testy)
scores.append(avg_bss)
names.append('all')
# plot results
pyplot.boxplot(scores, labels=names)
pyplot.show()
运行该示例为每个配置打印 10 个运行的平均每个模型的 BSS。
结果表明,单独学习率和树木数量的变化引起了对默认配置的一些提升。
结果还表明,包含每个变化的“所有”配置导致最佳平均 BSS。
><class 'sklearn.ensemble.gradient_boosting.GradientBoostingClassifier'>, bss=0.119972
><class 'sklearn.ensemble.gradient_boosting.GradientBoostingClassifier'>, bss=0.145596
><class 'sklearn.ensemble.gradient_boosting.GradientBoostingClassifier'>, bss=0.095871
><class 'sklearn.ensemble.gradient_boosting.GradientBoostingClassifier'>, bss=0.192175
创建来自每个配置的 BSS 分数的框和胡须图。我们可以看到包含所有更改的配置明显优于基线模型和其他配置组合。
也许通过对模型进行微调的参数可以获得进一步的收益。
调谐 GBM 模型的框和胡须图显示测试集上的 BSS 分数
有必要掌握论文中描述的参数模型以及使用它所需的数据,以便将其技能与最终模型的技能进行比较。
扩展
本节列出了一些扩展您可能希望探索的教程的想法。
- 探索使用前几天观察结果的模型框架。
- 探索使用 ROC 曲线图和 ROC AUC 测量的模型评估。
- 网格搜索梯度提升模型参数,并可能探索其他实现,如 XGBoost。
如果你探索任何这些扩展,我很想知道。
进一步阅读
如果您希望深入了解,本节将提供有关该主题的更多资源。
- 臭氧水平检测数据集,UCI 机器学习库。
- 预测偏差随机臭氧天数:分析和解决方案,2006 年。
- 预测有偏差的随机臭氧天数:分析,解决方案以及,2008 年。
- CAWCR 验证页面
- 维基百科上的接收器操作特性
摘要
在本教程中,您了解了如何开发概率预测模型来预测德克萨斯州休斯顿的空气污染。
具体来说,你学到了:
- 如何加载和准备臭氧日标准机器学习预测性建模问题。
- 如何开发一个朴素的预测模型,并使用 Brier 技能分数评估预测。
- 如何使用决策树集合开发技巧模型,并通过成功模型的超参数调整进一步提高技能。
你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。
如何开发一种熟练的机器学习时间序列预测模型
原文:
machinelearningmastery.com/how-to-develop-a-skilful-time-series-forecasting-model/
您交给数据并告知他们开发预测模型。
_ 你做什么的?_
这是一种常见的情况;远比大多数人想象的要普遍。
- 也许您会收到一个 CSV 文件。
- 也许您可以访问数据库。
- 也许你正在开始比赛。
问题可以合理地定义:
- 您拥有或可以访问历史时间序列数据。
- 您知道或可以找出需要预测的内容。
- 您知道或者可以了解在评估候选模型时最重要的是什么。
那么你如何解决这个问题呢?
除非你经历过这场审判,否则你可能会挣扎。
- 你可能很难,因为你是机器学习和时间序列领域的新手。
- 即使您拥有机器学习经验,也可能会因为时间序列数据不同而挣扎。
- 即使您具有时间序列预测背景,您也可能会遇到困难,因为机器学习方法可能优于您的数据的传统方法。
在所有这些情况下,您将从仔细和系统地解决问题中受益。
在这篇文章中,我想给你一个特定的,可操作的程序,你可以用它来处理你的时间序列预测问题。
让我们开始吧。
如何开发熟练的时间序列预测模型 照片由制作肯尼亚,保留一些权利。
流程概述
这个过程的目标是尽可能快地获得“_ 足够好的 _”预测模型。
这个过程可能会或可能不会提供最好的模型,但它会提供一个好的模型:一个比基线预测更好的模型,如果存在这样的模型。
通常,此过程将提供的模型占问题可达到的 80%到 90%。
这个过程很快。因此,它专注于自动化。基于仔细分析,搜索超参数而不是指定超参数。我们鼓励您并行测试模型套件,快速了解哪些有效,哪些无效。
尽管如此,这个过程非常灵活,如果您有时间和资源,您可以在给定的步骤中循环或尽可能深入。
这个过程分为四个部分;他们是:
- 定义问题
- 设计测试线束
- 测试模型
- 完成模型
您会注意到该过程不同于预测性建模问题的经典线性工作。这是因为它旨在快速获得工作预测模型,然后放慢速度,看看是否可以获得更好的模型。
您处理新的时间序列预测问题的过程是什么? 在评论中分享。
如何使用此过程
最大的错误是跳过步骤。
例如,几乎所有初学者所犯的错误就是直接进行建模,而没有深入了解正在解决的问题或如何稳健地评估候选解决方案。这几乎总会导致大量的浪费时间。
慢下来,按照流程,完成每一步。
我建议为每个可以随时重新运行的实验提供单独的代码。
这很重要,以便您在发现错误,修复代码和重新运行实验时可以回退。您正在运行实验并快速迭代,但如果您是草率的,那么您不能相信任何结果。在设计用于评估候选模型的测试工具时,这一点尤其重要。
让我们仔细看看这个过程的每一步。
1.定义问题
定义您的时间序列问题。
在每个主题中要考虑和激发问题的一些主题如下:
- 输入与输出
- 预测的输入和输出是什么?
- 内生与外生
- 什么是内生和外生变量?
- 非结构化与结构化
- 时间序列变量是非结构化的还是结构化的?
- 回归与分类
- 您正在研究回归或分类预测性建模问题吗?
- 有哪些方法来构建时间序列预测问题?
- 单变量与多变量
- 您是在处理单变量或多变量时间序列问题吗?
- 单步与多步
- 您需要单步骤还是多步骤预测?
- 静态与动态
- 您需要静态或动态更新的模型吗?
即使您必须估计或猜测,也要回答每个问题。
一些有用的工具可以帮助您获得答案:
- 数据可视化(例如线图等)。
- 统计分析(例如 ACF / PACF 图等)。
- 领域专家。
- 项目利益相关
在了解更多信息后,请更新这些问题的答案。
2.设计测试线束
设计可用于评估候选模型的测试工具。
这包括用于估计模型技能的方法和用于评估预测的度量。
如果您正在寻找想法,下面是一个常见的时间序列预测模型评估方案:
- 将数据集拆分为训练和测试集。
- 在训练数据集上拟合候选方法。
- 直接对测试集做出预测或使用前向验证。
- 计算将预测与预期值进行比较的指标。
测试工具必须坚固耐用,您必须完全信任它提供的结果。
一个重要的考虑因素是确保用于数据准备的任何系数仅从训练数据集中估算,然后应用于测试集。这可能包括数据标准化的平均值和标准差。
3.测试模型
使用您的测试工具测试许多模型。
我建议您仔细设计实验,以测试标准模型的一套配置并让它们运行。每个实验都可以将结果记录到文件中,以便您可以快速发现每次运行中最前三到五个最熟练的配置。
您可以设计实验的一些常见方法类别包括:
- 基线。
- 持久性(网格搜索持久的滞后观察)
- 滚动移动平均线。
- ...
- 自回归。
- ARMA 用于固定数据。
- ARIMA 用于趋势数据。
- SARIMA 提供季节性数据。
- ...
- 指数平滑。
- 简单的平滑
- Holt Winters 平滑
- ...
- 线性机器学习。
- 线性回归
- 岭回归
- 套索回归
- 弹性网络回归
- ...。
- 非线性机器学习。
- k-最近邻居
- 分类和回归树
- 支持向量回归
- ...
- 合奏机器学习。
- 套袋
- 推进
- 随机森林
- 梯度提升
- ...
- 深度学习。
- MLP
- CNN
- LSTM
- 杂种
- ...
此列表基于单变量时间序列预测问题,但您可以根据问题的具体情况对其进行调整,例如:使用 VAR / VARMA /等。在多变量时间序列预测的情况下。
根据您的需要,插入更多您最喜欢的经典时间序列预测方法和机器学习方法。
这里的顺序很重要,其结构越来越复杂,从古典到现代的方法。早期的方法很简单,快速提供良好的结果;后来的方法更慢,更复杂,但也有更高的标准,以明确熟练。
由此产生的模型技能可用于棘轮。例如,最佳持久性配置的技能提供了所有其他模型必须超越的基线技能。如果自回归模型比持久性更好,那么它就会成为一个优秀的新级别,以便将方法视为技巧。
理想情况下,您希望在进入下一个级别之前耗尽每个级别。例如。充分利用自回归方法,并在继续使用指数平滑方法之前,将结果用作新基线来定义“熟练”。
我最后深入学习,因为通常神经网络在时间序列预测方面很差,但在这方面仍有很大的改进和实验空间。
您拥有的时间和资源越多,您可以评估的配置就越多。
例如,有了更多的时间和资源,您可以:
- 在已知已经表现良好的配置周围以更精细的分辨率搜索模型配置。
- 搜索更多模型超参数配置。
- 使用分析在要搜索的模型超参数上设置更好的边界。
- 使用领域知识更好地准备数据或设计输入功能。
- 探索不同的潜在更复杂的方法。
- 探索表现良好的基础模型的集合。
我还鼓励您将数据准备方案作为模型运行的超参数包括在内。
一些方法将执行一些基本数据准备,例如 ARIMA 中的差分,然而,通常不清楚究竟需要什么数据准备方案或方案组合来最佳地将数据集呈现给建模算法。而不是猜测,网格搜索并根据实际结果决定。
一些要考虑的数据准备方案包括:
- 差异化以消除趋势。
- 季节性差异以消除季节性。
- 标准化为中心。
- 标准化为重新缩放。
- 电源转换使正常。
如此多的搜索可能会很慢。
加快模型评估的一些想法包括:
- 通过云硬件(例如 Amazon EC2)并行使用多台计算机。
- 减小训练或测试数据集的大小,以使评估过程更快。
- 如果以后有时间,请使用更粗糙的超参数网格并圈回。
- 也许不要为前进验证中的每个步骤重新设计模型。
4.完成模型
在上一个时间步骤结束时,您知道您的时间序列是否可预测。
如果它是可预测的,您将拥有一个熟悉该问题的前 5 到 10 个候选模型的列表。
您可以选择一个或多个模型并完成它们。这包括在所有可用的历史数据(训练和测试)上训练新的最终模型。
该模型已准备就绪;例如:
- 预测未来。
- 将模型保存到文件以供以后用于做出预测。
- 将模型合并到用于做出预测的软件中。
如果您有时间,可以随时回到上一步,看看是否可以进一步改进最终模型。
如果数据随时间显着变化,则可能需要定期进行此操作。
进一步阅读
如果您希望深入了解,本节将提供有关该主题的更多资源。
摘要
在这篇文章中,您发现了一个简单的四步过程,您可以使用该过程快速发现针对时间序列预测问题的熟练预测模型。
你觉得这个过程有用吗? 请在下面告诉我。
你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。