Keras fit_generator() - 时间序列的批处理是如何工作的?

15

背景:

我目前正在使用Keras和Tensorflow后端进行时间序列预测,并研究了这里提供的教程。

按照这个教程,我来到了描述fit_generator()方法的生成器的那一点。 这个生成器生成的输出如下所示(左样本,右目标):

[[[10. 15.]
  [20. 25.]]] => [[30. 35.]]     -> Batch no. 1: 2 Samples | 1 Target
  ---------------------------------------------
[[[20. 25.]
  [30. 35.]]] => [[40. 45.]]     -> Batch no. 2: 2 Samples | 1 Target
  ---------------------------------------------
[[[30. 35.]
  [40. 45.]]] => [[50. 55.]]     -> Batch no. 3: 2 Samples | 1 Target
  ---------------------------------------------
[[[40. 45.]
  [50. 55.]]] => [[60. 65.]]     -> Batch no. 4: 2 Samples | 1 Target
  ---------------------------------------------
[[[50. 55.]
  [60. 65.]]] => [[70. 75.]]     -> Batch no. 5: 2 Samples | 1 Target
  ---------------------------------------------
[[[60. 65.]
  [70. 75.]]] => [[80. 85.]]     -> Batch no. 6: 2 Samples | 1 Target
  ---------------------------------------------
[[[70. 75.]
  [80. 85.]]] => [[90. 95.]]     -> Batch no. 7: 2 Samples | 1 Target
  ---------------------------------------------
[[[80. 85.]
  [90. 95.]]] => [[100. 105.]]   -> Batch no. 8: 2 Samples | 1 Target
在教程中使用了TimeSeriesGenerator,但对于我的问题来说,使用自定义生成器或者这个类都是次要的。关于数据,我们有8个steps_per_epoch和一个形状为(8,1,2,2)的样本。生成器被馈送到由LSTM实现的递归神经网络中。
我的问题是:fit_generator()一次只允许一个目标,就像TimeSeriesGenerator输出的那样。当我第一次了解到可以批量处理的选项时,我认为我可以有多个样本和相应数量的目标(它们按批次处理,即逐行处理)。但是fit_generator()不允许这样做,因此显然是错误的。例如,这将看起来像:
[[[10. 15. 20. 25.]]] => [[30. 35.]]     
[[[20. 25. 30. 35.]]] => [[40. 45.]]    
    |-> Batch no. 1: 2 Samples | 2 Targets
  ---------------------------------------------
[[[30. 35. 40. 45.]]] => [[50. 55.]]    
[[[40. 45. 50. 55.]]] => [[60. 65.]]    
    |-> Batch no. 2: 2 Samples | 2 Targets
  ---------------------------------------------
...
其次,我的想法是,例如,[10,15]和[20,25]被连续用作RNN的输入,以获得目标[30,35],这意味着这相当于输入[10,15,20,25]。由于使用第二种方法的RNN输出不同(我进行了测试),因此这也必须是一个错误的结论。
因此,我的问题是:
1.为什么每个批次只允许一个目标(我知道有一些解决方法,但肯定有原因)?
2.我应该如何理解一个批次的计算?也就是说,类似[[[40,45],[50,55]]]=>[[60,65]]这样的输入是如何处理的,为什么它不能与[[[40,45,50,55]]]=>[[60,65]]类比?
#This is just a single batch - Multiple batches would be fed to fit_generator()
(array([[[0, 1, 2, 3, 4],
        [5, 6, 7, 8, 9]]]), 
                           array([[ 5,  6,  7,  8,  9],
                           [10, 11, 12, 13, 14]]))

这应该是一个包含两个长度为5的时间序列(5个连续的数据点/时间步长)及其对应序列的单个批次。 [ 5,6,7,8,9 ][0,1,2,3,4]的目标序列,而[10,11,12,13,14][5,6,7,8,9]的相应目标序列。
此样本的形状为shape(number_of_batches, number_of_elements_per_batch, sequence_size),目标形状为shape(number_of_elements_per_batch, sequence_size)
Keras看到有两个目标样本(在ValueError中),因为我需要提供3D样本作为输入和2D目标作为输出(也许我不知道如何提供3D目标..)。

无论如何,根据@todays的答案/评论,Keras将这解释为两个时间步长和五个特征。关于我的第一个问题(在其中我仍将序列视为序列的目标,例如在此处编辑示例中),我想了解是否可以实现这一点以及这样的批次会是什么样子的(就像我在问题中尝试可视化的那样)。


我更新了我的问题 - 现在对你来说更清楚了吗? - Markus
很遗憾,不是这样的。我认为您混淆了样本、时间步长、特征和目标。让我描述一下我的理解:在您提供的第一个示例中,每个输入样本似乎由2个时间步长组成,例如[10, 15][20, 25],其中每个时间步长包含两个特征,例如10和15或20和25。此外,相应的目标由一个时间步长组成,例如[30, 35],它也有两个特征。换句话说,批处理中的每个输入样本都必须有一个对应的目标。但是,每个输入样本及其目标的形状可能不相同。 - today
换句话说,在每个训练批次中,我们有与输入样本一样多的目标。 - today
1
这显然是一个多变量时间序列问题:每个时间步有多个(在这种情况下为两个)特征。这就是为什么作者使用 hstack 将两个单变量时间序列并排堆叠起来构建该时间序列的原因。它只是根据前两个时间步预测下一个时间步。 - today
1
引用自该教程:“每个样本将是一个三维数组,大小为[1, 2, 2],其中包含1个样本、2个时间步和2个特征或并行序列。输出将是一个二维序列,大小为[1, 2],其中包含1个样本和2个特征。” 这证实了我的假设和解释。 - today
显示剩余4条评论
1个回答

4

简短回答:

为什么一个批次只允许一个目标(我知道有一些解决方法,但必须有原因)?

实际上并不是这样的。在一个批次中,没有对目标样本数量限制。唯一的要求是每个批次中输入和目标样本的数量应该相同。详细说明请阅读长答案。

我该如何理解一个批次的计算?意思是,如何处理像 [[[40, 45], [50, 55]]] => [[60, 65]] 这样的输入,为什么它不类似于 [[[40, 45, 50, 55]]] => [[60, 65]]

第一个示例是多元时间序列(即每个时间步具有多个特征),而第二个示例是单变量时间序列(即每个时间步只有一个特征)。因此它们不是相等的。详细说明请阅读长答案。

长答案:

我将给出我在评论部分提到的答案,并尝试使用示例进行详细说明:

我认为你混淆了样本、时间步、特征和目标。让我描述一下我的理解方式:在你提供的第一个例子中,似乎每个输入样本由2个时间步组成,例如[10, 15][20, 25],其中每个时间步都包含两个特征,例如10和15或20和25。此外,相应的目标由一个时间步组成,例如[30, 35],该时间步也有两个特征。换句话说,在批次中,每个输入样本必须对应一个目标。但是,每个输入样本及其对应的目标的形状可能不一定相同。

例如,考虑一个输入和输出都是时间序列的模型。如果我们将 每个输入样本 的形状表示为 (input_num_timesteps, input_num_features),并将 每个目标(即输出)数组 的形状表示为 (output_num_timesteps, output_num_features),则会出现以下情况:

1)输入和输出时间步数相同(即 input_num_timesteps == output_num_timesteps)。例如,以下模型可以实现这一点:

from keras import layers
from keras import models

inp = layers.Input(shape=(input_num_timesteps, input_num_features))

# a stack of RNN layers on top of each other (this is optional)
x = layers.LSTM(..., return_sequences=True)(inp)
# ...
x = layers.LSTM(..., return_sequences=True)(x)

# a final RNN layer that has `output_num_features` unit
out = layers.LSTM(output_num_features, return_sequneces=True)(x)

model = models.Model(inp, out)

2)输入和输出的时间步数不同(即 input_num_timesteps ~= output_num_timesteps )。这通常是通过使用一层或多层LSTM对输入时间序列进行编码,并重复该向量output_num_timesteps次以获得所需长度的时间序列。对于重复操作,我们可以在Keras中轻松使用RepeatVector层。再次举例说明,以下模型可以实现此目的:

from keras import layers
from keras import models

inp = layers.Input(shape=(input_num_timesteps, input_num_features))

# a stack of RNN layers on top of each other (this is optional)
x = layers.LSTM(..., return_sequences=True)(inp)
# ...
x = layers.LSTM(...)(x)  # The last layer ONLY returns the last output of RNN (i.e. return_sequences=False)

# repeat `x` as needed (i.e. as the number of timesteps in output timseries)
x = layers.RepeatVector(output_num_timesteps)(x)

# a stack of RNN layers on top of each other (this is optional)
x = layers.LSTM(..., return_sequences=True)(x)
# ...
out = layers.LSTM(output_num_features, return_sequneces=True)(x)

model = models.Model(inp, out)

作为特殊情况,如果输出时间步数为1(例如,网络试图根据过去的t个时间步预测下一个时间步),我们可能不需要使用重复,而可以直接使用一个Dense层(在这种情况下,模型的输出形状将是(None,output_num_features),而不是(None,1,output_num_features))。
inp = layers.Input(shape=(input_num_timesteps, input_num_features))

# a stack of RNN layers on top of each other (this is optional)
x = layers.LSTM(..., return_sequences=True)(inp)
# ...
x = layers.LSTM(...)(x)  # The last layer ONLY returns the last output of RNN (i.e. return_sequences=False)

out = layers.Dense(output_num_features, activation=...)(x)

model = models.Model(inp, out)

请注意,上文提供的架构仅用于说明,您可能需要调整或适应它们,例如通过添加更多层(如Dense层),具体取决于您的用例和所要解决的问题。


更新:问题在于您在阅读时没有足够的注意力,无论是我的评论和答案还是Keras引发的错误。该错误明确指出:

... Found 1 input samples and 2 target samples.

因此,在仔细阅读这篇文章后,如果我是您,我会对自己说:“好的,Keras认为输入批次有1个输入样本,但我认为我提供了两个样本!!既然我是一个非常好的人(!),那么很可能是我错了,而不是Keras错了。所以让我们找出我做错了什么!”。一个简单而快速的检查方法就是只需检查输入数组的形状:

>>> np.array([[[0, 1, 2, 3, 4],
               [5, 6, 7, 8, 9]]]).shape
(1,2,5)

“哦,它显示的是(1,2,5)!这意味着有一个样本其有两个时间步长,每个时间步长有五个特征!!! 所以我之前的理解错了,以为这个数组包含了两个长度为5且每个时间步长的长度为1的样本!! 那我现在该怎么办呢?” 好的,你可以逐步修复:

# step 1: I want a numpy array
s1 = np.array([])

# step 2: I want it to have two samples
s2 = np.array([
               [],
               []
              ])

# step 3: I want each sample to have 5 timesteps of length 1 in them
s3 = np.array([
               [
                [0], [1], [2], [3], [4]
               ],
               [
                [5], [6], [7], [8], [9]
               ]
              ])

>>> s3.shape
(2, 5, 1)

哇!我们做到了!这是输入数组;现在检查目标数组,它必须有两个目标样本,每个样本长度为5,具有一个特征,即形状为(2, 5, 1)

>>> np.array([[ 5,  6,  7,  8,  9],
              [10, 11, 12, 13, 14]]).shape
(2,5)

差不多了!最后一维(即1)缺失了(注意:根据您的模型架构,您可能需要或不需要该最后一个轴)。因此,我们可以使用上面逐步的方法来找到我们的错误,或者我们可以聪明地只需在末尾添加一个维度:

>>> t = np.array([[ 5,  6,  7,  8,  9],
                  [10, 11, 12, 13, 14]])
>>> t = np.expand_dims(t, axis=-1)
>>> t.shape
(2, 5, 1)

抱歉,我不能比这解释得更好!但无论如何,当你在我的评论和答案中看到某些东西(例如输入/目标数组的形状)一遍又一遍地重复出现时,请假设这一定是重要的内容,应该加以检查。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接