循环神经网络(RNN)

为什么用RNN

在自然语言处理中,前面的内容对后面的内容有很大的影响,比如说:

今天天气很不错.

字后面是字的概率就比其他字大很多.同样字后面是字的概率也比较大.这就要求神经网络在做每一次预测的时候要结合上下文,每个时序的预测都能够结合能前面几次预测的结果.

普通的神经网络显然做不到这点,所以就有了循环神经网络 (RNN).

RNN的结构

一个基本的RNN结构如下图所示:

image

RNN由许多RNN单元组成,每一个单元处理一个时序. x^{ < t > } 为输入, y^{< t >} 为输出.RNN做预测时会结合前面的结果 a^{< t >} .

举个例子,我们要做一个翻译的任务,将中文:

今天天气很不错.

翻译成英文:

The weather is very good today.

此时输入x为中文,长度为 T_x , x^{<1>} ~ x^{<T_x>} 对应中文中的每个汉字.输出y为英文,长度为 T_y ,分别对应 y^{<1>} ~ y^{<T_y>} (后面会讨论 T_x T_y 不一致的情况).

需要注意的是,RNN模型并不是有很多RNN单元,而是将一个RNN单元在不同时序重复使用.

image

在RNN单元中,前面带来的影响 a^{\langle t-1 \rangle} 与当前时序带来的影响 x^{\langle t \rangle} 相融合:
a^{\langle t \rangle} = \tanh(W_{aa} a^{\langle t-1 \rangle} + W_{ax} x^{\langle t \rangle} + b_a)

然后RNN会使用融合后的影响 a^{\langle t \rangle} 进行预测,另外 a^{\langle t \rangle} 也会被传递到下一个时序:
\hat{y}^{\langle t \rangle} = softmax(W_{ya} a^{\langle t \rangle} + b_y)

RNN形态

为了满足不同的需求,RNN的形态有很多种.但是他们都有一个共同的特征就是每个RNN单元都会结合前面RNN单元所产生的影响.

在翻译或是语言识别领域, T_x T_y 相差很大,这种情况,我们使用Many-to-Many类型的RNN网络:

image

在情绪分类中,需要将一段文字转化为一个评分或是等级, T_x 可能会很大,但是 T_y 为1,这种情况是Many-to-One类型的RNN:

image

同样我们可以得到One-to-Many类型的RNN:

image

当然还有 T_x T_y 相等的Many-to-Many类型的RNN:

image

梯度爆炸与梯度消失

RNN的训练过程也是根据反向传播来更新权值的,因为RNN的链式结构,所以反向传播会计算比较深层的导数.

举个简单的例子,我们用反向传播对 b_1 求导:

image

得到:

{ \frac { \partial C } { \partial b _ { 1 } } = \frac { \partial C } { \partial y _ { 4 } } \frac { \partial y _ { 4 } } { \partial z _ { 4 } } \frac { \partial z _ { 4 } } { \partial x _ { 4 } } \frac { \partial x _ { 4 } } { \partial z _ { 3 } } \frac { \partial z _ { 3 } } { \partial x _ { 3 } } \frac { \partial x _ { 3 } } { \partial z _ { 2 } } \frac { \partial z _ { 2 } } { \partial x _ { 2 } } \frac { \partial x _ { 2 } } { \partial z _ { 1 } } \frac { \partial z _ { 1 } } { \partial b _ { 1 } } }

导数是很多项的连成,可以想象RNN的连乘比这还要多,连乘数过多就会造成一个现象,如果这些数都比较大,就会造成最终的结果比较大,甚至超出浮点数最大值,这就是梯度爆炸.如果这些数都比较小,那么会造成最终的结果非常小,导致前面的参数更新困难,这是梯度消失.梯度消失会导致RNN只有短期的记忆.

RNN的缺点并不能完全避免,但是以下两种RNN的升级版GRU及LSTM在一定程度上克服了RNN的弱点.

GRU

image

GRU比普通RNN多了一个记忆单元 c^{<t>} , c^{<t>} 的值与 a^{<t>} 相等.

GRU首先计算当前时序 x^{<t>} 对记忆单元的影响:

\tilde{c} ^ { < t > } = \tanh \left( W _ { c } \left[ c ^ { < t - 1 > } , x ^ { < t > } \right] + b _ { c } \right)

然后用 \tilde{c} ^ { < t > } 计算出了一个阀值:

\Gamma _ { u } = \sigma \left( W _ { u } \left[ c ^ { < t - 1 > } , x ^ { < t > } \right] + b _ { u } \right)

然后用这个反之 \Gamma _ { u } 来调节记忆对当前决策的影响:

c ^ { < t > } = \Gamma _ { u } * \tilde{c} ^ { < t > } + \left( 1 - \Gamma _ { u } \right) * c ^ { < t - 1 > }

LSTM

image

LSTM比普通RNN多了一个贯穿网络的变量 c^{\langle t \rangle} .这个变量用来跟踪语法结构,比如说一段文字中主语是单数还是复数. c^{\langle t \rangle} 定义为:

c^{\langle t \rangle} = \Gamma_f^{\langle t \rangle}* c^{\langle t-1 \rangle} + \Gamma_u^{\langle t \rangle} *\tilde{c}^{\langle t \rangle}

其中 c^{\langle t-1 \rangle} 为上一个时序单元传递的输出, \tilde{c}^{\langle t \rangle} 为当前时序对c的预估:

\tilde{c}^{\langle t \rangle} = \tanh(W_c[a^{\langle t-1 \rangle}, x^{\langle t \rangle}] + b_c)

\Gamma_f \Gamma_u 为两个0~1之间的变量,被称作遗忘门更新门.

当语法结构发生变化时,遗忘门帮助摆脱我们先前存储的记忆值。

\Gamma_f^{\langle t \rangle} = \sigma(W_f[a^{\langle t-1 \rangle}, x^{\langle t \rangle}] + b_f)

当语法结构发生变化时,更新门帮助记忆当前的状态。

\Gamma_u^{\langle t \rangle} = \sigma(W_u[a^{\langle t-1 \rangle}, x^{\langle t \rangle}] + b_u)

LSTM的输出会在RNN的基础上增加 \tanh(c^{\langle t \rangle}) :

\Gamma_o^{\langle t \rangle}= \sigma(W_o[a^{\langle t-1 \rangle}, x^{\langle t \rangle}] + b_o)

a^{\langle t \rangle} = \Gamma_o^{\langle t \rangle}* \tanh(c^{\langle t \rangle})


参考:
https://www.coursera.org/learn/nlp-sequence-models
https://www.kaggle.com/swimmingwhale/recurrent-neural-network

posted @ 2018-11-22 09:34:12
评论加载中...
发表评论