时间序列预测模型比较分析:SARIMAX、RNN、LSTM、Prophet 及 Transformer
时间序列预测根据过去的模式预测未来事件。我们的目标是找出最佳预测方法,因为不同的技术在特定条件下表现出色。本文章将探讨各种方法在不同数据集上的表现,为你在任何情况下选择和微调正确的预测方法提供真知灼见。
我们将探讨五种主要方法:
-
SARIMAX:检测重复出现的模式并考虑各种外部影响。
-
RNN: 分析顺序数据,适用于按时间顺序排列的信息。
-
LSTM:通过长时间保留数据来增强 RNN。
-
Prophet: 由 Facebook 开发,对数据缺口和重大趋势变化具有强大的抵抗能力。
-
Transformer: 利用自我关注,有效识别复杂模式。
我们在不同类型的数据上对这些方法进行了测试:
-
电力生产: 分析行业能源消耗的长期趋势。电力生产Kaggle 数据集[1]
-
洗发水销售: 监测洗发水销量的变化。洗发水销售Kaggle 数据集[2]
-
犯罪数据: 洞察公共安全和城市生活。犯罪数据数据集[3]
-
碰撞报告: 加强对车祸和道路安全的了解。碰撞报告数据集[4]
-
模拟数据: 利用自定义生成的时间序列对 RNN 和 LSTM 模型进行深入比较。
我们在各种数据集上应用这些模型(每个模型都有特定的配置),以评估它们的准确性、可靠性和速度。
一般方法
我们是这样做的
数据检查:
应用 “有马方法 ”的原则,我们首先评估静态趋势,并通过自相关函数(ACF)和部分自相关函数(PACF)分析检测模式。这一阶段有助于识别数据中反复出现的模式,从而为我们选择最合适的模型及其参数配置提供信息。
参数优化:
对于每种算法和数据集,我们都会精心选择参数,以提高预测的准确性。
模型训练和验证:
我们使用数据集训练每种算法,并留出一部分数据专门用于验证。
性能评估:
平均绝对百分比误差(MAPE)被用作所有验证数据的标准指标,以便进行直接比较。
这种方法有助于我们了解每种算法的优缺点,指导我们针对具体的时间序列预测挑战选择合适的算法。
时间序列识别概述
我们使用 “电力生产 ”数据集探索时间序列识别。我们的目标是计算该数据的月平均值,从而发现准确预测所必需的关键趋势和模式。
下面的 Python 脚本将对月度数据汇总进行处理和可视化:
import matplotlib.pyplot as plt
import pandas as pdData = pd.read_csv("Electric_Production.csv")
monthly_data = data.IPG2211A2N.resample('M').mean()
data.IPG2211A2N.resample('M').mean().plot()
plt.show()
图 (1): 月度电力生产数据可视化
该图(图 1)揭示了电力生产的潜在季节性变化,这对预测工作至关重要。
为了评估数据集的静态性并探索自回归和移动平均成分,我们进行了统计测试和分析,如 Dickey-Fuller 检验、自相关函数 (ACF) 和偏自相关函数 (PACF):
from statsmodels.tsa.stattools import adfuller, acf, pacf# Dickey-Fuller test
result = adfuller(monthly_data)
print(f'ADF Statistic: {result[0]}')
print(f'p-value: {result[1]}')# ACF and PACF
acf_values = acf(monthly_data, nlags=20)
pacf_values = pacf(monthly_data, nlags=20, method='ols')# Visualization
plt.figure(figsize=(10, 5))
plt.subplot(121)
plt.plot(acf_values)
plt.title('Autocorrelation Function')
plt.subplot(122)
plt.plot(pacf_values)
plt.title('Partial Autocorrelation Function')
plt.tight_layout()
plt.show()
图 (2): ACF 和 PACF 图
这些分析提供了以下结果:
-
Dickey-Fuller 检验: 显示非平稳性,表明需要进行差分。
-
ACF 和 PACF: 突显了自回归和移动平均成分的必要性,建议使用初始 ARIMA(1,1,0)模型。
这些发现使我们能够准确地准备和评估各种数据集,以便进行时间序列预测。
按照这一既定方法,我们对其他数据集的分析结果总结如下:
表(1) 数据识别结果
预测技术
利用 SARIMAX 进行时间序列预测
确定数据集的 ARIMA 模型参数后,我们就可以使用 SARIMAX 进行预测了。SARIMAX 代表带有外生因素的季节性自回归整合移动平均模型,通过纳入季节周期和外部变量的潜在影响来增强 ARIMA。
下面是将 SARIMAX 应用于 “电力生产” 数据集的 Python 示例,其中保留了最近三个月的数据以进行验证:
import pandas as pd
from statsmodels.tsa.statespace.sarimax import SARIMAX
from statsmodels.tsa.seasonal import seasonal_decomposedata=pd.read_csv("Electric_Production.csv")
monthly_data = data.IPG2211A2N.resample('M').mean().reset_index()# 将数据分成训练集和测试集
train_data = monthly_data['IPG2211A2N'][:-3]
test_data = monthly_data['IPG2211A2N'][-3:]# 拟合 ARIMA(1,1,1) 模型
model = ARIMA(train_data, order=(1,1,1))
model_fit = model.fit()# 预测过去三个月
forecast = model_fit.forecast(steps=3)
# 计算实际值和预测值之间的 MAPE
mape = mean_absolute_percentage_error(test_data, forecast)
print(f"Forecast: {forecast}")
print(f"Actual: {test_data}")
print(f"MAPE: {mape}")
我们使用平均绝对百分比误差 (MAPE) 作为评估预测准确性的指标。同样的方法可应用于其他数据集,从而确保我们预测方法的一致性。
利用 RNN 进行时间序列预测
递归神经网络(RNN)在时间序列预测中表现突出,因为它能通过隐藏状态动态记忆过去的信息。这与 SARIMAX 的线性建模方法形成鲜明对比,因为 RNN 可以以非线性方式对数据进行建模,使其在理解和预测随时间变化的模式方面表现出色。
下面,我们使用 RNN 对 “电力生产” 数据集进行预测,特别是针对过去三个月的数据进行验证,以评估模型的预测性能。
import torch
import torch.nn as nn
import numpy as np
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_absolute_percentage_error
from statsmodels.tsa.seasonal import seasonal_decompose
from torch.utils.data import DataLoader, TensorDataset# 假设 `monthly_data` 是包含时间序列列 'IPG2211A2N' 的 DataFrame
tmdata = monthly_data['IPG2211A2N']
data = tmdata.values.reshape(-1, 1) # 分解以去除季节性成分
result = seasonal_decompose(tmdata, model='additive', period=12)
deseasonalized = tmdata - result.seasonal# 对数据进行归一化处理
scaler = MinMaxScaler(feature_range=(-1, 1))
data_normalized = scaler.fit_transform(deseasonalized.values.reshape(-1, 1))# 将数据转换为序列
def create_sequences(data, seq_length):xs, ys = [], []for i in range(len(data)-seq_length-1):x = data[i:(i+seq_length)]y = data[i+seq_length]xs.append(x)ys.append(y)return np.array(xs), np.array(ys)seq_length = 12
X, y = create_sequences(data_normalized, seq_length)
X_train, X_test = X[:-3], X[-3-seq_length:-seq_length]
y_train, y_test = y[:-3], y[-3:]# Convert to PyTorch tensors
X_train = torch.FloatTensor(X_train)
y_train = torch.FloatTensor(y_train).view(-1)
X_test = torch.FloatTensor(X_test)
y_test = torch.FloatTensor(y_test).view(-1)class SimpleRNN(nn.Module):def __init__(self, input_size=1, hidden_layer_size=100, output_size=1):super(SimpleRNN, self).__init__()self.hidden_layer_size = hidden_layer_sizeself.rnn = nn.RNN(input_size, hidden_layer_size)self.linear = nn.Linear(hidden_layer_size, output_size)def forward(self, input_seq):rnn_out, _ = self.rnn(input_seq.view(len(input_seq) ,1, -1))predictions = self.linear(rnn_out.view(len(input_seq), -1))return predictions[-1]model = SimpleRNN()
criterion = nn.MSELoss()optimizer = torch.optim.Adam(model.parameters(), lr=0.018)
epochs = 220
for i in range(epochs):for seq, labels in zip(X_train, y_train):optimizer.zero_grad()y_pred = model(seq)single_loss = criterion(y_pred, labels.unsqueeze(-1))single_loss.backward()optimizer.step()if i % 10 == 0:print(f'epoch: {i:3} loss: {single_loss.item():10.8f}')model.eval()
preds_list = []
with torch.no_grad():for i in range(len(X_test)):seq = X_test[i].view(-1, 1, 1) # Reshape to (seq_len, batch_size=1, features=1)pred = model(seq)preds_list.append(pred.item())# 将预测列表转换为用于反向缩放的 numpy 数组
preds_array = np.array(preds_list).reshape(-1, 1)
preds_inverse = scaler.inverse_transform(preds_array)# 对实际测试标签进行反变换
y_test_inverse = scaler.inverse_transform(y_test.numpy().reshape(-1, 1))# 计算 MAPE
mape = np.mean(np.abs((y_test_inverse - preds_inverse) / y_test_inverse)) * 100
print(f'MAPE: {mape}%')
以下是简化代码概述:
-
预处理: 调整季节性并规范化数据,为 RNN 做准备。
-
序列准备: 将数据转换为序列,用于 RNN 训练,模拟时间依赖关系。
-
RNN 架构: 利用 RNN 层进行时间处理,利用线性层进行预测。
-
训练: 在历时上迭代以最小化损失,并通过反向传播更新模型。
-
预测: 应用所学模式预测测试集的未来值。
-
反变换: 将预测值调整回原始比例,以供评估。
-
准确度评估: 采用 MAPE 量化模型的预测准确性。
利用 LSTM 进行时间序列预测
长短期记忆(LSTM)网络旨在通过更好地管理长期依赖性和异常值来改进递归神经网络(RNN)。然而,LSTM 的真正功效因数据集而异,这凸显了经验验证的必要性。在即将对 “电力生产 ”等数据集进行的研究中,我们的目标是对不同算法进行数据驱动的评估,纯粹关注经验结果而非理论预期。以下是为 LSTM 量身定制的 Python 代码示例:
class LSTMModel(nn.Module):def __init__(self, input_size=1, hidden_layer_size=100, output_size=1):super(LSTMModel, self).__init__()self.lstm = nn.LSTM(input_size, hidden_layer_size)self.linear = nn.Linear(hidden_layer_size, output_size)def forward(self, input_seq):lstm_out, _ = self.lstm(input_seq.view(len(input_seq), 1, -1))predictions = self.linear(lstm_out.view(len(input_seq), -1))return predictions[-1]model = LSTMModel()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
epochs = 180
# 接下来是训练循环和预测生成。
与 RNN 相比,LSTM 在编码方面的显著差异在于模型的构建。LSTM 包括nn.LSTM
层,而不是 nn.RNN
,专门应对学习时间序列数据中长期依赖关系的挑战。这一结构调整是在实际预测任务中发挥 LSTM 理论优势的关键。
使用 Facebook Prophet 进行时间序列预测
从理论上讲,Facebook Prophet 旨在通过处理非线性趋势、季节性变化和节假日影响来改进预测。它对不同业务预测要求的适应性,尤其是管理缺失数据和适应趋势突变的能力,受到了关注。据报道,Prophet
特别适用于以下业务预测环境:
-
数据跨度从每月到每年,具有显著的季节性模式。
-
可预测发生的有影响的节假日。
-
诱发趋势变化的外部因素,如产品发布。
-
接近饱和的增长趋势。
以下是将 Prophet
应用于 “电力生产” 数据集的 Python 代码:
start_date = '2020-01-01'
dates = pd.date_range(start=start_date, periods=len(monthly_data['IPG2211A2N']), freq='M')
df_prophet = pd.DataFrame(data={'ds': dates, 'y': monthly_data['IPG2211A2N'].values})# 用额外的季节性成分初始化Prophet模型
model = Prophet(yearly_seasonality=True, seasonality_prior_scale=0.2)# 添加月度季节性的示例
model.add_seasonality(name='monthly', period=30.5, fourier_order=8) # 用数据帧拟合模型
模型.fit(df_prophet[:-3]) # 排除最后 3 个月进行验证# 为未来预测创建 DataFrame,包括最后 3 个月
future = model.make_future_dataframe(periods=3, freq='M')# 使用模型进行预测
预测 = model.predict(future)# 重点对最近 3 个月进行验证
forecast_last_3_months = forecast['yhat'][-3:].values# 过去 3 个月的实际值
actual_last_3_months = df_prophet['y'][-3:].values# 计算实际值和预测值之间的 MAPE
mape = mean_absolute_percentage_error(actual_last_3_months, forecast_last_3_months)print(f"Forecasted Values: {forecast_last_3_months}")
print(f"Actual Values: {actual_last_3_months}")
print(f"MAPE: {mape}")
影响预测的关键参数:
-
seasonality_prior_scale (0.2) :调整季节性的灵活性。较低的值会收紧季节性,有助于形成一致的模式,同时防止过度拟合。
-
fourier_order (8) :设置季节性模型的复杂性。较高的值可以捕捉到详细的波动,但也有过度拟合的风险。根据数据的季节性变化进行选择。
-
model.add_seasonality中的period(30.5) :定义添加季节性的周期长度,这里近似于一个月,根据数据的季节性频率定制。
使用注意力转换器进行时间序列预测
注意力转换器最初是为自然语言处理(NLP)而开发的,现在正被用于时间序列预测。从理论上讲,注意力变换器能够权衡不同输入数据点的重要性,从而能够细致入微地理解复杂的时间关系,这与 RNN 的顺序处理有所不同。
在时间序列中使用变形器还处于尝试阶段,目的是利用其注意力机制来预测不同数据集的趋势和季节性模式。
在此,我提供了将 Transformers 应用于 “电力生产” 数据集的代码:
class PositionalEncoding(nn.Module):def __init__(self, d_model, dropout=0.1, max_len=5000):super(PositionalEncoding, self).__init__()self.dropout = nn.Dropout(p=dropout)position = torch.arange(0, max_len).unsqueeze(1)div_term = torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000.0) / d_model))pe = torch.zeros(max_len, 1, d_model)pe[:, 0, 0::2] = torch.sin(position * div_term)pe[:, 0, 1::2] = torch.cos(position * div_term)self.register_buffer('pe', pe)def forward(self, x):x = x + self.pe[:x.size(0)]return self.dropout(x)class TransformerModel(nn.Module):def __init__(self, input_dim, d_model, nhead, num_layers, dim_feedforward, dropout=0.1):super(TransformerModel, self).__init__()self.model_type = 'Transformer'self.pos_encoder = PositionalEncoding(d_model, dropout)encoder_layers = nn.TransformerEncoderLayer(d_model, nhead, dim_feedforward, dropout)self.transformer_encoder = nn.TransformerEncoder(encoder_layers, num_layers)self.encoder = nn.Linear(input_dim, d_model)self.d_model = d_modelself.decoder = nn.Linear(d_model, 1)def forward(self, src):src = self.encoder(src) * math.sqrt(self.d_model)src = self.pos_encoder(src)output = self.transformer_encoder(src)output = self.decoder(output)return outputmodel = TransformerModel(input_dim=1, d_model=64, nhead=4, num_layers=4, dim_feedforward=256, dropout=0.2)
train_data = TensorDataset(torch.FloatTensor(X_train), torch.FloatTensor(y_train))
train_loader = DataLoader(train_data, batch_size=16, shuffle=False)optimizer = optim.Adam(model.parameters(), lr=0.001)
criterion = nn.MSELoss()# Training loop
for epoch in range(120):model.train()total_loss = 0for batch, (data, targets) in enumerate(train_loader):optimizer.zero_grad()data = data.permute(1, 0, 2) # Reshape for the transformer [seq_len, batch_size, features]output = model(data)loss = criterion(output.view(-1), targets)loss.backward()optimizer.step()total_loss += loss.item()if epoch % 10 == 0:print(f'Epoch: {epoch}, Loss: {total_loss / len(train_loader)}')# 预测和重新分级预测
model.eval()
preds = []
with torch.no_grad():for seq in torch.FloatTensor(X_test):seq = seq.unsqueeze(1) # Shape to [seq_len, batch_size=1, features]pred = model(seq)pred_last = pred[-1, :, :].squeeze().item()preds.append(pred_last)preds_inverse = scaler.inverse_transform(np.array(preds).reshape(-1, 1))
seasonal_component = result.seasonal[-len(preds):].values.reshape(-1, 1)
final_predictions = preds_inverse + seasonal_componenty_test_actual = monthly_data['IPG2211A2N'][-len(preds):].values.reshape(-1, 1)
mape = np.mean(np.abs((y_test_actual - final_predictions) / y_test_actual)) * 100
print(f'MAPE: {mape}%')
以下是使用 Transformers 进行时间序列预测的关键步骤:
-
位置编码: 为数据添加唯一的位置信息,帮助模型掌握序列顺序,而无需像 RNN 那样逐步处理数据。
-
transformer模型设置: 包括处理数据(编码器)和生成预测(解码器)的层,并根据时间序列的具体情况进行调整。
-
模型训练: 包括在训练数据上优化模型,以减少预测误差。
-
预测: 应用训练有素的模型预测未来值,重塑输入数据以符合转换器的预期。
-
重新添加季节性: 将之前去除的季节性模式与预测重新整合,以准确反映现实世界的相关性。
-
模型评估: 使用平均绝对百分比误差 (MAPE) 评估模型的性能。
结果与比较
在研究中,我们对一系列时间序列数据集采用了五种不同的预测方法。根据每个数据集的独特属性和初步验证的结果,对这些方法进行了微调。对于 RNN 和 LSTM 等神经网络方法,我们进行了多次迭代以减少训练过程的随机性,并采用平均 MAPE 来确定其性能。
所提供的 MAPE(平均绝对误差)值表总结了每种预测方法在不同数据集上的有效表现:
表(2) 模型性能 (MAPE)
研究结果摘要
-
RNN 和 Prophet: 这两种方法在准确性和一致性方面表现突出。RNN 尤其擅长处理复杂的数据集,而 Prophet 则在处理季节性较强的数据集时表现出色。
-
Transformers: 尽管 Transformers 在 NLP 中取得了成功,但在时间序列预测中的效果有限,这表明在应用于这一领域时需要进一步改进。
-
SARIMAX 和 Prophet: 这些方法适用于具有确定的 ARIMA 结构或较小规模的数据集,因为它们不太容易出现与神经网络相关的过拟合风险。
-
周期/周期处理: 准确识别和整合周期/循环信息至关重要。与需要手动输入的 RNN 和 LSTM 不同,Prophet 和 SARIMAX 能够更好地自动考虑季节效应。
-
参数重要性: 对于 Prophet,季节性先验尺度、周期和傅立叶阶数等参数对其季节性建模能力有很大影响。在 RNN 和 LSTM 中,学习率和 dropout=0.2 对实现有效学习和泛化起着至关重要的作用。
-
计算速度: Prophet 和 SARIMAX 的计算速度更快,与基于神经网络的方法(如 RNN 和 LSTM)相比具有明显优势,后者需要更多时间对大型数据集进行训练。
其他见解
LSTM vs. RNN: 我们预期 LSTM 的表现会比 RNN 好,但我们的测试并没有清楚地表明这一点。这让我们再次思考 LSTM 是否更擅长处理长期模式和意外数据。即使在调整设置后,RNN 也能很好地适应,尤其是当我们改变学习率时,这对 LSTM 的影响并不大。
这些令人惊讶的结果让我很好奇,因此我创建了一个特殊的数据集,以测试 LSTM 在它们应该擅长的情况下是否比 RNN 更好。这个数据集具有长期重复模式和突发偏差(异常值),为 LSTM 的能力提供了严格的测试。
数据生成代码:
# 生成时间序列:用 70 个数据点模拟时间序列
n_points = 70
t = np.arange(n_points)
# 正弦波 + 线性趋势 + 噪音
data = 2 * np.sin(t / 8) + 0.1 * t + np.random.normal(0, 0.5, n_points)
data[-5:] += np.array([3, -1, 2, -1, 2]) # Introducing outliers
在这个生成的数据集上运行 RNN 和 LSTM 模型后,我得到了以下结果:
LSTM MAPE:23.32%
RNN MAPE:33.65%
这些结果符合理论预期,即 LSTM 可增强长期依赖性管理和异常值处理能力。该实验强调了根据数据的具体特征选择正确模型的重要性。未来的研究将寻求用真实世界的数据场景来证实这些发现。
写在最后
在对 SARIMAX、RNN、LSTM、Prophet 和 Transformer 等时间序列预测方法的比较分析中,我们发现方法的选择对不同数据集的预测准确性有显著影响。RNN 和 Prophet 分别在处理复杂数据和季节性数据方面表现出色,而 LSTM 并不总是如预期那样优于 RNN。Transformers 在其 NLP 大本营以外的领域举步维艰,这表明它们需要适应时间序列预测。
我们的研究结果强调了选择适当模型和微调参数以适应当前数据集具体特征的重要性。尽管预计 LSTM 在管理长期依赖性方面具有优势,但我们的研究结果主张采用更细致的方法来选择模型。这一探索不仅挑战了现有的假设,还为进一步研究开辟了道路,从而在不断发展的时间序列分析领域提高预测精度。