lstm对时间数据的预测作用(多变量对多变量预测)

思考:

  • 如何处理原始数据,空值?无意义列?数据归一化?异常值?
  • 时序数据预测问题的数据格式如何构造(X,Y);
  • 如何使用LSTM来预测时序数据(输入输出?多加几成lstm注意什么?return_sequences=True?);
  • 如何用模型预测?生成结果注意什么?反归一化再进行比较。
  • 归(反归)一化注意什么?
  • 模型参数如何调整?例如:学习率如何调整?

1.使用lstm进行空气污染预测介绍

  • 采用空气质量数据集。数据来源自位于北京的美国大使馆在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 累积雨量lstm对时间数据的预测作用(多变量对多变量预测)_第1张图片

2.数据预处理

1. 数据清洗

  • 将日期(年月日时)合并为一个日期,并作为表的索引
  • 删除没有意义的NO列
  • 为列名重起好理解的名字
  • 空值处理(NA),充填0
  • 观察到2010年1月1日pm2.5为NA,将该天数据删除
  • 形成一个新表

导入包

#导入用到的包
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, Dropout
from numpy import concatenate
from math import sqrt
from keras.callbacks import ReduceLROnPlateau

数据清洗(详细注释):

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)   #删除no列(无意义)
    # manually specify column names
    dataset.columns = ['pollution', 'dew', 'temp', 'press', 'wnd_dir', 'wnd_spd', 'snow', 'rain']  #给每列换个好理解的名
    dataset.index.name = 'date'   #行的索引名为name,解析得到的日期格式列会作为DataFrame的第一列。则此时会以新生成的time_date列而不是name作为Index。因此保险的方法是指定列名,如index_col = 'name'

    # mark all NA values with 0
    dataset['pollution'].fillna(0, inplace=True)
    # drop the first 24 hours
    dataset = dataset[24:]     #删除第一天的数据,取列表24行之后的数据
    # summarize first 5 rows
    print(dataset.head(5))
    # save to file
    dataset.to_csv('pollution.csv')

形成的数据格式:
lstm对时间数据的预测作用(多变量对多变量预测)_第2张图片

2.对每列数据进行绘图观测(5年数据),除了日期。
绘图代码:

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

结果:
lstm对时间数据的预测作用(多变量对多变量预测)_第3张图片

3.LSTM的时序数据划分

  • 使用series_to_supervised(data, n_in=5, n_out=5, dropnan=True) :确定模型的输入输出(n_in=5表示输入5个时刻的数据,n_out=5表示预测后五个时刻的数据。如果n_in=1, n_out=1表示用前一刻数据预测后一刻数据)
  • 首先将数据划分为:
    代码:
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      #shape:(43791, 64)

提取需要的输入:

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()        #先构造encoder,通过fit函数传入需要编码的数据,在内部生成对应的key-value,然后encoder 用于需要转化的数据,用transform函数
    values[:, 4] = encoder.fit_transform(values[:, 4])  #先将object对象转化为硬编码
    # 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 删掉我们不想要的输出,留下40,48,56,64,72(5天的数据)
    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     #reframed.shape:(43791, 45)

生成结果:
lstm对时间数据的预测作用(多变量对多变量预测)_第4张图片

  • 下列对上面代码做个简单的介绍:
    例如用前个三个时刻的数据,预测后三个时刻的数据,使用shift操作生成对应的表,然后将它们拼接,删除空值的行,再后面对于不需要预测的列,进行删除,输出需要的信息。
    lstm对时间数据的预测作用(多变量对多变量预测)_第5张图片

4.模型的训练集和测试集的划分:

  • 首先对数据进行归一化处理,返回归一化的函数,后面好使用反归一化。归一化不关注数据的大小长度信息,关注数据与数据之间的联系,相对属性
  • 三年前的数据为训练集,三年后的为测试集
  • 调整模型数据的输入维度(lstm需要输入三维数据)
def train_test(reframed):
    # split into train and test sets
    
    values = reframed.values
    scaler11 = MinMaxScaler(feature_range=(0, 1))
    values = scaler11.fit_transform(values)
    
    n_train_hours = 365 * 24*3   # 
    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:]
    train_X = train_X.reshape((train_X.shape[0], 1, train_X.shape[1]))   #train_X开始2维,(26280,40)变成三维(26280, 1, 40)train_y:(26280,5)
    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, scaler11

结果为:
lstm对时间数据的预测作用(多变量对多变量预测)_第6张图片

5.模型构造

  • 使用三层的lstm,隐藏层有50个神经元,输出层5个神经元,输入变量是5个时间步的特征,损失函数采用Mean Absolute Error(MAE),优化算法采用Adam,模型采用100个epochs,每个batch的大小为72
  • 设置validation_data(验证集)参数,记录训练集和测试集的损失,并在完成训练和测试后绘制损失图。
  • 使用keras中的回调函数ReduceLROnPlateau()在训练过程中缩小学习率。
  • 注意:需要将预测结果和部分测试集数据组合然后进行比例反转(invert the scaling)。
      为什么进行比例反转,是因为我们将原始数据进行了预处理(使用归一化MinMaxScaler()操作),此时的误差损失计算是在处理之后的数据上进行的,为了计算在原始比例上的误差需要将数据转化为原始数据形式。反转时的数据维度一定要和原来的大小(shape)完全相同,否则就会报错。
      通过以上处理之后,再结合RMSE(均方根误差)计算损失。
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')
    
#     model.fit返回一个对象,对象有history属性,记录训练的数据的损失
    history = model.fit(train_X, train_y, epochs=100, batch_size=72, validation_data=(test_X, test_y), verbose=2,
                        shuffle=False, callbacks=[reduce_lr])
    # plot history
    pyplot.plot(history.history['loss'], label='train1')
    pyplot.plot(history.history['val_loss'], label='test2345')
    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]))
    inv_yhat = concatenate((test_X,yhat), axis=1)
    # invert scaling for forecast
    #inv_yhat = concatenate((yhat, test_X[:, 5:]), axis=1)
    inv_yhat = scaler.inverse_transform(inv_yhat)
    print(type(inv_yhat))
    print(inv_yhat[-1])
    print(inv_yhat[-1][-5:])
    print(inv_yhat[-1:,-5:])  #多一个括号

    inv_yhat = inv_yhat[:, -5:]
    print(inv_yhat[-1])
    print(inv_yhat[-1:])
    # invert scaling for actual
    inv1_yhat1 = concatenate((test_X,test_y), axis=1)
    inv_y = scaler.inverse_transform(inv1_yhat1)
    inv_y = inv_y[:, -5:]
    # calculate RMSE
    
    rmse = sqrt(mean_squared_error(inv_y, inv_yhat))
    print('Test RMSE: %.3f' % rmse)
    return inv_yhat, inv_y

实验结果:
lstm对时间数据的预测作用(多变量对多变量预测)_第7张图片
总结:

  • 误差相对于使用上一个时刻的数据预测下一个时刻的数据有些大,但是该模型的应用范围比较广。
  • 如果会多变量数据的预测就可以很简单预测单变量数据
  • 该模型还可继续进行不断的改进,满足各自的需求

参考:
1.使用Keras进行LSTM实战

你可能感兴趣的:(自然语言处理,python,算法)