使用Keras进行LSTM实战

0 前言

在上文中进行ARIMA时序预测后,了解到强大的LSTM在处理时序预测有更为优秀的表现,因此对LSTM进行了学习。

LSTM是一种时间递归神经网络,它出现的原因是为了解决RNN的一个致命的缺陷。原生的RNN会遇到一个很大的问题,叫做The vanishing gradient problem for RNNs,也就是后面时间的节点会出现老年痴呆症,也就是忘事儿,这使得RNN在很长一段时间内都没有受到关注,网络只要一深就没法训练。而LSTM网络具有“记忆性”,其原因在于不同“时间点”之间的网络存在连接,而不是单个时间点处的网络存在前馈或者反馈;并且LSTM擅长于处理多个变量的问题,该特性使其有助于解决时间序列预测问题。 

关于LSTM原理,初学者可以通过以下几篇文章简单了解一下,:

https://www.toutiao.com/a6502203093856289294/

https://zybuluo.com/hanbingtao/note/581764

本文内容取自Jason Brownlee的《Multivariate Time Series Forecasting with LSTMs in Keras》一文。在接下来的这篇博客中,你将学会如何利用深度学习库Keras搭建LSTM模型来处理多个变量的时间序列预测问题。

关于如何搭建Keras请看《windows下安装Keras(CPU版)》一文。 

阅读完本文后你将会掌握: 

  • 如何将原始数据转化为适合处理时序预测问题的数据格式; 
  • 如何准备数据并搭建LSTM来处理时序预测问题; 
  • 如何利用模型预测。

1 空气污染预测

  在这篇博客中,我们将采用空气质量数据集。数据来源自位于北京的美国大使馆在2010年至2014年共5年间每小时采集的天气及空气污染指数。 
  数据集包括日期、PM2.5浓度、露点、温度、风向、风速、累积小时雪量和累积小时雨量。原始数据中完整的特征如下:

1.No 行数
2.year 年
3.month 月
4.day 日
5.hour 小时
6.pm2.5 PM2.5浓度
7.DEWP 露点
8.TEMP 温度
9.PRES 大气压
10.cbwd 风向
11.lws 风速
12.ls 累积雪量
13.lr 累积雨量

我们可以利用此数据集搭建预测模型,利用前一个或几个小时的天气条件和污染数据预测下一个(当前)时刻的污染程度。

  可以在UCI Machine Learning Repository下载数据集。 也可以点击此处下载Beijing PM2.5 Data Set

2 数据处理

第一步,我们必须清洗数据。以下是原始数据集的前几行。

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

 

第一步是将日期时间信息整合为一个日期时间,以便我们可以将其用作Pandas的索引。我们需要快速显示前24小时的pm2.5的NA值。因此,我们需要删除第一行数据。在数据集中还有几个分散的“NA”值;我们现在可以用0值标记它们。

以下脚本加载原始数据集,并将日期时间信息解析为Pandas Data Frame索引。“No”列被删除,然后为每列指定更清晰的名称。最后,将NA值替换为“0”值,并删除前24小时。

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')

 运行该示例打印转换的数据集的前5行,并将数据集保存到“pollution.csv”。

                       pollution  dew  temp   press wnd_dir  wnd_spd  snow  rain
date
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

现在的数据格式已经更加适合处理,可以简单的对每列进行绘图。下面的代码加载了“pollution.csv”文件,并对除了类别型特性“风速”的每一列数据分别绘图。

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()

运行上述代码,并对7个变量在5年的范围内绘图。 

3 多变量LSTM预测模型

3.1 LSTM数据准备

第一步是为LSTM准备污染数据集。这涉及将数据集视为监督学习问题并对输入变量进行归一化处理。考虑到上一个时间段的污染测量和天气条件,我们将把监督学习问题作为预测当前时刻(t)的污染情况。根据过去24小时的天气情况和污染,预测下一个小时的污染,并给予下一个小时的“预期”天气条件。

我们可以使用在之前博客中所写的series_to_supervised()函数来转换数据集:如何将时间序列转换为Python中的监督学习问题

下面代码中首先加载“pollution.csv”文件,并利用sklearn的预处理模块对类别特征“风向”进行编码,当然也可以对该特征进行one-hot编码。 接着对所有的特征进行归一化处理,然后将数据集转化为有监督学习问题,同时将需要预测的当前时刻(t)的天气条件特征移除,完整代码如下:

# 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())

运行示例打印转换后的数据集的前5行。我们可以看到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

3.2 构造模型

  在这一节,我们将构造LSTM模型。 
  首先,我们需要将处理后的数据集划分为训练集和测试集。为了加速模型的训练,我们仅利用第一年数据进行训练,然后利用剩下的4年进行评估。 
  下面的代码将数据集进行划分,然后将训练集和测试集划分为输入和输出变量,最终将输入(X)改造为LSTM的输入格式,即[samples,timesteps,features]。

# 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)

运行上述代码打印训练集和测试集的输入输出格式,其中9K小时数据作训练集,35K小时数据作测试集。

(8760, 1, 8) (8760,) (35039, 1, 8) (35039,)

现在可以搭建LSTM模型了。 LSTM模型中,隐藏层有50个神经元,输出层1个神经元(回归问题),输入变量是一个时间步(t-1)的特征,损失函数采用Mean Absolute Error(MAE),优化算法采用Adam,模型采用50个epochs并且每个batch的大小为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()

# 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.3 模型评估

  接下里我们对模型效果进行评估。 
  值得注意的是:需要将预测结果和部分测试集数据组合然后进行比例反转(invert the scaling),同时也需要将测试集上的预期值也进行比例转换。 
  (We combine the forecast with the test dataset and invert the scaling. We also invert scaling on the test dataset with the expected pollution numbers.) 
  至于在这里为什么进行比例反转,是因为我们将原始数据进行了预处理(连同输出值y),此时的误差损失计算是在处理之后的数据上进行的,为了计算在原始比例上的误差需要将数据进行转化。同时笔者有个小Tips:就是反转时的矩阵大小一定要和原来的大小(shape)完全相同,否则就会报错。 
  通过以上处理之后,再结合RMSE(均方根误差)计算损失。

# 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((yhat, test_X[:, 1:]), axis=1)
inv_yhat = scaler.inverse_transform(inv_yhat)
inv_yhat = inv_yhat[:,0]
# invert scaling for actual
test_y = test_y.reshape((len(test_y), 1))
inv_y = concatenate((test_y, test_X[:, 1:]), axis=1)
inv_y = scaler.inverse_transform(inv_y)
inv_y = inv_y[:,0]
# calculate RMSE
rmse = sqrt(mean_squared_error(inv_y, inv_yhat))
print('Test RMSE: %.3f' % rmse)

本教程的完整代码如下:

import pandas as pd
from datetime import datetime
from matplotlib import pyplot
from sklearn.preprocessing import LabelEncoder,MinMaxScaler
from sklearn.metrics import mean_squared_error
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from numpy import concatenate
from math import sqrt

# load data
def parse(x):
	return datetime.strptime(x, '%Y %m %d %H')

def read_raw():
    dataset = pd.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')

def drow_pollution():
    dataset = pd.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(figsize=(10,10))
    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()

def series_to_supervised(data, n_in=1, n_out=1, 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)
    	return agg
    
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')
    # 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())
    return reframed,scaler
    
def train_test(reframed):
    # 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)
    return train_X,train_y,test_X,test_y

def fit_network(train_X,train_y,test_X,test_y,scaler):
    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()
    # 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((yhat, test_X[:, 1:]), axis=1)
    inv_yhat = scaler.inverse_transform(inv_yhat)
    inv_yhat = inv_yhat[:,0]
    # invert scaling for actual
    inv_y = scaler.inverse_transform(test_X)
    inv_y = inv_y[:,0]
    # calculate RMSE
    rmse = sqrt(mean_squared_error(inv_y, inv_yhat))
    print('Test RMSE: %.3f' % rmse)
    
if __name__ == '__main__':
    drow_pollution()
    reframed,scaler = cs_to_sl()
    train_X,train_y,test_X,test_y = train_test(reframed)
    fit_network(train_X,train_y,test_X,test_y,scaler)

运行结果如下:

Train on 8760 samples, validate on 35039 samples
Epoch 1/50
 - 2s - loss: 0.0567 - val_loss: 0.0464
Epoch 2/50
 - 1s - loss: 0.0390 - val_loss: 0.0423
Epoch 3/50
······
- 1s - loss: 0.0144 - val_loss: 0.0134
Epoch 48/50
 - 1s - loss: 0.0144 - val_loss: 0.0134
Epoch 49/50
 - 1s - loss: 0.0144 - val_loss: 0.0136
Epoch 50/50
 - 1s - loss: 0.0145 - val_loss: 0.0134

Test RMSE: 4.313

4进一步阅读

如果你想继续深入研究,本节提供更多的阅读资源: 

PS.本文为译文,若有疑问请联系原作者Jason Brownlee

原文地址:https://machinelearningmastery.com/multi-step-time-series-forecasting-long-short-term-memory-networks-python/

  • 155
    点赞
  • 1033
    收藏
    觉得还不错? 一键收藏
  • 70
    评论
### 回答1: Keras是一个Python深度学习库,可以方便地实现各种神经网络模型。其中,LSTM(长短时记忆)是一种特殊类型的循环神经网络(RNN),在序列数据处理和时间序列预测中十分常用。接下来,我们就来介绍一下如何使用Keras实战LSTM模型。 首先,我们需要安装Keras库,并导入所需的包: ``` !pip install keras import numpy as np from keras.models import Sequential from keras.layers import LSTM, Dense ``` 接下来,我们可以准备数据集。LSTM模型通常用于时间序列预测,在这里我们以一个简单的sin函数为例,生成一组时间序列数据作为输入和输出: ``` # 生成输入和输出数据 data = [] target = [] seq_length = 10 for i in range(1000): start = np.random.uniform(0, 2*np.pi) end = start + seq_length * 0.1 t = np.arange(start, end, 0.1) x = np.sin(t) y = np.sin(t+0.1) data.append(x) target.append(y) data = np.array(data) target = np.array(target) ``` 接下来,我们可以定义模型结构并进行训练: ``` # 定义模型结构 model = Sequential() model.add(LSTM(64, input_shape=(seq_length, 1))) model.add(Dense(1)) # 编译模型 model.compile(loss='mean_squared_error', optimizer='adam') # 训练模型 model.fit(data, target, epochs=10, batch_size=32) ``` 最后,我们可以使用训练好的模型进行预测: ``` # 使用模型预测 test_data = np.sin(np.arange(2*np.pi, 2*np.pi+seq_length*0.1, 0.1)) test_data = np.reshape(test_data, (1, seq_length, 1)) prediction = model.predict(test_data) ``` 以上就是使用Keras实战LSTM模型的基本步骤。你可以根据需要调整模型的参数和结构,进一步优化模型的性能。希望这个回答对你有帮助! ### 回答2: Keras是一个流行的Python深度学习库,它被广泛用于构建各种神经网络模型。其中,LSTM(长短期记忆网络)是一种特殊的循环神经网络架构,被广泛应用于处理序列数据。 在使用Keras进行LSTM实战时,我们可以按照以下步骤进行操作: 1. 数据预处理:首先,我们需要对输入数据进行预处理。这包括分割数据集为训练集和测试集,对数据进行标准化或归一化,以及将数据转换为适合LSTM模型的序列格式。 2. 构建LSTM模型:使用Keras的Sequential模型或函数式API构建LSTM模型。我们可以指定模型的层级结构、激活函数、dropout等参数,来实现我们想要的网络结构。 3. 编译模型:在编译模型之前,我们需要指定损失函数、优化器和评估指标。损失函数可以根据问题类型选择,优化器可以选择Adam或RMSprop等,并根据需求设置学习率等参数。 4. 模型训练:使用训练数据对构建的LSTM模型进行训练。使用Keras的fit()函数或fit_generator()函数可以指定训练数据、批量大小、训练周期数等参数,来完成模型的训练过程。 5. 模型评估:在完成模型训练后,可以使用测试数据对模型进行评估。通过调用evaluate()函数可以获取模型在测试数据上的损失值和评估指标值。 6. 模型预测:最后,我们可以使用训练好的LSTM模型进行预测。通过调用predict()函数,将输入数据传递给模型,可以获取模型预测的结果。 总结来说,使用Keras进行LSTM实战需要进行数据预处理、模型构建、编译模型、模型训练、模型评估和模型预测等步骤。Keras提供了简单且易于使用的API,使得LSTM实战变得更加高效和便捷。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 70
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值