吴恩达深度学习——序列模型
引言:序列模型可以是一段音频,一段视频,一句话等等!
一、定义序列模型的数学符号
对于输入x,我们通常使用 x < n > x^{<n>} x<n>来表示,n是1,2,3…
使用 x < t > x^{<t>} x<t>来表示一个序列的中间位置
对于输出y,我们通常使用 y < n > y^{<n>} y<n>来表示,n是1,2,3…
用 T x T_x Tx, T y T_y Ty来表示x,y的长度
x ( i ) < t > x^{(i)<t>} x(i)<t>表示第i个训练样本中的第t个元素, T x ( i ) T_x^{(i)} Tx(i)表示第i个训练样本的长度
同理: y ( i ) < t > y^{(i)<t>} y(i)<t>表示第i个输出中的第t个元素, T y ( i ) T_y^{(i)} Ty(i)表示第i个输出的长度
那么我们如何来表示一个单词呢?
首先我们要定义一个词典,然后对我们句子中的词语使用one-hot(独热)编码
词典 a hi zoo
[ a ] [ 1 ] [ 0 ] [ 0 ]
[and ] [ 0 ] [ 0 ] [ 0 ]
[... ] [...] [...] [...]
[ hi ] [ 0 ] [ 1 ] [ 0 ]
[... ] [...] [...] [...]
[zoo ] [ 0 ] [ 0 ] [ 1 ]
对于词典中没有出现的单词我们通常使用UNK来表示
二、循环神经网络
1.对于序列模型为什么不采用标准的神经网络进行处理呢?
以判断单词是否为人名为例子,如果我们使用的标准的神经网络,我们对一个9词的句子,我们最后得到标签应该是一个9x1的向量,被1标记的是人名。
在现实中,我们每个句子的长度都不一样,这使得我们神经网络中最后输出的标签不会是同一个标准,也许我们可以吧多余的部分用0表示,但是那仍然是一种不合适的表达方法。
除此之外,我们通过标准的神经网络,从文本不同位置学到的特征信息并不共享,对于一个序列模型来说,一个单词跟前后是相关的,使用标准的神经网络,使得我们的单词被独立出来。而我们希望的是在模型的部分提取出的特征信息可以应用到整体上,显然标准的神经网络并不合适。
2.循环神经网络的结构
下面我们给出循环神经网络的结构表达方式
我们把方框当成标准的神经网络,他的结点自然是不可能只有四个的,这只是简图,里面其实是一个完整的神经网络。
对于x到隐藏层的参数 W a x W_{ax} Wax我们称为输入到隐藏层的参数
W a a W_{aa} Waa是从一个神经网络的参数,这里需要一个启动的向量 a < 0 > a^{<0>} a<0>,一般我们取他为0向量,
W a y W_{ay} Way是从隐藏层到输出层的一个权重
这里的参数命名是有规律的,w下标的第一个表示的是终点,第二个表示起点,比如我们的 W a x W_{ax} Wax他表示的就是从x到a的一个参数
我们现在来看一看循环神经网络时怎么计算的
其实很简单,就是下面的公式
a
<
1
>
=
g
1
(
W
a
a
a
<
0
>
+
W
a
x
X
<
1
>
+
b
a
)
a^{<1>} = g_1(W_{aa}a^{<0>}+W_{ax}X^{<1>}+b_a)
a<1>=g1(Waaa<0>+WaxX<1>+ba)
y
H
a
t
<
1
>
=
g
2
(
W
y
a
a
<
1
>
+
b
y
)
yHat^{<1>} = g_2(W_{ya}a^{<1>}+b_y)
yHat<1>=g2(Wyaa<1>+by)
简单来说,就是对a,x,乘上对应的参数相加再加上b在投入激活函数中,生成我们的下一个a,然后再将我们的下一个a乘上对应的权重加上偏置,投入激活函数中获得我们的yHat
我们可以将上面的公式一般化
a
<
t
>
=
g
1
(
W
a
a
a
<
t
−
1
>
+
W
a
x
X
<
t
>
+
b
a
)
a^{<t>} = g_1(W_{aa}a^{<t-1>}+W_{ax}X^{<t>}+b_a)
a<t>=g1(Waaa<t−1>+WaxX<t>+ba)
y
H
a
t
<
t
>
=
g
2
(
W
y
a
a
<
t
>
+
b
y
)
yHat^{<t>} = g_2(W_{ya}a^{<t>}+b_y)
yHat<t>=g2(Wyaa<t>+by)
通常在 g 1 g_1 g1中我们使用的是tanh函数,有时候也使用ReLU
在 g 2 g_2 g2中如果是二分类问题我们选择sigmoid函数,多分类选择softmax函数
将公式简化成:
a
<
t
>
=
g
1
(
W
a
[
a
<
t
−
1
>
,
x
<
t
>
]
+
b
a
)
a^{<t>} = g_1(W_a[a^{<t-1>},x^{<t>}]+b_a)
a<t>=g1(Wa[a<t−1>,x<t>]+ba)
y
H
a
t
<
t
>
=
g
(
W
y
a
a
<
t
>
+
b
y
)
yHat^{<t>} = g(W_{ya}a^{<t>}+b_y)
yHat<t>=g(Wyaa<t>+by)
我们的 W a W_a Wa是 W a a W_{aa} Waa和 W a x W_{ax} Wax结合成的矩阵
W a = [ W a a ∣ W a x ] W_a = [W_{aa}|W_{ax}] Wa=[Waa∣Wax]
如果我们的 a < t − 1 > a^{<t-1>} a<t−1>是100维的,那么我们的 W a a W_{aa} Waa的维度应该是(100,100),如果 x < t > x^{<t>} x<t>是10000维的,那么我们的 W a x W_{ax} Wax维度是(100,10000),所以我们的 W a W_a Wa的维度应该是(100,10100)
3.循环神经网络的损失函数
对一个单一的单词我们定义它使用的是交叉熵损失函数:
L < t > ( y H a t < t > , y < t > ) = − y < t > l o g ( y H a t < t > ) − ( 1 − y < t > ) l o g ( 1 − y H a t < t > ) L^{<t>}(yHat^{<t>},y^{<t>}) =-y^{<t>}log (yHat^{<t>}) - (1-y^{<t>})log(1-yHat^{<t>}) L<t>(yHat<t>,y<t>)=−y<t>log(yHat<t>)−(1−y<t>)log(1−yHat<t>)
对整体的损失函数就是:
L ( y H a t < t > , y < t > ) = ∑ t = 1 T y L < t > ( y H a t < t > , y < t > ) L(yHat^{<t>},y^{<t>}) = \sum_{t=1}^{T_y}L^{<t>}(yHat^{<t>},y^{<t>}) L(yHat<t>,y<t>)=t=1∑TyL<t>(yHat<t>,y<t>)
对于其反向传播自然是,从过去的顺序进行反向求偏导,再使用梯度下降法进行参数更新,事实上,反向传播涉及大量的微积分知识,这里就不做过多的说明,大部分的框架都具备帮我们完成反向传播的能力。
4.不同类型的循环神经网络
一对一:他实质上就是一个标准的神经网络,我们甚至不需要把他做成RNN的类型
一对多:使用一个输入产生多个输出,广泛应用于音乐生成
多对一:使用多个输入产生一个输出,应用于文本情感分析
多对多(
T
x
T_x
Tx与
T
y
T_y
Ty相等):多个输出对应多个输出,比如判断单词词性
多对多(
T
x
T_x
Tx与
T
y
T_y
Ty不相等):多个输出对应多个输出,应用于机器翻译
三、语言模型与序列生成
在自然语言处理中,构建语言模型是最基础的也是最重要的工作之一,下面我们来看看如何构语言模型。
什么是语言模型?他其实就是用于在两句或者多句相似的句子中,选择可能性较高的句子,也就是说语言模型可以判断我们的句子可能是哪一句的概率
如何构建语言模型:
首先我们需要一个训练集,包含一个很大的英文文本语料库(其他语言也可)
面对一个句子,就要像之前说的一样先要创建一个词典,将句子中对应的词语转化成one-hot的形式,对于词典中没有出现的词就用 < U N K > <UNK> <UNK>来表示,对于文本的结尾我们需要用 < E O S > <EOS> <EOS>来表示,也可以吧我们的句号当成句子的结束
以下面的模型为例子
在第一层中我们的x表示的是第一个句子的第一个单词,经过第一层RNN处理后,我们可以得到 y < 1 > y^{<1>} y<1>他表示的是句子中第一个词出现的概率,如果我们有1000个词语,那么他的维度就是(1002,1)因为可能还会有一个UNK和EOS,然后我们将输出的 y < 1 > y^{<1>} y<1>投入到下一层中,开始经过第二层RNN从而产生第二个词语的概率,以此类推
对于这个过程的损失函数:
对于单个的损失函数来说,就是单纯使用softmax的损失函数即可
L
(
y
H
a
t
<
t
>
,
y
<
t
>
)
=
−
∑
i
y
i
<
t
>
l
o
g
(
y
H
a
t
i
<
t
>
)
L(yHat^{<t>},y^{<t>})=-\sum_iy^{<t>}_ilog(yHat_i^{<t>})
L(yHat<t>,y<t>)=−i∑yi<t>log(yHati<t>)
对于整体的损失函数
L
=
∑
t
L
<
t
>
(
y
H
a
t
<
t
>
,
y
<
t
>
)
L = \sum_tL^{<t>}(yHat^{<t>},y^{<t>})
L=t∑L<t>(yHat<t>,y<t>)
标准的RNN不适合处理长期依赖的问题,比如一个句子中复数名词要对应复数的be动词,但是之间可能有很多附加句子,我们的标准RNN可能做不到这样的影响从而导致梯度消失。
对于深层的神经网络来说,经常会碰到梯度爆炸的情况,一个解决办法就是用梯度袖箭来处理。简答来说就是如果我们的梯度大于某个阈值,我们就对其进行缩放,保证它不会太大
四、GRU(Gated Recurrent或者叫做门控循环)单元
我们先看一下简单的GRU单元
其实很简单,我们就是通过上一层输出的a与这一层的x横向排成一个矩阵,然后乘上一个权重与偏置,带入一个激活函数中,获得我们这层的输出,同时带入到另一个激活函数中获得这层的yHat
上面的结构其实还不完整只是一个思想,其实我们还需要一个C表示的是记忆细胞(memory cell)在这个GRU结构中,他和我们的a是同样的,就比如单复数的问题上,记忆细胞在前面触碰到的名词是单数,记忆细胞就会记住他,到后面对应的谓语动词上,记忆细胞就会发挥他的作用,使得谓语动词使用正确的形式。
先要综合我们的a与x计算我们的记忆细胞后选值 c ~ < t > \tilde{c}^{<t>} c~<t>
但我们的序列模型不一定能直接跳转的我们的位于动词,于是我们需要一个门(更新门) Γ u \Gamma_u Γu来判断记忆细胞与下一个输入单元的关系
我们直接来看他们间的公式怎么定义:
c
~
<
t
>
=
tanh
(
W
c
[
c
<
t
−
1
>
,
x
<
t
>
]
+
b
c
)
\tilde{c}^{<t>}=\tanh \left(W_{c}\left[c^{<t-1>}, x^{<t>}\right]+b_{c}\right)
c~<t>=tanh(Wc[c<t−1>,x<t>]+bc)
Γ
u
=
σ
(
W
u
[
c
<
t
−
1
>
,
x
<
t
>
]
+
b
u
)
\Gamma_{u}=\sigma\left(W_{u}\left[c^{<t-1>}, x^{<t>}\right]+b_{u}\right)
Γu=σ(Wu[c<t−1>,x<t>]+bu)
c
<
t
>
=
Γ
u
∗
c
~
<
t
>
+
(
1
−
Γ
u
)
+
c
<
t
−
1
>
c^{<t>}=\Gamma_{u} * \tilde{c}^{<t>}+\left(1-\Gamma_{u}\right)+c^{<t-1>}
c<t>=Γu∗c~<t>+(1−Γu)+c<t−1>
这样直接看公式不太好理解,那我们通过结构图来展示:
图中清楚地表示了上面公式中各个量在什么时候计算,图中紫色的框表示的是 c < t > c^{<t>} c<t>的计算过程
距离完整的GRU还差一个能联系 c < t − 1 > c^{<t-1>} c<t−1>和 c < t > c^{<t>} c<t>的参数(相关门) Γ r \Gamma_r Γr
我们下面列出完整的公式
c
~
<
t
>
=
tanh
(
W
c
[
Γ
r
∗
c
<
t
−
1
>
,
x
<
t
>
]
+
b
c
)
\tilde{c}^{<t>}=\tanh \left(W_{c}\left[\Gamma_{r}*c^{<t-1>}, x^{<t>}\right]+b_{c}\right)
c~<t>=tanh(Wc[Γr∗c<t−1>,x<t>]+bc)
Γ
u
=
σ
(
W
u
[
c
<
t
−
1
>
,
x
<
t
>
]
+
b
u
)
\Gamma_{u}=\sigma\left(W_{u}\left[c^{<t-1>}, x^{<t>}\right]+b_{u}\right)
Γu=σ(Wu[c<t−1>,x<t>]+bu)
Γ
r
=
σ
(
W
r
[
c
<
t
−
1
>
,
x
<
t
>
]
+
b
r
)
\Gamma_{r}=\sigma\left(W_{r}\left[c^{<t-1>}, x^{<t>}\right]+b_{r}\right)
Γr=σ(Wr[c<t−1>,x<t>]+br)
c
<
t
>
=
Γ
u
∗
c
~
<
t
>
+
(
1
−
Γ
u
)
+
c
<
t
−
1
>
c^{<t>}=\Gamma_{u} * \tilde{c}^{<t>}+\left(1-\Gamma_{u}\right)+c^{<t-1>}
c<t>=Γu∗c~<t>+(1−Γu)+c<t−1>
a
<
t
>
=
c
<
t
>
a^{<t>} = c^{<t>}
a<t>=c<t>
五、长短期记忆单元(LSTM)
比GRU更加强大和通用的RNN单元
我们来看看公式
对于输入单元的后选值我们去掉了我们的关系门,然后其他没有改变
c
~
<
t
>
=
tanh
(
W
c
[
a
<
t
−
1
>
,
x
<
t
>
]
+
b
c
)
\tilde{c}^{<t>}=\tanh \left(W_{c}\left[a^{<t-1>}, x^{<t>}\right]+b_{c}\right)
c~<t>=tanh(Wc[a<t−1>,x<t>]+bc)
我们的更新门也没有改变
Γ
u
=
σ
(
W
u
[
a
<
t
−
1
>
,
x
<
t
>
]
+
b
u
)
\Gamma_{u}=\sigma\left(W_{u}\left[a^{<t-1>}, x^{<t>}\right]+b_{u}\right)
Γu=σ(Wu[a<t−1>,x<t>]+bu)
在GRU计算
c
<
t
>
c^{<t>}
c<t>时运用了
(
1
−
Γ
u
)
(1-\Gamma_{u})
(1−Γu),这里我们用
Γ
f
\Gamma_{f}
Γf代替
Γ
f
=
σ
(
W
f
[
a
<
t
−
1
>
,
x
<
t
>
]
+
b
f
)
\Gamma_{f}=\sigma\left(W_{f}\left[a^{<t-1>}, x^{<t>}\right]+b_{f}\right)
Γf=σ(Wf[a<t−1>,x<t>]+bf)
我们新增了一个计算
a
<
t
>
a^{<t>}
a<t>的门
Γ
o
=
σ
(
W
o
[
a
<
t
−
1
>
,
x
<
t
>
]
+
b
o
)
\Gamma_{o}=\sigma\left(W_{o}\left[a^{<t-1>}, x^{<t>}\right]+b_{o}\right)
Γo=σ(Wo[a<t−1>,x<t>]+bo)
c
<
t
>
=
Γ
u
∗
c
~
<
t
>
+
Γ
f
∗
c
<
t
−
1
>
c^{<t>}=\Gamma_{u} * \tilde{c}^{<t>}+\Gamma_{f} * c^{<t-1>}
c<t>=Γu∗c~<t>+Γf∗c<t−1>
计算
a
<
t
>
a^{<t>}
a<t>的方式也不一样了
a
<
t
>
=
Γ
o
∗
tanh
c
<
t
>
a^{<t>}=\Gamma_{o} * \tanh c^{<t>}
a<t>=Γo∗tanhc<t>
LSTM计算图:
六、双向循环神经网络(BRNN)
不论是LSTM还是GRU他们都是只沿着一个方向进行的,也就是我们当前预测的词语可能不受后面的词语影响,这与我们的现实相冲突,于是,双向循环神经网络诞生了
其实他结构很简单,在RNN的基础上,在其左边再加一个RNN结构,但是他们是反向传递的,做个结构图:
画得有些杂乱,但是我们可以看出黑色部分是我们正向传播的RNN,绿色部分是我们反向传播的RNN,当然我们的反向传播应该是在正向传播完成之后完整的,我们可以看到我们的yHat使用的是前向的参数a和反向的参数a来共同预测的,他的公式是:
y ~ < t > = g ( W y [ a ⃗ < t > , a ← ] + b y ) \tilde{y}^{<t>} = g(W_y[\vec{a}^{<t>},\overleftarrow{a}]+b_y) y~<t>=g(Wy[a<t>,a]+by)
但是双向循环神经网络有一个缺点,他只能对完整的句子使用,对于一个语音识别系统来说,只有当人们在说完话后,这个神经网络才会运行。但是他确实做到了将我们的预测同过去和未来联系起来
七、深度循环神经网络(Deep RNN)
我们看一个结构图
看图很简答我们就是几个RNN结构堆叠在一起,我们的符号定义也不同了,[]内表示的是层数,<>内表示的时间数,我们的RNN结构除了标准的RNN外还可以使用LSTM,GRU,甚至是BRNN