一、为什么选择序列模型
序列模型可以用于很多领域,如语音识别,撰写文章等等。总之很多优点。。。
二、数学符号
为了后面方便说明,先将会用到的数学符号进行介绍。
以下图为例,假如我们需要定位一句话中人名出现的位置。
- 红色框中的为输入、输出值。可以看到人名输出用1表示,反之用0表示;
- 绿色框中的\(x^{<t>},y^{<t>}\)表示对应红色框中的输入输出值的数学表示,注意从1开始。
- 灰色框中的\(T_x,T_y\)分别表示输入输出序列的长度,在该例中,\(T_x=9,T_y=9\)
- 黄色框中\(X^{(i)<t>}\)上的表示第i个输入样本的第t个输入值,\(T_x^{(i)}\)则表示第i个输入样本的长度。输出y也同理。
输入值中每个单词使用One-shot来表示。即首先会构建一个字典(Dictionary),假设该例中的字典维度是10000*1(如图示)。第一个单词"Harry"的数学表示形式即为[0,0,0,……,1 (在第4075位) ,0,……,0],其他单词同理。
但是如果某一个单词并没有被包含在字典中怎么办呢?此时我们可以添加一个新的标记,也就是一个叫做Unknown Word的伪造单词,用 <UNK> 表示。具体的细节会在后面介绍。
三、循环神经网络模型
1.为什么不用标准网络
在介绍RNN之前,首先解释一下为什么之前的标准网络不再适用了。因为它有两个缺点:
- 输入和输出的长度不尽相同
- 无法共享从其他位置学来的特征。例如上一节中的Harry这个词是用\(x^{<1>}\)表示的,网络从该位置学习了它是一个人名。但是我们希望无论Harry在哪个位置出现网络都能识别出这是一个人名的一部分,而标准网络无法做到这一点。
2.RNN结构
还是以识别人名为例,第一个单词\(x^{<1>}\)输入神经网络得到输出\(y^{<1>}\)
同理由\(x^{<2>}\)将得到\(y^{<2>}\),以此类推。但是这就是传统网络存在的问题,即单词之间没有联系
为了将单词之间关联起来,所以将前一层的结果也作为下一层的输入数据。如下图示
整体的RNN结构有两种表示形式,如下图示
左边是完整的表达形式,注意第一层的\(a^{<0>}\)一般设置为0向量。
右边的示意图是RNN的简写示意图。
介绍完结构之后,我们还需要知道网络中参数的表达方式及其含义。如下图示,\(x^{<i>}\)到网络的参数用\(W_{ax}\)表示,\(a^{<i>}\)到网络的参数用\(W_{aa}\)表示,\(y^{<i>}\)到网络的参数用\(W_{ya}\)表示,具体含义将在下面进行说明。
如下图示,\(x^{<1>}\)通过网络可以传递到\(y^{<3>}\),但是这存在一个问题,即每个输出只与前面的输入有关,而与后面的无关。这个问题会在后续内容中进行改进。
3.RNN前向传播
如图示,\(a^{<0>}=\vec{0}\)
激活函数:\(g_1\)一般为tanh函数或者是Relu函数,\(g_2\)一般是Sigmod函数。
- \(a^{<1>}=g_1(W_{aa}a^{<0>}+W_{ax}x^{<1>}+b_a)\)
- \(y^{<1>}=g_2(W_{ya}a^{<1>}+b_y)\)
- \(a^{<t>}=g_1(W_{aa}a^{<t-1>}+W_{ax}x^{<t>}+b_a)\)
- \(y^{<t>}=g_2(W_{ya}a^{<t>}+b_y)\)
注意参数的下标是有顺序含义的,如\(W_{ax}\)下标的第一个参数表示要计算的量的类型,即要计算\(a\)矢量,第二个参数表示要进行乘法运算的数据类型,即需要与\(x\)矢量做运算。如 \(W_{ax} x^{t}\rightarrow{a}\)
4.简化RNN公式
下面将对如下公式进行化简:
- 1. 简化\(a^{<t>}\)
a^{<t>}&= g(W_{aa}a^{<t-1>}+W_{ax}x^{<t>}+b_a) \notag \\
&= g(W_a [a^{<t-1>},x^{<t>}]^{T}+b_a) \notag
\end{align}\]
注意,公式中使用了两个矩阵进行化简,分别是 \(W_a\)和\([a^{<t-1>},x^{<t>}]^T\)(使用转置符号更易理解),下面分别进行说明:
- \(W_a = [ W_{aa}, W_{ax}]\),假设\(W_{aa}\)是(100,100)的矩阵,\(W_{ax}\)是(100,10000)的矩阵,那么\(W\)则是(100,10100)的矩阵。
- \([a^{<t-1>},x^{<t>}]^T\)是下图示意。
故\(W_a [a^{<t-1>},x^{<t>}]^{T}\)矩阵计算如下图示
- 2.简化\(y^{<t>}\)
该节PPT内容:
四、通过时间的反向传播
下面将会对反向传播进行灰常灰常详细的介绍,跟着下面一张一张的图片走起来~
1.整体感受
- 首先再回顾一下RNN的整体结构
- 要进行反向传播,首先需要前向传播,传播方向如蓝色箭头所示,其次再按照红色箭头进行反向传播。
2.前向传播
- 首先给出所有输入数据,即从\(x^{<1>}\)到\(x^{<T_x>}\),\(T_x\)表示输入数据的数量。
- 初始化参数\(W_a,b_a\),将输入数据输入网络得到对应的\(a^{<t>}\)
- 再通过与初始化参数\(W_y,b_y\)得到\(y^{<t>}\)
3.损失函数定义
要进行反向传播,必须得有损失函数嘛,所以我们将损失函数定义如下:
- 每个节点的损失函数
\(L^{<t>}(\hat{y}^{<t>},y^{<t>})=y^{<t>}log(y^{<t>})-(1-y^{<t>})log(1-\hat{y}^{<t>})\)
- 整个网络的损失函数
\(L(\hat{y}^{<t>},y^{<t>)}) = \sum_{t=1}^{T_y}L^{<t>}(\hat{y}^{<t>},y^{<t>})\)
4.反向传播
计算出损失值后再通过梯度下降进行反向传播
5.整个流程图
五、不同类型的循环神经网络
本节主要介绍了其他更多类型的RNN结构,下图参考大数据文摘
六、语言模型和序列生成
1.什么是语言模型
凡事开头举个栗子,一切都好说:
假设一个语音识别系统听一句话得到了如下两种选择,作为正常人肯定会选择第二种。但是机器才如何做判断呢?
此时就需要通过语言模型来预测每句话的概率:
2.如何使用RNN构建语言模型
- 首先我们需要一个很大的语料库(Corpus)
- 将每个单词字符化(Tokenize,即使用One-shot编码)得到词典,,假设有10000个单词
- 还需要添加两个特殊的单词
-
: end of sentence. 终止符,表示句子结束.
-
: UNknown, 之前的笔记已介绍过.
-
: end of sentence. 终止符,表示句子结束.
3.构建语言模型示例
假设要对这句话进行建模:Cats average 15 hours of sleep a day. <EOS>
- 1.初始化
这一步比较特殊,即\(x^{<1>}\)和\(a^{<0>}\)都需要初始化为\(\vec{0}\)。
此时\(\hat{y}^{<1>}\)将会对第一个字可能出现的每一个可能进行概率的判断,即\(\hat{y}^{<1>}=[p(a),…,p(cats),…]\)。
当然在最开始的时候没有任何的依据,可能得到的是完全不相干的字,因为只是根据初始的值和激活函数做出的取样。
- 2.将真实值作为输入值
之所以将真实值作为输入值很好理解,如果我们一直传错误的值,将永远也无法得到字与字之间的关系。
如下图示,将\(y^{<1>}\)所表示的真实值Cats作为输入,即\(x^{<2>}=y^{<1>}\)得到\(\hat{y}^{<2>}\)。
此时的\(\hat{y}^{<2>}=[p(a|cats),…,p(average|cats),…]\)。
同理有\(\hat{y}^{<3>}=[p(a|cats\, average),…,p(average|cats\,average),…]\)
另外输入值满足: \(x^{<t>}=y^{<t-1>}\)
- 3.计算出损失值
下图给出了构建模型的过程以及损失值计算公式。
七、对新序列采样
当我们训练得到了一个模型之后,如果我们想知道这个模型学到了些什么,一个非正式的方法就是对新序列进行采样。具体方法如下:
在每一步输出\(\hat{y}\)时,通常使用 softmax 作为激活函数,然后根据输出的分布,随机选择一个值,也就是对应的一个字或者英文单词。
然后将这个值作为下一个单元的x输入进去(即\(x^{<t>}=\hat{y}^{<t-1>}\)), 直到我们输出了终结符,或者输出长度超过了提前的预设值n才停止采样。.
上述步骤具体如图示:
下图给出了采样之后得到的效果:
左边是对训练得到新闻信息模型进行采样得到的内容;
右边是莎士比亚模型采样得到的内容。
八、带有神经网络的梯度消失
1.RNN的梯度消失、爆炸问题
梯度值在RNN中也可能因为反向传播的层次太多导致过小或者过大。
- 当梯度值过小的时候,神经网络将无法有效地调整自己的权重矩阵导致训练效果不佳,称之为“梯度消失问题”(gradient vanishing problem);
- 过大时可能直接影响到程序的运作因为程序已经无法存储那么大的值,直接返回 NaN ,称之为“梯度爆炸问题”(gradient exploding problem)。
当梯度值过大的时候有一个比较简便的解决方法,每次将返回的梯度值进行检查,如果超出了预定的范围,则手动设置为范围的边界值。
if (gradient > max) {
gradient = max
}
但梯度值过小的解决方案要稍微复杂一点,比如下面两句话:
“The cat,which already ate apple,yogurt,banana,..., was full.”
“The cats,which already ate apple,yogurt,banana,..., were full.”
重点标出的 cat(s)和be动词(was,were) 是有很重要的关联的,但是中间隔了一个which引导的定语从句,对于前面所介绍的基础的RNN网络很难学习到这个信息,尤其是当出现梯度消失时,而且这种情况很容易发生。
我们知道一旦神经网络层次很多时,反向传播很难影响前面层次的参数。所以为了解决梯度消失问题,提出了GRU单元,下面一节具体介绍。
九、GRU单元
GRU(Gated Recurrent Unit)是一种用来解决梯度值过小的方法,首先来看下在一个时刻下的RNN单元,激活函数为 tanh
1.首先回顾一下普通RNN单元的结构示意图
如图示,输入数据为\(a^{<t-1>}\)和\(x^{<t>}\),与参数\(W_a\)进行线性运算后再使用tanh函数转化得到\(a^{<t>}\). 当然再使用softmax函数处理可以得到预测值。
2.GRU结构
- 记忆细胞
在GRU中会用到 “记忆细胞(Memory cell)” 这个概念,我们用变量\(c\)表示。这个记忆细胞提供了记忆功能,例如它能够帮助记住 cat对应was,cats对应were。
而在t时刻,记忆细胞所包含的值其实就是激活函数值,即\(c^{<t>}=a^{<t>}\)。
注意:在这里两个变量的值虽然一样,但是含义不同。另外在下节将介绍的LSTM中,二者值的大小有可能是不一样的,所以有必要使用这两种变量进行区分。
为了更新记忆细胞的值,我们引入\(\tilde{c}\)来作为候选值从而来更新\(c^{<t>}\),其公式为:
\]
- 更新门(update gate)
更新门是GRU的核心概念,它的作用是用于判断是否需要进行更新。
更新门用\(\Gamma_u\)表示,其公式为:
\]
如上图示,\(\Gamma_u\)值的大小大多分布在0或者1,所以可以将其值的大小粗略的视为0或者1。这就是为什么我们就可以将其理解为一扇门,如果\(\Gamma_u=1\),就表示此时需要更新值,反之不用。
- t时刻记忆细胞
有了更新门公式后,我们则可以给出t时刻记忆细胞的值的计算公式了:
\]
注意:上面公式中的 * 表示元素之间进行乘法运算,而其他公式是矩阵运算。
公式很好理解,如果\(\Gamma_u=1\),那么t时刻记忆细胞的值就等于候选值\(\tilde{c}\),反之等于前一时刻记忆细胞的值。
下图给出了该公式很直观的解释:
在读到“cat”的时候 ,其他时候一直为0,知道要输出“was”的时刻我们仍然知道“cat”的存在,也就知道它为单数了。
- GRU结构示意图
3.完整版GRU
上述是简化了的GRU,在完整版中还存在另一个符号 ,这个符号的意义是控制\(\tilde{c}\)和\(c^{<t-1>}\)之间的联系强弱,完整版公式如下:
注意,完整公式中多出了一个\(\Gamma_r\),这个符号的作用是控制\(\tilde{c}^{<t>}\)与\(c^{<t>}\)之间联系的强弱。
十、长短期记忆
介绍完GRU后,再介绍LSTM会更加容易理解。下图是二者公式对比:
GRU只有两个门,而LSTM有三个门,分别是更新门\(\Gamma_u\),遗忘门\(\Gamma_f\),输出门\(\Gamma_o\)
虽然LSTM比GRU更复杂,但是它比GRU更早提出哦。另外一般而言LSTM的表现要更好,但是计算量更大,毕竟多了一个门嘛。而GRU实际上是对LSTM的简化,它的表现也不错,能够更好地扩展到深层网络。所以二者各有优势。
下图是LSTM的结构示意图:
十一、双向递归神经网络
前面介绍的都是单向的RNN结构,在处理某些问题上得到的效果不尽人意
如下面两句话,我们要从中标出人名:
He said, "Teddy Roosevelt was a great President".
He said, "Teddy bears are on sale".
第一句中的Teddy Roosevelt是人名,但第二句中的Teddy bears是泰迪熊,同样都是单词Teddy对应的输出在第一句中应该是1,第二句中应该是0。
像这样的例子如果想让我们的序列模型明白就需要借助不同的结构比如 - 双向递归神经网络(Bidirectional RNN)。该神经网络首先从正面理解一遍这句话,再从反方向理解一遍。
双向递归神经网络结构如下:
下图摘自大数据文摘整理
十二、深层循环神经网络
深层,顾名思义就是层次增加。如下图是深层循环神经网络的示意图
横向表示时间展开,纵向则是层次展开。
注意激活值的表达形式有所改变,以\(a^{[1]<0>}\)为例进行解释:
- [1]表示第一层
- <0>表示第一个激活值
另外各个激活值的计算公式也略有不同,以\(a^{[2]<3>}\)为例,其计算公式如下: