原文towardsdatascience.com/time-series-arima-vs-sarima-vs-lstm-hands-on-tutorial-bd5630298da3.https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/9740fd65d260cffe20a725bc3a1dc5e8.png图片由 Djim Loic 在 Unsplash 提供在这篇文章中我们将深入探讨时间序列预测的世界。预测未来的时间价值对各种企业来说都很有价值。例如需求预测对在线零售商准备年终销售非常有帮助以确保有足够的库存来满足即将到来的购物需求。在金融领域股票交易者依赖复杂的预测模型来决定买卖哪些证券。在更基本的情况下我们依赖天气预报来决定是否携带雨伞或雨衣出门上班。这些都是时间序列预测在我们的生活中扮演重要角色的系统例子。在这篇文章中我们将讨论在这个领域中最常用的三种时间序列预测模型自回归积分滑动平均或 ARIMA季节性自回归积分滑动平均或 SARIMA长短期记忆或 LSTM。我们将通过实际操作来了解每个预测模型通过训练模型并使用每个训练模型生成预测来学习。然后我们将查看常用于评估序列数据如时间序列的指标最后可视化结果。在深入细节之前我将包括一个表格展示这三个方法之间的比较你可以将其作为未来的参考。如果你现在还认不出这个表格中的术语请不要担心。在我们通过这篇文章学习之后你将对时间序列预测术语更加熟练https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/6339d77b5c7354e0de44db6f1ec008af.pngARIMA、SARIMA 和 LSTMs 的比较现在已经介绍完毕让我们开始吧1. 数据集为了实现我们的示例我们将使用来自加州大学欧文分校机器学习存储库的开放源代码空气质量数据集该数据集在CC BY 4.0 许可下可用。该数据集包括一段时间内的每小时空气质量水平这使得它非常适合进行时间序列预测练习。我们的想法是看看我们根据过去的数据能够多好地预测一些空气质量水平。让我们从导入必要的库、加载数据和查看前 5 行开始。# import librariesimportpandasaspdimportnumpyasnp# load the dataset# https://archive.ics.uci.edu/dataset/360/airqualityair_qualitypd.read_csv(AirQualityUCI.csv,sep;,decimal,)air_quality.head()结果https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/05ffe63cbb2d696c39076a1314e618b0.png数据集概述现在让我们通过替换缺失值和删除一些列来清理数据。# replace missing values and drop unnecessary columnsair_quality.replace(-200,np.nan,inplaceTrue)air_quality.dropna(axis1,howall,inplaceTrue)air_quality.dropna(inplaceTrue)# replace periods with colons in the Time columnair_quality[Time]air_quality[Time].str.replace(.,:,regexFalse)# combine date and time into a single datetime columnair_quality[DateTime]pd.to_datetime(air_quality[Date] air_quality[Time],dayfirstTrue)# set DateTime as the indexair_quality.set_index(DateTime,inplaceTrue)air_quality.sort_index(inplaceTrue)# select target variable (e.g., NOx(GT))dataair_quality[NOx(GT)]# drop any remaining missing values in the target variabledata.dropna(inplaceTrue)接下来在我们开始预测之旅之前让我们先绘制数据并查看它。# import librariesimportmatplotlib.pyplotasplt%matplotlib inline# visualizeplt.figure(figsize(14,7))plt.plot(data.index,data.values)plt.title(Hourly NOx(GT) Levels)plt.xlabel(Date)plt.ylabel(NOx Concentration)plt.show()结果https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/17a4a17c3a501e61459ffe56dee0a535.png按日期可视化的空气质量数据最后我们将数据分为训练集和测试集就像我们会对任何机器学习任务做的那样。训练数据将被预测模型用来“学习”数据然后我们将使用未见的测试集来评估训练模型的表现。# break into train (80%) and testtrain_sizeint(len(data)*0.8)train,testdata.iloc[:train_size],data.iloc[train_size:]print(fTraining set size:{len(train)})print(fTesting set size:{len(test)})结果https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/8e51dc311f5f0a21390dce7d8cabfa9b.png现在我们对数据有了更多的了解我们可以继续进行预测任务。我们稍后会讨论数据模式所以现在不必担心这一点。2. 预测建模在本节中我们将开始使用 ARIMA、SARIMA 和 LSTM 进行建模。我们将分别在单独的章节中解释每一个以便更容易跟随。让我们从 ARIMA 开始2.1. ARIMA自回归积分滑动平均ARIMA是一种流行的时序预测方法归功于统计学领域它在简单的数据集中非常有帮助但也可以用于预测非平稳如下定义时间序列这使我们需要定义一些概念。让我们从什么是平稳数据开始。**平稳性**均值、方差和自相关等属性不会随时间变化。例如这意味着时间序列的平均值或均值、分布方差以及不同时间点的值之间的关系保持不变。例如在我家里我将恒温器设定在 70 华氏度。空调会根据房间温度开启和关闭试图将其保持在 70 度左右因此我们预计一天中我房间温度的变化会围绕 70 度波动并保持平均温度在这一点附近。我们有理由假设这个时间序列将是平稳的我们通过空调强制使其如此。**非平稳性**正如我们根据上面的定义所预期的对于非平稳时间序列相同的属性均值、方差和自相关会随时间变化。非平稳时间序列中可能有多种模式所以让我们定义这些模式**趋势**数据在一段时间内可以呈现上升或下降的轨迹或方向。想象一下如果我在一个冬天的一天回家室内温度是 30 度然后我将恒温器设置为 70 度。让我们进一步假设房间达到 70 度需要 5 个小时然后我们观察这 5 个小时的温度。我们会看到温度有一个上升的趋势或方向从 30 度开始到大约 70 度结束。让我们可视化这个情况# import librariesimportnumpyasnpimportmatplotlib.pyplotaspltfromsklearn.linear_modelimportLinearRegression# Parametersstart_temp30end_temp70hours5timenp.linspace(0,hours,num100)# 100 time points over 5 hours# Simulate temperature change with a linear upward trendtemperaturenp.linspace(start_temp,end_temp,num100)# add random noise to simulate slight fluctuations in the temperaturenoisenp.random.normal(0,1,temperature.shape)# mean0, standard deviation1temperature_with_noisetemperaturenoise# add noise to the temperature trend# linear regression for the trend linetime_reshapedtime.reshape(-1,1)modelLinearRegression().fit(time_reshaped,temperature_with_noise)trend_linemodel.predict(time_reshaped)# visualizeplt.figure(figsize(10,6))plt.plot(time,temperature_with_noise,labelRoom Temperature with Noise,colorb)plt.plot(time,trend_line,labelTrend Line,colorr,linestyle--)plt.title(Temperature Trend Over Time)plt.xlabel(Time (hours))plt.ylabel(Temperature (°F))plt.grid(True)plt.legend()plt.show()结果https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/a59436503004e991e6d36d01c2eb1fdd.png随时间温度上升趋势数据**季节性**意味着在几乎固定间隔内重复出现的模式。例如假设一个在线零售商在前三个季度的销售额相对稳定但销售额在第四季度往往会激增。那么这种每年重复出现的激增就代表了数据时间序列中的季节性。**方差**正如其名所示它描述的是数据随时间变化而变化或波动的情形。如上所述从解释中可以猜到预测平稳数据比非平稳数据更容易。非平稳时间序列的潜在模式不断变化因此我们需要识别它们然后进一步“转换”它们以提高我们预测模型的性能。将非平稳数据转换为平稳数据的一种方法称为“差分”。差分简单地说就是我们将前一个数据点或观察值从当前数据点中减去这可能会消除趋势等非平稳成分。为了确定时间序列中是否需要差分可以使用各种测试其中之一称为增强迪基-富勒ADF测试。出于本文的目的我们不需要理解其背后的数学但如果这个测试显示的 p 值大于 5%则表明数据是非平稳的因此需要差分。现在我们对平稳和非平稳数据以及如何将后者转换为前者有了更多的了解我们可以开始实施# import librariesfromstatsmodels.tsa.stattoolsimportadfuller# ADF testresultadfuller(train)print(ADF Statistic:,result[0])print(p-value:,result[1])# if p-value 0.05, the series is non-stationary and needs differencingifresult[1]0.05:print(needs differencing)else:print(time series is stationary)结果https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/c4a438225491e55723e46c535aeb19e6.png如上图所示p 值大于 5%因此数据是非平稳的正如我们根据之前创建的数据集图表所预期的。我们将使用一阶差分将非平稳数据转换为平稳数据然后进行验证以确保它是平稳的。# first-order differencingtrain_difftrain.diff().dropna()# review stationarity againresult_diffadfuller(train_diff)print(Differenced ADF Statistic:,result_diff[0])print(Differenced p-value:,result_diff[1])# if p-value 0.05, the series is non-stationary and needs differencingifresult[1]0.05:print(needs differencing)else:print(time series is stationary)结果https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/f9b9518c6c6e7f474ce4cf8ed35f088c.pngp 值仍然大于 5%因此我们可以继续进行二阶差分直到达到平稳状态。我想实现这一点以便我们可以看到并理解差分是如何工作的但在现实中我们可以使用自动实现 ARIMA 并找到最佳参数的库包括差分的顺序。所以让我定义在 ARIMA 模型中考虑的变量然后我们将实现自动方法。ARIMA 模型有以下参数p代表自回归项的数量ARIMA 中的“AR”部分。例如当 AR1 时当前值取决于立即前一个值。d代表使序列平稳ARIMA 中的“积分”或“I”部分所需的不同次数。所以如果 d1则只需要一阶差分。q代表移动平均项的数量ARIMA 中的“MA”部分。如果 MA1当前值仅取决于前一步的错误。这听起来优化起来相当手动所以让我们将其交给自动优化器# import librariesimportpmdarimaaspm# create the optimization model instancemodelpm.auto_arima(train,start_p1,start_q1,max_p5,max_q5,m1,# frequency of series (set to 1 for non-seasonal data)dNone,# let the model determine dseasonalFalse,# assuming no seasonalitytestadf,# use ADF test to find dtraceTrue,error_actionignore,suppress_warningsTrue,stepwiseTrue)print(model.summary())结果https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/7c5ded9e35ba8208d57ac8ea7751a30e.pngARIMA 模型摘要有趣的是优化器决定不需要差分就是一个足够好的选项。无论这是否是最佳方法我们先实现它然后再将性能与其他方法如 SARIMA 和 LSTM 进行比较。# import librariesfromstatsmodels.tsa.arima.modelimportARIMA# fit ARIMA modelarima_order(2,0,0)arima_modelARIMA(train,orderarima_order,trendc)arima_resultarima_model.fit()# forecastarima_forecastarima_result.forecast(stepslen(test))我们将在最后一起可视化所有结果所以让我们继续到 SARIMA。2.2. SARIMA季节性自回归积分移动平均Seasonal AutoRegressive Integrated Moving AverageSARIMA是 ARIMA 的扩展旨在更好地处理季节性时间序列数据。我们已经熟悉季节性并且 ARIMA 下定义的大多数概念在这里仍然适用。这里唯一的区别在于对于实现来说季节性有新的参数但这些参数也非常直观。我们定义 ARIMA 参数为“ARIMA(p, d, q)”。同样我们定义“SARIMA(p, d, q, P, D, Q, m)”其中 p、d 和 q 与之前相同代表模型的非平稳部分而 P、D 和 Q 控制季节性m 代表完整季节周期的步数——例如对于月度数据m12 将代表年度季节性。让我们分解数据看看是否有任何季节性。# import librariesfromstatsmodels.tsa.seasonalimportseasonal_decompose# decompose the datadecompositionseasonal_decompose(train,modeladditive,period24)# 24 hours in a daydecomposition.plot()plt.show()结果https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/4daa33d90f5bba2890387b7baf0df83b.png通过趋势、季节性和残差分解的数据我们可以看到一些季节性也许使用 SARIMA 会得到更好的预测。让我们来实现。fromstatsmodels.tsa.statespace.sarimaximportSARIMAX# fit SARIMA modelsarima_order(2,0,0)# this is what we had in our ARIMA modelseasonal_order(1,1,1,24)# daily seasonalitysarima_modelSARIMAX(train,ordersarima_order,seasonal_orderseasonal_order)sarima_resultsarima_model.fit(dispFalse)# Forecastsarima_forecastsarima_result.forecast(stepslen(test))作为下一步我们将使用 LSTM 进行预测然后比较整体预测结果。2.3. LSTM作为第三种方法我们将探讨长短期记忆LSTM网络这是一种循环神经网络RNN。这些 LSTM 在记住关于数据序列的信息方面非常有趣这在时间序列中非常有用。由于反向传播中梯度的方式传统的 RNN 方法无法记住太多关于过去的信息这被称为梯度消失问题导致这些网络更容易忘记但我们不需要深入了解这些细节。我们只需要知道在时间序列方面LSTM 可以是非常强大的预测工具。缺点是 LSTM 计算成本更高这在大型项目中是一个重要的考虑因素但对我们的小问题来说不是问题。让我们为我们的示例实现 LSTM我在代码中添加了注释以使其更容易理解然后最终查看结果以比较这三种方法。# import librariesfromsklearn.preprocessingimportMinMaxScaler# scale data to improve performancescalerMinMaxScaler(feature_range(0,1))scaled_datascaler.fit_transform(data.values.reshape(-1,1))# train and testing data splitsscaled_trainscaled_data[:train_size]scaled_testscaled_data[train_size:]# function to create sequences for lstm (takes in data and returns x and y numpy array sequences)defcreate_sequences(data,seq_length):x[]y[]foriinrange(len(data)-seq_length):x.append(data[i:iseq_length])y.append(data[iseq_length])returnnp.array(x),np.array(y)seq_length24# use past 24 hours to predict the next hourX_train,y_traincreate_sequences(scaled_train,seq_length)X_test,y_testcreate_sequences(scaled_test,seq_length)# import librariesfromtensorflow.keras.modelsimportSequentialfromtensorflow.keras.layersimportLSTM,Dense,Dropout# reshape input data to 3-dimensional that lstm expects (samples, timesteps, features)X_trainX_train.reshape((X_train.shape[0],X_train.shape[1],1))X_testX_test.reshape((X_test.shape[0],X_test.shape[1],1))# build lstm modelmodelSequential()model.add(LSTM(50,activationrelu,input_shape(seq_length,1)))model.add(Dropout(0.2))model.add(Dense(1))# compile the modelmodel.compile(optimizeradam,lossmean_squared_error)# train the modelhistorymodel.fit(X_train,y_train,epochs50,batch_size32,validation_data(X_test,y_test))# predictlstm_predictionsmodel.predict(X_test)lstm_predictionsscaler.inverse_transform(lstm_predictions)# adjust test values to align with predictionsadjusted_test_valuesdata.values[train_sizeseq_length:]3. 性能比较到目前为止我们已经准备好了所有预测无需更多延迟让我们可视化预测结果并进行讨论。# visualizeplt.figure(figsize(14,7))# actualsplt.plot(test.index,test.values,labelActual,colorblack)# ARIMA predictionsplt.plot(test.index,arima_forecast,labelARIMA Forecast,colorblue)# SARIMA predictionsplt.plot(test.index,sarima_forecast,labelSARIMA Forecast,colorgreen)# LSTM predictionsplt.plot(test.index[seq_length:],lstm_predictions,labelLSTM Forecast,colorred)plt.title(Actual vs. Predicted)plt.xlabel(Date)plt.ylabel(NOx Concentration)plt.legend()plt.show()结果https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/969d127936be7e04eccd5cd69a6fa4e4.png预测比较 – ARIMA, SARIMA 和 LSTM 与实际值我们可以看到“实际值”用黑色表示因此预测值越接近黑色线条这些预测就越好。正如我们所见ARIMA 在这种情况下表现出较差的预测能力随后 SARIMA 有所改进而 LSTM 则显示出与实际值最佳拟合。视觉检查这些结果总是好的但如果我们可以定义一些指标并对其进行定量测量那就更好了。为了做到这一点我们可以查看以下指标平均绝对误差 (MAE)衡量预测值与实际值之间误差的平均绝对值不考虑其方向。由于我们在计算中使用绝对值因此不考虑方向较低的值表示更好的模型性能。它可以描述如下https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/e9aad680f060b51644d837a011bff89b.png均方根误差 (RMSE)与 MAE 非常相似但给予较大误差更高的权重。它计算为实际值与预测值之间平方距离平均值的平方根在看到公式后更容易理解。由于实际值与预测值之间的差异是平方的因此与 MAE 相比它对异常值更敏感。与 MAE 类似较低的值更好。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/94eb4ccad7c636106a3ae5a55d1b4281.png平均绝对百分比误差 (MAPE)是预测值与实际值之间平均绝对百分比差异的计算。由于它是以另一种方式衡量实际值与预测值之间距离的方法因此较低的百分比更好。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/527bdade601d74c8c644782419314e00.png确定系数R-squared表示模型通过以下数学表示解释目标变量变异性程度的好坏。简而言之它计算残差的平方和这是实际值和预测值之间距离的度量然后将其除以总平方和即实际值的总方差。值范围从负无穷大到 1.0。接近 1.0 的值表示拟合度更好。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/28a41197638112d084d958363ace3a49.png现在我们已经了解了指标让我们计算我们三个预测模型的这些指标并分析结果。# import librariesfromsklearn.metricsimportmean_absolute_error,mean_squared_error,r2_score# a function to evaluate our forecast models using mae, rmse, mape and r²defevaluate_model(test,forecast):maemean_absolute_error(test,forecast)rmsenp.sqrt(mean_squared_error(test,forecast))mapenp.mean(np.abs((test-forecast)/test))*100r2r2_score(test,forecast)returnmae,rmse,mape,r2# evaluate our modelsarima_mae,arima_rmse,arima_mape,arima_r2evaluate_model(test.values,arima_forecast.values)sarima_mae,sarima_rmse,sarima_mape,sarima_r2evaluate_model(test.values,sarima_forecast.values)lstm_mae,lstm_rmse,lstm_mape,lstm_r2evaluate_model(adjusted_test_values,lstm_predictions.flatten())# results data frameresultspd.DataFrame({Model:[ARIMA,SARIMA,LSTM],MAE:[arima_mae,sarima_mae,lstm_mae],RMSE:[arima_rmse,sarima_rmse,lstm_rmse],MAPE:[arima_mape,sarima_mape,lstm_mape],R-squared:[arima_r2,sarima_r2,lstm_r2]})results结果https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/3c58f7292f155ca91b474a19c0fca652.png预测比较摘要现在我们可以比较这些模型了。全面来看正如我们之前从图中看到的那样与 ARIMA 和 SARIMA 相比LSTM 做得更好。最后让我们可视化残差即实际值和预测数据点在时间序列中的距离。正如其名所示我们希望残差尽可能接近零这意味着预测值与实际值更接近。# calculate residualsarima_residualstest.values-arima_forecast.values sarima_residualstest.values-sarima_forecast.values lstm_residualsadjusted_test_values-lstm_predictions.flatten()# visualizeplt.figure(figsize(14,7))plt.plot(test.index,arima_residuals,labelARIMA Residuals,colorblue)plt.plot(test.index,sarima_residuals,labelSARIMA Residuals,colorgreen)plt.plot(test.index[seq_length:],lstm_residuals,labelLSTM Residuals,colorred)plt.axhline(y0,colorblack,linestyle--)plt.title(Residuals of the Models)plt.xlabel(Date)plt.ylabel(Residuals)plt.legend()plt.show()结果https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/eec915a70c28807acb09d7ab13c44339.png预测残差随时间比较 - ARIMA、SARIMA、LSTM 与实际值4. 结论在这篇帖子中我们介绍了使用 ARIMA、SARIMA 和 LSTM 三种流行模型进行时间序列预测。在逐步实现这些模型的过程中我们讨论了相关的时间序列概念如趋势、季节性、平稳数据等。然后我们使用这些模型在测试集上进行预测并使用相关指标和可视化进行比较。感谢阅读如果你觉得这篇帖子有帮助请在 Medium 上关注我并订阅以接收我的最新帖子除非另有说明所有图像均由作者提供。