使用 RNN 进行非线性多元时间序列响应预测
Non-linear multivariate time-series response prediction using RNN
我正在尝试根据内部和外部气候预测墙壁的湿热响应。根据文献研究,我相信 RNN 应该可以做到这一点,但我无法获得很好的准确性。
数据集有 12 个输入特征(外部和内部气候数据的时间序列)和 10 个输出特征(湿热响应时间序列),均包含 10 年的每小时值。此数据是用湿热模拟软件创建的,没有遗漏数据。
数据集特征:
数据集目标:
与大多数时间序列预测问题不同,我想预测每个时间步的输入特征时间序列的全长响应,而不是时间序列的后续值(例如金融时间-系列预测)。我没能找到类似的预测问题(在类似或其他领域),所以如果你知道一个,非常欢迎参考。
我认为这应该可以通过 RNN 实现,所以我目前使用的是 Keras 的 LSTM。在训练之前,我按以下方式预处理我的数据:
- 舍弃第一年的数据,因为墙的湿热响应的第一个时间步长受初始温度和相对湿度的影响。
- 分成训练和测试集。训练集包含前8年的数据,测试集包含剩余2年的数据。
- 使用来自 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()
函数按照 here 的说明切割时间序列。我只有 1 个样本,所以 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
如何改进我的模型?我已经尝试过以下方法:
- 不丢弃数据集的第一年 -> 无显着差异
- 区分输入特征时间序列(从当前值中减去先前值)-> 稍差的结果
- 最多四个堆叠的 LSTM 层,都具有相同的超参数 -> 结果没有显着差异但训练时间更长
- LSTM 层之后的 Dropout 层(虽然这通常用于减少方差并且我的模型具有高偏差)-> 稍微好一点的结果,但差异可能不具有统计显着性
我是不是对有状态的 LSTM 做错了什么?我需要尝试不同的 RNN 模型吗?我应该以不同的方式预处理数据吗?
此外,训练非常缓慢:上面的模型大约需要 4 个小时。因此我不愿意做一个广泛的超参数网格搜索...
最后,我设法通过以下方式解决了这个问题:
- 使用更多样本进行训练而不是仅使用 1 个(我使用 18 个样本进行训练,6 个样本进行测试)
- 保留第一年的数据,因为所有样本的输出 time-series 具有相同的 'starting point' 并且模型需要此信息来学习
- 标准化输入和输出特征(零均值,单位方差)。我发现这提高了预测准确性和训练速度
- 按照 here 所述使用有状态 LSTM,但在纪元之后添加重置状态(代码见下文)。我使用了
batch_size = 6
和 T_after_cut = 1460
。 T_after_cut
越长,训练越慢;如果 T_after_cut
较短,准确度会略有下降。如果有更多样本可用,我认为使用更大的 batch_size
会更快。
- 使用 CuDNNLSTM 代替 LSTM,这加快了训练时间 x4!
- 我发现更多的单位导致更高的准确性和更快的收敛(更短的训练时间)。我还发现 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={}):
# 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):
该图显示了 2 年内墙壁的温度(左)和相对湿度(右)(训练中未使用的数据),红色为预测值,黑色为真实输出。残差表明误差非常小,并且 LSTM 学会了捕捉 long-term 依赖关系来预测相对湿度。
我正在尝试根据内部和外部气候预测墙壁的湿热响应。根据文献研究,我相信 RNN 应该可以做到这一点,但我无法获得很好的准确性。
数据集有 12 个输入特征(外部和内部气候数据的时间序列)和 10 个输出特征(湿热响应时间序列),均包含 10 年的每小时值。此数据是用湿热模拟软件创建的,没有遗漏数据。
数据集特征:
数据集目标:
与大多数时间序列预测问题不同,我想预测每个时间步的输入特征时间序列的全长响应,而不是时间序列的后续值(例如金融时间-系列预测)。我没能找到类似的预测问题(在类似或其他领域),所以如果你知道一个,非常欢迎参考。
我认为这应该可以通过 RNN 实现,所以我目前使用的是 Keras 的 LSTM。在训练之前,我按以下方式预处理我的数据:
- 舍弃第一年的数据,因为墙的湿热响应的第一个时间步长受初始温度和相对湿度的影响。
- 分成训练和测试集。训练集包含前8年的数据,测试集包含剩余2年的数据。
- 使用来自 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()
函数按照 here 的说明切割时间序列。我只有 1 个样本,所以 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
如何改进我的模型?我已经尝试过以下方法:
- 不丢弃数据集的第一年 -> 无显着差异
- 区分输入特征时间序列(从当前值中减去先前值)-> 稍差的结果
- 最多四个堆叠的 LSTM 层,都具有相同的超参数 -> 结果没有显着差异但训练时间更长
- LSTM 层之后的 Dropout 层(虽然这通常用于减少方差并且我的模型具有高偏差)-> 稍微好一点的结果,但差异可能不具有统计显着性
我是不是对有状态的 LSTM 做错了什么?我需要尝试不同的 RNN 模型吗?我应该以不同的方式预处理数据吗?
此外,训练非常缓慢:上面的模型大约需要 4 个小时。因此我不愿意做一个广泛的超参数网格搜索...
最后,我设法通过以下方式解决了这个问题:
- 使用更多样本进行训练而不是仅使用 1 个(我使用 18 个样本进行训练,6 个样本进行测试)
- 保留第一年的数据,因为所有样本的输出 time-series 具有相同的 'starting point' 并且模型需要此信息来学习
- 标准化输入和输出特征(零均值,单位方差)。我发现这提高了预测准确性和训练速度
- 按照 here 所述使用有状态 LSTM,但在纪元之后添加重置状态(代码见下文)。我使用了
batch_size = 6
和T_after_cut = 1460
。T_after_cut
越长,训练越慢;如果T_after_cut
较短,准确度会略有下降。如果有更多样本可用,我认为使用更大的batch_size
会更快。 - 使用 CuDNNLSTM 代替 LSTM,这加快了训练时间 x4!
- 我发现更多的单位导致更高的准确性和更快的收敛(更短的训练时间)。我还发现 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={}):
# 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):