【paper】 A Semantic Matching Energy Function for Learning with Multi-relational Data
【简介】 这篇文章是 Antoine Bordes 发表在 2014 年的 Machine Learning 上的工作,提出的模型和翻译模型的 UM(Unstructured Model)一毛一样,甚至使用的变量写法都是一样的,只是 UM 画了示意图、SME 没有,唯一和 UM 不同的是 SME 提供了实体和关系交互时使用的 g 函数的两种形式:线性和双线性,而 UM 只有双线性。
线性的实现:
class SME(PairwiseModel):
""" `A Semantic Matching Energy Function for Learning with Multi-relational Data`_
Semantic Matching Energy (SME) is an algorithm for embedding multi-relational data into vector spaces.
SME conducts semantic matching using neural network architectures. Given a fact (h, r, t), it first projects
entities and relations to their embeddings in the input layer. Later the relation r is combined with both h and t
to get gu(h, r) and gv(r, t) in its hidden layer. The score is determined by calculating the matching score of gu and gv.
There are two versions of SME: a linear version(SMELinear) as well as bilinear(SMEBilinear) version which differ in how the hidden layer is defined.
Args:
config (object): Model configuration parameters.
Portion of the code based on glorotxa_.
.. _glorotxa: https://github.com/glorotxa/SME/blob/master/model.py
.. _A Semantic Matching Energy Function for Learning with Multi-relational Data: http://www.thespermwhale.com/jaseweston/papers/ebrm_mlj.pdf
"""
def __init__(self, **kwargs):
super(SME, self).__init__(self.__class__.__name__.lower())
param_list = ["tot_entity", "tot_relation", "hidden_size"]
param_dict = self.load_params(param_list, kwargs)
self.__dict__.update(param_dict)
self.ent_embeddings = NamedEmbedding("ent_embedding", self.tot_entity, self.hidden_size)
self.rel_embeddings = NamedEmbedding("rel_embedding", self.tot_relation, self.hidden_size)
self.mu1 = NamedEmbedding("mu1", self.hidden_size, self.hidden_size)
self.mu2 = NamedEmbedding("mu2", self.hidden_size, self.hidden_size)
self.bu = NamedEmbedding("bu", self.hidden_size, 1)
self.mv1 = NamedEmbedding("mv1", self.hidden_size, self.hidden_size)
self.mv2 = NamedEmbedding("mv2", self.hidden_size, self.hidden_size)
self.bv = NamedEmbedding("bv", self.hidden_size, 1)
nn.init.xavier_uniform_(self.ent_embeddings.weight)
nn.init.xavier_uniform_(self.rel_embeddings.weight)
nn.init.xavier_uniform_(self.mu1.weight)
nn.init.xavier_uniform_(self.mu2.weight)
nn.init.xavier_uniform_(self.bu.weight)
nn.init.xavier_uniform_(self.mv1.weight)
nn.init.xavier_uniform_(self.mv2.weight)
nn.init.xavier_uniform_(self.bv.weight)
self.parameter_list = [
self.ent_embeddings,
self.rel_embeddings,
self.mu1,
self.mu2,
self.bu,
self.mv1,
self.mv2,
self.bv,
]
self.loss = Criterion.pairwise_hinge
def embed(self, h, r, t):
"""Function to get the embedding value.
Args:
h (Tensor): Head entities ids.
r (Tensor): Relation ids of the triple.
t (Tensor): Tail entity ids of the triple.
Returns:
Tensors: Returns head, relation and tail embedding Tensors.
"""
emb_h = self.ent_embeddings(h)
emb_r = self.rel_embeddings(r)
emb_t = self.ent_embeddings(t)
return emb_h, emb_r, emb_t
def _gu_linear(self, h, r):
"""Function to calculate linear loss.
Args:
h (Tensor): Head entities ids.
r (Tensor): Relation ids of the triple.
Returns:
Tensors: Returns the bilinear loss.
"""
mu1h = torch.matmul(self.mu1.weight, h.T) # [k, b]
mu2r = torch.matmul(self.mu2.weight, r.T) # [k, b]
return (mu1h + mu2r + self.bu.weight).T # [b, k]
def _gv_linear(self, r, t):
"""Function to calculate linear loss.
Args:
h (Tensor): Head entities ids.
r (Tensor): Relation ids of the triple.
Returns:
Tensors: Returns the bilinear loss.
"""
mv1t = torch.matmul(self.mv1.weight, t.T) # [k, b]
mv2r = torch.matmul(self.mv2.weight, r.T) # [k, b]
return (mv1t + mv2r + self.bv.weight).T # [b, k]
def forward(self, h, r, t):
"""Function to that performs semanting matching.
Args:
h (Tensor): Head entities ids.
r (Tensor): Relation ids of the triple.
t (Tensor): Tail ids of the triple.
Returns:
Tensors: Returns the semantic matchin score.
"""
h_e, r_e, t_e = self.embed(h, r, t)
norm_h = F.normalize(h_e, p=2, dim=-1)
norm_r = F.normalize(r_e, p=2, dim=-1)
norm_t = F.normalize(t_e, p=2, dim=-1)
return -torch.sum(self._gu_linear(norm_h, norm_r) * self._gv_linear(norm_r, norm_t), 1)
双线性的实现:
class SME_BL(SME):
""" `A Semantic Matching Energy Function for Learning with Multi-relational Data`_
SME_BL is an extension of SME_ that BiLinear function to calculate the matching scores.
Args:
config (object): Model configuration parameters.
.. _`SME`: api.html#pykg2vec.models.pairwise.SME
"""
def __init__(self, **kwargs):
super(SME_BL, self).__init__(**kwargs)
self.model_name = self.__class__.__name__.lower()
self.loss = Criterion.pairwise_hinge
def _gu_bilinear(self, h, r):
"""Function to calculate bilinear loss.
Args:
h (Tensor): Head entities ids.
r (Tensor): Relation ids of the triple.
Returns:
Tensors: Returns the bilinear loss.
"""
mu1h = torch.matmul(self.mu1.weight, h.T) # [k, b]
mu2r = torch.matmul(self.mu2.weight, r.T) # [k, b]
return (mu1h * mu2r + self.bu.weight).T # [b, k]
def _gv_bilinear(self, r, t):
"""Function to calculate bilinear loss.
Args:
h (Tensor): Head entities ids.
r (Tensor): Relation ids of the triple.
Returns:
Tensors: Returns the bilinear loss.
"""
mv1t = torch.matmul(self.mv1.weight, t.T) # [k, b]
mv2r = torch.matmul(self.mv2.weight, r.T) # [k, b]
return (mv1t * mv2r + self.bv.weight).T # [b, k]
def forward(self, h, r, t):
"""Function to that performs semanting matching.
Args:
h (Tensor): Head entities ids.
r (Tensor): Relation ids of the triple.
t (Tensor): Tail ids of the triple.
Returns:
Tensors: Returns the semantic matchin score.
"""
h_e, r_e, t_e = self.embed(h, r, t)
norm_h = F.normalize(h_e, p=2, dim=-1)
norm_r = F.normalize(r_e, p=2, dim=-1)
norm_t = F.normalize(t_e, p=2, dim=-1)
return torch.sum(self._gu_bilinear(norm_h, norm_r) * self._gv_bilinear(norm_r, norm_t), -1)