当前位置: 首页 > 科技观察

使用LSTM预测销量(Python代码)

时间:2023-03-14 20:06:44 科技观察

经常会遇到一些需要预测的场景,比如预测品牌销量,预测产品销量。今天给大家分享一波使用LSTM进行端到端时间序列预测的完整代码和详解。我们先来了解两个话题:什么是时间序列分析?什么是LSTM?时间序列分析:时间序列表示按时间顺序排列的一系列数据。它可以是秒、分钟、小时、天、周、月、年。未来的数据将取决于其先前的值。在现实世界的案例中,我们主要有两种类型的时间序列分析:单变量时间序列多变量时间序列对于单变量时间序列数据,我们将使用单列进行预测。如我们所见,只有一列,因此即将到来的未来值将仅取决于其先前的值。但在多变量时间序列数据的情况下,会有不同类型的特征值,目标数据将取决于这些特征。正如您在图片中看到的那样,多变量中将有多个列来对目标值进行预测。(上图中的“count”为目标值)在上述数据中,count不仅依赖于它之前的值,还依赖于其他特征。因此,为了预测即将到来的计数值,我们必须考虑包括目标列在内的所有列来对目标值进行预测。在进行多变量时间序列分析时必须记住一件事,我们需要使用多个特征来预测当前目标,让我们通过一个例子来理解:在训练时,如果我们使用5列[feature1,feature2,feature3,feature4,target]来训练模型,我们需要为即将到来的预测日提供4列[feature1,feature2,feature3,feature4]。LSTM不打算在本文中详细讨论LSTM。所以只提供一些简单的描述,如果你对LSTM了解不多,可以参考我们之前发表的文章。LSTM基本上是一种能够处理长期依赖性的循环神经网络。假设你正在看电影。所以当电影中发生任何事情时,你已经知道之前发生了什么,并且可以理解因为过去发生的事情而发生了新的事情。RNNs以同样的方式工作,它们记住过去的信息并用它来处理当前的输入。RNN的问题在于,由于梯度消失,它们无法记住长期依赖关系。因此,lstm就是为了避免长期依赖问题而设计的。现在我们讨论时间序列预测和LSTM理论部分。让我们开始编码吧。让我们首先导入进行预测所需的库:importnumpyasnpiimportpandasasppdffrommatplotlibimportpyplotaspltfromtensorflow.keras.modelsimportSequentialfromtensorflow.keras.layersimportLSTMfromtensorflow.keras.layersimportDense,DropoutfromsklearnimportMinMaxScalerfromkeras。wrappers.scikit_learnimportKerasRegressorfromsklearn.model_selectionimportGridSearchCV加载数据并检查输出: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,这是最重要的部分,正确阅读每一步。defcreateXY(dataset,n_past):dataX=[]dataY=[]foriinrange(n_past,len(dataset)):dataX.append(dataset[i-n_past:i,0:dataset.shape[1]])dataY.append(dataset[i,0])returnnp.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循环的每一部分:fortraining,dataset=df_for_training_scaled,n_past=30wheni=30:data_X.addend(df_for_training_scaled[i-n_past:i,0:df_for_training.shape[1]])fromn_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行开始取空(因为在预测中,我们只需要空列,所以列范围只有0,这表示空栏)。第一次将df_for_training_scaled[30,0]值存储在dataY列表中。因此,包含5列的前30行存储在dataX中,只有具有空列的第31行存储在dataY中。然后我们将dataX和dataY列表转换为数组,这些数组在LSTM中以数组格式进行训练。让我们看看形状。print("trainXShape--",trainX.shape)print("trainYShape--",trainY.shape)(4132,30,5)(4132,)print("testXShape--",testX.shape)print("testYShape--",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中。现在让我们训练模型,我使用gridsearchCV进行一些超参数调整以找到基本模型。defbuild_model(优化器):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)返回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模型中的period和unit。在第一个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("\nPredictionShape-",prediction.shape)testY和prediction长度相同。现在可以将testY与预测进行比较。但是我们首先对数据进行了缩放,所以首先我们必须进行一些缩放过程。scaler.inverse_transform(prediction)报错,这是因为缩放数据时,我们每行有5列,现在我们只有1列是目标列。所以我们要改变shape来使用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]现在让我们看看预测值andOriginalvalue:print("PredValues--",pred)print("\nOriginalValues--",original)最后画一个图来比较我们的pred和原始数据。plt.plot(original,color='red',label='真实股价')plt.plot(pred,color='blue',label='预测股价')plt.title('股价预测')plt.xlabel('Time')plt.ylabel('GoogleStockPrice')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”、“AdjClose”列即将到来的30个值来对“Open”列进行预测。df_30_days_future=pd.read_csv("test.csv",parse_dates=["Date"],index_col=[0])df_30_days_future去掉“Open”列后,在使用模型进行预测前需要做如下操作:scaling数据,因为删除了'Open'列,在缩放之前,添加了一个所有值为“0”的Open列。缩放后,将未来数据中的“Open”列值替换为“nan”,现在追加30天前和30天新(其中最后30个“open”值是nan)df_30_days_future["Open"]=0df_30_days_future=df_30_days_future[[“开盘价”、“高价”、“低价”、“收盘价”、“调整收盘价”]]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_calray)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=30foriinrange(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]])数据=np.array(data_x)prediction=my_model.predict(data_x)all_data.append(prediction)full_df.iloc[i,0]=prediction对于第一次预测,有30个之前的值,当for循环第一次运行时检查前30个值并预测第31个“打开”数据。当第二个for循环将尝试运行时,它将跳过第一行并尝试获取接下来的30个值[1:31]。这里会报错,因为Open栏的最后一行是“nan”,所以每次都需要把“nan”换成prediction。最后,需要对预测进行逆变换: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://github.com/sksujan58/Multivariate-time-series-forecasting-using-LSTM