【问题标题】:Keras: How to shape inputs for CNN and LSTM layers?Keras:如何塑造 CNN 和 LSTM 层的输入?
【发布时间】:2018-09-28 20:15:54
【问题描述】:

我正在构建一个模型来预测地理时空数据集。

我的数据具有原始维度(特征、纬度、经度、时间),即对于每个特征以及每个纬度/经度点都有一个时间序列。

我已经像这样使用 Keras 创建了一个 CNN-LSTM 模型(我假设需要修改以下内容,这只是第一次尝试):

def define_model_cnn_lstm(features, lats, lons, times):
    """
    Create and return a model with CN and LSTM layers. Input and output data is 
    expected to have shape (lats, lons, times).

    :param lats: latitude dimension of input 3-D array 
    :param lons: longitude dimension of input 3-D array
    :param times: time dimension of input 3-D array
    :return: CNN-LSTM model appropriate to the expected input array
    """
    # define the CNN model layers, wrapping each CNN layer in a TimeDistributed layer
    model = Sequential()
    model.add(TimeDistributed(Conv2D(features, (3, 3), 
                                     activation='relu', 
                                     padding='same', 
                                     input_shape=(lats, lons, times))))
    model.add(TimeDistributed(MaxPooling2D(pool_size=(2, 2))))
    model.add(TimeDistributed(Flatten()))

    # add the LSTM layer, and a final Dense layer
    model.add(LSTM(units=times, activation='relu', stateful=True))
    model.add(Dense(1))

    model.compile(optimizer='adam', loss='mse')

    return model

我的假设是该模型将采用形状(特征、纬度、经度、时间)的数据,例如,如果我的地理空间网格是 180 x 360 并且每个点有 100 个时间步长,并且我有 4 个特征每个观察/样本,那么形状将是 (4, 180, 360, 100)。

我假设我希望模型将具有形状(特征、纬度、经度、时间)的数组作为输入,并能够预测具有形状(标签、纬度、经度、时间)的标签数组作为输出。我首先使用单个变量作为我的标签,但以后能够具有多变量输出(即标签 > 1)可能会很有趣。

我应该如何最好地塑造我的输入数据,和/或如何以最适合此应用程序的方式构建模型层?

【问题讨论】:

  • 你有多个地理空间网格,对吧?即整个训练数据看起来像(num_grids, features, lats, lons, time)?
  • 不,只有一个地理空间网格 (lats x lons) 的值。每个纬度/经度点都有多个特征(在上述示例中为 4 个)。
  • 那我有点糊涂了:你们不是有时间序列的多通道(即特征)空间图吗?你想预测什么?沿时间维度的时间序列的下一步?
  • 相信我,我是在这里感到困惑的人。如果我理解正确,我应该将其视为“多通道”数据集,即每个特征都是一个通道。我试图预测的是一个相应的数据集,其中 y == f(X),并且模型被用作 f()。例如,在每个纬度/经度,我们都有一个带有温度和湿度值(特征)的时间序列,模型应该能够预测相应的降水时间序列(标签)。
  • 还有一个问题:您提到您没有多个网格,所以您的意思是例如您只有一个形状为(4, 180, 360, 100)的训练数据?那将是太少的数据。那么有多少个时间步长呢?也许时间序列的长度太长了?!

标签: python keras conv-neural-network lstm


【解决方案1】:

好吧,我认为最好将您的数据重塑为(time, lats, lons, features),即它是多通道(即特征)空间图的时间序列:

data = np.transpose(data, [3, 1, 2, 0])

然后您可以轻松地将Conv2DMaxPooling2D 层包裹在TimeDistributed 层中,以在每个时间步处理(多通道)地图:

num_steps = 50
lats = 128
lons = 128
features = 4
out_feats = 3

model = Sequential()
model.add(TimeDistributed(Conv2D(16, (3, 3), activation='relu', padding='same'), 
                          input_shape=(num_steps, lats, lons, features)))
model.add(TimeDistributed(MaxPooling2D(pool_size=(2, 2))))
model.add(TimeDistributed(Conv2D(32, (3, 3), activation='relu', padding='same')))
model.add(TimeDistributed(MaxPooling2D(pool_size=(2, 2))))
model.add(TimeDistributed(Conv2D(32, (3, 3), activation='relu', padding='same')))
model.add(TimeDistributed(MaxPooling2D(pool_size=(2, 2))))

到目前为止,我们将有一个形状为 (50, 16, 16, 32) 的张量。然后我们可以使用Flatten 层(当然,包裹在TimeDistributed 层中以不丢失时间轴)并将结果提供给一个或多个LSTM 层(使用return_sequence=True 来获取每个时间步的输出):

model.add(TimeDistributed(Flatten()))

# you may stack multiple LSTM layers on top of each other here
model.add(LSTM(units=64, return_sequences=True))

然后我们需要回去。所以我们需要先对 LSTM 层的结果进行 reshape 使其成为 2D,然后使用 UpSampling2DConv2D 层的组合来恢复原始地图的形状:

model.add(TimeDistributed(Reshape((8, 8, 1))))
model.add(TimeDistributed(UpSampling2D((2,2))))
model.add(TimeDistributed(Conv2D(32, (3,3), activation='relu', padding='same')))
model.add(TimeDistributed(UpSampling2D((2,2))))
model.add(TimeDistributed(Conv2D(32, (3,3), activation='relu', padding='same')))
model.add(TimeDistributed(UpSampling2D((2,2))))
model.add(TimeDistributed(Conv2D(16, (3,3), activation='relu', padding='same')))
model.add(TimeDistributed(UpSampling2D((2,2))))
model.add(TimeDistributed(Conv2D(out_feats, (3,3), padding='same')))

这是模型摘要:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
time_distributed_132 (TimeDi (None, 50, 128, 128, 16)  592       
_________________________________________________________________
time_distributed_133 (TimeDi (None, 50, 64, 64, 16)    0         
_________________________________________________________________
time_distributed_134 (TimeDi (None, 50, 64, 64, 32)    4640      
_________________________________________________________________
time_distributed_135 (TimeDi (None, 50, 32, 32, 32)    0         
_________________________________________________________________
time_distributed_136 (TimeDi (None, 50, 32, 32, 32)    9248      
_________________________________________________________________
time_distributed_137 (TimeDi (None, 50, 16, 16, 32)    0         
_________________________________________________________________
time_distributed_138 (TimeDi (None, 50, 8192)          0         
_________________________________________________________________
lstm_13 (LSTM)               (None, 50, 64)            2113792   
_________________________________________________________________
time_distributed_139 (TimeDi (None, 50, 8, 8, 1)       0         
_________________________________________________________________
time_distributed_140 (TimeDi (None, 50, 16, 16, 1)     0         
_________________________________________________________________
time_distributed_141 (TimeDi (None, 50, 16, 16, 32)    320       
_________________________________________________________________
time_distributed_142 (TimeDi (None, 50, 32, 32, 32)    0         
_________________________________________________________________
time_distributed_143 (TimeDi (None, 50, 32, 32, 32)    9248      
_________________________________________________________________
time_distributed_144 (TimeDi (None, 50, 64, 64, 32)    0         
_________________________________________________________________
time_distributed_145 (TimeDi (None, 50, 64, 64, 16)    4624      
_________________________________________________________________
time_distributed_146 (TimeDi (None, 50, 128, 128, 16)  0         
_________________________________________________________________
time_distributed_147 (TimeDi (None, 50, 128, 128, 3)   435       
=================================================================
Total params: 2,142,899
Trainable params: 2,142,899
Non-trainable params: 0
_________________________________________________________________

如您所见,我们有一个形状为 (50, 128, 128, 3) 的输出张量,其中 3 表示我们想要在每个时间步预测位置的所需标签的数量。

补充说明:

  • 随着层数和参数的增加(即模型变得更深),您可能需要处理梯度消失(12)和过拟合(1、@ 987654324@,3)。前者的一种解决方案是在每个(可训练的)层之后使用BatchNormalization 层,以确保馈送到下一层的数据是标准化的。为防止过度拟合,您可以使用Dropout 层(和/或在LSTM 层中设置dropoutrecurrent_dropout 参数)。

  • 正如您在上面看到的,我假设我们正在为模型提供长度为 50 的时间序列。这与数据预处理步骤有关,您需要从整个 ( long) 时间序列并将它们分批提供给您的模型进行训练。

  • 正如我在代码中所评论的,您可以在彼此之上添加多个 LSTM 层以增加网络的表示能力。但请注意,它可能会增加训练时间,并使您的模型(更多)容易过度拟合。如果你有正当的理由(即你已经尝试了一个 LSTM 层但没有得到好的结果),那就这样做吧。或者,您可以改用GRU 层,但与 LSTM 层相比,表示容量和计算成本(即训练时间)之间可能存在折衷。

  • 要使网络的输出形状与您的数据形状兼容,您可以在 LSTM 层之后使用Dense 层或调整最后一个 LSTM 层的单元数。

  • 显然,上面的代码只是为了演示,你可能需要调整它的超参数(例如层数、过滤器数量、内核大小、使用的优化器、激活函数等)和实验(很多!) 以实现高精度的最终工作模型。

  • 如果您在 GPU 上进行训练,则可以使用 CuDNNLSTM (CuDNNGRU) 层而不是 LSTM (GRU) 来提高训练速度,因为它已针对 GPU 进行了优化。

  • 别忘了对训练数据进行标准化(这非常重要,对训练过程有很大帮​​助)。

【讨论】:

  • 非常感谢您的帮助,这是金。
  • 就为 Conv2D、MaxPooling2D、LSTM、Reshape 和 UpSampling2D 层提供的各种数字参数而言:我是否可以为这些参数使用各种输入尺寸大小及其比率(例如LSTM 的单位参数或 Conv2D 的过滤器和 kernel_size 参数)在通用模型定义函数中,还是将这些值硬编码对应于已知的输入数据维度更常见?如果是这样,那么我不需要事先知道 lats/lons/steps 的数量,从而允许使用更通用的模型。可能野心太大……
  • @JamesAdams 好吧,当然你可以编写一个函数,将这些参数作为输入,并基于它们创建一个模型。它甚至可能会加快实验过程(不要忘记,正如我所说,您需要进行大量实验才能找到最终模型)。但是,使用了一些常用值。例如,卷积层的内核大小通常是 3 或 5,或者过滤器的数量是 2 的幂(16、32、64,...),随着我们深入,过滤器的数量会增加(并且空间维度会减少,因为应用 MaxPooling2D)。 >>>
  • @JamesAdams >>> 最后,关于超参数没有明确的答案,它是一个活跃的研究领域。这实际上取决于您拥有的数据和您要解决的问题。有一些像 hyperas 这样的包可以进行这种超参数调整。
  • Conv2DTranspose代替UpSampling2D不是更好吗?
猜你喜欢
  • 1970-01-01
  • 2018-06-05
  • 2018-12-10
  • 2017-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-20
  • 2019-09-07
相关资源
最近更新 更多