2020-05-01 18:16:51 +00:00
|
|
|
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 numpy
|
|
|
|
|
|
|
|
|
|
|
|
# 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.fillna(0, inplace=True)
|
|
|
|
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, value):
|
|
|
|
new_row = [x for x in X] + [value]
|
|
|
|
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):
|
2020-05-01 18:21:28 +00:00
|
|
|
print("Epoch {}/{}".format(i, nb_epoch))
|
2020-05-01 18:16:51 +00:00
|
|
|
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]
|
|
|
|
|
|
|
|
|
2020-05-02 01:33:14 +00:00
|
|
|
batch_size = 12
|
|
|
|
|
2020-05-01 18:16:51 +00:00
|
|
|
# load dataset
|
|
|
|
series = read_csv('data.csv', header=0, index_col=0, squeeze=True)
|
|
|
|
|
|
|
|
# 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 the model
|
2020-05-02 01:33:14 +00:00
|
|
|
lstm_model = fit_lstm(train_scaled, batch_size, 30, 4)
|
2020-05-01 18:16:51 +00:00
|
|
|
# forecast the entire training dataset to build up state for forecasting
|
|
|
|
train_reshaped = train_scaled[:, 0].reshape(len(train_scaled), 1, 1)
|
2020-05-02 01:33:14 +00:00
|
|
|
lstm_model.predict(train_reshaped, batch_size=batch_size)
|
2020-05-01 18:16:51 +00:00
|
|
|
|
|
|
|
# walk-forward validation on the test data
|
|
|
|
predictions = list()
|
2020-05-02 00:50:39 +00:00
|
|
|
for j in range(len(test_scaled)):
|
2020-05-01 18:16:51 +00:00
|
|
|
# make one-step forecast
|
2020-05-02 01:34:28 +00:00
|
|
|
#X, y = test_scaled[j, 0:-1], test_scaled[j, -1]
|
|
|
|
yhat = forecast_lstm(lstm_model, batch_size, test_scaled)
|
2020-05-01 18:16:51 +00:00
|
|
|
# invert scaling
|
2020-05-02 01:34:28 +00:00
|
|
|
yhat = invert_scale(scaler, test_scaled, yhat)
|
2020-05-01 18:16:51 +00:00
|
|
|
# invert differencing
|
2020-05-02 00:50:39 +00:00
|
|
|
yhat = inverse_difference(raw_values, yhat, len(test_scaled) + 1 - j)
|
2020-05-01 18:16:51 +00:00
|
|
|
# store forecast
|
|
|
|
predictions.append(yhat)
|
2020-05-02 00:50:39 +00:00
|
|
|
expected = raw_values[len(train) + j + 1]
|
|
|
|
print('Month=%d, Predicted=%f, Expected=%f' % (j + 1, yhat, expected))
|
2020-05-01 18:16:51 +00:00
|
|
|
|
|
|
|
# report performance
|
|
|
|
rmse = sqrt(mean_squared_error(raw_values[-12:], predictions))
|
|
|
|
print('Test RMSE: %.3f' % rmse)
|