吴恩达深度学习——循环神经网络
文章目录引言序列模型能解决什么问题符号定义循环神经网络通过时间的方向传播不同类型的循环神经网络语言模型和序列生成引言本文是吴恩达深度学习第五课:序列模型。本次课程将会学到如何为自然语言、语言和其他序列数据构建模型。会了解如何构建并训练一个循环神经网络,和常用的变体,比如GRU和LSTM。能应用序列模型到自然语音处理,包括情感分析。能应用序列模型到语音应用,包括语音识别和智能作曲。第五课有以下三个部
文章目录
引言
本文是吴恩达深度学习第五课:序列模型。本次课程将会学到如何为自然语言、语言和其他序列数据构建模型。会了解如何构建并训练一个循环神经网络,和常用的变体,比如GRU和LSTM。能应用序列模型到自然语音处理,包括情感分析。能应用序列模型到语音应用,包括语音识别和智能作曲。
第五课有以下三个部分,本文是第一部分。
- 循环神经网络
- 自然语音处理与词嵌入
- 序列模型与注意力机制
序列模型能解决什么问题
所有的这些问题都可以使用有标签数据 ( X , Y ) (X,Y) (X,Y)作为训练集,以监督学习的方式来解决。但是从中可以看到有很多不同的序列问题。
有的问题
X
,
Y
X,Y
X,Y都为序列,比如语音识别问题中。
有的问题
X
,
Y
X,Y
X,Y可以有不同的长度,比如情感分类和命名识别识别。
下面几节探讨的是如何构建序列模型。
符号定义
我们先从符号定义开始,一步一步构建序列模型。
假如你要构建的序列模型,它的输入语句是这样的。
假设你想建立一个序列模型,能自动识别语句中人名的位置,这种问题是命名实体识别问题。
给定一个这样的输入,你想要一个序列模型输出
y
y
y,使得输入的每个单词都对应一个输出值。同时能表示对应的单词是否是人名的一部分。
作为输入的序列数据中只有9个单词,所以我们会得到9组特征来代表9个单词。并按序列中的位置进行索引,用上标<1>
到<9>
来索引不同的位置。
用
T
x
T_x
Tx来表示输入序列的长度,用
T
y
T_y
Ty来表示输出序列的长度。
表示第 i i i个样本序列中的第 t t t个元素。
并且训练集里不同的训练样本可以有不同的长度,表示第 i i i个样本输入序列的长度。
在自然语言处理问题中,一件优先需要解决的问题是如何表示一个序列里单独的单词。
常用的方法是做一张词表(词典),
在本例中,词典的大小为10000。如果你想要构建一个这样的词典,那么需要到你的训练集中去查找出现频率最高的1万的单词,或者用网上的字典。然后通过one-hot形式来表示每个单词。
比如单词Harry背表示为一个向量,向量的大小和词典一样大,只有单词Harry的位置为1,其余都为0。
这个例子中,我们就有9个one-hot向量。如果遇到不在词典中的单词怎么办,常用的办法是创建一个未知单词标记,来表示不在词典中的单词。
循环神经网络
关于循环神经网络个人觉得李宏毅老师讲的比较好,课堂笔记见循环神经网络
现在我们来探讨一下如何建一个神经网络模型来学习 X X X到 Y Y Y的映射。
先看一下如果我们使用标准的网络要怎样解决这个问题。
输出是1或0,表示对应的单词是否是人名的一部分。
但是这个网络结构并不好,为什么呢,来看一下:
- 输入和输出在不同的样本中可以有不同的长度
- 这种结构就无法满足这种灵活性
- 没有共享文本序列不同位置上学到的特征
- 比如已经学到了出现在位置1的Harry是人名的一部分,如果Harry出现在其他位置时,应该也要能识别为人名的一部分。但普通的神经网络不具备这种性质。
我们要学习的循环神经网络就没有这个缺点。
我们用循环神经网络来解决这个问题,如果从左到右的顺序处理这个文本序列,那么先将第一个单词输入到神经网络。
在将第二个单词输入到神经网络时,循环神经网络不仅通过输入来决定,还利用了时间步1的激活值。
剩下的输入也是这样
所以在每一个时间步中,循环神经网络都传递了一个激活值到下一个时间步中,这里指定了一个初始的激活值,,一般是零向量。
从这里可以看到,循环神经网络是从左向右扫描数据,同时每个时间步的参数也是共享的。控制从 x < 1 > x^{<1>} x<1>到隐藏层的参数 W a x W_{ax} Wax,在每个时间步都是相同的(其实这就是同一个神经网络,只不过按照读入顺序展开了而已)。
然后激活值
a
a
a是由参数
W
a
a
W_{aa}
Waa决定的,输出结果是由
W
y
a
W_{ya}
Wya决定的。
这里要指出的是,后面的输出是由前面所有输入影响的。比如输出
y
^
<
3
>
\hat y^{<3>}
y^<3>不仅由输入
x
<
3
>
x^{<3>}
x<3>决定,还受
x
<
1
>
,
x
<
2
>
x^{<1>},x^{<2>}
x<1>,x<2>的影响。
从这里可以看到这个网络有一个缺点是,它只用到了之前的信息做决定,没有用到后面的信息。
比如为了判断Teddy是否是人名的一部分,仅仅知道He ,said, Teddy这几个词是不够的,如果能知道后面的信息会更有帮助。
下面这段话中的Teddy就不是人名。
Teddy bear are on sale(泰迪熊在售)
所以这种网络的缺点是在某一时刻的预测只使用了该时间点序列之前的信息。
我们写出这个神经网络所做的计算。
介绍下命名,这里
W
a
x
W_{ax}
Wax下标中的
x
x
x表示要乘以的值,这里要乘以
x
x
x;而下标中的
a
a
a表示要计算的值,整个式子是要计算
a
a
a。
RNN中计算
a
a
a的激活函数通常是Tanh。
而计算输出
y
^
\hat y
y^的激活函数根据业务的不同选择也会不同。
计算时刻
t
t
t的公式如下:
这些公式定义了RNN神经网络的前向传播。
为了更好的描述更加复杂的神经网络,我们简化一下上面这些公式表示。
把
W
a
a
W_{aa}
Waa和
W
a
x
W_{ax}
Wax按列叠加起来得到
W
a
W_a
Wa。
这个符号表示按行堆叠起来。这样一来右边的等式和左边的等式是一样的。
第二个等式也简化了一下表示。
穿越时光反向传播
我们上一节中已经学过了正向传播的计算,而反向传播的方向是与正向传播刚好相反的。如红色箭头所示。
前向传播的计算可以这样表示出来,每次计算都使用相同的参数。
有了
a
a
a的值之后就可以计算
y
^
\hat y
y^。
为了计算反向传播,我们需要一个损失函数,假设这里做的是命名识别识别,识别是否为人名,并且输出概率。
那么对于这种是否的二分类问题,我们可以使用交叉熵损失函数。
计算每个时间点的损失,累加起来就是总体的损失函数。
反向传播就是从损失函数开始,验证相反的反向求梯度。
在这个反向传播的过程中,最重要的信息传递就是上图粉色框框出来的那部分,在计算正向传播时,从左到右,时间点不断增加;而计算反向传播时,时间点不断减小,就像时光倒流一样。
不同类型的循环神经网络
我们在第一节中看过这些例子。下面看我们如何设计RNN来处理这些情况。
先来看下 T x = T y T_x=T_y Tx=Ty的情况。
这是一种多对多的结构,这种我们已经见过了。下面来看处理情感分类问题中,输入是一个句子,输出可能是0/1(正面评价/负面评价),或者是1-5(评分)。
这种是多对一的情况,输入一个句子,最终才得到一个输出。除了有多对一的结构,还有一对多。
那就是音乐生成,在这个例子中,输入可以是一个整数(可能表示想要的音乐类型,或第一个音符),输出就是一段音乐。
在多对多的例子中,还有可能输入长度和输出长度是不一样的。
比如在机器翻译中,会有两种结构,左边的是编码器,用来读取输入。右边的是解码器,用来进行翻译。
最后还有一种简单的一对一的结构。
语言模型和序列生成
在本节中我们来学习如何用RNN来构建一个语言模型。
以语音识别为例来解释语言模型。
在语音识别中,假设有人说了这样一个句子。但是pair和pear的发音相近,那么应该识别成哪句话呢,其实这里可以根据经验,即出现哪个单词的概率最大,就输出哪个单词。
使用语言模型,能计算出每句话出现的可能性。
假设计算出这两句话的概率如上,我们就知道应该选第二句话。具体的是估计句子中每个单词出现的概率。
那么如何建立一个语言模型呢,你需要一个很大的语料库,比如英文文本语料库。
语料库:数量很多的句子组成的文本
假设你的训练集中有这样一句话,猫一天睡15个小时。我们要生成这样一句话。
第一件要做的事情是向量化这个句子,需要一个很大的词典,然后转换为one-hot向量。
在这之前,先进行标记化(标记这个句子),可能还要定义一个句尾标识。
在标记化的过程中,可以考虑是否识别标点符号。
如果你遇到了一些不在你字典中的词汇,那么可以把这些词汇标记为未知(UNK)。
在完成了标记化后,我们将输入句子的每个单词,映射到了字典中的各个词上。
接下里需要构建一个RNN来建立这些序列的概率模型。
在第0个时间步,需要通过softmax来预测第一个词的概率
y
^
<
1
>
\hat y^{<1>}
y^<1>,此时
a
<
0
>
a^{<0>}
a<0>和
x
<
1
>
x^{<1>}
x<1>都是零向量。
通过softmax来预测字典中任意词汇会是第一个词的概率。假设我们字典大小为10000的话,那么就会有10002个结果(加上了UNK和EOS)。这里假设输出的是cats。
接下来计算出第二个词会是什么。
这里输入
x
<
2
>
x^{<2>}
x<2>是正确的第一个单词,然后输出是考虑给定第一个词为cats的条件下,第二词最有可能是什么。
按照这样的方式就可以生成整个句子。为了训练这个网络,我们需要定义代价函数,因为用到了softmax函数,我们可以用softmax损失函数。
如果用了很大的训练集来训练这个RNN,你就可以通过开头的一些单词,来预测之后单词的概率。
也可以计算新句子出现的概率。
第一个softmax输出告诉你
P
(
y
<
1
>
)
P(y^{<1>})
P(y<1>),第二个告诉你
P
(
y
<
2
>
∣
y
<
1
>
)
P(y^{<2>}|y^{<1>})
P(y<2>∣y<1>),第三个softmax层输出
P
(
y
<
3
>
∣
y
<
1
>
,
y
<
2
>
)
P(y^{<3>}|y^{<1>},y^{<2>})
P(y<3>∣y<1>,y<2>)。把这些概率相乘就是这个新句子出现的概率。
这就是用RNN来训练一个语言模型。
对新序列采样
在我们训练了一个序列模型以后,我们可以通过采样新的序列来了解它学到了什么。
假设我们已经训练好这样一个模型,那如何采样呢。
输入都是零向量,第一个输出
y
^
<
1
>
\hat y^{<1>}
y^<1>输出了字典中每个词出现的概率。然后用numpy.random.choice
随机选择一个单词。
在采样第二个单词时,这里输入的生成的第一个单词。即计算给定第一个单词的情况下,每个单词出现的概率。
重复这个步骤,知道遇到了句子结尾标志(EOS),此时说明句子生成完毕了。如果没有EOS的话,那么需要定义一个句子的长度。
这样我们就构建好了一个字级(词汇级)RNN,如果词典中是每个字符,那么构建的就是字符级RNN。
RNN的梯度消失问题
以语言模型的例子为例,假设要生成的句子为The cat(cats) ,which already ate …,was(were) full.
在英语中要考虑单复数,如果前面是cat,那么后面就是was;如果前面是cats,后面就是were。
在这个例子中,后面的单词对前面的单词有长期的依赖,但是我们现在见过的基本RNN模型无法处理这种长期依赖。
这个问题和我们之前看到的很深的网络一样,假设这里有100层,由于梯度消失的问题,输出
h
a
t
y
hat y
haty的梯度很难传递到最前面的几层。RNN同样有这个问题。
这是基本RNN的一个缺点,我们下面几节会看到如何处理这个问题。
在很深的神经网络中,还存在梯度爆炸的问题,导致参数值过大,然后出现很多NaN的情况。这是数值过大导致计算结果溢出。
梯度爆炸有一个解决方法是梯度修剪(gradient clipping),就是设定一个阈值,当梯度向量超过某个阈值时,将它减少到阈值。
然而梯度消失问题更难解决,因此也是我们下几节重点考虑的。
GRU单元
我们已经见过这个公式了,这里的激活函数是tanh函数。画出RNN单元的话如下,
我们将会使用类似的图片来介绍门控循环单元(GRU)。
Cho et al., 2014. On the properties of neural machine translation: Encoder-decoder approaches
Chung et al., 2014. Empirical Evaluation of Gated Recurrent Neural Networks on Sequence Modeling.
还是以这个句子为例,这里是单数,后面应该是was,现在我们来看下GRU单元会怎么做。
GRU单元会有个新的变量,c
,代表记忆细胞。可以记住前面是单数还是复数。
对于GRU来说,
c
<
t
>
c^{<t>}
c<t>的值等于
a
<
t
>
a^{<t>}
a<t>。在每个时间步,我们将用一个候选值来覆盖记忆细胞内的值,这个候选值的计算公式如下:
在GRU中真正重要的思想是,我们有一个门
Γ
u
\Gamma_u
Γu,它的值在0到1之间,可以控制是否更新记忆细胞内的值。
把tanh中的式子代入sigmoid函数即可得到0到1之间的值。
通过tanh函数计算候选值,通过sigmoid函数来判断是否更新值,
可以这么理解,假设单数的cat 的c值为1,然后我们一直存储它,直到was的位置,就知道前面为单数。而门
Γ
u
\Gamma_u
Γu决定什么时候更新这个值。
下面写出门控制更新的式子:
可以看到,但门的值为1时,就用候选值覆盖细胞内的值;否则当门值为0时,则保存细胞内的值不变。
下面画出GRU的示意图。
上图中紫色部分就是上面带有门的那个公式。
GRU的优势是,当从左到右扫描一个句子的时候,通过门来决定是否要更新记忆细胞内的值,这个例子句子中是已知保存细胞内的值,直到was的位置。
只要sigmoid的参数值是一个很大的负数,那么整个sigmoid就容易取到零值,就很容易保持细胞的值不变。
这样即使经过很多的时间步, c < t > = c < t − 1 > c^{<t>}=c^{<t-1>} c<t>=c<t−1>值也能被保留,这样就能缓解梯度消失的问题。
c < t > c^{<t>} c<t>可以是一个向量,候选值和门值都和它是同样的维度。如果门控是100维向量,可以把它看成是100位(bit)的值,它告诉你这个100维记忆单元哪一位是你想要更新的。
对位元素进行元素乘法的做法,只是一位一位的告诉GRU单元,在记忆细胞中哪个向量的维度在每步计算时需要更新,因此可以选择更新其他位时保持某些位不变。 比如用1位来保存猫的单复数,用一些其他的位来标识谈论的内容是食物(里面出现了eat这个词)。
我们上面介绍的实际上是一个简化版的GRU单元,下面看下完整的GRU单元。
这是我们之前介绍的公式,这里要改写一下。再增加一个控制门
Γ
r
\Gamma_r
Γr,这个门告诉你如何通过
c
<
t
−
1
>
c^{<t-1>}
c<t−1>来计算下一个候选值。
长短记忆网络(LSTM)
除了GRU可以让你在序列中学习比较深的连接,其他类型的单元也可以做到这一点,比如LSTM。
Hochreiter & Schmidhuber 1997. Long short-term memory
LSTM是一个比GRU更强大和通用的单元。
它的计算式子如下:
一个LSTM的新特性是,它不只有一个更新门控制,还有一个
Γ
f
\Gamma_f
Γf(遗忘门),以及一个新的sigmoid的输出门
Γ
o
\Gamma_o
Γo(输出门)。
最后记忆细胞的更新如上。可以结合下面这个图来理解。
最后输出门
Γ
o
\Gamma_o
Γo主要控制了输出
a
<
t
>
a^{<t>}
a<t>的取值。
所以LSTM有三个门,还是看一下吴老师画的图吧。
用
a
<
t
−
1
>
a^{<t-1>}
a<t−1>与
x
<
t
>
x^{<t>}
x<t>分别计算了遗忘门,更新门和输出门的结果。
然后通过tanh函数来计算候选值。
你可以生成一堆类似的单元,然后根据时序将它们连接起来。
前一个时间步的输出是下个时间步的输入。图中上部有一条流程直线(红色的线)表示如何计算,只要你恰当的设置了遗忘门和更新门,LSTM可以相对简单地将
c
<
0
>
c^{<0>}
c<0>的值传递到图的右侧。
这就是为什么LSTM可以长时间记住某些数值的原因。
双向RNN神经网络
我们已经学习了RNN网络的基础构建,但还有两个想法可以让你建立更强大的模型,一个是双向RNN,它可以让你在一个时间步同时获得序列中前部分和后部分的信息;第二个是深层RNN,会在下节中介绍。
为了引入双向RNN,我们先看下这个网络,这个网络的一个问题是,为了弄清楚第三个单词Teddy是否为人名的一部分,我们只考虑句子的前半部分是不够的,因此这是单向(正向)RNN的缺点。
一个双向RNN(BRNN)可以解决这个问题。BRNN的工作原理如下:
现在看到的还只是正向(前向递归层)的部分,下面来画出反向(后向递归层)的部分。
给定一个输入序列,
x
<
1
>
x^{<1>}
x<1>到
x
<
4
>
x^{<4>}
x<4>。前向序列将首先计算
a
<
1
>
a^{<1>}
a<1>,然后是
a
<
2
>
a^{<2>}
a<2>,直到
a
<
4
>
a^{<4>}
a<4>。
相反地,后向序列将从 a < 4 > a^{<4>} a<4>开始,反向计算 a < 3 > a^{<3>} a<3>,直到 a < 1 > a^{<1>} a<1>。
注意,以上都是前向传播,有一部分是从左到右,另一部分是从右到左。
计算完了所有的激活值后,就可以做出预测了。预测的公式如下:
比如在输出 y ^ < 3 > \hat y ^{<3>} y^<3>时,这样左边和右边的信息都能考虑到。
BRNN的缺点是,需要整个序列,然后才能在任何地方进行预测。
深层RNN
在学习非常复杂的函数,有时把多层RNN结构堆叠在一起,形成更深层的模型会更有帮助。
在本节我们将看到如何构建这些深层的RNN。
假设构建了一个3层的RNN。
上图是我们所见过的标准RNN,这里更改了一些符号,比如输入
a
[
1
]
<
0
>
a^{[1]<0>}
a[1]<0>表示时刻0的第一层的激活值。
把它们堆叠到一起就得到了下图:
因为我们要同时考虑激活值的层数和时间点。下面我们看一下如何计算这个数值,以
a
[
2
]
<
3
>
a^{[2]<3>}
a[2]<3>为例,看如何计算。
它有一个来自底部的输入,和一个来自左边的输入。
对于RNN来说,有三层已经算很多了,因为时间这一维度的存在,使得即使只有很少的层数的网络也会变得很大。
参考
更多推荐
所有评论(0)