使用RNN进行非线性多变量时间序列响应预测

我试图根据室内和室外气候预测墙体的湿热响应。基于文献研究,我认为这应该是可以用RNN实现的,但我一直无法获得良好的准确性。

数据集有12个输入特征(室外和室内气候数据的时间序列)和10个输出特征(湿热响应的时间序列),两者都包含10年的每小时值。这些数据是通过湿热模拟软件创建的,没有缺失数据。

数据集特征:输入特征

数据集目标:输出特征

与大多数时间序列预测问题不同,我希望在每个时间步预测输入特征时间序列的整个长度的响应,而不是时间序列的后续值(例如金融时间序列预测)。我没有找到类似的预测问题(在类似或其他领域),所以如果你知道这方面的例子,非常欢迎提供参考文献。


我认为这应该是可以用RNN实现的,所以我目前正在使用Keras中的LSTM。在训练之前,我以以下方式预处理数据:

  1. 丢弃第一年的数据,因为墙体湿热响应的初始时间步骤受初始温度和相对湿度的影响。
  2. 将数据分为训练集和测试集。训练集包含前8年的数据,测试集包含剩余的2年数据。
  3. 使用Sklearn中的StandardScaler对训练集进行标准化(零均值,单位方差)。使用训练集的均值和方差对测试集进行类似的标准化处理。

这导致的结果是:X_train.shape = (1, 61320, 12)y_train.shape = (1, 61320, 10)X_test.shape = (1, 17520, 12)y_test.shape = (1, 17520, 10)

由于这些是长时间序列,我使用有状态的LSTM,并按照这里所解释的切割时间序列,使用stateful_cut()函数。我只有一个样本,所以batch_size为1。对于T_after_cut,我尝试了24和120(24*5);24似乎能提供更好的结果。这导致X_train.shape = (2555, 24, 12)y_train.shape = (2555, 24, 10)X_test.shape = (730, 24, 12)y_test.shape = (730, 24, 10)

接下来,我构建并训练LSTM模型如下:

model = Sequential()
model.add(LSTM(128,
               batch_input_shape=(batch_size,T_after_cut,features),
               return_sequences=True,
               stateful=True,
               ))
model.add(TimeDistributed(Dense(targets)))
model.compile(loss='mean_squared_error', optimizer=Adam())
model.fit(X_train, y_train, epochs=100, batch_size=batch_size, verbose=2, shuffle=False)

不幸的是,我没有得到准确的预测结果;即使是对于训练集也是如此,因此模型存在高偏差。

LSTM模型对所有目标的预测结果


我如何改进我的模型?我已经尝试了以下方法:

  1. 不丢弃数据集的第一年 -> 没有显著差异
  2. 对输入特征的时间序列进行差分处理(从当前值中减去前一个值)-> 结果略差
  3. 最多四个堆叠的LSTM层,所有层使用相同的超参数 -> 结果没有显著差异,但训练时间更长
  4. 在LSTM层后添加Dropout层(尽管这通常用于减少方差,而我的模型具有高偏差)-> 结果略有改善,但差异可能没有统计学意义

我在使用有状态的LSTM时是否做错了什么?我是否需要尝试不同的RNN模型?我应该以不同的方式预处理数据吗?

此外,训练速度非常慢:上述模型大约需要4小时。因此,我不太愿意进行广泛的超参数网格搜索…


回答:

最终,我以以下方式解决了这个问题:

  • 使用更多样本进行训练,而不是仅使用1个样本(我使用了18个样本进行训练,6个样本进行测试)
  • 保留第一年的数据,因为所有样本的输出时间序列具有相同的“起点”,模型需要这些信息来学习
  • 标准化输入和输出特征(零均值,单位方差)。我发现这提高了预测准确性和训练速度
  • 按照这里描述的使用有状态的LSTM,但在每个epoch后添加重置状态(见下方代码)。我使用了batch_size = 6T_after_cut = 1460。如果T_after_cut更长,训练速度会更慢;如果T_after_cut更短,准确性会略有下降。如果有更多样本可用,我认为使用更大的batch_size会更快。
  • 使用CuDNNLSTM代替LSTM,这将训练时间加速了4倍!
  • 我发现更多的单位会导致更高的准确性和更快的收敛(更短的训练时间)。此外,我发现GRU与LSTM同样准确,但在相同数量的单位下收敛速度更快。
  • 在训练过程中监控验证损失并使用早停机制

LSTM模型的构建和训练如下所示:

def define_reset_states_batch(nb_cuts):
    class ResetStatesCallback(Callback):
        def __init__(self):
            self.counter = 0

        def on_batch_begin(self, batch, logs={}):
            # 在完成nb_cuts批次后重置状态
            if self.counter % nb_cuts == 0:
                self.model.reset_states()
            self.counter += 1

        def on_epoch_end(self, epoch, logs={}):
            # 在每个epoch结束后重置状态
            self.model.reset_states()

    return(ResetStatesCallback)

model = Sequential()
model.add(layers.CuDNNLSTM(256, batch_input_shape=(batch_size,T_after_cut,features),
                           return_sequences=True,
                           stateful=True))
model.add(layers.TimeDistributed(layers.Dense(targets, activation='linear')))
optimizer = RMSprop(lr=0.002)
model.compile(loss='mean_squared_error', optimizer=optimizer)
earlyStopping = EarlyStopping(monitor='val_loss', min_delta=0.005, patience=15, verbose=1, mode='auto')
ResetStatesCallback = define_reset_states_batch(nb_cuts)
model.fit(X_dev, y_dev, epochs=n_epochs, batch_size=n_batch, verbose=1, shuffle=False, validation_data=(X_eval,y_eval), callbacks=[ResetStatesCallback(), earlyStopping])

这给我带来了非常令人满意的准确性(R2超过0.98):预测此图显示了墙体内2年的温度(左)和相对湿度(右)(未用于训练的数据),红色为预测值,黑色为真实输出值。残差显示误差非常小,并且LSTM学会了捕捉长期依赖性以预测相对湿度。

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注