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

Mar*_*kus 11 python generator keras tensorflow recurrent-neural-network

内容:

我目前正在使用带有Tensorflow后端的Keras进行时间序列预测,因此研究了此处提供的教程。

在学习完本教程之后,我开始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
Run Code Online (Sandbox Code Playgroud)

在本教程中TimeSeriesGenerator,使用,但是我的问题是,如果使用自定义生成器或此类,它是次要的。关于数据,我们有8个step_per_epoch和一个形状为(8,1,2,2)的样本。生成器被馈送到由LSTM实现的递归神经网络。

我的问题

fit_generator()每批只允许一个目标,如的输出TimeSeriesGenerator。当我第一次阅读关于fit()的批处理选项时,我以为我可以有多个样本和相应数量的目标(逐批处理,意味着逐行处理)。但这是不允许的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
  ---------------------------------------------
...
Run Code Online (Sandbox Code Playgroud)

其次,我认为,例如,[10,15]和[20,25]被用作目标[30,35]的RNN的连续输入,这意味着这类似于输入[10,15,20 ,25]。由于使用第二种方法(我对其进行了测试),RNN的输出有所不同,因此这也必须是错误的结论。

因此,我的问题是:

  1. 为什么每批只允许一个目标(我知道有一些解决方法,但是必须有原因)?
  2. 我如何理解一批的计算?意思是,某些输入如何[[[40, 45], [50, 55]]] => [[60, 65]]处理,为什么不类似于 [[[40, 45, 50, 55]]] => [[60, 65]]



根据今天的答案进行编辑
由于我对样本和目标的定义存在一些误解-我遵循我理解的Keras在说时要告诉我的内容:

ValueError:输入数组应具有与目标数组相同数量的样本。找到1个输入样本和2个目标样本。

例如,当我创建如下所示的批处理时,就会发生此错误:

#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]]))
Run Code Online (Sandbox Code Playgroud)

假定这是一个包含两个长度为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看到了2个目标样本(在ValueError中),因为我有两个提供3D样本作为输入,而提供2D目标作为输出(也许我只是不知道如何提供3D目标。)。

无论如何,根据@todays的答复/评论,Keras将其解释为两个时间步长和五个功能。关于我的第一个问题(在这个编辑示例中,我仍然在其中看到序列作为序列的目标),我寻求信息以了解如何/是否可以实现这一目标以及该批次的外观(例如,我试图在问题)。

tod*_*day 4

简短的答案:

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

事实并非如此。批次中的目标样品数量没有限制。唯一的要求是每批中的输入样本和目标样本数量应相同。阅读长答案以获得进一步的说明。

如何理解一批的计算?意思是,某些输入是如何[[[40, 45], [50, 55]]] => [[60, 65]]处理的以及为什么它不类似于[[[40, 45, 50, 55]]] => [[60, 65]]

第一个是多变量时间序列(即每个时间步长有多个特征),第二个是单变量时间序列(即每个时间步长有一个特征)。所以它们并不等价。阅读长答案以获得进一步的说明。

长答案:

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

我认为你正在混合样本、时间步长、特征和目标。让我描述一下我的理解:在您提供的第一个示例中,似乎每个输入样本都由 2 个时间步长组成,例如[10, 15][20, 25],其中每个时间步长由两个特征组成,例如 10 和 15 或 20 和 25。此外,相应的target 由一个时间步组成,例如[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)
Run Code Online (Sandbox Code Playgroud)

2)输入和输出时间步数不同(即input_num_timesteps ~= output_num_timesteps)。这通常是通过首先使用一个或多个 LSTM 层的堆栈将输入时间序列编码为向量,然后重复该向量output_num_timesteps时间以获得所需长度的时间序列来实现的。对于重复操作,我们可以轻松地使用RepeatVectorKeras 中的层。同样,作为一个例子,以下模型可以实现这一点:

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)
Run Code Online (Sandbox Code Playgroud)

作为一种特殊情况,如果输出时间步数为 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)
Run Code Online (Sandbox Code Playgroud)

请注意,上面提供的架构仅用于说明,您可能需要Dense根据您的用例和您要解决的问题来调整或调整它们,例如添加更多层(例如层)。


更新:问题是你在阅读我的评论和回答以及Keras提出的错误时没有给予足够的重视。该错误明确指出:

...找到 1 个输入样本和 2 个目标样本。

因此,仔细阅读本文后,如果我是你,我会对自己说:“好吧,Keras 认为输入批次有 1 个输入样本,但我认为我提供了两个样本!!因为我是一个非常好的人(! ),我认为我很可能会比 Keras 错,所以让我们看看我做错了什么!”。一个简单而快速的检查就是检查输入数组的形状:

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

“哦,它说(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)
Run Code Online (Sandbox Code Playgroud)

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

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

几乎!最后一个维度(即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)
Run Code Online (Sandbox Code Playgroud)

抱歉,我无法比这更好地解释了!但无论如何,当您看到某些内容(即输入/目标数组的形状)在我的评论和答案中一遍又一遍地重复时,请假设它一定是重要的内容并且应该进行检查。