天天看点

Stanford CS224n 第八讲:RNN和语言模式

1. 传统的语言模型

2. 循环神经网络模型(RNN)

3. 训练神经网络面临的问题和技巧

  • 梯度消失/爆炸出现的原因
  • 梯度消失/爆炸实例
  • 处理梯度消失/爆炸技巧

5. RNN应用于其他序列任务

\newline

\newline

本节课主要讲了循环神经网络,下面将上面几个部分详述:

1. 传统的语言模型

语言模型是为了计算一个单词序列出现的概率 P ( w 1 , w 2 , . . . , w T ) P(w_1,w_2,...,w_T) P(w1​,w2​,...,wT​),使最有可能出现的序列概率最大,例如在机器翻译的问题中:

在翻译"猫很小"时,单词的语序问题中我们会使概率 P(the cat is small) > P(small the is cat);

在翻译"放学后走回家"时,单词的选择问题中我们会使概率 P(walking home after school) > P(walking house after school)

当前我们很难为所有的语序计算概率,所以我们只关注窗口下的前n个单词来预测当前词的概率。当然这是不正确但是必须的马尔科夫假设:

P ( w 1 , . . . , w m ) = ∏ i = 1 m P ( w i ∣ w 1 , . . . w i − 1 ) ≈ ∏ i = 1 m P ( w i ∣ w i − ( n − 1 ) , . . . w i − 1 ) P(w_1,...,w_m)=\prod_{i=1}^m{P(w_i|w_1,...w_{i-1})}\approx\prod_{i=1}^m{P(w_i|w_{i-(n-1)},...w_{i-1})} P(w1​,...,wm​)=i=1∏m​P(wi​∣w1​,...wi−1​)≈i=1∏m​P(wi​∣wi−(n−1)​,...wi−1​)

通过语料中的先验也就是通过计数的方式来计算一元和二元的条件概率公式如下:

n=1时,概率公式为: P ( w 2 ∣ w 1 ) = c o u n t ( w 1 , w 2 ) c o u n t ( w 1 ) P(w_2|w_1)={count(w_1,w_2)\over{count(w_1)}} P(w2​∣w1​)=count(w1​)count(w1​,w2​)​

n=2时,概率公式为: P ( w 3 ∣ w 1 , w 2 ) = c o u n t ( w 1 , w 2 , w 3 ) c o u n t ( w 1 , w 2 ) P(w_3|w_1,w_2)={count(w_1,w_2,w_3)\over{count(w_1,w_2)}} P(w3​∣w1​,w2​)=count(w1​,w2​)count(w1​,w2​,w3​)​

n=m时,概率公式为: P ( w m + 1 ∣ w 1 , . . . , w m ) = c o u n t ( w 1 , . . . , w m + 1 ) c o u n t ( w 1 , . . . , w m ) P(w_{m+1}|w_1,...,w_m)={count(w_1,...,w_{m+1})\over{count(w_1,...,w_m)}} P(wm+1​∣w1​,...,wm​)=count(w1​,...,wm​)count(w1​,...,wm+1​)​

其中count()为一个计数函数,它是负责计算对应词序在语料中出现的频数。由上面几部分显然,当n越大,马尔科夫假设效果就越弱,计算出来的概率值也就越接近于真实值,当然n越大,我们的计算量也就越大(鱼和熊掌不可兼得)。

2. 循环神经网络模型(RNN)

目前对于语言模型,我们采用的是循环神经网络,先通过模型图来了解一下:

Stanford CS224n 第八讲:RNN和语言模式

上图的展开形式如下:

Stanford CS224n 第八讲:RNN和语言模式

一般情况下我们以展开形式来理解RNN,但是实际上他们就是一个相同单元的展开,可以理解为每个单元的参数是共享的。

RNN的公式定义为:

给定一个词向量列表: x 1 , . . . , x t − 1 , x t , x t + 1 . . . , x T x_1,...,x_{t-1},x_t,x_{t+1}...,x_T x1​,...,xt−1​,xt​,xt+1​...,xT​

h t = σ ( W h h h t − 1 + W h x x t ) h_t=\sigma(W^{hh}h_{t-1}+W^{hx}x_t) ht​=σ(Whhht−1​+Whxxt​)

y ^ = s o f t m a x ( W ( S ) h t ) \hat{y}=softmax(W^{(S)}h_t) y^​=softmax(W(S)ht​)

其中:

x t ∈ R d ∗ 1 x_t\in R^{d*1} xt​∈Rd∗1,是在t时刻输入模型的 d x ∗ 1 d_x*1 dx​∗1维度的词向量;

W h h ∈ R D h ∗ D h W^{hh}\in R^{D_h*D_h} Whh∈RDh​∗Dh​,是对输入的上一层的隐向量 h t − 1 h_{t-1} ht−1​进行线性变换的权重矩阵;

W h x ∈ R D h ∗ d W^{hx}\in R^{D_h*d} Whx∈RDh​∗d,是对输入的词向量 x t x_t xt​进行线性变换的权重矩阵;

h t ∈ R D h ∗ 1 h_t\in R^{D_h*1} ht​∈RDh​∗1,是通过 σ \sigma σ()函数对输入和上一层传过来的隐向量之和记性非线性变换;

W ( S ) ∈ R ∣ V ∣ ∗ D h W^{(S)}\in R^{|V|*D_h} W(S)∈R∣V∣∗Dh​,是通过 W ( S ) W^{(S)} W(S)矩阵对 h t h_t ht​映射成|V|*1维的列向量,其中|V|表示待分类的类别数;

y ^ ∈ R ∣ V ∣ ∗ 1 \hat{y}\in R^{|V|*1} y^​∈R∣V∣∗1,通过softmax函数,将 W ( S ) h t W^{(S)}h_t W(S)ht​的结果转换成各个类别的概率值。

还有一点需要注意的是,当刚开始的时候RNN没有上一层传入的隐向量,一般情况下,我们初始化一个全零的 h 0 h_0 h0​作为第一层传入的隐向量。

我们现在的任务不是分类而是预测下一个单词出现的概率值,所以我们需要拟合的就是

P ^ ( x t + 1 = v j ∣ x 1 , . . . , x t ) = y ^ t , j 其 中 v 表 示 语 料 库 大 小 \hat{P}(x_{t+1}=v_j|x_1,...,x_t)=\hat{y}_{t,j} 其中v表示语料库大小 P^(xt+1​=vj​∣x1​,...,xt​)=y^​t,j​其中v表示语料库大小

因此在t时刻损失函数也采用交叉熵loss function,但是此时已经不是预测类别,而是预测单词:

J t ( θ ) = − ∑ j = 1 ∣ V ∣ y t . j l o g y ^ t . j 其 中 ∣ V ∣ 表 示 语 料 库 大 小 J^t(\theta)=-\sum^{|V|}_{j=1}y_{t.j}log\hat{y}_{t.j} 其中|V|表示语料库大小 Jt(θ)=−j=1∑∣V∣​yt.j​logy^​t.j​其中∣V∣表示语料库大小

由上式我们显然可得到,整个序列的损失函数为:

J t ( θ ) = − 1 T ∑ t = 1 T ∑ j = 1 ∣ V ∣ y t . j l o g y ^ t . j 其 中 ∣ V ∣ 表 示 语 料 库 大 小 J^t(\theta)=-{1\over T}\sum^{T}_{t=1}\sum^{|V|}_{j=1}y_{t.j}log\hat{y}_{t.j} 其中|V|表示语料库大小 Jt(θ)=−T1​t=1∑T​j=1∑∣V∣​yt.j​logy^​t.j​其中∣V∣表示语料库大小

3. 训练神经网络面临的问题和技巧

RNN和普通的神经网络的训练过程有些许不同,对于普通的神经网络,我们一般是自上而下的进行梯度计算,但是RNN特殊之处就在于,他的梯度有自上而下,自右向左两个方向来源,如下图:

Stanford CS224n 第八讲:RNN和语言模式
  • 梯度消失/爆炸出现的原因

    梯度消失是什么?为什么会出现梯度消失?下面我们来看一下RNN的反向传播过程。

    假设我们的RNN公式形式和上面定义保持一致:

    h t = σ ( W h h h t − 1 + W h x x t ) h_t=\sigma(W^{hh}h_{t-1}+W^{hx}x_t) ht​=σ(Whhht−1​+Whxxt​)

    y t ^ = s o f t m a x ( W ( S ) h t ) \hat{y_t}=softmax(W^{(S)}h_t) yt​^​=softmax(W(S)ht​)

    对于RNN结构来说,由于我们在序列的每个位置都有损失,所以最终的损失函数为:

    J = ∑ t = 1 T J t J=\sum^T_{t=1}J^t J=∑t=1T​Jt

    其中 W ( S ) W^{(S)} W(S)的梯度计算是比较简单的:

    ∂ J ∂ W ( S ) = ∑ t = 1 T ∂ J t ∂ W ( S ) = ∑ t = 1 T ∂ J t ∂ y ^ t ∂ y ^ t ∂ W ( S ) {{\partial J}\over{\partial W^{(S)}}}=\sum^T_{t=1}{\partial J^t \over \partial W^{(S)}}=\sum^T_{t=1}{\partial J^t \over \partial \hat y_t}{\partial \hat y_t \over \partial W^{(S)}} ∂W(S)∂J​=t=1∑T​∂W(S)∂Jt​=t=1∑T​∂y^​t​∂Jt​∂W(S)∂y^​t​​

    但是 W h h W^{hh} Whh和 W h x W^{hx} Whx的梯度计算就比较的复杂了。从RNN的模型可以看出,在反向传播时,在t时刻的梯度损失由当前位置的输出对应的梯度损失(从上到下)和t+1时的梯度损失(从右到左)两部分共同决定。对于W在某一时刻t的梯度损失需要反向传播一步步的计算。为了方便后面的重复利用,我们定义时刻t的隐藏状态的梯度为 δ ( t ) \delta^{(t)} δ(t):

    δ ( t ) = ∂ J ∂ h t \delta^{(t)}={\partial J \over \partial h_t} δ(t)=∂ht​∂J​

    由此我们可以跟普通的神经网络一样,由 δ ( t + 1 ) \delta^{(t+1)} δ(t+1)推导出 δ ( t ) \delta^{(t)} δ(t):

    δ ( t ) = ∂ J ∂ y t ^ ∂ y t ^ ∂ h t + ∂ J ∂ h t + 1 ∂ h t + 1 ∂ h t = ∂ J ∂ y t ^ ∂ y t ^ ∂ h t + δ ( t + 1 ) ∂ h t + 1 ∂ h t \delta^{(t)}={\partial J \over \partial \hat {y_t}}{\partial \hat {y_t} \over \partial h_t}+{\partial J \over \partial h_{t+1}}{ \partial h_{t+1} \over \partial h_t}={\partial J \over \partial \hat {y_t}}{\partial \hat {y_t} \over \partial h_t}+{\delta^{(t+1)}}{ \partial h_{t+1} \over \partial h_t} δ(t)=∂yt​^​∂J​∂ht​∂yt​^​​+∂ht+1​∂J​∂ht​∂ht+1​​=∂yt​^​∂J​∂ht​∂yt​^​​+δ(t+1)∂ht​∂ht+1​​

    显然上式中第一部分为从上到下传播的梯度;第二部分为从右往左传播的梯度。

为了方便运算书写,我们假设 h t h_t ht​的激活函数 σ ( ) \sigma() σ()变为tanh()函数(PS:激活函数的一种,不影响求导思路,此处替换仅仅是为了方便运算),因此 W h h 和 W ( h x ) W^{hh}和W^(hx) Whh和W(hx)的求偏导过程如下:

∂ J ∂ W ( h h ) = ∑ t = 1 T ∂ J ∂ h t ∂ h t ∂ W ( h h ) = ∑ t = 1 T δ ( t ) ⨀ ( 1 ∈ R D h ∗ 1 − ( h t ) 2 ) h t − 1 T {\partial J\over \partial W^{(hh)}}=\sum^T_{t=1}{\partial J \over \partial h_t}{\partial h_t \over \partial W^{(hh)}}=\sum^T_{t=1}\delta^{(t)}\bigodot(1^{\in R^{D_h*1}}-(h_t)^2) h_{t-1}^T ∂W(hh)∂J​=t=1∑T​∂ht​∂J​∂W(hh)∂ht​​=t=1∑T​δ(t)⨀(1∈RDh​∗1−(ht​)2)ht−1T​

∂ J ∂ W ( h x ) = ∑ t = 1 T ∂ J ∂ h t ∂ h t ∂ W ( h x ) = ∑ t = 1 T δ ( t ) ⨀ ( 1 ∈ R D h ∗ 1 − ( h t ) 2 ) x t T {\partial J\over \partial W^{(hx)}}=\sum^T_{t=1}{\partial J \over \partial h_t}{\partial h_t \over \partial W^{(hx)}}=\sum^T_{t=1}\delta^{(t)}\bigodot(1^{\in R^{D_h*1}}-(h_t)^2) x_t^T ∂W(hx)∂J​=t=1∑T​∂ht​∂J​∂W(hx)∂ht​​=t=1∑T​δ(t)⨀(1∈RDh​∗1−(ht​)2)xtT​

上式中:

⨀ \bigodot ⨀为哈达玛乘积,也就是矩阵对应元素相乘

δ ( t ) ∈ R D h ∗ 1 \delta^{(t)}\in R^{D_h*1} δ(t)∈RDh​∗1

h t ∈ R D h ∗ 1 h_t\in R^{D_h*1} ht​∈RDh​∗1

x t ∈ R d ∗ 1 x_t\in R^{d*1} xt​∈Rd∗1

激活函数tanh(x)求导有如下性质: f ( x ) = t a n h ( x ) f(x)=tanh(x) f(x)=tanh(x), f ’ ( x ) = 1 − ( f ( x ) ) 2 f^’(x)=1-(f(x))^2 f’(x)=1−(f(x))2

其实以上过程也跟第四讲中的推倒过程类似,我是先通过求 W h h W^{hh} Whh矩阵中单一元素的偏导数,然后拓展到矩阵求导,下面是对 W i j h h W^{hh}_{ij} Wijhh​的求导过程, W i j h x W^{hx}_{ij} Wijhx​类似,所以此处只推导 W i j h h W^{hh}_{ij} Wijhh​:

∂ h t ∂ W i j h h = ( 1 − ( h t ( i ) ) 2 ) ∂ ( W h h h t − 1 + W h x x t ) ∂ W i j h h = ( 1 − ( h t ( i ) ) 2 ) ∂ ( ∑ m = 1 D h W i m h h h t − 1 ( m ) + W h x x t ) ∂ W i j h h = ( 1 − ( h t ( i ) ) 2 ) h t − 1 ( j ) {\partial h_t \over \partial W^{hh}_{ij}}=(1-(h^{(i)}_t)^2){\partial (W^{hh}h_{t-1}+W^{hx}x_t) \over \partial W^{hh}_{ij}}=(1-(h^{(i)}_t)^2){\partial (\sum ^{D_h}_{m=1}W^{hh}_{im}h^{(m)}_{t-1}+W^{hx}x_t) \over \partial W^{hh}_{ij}}=(1-(h^{(i)}_t)^2)h^{(j)}_{t-1} ∂Wijhh​∂ht​​=(1−(ht(i)​)2)∂Wijhh​∂(Whhht−1​+Whxxt​)​=(1−(ht(i)​)2)∂Wijhh​∂(∑m=1Dh​​Wimhh​ht−1(m)​+Whxxt​)​=(1−(ht(i)​)2)ht−1(j)​

由上式单个变量的偏导数我们就可以推导出他们的矩阵形式,以上推导过程如有错误,请不吝赐教。

从上面的推导过程中我们可以看到在对W求导的时候:

∂ J ∂ W ( h h ) = ∑ t = 1 T ∂ J t ∂ y t ^ ∂ y t ^ ∂ h t ∂ h t ∂ h k ∂ h k ∂ W ( h h ) {\partial J\over \partial W^{(hh)}}=\sum^T_{t=1}{\partial J_t \over \partial \hat {y_t}}{\partial \hat {y_t} \over \partial h_t}{\partial h_t \over \partial h_k}{\partial h_k \over \partial W^{(hh)}} ∂W(hh)∂J​=t=1∑T​∂yt​^​∂Jt​​∂ht​∂yt​^​​∂hk​∂ht​​∂W(hh)∂hk​​

其中上式中:

∂ h t ∂ h k = ∏ j = k + 1 t ∂ h j ∂ h j − 1 {\partial h_t \over \partial h_k}=\prod^t_{j=k+1}{\partial h_j \over \partial h_{j-1}} ∂hk​∂ht​​=j=k+1∏t​∂hj−1​∂hj​​

上式中每一个偏导数都是一个雅可比Jacobain行列式:

Stanford CS224n 第八讲:RNN和语言模式

对于雅可比行列式,我们可以假设他的范数的上限为 β \beta β,则我们可以得到:

Stanford CS224n 第八讲:RNN和语言模式

则W的梯度是一系列雅可比行列式范数的乘积:

Stanford CS224n 第八讲:RNN和语言模式

由上式我们可知,当我们初始化时,行列式的范数大于1时,梯度就会出现变得无限大,这就是梯度爆炸;行列式范数小于1时,梯度就会变得无限小,这就是梯度消失。对于普通的神经网络梯度消失会造成神经网络前几层的参数变化很小或者几乎不更新。

  • 梯度消失出现的实例

    话不多说,先把代码贴上(Git地址)再分析结果:

import numpy as np
import matplotlib.pyplot as plt

#sigmoid函数
def sigmoid(x):
    x=1/(1+np.exp(-x))
    return x
#sigmoid导函数
def sigmoid_grad(x):
    return x*(1-x)
#Relu激活函数
def relu(x):
    return np.maximum(0,x)

def three_layer_net(NONLINEARITY,X,y,model,step_size,reg):
    #参数初始化
    h=model['h']
    h2=model['h2']
    W1=model['W1']
    W2 = model['W2']
    W3 = model['W3']
    b1=model['b1']
    b2 = model['b2']
    b3 = model['b3']

    num_examples=X.shape[0]
    plot_array_1=[]
    plot_array_2=[]
    for i in range(50000):
        #前向传播
        if NONLINEARITY=='RELU':
            hidden_layer=relu(np.dot(X,W1)+b1)
            hidden_layer2=relu(np.dot(hidden_layer,W2)+b2)
            scores=np.dot(hidden_layer2,W3)+b3
        elif NONLINEARITY == 'SIGM':
            hidden_layer = sigmoid(np.dot(X, W1) + b1)
            hidden_layer2 = sigmoid(np.dot(hidden_layer, W2) + b2)
            scores = np.dot(hidden_layer2, W3) + b3
        exp_scores=np.exp(scores)
        probs=exp_scores/np.sum(exp_scores,axis=1,keepdims=True)#softmax概率化得分[N*K]

        #计算损失
        correct_logprobs=-np.log(probs[range(num_examples),y])#整体的交叉熵损失
        data_loss=np.sum(correct_logprobs)/num_examples #平均交叉熵损失
        reg_loss=0.5*reg*np.sum(W1*W1)+0.5*reg*np.sum(W2*W2)+0.5*reg*np.sum(W3*W3)#正则化损失,W1*W1表示哈达玛积
        loss=data_loss+reg_loss
        if i%1000==0:
            print("iteration %d: loss %f"%(i,loss))

        #计算scores的梯度
        dscores=probs
        dscores[range(num_examples),y]-=1 #此处为交叉熵损失函数的求导结果,详细过程请看:https://blog.csdn.net/qian99/article/details/78046329
        dscores/=num_examples

        #反向传播过程
        dW3=(hidden_layer2.T).dot(dscores)
        db3=np.sum(dscores,axis=0,keepdims=True)


        if NONLINEARITY == 'RELU':
            # 采用RELU激活函数的反向传播过程
            dhidden2 = np.dot(dscores, W3.T)
            dhidden2[hidden_layer2 <= 0] = 0
            dW2 = np.dot(hidden_layer.T, dhidden2)
            plot_array_2.append(np.sum(np.abs(dW2)) / np.sum(np.abs(dW2.shape)))
            db2 = np.sum(dhidden2, axis=0)
            dhidden = np.dot(dhidden2, W2.T)
            dhidden[hidden_layer <= 0] = 0

        elif NONLINEARITY == 'SIGM':
            # 采用SIGM激活函数的反向传播过程
            dhidden2 = dscores.dot(W3.T) * sigmoid_grad(hidden_layer2)
            dW2 = (hidden_layer.T).dot(dhidden2)
            plot_array_2.append(np.sum(np.abs(dW2)) / np.sum(np.abs(dW2.shape)))
            db2 = np.sum(dhidden2, axis=0)
            dhidden = dhidden2.dot(W2.T) * sigmoid_grad(hidden_layer)

        dW1 = np.dot(X.T, dhidden)
        plot_array_1.append(np.sum(np.abs(dW1)) / np.sum(np.abs(dW1.shape)))#第一层的平均梯度记录下来
        db1 = np.sum(dhidden, axis=0)

        # 加入正则化得到的梯度
        dW3 += reg * W3
        dW2 += reg * W2
        dW1 += reg * W1

        # 记录梯度
        grads = {}
        grads['W1'] = dW1
        grads['W2'] = dW2
        grads['W3'] = dW3
        grads['b1'] = db1
        grads['b2'] = db2
        grads['b3'] = db3

        # 更新梯度
        W1 += -step_size * dW1
        b1 += -step_size * db1
        W2 += -step_size * dW2
        b2 += -step_size * db2
        W3 += -step_size * dW3
        b3 += -step_size * db3
    # 评估模型的准确度
    if NONLINEARITY == 'RELU':
        hidden_layer = relu(np.dot(X, W1) + b1)
        hidden_layer2 = relu(np.dot(hidden_layer, W2) + b2)
    elif NONLINEARITY == 'SIGM':
        hidden_layer = sigmoid(np.dot(X, W1) + b1)
        hidden_layer2 = sigmoid(np.dot(hidden_layer, W2) + b2)
    scores = np.dot(hidden_layer2, W3) + b3
    predicted_class = np.argmax(scores, axis=1)
    print('training accuracy: %.2f' % (np.mean(predicted_class == y)))
    # 返回梯度和参数
    return plot_array_1, plot_array_2, W1, W2, W3, b1, b2, b3
if __name__=='__main__':
    plt.rcParams['figure.figsize']=(10.0,8.0)#设置画布的默认大小
    plt.rcParams['image.interpolation']='nearest'#设置插值的风格
    plt.rcParams['image.cmap']='gray'#设置画布的颜色

    np.random.seed(0)#保证后面seed(0)生成的随机数相同
    N=100 #每一个类别的数量
    D=2 #维度
    K=3 #类别数
    h=50
    h2=50
    X=np.zeros((N*K,D))
    num_train_examples=X.shape[0]
    y=np.zeros(N*K,dtype='uint8')
    for j in range(K):
        ix=range(N*j,N*(j+1))
        r=np.linspace(0.0,1,N) # 半径
        t=np.linspace(j*4,(j+1)*4,N)+np.random.randn(N)*0.2 # theta
        X[ix]=np.c_[r*np.sin(t),r*np.cos(t)]# 按行拼接两个矩阵
        y[ix]=j
    fig=plt.figure()
    plt.scatter(X[:,0],X[:,1],c=y,s=40,cmap=plt.cm.Spectral)#画点,c代表颜色,s代表点的大小
    plt.xlim([-1,1])#设置横坐标的范围大小
    plt.ylim([-1,1])#设置纵坐标的范围大小
    plt.show()

    #初始化模型参数
    h = 50
    h2 = 50
    model={}
    model['h'] = h # hidden layer 1 大小
    model['h2']= h2# hidden layer 2 大小
    model['W1']= 0.1 * np.random.randn(D,h)
    model['b1'] = np.zeros((1,h))
    model['W2'] = 0.1 * np.random.randn(h,h2)
    model['b2']= np.zeros((1,h2))
    model['W3'] = 0.1 * np.random.randn(h2,K)
    model['b3'] = np.zeros((1,K))
    Activation_Function='RELU'#选择激活函数  SIGM/RELU
    (plot_array_1, plot_array_2, W1, W2, W3, b1, b2, b3) = three_layer_net(Activation_Function, X, y, model,step_size=1e-1, reg=1e-3)

    #模型采用两种激活函数时,分别画出模型梯度的变化趋势

    plt.plot(np.array(plot_array_1))
    plt.plot(np.array(plot_array_2))
    plt.title('Sum of magnitudes of gradients -- hidden layer neurons')
    plt.legend((Activation_Function+" first layer", Activation_Function+" second layer"))
    plt.show()
           

上述程序中先制造训练数据,如下图:

Stanford CS224n 第八讲:RNN和语言模式

然后分别采用sigmoid和RELU函数当做激活函数来训练模型,并记录模型中各层梯度的变化,效果如下:

Stanford CS224n 第八讲:RNN和语言模式

由上图我们可以看出(PS:上图中的第一层和第二层,是相对于神经网络正向传播时距离起点输入的层数),反向传播的过程中,距离反向传播的起点越远,梯度的变化值越小,如果神经网络的层数很多,最终可能梯度就会消失,导致前面层数的参数不更新,这就是梯度消失现象。

  • 处理梯度爆炸技巧

    处理梯度爆炸通常采用clip gradient算法,其伪代码如下:

    Stanford CS224n 第八讲:RNN和语言模式

    这个方法是Thomas Mikolov首先提出的,大体思想就是设置一个阈值,当梯度大于这个阈值的时候就截断,这个方法听上去很无脑,但是实际效果还不错。

    但是这种方法没法儿应用到梯度消失,因为当梯度很小的时候,说明某一些维度的特征权重要不变了,如果这时候设置一个最小值的阈值,则会让这些维度的特征权重继续变化,因此得证:clip gradient不能应用到梯度消失。

  • 处理梯度消失技巧

处理梯度消失的方法大体就是

(1) 对W不采用随机初始化,初始化为单位矩阵;这样初始效果就是上下文向量和词向量具有相同权重,减少随机性。

(2)激活函数采用Relu函数,从Relu和Sigmoid函数的图像我们可以知道,sigmoid的梯度在饱和区域非常平缓,接近于0,很容易造成梯度消失的问题;而Relu的梯度大多数情况下是常数。

Stanford CS224n 第八讲:RNN和语言模式

5. RNN应用于其他序列任务

有了RNN,我们可以用它来做哪些任务呢?课程中讲到,有NER(命名实体识别)、上下文中实体层次情感分析、观点表达分类等 。

文中以RNN进行意见挖掘为例:

Stanford CS224n 第八讲:RNN和语言模式

其中,意见挖掘任务就是将每个词语归类为:

DSE:直接主观描述(明确表达观点等)

ESE:间接主观描述(间接地表达情感等)

具体的标注方法BIO标注,B表示实体的开始,I表示实体的延续,O表示other。

我们可以用简单的RNN来完成上面任务:

Stanford CS224n 第八讲:RNN和语言模式

其中x代表输入tokens;y代表标签BIO。

如果过我们考虑到RNN的单向性,而打标签的过程可能依赖上下文,那么我们可以采用双向RNN:

Stanford CS224n 第八讲:RNN和语言模式

当然为了实验效果,也可以加深网络:

Stanford CS224n 第八讲:RNN和语言模式

最后的实验结果(F1值)表明,神经网络不是层数越大越好:

Stanford CS224n 第八讲:RNN和语言模式

好了,这节课就写到这儿吧。这次是我公式写的最多的一次,继续努力。。。

继续阅读