【问题标题】:PyTorch: Predicting future values with LSTMPyTorch:使用 LSTM 预测未来值
【发布时间】:2021-02-04 15:13:24
【问题描述】:

我目前正在构建一个 LSTM 模型,以使用 PyTorch 预测时间序列数据。我使用滞后特征将前面的 n 步作为输入来训练网络。我将数据分成三组,即train-validation-test split,并使用前两组来训练模型。我的验证函数从验证数据集中获取数据,并通过使用 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

类 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

【问题讨论】:

    标签: machine-learning deep-learning time-series pytorch lstm


    【解决方案1】:

    我终于找到了一种基于早期观察结果的预测值来预测值的方法。正如预期的那样,预测在短期内相当准确,从长期来看会稍微变差。未来的预测会随着时间的推移而偏离并不奇怪,因为它们不再依赖于实际值。回顾我的结果和我对该主题的讨论,以下是我的收获:

    • 在实际案例中,可以在预测的每一步(无论是每周、每天还是每小时)检索真实值并将其输入到模型中,以便可以使用来自前一步。因此,根据测试集中的实际值来测试性能,可能在一定程度上反映了定期维护的模型的真实性能。

    • 但是,为了预测长期的未来值,如果您愿意,您需要进行多个单步预测或跨越您希望预测的时间段的多步预测。

    • 根据模型预测的值进行多个单步预测会在短期内产生合理的结果。随着预测周期的增加,预测变得不那么准确,因此不太适合预测的目的。

    • 要进行多个单步预测并在每次预测后更新输入,我们必须逐个遍历数据集,就好像我们在测试集上进行 for 循环一样。毫不奇怪,这使我们失去了矩阵运算和小批量训练为我们提供的所有计算优势。

    • 另一种方法是预测值序列,而不是仅预测下一个值,例如使用具有多对多或 seq-to-seq 结构的多维输出的 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
    

    以下行将张量的第二维中的值移动 1,使张量 [[[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)

    最近,我决定把我学到的东西和我想早点知道的东西放在一起。如果你想看看,你可以在下面找到链接。我希望你会发现它有用。如果您同意或不同意我在上面所做的任何评论,请随时发表评论或与我联系。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-22
      • 1970-01-01
      • 1970-01-01
      • 2020-08-24
      • 2019-12-01
      • 2020-06-02
      • 2017-12-07
      • 2018-11-29
      相关资源
      最近更新 更多