一.前言
LSTM是一种长短期记忆网络,也称为时间递归神经网络,它出现的原因是为了解决RNN的一个致命的缺陷—梯度消失现象。即无法处理长时依赖情况。叫做The vanishing gradient problem for RNNs,通俗一点就是后面时间节点会出现老年痴呆症。这使得RNN只要网络一深就没有办法训练。根据实战经验的验证,LSTM网络已被证明比传统的RNNs更加有效。
优点:LSTM适合多输入变量的神经网络。例如水质,大气预测等。
二.数据集
美国驻北京大使馆五年内报告天气和污染水平。数据类型主要包括:日期时间,PM2.5,露点温度,温度,压力,风向,风速,和累计的降雪小时数。其中指标定义如下:
NO:行号,year:年,month:月,day:日,hour:小时,pm2.5:pm2.5浓度,DEWP:露点温度,TEMP:温度,PRES:压力,cbwd: 风向,lws:风速,ls:积雪时间,lr:累计下雪时长。
三.方法
使用收集的公开数据集构建一个预测问题。基于天气条件和过去前几个小时的污染,进而预测下一小时的污染状况。
1.数据清洗
No,year,month,day,hour,pm2.5,DEWP,TEMP,PRES,cbwd,Iws,Is,Ir
1,2010,1,1,0,NA,-21,-11,1021,NW,1.79,0,0
2,2010,1,1,1,NA,-21,-12,1020,NW,4.92,0,0
3,2010,1,1,2,NA,-21,-11,1019,NW,6.71,0,0
4,2010,1,1,3,NA,-21,-14,1019,NW,9.84,0,0
5,2010,1,1,4,NA,-20,-12,1018,NW,12.97,0,0
首先将日期整合为一个规范的年月日时间。其次显示前24小时的pm2.5的NA值。数据集中分散的NA值,用0值标记他们。
from pandas import read_csv
from datetime import datetime
# load data
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)
dataset.drop('No', axis=1, inplace=True)
# manually specify column names
dataset.columns = ['pollution', 'dew', 'temp', 'press', 'wnd_dir', 'wnd_spd', 'snow', 'rain']
dataset.index.name = 'date'
# mark all NA values with 0
dataset['pollution'].fillna(0, inplace=True)
# drop the first 24 hours
dataset = dataset[24:]
# summarize first 5 rows
print(dataset.head(5))
# save to file
dataset.to_csv('pollution.csv')
上述编码为加载原始数据集,并将日期时间信息解析为pandas可索引项。
下述代码运行前5行,并将数据集保存到数据集:pollution.csv中。
Date pollution dew temp press wnd_dir wnd_spd snow rain
2010-01-02 00:00:00 129.0 -16 -4.0 1020.0 SE 1.79 0 0
2010-01-02 01:00:00 148.0 -15 -4.0 1020.0 SE 2.68 0 0
2010-01-02 02:00:00 159.0 -11 -5.0 1021.0 SE 3.57 0 0
2010-01-02 03:00:00 181.0 -7 -5.0 1022.0 SE 5.36 1 0
2010-01-02 04:00:00 138.0 -7 -5.0 1022.0 SE 6.25 2 0
下面代码则是将每个指标因素作为单独子图绘制。风速除外。
from pandas import read_csv
from matplotlib import pyplot
# load dataset
dataset = read_csv('pollution.csv', header=0, index_col=0)
values = dataset.values
# specify columns to plot
groups = [0, 1, 2, 3, 5, 6, 7]
i = 1
# plot each column
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()
下图为显示每个指标五年的数据。
2.多变量LSTM预测模型建立
1.数据准备
将数据变量值进行归一化处理。我们将预测问题转化为预测当前时刻t的污染状况。根据过去24小时的天情况和污染,预测下一个小时的污染。
# convert series to supervised learning
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
# load dataset
dataset = 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')
# normalize features
scaler = MinMaxScaler(feature_range=(0, 1))
scaled = scaler.fit_transform(values)
# frame as supervised learning
reframed = series_to_supervised(scaled, 1, 1)
# drop columns we don't want to predict
reframed.drop(reframed.columns[[9,10,11,12,13,14,15]], axis=1, inplace=True)
print(reframed.head())
运行后打印前五行。我们可以看到8个输入变量和1个输出变量
var1(t-1) var2(t-1) var3(t-1) var4(t-1) var5(t-1) var6(t-1) \
1 0.129779 0.352941 0.245902 0.527273 0.666667 0.002290
2 0.148893 0.367647 0.245902 0.527273 0.666667 0.003811
3 0.159960 0.426471 0.229508 0.545454 0.666667 0.005332
4 0.182093 0.485294 0.229508 0.563637 0.666667 0.008391
5 0.138833 0.485294 0.229508 0.563637 0.666667 0.009912
var7(t-1) var8(t-1) var1(t)
1 0.000000 0.0 0.148893
2 0.000000 0.0 0.159960
3 0.000000 0.0 0.182093
4 0.037037 0.0 0.138833
5 0.074074 0.0 0.109658
注:必须提供超过一个小时的输入步长。
2.定义和拟合模型
第一年作为训练数据,后四年作为检测数据。输入结果为【样本,时间步长,特征】
# split into train and test sets
values = reframed.values
n_train_hours = 365 * 24
train = values[:n_train_hours, :]
test = values[n_train_hours:, :]
# split into input and outputs
train_X, train_y = train[:, :-1], train[:, -1]
test_X, test_y = test[:, :-1], test[:, -1]
# 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)
运行示例结果:
(8760, 1, 8) (8760,) (35039, 1, 8) (35039,)
将在第一个隐层中定义具有50个神经元的LSTM和用于预测污染的输出层中的1个神经元。输入形状是1个时间步长,具有8个特征。
在模型中使用平均绝对误差(MAE)损失函数。该模型将拟合50个批量大小为72的训练时期。最后,我们通过在fit()函数中设置validation_data参数来跟踪训练过程中的训练和测试失败。在运行结束时,绘制训练和测试损失。
# design network
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')
# fit network
history = model.fit(train_X, train_y, epochs=50, batch_size=72, validation_data=(test_X, test_y), verbose=2, shuffle=False)
# plot history
pyplot.plot(history.history['loss'], label='train')
pyplot.plot(history.history['val_loss'], label='test')
pyplot.legend()
pyplot.show()
3.评估模型
模型拟合后,我们可以预测整个测试数据集。
我们将预测的数据集与测试数据集相结合,并反演缩放。我们还可以用预期的污染数字来反演测试数据集的缩放。使用预测值和实际值,我们可以计算模型的误差分数。并且我们还可以计算出与变量本身相同的单位产生误差的均方根误差(RMSE)。
运行示例创建一个绘图,显示训练中的训练损失和测试损失:
可以看出测试损失低于训练损失,该模型可能过度拟合。
训练损失和测试损失在每个训练时期结束时打印。在运行结束时,打印测试数据集上模型的最终RMSE。我们可以看到,该模型RMSE:3.8。
Epoch 46/50
0s - loss: 0.0143 - val_loss: 0.0133
Epoch 47/50
0s - loss: 0.0143 - val_loss: 0.0133
Epoch 48/50
0s - loss: 0.0144 - val_loss: 0.0133
Epoch 49/50
0s - loss: 0.0143 - val_loss: 0.0133
Epoch 50/50
0s - loss: 0.0144 - val_loss: 0.0133
Test RMSE: 3.836
四.参考
本文结合了风信子Al的博客。