lstm 预测未来多天
前言:
由于原模型只能预测一天,不满足需求,所以在上篇的基础模型上进行修改,使原模型可以预测未来多天结果。
修改之后,新模型可以根据多天的数据预测未来多天的结果。应用范围广泛,可以用于,股票预测,汇率预测,安全仓库预测,电力负荷预测等各种实际的应用。可以根据数据集的不同,使用该模型解决各种实际的预测问题。
由于项目数据集不公开,本文使用公开数据集,Beijing PM2.5 Data Set进行仿真实验。具体来说,根据前五天的环境变量信息,预测未来五天的污染值。
(23条消息) lstm多变量预测_wh来啦的博客-CSDN博客
模型结构对比:
原模型:
改进之后的模型:
使用公开数据集:
Beijing PM2.5 Data Set
下载数据集地址:
http://archive.ics.uci.edu/ml/datasets/Beijing+PM2.5+Data
如果失效,百度直接搜索Beijing PM2.5 Data Set,也可以免费获取数据集。
数据预处理:
对数据进行清洗,得到可以使用的数据。数据清洗时需要根据数据本身的特点,进行针对性的清洗。
数据划分:
首先将数据划分为X,Y。本次实验需要设计输入五天的变量,输出未来五天的预测值,所以,n_in=5,表示需要历史五天的数据,n_out=5,表示预测未来五天的值,代码如下:
def series_to_supervised(data, n_in=5, n_out=5, dropnan=True):
# convert series to supervised learning
n_vars = 1 if type(data) is list else data.shape[1]
df = pd.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 = pd.concat(cols, axis=1)
agg.columns = names
# drop rows with NaN values
if dropnan:
agg.dropna(inplace=True)
# normalize features
return agg
将不需要预测的数据drop掉,得到我们希望预测的数据结构。
def cs_to_sl():
# load dataset
dataset = pd.read_csv('pollution.csv', header=0, index_col=0)
values = dataset.values
# integer encode direction
encoder = LabelEncoder()
values[:, 4] = encoder.fit_transform(values[:, 4])
# ensure all data is float
values = values.astype('float32')
# frame as supervised learning
reframed = series_to_supervised(values, 5, 5)
# drop columns we don't want to predict
reframed.drop(reframed.columns[[41,42,43,44,45,46,47,49,50,51,52,53,54,55,57,58,59,60,61,62,63,65,66,67,68,69,70,71,73, 74, 75, 76, 77, 78, 79]], axis=1, inplace=True)
print(reframed.head())
return reframed
因为一天有八个变量,需要历史五天的数据,所有X就有8*5列(40列)数据,而要预测未来五天的污染物值,所以Y有5列数据,合在一起就有45列数据。,具体的历史数据,预测数据结构如下:
然后将数据划分为训练集(train),测试集(test)。由于数据列表数发生变化,归一化的过程调整到这一部分,否者列数对应不上,反归一化时会报错。由于预测的列数发生变化,划分train_x,train_y,test_x,test_y时,需要调整相应的维度,这里调整为.[:,:-5][:,-5:]。具体代码如下:
def train_test(reframed):
# split into train and test sets
values = reframed.values
n_train_hours = 365 * 24*3
scaler = MinMaxScaler(feature_range=(0, 1))
values = scaler.fit_transform(values)
train = values[:n_train_hours, :]
test = values[n_train_hours:, :]
# split into input and outputs
train_X, train_y = train[:, :-5],train[:, -5:]
test_X, test_y = test[:, :-5], test[:, -5:]
# reshape input to be 3D [samples, timesteps, features]
train_X = train_X.reshape((train_X.shape[0], 1, train_X.shape[1]))
test_X = test_X.reshape((test_X.shape[0], 1, test_X.shape[1]))
print(train_X.shape, train_y.shape, test_X.shape, test_y.shape)
return train_X, train_y, test_X, test_y, scaler
构建模型并训练:
调整模型结构,从原来的一层lstm,调整到三层lstm。为了避免过拟合的问题,使用Dropout函数。由于要预测未来五天的数据,所以最后一个dense的输出,从原来的1,调整为5。同时,修改模型的学习率,使模型取得更好的效果。最后,在对应的列上,使用反归一化,得到未来预测的结果。其他参数,如epoch,batchsize等,可以自己调整,就不细说了。嘻嘻
def fit_network(train_X, train_y, test_X, test_y, scaler):
model = Sequential()
model.add(LSTM(50, return_sequences=True,input_shape=(train_X.shape[1], train_X.shape[2])))
model.add(Dropout(0.3))
model.add(LSTM(50,return_sequences=True))
model.add(Dropout(0.3))
model.add(LSTM(50))
model.add(Dense(5))
model.compile(loss='mae', optimizer='adam')
# fit network
reduce_lr = ReduceLROnPlateau(monitor='val_loss', patience=10, mode='auto')
history = model.fit(train_X, train_y, epochs=50, batch_size=72, validation_data=(test_X, test_y), verbose=2,
shuffle=False, callbacks=[reduce_lr])
# plot history
pyplot.plot(history.history['loss'], label='train')
pyplot.plot(history.history['val_loss'], label='test')
pyplot.legend()
pyplot.show()
# make a prediction
yhat = model.predict(test_X)
test_X = test_X.reshape((test_X.shape[0], test_X.shape[2]))
# invert scaling for forecast
inv_yhat = concatenate((test_X, yhat),axis=1)
inv_yhat = scaler.inverse_transform(inv_yhat)
print(inv_yhat[-1:])
inv_yhat = inv_yhat[:, -5:]
# invert scaling for actual
inv_y = concatenate((test_X, test_y), axis=1)
inv_y = scaler.inverse_transform(inv_y)
inv_y = inv_y[:, -5:]
# calculate RMSE
rmse = sqrt(mean_squared_error(inv_y, inv_yhat))
print('Test RMSE: %.3f' % rmse)
最后展示一下运行结果:
结果分析: 至于为什么自己误差那么大,可能是因为数据选得不好;或者是因为模型结构不够完美,需要微调;或者是因为一次预测五天,相比预测一天,误差本来就会很大。这个就得慢慢分析,调整了,嘻嘻。
来源:wh来啦