convLSTM2D 层使用方法解析(Keras库)

image-20230507121348596

最近在研究时序图像分类问题,需要用到convLSTM层提取特征,所以在此仔细分析一下keras.layers.ConvLSTM2D层的使用方法。深度学习框架是tensorflow

官方文档:recurrent/#convlstm2d - Keras 中文文档

下面这部分内容摘自官方文档


ConvLSTM2D

keras.layers.ConvLSTM2D(filters, 
                        kernel_size, 
                        strides=(1, 1),
                        padding='valid', 
                        data_format=None, 
                        dilation_rate=(1, 1), 
                        activation='tanh', 
                        recurrent_activation='hard_sigmoid',
                        use_bias=True, 
                        kernel_initializer='glorot_uniform',
                        recurrent_initializer='orthogonal', 
                        bias_initializer='zeros',
                        unit_forget_bias=True,
                        kernel_regularizer=None, 
                        recurrent_regularizer=None, 
                        bias_regularizer=None, 
                        activity_regularizer=None, 
                        kernel_constraint=None, 
                        recurrent_constraint=None,
                        bias_constraint=None, 
                        return_sequences=False, 
                        go_backwards=False,
                        stateful=False,
                        dropout=0.0,
                        recurrent_dropout=0.0)

卷积 LSTM。

它类似于 LSTM 层,但输入变换和循环变换都是卷积的。

参数

  • filters: 整数,输出空间的维度 (即卷积中滤波器的输出数量)。
  • kernel_size: 一个整数,或者 n 个整数表示的元组或列表, 指明卷积窗口的维度。
  • strides: 一个整数,或者 n 个整数表示的元组或列表, 指明卷积的步长。 指定任何 stride 值 != 1 与指定 dilation_rate 值 != 1 两者不兼容。
  • padding: "valid""same" 之一 (大小写敏感)。
  • data_format: 字符串, channels_last (默认) 或 channels_first 之一。 输入中维度的顺序。 channels_last 对应输入尺寸为 (batch, time, ..., channels)channels_first 对应输入尺寸为 (batch, time, channels, ...)。 它默认为从 Keras 配置文件 ~/.keras/keras.json 中 找到的 image_data_format 值。 如果你从未设置它,将使用 "channels_last"
  • dilation_rate: 一个整数,或 n 个整数的元组/列表,指定用于膨胀卷积的膨胀率。 目前,指定任何 dilation_rate 值 != 1 与指定 stride 值 != 1 两者不兼容。
  • activation: 要使用的激活函数 (详见 activations)。 如果传入 None,则不使用激活函数 (即 线性激活:a(x) = x)。
  • recurrent_activation: 用于循环时间步的激活函数 (详见 activations)。
  • use_bias: 布尔值,该层是否使用偏置向量。
  • kernel_initializer: kernel 权值矩阵的初始化器, 用于输入的线性转换 (详见 initializers)。
  • recurrent_initializer: recurrent_kernel 权值矩阵 的初始化器,用于循环层状态的线性转换 (详见 initializers)。
  • bias_initializer:偏置向量的初始化器 (详见initializers).
  • unit_forget_bias: 布尔值。 如果为 True,初始化时,将忘记门的偏置加 1。 将其设置为 True 同时还会强制 bias_initializer="zeros"。 这个建议来自 Jozefowicz et al.
  • kernel_regularizer: 运用到 kernel 权值矩阵的正则化函数 (详见 regularizer)。
  • recurrent_regularizer: 运用到 recurrent_kernel 权值矩阵的正则化函数 (详见 regularizer)。
  • bias_regularizer: 运用到偏置向量的正则化函数 (详见 regularizer)。
  • activity_regularizer: 运用到层输出(它的激活值)的正则化函数 (详见 regularizer)。
  • kernel_constraint: 运用到 kernel 权值矩阵的约束函数 (详见 constraints)。
  • recurrent_constraint: 运用到 recurrent_kernel 权值矩阵的约束函数 (详见 constraints)。
  • bias_constraint: 运用到偏置向量的约束函数 (详见 constraints)。
  • return_sequences: 布尔值。是返回输出序列中的最后一个输出,还是全部序列。
  • go_backwards: 布尔值 (默认 False)。 如果为 True,则向后处理输入序列并返回相反的序列。
  • stateful: 布尔值 (默认 False)。 如果为 True,则批次中索引 i 处的每个样品的最后状态 将用作下一批次中索引 i 样品的初始状态。
  • dropout: 在 0 和 1 之间的浮点数。 单元的丢弃比例,用于输入的线性转换。
  • recurrent_dropout: 在 0 和 1 之间的浮点数。 单元的丢弃比例,用于循环层状态的线性转换。

输入尺寸

  • 如果 data_format=‘channels_first’, 输入 5D 张量,尺寸为: (samples,time, channels, rows, cols)
  • 如果 data_format=‘channels_last’, 输入 5D 张量,尺寸为: (samples,time, rows, cols, channels)

输出尺寸

  • 如果return_sequences =true
    • 如果 data_format=‘channels_first’,返回 5D 张量,尺寸为:(samples, time, filters, output_row, output_col)
    • 如果 data_format=‘channels_last’,返回 5D 张量,尺寸为:(samples, time, output_row, output_col, filters)
  • 否则,
    • 如果 data_format =‘channels_first’,返回 4D 张量,尺寸为:(samples, filters, output_row, output_col)
    • 如果 data_format=‘channels_last’,返回 4D 张量,尺寸为:(samples, output_row, output_col, filters)

o_row 和 o_col 取决于 filter 和 padding 的尺寸。


为了更深度了解ConvLSTM2D层的用法,我在Keras官方仓库里找到了ConvLSTM2D层的测试程序:

keras/conv_lstm_test.py at master · keras-team/keras · GitHub

下面我们基于官方例程,看一下如何调用此网络层

测试例程1

def test_conv_lstm(self, data_format, return_sequences):
    	# 卷积核的长和宽
        num_row = 3
        num_col = 3
        # 卷积核的数量=输出的通道数
        filters = 2
        # 样本数量
        num_samples = 1
        # 输入数据的通道数C
        input_channel = 2
        # 输入图像的大小H*W
        input_num_row = 5
        input_num_col = 5
        # 输入图像时间序列的长度S
        sequence_len = 2
        # 参数:data_format: 字符串, "channels_last" (默认) 或 "channels_first"
        #      代表输入数据维度的顺序,channels_last代表通道维在最后一维
        if data_format == "channels_first":
            inputs = np.random.rand(
                num_samples,
                sequence_len,
                input_channel,
                input_num_row,
                input_num_col,
            )
        else:
            inputs = np.random.rand(
                num_samples,
                sequence_len,
                input_num_row,
                input_num_col,
                input_channel,
            )

        # test for return state:
        x = keras.Input(batch_shape=inputs.shape)
        # 参数:return_sequences,布尔值。是返回输出序列中的最后一个输出,还是全部序列
        # 参数:stateful,布尔值 (默认 False)。 如果为 True,
        #     则批次中索引 i 处的每个样品的最后状态 将用作下一批次中索引 i 样品的初始状态。
        # 参数:return_state:,布尔值。除了输出之外是否返回最后一个状态。
        kwargs = {
            "data_format": data_format,
            "return_sequences": return_sequences,
            "return_state": True,
            "stateful": True,
            "filters": filters,
            "kernel_size": (num_row, num_col),
            "padding": "valid",
        }
        layer = keras.layers.ConvLSTM2D(**kwargs)
        layer.build(inputs.shape)
        outputs = layer(x)
        # 返回输出和状态变量
        _, states = outputs[0], outputs[1:]
        self.assertEqual(len(states), 2)
        # states[0] 表示整个神经网络中的第一个状态变量
        model = keras.models.Model(x, states[0])
        state = model.predict(inputs)

        self.assertAllClose(
            keras.backend.eval(layer.states[0]), state, atol=1e-4
        )

        # test for output shape:
        test_utils.layer_test(
            keras.layers.ConvLSTM2D,
            kwargs={
                "data_format": data_format,
                "return_sequences": return_sequences,
                "filters": filters,
                "kernel_size": (num_row, num_col),
                "padding": "valid",
            },
            input_shape=inputs.shape,
        )

测试例程2

def test_conv_lstm_statefulness(self):
        # Tests for statefulness
        num_row = 3
        num_col = 3
        filters = 2
        num_samples = 1
        input_channel = 2
        input_num_row = 5
        input_num_col = 5
        sequence_len = 2
        inputs = np.random.rand(
            num_samples,
            sequence_len,
            input_num_row,
            input_num_col,
            input_channel,
        )

        with self.cached_session():
            model = keras.models.Sequential()
            # "return_sequences" = False,只返回最后一个预测结果
            # "stateful": True, 上次预测得到的记忆输出作为下次记忆的输入
            kwargs = {
                "data_format": "channels_last",
                "return_sequences": False,
                "filters": filters,
                "kernel_size": (num_row, num_col),
                "stateful": True,
                "batch_input_shape": inputs.shape,
                "padding": "same",
            }
            layer = keras.layers.ConvLSTM2D(**kwargs)

            model.add(layer)
            model.compile(optimizer="sgd", loss="mse")
            out1 = model.predict(np.ones_like(inputs))

            # train once so that the states change
            model.train_on_batch(
                np.ones_like(inputs), np.random.random(out1.shape)
            )
            out2 = model.predict(np.ones_like(inputs))

            # 如果状态变量没有重置,2个的输出结果是不同的
            self.assertNotEqual(out1.max(), out2.max())

            # 重置层的状态,再次进行预测,输出结果应该是相同的
            layer.reset_states()
            out3 = model.predict(np.ones_like(inputs))
            self.assertNotEqual(out3.max(), out2.max())

            # check that container-level reset_states() works
            model.reset_states()
            out4 = model.predict(np.ones_like(inputs))
            self.assertAllClose(out3, out4, atol=1e-5)

            # check that the call to `predict` updated the states
            out5 = model.predict(np.ones_like(inputs))
            self.assertNotEqual(out4.max(), out5.max())

测试例程3

 def test_conv_lstm_regularizers(self):
        # check regularizers
        num_row = 3
        num_col = 3
        filters = 2
        num_samples = 1
        input_channel = 2
        input_num_row = 5
        input_num_col = 5
        sequence_len = 2
        inputs = np.random.rand(
            num_samples,
            sequence_len,
            input_num_row,
            input_num_col,
            input_channel,
        )
		# 添加权值矩阵的L2正则化函数
        # 
        with self.cached_session():
            kwargs = {
                "data_format": "channels_last",
                "return_sequences": False,
                "kernel_size": (num_row, num_col),
                "stateful": True,
                "filters": filters,
                "batch_input_shape": inputs.shape,
                "kernel_regularizer": keras.regularizers.L1L2(l1=0.01),
                "recurrent_regularizer": keras.regularizers.L1L2(l1=0.01),
                "activity_regularizer": "l2",
                "bias_regularizer": "l2",
                "kernel_constraint": "max_norm",
                "recurrent_constraint": "max_norm",
                "bias_constraint": "max_norm",
                "padding": "same",
            }

            layer = keras.layers.ConvLSTM2D(**kwargs)
            layer.build(inputs.shape)
            self.assertEqual(len(layer.losses), 3)
            layer(keras.backend.variable(np.ones(inputs.shape)))
            self.assertEqual(len(layer.losses), 4)

测试例程4

def test_conv_lstm_with_initial_state(self):
        num_samples = 32
        sequence_len = 5
        encoder_inputs = keras.layers.Input((None, 32, 32, 3))
        encoder = keras.layers.ConvLSTM2D(
            filters=32,
            kernel_size=(3, 3),
            padding="same",
            return_sequences=False,
            return_state=True,
        )
        _, state_h, state_c = encoder(encoder_inputs)
        encoder_states = [state_h, state_c]

        decoder_inputs = keras.layers.Input((None, 32, 32, 4))
        decoder_lstm = keras.layers.ConvLSTM2D(
            filters=32,
            kernel_size=(3, 3),
            padding="same",
            return_sequences=False,
            return_state=False,
        )
        decoder_outputs = decoder_lstm(
            decoder_inputs, initial_state=encoder_states
        )
        output = keras.layers.Conv2D(
            1, (3, 3), padding="same", activation="relu"
        )(decoder_outputs)
        model = keras.Model([encoder_inputs, decoder_inputs], output)

        model.compile(
            optimizer="sgd",
            loss="mse",
            run_eagerly=test_utils.should_run_eagerly(),
        )
        x_1 = np.random.rand(num_samples, sequence_len, 32, 32, 3)
        x_2 = np.random.rand(num_samples, sequence_len, 32, 32, 4)
        y = np.random.rand(num_samples, 32, 32, 1)
        model.fit([x_1, x_2], y)

        model.predict([x_1, x_2])

总结

对于例程和例程4目前我还不是很理解,没有实际用过。但是按照我个人的理解ConvLSTM2D有如下几个重要的参数:

  1. filters: 整数,输出空间的维度

  2. kernel_size: 一个整数,或者 n 个整数表示的元组或列表, 指明卷积窗口的维度

  3. return_sequences,布尔值。是返回输出序列中的最后一个输出,还是全部序列

  4. return_state:,布尔值。除了输出之外是否返回最后一个状态。

假设输入图像的维度为(T*H*W*C),基于上述参数总结了3个如下用法:

  1. 预测:输出图像时间序列的下一张图像预测结果(H*W*C1)

    x = ConvLSTM2D(filters=32, kernel_size=(3, 3), padding='same', return_sequences= return_sequences=False)
    
  2. 多层网络:构建多层convLSTM网络(T*H*W*C1)

    x = ConvLSTM2D(filters=32, kernel_size=(3, 3), padding='same', return_sequences=True)(inputs)
    x = ConvLSTM2D(filters=32, kernel_size=(3, 3), padding='same', return_sequences=True)(x)
    
  3. 特征提取:提取输入影像的时间维度特征,使用记忆变量C作为输出(H*W*C1)

    _, _, state_c = ConvLSTM2D(filters=32, kernel_size=(1, 1),activation='relu',padding='same',return_sequences=False,return_state=True)(input_tensor)
    
  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值