bka*_*glu 3 machine-learning time-series deep-learning lstm pytorch
我目前正在致力于使用 PyTorch 构建 LSTM 模型来预测时间序列数据。我使用滞后特征将前面的 n 个步骤作为输入来训练网络。我将数据分为三组,即训练-验证-测试分割,并使用前两组来训练模型。我的验证函数从验证数据集中获取数据,并使用 DataLoaders 和 TensorDataset 类将其传递到 LSTM 模型来计算预测值。最初,我得到了相当好的结果,R2 值在 0.85-0.95 范围内。
然而,我对这个验证功能是否也适合测试我的模型的性能有一种不安的感觉。因为该函数现在从 DataLoader 获取实际的 X 值(即时滞特征)来预测 y^ 值(即预测的目标值),而不是使用预测的 y^ 值作为下一次预测中的特征。这种情况似乎与现实相去甚远,模型不知道之前时间步长的实际值,特别是如果您预测较长时间段(例如 3-6 个月)的时间序列数据。
我目前对解决这个问题并定义一个函数来根据模型的值而不是测试集中的实际值来预测未来值感到有点困惑。我有以下函数predict,它可以进行一步预测,但我还没有真正弄清楚如何使用 DataLoader 预测整个测试数据集。
def predict(self, x):
# convert row to data
x = x.to(device)
# make prediction
yhat = self.model(x)
# retrieve numpy array
yhat = yhat.to(device).detach().numpy()
return yhat
Run Code Online (Sandbox Code Playgroud)
您可以在下面找到我如何拆分和加载数据集、LSTM 模型的构造函数以及验证函数。如果您需要更多信息,请随时与我联系。
分割和加载数据集
def create_tensor_datasets(X_train_arr, X_val_arr, X_test_arr, y_train_arr, y_val_arr, y_test_arr):
train_features = torch.Tensor(X_train_arr)
train_targets = torch.Tensor(y_train_arr)
val_features = torch.Tensor(X_val_arr)
val_targets = torch.Tensor(y_val_arr)
test_features = torch.Tensor(X_test_arr)
test_targets = torch.Tensor(y_test_arr)
train = TensorDataset(train_features, train_targets)
val = TensorDataset(val_features, val_targets)
test = TensorDataset(test_features, test_targets)
return train, val, test
def load_tensor_datasets(train, val, test, batch_size=64, shuffle=False, drop_last=True):
train_loader = DataLoader(train, batch_size=batch_size, shuffle=shuffle, drop_last=drop_last)
val_loader = DataLoader(val, batch_size=batch_size, shuffle=shuffle, drop_last=drop_last)
test_loader = DataLoader(test, batch_size=batch_size, shuffle=shuffle, drop_last=drop_last)
return train_loader, val_loader, test_loader
Run Code Online (Sandbox Code Playgroud)
LSTM类
class LSTMModel(nn.Module):
def __init__(self, input_dim, hidden_dim, layer_dim, output_dim, dropout_prob):
super(LSTMModel, self).__init__()
self.hidden_dim = hidden_dim
self.layer_dim = layer_dim
self.lstm = nn.LSTM(
input_dim, hidden_dim, layer_dim, batch_first=True, dropout=dropout_prob
)
self.fc = nn.Linear(hidden_dim, output_dim)
def forward(self, x, future=False):
h0 = torch.zeros(self.layer_dim, x.size(0), self.hidden_dim).requires_grad_()
c0 = torch.zeros(self.layer_dim, x.size(0), self.hidden_dim).requires_grad_()
out, (hn, cn) = self.lstm(x, (h0.detach(), c0.detach()))
out = out[:, -1, :]
out = self.fc(out)
return out
Run Code Online (Sandbox Code Playgroud)
验证(在培训师课程中定义)
def validation(self, val_loader, batch_size, n_features):
with torch.no_grad():
predictions = []
values = []
for x_val, y_val in val_loader:
x_val = x_val.view([batch_size, -1, n_features]).to(device)
y_val = y_val.to(device)
self.model.eval()
yhat = self.model(x_val)
predictions.append(yhat.cpu().detach().numpy())
values.append(y_val.cpu().detach().numpy())
return predictions, values
Run Code Online (Sandbox Code Playgroud)
我终于找到了一种根据早期观察的预测值来预测值的方法。正如预期的那样,短期内的预测相当准确,但长期来看略有恶化。随着时间的推移,未来的预测会出现偏差,这并不奇怪,因为它们不再依赖于实际值。反思我的结果以及我对该主题的讨论,以下是我的要点:
\n在现实生活中,可以在预测的每个步骤(每周、每天或每小时)检索真实值并将其输入模型,以便可以使用上一步的实际值来预测下一步。因此,根据测试集的实际值测试性能可能会在一定程度上反映定期维护的模型的真实性能。
\n然而,为了预测长期的未来值,如果愿意的话,您需要进行多个一步预测或多步预测,跨越您希望预测的时间段。
\n根据模型预测值进行多个一步预测会在短期内产生合理的结果。随着预测周期的增加,预测的准确性会降低,因此不太适合预测的目的。
\n为了进行多个单步预测并在每次预测后更新输入,我们必须逐一处理数据集,就像我们在测试集上进行 for 循环一样。毫不奇怪,这使我们失去了矩阵运算和小批量训练为我们提供的所有计算优势。
\n另一种方法是预测值序列,而不是仅预测下一个值,例如使用具有多对多或 seq-to-seq 结构的多维输出的 RNN。它们可能更难训练,并且在对不同时间段进行预测时灵活性较差。编码器-解码器结构可能对解决这个问题很有用,尽管我自己还没有实现它。
\n您可以找到我的函数的代码,该函数n_steps根据数据集的最后一行X(时滞特征)和y(目标值)预测下一个。为了迭代数据集中的每一行,我将设置batch_size为 1 和n_features滞后观察的数量。
def forecast(self, X, y, batch_size=1, n_features=1, n_steps=100):\n predictions = []\n X = torch.roll(X, shifts=1, dims=2)\n X[..., -1, 0] = y.item(0)\n with torch.no_grad():\n self.model.eval()\n for _ in range(n_steps):\n X = X.view([batch_size, -1, n_features]).to(device)\n yhat = self.model(X)\n yhat = yhat.to(device).detach().numpy()\n X = torch.roll(X, shifts=1, dims=2)\n X[..., -1, 0] = yhat.item(0)\n predictions.append(yhat)\n\n return predictions\nRun Code Online (Sandbox Code Playgroud)\n下面的代码行将张量第二维中的值移一,使张量[[[x1, x2, x3, ... , xn ]]]变为[[[xn, x1, x2, ... , x(n-1)]]]。
X = torch.roll(X, shifts=1, dims=2)
并且,下面的行从 3d 张量的最后一个维度中选择第一个元素,并将该项目设置为存储在 NumPy ndarray (yhat) 中的预测值[[xn+1]]。然后,新的输入张量变为[[[x(n+1), x1, x2, ... , x(n-1)]]]
X[..., -1, 0] = yhat.item(0)
最近,我决定将我学到的东西和我之前想知道的东西放在一起。如果您想查看,可以在下面找到链接。我希望你会发现它很有用。如果您同意或不同意我上面的任何言论,请随时发表评论或与我联系。
\n\n| 归档时间: |
|
| 查看次数: |
2184 次 |
| 最近记录: |