LSTM 无法学习时间序列的长期模式

问题描述 投票:0回答:1

我想用张量流keras中的简单lstm模型来预测简单的时间序列。但模型似乎没有学习数据集的长期模式。

时间序列数据是航空公司乘客数据集:https://www.kaggle.com/datasets/rakannimer/air-passengers/data

数据被标准化为 0 到 1 之间。接下来,对于每个时间步长/目标,生成输入时前 30 个元素的时间序列(从数据集的 300 个元素开始)

然后将这些输入和目标输入到具有 1 个 lstm 层和 1 个密集层的简单模型中。

从此模型中,对 80 步进行了一步预测。从最后一班火车时间序列开始。 (每个新的预测都用作时间序列的最后一个元素,而第一个元素被删除)

这会产生一个模型,该模型显示数据系列的形状,但不显示其斜率。

我使用了以下代码,删除了对航空公司乘客数据上部的验证以保持代码的重点:

def main():
    airline_passengers = AirlinePassengersDataSet()
    train_scaler = MinMaxScaler(feature_range=(0, 1))
    scaled_train = train_scaler.fit_transform(airline_passengers.get_train_data().reshape(-1, 1))

    lookback: int = 30
    train_timeseries, train_targets = TimeSeriesGenerator(scaled_train, lookback).create_timeseries()

    model = LSTMModel(lookback)
    model.compile(optimizer=tf.keras.optimizers.Adam(0.001), loss='mean_squared_error')
    model.fit(train_timeseries, train_targets, epochs=1000, batch_size=1)

    start_index = -1
    start_value = train_timeseries[start_index]
    start_value_reshaped = start_value.reshape(1, start_value.shape[0], start_value.shape[1])
    number_of_predictions = 80
    prediction_results = one_step_ahead_forecast(model, start_value_reshaped, number_of_predictions)

    prediction = pd.DataFrame()
    prediction["one_step_prediction"] = train_scaler.inverse_transform([prediction_results]).flatten()
    prediction.index += airline_passengers.threshold + start_index

    plt.plot(airline_passengers.data["Passengers"], color="red", label="dataset")
    plt.plot(airline_passengers.get_train_data(), color="green", label="training")
    plt.plot(prediction["one_step_prediction"], color="orange", label="one_step_prediction")
    plt.title("airline passengers prediction LSTM")
    plt.xlabel("Time[Month]")
    plt.ylabel("Passengers[x1000]")
    plt.xticks(range(0, 200, 20))
    plt.yticks(range(0, 1000, 100))
    plt.legend(loc="upper left")
    plt.savefig("./img/airlinePassengers_keras_lstm.png")
    plt.show()

class AirlinePassengersDataSet:
    def __init__(self):
        self.data = pd.read_csv("../../AirlinePassengers.csv", sep=";")
        self.threshold = 107

    def get_train_data(self):
        data = self.data.loc[0:self.threshold, "Passengers"].reset_index(drop=True)
        return data.to_numpy()

    def get_test_data(self):
        data = self.data.loc[self.threshold:142, "Passengers"].reset_index(drop=True)
        return data.to_numpy()


class TimeSeriesGenerator:
    def __init__(self, data, lookback):
        self.data = data
        self.lookback = lookback

    def create_timeseries(self):
        inputs, targets = list(), list()
        for element in range(self.lookback, len(self.data)-1):
            inputs.append(self.__get_timeseries(element))
            targets.append(self.__get_targets(element))
        return np.array(inputs), np.array(targets)

    def __get_targets(self, element):
        return self.data[element]

    def __get_timeseries(self, element):
        return self.data[element-self.lookback: element]


class LSTMModel(tf.keras.Model):
    def __init__(self, lookback):
        super().__init__()
        self.lstm = tf.keras.layers.LSTM(units=50,
                                   activation="tanh",
                                   recurrent_activation="sigmoid",
                                   input_shape=(lookback, 1),
                                   kernel_initializer="glorot_uniform",
                                   recurrent_initializer="orthogonal",
                                   bias_initializer="zeros",
                                   )
        self.dense1 = tf.keras.layers.Dense(units=1,
                                            activation="relu",
                                            kernel_initializer=tf.keras.initializers.RandomNormal(stddev=0.01),
                                            bias_initializer=tf.keras.initializers.Zeros())

    def call(self, inputs):
        x = self.lstm(inputs)
        x = self.dense1(x)
        return x


def one_step_ahead_forecast(model, current_value, number_of_predictions):
    one_step_ahead_forecast = list()
    for element in range(0, number_of_predictions):
        prediction = model.predict(current_value)
        one_step_ahead_forecast.append(prediction[0][0])
        current_value = np.delete(current_value, 0)
        current_value = np.append(current_value, prediction)
        current_value = current_value.reshape(1, current_value.shape[0], 1)
    return one_step_ahead_forecast


if __name__ == "__main__":
    main()

结果图如下所示:

我尝试了以下操作,结果没有改变:

  • 增加时间序列的大小(最多 50)
  • 使用不同的模型方法,例如堆叠 lstm 和双向 lstm
  • epoch、损失函数和优化器的各种变化

有趣的是,通过简单的密集层模型,我可以获得更好的结果。但我想知道为什么 lstm 没有学习数据集的长期模式?

keras deep-learning lstm
1个回答
0
投票

一些尝试的想法。

数据标准化为 0 到 1 之间。

LSTM 配置有 tanh 激活,同时目标缩放到 [0, 1] 范围。尝试使用

MinMaxScaler((-1, 1))
对齐它们;这样网络的输出和目标都覆盖范围 (-1, 1)`。

提前一步预测 80 步。

这可能适用于几个步骤,但错误会累积起来。您可以将窗口移动 1 步,然后使用该新窗口进行下一个预测,而不是向网络提供之前的预测以进行下一个预测。

增长趋势意味着 LSTM 最终将看到超出其训练观察范围的样本(因为验证数据继续增长超出训练部分)。通常您至少希望消除数据的趋势。您可以稍后添加趋势。

去趋势看起来像这样:从训练数据中学习线性/二次趋势,并在训练 LSTM 之前用它来去趋势训练数据。在推理时,使用步骤 1 中已学习的系数对验证数据进行去趋势化。然后通过模型运行去趋势化的验证集。最后,重新添加趋势(或者为了简单起见,最初只查看去趋势结果)。

self.lstm = tf.keras.layers.LSTM(单位=50,

尝试较小的模型尺寸,甚至减少到 2 个左右单位。 LSTM 具有大量参数,很容易过度拟合小数据集。

© www.soinside.com 2019 - 2024. All rights reserved.