SimplE:SimplE Embedding for Link Prediction in Knowledge Graphs+代码

【论文笔记】SimplE Embedding for Link Prediction in Knowledge Graphs 摘要 张量因子分解方法已被证明对此类链路预测问题很有前途。1927年提出的正则多元分解(CP)是最早的张量分解方法之一。CP通常在链路预测方面表现不佳,因为它为每个实体学习两个独立的嵌入向量,而它们实际上是并列的。我们提出了一个简单的CP增强(我们称之为simple),允许独立地学习每个实体的两个嵌入。 背景 一种张量因子分解方法是正则多变量(CP)分解。该方法为每个关系学习一个嵌入向量,为每个实体学习两个嵌入向量,当实体为头部时使用一个嵌入向量,当实体为尾部时使用另一个嵌入向量。实体的头部嵌入是独立于尾部 阅读详情


本文主要对知识图谱补全论文进行讲解,对其原理、思路、代码等方面进行详细讲解。

1 介绍

1.1 知识图谱

现实世界中可以通过三元组,对现实世界进行描述,三元组描述的形式为:实体-关系-实体或实体-关系-属性,通常可以用符号表示(h, r, t)其中h表示头实体,r表示实体和实体之间的关系,t表示尾实体。以该方式存储现实世界的各种关系以及属性,由于现实世界是非常的庞大的,各种关系容易缺失,因此涉及知识图谱补全,本文SimplE就是一篇针对知识图谱补全的论文。

1.2 知识图谱补全方法

基础的知识图谱补全涉及以下几种方法

  • 双线性模型(disMult)
  • 神经网络模型 (ConvKB)
  • 转换模型(Trans系列)
  • 图卷积模型(GCN)

1.3知识图谱补全(Knowledge Graph Completion,KGC)

目前主要被抽象成一个预测问题,即预测出三元组中缺失的部分。所以可分成3个子任务:

  • 头实体预测:(?, r, t)
  • 关系预测:(h, ?, t)
  • 尾实体预测:(h, r, ?)

1.4 关系分类

E \mathcal{E} E and R \mathcal{R} R 分别表示实体和关系,一个三元组用 ( h , r , t ) (h, r, t) (h,r,t)表示, 其中 h ∈ E h \in \mathcal{E} h∈E 是头实体, r ∈ R r \in \mathcal{R} r∈R 是关系, and t ∈ E t \in \mathcal{E} t∈E 是尾实体。 ζ \zeta ζ 表示 一个真实的三元组(e.g., (paris, capitalOf, france)), 而 ζ ′ \zeta^{\prime} ζ′ 表示错误的三元组 (e.g., ( paris, capitalOf, italy)). 知识图谱 K G \mathcal{K} \mathcal{G} KG 是 ζ 的 子 集 . \zeta的子集 . ζ的子集. A relation

  • 对称(symmetric): ( e 1 , r , e 2 ) ∈ ζ ⟺ ( e 2 , r , e 1 ) ∈ ζ \left(e_{1}, r, e_{2}\right) \in \zeta \Longleftrightarrow\left(e_{2}, r, e_{1}\right) \in \zeta (e1​,r,e2​)∈ζ⟺(e2​,r,e1​)∈ζ,其中 e 1 , e 2 ∈ E e_{1}, e_{2} \in \mathcal{E} e1​,e2​∈E
  • 反身 ( reflexive): ( e , r , e ) ∈ ζ (e, r, e) \in \zeta (e,r,e)∈ζ for all entities e ∈ E e \in \mathcal{E} e∈E
  • 反对称(anti-symmetric): ( e 1 , r , e 2 ) ∈ ζ ⟺ ( e 2 , r , e 1 ) ∈ ζ ′ \left(e_{1}, r, e_{2}\right) \in \zeta \Longleftrightarrow\left(e_{2}, r, e_{1}\right) \in \zeta^{\prime} (e1​,r,e2​)∈ζ⟺(e2​,r,e1​)∈ζ′
  • 传递(transitive): ( e 1 , r , e 2 ) ∈ ζ ∧ ( e 2 , r , e 3 ) ∈ ζ ⇒ ( e 1 , r , e 3 ) ∈ ζ \left(e_{1}, r, e_{2}\right) \in \zeta \wedge\left(e_{2}, r, e_{3}\right) \in \zeta \Rightarrow\left(e_{1}, r, e_{3}\right) \in \zeta (e1​,r,e2​)∈ζ∧(e2​,r,e3​)∈ζ⇒(e1​,r,e3​)∈ζ ,其中 e 1 , e 2 , e 3 ∈ E e_{1}, e_{2}, e_{3} \in \mathcal{E} e1​,e2​,e3​∈E
  • 逆关系(inverse): ( e i , r , e j ) ∈ ζ ⟺ ( e j , r − 1 , e i ) ∈ ζ \left(e_{i}, r, e_{j}\right) \in \zeta \Longleftrightarrow\left(e_{j}, r^{-1}, e_{i}\right) \in \zeta (ei​,r,ej​)∈ζ⟺(ej​,r−1,ei​)∈ζ

2 模型

2.1 双线性模型

所谓的双线性模型即实体关系之间采用乘的方式,其乘的方式为 ⟨ v , w , x ⟩ ≐ ∑ j = 1 d v [ j ] ∗ w [ j ] ∗ x [ j ] \langle v, w, x\rangle \doteq \sum_{j=1}^{d} v[j] * w[j] * x[j] ⟨v,w,x⟩≐∑j=1d​v[j]∗w[j]∗x[j],Hadamard乘积,每个元素元素之间进行相乘,然后累加,其中论文disMult也采用同样的方式。

2.2 核心公式

两个向量 h e , t e ∈ R d h_{e}, t_{e} \in \mathbb{R}^{d} he​,te​∈Rd作为实体 e e e的嵌入,向量 v r , v r − 1 ∈ R a v_{r}, v_{r}^{-1} \in \mathbb{R}^{a} vr​,vr−1​∈Ra 作为关系 r r r的嵌入。
1 2 ( ⟨ h e i , v r , t e j ⟩ + ⟨ h e j , v r − 1 , t e i ⟩ ) \frac{1}{2}\left(\left\langle h_{e_{i}}, v_{r}, t_{e_{j}}\right\rangle+\left\langle h_{e_{j}}, v_{r^{-1}}, t_{e_{i}}\right\rangle\right) 21​(⟨hei​​,vr​,tej​​⟩+⟨hej​​,vr−1​,tei​​⟩)作为模型的核心,计算得分函数。

2.3 负采样

模型采用随机的方法,对头实体或者尾实体进行负采样,随机从[0,num_ent-1]中抽取一个不同于原始的数据, num_ent表示实体总数。正确的三元组标记label为1,错位的三元组即复杂采样的结果标记label为-1。

2.4 损失函数

min ⁡ θ ∑ ( ( h , r , t ) , l ) ∈ L B softplus ⁡ ( − l ⋅ ϕ ( h , r , t ) ) + λ ∥ θ ∥ 2 2 \min _{\theta} \sum_{((h, r, t), l) \in \mathbf{L B}} \operatorname{softplus}(-l \cdot \phi(h, r, t))+\lambda\|\theta\|_{2}^{2} minθ​∑((h,r,t),l)∈LB​softplus(−l⋅ϕ(h,r,t))+λ∥θ∥22​,其中 θ \theta θ 代表模型参数(embeddings 的参数), l l l表示标签范围为-1或者+1即正确三元组或错误三元组。 ϕ ( h , r , t ) \phi(h, r, t) ϕ(h,r,t)表示三元组 ( h , r , t ) (h, r, t) (h,r,t)的得分函数, softplus ⁡ ( x ) = log ⁡ ( 1 + exp ⁡ ( x ) ) \operatorname{softplus}(x)=\log (1+\exp (x)) softplus(x)=log(1+exp(x))

2.5 评价

知识图谱补全评价指标有hit@n, mrr, mr等方法,博客参考KGE性能指标:MRR,MR,HITS@1,HITS@3,HITS@10

  • MRR

    MRR的全称是Mean Reciprocal Ranking,其中Reciprocal是指“倒数的”的意思。具体的计算方法如下:
    M R R = 1 ∣ S ∣ ∑ i = 1 1 rank ⁡ i = 1 ∣ S ∣ ( 1 rank ⁡ 1 + 1 rank ⁡ 2 + … + 1 rank ⁡ ∣ S ∣ ) \mathrm{MRR}=\frac{1}{|S|} \sum_{i=1} \frac{1}{\operatorname{rank}_{i}}=\frac{1}{|S|}\left(\frac{1}{\operatorname{rank}_{1}}+\frac{1}{\operatorname{rank}_{2}}+\ldots+\frac{1}{\operatorname{rank}_{|S|}}\right) MRR=∣S∣1​∑i=1​ranki​1​=∣S∣1​(rank1​1​+rank2​1​+…+rank∣S∣​1​),其中S是三元组集合,|S|是三元组集合个数, r a n k i rank_{i} ranki​是指第 i i i个三元组的链接预测排名。该指标越大越好。

  • MR

    MR的全称是Mean Rank。具体的计算方法如下:
    M R = 1 ∣ S ∣ ∑ i = 1 ∣ S ∣ rank ⁡ i = 1 ∣ S ∣ ( rank ⁡ 1 + rank ⁡ 2 + … + rank ⁡ ∣ S ∣ ) \mathbf{M R}=\frac{1}{|S|} \sum_{i=1}^{|S|} \operatorname{rank}_{i}=\frac{1}{|S|}\left(\operatorname{rank}_{1}+\operatorname{rank}_{2}+\ldots+\operatorname{rank}_{|S|}\right) MR=∣S∣1​∑i=1∣S∣​ranki​=∣S∣1​(rank1​+rank2​+…+rank∣S∣​)
    上述公式涉及的符号和MRR计算公式中涉及的符号一样。该指标越小越好。

  • HITS@n

    该指标是指在链接预测中排名小于n的三元组的平均占比。具体的计算方法如下:
    HITS ⁡ @ n = 1 ∣ S ∣ ∑ i = 1 ∣ S ∣ I ( rank ⁡ i ⩽ n ) \operatorname{HITS} @ n=\frac{1}{|S|} \sum_{i=1}^{|S|} \mathbb{I}\left(\operatorname{rank}_{i} \leqslant n\right) HITS@n=∣S∣1​∑i=1∣S∣​I(ranki​⩽n)

    其中,上述公式涉及的符号和MRR计算公式中涉及的符号一样,另外 I ( ⋅ ) \mathbb{I}(\cdot) I(⋅) 是indicator函数(若条件真则函数值为1,否则为0)。一般地,取n等于1、3或者10。该指标越大越好。

3 代码

代码包括6个模块,分别为:数据处理,模型模块,训练模块,测试模块,评价模块,主模块等,如图所示:
Refused

3.1 数据处理模块 dataset.py

import numpy as np
import random
import torch
import math

class Dataset:
    def __init__(self, ds_name):
        self.name = ds_name
        self.dir = "datasets/" + ds_name + "/"
        self.ent2id = {}
        self.rel2id = {}
        self.data = {spl: self.read(self.dir + spl + ".txt") for spl in ["train", "valid", "test"]}
        self.batch_index = 0
       
    def read(self, file_path): #读取文件,train,text, valid数据集
        with open(file_path, "r") as f:
            lines = f.readlines()
        
        triples = np.zeros((len(lines), 3)) #初始化数据为(0 0 0)形式
		#[(0, 0, 0),
		#(0, 0, 0),
		#(0, 0, 0)]
        for i, line in enumerate(lines):#填充数据
            triples[i] = np.array(self.triple2ids(line.strip().split("\t")))
        return triples
    
    def num_ent(self): #总实体个数
        return len(self.ent2id)
    
    def num_rel(self):#总关系个数
        return len(self.rel2id)
                     
    def triple2ids(self, triple):#将实体转化成id的形式
        return [self.get_ent_id(triple[0]), self.get_rel_id(triple[1]), self.get_ent_id(triple[2])]
                     
    def get_ent_id(self, ent):#实体转成id的形式
        if not ent in self.ent2id:
            self.ent2id[ent] = len(self.ent2id)
        return self.ent2id[ent]
            
    def get_rel_id(self, rel):#关系转成id的形式
        if not rel in self.rel2id:
            self.rel2id[rel] = len(self.rel2id)
        return self.rel2id[rel]
                     
    def rand_ent_except(self, ent):  #进行随机生成一个[0, num_ent-1]之间,但不能随机生成真实的值,如果生成则扔掉
        rand_ent = random.randint(0, self.num_ent() - 1)
        while(rand_ent == ent):#while循环的作用就是提出真实值
            rand_ent = random.randint(0, self.num_ent() - 1)
        return rand_ent
                     
    def next_pos_batch(self, batch_size):#进行batch操作,每次取一个batch_size,
        if self.batch_index + batch_size < len(self.data["train"]):#数据足够当前batch_size
            batch = self.data["train"][self.batch_index: self.batch_index+batch_size]
            self.batch_index += batch_size
        else:#数据不够当前的batch_size
            batch = self.data["train"][self.batch_index:]
            self.batch_index = 0
        return np.append(batch, np.ones((len(batch), 1)), axis=1).astype("int") #appending the +1 label
		#增加一列,表示lable,因为都是都是正确的三元组,所以lable都是1
		#[(1,1, 3, 1),
		#(1,1, 3, 1),
		#(1,1, 3, 1)]
                     
    def generate_neg(self, pos_batch, neg_ratio):#进行负采样,neg_ratio表示负载样的个数
        neg_batch = np.repeat(np.copy(pos_batch), neg_ratio, axis=0)#对正确的三元组进行复制,复制neg_ratio个
        for i in range(len(neg_batch)):
            if random.random() < 0.5:#表示随机对头实体或者尾实体进行负采样
                neg_batch[i][0] = self.rand_ent_except(neg_batch[i][0]) #flipping head
            else:
                neg_batch[i][2] = self.rand_ent_except(neg_batch[i][2]) #flipping tail
		#负采样所有的label都是-1
        neg_batch[:,-1] = -1
        return neg_batch

    def next_batch(self, batch_size, neg_ratio, device):#取一个batch
        pos_batch = self.next_pos_batch(batch_size)#生成一个争取的pos_batch
        neg_batch = self.generate_neg(pos_batch, neg_ratio)#生成错误的neg_batch
        batch = np.append(pos_batch, neg_batch, axis=0)#将正负batch放一起
        np.random.shuffle(batch)#进行shuffle,混乱
		#提取head, rel, tail, lable
        heads  = torch.tensor(batch[:,0]).long().to(device)
        rels   = torch.tensor(batch[:,1]).long().to(device)
        tails  = torch.tensor(batch[:,2]).long().to(device)
        labels = torch.tensor(batch[:,3]).float().to(device)
        return heads, rels, tails, labels
    
    def was_last_batch(self):
        return (self.batch_index == 0)

    def num_batch(self, batch_size):
        return int(math.ceil(float(len(self.data["train"])) / batch_size))


3.2 模型模块 model.py

import torch
import torch.nn as nn
import math

class SimplE(nn.Module):
    def __init__(self, num_ent, num_rel, emb_dim, device):
        super(SimplE, self).__init__()
        self.num_ent = num_ent
        self.num_rel = num_rel
        self.emb_dim = emb_dim
        self.device = device
		#进行配置两个实体的embedding和两个关系的embedding
		#头实体的embedding
        self.ent_h_embs   = nn.Embedding(num_ent, emb_dim).to(device)
		#尾实体的embedding 
        self.ent_t_embs   = nn.Embedding(num_ent, emb_dim).to(device)
		#正关系的embedding
        self.rel_embs     = nn.Embedding(num_rel, emb_dim).to(device)
		#逆关系的embedding
        self.rel_inv_embs = nn.Embedding(num_rel, emb_dim).to(device)

        sqrt_size = 6.0 / math.sqrt(self.emb_dim)
		#embedding 参数进行初始化
        nn.init.uniform_(self.ent_h_embs.weight.data, -sqrt_size, sqrt_size)
        nn.init.uniform_(self.ent_t_embs.weight.data, -sqrt_size, sqrt_size)
        nn.init.uniform_(self.rel_embs.weight.data, -sqrt_size, sqrt_size)
        nn.init.uniform_(self.rel_inv_embs.weight.data, -sqrt_size, sqrt_size)
        
	#embedding 参数l2范式
    def l2_loss(self):
        return ((torch.norm(self.ent_h_embs.weight, p=2) ** 2) + (torch.norm(self.ent_t_embs.weight, p=2) ** 2) + (torch.norm(self.rel_embs.weight, p=2) ** 2) + (torch.norm(self.rel_inv_embs.weight, p=2) ** 2)) / 2

    def forward(self, heads, rels, tails):
        hh_embs = self.ent_h_embs(heads)
        ht_embs = self.ent_h_embs(tails)
        th_embs = self.ent_t_embs(heads)
        tt_embs = self.ent_t_embs(tails)
        r_embs = self.rel_embs(rels)
        r_inv_embs = self.rel_inv_embs(rels)

        scores1 = torch.sum(hh_embs * r_embs * tt_embs, dim=1)
        scores2 = torch.sum(ht_embs * r_inv_embs * th_embs, dim=1)
		#核心score函数
        return torch.clamp((scores1 + scores2) / 2, -20, 20)
        

3.3 训练模块 Trainer.py

from dataset import Dataset
from SimplE import SimplE
import torch
import torch.nn as nn
import torch.nn.functional as F
import os 

class Trainer:
    def __init__(self, dataset, args):
        self.device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
		#设置模型
        self.model = SimplE(dataset.num_ent(), dataset.num_rel(), args.emb_dim, self.device)
        #数据集处理类
		self.dataset = dataset
        self.args = args
        
    def train(self):
        self.model.train()

        optimizer = torch.optim.Adagrad(
            self.model.parameters(),
            lr=self.args.lr,
            weight_decay= 0,
            initial_accumulator_value= 0.1 #this is added because of the consistency to the original tensorflow code
        )
		#进行训练
        for epoch in range(1, self.args.ne + 1):
            last_batch = False
            total_loss = 0.0

            while not last_batch:
                #获取一个batch的h, r, t, l,注意是l不是数字1
				h, r, t, l = self.dataset.next_batch(self.args.batch_size, neg_ratio=self.args.neg_ratio, device = self.device)
                last_batch = self.dataset.was_last_batch()
                optimizer.zero_grad()
				#打分函数
                scores = self.model(h, r, t)
				#损失函数
                loss = torch.sum(F.softplus(-l * scores))+ (self.args.reg_lambda * self.model.l2_loss() / self.dataset.num_batch(self.args.batch_size))
                loss.backward()
                optimizer.step()
                total_loss += loss.cpu().item()

            print("Loss in iteration " + str(epoch) + ": " + str(total_loss) + "(" + self.dataset.name + ")")
        
            if epoch % self.args.save_each == 0:
                self.save_model(epoch)

    def save_model(self, chkpnt):#保存模型
        print("Saving the model")
        directory = "models/" + self.dataset.name + "/"
        if not os.path.exists(directory):
            os.makedirs(directory)
        torch.save(self.model, directory + str(chkpnt) + ".chkpnt")

3.4 测试模块 Test.py

import torch
from dataset import Dataset
import numpy as np
from measure import Measure
from os import listdir
from os.path import isfile, join

class Tester:
    def __init__(self, dataset, model_path, valid_or_test):
        self.device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
        self.model = torch.load(model_path, map_location = self.device)
        self.model.eval()
        self.dataset = dataset
        self.valid_or_test = valid_or_test
        self.measure = Measure()
        self.all_facts_as_set_of_tuples = set(self.allFactsAsTuples())

	#计算rank,通过和正确数据进行比较
    def get_rank(self, sim_scores):#assuming the test fact is the first one
        return (sim_scores >= sim_scores[0]).sum()
	
	#进行采用,生成所有实体对应的数据
	#[(1, 2, 3),
	#(2, 2, 3),
	#(3, 2, 3),
	#.....
	#(n, 2, 3)]
    def create_queries(self, fact, head_or_tail):
        head, rel, tail = fact
        if head_or_tail == "head":
            return [(i, rel, tail) for i in range(self.dataset.num_ent())]
        elif head_or_tail == "tail":
            return [(head, rel, i) for i in range(self.dataset.num_ent())]
	
	#raw 所有数据
	#fil是提出训练集出现的数据
    def add_fact_and_shred(self, fact, queries, raw_or_fil):
        if raw_or_fil == "raw":
            result = [tuple(fact)] + queries
        elif raw_or_fil == "fil":
            result = [tuple(fact)] + list(set(queries) - self.all_facts_as_set_of_tuples)

        return self.shred_facts(result)

    # def replace_and_shred(self, fact, raw_or_fil, head_or_tail):
    #     ret_facts = []
    #     head, rel, tail = fact
    #     for i in range(self.dataset.num_ent()):
    #         if head_or_tail == "head" and i != head:
    #             ret_facts.append((i, rel, tail))
    #         if head_or_tail == "tail" and i != tail:
    #             ret_facts.append((head, rel, i))

    #     if raw_or_fil == "raw":
    #         ret_facts = [tuple(fact)] + ret_facts
    #     elif raw_or_fil == "fil":
    #         ret_facts = [tuple(fact)] + list(set(ret_facts) - self.all_facts_as_set_of_tuples)

    #     return self.shred_facts(ret_facts)
    
    def test(self):
        settings = ["raw", "fil"] if self.valid_or_test == "test" else ["fil"]
        
        for i, fact in enumerate(self.dataset.data[self.valid_or_test]):
            for head_or_tail in ["head", "tail"]:
                queries = self.create_queries(fact, head_or_tail)
                for raw_or_fil in settings:
                    h, r, t = self.add_fact_and_shred(fact, queries, raw_or_fil)
                    sim_scores = self.model(h, r, t).cpu().data.numpy()
                    rank = self.get_rank(sim_scores)
                    self.measure.update(rank, raw_or_fil)

        self.measure.normalize(len(self.dataset.data[self.valid_or_test]))
        self.measure.print_()
        return self.measure.mrr["fil"]


	#获取h, r, t
    def shred_facts(self, triples):
        heads  = [triples[i][0] for i in range(len(triples))]
        rels   = [triples[i][1] for i in range(len(triples))]
        tails  = [triples[i][2] for i in range(len(triples))]
        return torch.LongTensor(heads).to(self.device), torch.LongTensor(rels).to(self.device), torch.LongTensor(tails).to(self.device)
	#所有的正确的三元组, train, test, valid
    def allFactsAsTuples(self):
        tuples = []
        for spl in self.dataset.data:
            for fact in self.dataset.data[spl]:
                tuples.append(tuple(fact))
        
        return tuples	

3.5 评价模块 Measure.py

class Measure:
    def __init__(self):
        self.hit1  = {"raw": 0.0, "fil": 0.0}
        self.hit3  = {"raw": 0.0, "fil": 0.0}
        self.hit10 = {"raw": 0.0, "fil": 0.0}
        self.mrr   = {"raw": 0.0, "fil": 0.0}
        self.mr    = {"raw": 0.0, "fil": 0.0}

    def update(self, rank, raw_or_fil):
        if rank == 1:
            self.hit1[raw_or_fil] += 1.0
        if rank <= 3:
            self.hit3[raw_or_fil] += 1.0
        if rank <= 10:
            self.hit10[raw_or_fil] += 1.0

        self.mr[raw_or_fil]  += rank
        self.mrr[raw_or_fil] += (1.0 / rank)
    
    def normalize(self, num_facts):
        for raw_or_fil in ["raw", "fil"]:
            self.hit1[raw_or_fil]  /= (2 * num_facts)
            self.hit3[raw_or_fil]  /= (2 * num_facts)
            self.hit10[raw_or_fil] /= (2 * num_facts)
            self.mr[raw_or_fil]    /= (2 * num_facts)
            self.mrr[raw_or_fil]   /= (2 * num_facts)

    def print_(self):
        for raw_or_fil in ["raw", "fil"]:
            print(raw_or_fil.title() + " setting:")
            print("\tHit@1 =",  self.hit1[raw_or_fil])
            print("\tHit@3 =",  self.hit3[raw_or_fil])
            print("\tHit@10 =", self.hit10[raw_or_fil])
            print("\tMR =",     self.mr[raw_or_fil])
            print("\tMRR =",    self.mrr[raw_or_fil])
            print("")

3.6 主模块 Main.py

from trainer import Trainer
from tester import Tester
from dataset import Dataset
import argparse
import time
def get_parameter():
    parser = argparse.ArgumentParser()
    parser.add_argument('-ne', default=1000, type=int, help="number of epochs")
    parser.add_argument('-lr', default=0.1, type=float, help="learning rate")
    parser.add_argument('-reg_lambda', default=0.03, type=float, help="l2 regularization parameter")
    parser.add_argument('-dataset', default="WN18", type=str, help="wordnet dataset")
    parser.add_argument('-emb_dim', default=200, type=int, help="embedding dimension")
    parser.add_argument('-neg_ratio', default=1, type=int, help="number of negative examples per positive example")
    parser.add_argument('-batch_size', default=1415, type=int, help="batch size")
    parser.add_argument('-save_each', default=50, type=int, help="validate every k epochs")
    args = parser.parse_args()
    return args

if __name__ == '__main__':
    args = get_parameter()
    dataset = Dataset(args.dataset)

    print("~~~~ Training ~~~~")
    trainer = Trainer(dataset, args)
    trainer.train()

    print("~~~~ Select best epoch on validation set ~~~~")
    epochs2test = [str(int(args.save_each * (i + 1))) for i in range(args.ne // args.save_each)]
    dataset = Dataset(args.dataset)
    
    best_mrr = -1.0
    best_epoch = "0"
    for epoch in epochs2test:
        start = time.time()
        print(epoch)
        model_path = "models/" + args.dataset + "/" + epoch + ".chkpnt"
        tester = Tester(dataset, model_path, "valid")
        mrr = tester.test()
        if mrr > best_mrr:
            best_mrr = mrr
            best_epoch = epoch
        print(time.time() - start)

    print("Best epoch: " + best_epoch)

    print("~~~~ Testing on the best epoch ~~~~")
    best_model_path = "models/" + args.dataset + "/" + best_epoch + ".chkpnt"
    tester = Tester(dataset, best_model_path, "test")
    tester.test()

(2018 -NIPS)SimplE embedding for link prediction in knowledge (2018 -NIPS)SimplE embedding for link prediction in knowledge 本文为阅读论文过程中的个人总结加上翻译内容构成。 摘要 介绍知识图谱,知识图谱补全(链接预测),并说明了张量分解方法(Tensor factorization approaches)对于这类问题有着显著的效果,其中正则多元分解( Canonical Polyadic (CP))是最早的张量分解方法,但CP是有局限性的,它学习了每个实体的两个独立的嵌入向量,而它们实际上是绑定的。我们提出 阅读详情

相关推荐

知识图谱——知识补全算法综述

知识图谱——知识补全综述 1、知识图谱补全概念 目标:预测出三元组中缺失的部分,从而使得知识图谱变得更加完整。 背景:对于一个知识图谱G而言,其基本的组成部分包括实体集E=e1,e2,...emE={e_1,e_2,...e_m}E=e1​,e2​,...em​(m表示实体的个数)。关系集R=r1,r2,....rNR={r_1,r_2,....r_N}R=r1​,r2​,....rN​(N表示关...

hei653779919的博客 1万+

SimplE Embedding for Link Prediction in Knowledge Graphs

本文研究的问题是KG中的链路预测问题,首先,什么是链路预测,KG中使由许多三元组构成的,这些三元组为(h,r,t),链路预测便是在这些已知的三元组上完成KG completion工作,是对新的三元组进行预测,以达到补充KG的目的。 当然对于LP(Link Prediction)问题,已经有很多解决的方法了,最常见的便是知识图谱嵌入,为所有的实体与关系生成连续的变量空间,以满足KG中的各种关系。而本文中提出了一种SimplE Embedding方法来解决这个问题,而这个方法是在CP方法的基础...

qq_39778575的博客 1542

GAEAT: 面向知识图谱补全的图自动编码注意网络

核心问题: 现有方法专注于独立处理三元组中的实体和关系,无法捕获三元组周围局部邻域内固有的隐藏的复杂信息,例如,在图1中,我们可以利用与KG中的Jada Ko- ren Pinkett-Smith相关的两个三元组,即(Will Smith,妻子,Jada Koren Pinkett-Smith)和(Jada Koren Pinkett-Smith,是Jaden Smith的母亲)来推断相应的三重事实(Will Smith,是Jaden Smith的父亲)的知识。 ...

weixin_50180730的博客 1400

**探索SimplE:革新知识图谱链接预测的高效解决方案**

探索SimplE:革新知识图谱链接预测的高效解决方案 在当前信息爆炸的时代,如何有效地理解与组织数据成为了科技领域的一大挑战。而知识图谱作为连接实体间关系的强大工具,在这一过程中扮演了至关重要的角色。SimplE,全名为Simple Embedding for Link Prediction in Knowledge Graphs,正是为解决这一难题应运而生的一款创新工具。 项目介绍 SimplE...

gitblog_00039的博客 603

知识图谱-KGE-第三方库:LibKGE库【包含:TransE、TransH、ConvE、DistMult、ComplEx、TuckER、SimplE...】

LibKGE的主要目的是为了助力知识图谱表示模型复现研究并提供训练方法。LibKGE提供整洁的训练实现,超参优化和评估策略。并且所有的现在的启发式实现都是基于本地配置文件,符合了软件的开闭原则,不同的参数,模型选择,只需要修改配置文件即可。

u013250861的博客 1500

知识图谱-KGE-语义匹配-双线性模型-2018:SimplE

【paper】 SimplE Embedding for Link Prediction in Knowledge Graphs 【简介】 本文是加拿大英属哥伦比亚大学的两位学者发表在 NIPS 2018 上的工作,文章提出了 SimplE(Simple Embedding)。这篇和前面一篇差不多,也是对 1927 年的 CP 进行改进(简化),传统CP为实体分配的两个向量是独立训练的,SimplE利用关系的逆在三元组打分函数中加上了一个对称项,使得每个实体的两个向量依赖学习,并且 SimplE 的复杂度随

u013250861的博客 957

PointWorld: 动作条件的全场景3D点flow 预测(用同一种3D物理空间模态来表示状态和动作)

PointWorld: Scaling 3D World Models for In-The-Wild Robotic Manipulation

u013250861的博客 320

知识图谱实战应用6-基于知识推理进行知识补全的功能

大家好,我是微学AI,今天给大家讲一下知识图谱实战应用6-基于知识推理进行知识补全的功能。基于知识推理进行知识补全,是指通过利用领域内已有的知识和信息,自动推断出缺失的知识或信息,从而进行知识的补全。这种方法主要应用在知识图谱构建、搜索引擎、智能客服等领域。

微学AI的博客 1672

推荐项目:快速高效的SimplE模型实现——智能知识图谱链接预测工具

推荐项目:快速高效的SimplE模型实现——智能知识图谱链接预测工具 去发现同类优质开源项目:https://gitcode.com/ 在人工智能的璀璨星空中,知识图谱犹如一座座灯塔,照亮了理解世界的道路。而今天,我们要向您推荐的正是一款在知识图谱链接预测领域中闪耀的新星——一个基于SimplE Embedding模型的高效实现。让我们一同探索它的魅力所在。 项目介绍 这款开源项目旨在提供一个更快...

gitblog_00014的博客 630

基于组合关系路径的知识图谱补全方法研究

现有的知识图谱补全方法往往只考虑直接连接的两个节点所组成的路径,忽略了节点与其他多个节点组成的关系路径所带来的信息。针对该问题,基于节点之间连接的方向,定义了在三节点结构下三种不同的关系路径连接模式,提出知识图谱中以三节点为主的组合关系路径补全模型(composite relational path completion,CRPC)。最后,在WordNet和FreeBase数据集上验证了该模型的准确性。

TuckER:Tensor Factorization for Knowledge Graph Completion.pdf

Knowledge graphs are structured representations of real world facts. However, they typically contain only a small subset of all possible facts. Link prediction is a task of inferring missing facts based on existing ones. We propose TuckER, a relatively simple but powerful linear model based on Tucker decomposition of the binary tensor representation of knowledge graph triples. TuckER outperforms all previous state-of-the-art models acrossstandardlinkpredictiondatasets. Weprove that TuckER is a fully expressive model, deriving the bound on its entity and relation embedding dimensionality for full expressiveness which is several orders of magnitude smaller than the bound of previous state-of-the-art models ComplEx and SimplE. We further show that several previously introducedlinearmodelscanbeviewedasspecial cases of TuckER.

transH算法实现知识图谱补全实验

transH算法实现知识图谱补全实验 1. 目的 使用transH算法进行知识图谱补全实验 2. 数据集 本次实验采用freebase数据集的FB15k, 该数据集共有entity2id.txt,relation2id.txt,test.txt,train.txt和valid.txt共五个文件。实验过程中,训练时主要采用entity2id.txt,relation2id.txt,train.txt三个文件,测试集使用test.txt。 3. 方法 本次实验主要采用transH模型进行知识图谱补全实验,

Crashyi的博客 5703

ChipCamp探索系列 -- 7A. 开源CPU之BOOM的来龙去脉

本文梳理一下Berkeley的乱序超标量CPU项目----Berkeley Out of Order Machine (BOOM),作为探索7系的首篇(7A)。

ChipCamp的博客---https://chipcamp.blog.youkuaiyun.com/ 1558

知识图谱补全(KGC)的常用方法

知识图谱补全(KGC)的常用方法

syy1292的博客 5329

无刷直流电机计算转速

一个霍尔传感器周期(Tms)有两种模式输出方式(3FG/1FG),一个电周期是6个60°电角度,电机磁极数是P,磁极对数PP = P / 2 3FG模式: 1个电周期= 6 * T 1FG模式: 1个电周期= 2 * T 1秒钟电周期个数: 1分钟电周期个数: 1分钟转速: 举例: 假设电机磁极数是8,3FG模式下,霍尔周期700us,计算该条件下转速 ...

meimiaorenshengqq的博客 9670

告别臃肿!保姆级教程:用NTLite深度精简Win10,打造你的专属纯净系统(附完整组件移除清单)

本文提供了一份详细的NTLite教程,帮助用户深度精简Windows10系统,移除冗余组件并优化性能。从准备工作到组件移除清单,再到系统配置优化和无人值守安装,逐步指导用户打造专属纯净系统。附完整组件移除清单和实战经验,确保系统稳定高效运行。

weixin_30945039的博客 850

Knowledge Graph Completion(知识图谱补全)

知识图谱补全算法能让知识图谱变得更加完整,按照能否处理新实体或者新关系,可以将知识图谱补全算法分成两类: 静态知识图谱补全(Static KGC),该场景的作用是补全已知实体之间的隐含关系。仅能处理实体以及关系都是固定的场景,扩展性较差 动态知识图谱补全(Dynamic KGC),涉及不止知 识图谱G中的实体或关系,该场景能够建立知识图谱与外界的关联,从而扩大知识图谱的实体集、关系集以及三元组集。可以处理含有新实体或者新关系的场景,能够构造动态的知识图谱,具有更好的现实意义 虽然知识图谱能提供高质量的结

nakaizura 2万+

GX Works2系统启动失败排查指南:从环境配置到修复方案

在工业自动化与PLC编程调试中,软件启动异常是常见的工程痛点。当开发工具因系统环境配置不当而频繁报错时,往往不是简单的重启能解决的。运行库缺失、区域设置错误、临时目录失效或系统服务被禁用,都可能导致编程软件无法完成初始化流程。掌握从基础环境检查到系统组件修复的系统性排查思路,是提升工控开发效率的关键能力。本文从软件初始化原理出发,结合Windows系统维护实践,梳理一套适用于工业软件启动故障的通用诊断方法,并自然聚焦到三菱GX Works2常见启动失败问题的实际处理,帮助工程师快速定位并解决环境性故障,保障

weixin_42567046的博客 449

知识图谱补全技术-TransE篇

TransE通过将知识图谱中的实体和关系嵌入到低维向量空间,并使用简单的线性变换来表示实体之间的关系,提供了一种有效的方法来处理知识图谱嵌入任务。虽然它在处理复杂关系上存在一定的局限性,但其简单性和高效性使其成为知识图谱嵌入的一个重要基准方法。TransE模型通过将知识图谱中的实体和关系嵌入到低维向量空间,并使用简单的线性变换来表示实体之间的关系,提供了一种有效的知识图谱嵌入方法。虽然在处理复杂关系方面存在一定局限,但其简单性和高效性使其成为知识图谱嵌入的一个重要基准方法。

weixin_47482014的博客 5727

手把手教你搞定爱思唯尔LaTeX投稿:从模板到PDF生成的全流程避坑指南

本文详细介绍了使用LaTeX投稿爱思唯尔期刊的全流程避坑指南,包括模板选择、文件结构规范、投稿系统文件上传策略、PDF生成与检查等关键步骤。特别强调了避免常见错误如宏包冲突、图片丢失和参考文献问题,并提供了投稿后的应急方案和高阶自动化校验脚本,帮助研究者顺利完成投稿。

weixin_42525289的博客 506

CoaXPress FMC子卡:高速图像采集与FPGA原型开发实战指南

在机器视觉和工业检测领域,高速、可靠的数据传输是核心需求。CoaXPress(CXP)协议作为一种基于同轴电缆的高速接口标准,通过其高带宽、长距离传输和低延迟的特性,有效解决了传统接口在确定性实时传输方面的短板。其技术价值在于为海量图像数据提供了稳定、高效的物理层和数据链路层解决方案,广泛应用于半导体检测、医疗成像等对吞吐量有极致要求的场景。要实现CXP系统的快速原型验证,集成了高速SerDes接口和时钟管理的FMC子卡与FPGA开发板的组合成为关键。这种平台将复杂的硬件设计封装,让开发者能聚焦于图像处理算

weixin_31815843的博客 278

Python 知识图谱补全,Knowledge Graph Completion,基于大模型的知识图谱补全,基于LLMs的KGC任务

Python 知识图谱补全,Knowledge Graph Completion,基于大模型的知识图谱补全,基于LLMs的KGC任务

L_goodboy的博客 2141

因素空间与可拓学的互补性分析及问题处理融合模型

随着知识与信息数量的快速增长以及信息获取便利性的增加,企事业组织所面临的内外部环境更加复杂,业务问题呈现非线性、不确定性、多维化和实时交互性等特点。业务环境的高度不确定性和面临问题的开放性,人工智能与问题处理的深入研究迫切需要有一种自觉地为信息科学与智能科学服务的定性定量相结合的智能数学模型。经典数学适于描述物质世界的数量与空间关系,智能数学必须能描述信息拓展与智能机理。

weixin_70923796的博客 260

知识图谱补全-ProjE: Embedding Projection for Knowledge Graph Completion

ProjE: Embedding Projection for Knowledge Graph Completion阅读笔记backgroundmotivationmodelexperimentsconclusion 会议:Proceedings of the Thirty-First AAAI Conference on Artificial Intelligence (AAAI-17) author : Baoxu Shi and Tim Weninger University of Notre Dam

UpUp的博客 2810

34、高效探索柔性蛋白质环构象空间的算法

本文介绍了一种高效探索柔性蛋白质环构象空间的算法,通过种子采样和变形采样两种技术,同时满足运动学闭合和避免空间位阻冲突的约束条件。种子采样能够在闭合无冲突构象空间中广泛生成构象,而变形采样则可以对“有趣”区域进行更密集的探索。实验结果表明,这两种方法在不同大小和受限程度的蛋白质环上均表现出色,运行时间在可接受范围内。相关算法已集成到工具包LoopTK中,可在https://simtk.org/home/looptk获取。

moon9的博客 127

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

SimKGC: Simple Contrastive Knowledge Graph Completion with Pre-trained Language Models

目前,比较流行的知识图谱补全模型,通常喜欢使用结构化的信息即(h,r,t)的这种形式进行知识图谱补全。但是这种方式其实可以使用的特征有限,增加额外信息-实体描述(或者叫文本信息,我习惯性交实体描述,差不多一个意思,不要纠结),可以提高模型的表达或者特征。目前在WN18RR数据集上评分最高的模型,就基于实体描述信息,为什么只提WN18RR,因为在FB15k-237上,效果不咋地,后面细说。

REfusing的博客 1982

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

上一篇: art-template渲染界面时,渲染内容中有标签无法编译
下一篇: A Neighborhood-Attention Fine-grained Entity Typing for Knowledge Graph Completion:AttEt
Re:fused
博客等级 码龄9年 474粉丝 162原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值