Unsupervised Latent Tree Induction with Deep Inside-Outside Recursive Autoencoders阅读笔记

标题

《Unsupervised Latent Tree Induction with Deep Inside-Outside Recursive Autoencoders》
论文链接
代码链接

背景

语法解析树(短语结构树?)(Syntactic parse trees)对于关系抽取、语义角色标注、机器翻译、文本分类都很有用,因为"最有效的句子表示将来自于遵循真正的句法结构。"。但传统模型都需要标注数据。标注数据的规模局限,而且只适合特定领域。与此同时,无监督的BERT却很成功。

模型结构

在这里插入图片描述

明确输入

假定我们的输入句子为[“the”,“cat”,“drank”],我们先把它们输入到一个embedding层获得各自的单词词向量。

代码:
embed = self.embed(batch)
论文:
在这里插入图片描述

然后我们对[ v t h e v_{the} vthe, v c a t v_{cat} vcat, v d r a n k v_{drank} vdrank] (x)进行一些非线性变换,但维度不变。
可以看到,下面的代码返回两个东西,一个是h,一个是c,我们把c忽略,因为它是treelstm的内容,在我看的代码中c都是0。而h与x大小一样,是我们要用到的东西。

代码:
h = torch.tanh(torch.matmul(x, self.V) + self.B)
device = torch.cuda.current_device() if self.is_cuda else None
c = torch.full(h.shape, 0, dtype=torch.float32, device=device)
return h, c
论文:
在这里插入图片描述

接着是理解算法的关键,那便是初始化inside_h。什么是inside_h?考虑输入的句子是[“the”,“cat”,“drank”],那么它组成的二叉树(如上面的图),就一共有6(3+2+1)个节点,而inside_h就是6xhidden_dim大小的一个矩阵。初始化它,就是让它的前length(这里为3)行的向量赋值为上面的h。接下来开始inside pass

代码:
self.chart = Chart(batch_size, length, size, dtype=torch.float32, cuda=self.is_cuda)
self.chart.inside_h[:, :self.length] = h

inside pass

我们记Length=句子长度=3,Level=树的高度=0\1\2(第一层高0,第二层高1,第三层高2), 通过上面的初始化,我们已经填好了第一层的三个向量,接着我们依次填第二层和第三层:

代码:
for level in range(1, self.length):
(\t) h, c, s = inside_func(compose_func, score_func, batch_info, chart, index,normalize_func=normalize_func)

当level=1的时候,h的大小为(2,hidden_dim),s的大小为(2,)
当level=2的时候,h的大小为(1,hidden_dim),s的大小为(1,)
首先明确h是啥、s是啥

h是啥,s又是啥

h是接下去解析树的结点的向量表示,s是接下去解析树的结点的组合分数。哦对,还没有说向量表示是怎么计算的,组合分数又是什么呢!
下面通过计算“The”,“Cat"的父亲结点的向量表示和组合分数来讲解。
首先,我们要初始化"The”,"Cat"这两个叶子结点的分数为0(这两个叶子结点的向量表示我们之前已经初始化过了)。
在这里插入图片描述
我们明确现在有的四个东西:"The"的向量表示(记作a(i)),"Cat"的向量表示(记作a(j)),"The"的分数(记作e(i)),“Cat"的分数(记作e(j))。然后我们先通过一个双线性函数,将"The”,”cat“这两个叶子结点的向量表示分数去计算一个组合分数:

代码:
bma = torch.matmul(vector1, self.mat).unsqueeze(1) # b 1 n
ba = torch.matmul(bma, vector2.unsqueeze(2)).view(-1, 1) # b n 1
s = ba + ss[0] + ss[1]
论文:
在这里插入图片描述
在这里插入图片描述

然后我们用"The","Cat"这两个叶子结点的向量表示,通过两层的线性层将它们组合,得到一个组合的向量表示(注意:原文中说,下面的Compose函数也可以替换成Tree-LSTM,关于Tree-LSTM那篇论文,我也做了点笔记。应该使用的是N-ary Tree-LSTMs):

代码:
input_h = torch.cat(hs, 1) # hs = [vector1, vector2]
h = torch.relu(torch.matmul(input_h, self.W_0) + self.B)
h = torch.relu(torch.matmul(h, self.W_1) + self.B_1)
device = torch.cuda.current_device() if self.is_cuda else None
c = torch.full(h.shape, 0, dtype=torch.float32, device=device)
return h, c
论文:在这里插入图片描述

现在是不是父亲结点的组合分数和向量表示都有了!同样的,"cat"和"drank"也可以这样计算,去得到它们的父亲结点的组合分数和向量表示。所以上面level=1的时候,h的大小是(2,hidden_dim),因为level=1的这一层,有两个父亲结点。那么我们再来考虑level=2

理解inside pass的关键

首先我们记

  • “The”,“Cat”,"Drink"这三个叶子结点分别为n1,n2,n3
  • n1和n2的父亲结点为n4
  • n2和n3的父亲结点为n5

我们上面讨论的是n4和n5的向量表示和分数是怎么来的,现在我们计算level=2的时候,也就是root,也就是n4和n5的父亲结点(记n6)的向量表示和分数!
聪明的你是不是认为n6的向量表示和组合分数就是用n4和n5通过与上面一样的计算来得到,其实并不是这样。其实是:

  1. 我们先计算n1和n5的组合向量表示和组合分数
  2. 我们再计算n4和n3的组合向量表示和组合分数
  3. 我们将上面两个组合分数进行归一化,得到一组权重
  4. 使用这组权重对两个组合向量表示进行加权求和,得到最终的n6的组合向量表示
  5. 使用这组权重对两个组合分数进行加权求和,得到最终的n6的组合分数

代码
h_agg = torch.sum(h.view(B, L, N, -1) * p, 2)
c_agg = torch.sum(c.view(B, L, N, -1) * p, 2)
s_agg = torch.sum(s * p, 2)
论文:
在这里插入图片描述
在这里插入图片描述

可以看到,当输入的length=3,level=2的时候,有 length-level=1个父亲结点,每个父亲结点需要考虑level(2)组如上所述的(i,j)成分对;同样的,再上面level=1的时候,有length-level=2个父亲节点,每个父亲节点需要考虑level(1)组成分对。

做个练习彻底理解inside pass

如果我们输入的句子长度Length是5,那么我们inside_h的第0维应该是 5*6/2 = 15(5+4+3+2+1)。考虑level=3的时候,这一层有2(length-level)个父亲结点,我们考虑位于左边的父亲节点。左边这个父亲结点需要考虑3(level)组成分对。就比如输入句子是 [“i”,“love”,“china”,“very”,“much”],level=3这一层的左边的父亲结点需要考虑下面这三组成分对

  • (“i”, (“love”,“china”,“very”))
  • ((“i”,“love”),(“china”,“very”))
  • ((“i”,“love”,“china”),“very”)
    而就比如(“love”,“china”,“very”),我们早就在level=2的时候通过(“love”,(“china”,“very”))、((“love”,“china”),“very”)这两个成分对通过上面"理解inside pass的关键"部分的计算,得到了。所以inside pass本质上是一个动态规划、填表的过程。

outside pass

这部分,我就大致说一下:
outside pass的过程正好和inside pass反过来,我们先初始化root结点的向量表示,然后往下计算,最后得到每个叶子结点的向量表示。而diora训练的目标就是:让这里得到的向量表示去拟合一开始的预训练词向量。

论文:
在这里插入图片描述
在这里插入图片描述
按照上面公式,考虑length=5,level=2时的计算流程:
在这里插入图片描述
即:

  • 计算(2,2)这个论文中所说的"missing sibling cell"时,考虑它有(3,3)、(4,4)两个父亲节点,(3,3)时它的兄弟节点为(0,3),(4,4)时它的兄弟节点为(1,4)
  • (2,3)考虑((3,3),(0,0)) 和 ((3,4),(0,4))
  • (2,4)考虑((3,4),(0,1)) 和 ((4,4),(1,1))
    同样的,计算到叶子节点的时候:
  • (0,0)考虑(1,1)、(2,2)、(3,3)、(4,4)四个父亲节点,四个父亲节点下,兄弟节点分别是(0,1)、(1,2)、(2,3)、(3,4)
  • (0,1)考虑(1,1)、(1,2)、(2,3)、(3,4)四个父亲节点,四个父亲节点下,兄弟节点分别是(0,0)、(0,2)、(1,3)、(2,4)

得到了outside pass中的叶子节点预测向量,就可以计算损失函数。

损失函数

损失函数1:
在这里插入图片描述
i ∗ i^* i是根据词频随机抽取的N个负例样本中的一个样本的下标。我们希望经过outside pass产生的第i个单词的预测向量 b ( i ) b(i) b(i)接近于原来inside pass中该单词的向量表示 a i a_i ai。损失函数1有三种可能的情况:

  • b ( i ) ⋅ a ( i ) < b ( i ) ⋅ a ( i ∗ ) b(i)·a(i)<b(i)·a(i^*) b(i)a(i)b(i)a(i)时, m a x ( 0 , 1 − b ( i ) ⋅ a ( i ) + b ( i ) ⋅ a ( i ∗ ) ) max(0, 1-b(i)·a(i)+b(i)·a(i^*)) max(0,1b(i)a(i)+b(i)a(i)) = m a x ( 0 , 1 − ( b ( i ) ⋅ a ( i ) − b ( i ) ⋅ a ( i ∗ ) ) ) max(0, 1-(b(i)·a(i)-b(i)·a(i^*))) max(0,1(b(i)a(i)b(i)a(i)))>1,产生损失
  • b ( i ) ⋅ a ( i ) > b ( i ) ⋅ a ( i ∗ ) b(i)·a(i) > b(i)·a(i^*) b(i)a(i)>b(i)a(i)时,且 b ( i ) ⋅ a ( i ) − b ( i ) ⋅ a ( i ∗ ) < 1 b(i)·a(i) - b(i)·a(i^*) <1 b(i)a(i)b(i)a(i)<1 m a x ( 0 , 1 − b ( i ) ⋅ a ( i ) + b ( i ) ⋅ a ( i ∗ ) ) max(0, 1-b(i)·a(i)+b(i)·a(i^*)) max(0,1b(i)a(i)+b(i)a(i)) = m a x ( 0 , 1 − ( b ( i ) ⋅ a ( i ) − b ( i ) ⋅ a ( i ∗ ) ) ) max(0, 1-(b(i)·a(i)-b(i)·a(i^*))) max(0,1(b(i)a(i)b(i)a(i)))>0, 产生损失
  • 但, b ( i ) ⋅ a ( i ) > b ( i ) ⋅ a ( i ∗ ) b(i)·a(i) > b(i)·a(i^*) b(i)a(i)>b(i)a(i)时,且 b ( i ) ⋅ a ( i ) − b ( i ) ⋅ a ( i ∗ ) > = 1 b(i)·a(i) - b(i)·a(i^*) >=1 b(i)a(i)b(i)a(i)>=1 m a x ( 0 , 1 − b ( i ) ⋅ a ( i ) + b ( i ) ⋅ a ( i ∗ ) ) max(0, 1-b(i)·a(i)+b(i)·a(i^*)) max(0,1b(i)a(i)+b(i)a(i)) = m a x ( 0 , 1 − ( b ( i ) ⋅ a ( i ) − b ( i ) ⋅ a ( i ∗ ) ) ) max(0, 1-(b(i)·a(i)-b(i)·a(i^*))) max(0,1(b(i)a(i)b(i)a(i)))<0, 就不产生损失了。

所以引出损失函数2:
在这里插入图片描述
上式中,分子肯定比分母小,所以T个单词都会产生一定的损失。

Parsing

DIORA不仅可以用上面的类似于Bert的训练,来得到span的向量表示。训练完以后,我们可以借助CKY算法,从下到上来进行parsing。关于CKY的过程,可以看我的inside-outside algorithm详解。CKY的输出是一棵确定的树。

下游任务

unsupervised parsing

segment recall

phrase representations

结论

通过上面的inside pass和outside pass,我们得到了什么?——首先inside pass使得一整棵解析树(二叉树)的每一个结点都拥有一个向量表示和一个分数

  • 这个向量表示被称作span representation,它吸收了句子内部的结构,能够用作down stream
  • 这个分数可以通过CKY解码,来得到该句子上面的一棵好的解析树

其次,outside pass也给解析树的每一个结点一个向量表示

  • 这个向量表示更多地吸收了句子上下文关系,但我目前认为,由于训练目标是去拟合预训练词向量,所以outside pass部分得到的叶子结点向量表示并没有什么用,但它的level>=1的部分的span representation应该是有用的
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值