We can tie all of these pieces together and fit an LSTM network to the multi-step time series forecasting problem.
我们可以将所有这些部分组合在一起,并将LSTM网络应用于多步时间序列预测问题。
The complete code listing is provided below.
下面提供了完整的代码清单。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
|
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
from
matplotlib
import
pyplot
from
numpy
import
array
# date-time parsing function for loading the dataset
def
parser
(
x
)
:
return
datetime
.
strptime
(
'190'
+
x
,
'%Y-%m'
)
# convert time series into supervised learning problem
def
series_to_supervised
(
data
,
n_in
=
1
,
n_out
=
1
,
dropnan
=
True
)
:
n_vars
=
1
if
type
(
data
)
is
list
else
data
.
shape
[
1
]
df
=
DataFrame
(
data
)
cols
,
names
=
list
(
)
,
list
(
)
# input sequence (t-n, ... t-1)
for
i
in
range
(
n_in
,
0
,
-
1
)
:
cols
.
append
(
df
.
shift
(
i
)
)
names
+=
[
(
'var%d(t-%d)'
%
(
j
+
1
,
i
)
)
for
j
in
range
(
n_vars
)
]
# forecast sequence (t, t+1, ... t+n)
for
i
in
range
(
0
,
n_out
)
:
cols
.
append
(
df
.
shift
(
-
i
)
)
if
i
==
0
:
names
+=
[
(
'var%d(t)'
%
(
j
+
1
)
)
for
j
in
range
(
n_vars
)
]
else
:
names
+=
[
(
'var%d(t+%d)'
%
(
j
+
1
,
i
)
)
for
j
in
range
(
n_vars
)
]
# put it all together
agg
=
concat
(
cols
,
axis
=
1
)
agg
.
columns
=
names
# drop rows with NaN values
if
dropnan
:
agg
.
dropna
(
inplace
=
True
)
return
agg
# 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
)
# transform series into train and test sets for supervised learning
def
prepare_data
(
series
,
n_test
,
n_lag
,
n_seq
)
:
# extract raw values
raw_values
=
series
.
values
# transform data to be stationary
diff_series
=
difference
(
raw_values
,
1
)
diff_values
=
diff_series
.
values
diff_values
=
diff_values
.
reshape
(
len
(
diff_values
)
,
1
)
# rescale values to -1, 1
scaler
=
MinMaxScaler
(
feature_range
=
(
-
1
,
1
)
)
scaled_values
=
scaler
.
fit_transform
(
diff_values
)
scaled_values
=
scaled_values
.
reshape
(
len
(
scaled_values
)
,
1
)
# transform into supervised learning problem X, y
supervised
=
series_to_supervised
(
scaled_values
,
n_lag
,
n_seq
)
supervised_values
=
supervised
.
values
# split into train and test sets
train
,
test
=
supervised_values
[
0
:
-
n_test
]
,
supervised_values
[
-
n_test
:
]
return
scaler
,
train
,
test
# fit an LSTM network to training data
def
fit_lstm
(
train
,
n_lag
,
n_seq
,
n_batch
,
nb_epoch
,
n_neurons
)
:
# reshape training into [samples, timesteps, features]
X
,
y
=
train
[
:
,
0
:
n_lag
]
,
train
[
:
,
n_lag
:
]
X
=
X
.
reshape
(
X
.
shape
[
0
]
,
1
,
X
.
shape
[
1
]
)
# design network
model
=
Sequential
(
)
model
.
add
(
LSTM
(
n_neurons
,
batch_input_shape
=
(
n_batch
,
X
.
shape
[
1
]
,
X
.
shape
[
2
]
)
,
stateful
=
True
)
)
model
.
add
(
Dense
(
y
.
shape
[
1
]
)
)
model
.
compile
(
loss
=
'mean_squared_error'
,
optimizer
=
'adam'
)
# fit network
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
# make one forecast with an LSTM,
def
forecast_lstm
(
model
,
X
,
n_batch
)
:
# reshape input pattern to [samples, timesteps, features]
X
=
X
.
reshape
(
1
,
1
,
len
(
X
)
)
# make forecast
forecast
=
model
.
predict
(
X
,
batch_size
=
n_batch
)
# convert to array
return
[
x
for
x
in
forecast
[
0
,
:
]
]
# evaluate the persistence model
def
make_forecasts
(
model
,
n_batch
,
train
,
test
,
n_lag
,
n_seq
)
:
forecasts
=
list
(
)
for
i
in
range
(
len
(
test
)
)
:
X
,
y
=
test
[
i
,
0
:
n_lag
]
,
test
[
i
,
n_lag
:
]
# make forecast
forecast
=
forecast_lstm
(
model
,
X
,
n_batch
)
# store the forecast
forecasts
.
append
(
forecast
)
return
forecasts
# invert differenced forecast
def
inverse_difference
(
last_ob
,
forecast
)
:
# invert first forecast
inverted
=
list
(
)
inverted
.
append
(
forecast
[
0
]
+
last_ob
)
# propagate difference forecast using inverted first value
for
i
in
range
(
1
,
len
(
forecast
)
)
:
inverted
.
append
(
forecast
[
i
]
+
inverted
[
i
-
1
]
)
return
inverted
# inverse data transform on forecasts
def
inverse_transform
(
series
,
forecasts
,
scaler
,
n_test
)
:
inverted
=
list
(
)
for
i
in
range
(
len
(
forecasts
)
)
:
# create array from forecast
forecast
=
array
(
forecasts
[
i
]
)
forecast
=
forecast
.
reshape
(
1
,
len
(
forecast
)
)
# invert scaling
inv_scale
=
scaler
.
inverse_transform
(
forecast
)
inv_scale
=
inv_scale
[
0
,
:
]
# invert differencing
index
=
len
(
series
)
-
n_test
+
i
-
1
last_ob
=
series
.
values
[
index
]
inv_diff
=
inverse_difference
(
last_ob
,
inv_scale
)
# store
inverted
.
append
(
inv_diff
)
return
inverted
# evaluate the RMSE for each forecast time step
def
evaluate_forecasts
(
test
,
forecasts
,
n_lag
,
n_seq
)
:
for
i
in
range
(
n_seq
)
:
actual
=
[
row
[
i
]
for
row
in
test
]
predicted
=
[
forecast
[
i
]
for
forecast
in
forecasts
]
rmse
=
sqrt
(
mean_squared_error
(
actual
,
predicted
)
)
print
(
't+%d RMSE: %f'
%
(
(
i
+
1
)
,
rmse
)
)
# plot the forecasts in the context of the original dataset
def
plot_forecasts
(
series
,
forecasts
,
n_test
)
:
# plot the entire dataset in blue
pyplot
.
plot
(
series
.
values
)
# plot the forecasts in red
for
i
in
range
(
len
(
forecasts
)
)
:
off_s
=
len
(
series
)
-
n_test
+
i
-
1
off_e
=
off_s
+
len
(
forecasts
[
i
]
)
+
1
xaxis
=
[
x
for
x
in
range
(
off_s
,
off_e
)
]
yaxis
=
[
series
.
values
[
off_s
]
]
+
forecasts
[
i
]
pyplot
.
plot
(
xaxis
,
yaxis
,
color
=
'red'
)
# show the plot
pyplot
.
show
(
)
# load dataset
series
=
read_csv
(
'shampoo-sales.csv'
,
header
=
0
,
parse_dates
=
[
0
]
,
index_col
=
0
,
squeeze
=
True
,
date_parser
=
parser
)
# configure
n_lag
=
1
n_seq
=
3
n_test
=
10
n_epochs
=
1500
n_batch
=
1
n_neurons
=
1
# prepare data
scaler
,
train
,
test
=
prepare_data
(
series
,
n_test
,
n_lag
,
n_seq
)
# fit model
model
=
fit_lstm
(
train
,
n_lag
,
n_seq
,
n_batch
,
n_epochs
,
n_neurons
)
# make forecasts
forecasts
=
make_forecasts
(
model
,
n_batch
,
train
,
test
,
n_lag
,
n_seq
)
# inverse transform forecasts and test
forecasts
=
inverse_transform
(
series
,
forecasts
,
scaler
,
n_test
+
2
)
actual
=
[
row
[
n_lag
:
]
for
row
in
test
]
actual
=
inverse_transform
(
series
,
actual
,
scaler
,
n_test
+
2
)
# evaluate forecasts
evaluate_forecasts
(
actual
,
forecasts
,
n_lag
,
n_seq
)
# plot forecasts
plot_forecasts
(
series
,
forecasts
,
n_test
+
2
)
|
Running the example first prints the RMSE for each of the forecasted time steps.
运行这个例子,首先打印每个预测时间步的RMSE.
We can see that the scores at each forecasted time step are better, in some cases much better, than the persistence forecast.
我们可以看到,在每个预测时间步中的得分比持续预测更好,在某些情况下要好得多。
This shows that the configured LSTM does have skill on the problem.
这表明配置的LSTM在这个问题上确实有技巧.
It is interesting to note that the RMSE does not become progressively worse with the length of the forecast horizon, as would be expected. This is marked by the fact that the t+2 appears easier to forecast than t+1. This may be because the downward tick is easier to predict than the upward tick noted in the series (this could be confirmed with more in-depth analysis of the results).
有趣的是,如预期的那样,随着预测时间的长度,RMSE不会变得越来越差。 这标志着t + 2比t + 1更容易预测的事实。 这可能是因为下降趋势比系列中提到的上升趋势更容易预测(这可以通过对结果进行更深入的分析来确认)。
1
2
3
|
t+1 RMSE: 95.973221
t+2 RMSE: 78.872348
t+3 RMSE: 105.613951
|
A line plot of the series (blue) with the forecasts (red) is also created.
系列(蓝色)与预测(红色)的线图也被创建。
The plot shows that although the skill of the model is better, some of the forecasts are not very good and that there is plenty of room for improvement.
这图表明,虽然模型的技巧更好,但一些预测不是很好,还有很大的改进空间。