zoukankan      html  css  js  c++  java
  • 递归神经网络(Recursive Neural Network, RNN)

    信息往往还存在着诸如树结构、图结构等更复杂的结构。这就需要用到递归神经网络 (Recursive Neural Network, RNN),巧合的是递归神经网络的缩写和循环神经网络一样,也是RNN,递归神经网络可以处理树、图这样的递归结构

    递归神经网络

    神经网络的输入层单元个数是固定的,因此必须用循环或递归的方式来处理长度可变的输入。循环神经网络实现通过长度不定的输入分割为等长度的小块,然后再依次的输入到网络中,从而实现了神经网络对变长输入的处理。一个典型的例子是,当我们处理一句话的时候,我们可以把一句话看作是词组成的序列,然后,每次向循环神经网络输入一个词,如此循环直至整句话输入完毕,循环神经网络将产生对应的输出。如此,我们就能处理任意长度的句子了。如下图所示:

    然而有时候把句子看作是词的序列是不够的,比如下面这句话『两个外语学院的学生』:

    上面两个图分别表示了两个不听的语法解析树。

    第一个:『两个外语学院的/学生』,也就是学生可能有许多,但他们来自于两所外语学校;

    第二个:『两个/外语学院的学生』,也就是只有两个学生,他们是外语学院的。

      我们的模型必须能够按照树结构去处理信息,而不是序列,这就是递归神经网络的作用。递归神经网络可以把一个树/图结构信息编码为一个向量,也就是把信息映射到语义向量空间中。这个语义向量空间满足某类性质,比如语义相似的向量更近。也就是(尽管内容不同)它的意思是相似的,那么把他们分别编码后的两个向量的距离也相近;反之如果两句话的意思截然不同,那么编码后向量的距离则很远。

    从上图我们可以看出,递归神经网络将所有的词、句都映射到一个2维向量空间中。句子『the country of my birth』和句子『the place where I was born』的意思是非常接近的,所以表示它们的两个向量在向量空间中的距离很近。另外两个词『Germany』和『France』因为表示的都是地点,它们的向量与上面两句话的向量的距离,就比另外两个表示时间的词『Monday』和『Tuesday』的向量的距离近得多。这样,通过向量的距离,就得到了一种语义的表示

    上图还显示了自然语言可组合的性质:词可以组成句、句可以组成段落、段落可以组成篇章,而更高层的语义取决于底层的语义以及它们的组合方式。递归神经网络是一种表示学习,它可以将词、句、段、篇按照他们的语义映射到同一个向量空间中,也就是把可组合(树/图结构)的信息表示为一个个有意义的向量。比如上面这个例子,递归神经网络把句子"the country of my birth"表示为二维向量[1,5]。有了这个『编码器』之后,我们就可以以这些有意义的向量为基础去完成更高级的任务(比如情感分析等)。如下图所示,递归神经网络在做情感分析时,可以比较好的处理否定句,这是胜过其他一些模型的:

    在上图中,蓝色表示正面评价,白色是中性评价,红色表示负面评价。每个节点是一个向量,这个向量表达了以它为根的子树的情感评价。比如"intelligent humor"是正面评价,而"care about cleverness wit or any other kind of intelligent humor"是中性评价。我们可以看到,模型能够正确的处理doesn't的含义,将正面评价转变为负面评价。

    尽管递归神经网络具有更为强大的表示能力,但是在实际应用中并不太流行。其中一个主要原因是,递归神经网络的输入是树/图结构,而这种结构需要花费很多人工去标注。想象一下,如果我们用循环神经网络处理句子,那么我们可以直接把句子作为输入。然而,如果我们用递归神经网络处理句子,我们就必须把每个句子标注为语法解析树的形式,这无疑要花费非常大的精力。很多时候,相对于递归神经网络能够带来的性能提升,这个投入是不太划算的。

    递归神经网络的前向计算

    递归神经网络主要处理 树/图 结构的信息。我们以“树”来进行介绍递归神经网络。

    递归神经网络的输入是两个子节点(也可以是多个),输出就是将这两个子节点编码后产生的父节点,父节点的维度和每个子节点是相同的,如下图所示:

    $c_1$和$c_2$分别是表示两个子节点的向量,P是表示父节点的向量。子节点和父节点组成一个全连接神经网络,也就是子节点的每个神经元都和父节点的每个神经元两两相连。我们用矩阵$W$表示这些连接上的权重,它的维度将是$d x 2d$,其中$d$表示每个节点的维度。父节点的计算公式可以写成:

    $$mathbf{p} = tanh(Wegin{bmatrix}mathbf{c}_1\mathbf{c}_2end{bmatrix}+mathbf{b})qquad(式1)$$

    在上式中,tanh是激活函数,$b$是激活项,它也是一个维度为$d$的向量。

    然后我们再把产生父节点的向量和其他子节点的向量再次作为网络的输入,再次产生他们的父节点。如此递归下去,直至整棵树处理完毕。最终,我们将得到根节点的向量,我们可以认为它是对整棵树的表示,这样我们就实现了把树映射为一个向量。在下图中我们使用递归神经网络处理一棵树,最终得到的向量$P_3$,就是对整棵树的表示:

    举个例子,我们使用递归神经网络将『两个外语学校的学生』映射为一个向量,如下图所示:

    最后得到的向量$P_3$就是对整个句子『两个外语学校的学生』的表示。事实上,树中的每个根节点都是以其子节点为表示的,比如:在左边的这棵树中,向量$P_2$是短语『外语学院的学生』的表示,而向量$P_1$是短语『外语学院的』的表示。

    式1就是递归神经网络的前向计算算法,它和全连接神经网络的计算没什么区别,只是在输入的过程中需要根据输入的树结构一次输入每个子节点。

    需要特别注意的是,递归神经网络的权重$W$和偏置项$b$在所有节点都是共享的。

    递归神经网络的训练

    递归神经网络的训练算法和循环神经网络类似,两者不同之处在于,前者需要将残差$delta$从根节点反向传播到各个子节点,而后者是将残差$delta$从当前时刻$t_k$反向传播到初始时刻$t_1$。下面我们介绍适用于递归神经网络的训练算法,也就是BPTS算法

    误差项的传递

    首先,我们先推导将误差从父节点传递到子节点的公式,如下图:

    定义$delta_p$为误差函数E相对于父节点$p$的加权输入$net_p$的导数,即:

    $$delta_poverset{def}{=}frac{partial{E}}{partial{mathbf{net}_p}}$$

    $net_p$是父节点的加权输入,则

    $$mathbf{net}_p=Wegin{bmatrix}mathbf{c}_1\mathbf{c}_2end{bmatrix}+mathbf{b}$$

    在上述式子里,$net_p$、$c_1$、$c_2$都是向量,而$W$是矩阵。我们展开:

    $$egin{align}
    egin{bmatrix}
    net_{p_1}\
    net_{p_2}\
    ...\
    net_{p_n}
    end{bmatrix}&=
    egin{bmatrix}
    w_{p_1c_{11}}&w_{p_1c_{12}}&...&w_{p_1c_{1n}}&w_{p_1c_{21}}&w_{p_1c_{22}}&...&w_{p_1c_{2n}}\
    w_{p_2c_{11}}&w_{p_2c_{12}}&...&w_{p_2c_{1n}}&w_{p_2c_{21}}&w_{p_2c_{22}}&...&w_{p_2c_{2n}}\
    ...\
    w_{p_nc_{11}}&w_{p_nc_{12}}&...&w_{p_nc_{1n}}&w_{p_nc_{21}}&w_{p_nc_{22}}&...&w_{p_nc_{2n}}\
    end{bmatrix}
    egin{bmatrix}
    c_{11}\
    c_{12}\
    ...\
    c_{1n}\
    c_{21}\
    c_{22}\
    ...\
    c_{2n}
    end{bmatrix}+egin{bmatrix}\
    b_1\
    b_2\
    ...\
    b_n\
    end{bmatrix}
    end{align}$$

    在上面的公式中,$p_i$表示父节点p的第i个分量;$c_{1i}$表示$c_1$子节点的第i个分量;$c_{2i}$表示$c_2$子节点的第i个分量;$w_{p_ic_{jk}}$表示子节点$c_j$的第k个分量到父节点p的第i个分量的权重。根据上面展开后的矩阵乘法形式,我们不难看出,对于子节点$c_{jk}$来说,它会影响父节点所有的分量。因此,我们求误差函数E对$c_{jk}$的导数时,必须用到全导数公式,也就是:

    $$egin{align}
    frac{partial{E}}{partial{c_{jk}}}&=sum_i{frac{partial{E}}{partial{net_{p_i}}}}frac{partial{net_{p_i}}}{partial{c_{jk}}}\
    &=sum_i{delta_{p_i}}w_{p_ic_{jk}}
    end{align}$$

    有了上式,我们就可以把它表示为矩阵形式,从而得到一个向量化表达:

    $$egin{align}
    frac{partial{E}}{partial{mathbf{c}_j}}&=U_jdelta_p
    end{align}$$

    其中,矩阵$U_j$是从矩阵W中提取部分元素组成的矩阵。其单元为:

    $$u_{j_{ik}}=w_{p_kc_{ji}}$$

    上式看上去可能会让人晕菜,从下图,我们可以直观的看到$U_j$到底是啥。首先我们把W矩阵拆分为两个矩阵$W_1$和$W_2$,如下图所示:

     

  • 相关阅读:
    云计算架构
    Java多线程中static变量的使用
    Spring单例与线程安全小结
    sparkSQL实战详解
    sparkSQL整体实现框架
    spark架构
    如何快速定位出一个IP地址的归属地?——二分查找变体
    如何在 1000 万个整数中快速查找某个整数?——二分查找
    语言库中常用的排序算法qsort()底层结构
    链表常考笔试面试题(常备)
  • 原文地址:https://www.cnblogs.com/LXP-Never/p/10403048.html
Copyright © 2011-2022 走看看