CS224n学习笔记(三)

语言模型

对于一个文本中出现的单词 (w_i) 的概率,他更多的依靠的是前 (n) 个单词,而不是这句话中前面所有的单词。

[Pleft(w_{1}, ldots, w_{m} ight)=prod_{i=1}^{i=m} Pleft(w_{i} | w_{1}, ldots, w_{i-1} ight) approx prod_{i=1}^{i=m} Pleft(w_{i} | w_{i-n}, ldots, w_{i-1} ight) ]

在翻译系统中就是对于输入的短语,通过对所有的输出的语句进行评分,得到概率最大的那个输出,作为预测的概率。

n-gram 语言模型

对于N-Gram模型,首先要知道其中的该模型中的 count的意思,count 可以用来表示单词出现的频率,这个模型与条件概率密切相关,其中,

[egin{aligned} pleft(w_{2} | w_{1} ight) &=frac{operatorname{count}left(w_{1}, w_{2} ight)}{operatorname{count}left(w_{1} ight)} \ pleft(w_{3} | w_{1}, w_{2} ight) &=frac{operatorname{count}left(w_{1}, w_{2}, w_{3} ight)}{operatorname{count}left(w_{1}, w_{2} ight)} end{aligned} ]

对于上面的式子的解释就是,我们将 连续单词出现的频率作为概率,然后通过条件概率的形式预测出下一个单词。这个模型面临的问题是,选取多大的框,也就是选取前面多少个单词,这里面还牵涉到稀疏问题,不必要的信息就不存储,同时还要存储必要信息。

对于上面的模型需要考虑一些问题,首先是,分母分子为零的

基于窗口的神经语言模型

传统的模型可以简化为下面的图片中的样式:

蓝色的一层是对输入的处理,获取单词向量:(oldsymbol{e}=left[oldsymbol{e}^{(1)} ; oldsymbol{e}^{(2)} ; oldsymbol{e}^{(3)} ; oldsymbol{e}^{(4)} ight])。然后红色就是中间的隐含层,(h=fleft(oldsymbol{W} oldsymbol{e}+oldsymbol{b}_{1} ight)),最后加一个 (Softmax) 层,就是分类的意思,(hat{y}=operatorname{softmax}left(U h+b_{2} ight))

Recurrent Neural Networks (RNN)

传统的 (RNN) 如下图所示,是一个比较简单的结构,我们可以用两个式子来表示:

对于每一层的输入,像下面这个式子,其中激活函数一般会使用 (tanh()), 也可以使用 (sigmoid),下面的两个矩阵 (W^{(hh)}) 和矩阵 (W^{(hx)}) 维度不同,因为本质都是线性的,所以维度不同没有关系。

[h_{t}=sigmaleft(W^{(h h)} h_{t-1}+W^{(h x)} x_{[t]} ight) ]

然后先通过一个矩阵 (W^{(S)}) 然后通过一个 (Softmax) 函数就可以了,

[hat{y}_{t}=operatorname{softmax}left(W^{(S)} h_{t} ight) ]

对于 (softmax) 函数,我们还要知道,(hat{y}_{t}) 是最终预测的得分,而我们 (RNN) 传递的是 (h_t)。其中(W^{(S)} in mathbb{R}^{|V| imes D_{h}}) and (hat{y} in mathbb{R}^{|V|})

上图就是对于一个句子的翻译,可以看出传统方法,与 RNN的区别。

RNN 的损失函数

我们通常用交叉熵损失函数来表示错误率,交叉熵损失函数就是统计预测正确的本来正确的概率,然后取反作为目标函数,通常还会取一个对数,

[J^{(t)}( heta)=-sum_{j=1}^{|V|} y_{t, j} imes log left(hat{y}_{t, j} ight) ]

如果一个语料库的大小为 (T),那么交叉熵损失函数就是应用于 大小为 (T) 的每一个单词:

[J=frac{1}{T} sum_{t=1}^{T} J^{(t)}( heta)=-frac{1}{T} sum_{t=1}^{T} sum_{j=1}^{|V|} y_{t, j} imes log left(hat{y}_{t, j} ight) ]

RNN还有一个概念叫做困惑,所谓困惑,定义如下:

[Perplexity =2^{J} ]

RNN 的优缺点及其使用

使用 RNN的时的一些问题,

  1. RNN 网络具有记忆性,
  2. RNN 网络使用时,内存大小与语句大小成比例。
  3. RNN 中矩阵的大小与迭代的次数有关。

RNN 中的梯度消失与梯度爆炸问题

举个例子,对于两个句子:

"Jane walked into the room. John walked in too. Jane said hi to ___"

"Jane walked into the room. John walked in too. It was late in the day, and everyone was walking home after a long day at work. Jane said hi to ___"

使用RNN预测空格里面的单词,第一个句子预测正确的概率要更大一些。

下面从数学的角度解释梯度消失问题。

就像传统神经网络反向传播的时候一样,我们要对参数矩阵求导,以此获得最佳的参数。而在 RNN中,我们需要将每一次的损失加起来,也就是下面的式子:

[frac{partial E}{partial W}=sum_{t=1}^{T} frac{partial E_{t}}{partial W} ]

对于每一个时间点 (t),我们使用链式规则,

[frac{partial E_{t}}{partial W}=sum_{k=1}^{t} frac{partial E_{t}}{partial y_{t}} frac{partial y_{t}}{partial h_{t}} frac{partial h_{t}}{partial h_{k}} frac{partial h_{k}}{partial W} ]

注意对于时间点 t, 我们要考虑从起始点一直到时间点 t,这是因为 RNN是链式的,而不像传统的神经网络不是链式的,相当于每一步都要进行一次优化矩阵 W,所以计算 (d h_{t} / d h_{k}) 可以用下面的方法:

[frac{partial h_{t}}{partial h_{k}}=prod_{j=k+1}^{t} frac{partial h_{j}}{partial h_{j-1}}=prod_{j=k+1}^{t} W^{T} imes operatorname{diag}left[f^{prime}left(h_{j-1} ight) ight] ]

所以链式规则等价于下面的式子:

[frac{partial E}{partial W}=sum_{t=1}^{T} sum_{k=1}^{t} frac{partial E_{t}}{partial y_{t}} frac{partial y_{t}}{partial h_{t}}left(prod_{j=k+1}^{t} frac{partial h_{j}}{partial h_{j-1}} ight) frac{partial h_{k}}{partial W} ]

上周讲到神经网络中的梯度下降的时候,对矩阵的链式求导,我们讲到了雅可比矩阵,这里就用雅可比矩阵。

[frac{partial h_{j}}{partial h_{j-1}}=left[frac{partial h_{j}}{partial h_{j-1,1}} dots frac{partial h_{j}}{partial h_{j-1, D_{n}}} ight]=left[ egin{array}{ccc}{frac{partial h_{j, 1}}{partial h_{j-1,1}}} & {cdots} & {frac{partial h_{j, 1}}{partial h_{j-1, D_{n}}}} \ {vdots} & {ddots} & {vdots} \ {frac{partial h_{j, D_{n}}}{partial h_{j-1,1}}} & {cdots} & {frac{partial h_{j, D_{n}}}{partial h_{j-1, D_{n}}}}end{array} ight] ]

上面的式子很重要,因为要计算 (h_j)(h_{j-1})之间的关系,(h_{t}=sigmaleft(W^{(h h)} h_{t-1}+W^{(h x)} x_{[t]} ight))。对这个函数中 (h_{j-1}) 求偏导。根据求导的链式法则

[frac{partial h_{j}}{partial h_{j-1}} = W^{T} imes operatorname{diag}left[f^{prime}left(h_{j-1} ight) ight] ]

我们用 (eta_{W} ,eta_{h})分别表示两个行列式的上确界,而对于上面的式子,我们有:

[left|frac{partial h_{j}}{partial h_{j-1}} ight| leqleft|W^{T} ight|left|operatorname{diag}left[f^{prime}left(h_{j-1} ight) ight] ight| leq eta_{W} eta_{h} ]

因此用于链式法则就是:

[left|frac{partial h_{t}}{partial h_{k}} ight|=left|prod_{j=k+1}^{t} frac{partial h_{j}}{partial h_{j-1}} ight| leqleft(eta_{W} eta_{h} ight)^{t-k} ]

我们将最初的式子替换一下,就是下面这样:

[frac{partial E_{t}}{partial W}=sum_{k=1}^{t} frac{partial E_{t}}{partial y_{t}} frac{partial y_{t}}{partial h_{t}} left(eta_{W} eta_{h} ight)^{t-k} frac{partial h_{k}}{partial W} ]

显然这里有指数的问题,所以问题就与直接与 (eta_{W} ,eta_{h})相关了。所以梯度消失的问题就是 (eta_{W} eta_{h}) 与 1 的大小关系的问题了。当 (eta_{W} eta_{h}) 大于 1的时候我们成为梯度爆炸,接近 0 的时候,我们称为梯度消失。

RNN 梯度下降与梯度消失的解决方法

处理梯度上升的一个简单的策略就是设置阈值:

[egin{array}{c}{hat{g} leftarrow frac{partial E}{partial W}} \ { ext { if }|hat{g}| geq ext { threshold then }} \ {hat{g} leftarrow frac{ ext {threshold}}{|hat{g}|} hat{g}} \ { ext { end if }}end{array} ]

为了解决梯度消失问题,可以采用两种方法,分别是初始化矩阵 (W^{(h h)}),而不是随机取这个矩阵。另一种方法是,激活函数使用 (ReLU) 而不是(sigmod) 函数,因为 ReLU 函数的导数要么是 0,要么是 1。

深度双向RNN

双向 RNN,顾名思义就是有两个方向相反的 RNN,如果未知的是中间位置的单词,我们也可以反过来预测,这就是反向 RNN的思想。就如同下面的公式所说的:

[vec{h}_{t}=fleft(vec{W} x_{t}+vec{V} vec{h}_{t-1}+vec{b} ight) ]

[stackrel{leftarrow}{h}_{t}=fleft(stackrelleftarrow{W} x_{t}+stackrel{leftarrow}{V} stackrel{leftarrow}h_{t+1}+stackrel{leftarrow}{b} ight) ]

[hat{y}_{t}=gleft(U h_{t}+c ight)=gleft(Uleft[stackrel{ ightarrow}{h}_{t} ; stackrel{leftarrow}{h}_{t} ight]+c ight) ]

上面右图是多层 RNN 的模型,对于 (h_t^{(i)}),既与前一层 t 时刻的神经元 (h_t^{(i-1)}) 有关,还和 (h_{t-1}^{(i)}) 这一层前一个时刻的神经元有关,也就是每一个时间步长,不仅会纵向传播(RNN的序列传播),还会横向传播,所有的层,并行的向前传播),用公式表示就是:

[vec{h}_{t}^{(i)}=fleft(vec{W}^{(i)} h_{t}^{(i-1)}+vec{V}^{(i)} vec{h}_{t-1}^{(i)}+vec{b}^{(i)} ight) ]

[stackrel{leftarrow}{h}_{t}^{(i)}=fleft(stackrel{leftarrow}{W}^{(i)} h_{t}^{(i-1)}+stackrel{leftarrow}{V}^{(i)} stackrel{leftarrow}{h}_{t-1}^{(i)}+stackrel{leftarrow}{b}^{(i)} ight) ]

[hat{y}_{t}=gleft(U h_{t}+c ight)=gleft(Uleft[vec{h}_{t}^{(L)} ; stackrel{leftarrow}{h}_{t}^{(L)} ight]+c ight) ]

RNN 翻译模型

我们使用 RNN来完成翻译,首先是 encoder,前面我们讲过神经依赖解析算法,如果我们用 RNN实现这一步,那就是这里的encoder了,这一步主要是讲源语句转化为 dense的单词向量。下面左图中的,前三层神经网络就是encoder的过程,然后最后两层就是将向量转换为另一种语言,也就是decoder的过程,其实decoder真正做的是,将向量转为另一种语言表示的向量。

下面的等式:

[h_{t}=phileft(h_{t-1}, x_{t} ight)=fleft(W^{(h h)} h_{t-1}+W^{(h x)} x_{t} ight) ]

表示的是encoder的过程。在decoder阶段,

[h_{t}=phileft(h_{t-1} ight)=fleft(W^{(h h)} h_{t-1} ight) ]

[y_{t}=operatorname{softmax}left(W^{(S)} h_{t} ight) ]

对于上面的模型,我们可以使用交叉熵损失函数作为目标函数:

[max _{ heta} frac{1}{N} sum_{n=1}^{N} log p_{ heta}left(y^{(n)} | x^{(n)} ight) ]

拓展

对于 decoder阶段的隐层状态可以有三种不同的输入,分别是:

  • decoder 前一个时间段的状态 (h_{t-1}).
  • encoder 的最后一层的状态,就像图 11所示
  • 前一个预测的输出的单词

所以我们可以考虑结合这三个输出,也就得到下面的模型:

[h_{t}=phileft(h_{t-1}, c, y_{t-1} ight) ]

这个顺带提到,在谷歌提出的 sequence to sequence中,可以考虑将输入单词反转来提高翻译的准确率。

GRU

我们先看下 GRU直观的解释。

新存储信息的产生

[ ilde{h}_{t}= anh left(r_{t} circ U h_{t-1}+W x_{t} ight) ]

一个新的内存状态表示,对于一个新输入的单词,考虑过去的状态,既根据语境结合新的单词与前面上下文的语境。比如我们读到句子中的一个毫不相关的单词,那么就不考虑这个单词的效益。

Reset Gate

[r_{t}=sigmaleft(W^{(r)} x_{t}+U^{(r)} h_{t-1} ight) ]

可以看作是一个经过激活函数的门,来判断 (h_{t-1})(h_t) 的影响,判断是否可以完全忽略 (h_{t-1}) 如果 (h_{t-1}) 与产生新内存无关。比如我们进入一个完全新的句子,在长语句中进入一个完全新的句子,就可以不考虑 (h_{t-1})

Update Gate

[z_{t}=sigmaleft(W^{(z)} x_{t}+U^{(z)} h_{t-1} ight) ]

这也是一个门,用来判断更新的时候的取值,从上面的图中可以打看到, (z_t) 是在 ( ilde{h}_{t})(h_{(mathrm{t}-1)}) 之间的权衡的。我们可以这么说,当 (z_t) (approx 1) 的时候,(h_{t-1}) 这一层几乎全部复制给 (h_t),而当 (z_t) $approx 0 $ 的时候,我们更多的将 ( ilde{h}_{t}) 传到下一个状态。

Hidden state:

[h_{t}=left(1-z_{t} ight) circ ilde{h}_{t}+z_{t} circ h_{t-1} ]

隐含层在计算的时候,是考虑到对 ( ilde{h}_{t})(h_{(mathrm{t}-1)}) 之间的权衡取舍,这也是 GRU的核心之一。GRU的核心就是,首先判断新来的单词与上下文之间的关系,需要联合语句构造新的语境。

LSTM

LSTM之前有所接触,感觉理解的不够深刻,比起 GRU,门明显变多了,但是本质差不了太多,先看下直观解释:

New memory generation:

[ ilde{c}_{t}= anh left(W^{(c)} x_{t}+U^{(c)} h_{t-1} ight) ]

这一步与 GRU类似。

Input Gate:

[i_{t}=sigmaleft(W^{(i)} x_{t}+U^{(i)} h_{t-1} ight) ]

判断新的单词是否值得作为参数,和 GRU一样。通过与上下文的结合,判断这个输入,有多大的权重作为新的特征。

Forget Gate:

[f_{t}=sigmaleft(W^{(f)} x_{t}+U^{(f)} h_{t-1} ight) ]

忘记层,与 Input Gate 类似,但是这里是判断是否忘记,也就是判断是否忘记前面所记录的信息,我们可以理解为特征。

Final memory generation:

[c_{t}=f_{t} circ c_{t-1}+i_{t} circ ilde{c}_{t} ]

这一层就是根据前面的几个门,信息留下的权重,新的信息考虑进去的权重。

Output/Exposure Gate:

[o_{t}=sigmaleft(W^{(o)} x_{t}+U^{(o)} h_{t-1} ight) ]

[h_{t}=o_{t} circ anh left(c_{t} ight) ]

这里的两个函数的目的是将 (h_t)(C_t) 分开,因为 (C_t) 中存储了很多不必要的信息。因为隐含层作为每一个门的参数,所以这里需要考虑哪些信息留在隐含层。所以使用了一个门 (O_t)

原文地址:https://www.cnblogs.com/wevolf/p/10853916.html