将时间序列数据提供给有状态 LSTM 的正确方法?

Posted

技术标签:

【中文标题】将时间序列数据提供给有状态 LSTM 的正确方法?【英文标题】:Proper way to feed time-series data to stateful LSTM? 【发布时间】:2020-02-05 03:35:27 【问题描述】:

假设我有一个整数序列:

0,1,2, ..

并想根据最后 3 个整数预测下一个整数,例如:

[0,1,2]->5[3,4,5]->6

假设我这样设置我的模型:

batch_size=1
time_steps=3
model = Sequential()
model.add(LSTM(4, batch_input_shape=(batch_size, time_steps, 1), stateful=True))
model.add(Dense(1))

据我了解,模型的结构如下(请原谅粗图):

第一个问题:我的理解正确吗?

注意我已经绘制了之前的状态C_t-1, h_t-1 进入图片,因为这是在指定stateful=True 时暴露的。在这个简单的“下一个整数预测”问题中,性能应该会通过提供这些额外信息来提高(只要前一个状态来自前 3 个整数)。

这让我想到了我的主要问题: 似乎标准做法(例如参见 blog post 和 TimeseriesGenerator keras 预处理实用程序)是将一组交错的输入提供给训练期间的模型。

例如:

batch0: [[0, 1, 2]]
batch1: [[1, 2, 3]]
batch2: [[2, 3, 4]]
etc

这让我很困惑,因为这似乎需要第一个 Lstm 单元的输出(对应于第一个时间步)。看这个图:

来自张量流docs:

有状态:布尔值(默认为 False)。如果为 True,则每个状态的最后一个状态 批次中索引 i 处的样本将用作 下一批中索引 i 的样本。

似乎这种“内部”状态不可用,所有可用的都是最终状态。看这个图:

所以,如果我的理解是正确的(显然不是),我们不应该在使用 stateful=True 时向模型提供不重叠的样本窗口吗?例如:

batch0: [[0, 1, 2]]
batch1: [[3, 4, 5]]
batch2: [[6, 7, 8]]
etc

【问题讨论】:

如我所料,这是两个问题合二为一。简要回答您的第一个问题:可能是的。更重要的是您认为该图像所描绘的内容 - 但要点是准确的:LSTM 跨隐藏状态传递信息,并且仅将一个特征张量传递给 Dense 进行预测。 (多对一)。 “您认为该图像所描绘的内容”是什么意思?你是说它在概念上是准确的,但比我画的要多得多? 我是说这是一个非常高级的表示,并且有很多内部 LSTM - 例如kernelrecurrent 权重,每个门的角色,以及信息如何在时间步之间流动。 【参考方案1】:

答案是:取决于手头的问题。对于您的一步预测的情况 - 是的,您可以,但您不必这样做。但无论你是否这样做,都会对学习产生重大影响。


批处理与样本机制(“参见 AI”=参见“附加信息”部分)

所有模型都将样本视为独立的样本;一批 32 个样品就像一次喂 1 个样品,32 次(有差异 - 见 AI)。从模型的角度来看,数据分为批处理维度batch_shape[0] 和特征维度batch_shape[1:]——这两个“不要说话”。两者之间的唯一关系是通过梯度(参见 AI)。


重叠与非重叠批次

也许理解它的最佳方法是基于信息。我将从时间序列二进制分类开始,然后将其与预测联系起来:假设您有 10 分钟的 EEG 记录,每个记录有 240000 个时间步长。任务:扣押还是不扣押?

由于 RNN 无法处理 240k,因此我们使用 CNN 进行降维 我们可以选择使用“滑动窗口” - 即一次输入一个子段;让我们使用 54k

取 10 个样本,形状为 (240000, 1)。如何喂养?

    (10, 54000, 1),包括所有样本,切片为sample[0:54000]; sample[54000:108000] ... (10, 54000, 1),包括所有样本,切片为sample[0:54000]; sample[1:54001] ...

以上两个你选哪个?如果 (2),您的神经网络将永远不会混淆这 10 个样本的癫痫发作与非癫痫发作。但它也会对任何其他样本一无所知。即,它会严重过拟合,因为它每次迭代看到的信息几乎没有什么不同(1/54000 = 0.0019%)——所以你基本上是在喂它同一批次连续多次。现在假设(3):

    (10, 54000, 1),包括所有样本,切片为sample[0:54000]; sample[24000:81000] ...

更合理;现在我们的窗口有 50% 的重叠,而不是 99.998%。


预测:重叠不好?

如果您正在进行一步预测,那么信息格局现在已经改变:

很有可能,您的序列长度从 240000 开始,因此任何类型的重叠都不会受到“同一批次多次”的影响 预测与分类的根本区别在于,您输入的每个子样本的标签(下一个时间步长)都不同;分类对整个序列使用一个

这极大地改变了您的损失函数,以及将其最小化的“良好做法”:

预测器必须对其初始样本具有鲁棒性,尤其是对于 LSTM - 因此我们通过滑动您展示的序列来训练每个这样的“开始” 由于标签在时间步之间存在差异,因此损失函数会在时间步之间发生显着变化,因此过度拟合的风险要小得多

我该怎么办?

首先,请确保您了解整篇文章,因为这里没有什么是真正“可选”的。然后,这是关于重叠与不重叠的关键,每批

    一个样本转移:模型学习更好地预测每个起始步骤的前一步 - 含义:(1)LSTM 对初始细胞状态的鲁棒性; (2) LSTM 可以很好地预测给定落后 X 步的任何领先一步 许多样本,稍后批次转移:模型不太可能“记住”训练集和过拟合

你的目标:平衡两者; 1 对 2 的主要优势是:

2 可以通过让模型忘记见过的样本来妨碍模型 1 允许模型通过检查多个起点和终点(标签)的样本并相应地平均梯度来提取更好的质量特征

我应该在预测中使用 (2) 吗?

如果您的序列长度很长,并且您可以负担得起“滑动窗口”w/ ~50% 的长度,也许,但这取决于数据的性质:信号 (EEG)?是的。股票,天气?怀疑。 多对多预测;更常见的是 (2),在较长的序列中较大。

LSTM 有状态:实际上可能对您的问题完全无用。

当 LSTM 无法一次处理整个序列时使用有状态的,因此它被“拆分” - 或者当反向传播需要不同的梯度时。对于前者,想法是 - LSTM 在评估后者时考虑前一个序列:

t0=seq[0:50]; t1=seq[50:100] 有道理; t0 逻辑上导致t1 seq[0:50] --> seq[1:51] 没有意义; t1 并非源自 t0

换句话说:不要在有状态的不同批次中重叠。同一批次是可以的,同样,独立性 - 样本之间没有“状态”。

何时使用有状态:当 LSTM 受益于在评估下一批时考虑前一批时。这可以包括一步预测,但前提是你不能一次提供整个序列:

期望:100 个时间步。可以做到:50。所以我们在上面的第一个项目符号中设置t0, t1问题:以编程方式实现并不简单。您需要找到一种在不应用渐变的情况下提供给 LSTM 的方法 - 例如冻结重量或设置lr = 0

LSTM 何时以及如何在有状态中“传递状态”?

:仅批次到批次;样本完全独立 如何:在 Keras 中,只有 batch-sample 到 batch-samplestateful=True 要求你指定 batch_shape 而不是 input_shape - 因为,Keras 在编译时构建了batch_size LSTM 的不同状态

根据上述情况,您不能这样做:

# sampleNM = sample N at timestep(s) M
batch1 = [sample10, sample20, sample30, sample40]
batch2 = [sample21, sample41, sample11, sample31]

这意味着21 会跟随10 - 并且会破坏训练。而是这样做:

batch1 = [sample10, sample20, sample30, sample40]
batch2 = [sample11, sample21, sample31, sample41]

批次与样本:附加信息

“批次”是一组样本 - 1 个或更大(假设此答案总是后者) .三种迭代数据的方法:批量梯度下降(一次整个数据集)、随机 GD(一次一个样本)和小批量 GD (in-between)。 (然而,在实践中,我们也称最后一个 SGD 并且只区分 vs BGD - 假设这个答案是这样。)差异:

SGD 从未真正优化过训练集的损失函数——只是它的“近似值”;每个批次都是整个数据集的一个子集,计算的梯度仅与最小化该批次的损失有关。批大小越大,其损失函数就越接近训练集。 以上可以扩展到拟合批次与样本:样本是批次的近似值,或者是数据集的较差近似值 首先拟合 16 个样本,然后再拟合 16 个样本与一次拟合 32 个样本相同 - 因为 权重在两者之间更新,因此后半部分的模型输出会改变 实际上,选择 SGD 而不是 BGD 的主要原因不是计算限制,而是 它的优势,大多数时候。简单解释:使用 BGD 更容易过度拟合,并且 SGD 通过探索更多样化的损失空间来收敛到更好的测试数据解决方案。

奖金图表


【讨论】:

我仍在消化这个精彩的答案(谢谢),但我仍然不清楚我后面的两个数字中的哪一个是“正确的”:当stateful=True 做“最终”时LSTM 状态(对应于给定整个样本到该点的时间序列的输出)传递到下一批?还是某种中间状态? @rmccabe3701 是的,我刚刚意识到我的答案是不完整的 - 这是一个地狱般的问题。正在努力 @rmccabe3701 已更新——我实际上并不完全确定您的图表所显示的内容,但它们看起来确实不对;如果还有什么不清楚的地方,请告诉我。 (编辑:仔细看,你可能认为第二张图“正确”是对的——但我现在要休息一下) 哇,你的第一个“奖金图”正是我要问的。所以看起来我的第二张图(将“最终”状态输入下一批)是最准确的(你的图更清晰)。我几乎准备好将此问题标记为已解决。但在此之前:如果输入交错,我仍然不清楚以这种方式传递状态的有效性。我完全理解你为什么错开输入是有用的激励例子,但我的困惑是在这种情况下继承状态的算法明显不一致。 @rmccabe3701 “交错”是什么意思?拆分序列,还是按一个时间步切片?

以上是关于将时间序列数据提供给有状态 LSTM 的正确方法?的主要内容,如果未能解决你的问题,请参考以下文章

技术 | 如何在Python下生成用于时间序列预测的LSTM状态

了解有状态的 LSTM [关闭]

如何在Keras训练LSTM的初始状态?

如何正确地为 PyTorch 中的嵌入、LSTM 和线性层提供输入?

将 Pytorch LSTM 的状态参数转换为 Keras LSTM

Tensorflow神经网络之LSTM