在本文中咱们将应用深度学习办法 (LSTM) 执行多元工夫序列预测。

咱们先来理解两个主题——

  • 什么是工夫序列剖析?
  • 什么是 LSTM?

工夫序列剖析:工夫序列示意基于工夫程序的一系列数据。它能够是秒、分钟、小时、天、周、月、年。将来的数据将取决于它以前的值。

在事实世界的案例中,咱们次要有两种类型的工夫序列剖析——

  • 单变量工夫序列
  • 多元工夫序列

对于单变量工夫序列数据,咱们将应用单列进行预测。

正如咱们所见,只有一列,因而行将到来的将来值将仅取决于它之前的值。

然而在多元工夫序列数据的状况下,将有不同类型的特征值并且指标数据将依赖于这些特色。

正如在图片中看到的,在多元变量中将有多个列来对目标值进行预测。(上图中“count”为目标值)

在下面的数据中,count不仅取决于它以前的值,还取决于其余特色。因而,要预测行将到来的count值,咱们必须思考包含指标列在内的所有列来对目标值进行预测。

在执行多元工夫序列剖析时必须记住一件事,咱们须要应用多个特色预测以后的指标,让咱们通过一个例子来了解 -

在训练时,如果咱们应用 5 列 [feature1, feature2, feature3, feature4, target] 来训练模型,咱们须要为行将到来的预测日提供 4 列 [feature1, feature2, feature3, feature4]。

LSTM

本文中不打算具体探讨LSTM。所以只提供一些简略的形容,如果你对LSTM没有太多的理解,能够参考咱们以前公布的文章。

LSTM基本上是一个循环神经网络,可能解决长期依赖关系。

假如你在看一部电影。所以当电影中产生任何状况时,你都曾经晓得之前产生了什么,并且能够了解因为过来产生的事件所以才会有新的状况产生。RNN也是以同样的形式工作,它们记住过来的信息并应用它来解决以后的输出。RNN的问题是,因为突变隐没,它们不能记住长期依赖关系。因而为了防止长期依赖问题设计了lstm。

当初咱们探讨了工夫序列预测和LSTM实践局部。让咱们开始编码。

让咱们首先导入进行预测所需的库

import numpy as npimport pandas as pdfrom matplotlib import pyplot as pltfrom tensorflow.keras.models import Sequentialfrom tensorflow.keras.layers import LSTMfrom tensorflow.keras.layers import Dense, Dropoutfrom sklearn.preprocessing import MinMaxScalerfrom keras.wrappers.scikit_learn import KerasRegressorfrom sklearn.model_selection import GridSearchCV

加载数据,并查看输入-

df=pd.read_csv("train.csv",parse_dates=["Date"],index_col=[0])df.head()

df.tail()

当初让咱们花点工夫看看数据:csv文件中蕴含了谷歌从2001-01-25到2021-09-29的股票数据,数据是依照天数频率的。

[如果您违心,您能够将频率转换为“B”[工作日]或“D”,因为咱们不会应用日期,我只是放弃它的现状。]

这里咱们试图预测“Open”列的将来值,因而“Open”是这里的指标列

让咱们看一下数据的形态

df.shape(5203,5)

当初让咱们进行训练测试拆分。这里咱们不能打乱数据,因为在工夫序列中必须是程序的。

test_split=round(len(df)*0.20)df_for_training=df[:-1041]df_for_testing=df[-1041:]print(df_for_training.shape)print(df_for_testing.shape)(4162, 5)(1041, 5)

能够留神到数据范畴十分大,并且它们没有在雷同的范畴内缩放,因而为了防止预测谬误,让咱们先应用MinMaxScaler缩放数据。(也能够应用StandardScaler)

scaler = MinMaxScaler(feature_range=(0,1))df_for_training_scaled = scaler.fit_transform(df_for_training)df_for_testing_scaled=scaler.transform(df_for_testing)df_for_training_scaled

将数据拆分为X和Y,这是最重要的局部,正确浏览每一个步骤。

def createXY(dataset,n_past):    dataX = []    dataY = []    for i in range(n_past, len(dataset)):            dataX.append(dataset[i - n_past:i, 0:dataset.shape[1]])            dataY.append(dataset[i,0])    return np.array(dataX),np.array(dataY)trainX,trainY=createXY(df_for_training_scaled,30)testX,testY=createXY(df_for_testing_scaled,30)

让咱们看看下面的代码中做了什么:

N_past是咱们在预测下一个目标值时将在过来查看的步骤数。

这里应用30,意味着将应用过来的30个值(包含指标列在内的所有个性)来预测第31个目标值。

因而,在trainX中咱们会有所有的特征值,而在trainY中咱们只有目标值。

让咱们合成for循环的每一部分

对于训练,dataset = df_for_training_scaled, n_past=30

当i= 30:

data_X.addend (df_for_training_scaled[i - n_past:i, 0:df_for_training.shape[1]])

从n_past开始的范畴是30,所以第一次数据范畴将是-[30 - 30,30,0:5] 相当于 [0:30,0:5]

因而在dataX列表中,df_for_training_scaled[0:30,0:5]数组将第一次呈现。

当初, dataY.append(df_for_training_scaled[i,0])

i = 30,所以它将只取第30行开始的open(因为在预测中,咱们只须要open列,所以列范畴仅为0,示意open列)。

第一次在dataY列表中存储df_for_training_scaled[30,0]值。

所以蕴含5列的前30行存储在dataX中,只有open列的第31行存储在dataY中。而后咱们将dataX和dataY列表转换为数组,它们以数组格局在LSTM中进行训练。

咱们来看看形态。

print("trainX Shape-- ",trainX.shape)print("trainY Shape-- ",trainY.shape)(4132, 30, 5)(4132,)print("testX Shape-- ",testX.shape)print("testY Shape-- ",testY.shape)(1011, 30, 5)(1011,)

4132 是 trainX 中可用的数组总数,每个数组共有 30 行和 5 列, 在每个数组的 trainY 中,咱们都有下一个目标值来训练模型。

让咱们看一下蕴含来自 trainX 的 (30,5) 数据的数组之一 和 trainX 数组的 trainY 值

print("trainX[0]-- \n",trainX[0])print("trainY[0]-- ",trainY[0])

如果查看 trainX[1] 值,会发现到它与 trainX[0] 中的数据雷同(第一列除外),因为咱们将看到前 30 个来预测第 31 列,在第一次预测之后它会主动挪动 到第 2 列并取下一个 30 值来预测下一个目标值。

让咱们用一种简略的格局来解释这所有——

trainX — — →trainY[0 : 30,0:5] → [30,0][1:31, 0:5] → [31,0][2:32,0:5] →[32,0]

像这样,每个数据都将保留在 trainX 和 trainY 中

当初让咱们训练模型,我应用 girdsearchCV 进行一些超参数调整以找到根底模型。

def build_model(optimizer):    grid_model = Sequential()    grid_model.add(LSTM(50,return_sequences=True,input_shape=(30,5)))    grid_model.add(LSTM(50))    grid_model.add(Dropout(0.2))    grid_model.add(Dense(1))grid_model.compile(loss = 'mse',optimizer = optimizer)    return grid_modelgrid_model = KerasRegressor(build_fn=build_model,verbose=1,validation_data=(testX,testY))parameters = {'batch_size' : [16,20],              'epochs' : [8,10],              'optimizer' : ['adam','Adadelta'] }grid_search  = GridSearchCV(estimator = grid_model,                            param_grid = parameters,                            cv = 2)

如果你想为你的模型做更多的超参数调整,也能够增加更多的层。然而如果数据集十分大倡议减少 LSTM 模型中的期间和单位。

在第一个 LSTM 层中看到输出形态为 (30,5)。它来自 trainX 形态。(trainX.shape[1],trainX.shape[2]) → (30,5)

当初让咱们将模型拟合到 trainX 和 trainY 数据中。

grid_search = grid_search.fit(trainX,trainY)

因为进行了超参数搜寻,所以这将须要一些工夫来运行。

你能够看到损失会像这样缩小——

当初让咱们查看模型的最佳参数。

grid_search.best_params_{‘batch_size’: 20, ‘epochs’: 10, ‘optimizer’: ‘adam’}

将最佳模型保留在 my_model 变量中。

my_model=grid_search.best_estimator_.model

当初能够用测试数据集测试模型。

prediction=my_model.predict(testX)print("prediction\n", prediction)print("\nPrediction Shape-",prediction.shape)

testY 和 prediction 的长度是一样的。当初能够将 testY 与预测进行比拟。

然而咱们一开始就对数据进行了缩放,所以首先咱们必须做一些逆缩放过程。

scaler.inverse_transform(prediction)

报错了,这是因为在缩放数据时,咱们每行有 5 列,当初咱们只有 1 列是指标列。

所以咱们必须扭转形态来应用 inverse_transform

prediction_copies_array = np.repeat(prediction,5, axis=-1)

5 列值是类似的,它只是将单个预测列复制了 4 次。所以当初咱们有 5 列雷同的值 。

prediction_copies_array.shape(1011,5)

这样就能够应用 inverse_transform 函数。

pred=scaler.inverse_transform(np.reshape(prediction_copies_array,(len(prediction),5)))[:,0]

然而逆变换后的第一列是咱们须要的,所以咱们在最初应用了 → [:,0]。

当初将这个 pred 值与 testY 进行比拟,然而 testY 也是按比例缩放的,也须要应用与上述雷同的代码进行逆变换。

original_copies_array = np.repeat(testY,5, axis=-1)original=scaler.inverse_transform(np.reshape(original_copies_array,(len(testY),5)))[:,0]

当初让咱们看一下预测值和原始值 →

print("Pred Values-- " ,pred)print("\nOriginal Values-- " ,original)

最初绘制一个图来比照咱们的 pred 和原始数据。

plt.plot(original, color = 'red', label = 'Real Stock Price')plt.plot(pred, color = 'blue', label = 'Predicted Stock Price')plt.title('Stock Price Prediction')plt.xlabel('Time')plt.ylabel('Google Stock Price')plt.legend()plt.show()

看样子还不错,到目前为止,咱们训练了模型并用测试值查看了该模型。当初让咱们预测一些将来值。

从主 df 数据集中获取咱们在开始时加载的最初 30 个值[为什么是 30?因为这是咱们想要的过来值的数量,来预测第 31 个值]

df_30_days_past=df.iloc[-30:,:]df_30_days_past.tail()

能够看到有包含指标列(“Open”)在内的所有列。当初让咱们预测将来的 30 个值。

在多元工夫序列预测中,须要通过应用不同的特色来预测单列,所以在进行预测时咱们须要应用特征值(指标列除外)来进行行将到来的预测。

这里咱们须要“High”、“Low”、“Close”、“Adj Close”列的行将到来的 30 个值来对“Open”列进行预测。

df_30_days_future=pd.read_csv("test.csv",parse_dates=["Date"],index_col=[0])df_30_days_future

剔除“Open”列后,应用模型进行预测之前还须要做以下的操作:

缩放数据,因为删除了‘Open’列,在缩放它之前,增加一个所有值都为“0”的Open列。

缩放后,将将来数据中的“Open”列值替换为“nan”

当初附加 30 天旧值和 30 天新值(其中最初 30 个“关上”值是 nan)

df_30_days_future["Open"]=0df_30_days_future=df_30_days_future[["Open","High","Low","Close","Adj Close"]]old_scaled_array=scaler.transform(df_30_days_past)new_scaled_array=scaler.transform(df_30_days_future)new_scaled_df=pd.DataFrame(new_scaled_array)new_scaled_df.iloc[:,0]=np.nanfull_df=pd.concat([pd.DataFrame(old_scaled_array),new_scaled_df]).reset_index().drop(["index"],axis=1)

full_df 形态是 (60,5),最初第一列有 30 个 nan 值。

要进行预测必须再次应用 for 循环,咱们在拆分 trainX 和 trainY 中的数据时所做的。然而这次咱们只有 X,没有 Y 值

full_df_scaled_array=full_df.valuesall_data=[]time_step=30for i in range(time_step,len(full_df_scaled_array)):    data_x=[]    data_x.append(     full_df_scaled_array[i-time_step :i , 0:full_df_scaled_array.shape[1]])    data_x=np.array(data_x)    prediction=my_model.predict(data_x)    all_data.append(prediction)    full_df.iloc[i,0]=prediction

对于第一个预测,有之前的 30 个值,当 for 循环第一次运行时它会查看前 30 个值并预测第 31 个“Open”数据。

当第二个 for 循环将尝试运行时,它将跳过第一行并尝试获取下 30 个值 [1:31] 。这里会报错谬误因为Open列最初一行是 “nan”,所以须要每次都用预测替换“nan”。

最初还须要对预测进行逆变换→

new_array=np.array(all_data)new_array=new_array.reshape(-1,1)prediction_copies_array = np.repeat(new_array,5, axis=-1)y_pred_future_30_days = scaler.inverse_transform(np.reshape(prediction_copies_array,(len(new_array),5)))[:,0]print(y_pred_future_30_days)

这样一个残缺的流程就曾经跑通了。

如果你想看残缺的代码,能够在这里查看:

https://www.overfit.cn/post/1...

作者:Sksujanislam