LSTM可以实现时序数据的预测,下面以一个环境质量变化的时序序列来介绍其实现和处理过程。
LSTM模型
数据集介绍
首先是数据集:里面有9个变量值,其中包含时间和其他环境指标变量 ,经过数据处理过程效果如下:
关于数据集结构
如果 [batch_size, time_step, input_size]=[30,5,7] 那么,上图中,黑色框代表的就是一个batch_size中所含有的数据的量。 那么,从上到下的3个红色框就为 time_step为5的时候,每次细胞输入门所输入的数据量。 那么,列B~列H,一共7列,就为 input_size
- input_size 是根据你的训练问题而确定的。
- time_step是LSTM神经网络中的重要参数,time_step在神经网络模型建好后一般就不会改变了。
- 与time_step不同的是,batch_size是模型训练时的训练参数,在模型训练时可根据模型训练的结果以及loss随时进行调整,达到最优。
from pandas import read_csv
from datetime import datetime
def parse(x):
return datetime.strptime(x, '%Y %m %d %H')
dataset = read_csv('raw.csv', parse_dates = [['year', 'month', 'day', 'hour']], index_col=0, date_parser=parse)
print(dataset.head(5))
dataset.drop('No', axis=1, inplace=True)
dataset.columns = ['pollution', 'dew', 'temp', 'press', 'wnd_dir', 'wnd_spd', 'snow', 'rain']
dataset.index.name = 'date'
dataset['pollution'].fillna(0, inplace=True)
dataset = dataset[:]
print(dataset.head(5))
dataset.to_csv('pollution.csv')
主要是将原数据的日期格式进行规范,删除一些缺失值数据来生成我们需要的数据集。
图表展示
将数据进行图表输出:
from pandas import read_csv
from matplotlib import pyplot
dataset = read_csv('pollution.csv', header=0, index_col=0)
values = dataset.values
groups = [0, 1, 2, 3, 5, 6, 7]
i = 1
pyplot.figure()
for group in groups:
pyplot.subplot(len(groups), 1, i)
pyplot.plot(values[:, group])
pyplot.title(dataset.columns[group], y=0.5, loc='right')
i += 1
pyplot.show()
接下便是模型读取数据,训练,评估的过程了: 其实现的是一个多步长,多变量的时间序列预测过程:
生成监督数据
首先对数据集进行处理为监督数据,将其转换为3–>1的预测数据格式。
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()
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)]
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)]
agg = concat(cols, axis=1)
agg.columns = names
print(agg)
if dropnan:
agg.dropna(inplace=True)
return agg
读取数据,并进行风向编码:将单词变为数据,并进行标准化缩放
dataset = read_csv('pollution.csv', header=0, index_col=0)
values = dataset.values
encoder = LabelEncoder()
values[:,4] = encoder.fit_transform(values[:,4])
print(values[:,4])
values = values.astype('float32')
scaler = MinMaxScaler(feature_range=(0, 1))
scaled = scaler.fit_transform(values)
转换为监督数据:
n_hours = 3
n_features = 8
reframed = series_to_supervised(scaled, n_hours, 1)
print(reframed.shape)
所得的数据格式:(43797, 32)即有43797条数据,按每个小时划分,32个变量值 划分训练集与测试集,并设置输入值与输出值,设置前一年的数据进行训练,取每组数据的前38个为输入值,后18为输出值,取剩下的数据,即三年数据为测试集。
values = reframed.values
n_train_hours = 365 * 24
train = values[:n_train_hours, :]
test = values[n_train_hours:, :]
n_obs = n_hours * n_features
train_X, train_y = train[:, :n_obs], train[:, -n_features]
test_X, test_y = test[:, :n_obs], test[:, -n_features]
print(train_X.shape, len(train_X), train_y.shape)
(8760, 24) 8760 (8760,) (8760, 3, 8) (8760,) (35037, 3, 8) (35037,)
设计网络模型
model = Sequential()
model.add(LSTM(50, input_shape=(train_X.shape[1], train_X.shape[2])))
model.add(Dense(1))
model.compile(loss='mae', optimizer='adam')
拟合网络
history = model.fit(train_X, train_y, epochs=50, batch_size=8, validation_data=(test_X, test_y), verbose=2, shuffle=False)
图表展示数据误差值
pyplot.plot(history.history['loss'], label='train')
pyplot.plot(history.history['val_loss'], label='test')
pyplot.legend()
pyplot.show()
数据预测
yhat = model.predict(test_X)
test_X = test_X.reshape((test_X.shape[0], n_hours*n_features))
inv_yhat = concatenate((yhat, test_X[:, -7:]), axis=1)
inv_yhat = scaler.inverse_transform(inv_yhat)
inv_yhat = inv_yhat[:,0]
test_y = test_y.reshape((len(test_y), 1))
inv_y = concatenate((test_y, test_X[:, -7:]), axis=1)
inv_y = scaler.inverse_transform(inv_y)
inv_y = inv_y[:,0]
rmse = sqrt(mean_squared_error(inv_y, inv_yhat))
print('Test RMSE: %.3f' % rmse)
系统改进
之前博主在看这个系统时,发现了几个问题,即他最终的输出结果是pollution等级,他是如何实现的呢? 首先,在切分数据集是,他取前24列作为已知序列,使用倒数第8列为预测数列,即标签:
train_X, train_y = train[:, :n_obs], train[:, -n_features]
test_X, test_y = test[:, :n_obs], test[:, -n_features]
确定了我们的输出结果为1列,那么我们最后的全连接层设为1即可:
model = Sequential()
model.add(LSTM(50, input_shape=(train_X.shape[1], train_X.shape[2])))
model.add(Dense(1))
然后我们的数据格式如何规范呢,在我们的fit这里就做了限定:
history = model.fit(train_X, train_y, epochs=2, batch_size=8, validation_data=(test_X, test_y), verbose=2, shuffle=False)
那么到这里问题就很清晰了,如果我想让我的结果为八个属性值呢,我们只需要这样做:将标签取后8个:
train_X, train_y = train[:, :n_obs], train[:, -n_features:]
test_X, test_y = test[:, :n_obs], test[:, -n_features:]
print(train_X.shape, len(train_X), train_y.shape,train_y)
train_X = train_X.reshape((train_X.shape[0], n_hours, n_features))
test_X = test_X.reshape((test_X.shape[0], n_hours, n_features))
train_Y = train_y.reshape((train_y.shape[0], 1, n_features))
test_Y = test_y.reshape((test_y.shape[0], 1, n_features))
对网络的输出结果进行限定,全连接层改为8
model = Sequential()
model.add(LSTM(50,input_shape=(train_X.shape[1], train_X.shape[2])))
model.add(Dense(8))
其余不用改变,这样他的输出结果变为后8个变量了 然后我们对所得的八个结果进行逆缩放为原理的数据:
yhat = model.predict(test_X)
print(yhat.shape)
inv_yhat = scaler.inverse_transform(yhat)
test_y = test_y.reshape((len(test_y), n_features))
inv_y = scaler.inverse_transform(test_y)
将实验预测结果与实际结果进行对比:
groups = [0, 1, 2, 3,4, 5, 6, 7]
i = 1
pyplot.figure()
for group in groups:
pyplot.subplot(len(groups), 1, i)
pyplot.plot(inv_yhat[:, group],label='predict',c='blue')
pyplot.plot(inv_y[:,group],label='true',c='red')
i += 1
pyplot.show()
rmse = sqrt(mean_squared_error(inv_y, inv_yhat))
print('Test RMSE: %.3f' % rmse)
拟合效果不错 Test RMSE: 16.705
|