Keras:如何调整CNN和LSTM层的输入?

Jam*_*ams 2 python conv-neural-network lstm keras

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

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

我已经使用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
Run Code Online (Sandbox Code Playgroud)

我的假设是,此模型将采用形状(特征,经度,经度,时间)的数据,因此,例如,如果我的地理空间网格为180 x 360,并且每个点有100个时间步长,并且每个观测值有4个特征,样本,则形状将为(4,180,360,100)。

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

谁能建议我如何最好地整形数据以进行输入,和/或如何以最适合此应用程序的方式构造模型层?提前致谢...

tod*_*day 5

好吧,我认为最好将数据重塑为(time, lats, lons, features),即,这是一个多通道(即要素)空间地图的时间序列:

data = np.transpose(data, [3, 1, 2, 0])
Run Code Online (Sandbox Code Playgroud)

然后,您可以轻松地在图层中包装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))))
Run Code Online (Sandbox Code Playgroud)

到目前为止,我们将拥有形状为的张量(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))
Run Code Online (Sandbox Code Playgroud)

然后我们需要回去。因此,我们需要先重塑LSTM层的结果,使其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')))
Run Code Online (Sandbox Code Playgroud)

这是模型摘要:

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

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

进一步说明:

  • As层和参数的数量增加(即,模型变深),则可能需要处理诸如消失梯度(问题12)和过拟合(123)。前者的一种解决方案是BatchNormalization在每个(可训练的)层之后立即使用层,以确保馈送到下一层的数据被规范化。为了防止过度拟合,您可以使用Dropout图层(和/或图层中的set dropoutrecurrent_dropoutarguments LSTM)。

  • 正如您在上面看到的,我假设我们正在向模型提供长度为50的时间序列。这与数据预处理步骤有关,在此步骤中,您需要从整个(长)时间序列中创建加窗训练(和测试)样本并将其提供给它们分批对您的模型进行训练。

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

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

  • 显然,以上代码仅用于演示,您可能需要调整其超参数(例如,层数,过滤器数,内核大小,使用的优化程序,激活函数等)并进行实验(很多!)以实现最终效果工作模型的准确性很高。

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

  • 并且不要忘了规范化训练数据(这非常重要,并且对训练过程有很大帮​​助)。