PyTorch:使用LSTM预测未来值

3
我正在使用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

以下是我分割和加载数据集的方法、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

Class 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

验证(在训练器类中定义)

    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
1个回答

5
我终于找到了一种方法,可以根据早期观察到的预测值来预测价值。正如预期的那样,在短期内,预测结果相当准确,但随着时间的推移,在长期内稍微变差了一些。未来的预测结果随着时间的推移,产生偏差,因为它们不再依赖实际价值。回顾我的结果和讨论的内容,以下是我的要点:
  • 在现实情况下,可以检索实际价值并将其馈入模型,以便在每个预测步骤(无论是每周、每天还是每小时)中使用前一步的实际值来预测下一步。因此,基于测试集中的实际值测试性能可能在一定程度上反映定期维护的模型的实际性能。
  • 然而,要对长期预测未来价值进行预测,您需要进行多次单步预测或跨越您希望预测的时间段的多步预测。
  • 根据模型预测值进行多次单步预测可以在短期内产生可行的结果。随着预测期间的增加,预测结果变得不那么准确,因此不适合于预测目的。
  • 为了进行多次单步预测并在每次预测之后更新输入,我们必须逐一遍历数据集,就像在测试集上进行for循环一样。毫不奇怪,这使我们失去了矩阵运算和小批量训练所带来的所有计算优势。
  • 另一种选择是预测值序列,而不仅仅是下一个值,例如使用具有多对多或序列到序列结构的RNN进行多维输出。它们可能更难以训练,并且对于不同时间段的预测可能不太灵活。编码器-解码器结构可能证明是解决此问题的有用方法,但我自己尚未实施此方法。
您可以在我的函数代码中找到基于数据集X(时间滞后特征)和y(目标值)的最后一行预测下n_steps的代码。要迭代每行数据,可以将batch_size设置为1,将n_features设置为滞后观察数量。
    def forecast(self, X, y, batch_size=1, n_features=1, n_steps=100):
        predictions = []
        X = torch.roll(X, shifts=1, dims=2)
        X[..., -1, 0] = y.item(0)
        with torch.no_grad():
            self.model.eval()
            for _ in range(n_steps):
                X = X.view([batch_size, -1, n_features]).to(device)
                yhat = self.model(X)
                yhat = yhat.to(device).detach().numpy()
                X = torch.roll(X, shifts=1, dims=2)
                X[..., -1, 0] = yhat.item(0)
                predictions.append(yhat)

        return predictions

以下代码将张量第二维的值向右移动一个单位,使得张量[[[x1, x2, x3, ... , xn ]]]变为[[[xn, x1, x2, ... , x(n-1)]]]X = torch.roll(X, shifts=1, dims=2) 下一行代码从三维张量的最后一维中选择第一个元素,并将该项设置为存储在NumPy ndarray中的预测值(yhat)[[xn+1]]。然后,新的输入张量变为[[[x(n+1), x1, x2, ... , x(n-1)]]]X[..., -1, 0] = yhat.item(0) 最近,我决定把我学到的东西和我希望早些知道的东西结合起来。如果您想看一看,可以在下面找到链接。我希望你会觉得它有用。如果您对我上面所做的任何评论或意见表示认同或不认同,请随时与我联系。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接