一点简短的ConvLSTM笔记

Author: sandyzikun

如题,是一份slides
等有空整理出来。

Download

其实真写出来感觉学过的人不用看,给没学过的人看也没讲清楚。

Intro

ConvLSTM1是由 Xingjian Shi 博士提出的深度学习模型。其定义是递进的,为了说明什么是ConvLSTM,笔者将会先后浅谈以下几个模型:

  • FC (Full Connection, 全连接层)
  • RNN (Recurrent Neural Network, 循环神经网络)
  • LSTM (Long-Short Term Memory, 长短期记忆网络)
  • ConvLSTM

但之所以要从FC倒着讲回ConvLSTM,原因在于:
ConvLSTMLSTM中使用卷积运算代替矩阵乘法的模型,
LSTMRNN中引入「细胞状态」的模型,
RNNFC对时间序列特化形成的模型,
FC是神经网络的基础结构。

FC

FC的全称为 Full Connection ,是意为全连接层的结构。
最为早期的神经网络是输入、输出矢量,矢量中的每一个分量在网络中根据不同的加权,进行线性组合形成新的矢量。

以上图为例,当我们输入矢量 $x = (x_1, x_2, \cdots, x_n )^\mathrm{T}$ 之后,矢量x会经过如下变换:

亦即:

上式可以表示为矩阵乘法:

在上图中对应着的超参数 m=500, n=800 ,分别为输出节点数(矢量维数)与输入节点数(矢量维数),亦是权重矩阵W的尺寸。
但全连接层在使用时除了权重矩阵W外,也会常常结合偏置矢量(bias)b与激励函数2(activation)f使用,此时真正的输出为:

关于偏置矢量与激励函数,笔者在此暂不赘述。

RNN

在考虑何为LSTM之前让我们先明确一下RNN
众所周知RNN是一种机器学习模型,用于处理时间序列,这里的时间顺序不仅可以是传统意义上的时间顺序,也可以认为是借由因果关系串联而成的事情发展顺序。

例如「随着某课程章节推进,每一章作业量所组成的序列」,在此选取的每个元素即「每一章节的作业量」所对应的时间间隔并不均匀(课程中讲解重点的章节所花费的时间远比一般的章节要长),所以此处的「时间」为课程中章节的推进而非传统的年月日时分秒等…

RNN是为了方便处理时间序列而对FC进行优化后的机器学习模型,其引入了一个非常重要的「时间」维度,并以一个单独的矢量存储每一时刻的状态。

刚才提到全连接层的实质就是矩阵乘法,那么从上图我们可以看到权重矩阵 U, W, V 形成了三个以全连接层为基础的结构。

现在我们来解释为什么RNNFC对时间序列的「特化」:

我们可以看到比起FCRNN在中间插入了一个隐藏层,其对应着矢量 $\vec{s_t}$ , $\vec{s_t}$ 同时受 $\vec{s_{t-1}}$ 与 $\vec{x_t}$ 的影响,这便是随着t不断变化,这便是用于存储「时间」状态的变量。

同时我们可以看出,FC输入时接收的是一个矢量 $\vec{x}$ ,而RNN输入的时候接收的是多个矢量组成的序列 $\vec{x_1}, \vec{x_2}, \cdots, \vec{x_T}$ 。

根据前面讲过的式子,对于每一个时刻t,都会根据 $\vec{s_t}$ 产生相应的 $\vec{o_t}$ ,这也对应着每一个时刻相应的输入 $\vec{x_t}$ 。

如何理解?

  • 我们先来看一例由Keras实现的FC

    Python
    1
    2
    3
    4
    5
    6
    7
    8
    >>> import keras
    >>> model = keras.Sequential([
    keras.layers.Dense(
    units = 128,
    input_shape = (32,),
    ),
    ])
    >>> model.summary()

    通过调用.summary()查看其结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    _________________________________________________________________
    Layer (type) Output Shape Param #
    =================================================================
    dense_1 (Dense) (None, 128) 4224
    =================================================================
    Total params: 4,224
    Trainable params: 4,224
    Non-trainable params: 0
    _________________________________________________________________
  • 再看一例由Keras实现的SimpleRNN

    Python
    1
    2
    3
    4
    5
    6
    7
    8
    9
    >>> import keras
    >>> model = keras.Sequential([
    keras.layers.Embedding(10000, 32),
    keras.layers.SimpleRNN(
    units = 128,
    return_sequences = True,
    ),
    ])
    >>> model.summary()

    通过调用.summary()查看其结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    _________________________________________________________________
    Layer (type) Output Shape Param #
    =================================================================
    embedding_1 (Embedding) (None, None, 32) 320000
    _________________________________________________________________
    simple_rnn_1 (SimpleRNN) (None, None, 128) 20608
    =================================================================
    Total params: 340,608
    Trainable params: 340,608
    Non-trainable params: 0
    _________________________________________________________________

相比于FC的输出形状(None, 128)RNN的输出形状(None, None, 128)中间多了一个None分量,代表着每次RNN不像FC一样只输出一个矢量,其输出的也是一个矢量序列,且该矢量序列的长度可以不固定。

所以我们可以看出,不同于对输入的一个矢量进行处理的FCRNN是对输入的一个矢量序列进行处理的模型,其中存在着以(随时间变化)矢量 $\vec{s_t}$ 用于存储模型随时间的状态改变,这是RNN能够处理时间序列的结构基础。

虽然RNN输入的是一个矢量序列,但如若只需要最后时刻的结果,其输出的也可以只是输出的矢量序列中最后一个,相当于舍弃了前面的 $\vec{o_1}, \vec{o_2}, \cdots, \vec{o_{T-1}}$ 从而只保留 $\vec{o_T}$ 一个矢量。

Keras也可以实现只返回最后一个输出向量的RNN

Python
1
2
3
4
5
6
7
8
9
>>> import keras
>>> model = keras.Sequential([
keras.layers.Embedding(10000, 32),
keras.layers.SimpleRNN(
units = 128,
return_sequences = False,
),
])
>>> model.summary()

通过调用.summary()查看其结构:

1
2
3
4
5
6
7
8
9
10
11
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_2 (Embedding) (None, None, 32) 320000
_________________________________________________________________
simple_rnn_2 (SimpleRNN) (None, 128) 20608
=================================================================
Total params: 340,608
Trainable params: 340,608
Non-trainable params: 0
_________________________________________________________________

LSTM

LSTM的诞生来源于在表示时间状态的向量 $\vec{h_t}$ 不断变化时,输入时刻靠前的输入向量的特征较早吸收,但会被后来者「冲淡」。
为了解决这一问题,保证靠前的输入向量的特征也能较好地吸收,LSTM应运而生。

解决的方式很简单, $\vec{h_t}$ 是一个不断接收上一时刻 $\vec{h_{t-1}}$ 与此时刻输入向量 $\vec{x_t}$ 的状态向量,那么我们引入一个与之不同,只随着上一时刻的 $\vec{c_{t-1}}$ 变化、不直接接收此时的输入向量 $\vec{x_t}$ 的另一个状态向量 $\vec{c_t}$ 即可。

从图中我们可以看到 $\vec{c_t}$ 为 $\vec{c_{t-1}}$ 吸取经过处理的 $\vec{h_{t-1}}$ 与 $\vec{x_t}$ 的特征而不断迭代的状态向量,而 $\vec{h_t}$ 的每一次迭代为根据 $\vec{h_{t-1}}$ 与 $\vec{x_t}$ 的重构,其迭代过程远比 $\vec{c_t}$ 的要复杂,因此变化也比 $\vec{c_t}$ 要剧烈,变化更快。
不同于 $\vec{c_t}$ 变化缓慢,带有的记忆更长, $\vec{h_t}$ 所带有的记忆更短,变化更快。
这是LSTM能够同时记住近期与早期输入数据特征,使靠前的输入向量的特征也能较好吸收的关键结构。

整理一下:

  • $\vec{h_t}$ 的变化所受因素更多,迭代程度更剧烈、迭代速度更快,构成LSTM的短期记忆;
  • $\vec{c_t}$ 的变化所受因素较少,迭代程度不明显、迭代速度更慢,构成LSTM的长期记忆。

所以这个模型被称为 Long-Short Term Memory ,长短期记忆网络。

ConvLSTM

在进入LSTM之前,我们先看一看卷积(Convolution),一种特殊的矩阵间乘法运算。
虽然说是乘法运算,但对于参与卷积运算的两个矩阵,其两者位置不能随意交换。
因为卷积运算可以视为由尺寸较小的一个矩阵在尺寸较大的矩阵上「取样」,把不同位置取到的结果根据相对位置关系排列为新的矩阵。
我们看如下的动图简单理解一下:

当我们的输入为向量序列的时候,每一时刻输入模型的是该时刻的向量,在进行乘法的时候直接使用矩阵乘法即可,但是矩阵乘法的实质(于右位而言)是对矩阵中不同列的线性变换与组合,无法提取不同行之间的相关特征,因此在输入为矩阵序列而非向量序列的时候,我们使用卷积替代传统的矩阵乘法。

References

1. X. Shi, Z. Chen, H. Wang, D.-Y. Yeung, W.-K. Wong, and W.-C. Woo, “Convolutional LSTM Network: A Machine Learning Approach for Precipitation Nowcasting”, 2015, arXiv: 1506.04214
2. 百度百科词条 激活函数