【问题标题】:Adding exogenous variables to my univariate LSTM model将外生变量添加到我的单变量 LSTM 模型中
【发布时间】:2020-12-17 17:56:57
【问题描述】:

我的数据框是按小时计算的(我的 df 的索引),我想预测 y。

> df.head()

          Date           y             
    2019-10-03 00:00:00 343   
    2019-10-03 01:00:00 101  
    2019-10-03 02:00:00 70  
    2019-10-03 03:00:00 67  
    2019-10-03 04:00:00 122  

我现在将导入库并训练模型:

  from keras.models import Sequential
  from keras.layers import Dense
  from keras.layers import LSTM
  from sklearn.preprocessing import MinMaxScaler
  min_max_scaler = MinMaxScaler()
  prediction_hours = 24
  df_train= df[:len(df)-prediction_hours]
  df_test= df[len(df)-prediction_hours:]
  print(df_train.head())
  print('/////////////////////////////////////////')
  print (df_test.head())
  training_set = df_train.values
  training_set = min_max_scaler.fit_transform(training_set)

  x_train = training_set[0:len(training_set)-1]
  y_train = training_set[1:len(training_set)]
  x_train = np.reshape(x_train, (len(x_train), 1, 1))
  num_units = 2
  activation_function = 'sigmoid'
  optimizer = 'adam'
  loss_function = 'mean_squared_error'
  batch_size = 10
  num_epochs = 100
  regressor = Sequential()
  regressor.add(LSTM(units = num_units, activation = activation_function, input_shape=(None, 1)))
  regressor.add(Dense(units = 1))
  regressor.compile(optimizer = optimizer, loss = loss_function)
  regressor.fit(x_train, y_train, batch_size = batch_size, epochs = num_epochs)

在训练之后,我实际上可以在我的测试数据上使用它:

 test_set = df_test.values
 inputs = np.reshape(test_set, (len(test_set), 1))
 inputs = min_max_scaler.transform(inputs)
 inputs = np.reshape(inputs, (len(inputs), 1, 1))
 predicted_y = regressor.predict(inputs)
 predicted_y = min_max_scaler.inverse_transform(predicted_y)

这是我得到的预测:

预测实际上相当不错:是不是好得令人难以置信?我做错什么了吗?我从一个 GitHub 实现一步一步地跟着实现。

我想添加一些外生变量,即v1、v2、v3。如果我的数据集现在看起来像这样带有新变量,

df.head()

          Date           y   v1   v2   v3          
    2019-10-03 00:00:00 343  4     6    10  
    2019-10-03 01:00:00 101  3     2    24
    2019-10-03 02:00:00 70   0     0    50  
    2019-10-03 03:00:00 67   0     4    54
    2019-10-03 04:00:00 122  3     3    23

如何在我的 LSTM 模型中包含这些变量 v1、v2 和 v3?多元 LSTM 的实现让我很困惑。

编辑以回答 Yoan 的建议

对于以日期为索引并具有 y、v1、v2 和 v3 列的数据框,我按照建议执行了以下操作:

  from keras.models import Sequential
  from keras.layers import Dense
  from keras.layers import LSTM
  from sklearn.preprocessing import MinMaxScaler
  min_max_scaler = MinMaxScaler()
  prediction_hours = 24
  df_train= df[:len(df)-prediction_hours]
  df_test= df[len(df)-prediction_hours:]
  print(df_train.head())
  print('/////////////////////////////////////////')
  print (df_test.head())
  training_set = df_train.values
  training_set = min_max_scaler.fit_transform(training_set)

  x_train = np.reshape(x_train, (len(x_train), 1, 4))
  y_train = training_set[0:len(training_set),1] #I've tried with 0:len.. and 
                                                                #for 1:len..
  
  num_units = 2
  activation_function = 'sigmoid'
  optimizer = 'adam'
  loss_function = 'mean_squared_error'
  batch_size = 10
  num_epochs = 100
  regressor = Sequential()
  regressor.add(LSTM(units = num_units, activation = activation_function, 
  input_shape=(None, 1,4)))
  regressor.add(Dense(units = 1))
  regressor.compile(optimizer = optimizer, loss = loss_function)
  regressor.fit(x_train, y_train, batch_size = batch_size, epochs = 
  num_epochs)

但我收到以下错误:

 only integer scalar arrays can be converted to a scalar index

【问题讨论】:

  • 考虑到您的模型建立在对如何为 RNN 准备数据的许多误解之上,我不知道从哪里开始。使用 LSTM 的主要目的是从数据的序列中学习。因此,您需要在给定输入数据的情况下以某种方式构建这些序列。我的建议是通读 tensorflow.org/tutorials/structured_data/… 或查看 Kaggle.com 上与 LSTM 相关的笔记本
  • 为了具体提示您的问题,您是否提供单个或多个变量作为输入应该没有区别,因为您可以像在代码中一样使用整个 DataFrame
  • 您的输出不太好,令人难以置信:它显示了所有点 除了 2 的错误率。似乎您的测试数据很少(只有 24 个样本),而您'将 LSTM 与 2 个单元一起用于只有一个点的序列 - 而不是序列(这是一种浪费,因为您根本没有使用 LSTM 内存并忘记了门)
  • x_train 进行两次整形正常吗?这可能就是您获得额外维度的原因
  • @YoanB.M.Sc 我只改造过一次。我在这里写错了。

标签: python keras lstm recurrent-neural-network forecasting


【解决方案1】:

将辅助特征与序列相结合

有多种使用 LSTM 处理辅助特征的方法,所有这些方法都受到您的数据包含的内容以及您希望如何对这些特征建模的启发。我将在下面使用一些虚拟代码讨论 4 种不同的场景和策略供您参考。

  1. 场景 1:如果您有简单的连续特征,只需将它们传递给 LSTM!
  2. 场景 2:如果您有多个标签编码序列,请将它们分别嵌入并编码到 LSTM 中,然后将它们连接起来以进行下游预测
  3. 如果你有一个标签编码序列和一些辅助特征,你可以——
    • 场景 3: 在嵌入后附加它们,然后将它们传递到 LSTM 中
    • 场景 4: 将它们附加到 LSTM 的输出并选择将它们传递给另一组 LSTM

场景一:

假设您有 4 个连续特征,并且所有这些特征都是连续的(不是以文本或分类形式编码的标签)。在这种情况下,LSTM 可以很好地直接处理这些特征。 LSTM 层需要(batch, sequence, features) 的形状,因此无需任何修改就可以很好地适应这种情况。

Features --> LSTM --> Process --> Predict

代码

from tensorflow.keras import layers, Model, utils

#Four continuous features
X = np.random.random((100,10,4))
Y = np.random.random((100,))

###Define model###
inp = layers.Input((10,4))

#LSTMs
x = layers.LSTM(8, return_sequences=True)(inp)
x = layers.LSTM(8)(x)
out = layers.Dense(1)(x)

model = Model(inp, out)
utils.plot_model(model, show_layer_names=False, show_shapes=True)

场景 2:

接下来,让我们假设另一个简单的案例。您有 2 个标签编码序列(例如文本)。正如人们所想的那样,您要做的就是通过为每个特征构建 LSTM,然后在下游预测任务之前将它们连接起来,分别创建顺序特征。

Sequence --> Embed --> LSTM -->|
                               * --> Append --> Process --> Predict
Sequence --> Embed --> LSTM -->|

代码

from tensorflow.keras import layers, Model, utils

#Two sequential, label encoded features
X = np.random.random((100,10,2))
Y = np.random.random((100,))

###Define model###
inp = layers.Input((10,2))
feature1 = layers.Lambda(lambda x: x[...,0])(inp)
feature2 = layers.Lambda(lambda x: x[...,1])(inp)

#Append embeddings features
x1 = layers.Embedding(1000, 5)(feature1)
x2 = layers.Embedding(1200, 7)(feature2)

#LSTMs
x1 = layers.LSTM(8, return_sequences=True)(x1)
x1 = layers.LSTM(8)(x1)

x2 = layers.LSTM(8, return_sequences=True)(x2)
x2 = layers.LSTM(8)(x2)

#Combine LSTM final states
x = layers.concatenate([x1,x2])
out = layers.Dense(1)(x)

model = Model(inp, out)
utils.plot_model(model, show_layer_names=False, show_shapes=True)

场景 3:

下一个场景,假设您正在使用标签编码序列(比如文本)这一功能。在将此功能传递给 LSTM 之前,您必须使用embedding 层将其编码为n 维向量。这将导致 LSTM 的(batch, sequence, embedding_dim) 形输入完全没有问题。但是,假设您还有 3 个连续的辅助特征(并且正确规范化)。您可以做的一件简单的事情就是将 append 这些到嵌入层的输出以获得 LSTM 也可以处理的 (batch, sequence, embedding_dims+auxiliary) 输入!

Sequence --> Embed ----->|
                         *--> Append --> LSTM -> Process --> Predict
Auxiliary --> Process -->|

代码

from tensorflow.keras import layers, Model, utils

#One sequential, label encoded feature & 3 auxilary features for each timestep
X = np.random.random((100,10,4))
Y = np.random.random((100,))

###Define model###
inp = layers.Input((10,4))
feature1 = layers.Lambda(lambda x: x[...,0])(inp)
feature2 = layers.Lambda(lambda x: x[...,1:4])(inp)

#Append embeddings features
x = layers.Embedding(1000, 5)(feature1)
x = layers.concatenate([x, feature2])

#LSTMs
x = layers.LSTM(8, return_sequences=True)(x)
x = layers.LSTM(8)(x)
out = layers.Dense(1)(x)

model = Model(inp, out)
utils.plot_model(model, show_layer_names=False, show_shapes=True)

在上面的例子中,在标签编码的输入嵌入到5-dimensional向量之后,附加了3个辅助输入,然后(10,8)维度序列被传递给LSTMs来发挥它们的作用。

场景 4:

假设您有与上述相同的场景,但您希望序列特征在附加辅助输入之前具有更丰富的表示。在这里,您可以简单地将顺序特征传递给 LSTM,并将辅助输入附加到 LSTM 的 OUTPUT,然后在需要时决定将其传递给另一个 LSTM。这将需要您return_sequences=True,以便您可以获得相同长度的序列,该序列可以附加到该组时间步长的辅助特征中。

Sequence --> Embed --> LSTM(seq) -->|
                                    *--> Append --> Process --> Predict
Auxiliary --> Process ------------->|

代码

from tensorflow.keras import layers, Model, utils

#One sequential, label and 3 auxilary continous features
X = np.random.random((100,10,4))
Y = np.random.random((100,))

###Define model###
inp = layers.Input((10,4))
feature1 = layers.Lambda(lambda x: x[...,0])(inp)
feature2 = layers.Lambda(lambda x: x[...,1:4])(inp)
#feature2 = layers.Reshape((-1,1))(feature2)

#Append embeddings features
x = layers.Embedding(1000, 5)(feature1)

#LSTMs
x = layers.LSTM(8, return_sequences=True)(x)
x = layers.concatenate([x, feature2])
x = layers.LSTM(8)(x)

#Combine LSTM final states
out = layers.Dense(1)(x)

model = Model(inp, out)
utils.plot_model(model, show_layer_names=False, show_shapes=True)

有些架构会在 LSTM 的输出中添加单个特征,然后在 LSTM 中再次对其进行编码,然后再添加下一个特征等等,而不是将所有特征加在一起。这是一种设计选择,必须针对您的特定数据进行测试。

希望这能澄清您的问题。

【讨论】:

  • 在预测的上下文中,假设您在 n 个时间步长的输入序列中具有特征(power_kwh、tempC、湿度 ...),因为您可以看到一些特征与天气相关,您可以还检索您预测的下一个时间步的预测,您建议如何包含这些?你会将它们连接到 LSTM 的输出还是将它们包含在 LSTM 输入中?
  • 在最后一种情况下,您会将它们作为附加功能添加还是通过更改现有天气功能来添加? (在这种情况下,您将失去输入序列中的时间相关性)
【解决方案2】:

KerasLSTM 的默认实现@期望输入形状:(batch, sequence, features)

所以当重塑 x_train 而不是这样做时:

x_train = np.reshape(x_train, (len(x_train), 1, 1))

您只需:

x_train = np.reshape(x_train, (len(x_train), 1, num_features))

从您的帖子中不清楚您是否还想预测这个新功能(多变量预测),或者您是否仍然只想预测 y

在第一种情况下,您需要修改 Dense 层以考虑目标的新维度:

regressor.add(Dense(units = num_features))

在第二种情况下,您需要重塑 y_train 以仅采用 y

y_train = training_set[1:len(training_set),1] # (assuming Date is not the index)

最后,您的 LSTM 输入形状必须更新以接受新的重构 x_train

regressor.add(LSTM(units = num_units, activation = activation_function, input_shape=(None, 1, num_features)))

【讨论】:

  • 我的目标是只预测 y。想象一下 v1、v2 和 v3 是天气变量。如果我想预测明天的 y 并且我知道明天的 v1、v2、v3 估计值(来自气象服务),我只需要预测 y。
  • @Numbermind,因此您可以保持 Dense 层不变。并根据答案重塑其余数据和LSTMinput_shape
  • 我已按照您的建议进行操作,现在我收到一条错误消息:lstm_51 层的输入 0 与该层不兼容:预期 ndim=3,发现 ndim=4。收到的完整形状:[无,无,5]。 (我的数据有以下变量 y、v1、v2、v3、v4 和 datetime 作为索引)。
  • @Numbermind 你能用你的修改编辑你的 OP,这样我就可以看到它来自哪里。您的输入似乎有很多方面。
猜你喜欢
  • 2012-12-26
  • 1970-01-01
  • 2015-05-14
  • 1970-01-01
  • 2012-10-17
  • 1970-01-01
  • 1970-01-01
  • 2013-04-28
  • 1970-01-01
相关资源
最近更新 更多