知识表示学习Trans系列梳理(论文+代码)

2020 年 5 月 28 日 AINLP





知识表示学习Trans系列梳理


导语本文将简短梳理知识表示学习Trans系列方法,包含TransE、TransH、TransR、TransD、TransA、TransG、TranSparse以及KG2E,并且结合Github清华开源的高星代码了解一下实现过程,以便能通过代码看出他们之间的联系和区别。


1

    简谈TransE

Author:Bordes A, Usunier N, Garciaduran A, et al.

TitleTranslating Embeddings for Modeling Multi-relational Data

Accepted: Neural Information Processing Systems, NIPS2013.


    翻译模型从 TransE 开始,并衍生出一系列模型。


    首先来看看 TransE 的名字来源,它的论文标题是:Translating Embeddings for Modeling Multi-relational Data。从这个标题我们不难猜想出为什么简称为 TransE,以及为什么称之为翻译模型。除此之外,这篇论文也是始于2013年,发表在NIPS


     TransE 模型的基本思想就是把 relation 看做是 head 到 tail 的翻译,认为一个正确的知识三元组应该满足 h+ r =(约等于) t,而错误的不满足,通俗来讲就是头实体 embedding 加上关系 embedding 近似等于尾实体 embedding ,见下图,思想就是这么的简单但却高效。



    TransE 定义了一个距离函数d(h + r, t)来衡量 h + r 和 t 之间的距离,我们也可以使用 L1 或 L2 范数。TransE采用最大间隔的方法,其目标函数如下:



    其中,S是正确的三元组,S’是通过替换 h 或 t 所得错误的三元组。γ 是间隔距离超参数,[x]+表示正值函数,即 x > 0 时,[x]= x;当 x ≤ 0 时,[x]= 0 。


    我们来看一下OpenKE中的 TransE 代码部分:


    最开始是在初始化__init__中定义了两个Embedding矩阵,分别为实体Embedding和关系Embedding,并且对其初始化。

self.ent_embeddings = nn.Embedding(self.ent_tot, self.dim)self.rel_embeddings = nn.Embedding(self.rel_tot, self.dim)对其采用xavier_uniform_初始化或者uniform_初始化的方式


接下来我们看下forward中的实现。

  1. forward中主要是获取头尾实体以及关系的Embedding,其中头尾实体都是共用一个实体Embedding矩阵

  2. 最主要的就是 _calc 这部分,其实也很简单,核心就是计算score,并且做个范数的约束

   def _calc(self, h, t, r, mode):    if self.norm_flag:      h = F.normalize(h, 2, -1)      r = F.normalize(r, 2, -1)      t = F.normalize(t, 2, -1)    if mode != 'normal':      h = h.view(-1, r.shape[0], h.shape[-1])      t = t.view(-1, r.shape[0], t.shape[-1])      r = r.view(-1, r.shape[0], r.shape[-1])    if mode == 'head_batch':      score = h + (r - t)    else:      score = (h + r) - t    score = torch.norm(score, self.p_norm, -1).flatten()    return score
def forward(self, data): batch_h = data['batch_h'] batch_t = data['batch_t'] batch_r = data['batch_r'] mode = data['mode'] h = self.ent_embeddings(batch_h) t = self.ent_embeddings(batch_t) r = self.rel_embeddings(batch_r) score = self._calc(h, t, r, mode) if self.margin_flag: return self.margin - score else: return score


在计算Loss部分,采用的是MarginLoss,margin参数值设置为5.0

# define the loss functionmodel = NegativeSampling(  model = transe,   loss = MarginLoss(margin = 5.0),  batch_size = train_dataloader.get_batch_size())


以上便是 TransE 的简短介绍和代码简析,我们接下来再看一下 TransH。


2

    简谈TransH

Title: Knowledge graph embedding by translating on hyperplanes

Author:Wang Z, Zhang J, Feng J, et al.

Accepted:National conference on artificial intelligence, AAAI 2014.


    TransE 模型由于它简单高效所以取得了很大的突破,但是优点有时候也往往伴随着缺点,TransE 的对简单关系的建模效果显著,但是对复杂关系的建模效果却十分不理想,如1-N,N-1,N-N这样的复杂关系,所以才提出了后续的很多模型变种,TransH 便是其中一个。


    为了更好的理解上面所述的复杂关系,我们在此说明按照知识库中关系两端连接实体的数量,可以将关系类型划分为 1-1、 1-N、N-1 和 N-N 四种。以 N-1 类型关系为例,指的是该类型关系中的一个尾实体会平均对应多个头实体,其他的类推。


    接下来我们看一下为什么 TransE 对复杂关系建模效果不好?以及 TransH 的建模思想



    上面说 TransE 对复杂关系建模不好,假定我们存在多个关系三元组,其中关系和尾实体都是固定不变的,而头实体对应多个,这就是N-1的关系,那么根据 TransE 的思想(上图a)建模,多个头实体将会得到非常相近的向量表示,可能近乎为统一含义,这对于不同业务下的不同场景是不合理的


    TransH 的动机就是解决这种复杂关系,那么它具体是怎么解决的呢?


    TransH 的核心思想是对于关系每一个 r,有一个超平面