零基础入门深度学习(7) - 递归神经网络

零基础入门深度学习(7) - 递归神经网络

机器学习 深度学习入门


 

无论即将到来的是大数据时代还是人工智能时代,亦或是传统行业使用人工智能在云上处理大数据的时代,作为一个有理想有追求的程序员,不懂深度学习(Deep Learning)这个超热的技术,会不会感觉马上就out了?现在救命稻草来了,《零基础入门深度学习》系列文章旨在讲帮助爱编程的你从零基础达到入门级水平。零基础意味着你不需要太多的数学知识,只要会写程序就行了,没错,这是专门为程序员写的文章。虽然文中会有很多公式你也许看不懂,但同时也会有更多的代码,程序员的你一定能看懂的(我周围是一群狂热的Clean Code程序员,所以我写的代码也不会很差)。

文章列表

零基础入门深度学习(1) - 感知器
零基础入门深度学习(2) - 线性单元和梯度下降
零基础入门深度学习(3) - 神经网络和反向传播算法
零基础入门深度学习(4) - 卷积神经网络
零基础入门深度学习(5) - 循环神经网络
零基础入门深度学习(6) - 长短时记忆网络(LSTM)
零基础入门深度学习(7) - 递归神经网络

往期回顾

在前面的文章中,我们介绍了循环神经网络,它可以用来处理包含序列结构的信息。然而,除此之外,信息往往还存在着诸如树结构、图结构等更复杂的结构。对于这种复杂的结构,循环神经网络就无能为力了。本文介绍一种更为强大、复杂的神经网络:递归神经网络 (Recursive Neural Network, RNN),以及它的训练算法BPTS (Back Propagation Through Structure)。顾名思义,递归神经网络(巧合的是,它的缩写和循环神经网络一样,也是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的含义,将正面评价转变为负面评价。

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

我们已经基本了解了递归神经网络是做什么用的,接下来,我们将探讨它的算法细节。

递归神经网络的前向计算

接下来,我们详细介绍一下递归神经网络是如何处理树/图结构的信息的。在这里,我们以处理树型信息为例进行介绍。

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

以及最终的梯度。它们都采用递归算法,先序遍历整个树,并逐一完成每个节点的计算。

下面是梯度下降算法的实现(没有weight decay),这个非常简单:

  
  
  1.     def update(self):
  2.         '''
  3.         使用SGD算法更新权重
  4.         '''
  5.         self.W -= self.learning_rate * self.W_grad
  6.         self.b -= self.learning_rate * self.b_grad

以上就是递归神经网络的实现,总共100行左右,和上一篇文章的LSTM相比简单多了。

最后,我们用梯度检查来验证程序的正确性:

  
  
  1. def gradient_check():
  2.     '''
  3.     梯度检查
  4.     '''
  5.     # 设计一个误差函数,取所有节点输出项之和
  6.     error_function = lambda o: o.sum()
  7.     rnn = RecursiveLayer(2, 2, IdentityActivator(), 1e-3)
  8.     # 计算forward值
  9.     x, d = data_set()
  10.     rnn.forward(x[0], x[1])
  11.     rnn.forward(rnn.root, x[2])
  12.     # 求取sensitivity map
  13.     sensitivity_array = np.ones((rnn.node_width, 1),
  14.                                 dtype=np.float64)
  15.     # 计算梯度
  16.     rnn.backward(sensitivity_array)
  17.     # 检查梯度
  18.     epsilon = 10e-4
  19.     for i in range(rnn.W.shape[0]):
  20.         for j in range(rnn.W.shape[1]):
  21.             rnn.W[i,j] += epsilon
  22.             rnn.reset_state()
  23.             rnn.forward(x[0], x[1])
  24.             rnn.forward(rnn.root, x[2])
  25.             err1 = error_function(rnn.root.data)
  26.             rnn.W[i,j] -= 2*epsilon
  27.             rnn.reset_state()
  28.             rnn.forward(x[0], x[1])
  29.             rnn.forward(rnn.root, x[2])
  30.             err2 = error_function(rnn.root.data)
  31.             expect_grad = (err1 - err2) / (2 * epsilon)
  32.             rnn.W[i,j] += epsilon
  33.             print 'weights(%d,%d): expected - actural %.4e - %.4e' % (
  34.                 i, j, expect_grad, rnn.W_grad[i,j])
  35.     return rnn

下面是梯度检查的结果,完全正确,OH YEAH!

递归神经网络的应用

自然语言和自然场景解析

在自然语言处理任务中,如果我们能够实现一个解析器,将自然语言解析为语法树,那么毫无疑问,这将大大提升我们对自然语言的处理能力。解析器如下所示:

可以看出,递归神经网络能够完成句子的语法分析,并产生一个语法解析树。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值