我想用张量流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()
结果图如下所示:
我尝试了以下操作,结果没有改变:
有趣的是,通过简单的密集层模型,我可以获得更好的结果。但我想知道为什么 lstm 没有学习数据集的长期模式?
一些尝试的想法。
数据标准化为 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 具有大量参数,很容易过度拟合小数据集。