DeepLearning笔记(13)——RNN

2018-04-10 Alex Sun 更多博文 » 博客 » GitHub »

原文链接 https://syaning.github.io/2018/04/10/dl-rnn/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


1. 序列模型的例子

2. 符号定义

例如一个识别人名的模型,输入 $x$ 和输出 $y$ 如下表所示:

$x$ Harry Potter and Hermione Granger invented a new spell $x^{\langle 1 \rangle}$ $x^{\langle 2 \rangle}$ $x^{\langle 3 \rangle}$ $x^{\langle 4 \rangle}$ $x^{\langle 5 \rangle}$ $x^{\langle 6 \rangle}$ $x^{\langle 7 \rangle}$ $x^{\langle 8 \rangle}$ $x^{\langle 9 \rangle}$ $y$ 1 1 0 1 1 0 0 0 0 $y^{\langle 1 \rangle}$ $y^{\langle 2 \rangle}$ $y^{\langle 3 \rangle}$ $y^{\langle 4 \rangle}$ $y^{\langle 5 \rangle}$ $y^{\langle 6 \rangle}$ $y^{\langle 7 \rangle}$ $y^{\langle 8 \rangle}$ $y^{\langle 9 \rangle}$

  • $x^{(i)}$ 表示第 $i$ 个样本的输入
  • $x^{(i)\langle t \rangle}$ 表示第 $i$ 个样本的输入的第 $t$ 个位置
  • $y^{(i)}$ 表示第 $i$ 个样本的输出
  • $y^{(i)\langle t \rangle}$ 表示第 $i$ 个样本的输出的第 $t$ 个位置
  • $T_x^{(i)}$ 表示第 $i$ 个样本的输入的序列长度
  • $T_y^{(i)}$ 表示第 $i$ 个样本的输出的序列长度

那么如何表示一个句子里的一个单词呢?首先需要一张词汇表,例如:

$$ \begin{bmatrix}\text{a} \ \vdots \ \text{and} \ \vdots \ \text{harry} \ \vdots \ \text{potter} \ \vdots \ \text{zulu}\end{bmatrix} \begin{array}{} 1 \ \vdots \ 367 \ \vdots \ 4075 \ \vdots \ 6830 \ \vdots \ 10000 \end{array} $$

然后对每样本句子中的每一个单词使用 one-hot 方式来表示。

3. RNN

RNN的基本结构如下图所示,可以看到,每一个时刻 $t$ 的计算,依赖了之前序列的输出。

在每个RNN单元内,计算如下:

即:

$$ \begin{array}{} a^{\langle t \rangle} = g(W_{aa} a^{\langle t-1 \rangle} + W_{ax} x^{\langle t \rangle} + b_a) \ \hat{y}^{\langle t \rangle} = g(W_{ya} a^{\langle t \rangle} + b_y) \ \end{array} $$

第一个式子里,激活函数通常为 $tanh$,第二个式子里,激活函数为 $sigmoid$ 或者 $softmax$。

可以令:

$$ [a^{\langle t-1 \rangle},x^{\langle t \rangle}]=\begin{bmatrix}a^{\langle t-1 \rangle} \ x^{\langle t \rangle} \end{bmatrix} $$

因此上面的式子可以简化为:

$$ \begin{array}{} a^{\langle t \rangle} = g(W_a [a^{\langle t-1 \rangle},x^{\langle t \rangle}] + b_a) \ \hat{y}^{\langle t \rangle} = g(W_y a^{\langle t \rangle} + b_y) \ \end{array} $$

在反向传播过程中,每个RNN单元内的计算如下图所示:

4. 不同类型的RNN

RNN根据实际场景,有着不同的结构。如下图所示:

  • one-to-one:即普通的神经网络
  • one-to-many:序列生成。例如根据一个输入(音乐流派,第一个音符,或者空值)生成一段音乐
  • many-to-one:例如对影评进行评分;分析文本的情感倾向灯
  • many-to-many ($T_x=T_y$):例如上文提到的判断一句话里哪些单词是人名
  • many-to-many ($T_x \neq T_y$):例如语言翻译

5. 语言模型

常用于语音识别和机器翻译。例如两句话:

  • The apple and pair salad.
  • The apple and pear salad.

语音识别系统可能会认为用户说的更有可能是第二句话。事实上,在选择的过程中,其实是计算了每句话的概率,从而选取可能性大的那句话。语言模型要做的就是判断一个句子出现的概率,即 $P(\text{sentence})$ 或 $P(y^{\langle 1 \rangle},y^{\langle 2 \rangle},\dots,y^{\langle T_y \rangle})$(其中 $y^{\langle 1 \rangle},y^{\langle 2 \rangle},\dots,y^{\langle T_y \rangle}$ 组成了完整的句子)。

为了构建语言模型,需要有一个很大的语料库(corpus)作为训练样本。假设语料库中有一个句子是:Cats average 15 hours of sleep a day. 那么需要做的是:

  1. 针对句子中的每一个单词,根据词典来得到其 one-hot 的向量化形式
    • 标点符号可以自行决定要不要作为一个token
    • 如果字典中没有该单词,则使用一个特殊标记 $\langle UNK \rangle$ 来表示(UNKNOWN)
  2. 增加一个特殊标记 $\langle EOS \rangle$,表示句子的结尾(可选,根据需求而定)
  3. 构建RNN网络来进行训练

    • $x^{\langle t \rangle}=y^{\langle t-1 \rangle}$
    • $y^{\langle 1 \rangle}$ 计算的是 $P(\text{cat})$,即 cat 作为句子第一个词出现的概率;$y^{\langle 2 \rangle}$ 计算的是 $P(\text{average}\mid\text{cat})$,即在 cat 是句子开头的情况下,average 是第二个单词的概率;依此类推

  4. $\mathcal{L}(\hat{y}^{\langle t \rangle},y^{\langle t \rangle})=-\sum_i y_i^{\langle t \rangle}\text{log}\hat{y}_i^{\langle t \rangle}$

  5. $\mathcal{L}=\sum_t\mathcal{L}(\hat{y}^{\langle t \rangle},y^{\langle t \rangle})$

训练完成之后,假设有一个句子有三个单词 $y^{\langle 1 \rangle},y^{\langle 2 \rangle},y^{\langle 3 \rangle}$,则:

$$P(y^{\langle 1 \rangle},y^{\langle 2 \rangle},y^{\langle 3 \rangle})=P(y^{\langle 1 \rangle})P(y^{\langle 2 \rangle} \mid y^{\langle 1 \rangle})P(y^{\langle 3 \rangle} \mid y^{\langle 1 \rangle},y^{\langle 2 \rangle})$$

6. 新序列采样

训练序列模型过程中网络结构如下图所示:

在训练好了一个序列模型后,要想知道这个模型学到了什么,可以进行一次新序列采样,从而生成一段随机的序列。在对新序列采样过程中,网络结构有一些不同,如下所示:

  • 通过输入 $a^{\langle 0 \rangle}$ 和 $x^{\langle 1 \rangle}$,得到 $\hat{y}^{\langle 1 \rangle}$,然后通过 numpy.random.choice 进行采样作为句子第一个单词。
  • 然后将 $\hat{y}^{\langle 1 \rangle}$ 作为输入,得到 $\hat{y}^{\langle 2 \rangle}$,然后通过采样得到句子的第二个单词。
  • 依此类推,生成一个完整的句子。
    • 结束条件:限定序列的长度或者遇到 $\langle EOS \rangle$
    • 生成过程中遇到 $\langle UNK \rangle$,根据需要直接使用,或者忽略然后继续采样

7. GRU单元

相关论文:

上面介绍的基础的RNN只能处理临近依赖,而无法有效地处理长期依赖。例如下面两个句子:

  • The cat, which already ate …, was full.
  • The cats, which already ate …, were full.

后面是 was 还是 were,依赖于前面是 cat 还是 cats。

GRU (Gated Recurrent Unit,门控循环单元) 可以更好地捕获深层连接,处理长期依赖,并且改善了梯度消失的问题。

$$ \begin{array}{} \Gamma_r=\sigma(W_r[c^{\langle t-1 \rangle},x^{\langle t \rangle}]+b_r) \ \Gamma_u=\sigma(W_u[c^{\langle t-1 \rangle},x^{\langle t \rangle}]+b_u) \ \tilde{c}^{\langle t \rangle}=\text{tanh}(W_c[\Gamma_r*c^{\langle t-1 \rangle},x^{\langle t \rangle}]+b_c) \ c^{\langle t \rangle}=\Gamma_u*\tilde{c}^{\langle t \rangle} + (1-\Gamma_u)*c^{\langle t-1 \rangle} \end{array} $$

8. LSTM

LSTM (Long Short Term Memory,长短期记忆) 单元,是另外一种更加强大的RNN单元结构。

$$ \begin{array}{} \tilde{c}^{\langle t \rangle}=\text{tanh}(W_c[a^{\langle t-1 \rangle},x^{\langle t \rangle}]+b_c) \ \Gamma_u=\sigma(W_u[a^{\langle t-1 \rangle},x^{\langle t \rangle}]+b_u) \ \Gamma_f=\sigma(W_f[a^{\langle t-1 \rangle},x^{\langle t \rangle}]+b_f) \ \Gamma_o=\sigma(W_o[a^{\langle t-1 \rangle},x^{\langle t \rangle}]+b_o) \ \tilde{c}^{\langle t \rangle}=\Gamma_u*\tilde{c}^{\langle t \rangle}+\Gamma_f*c^{\langle t-1 \rangle} \ a^{\langle t \rangle}=\Gamma_o*\text{tanh}(c^{\langle t \rangle}) \end{array} $$

与GRU相比,LSTM有三个门(遗忘门、更新门、输出门),因此更为强大。不过GRU由于结构相对简单,性能相对高一些,可以构建更大规模的网络。

9. BRNN

例如一个识别句子中人名的模型,输入下面两个句子:

  • He said, "Teddy bears are on sale!"
  • He said, "Teddy Roosevelt was a great President!"

在遇到第三个单词 Teddy 的时候,只看句子前面部分是不够的,还需要句子后半部分的信息。此时需要使用双向RNN (Bidirectional RNN)。如下图所示:

10. 深层RNN

通过增加隐藏层来构建更深的RNN网络。如图所示:

其中每一个单元可以是普通的RNN单元,GRU或者LSTM。