代码之家  ›  专栏  ›  技术社区  ›  Astrid

基于RNN的非线性多变量时间序列响应预测

  •  8
  • Astrid  · 技术社区  · 8 年前

    考虑到室内和室外的气候,我试图预测墙壁的湿热反应。根据文献研究,我相信这应该可以与RNN,但我一直无法获得良好的准确性。

    该数据集有12个输入特征(外部和内部气候数据的时间序列)和10个输出特征(湿热响应的时间序列),均包含10年的小时值。该数据是用湿热模拟软件创建的,没有缺失数据。

    数据集功能: Input features

    数据集目标: Output features

    与大多数时间序列预测问题不同,我希望预测每个时间步的输入特征时间序列的全长响应,而不是时间序列的后续值(例如金融时间序列预测)。我还没有发现类似的预测问题(在类似或其他领域),所以如果你知道其中一个,欢迎参考。


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

    1. 丢弃第一年的数据,因为墙湿热响应的第一时间步受初始温度和相对湿度的影响。
    2. 分为培训和测试集。训练集包含前8年的数据,测试集包含剩余的2年。
    3. 使用 StandardScaler 来自Sklearn。使用训练集的平均方差对测试集进行类似的正态化。

    这将导致: 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并按说明剪切时间序列 here ,使用 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.addTimeDistributed(Dense(targets)))
    model.compile(loss='mean_squared_error', optimizer=Adam())
    
    model.fit(X_train, y_train, epochs=100, batch_size=batch=batch_size, verbose=2, shuffle=False)
    

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

    The prediction results of the LSTM model for all targets


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

    1. 不放弃数据集的第一年->无显著差异
    2. 区分输入特征时间序列(从当前值中减去以前的值)->稍差的结果
    3. 最多四个堆叠的LSTM层,都具有相同的超参数->结果无显著差异,但训练时间更长
    4. LSTM层之后的退出层(尽管这通常用于减少方差,并且我的模型有很高的偏差)>稍好的结果,但差异可能没有统计学意义

    我对有状态的LSTM做了什么错事吗?我需要尝试不同的RNN型号吗?我应该以不同的方式预处理数据吗?

    此外,培训速度非常慢:上述型号约4小时。因此,我不愿意进行广泛的超参数网格搜索。。。

    1 回复  |  直到 6 年前
        1
  •  7
  •   mEEGal agittarius    6 年前

    最后,我通过以下方式解决了这个问题:

    • 使用更多样本进行训练,而不是仅使用1个样本(我使用18个样本进行训练,6个样本进行测试)
    • 保留第一年的数据,因为所有样本的输出时间序列都具有相同的“起点”,模型需要了解这些信息
    • 标准化输入和输出特征(零均值、单位方差)。我发现这提高了预测精度和训练速度
    • 如前所述,使用有状态LSTM here ,但在历元之后添加重置状态(代码见下文)。我用过 batch_size = 6 T_after_cut = 1460 . 如果 T_after_cut 时间越长,训练越慢;如果 切割后的T\U 越短,精度略有下降。如果有更多的样品,我想使用更大的 batch_size 会更快。
    • 使用CuDNNLSTM代替LSTM,这将加快训练时间x4!
    • 我发现单位越多,精确度越高,收敛速度越快(训练时间越短)。我还发现,对于相同数量的单元,GRU与LSTM-tough一样精确,收敛速度更快。
    • 在培训期间监控验证损失,并使用提前停止

    LSTM模型的构建和培训如下:

    def define_reset_states_batch(nb_cuts):
      class ResetStatesCallback(Callback):
        def __init__(self):
          self.counter = 0
    
        def on_batch_begin(self, batch, logs={}):
        # reset states when nb_cuts batches are completed
          if self.counter % nb_cuts == 0:
            self.model.reset_states()
          self.counter += 1
    
        def on_epoch_end(self, epoch, logs={}):
        # reset states after each 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): Prediction 此图显示了两年来墙内的温度(左)和相对湿度(右)(培训中未使用数据),预测为红色,实际输出为黑色。残差表明误差非常小,并且LSTM学会捕捉长期相关性来预测相对湿度。