简介
知识图谱是一个结构化的知识库,通过图的形式展示实体(如人、地点、事物)及其之间的关系。实体是图中的节点,而关系则是连接这些节点的边。它通常用于整合来自不同来源的数据,提供丰富的语义信息,支持推理、查询和数据分析。知识图谱在搜索引擎、智能助理和推荐系统中都有广泛应用。
KGE就是将实体和关系嵌入到低维向量空间中,同时保留KG的结构和语义信息。
transE模型
KGE经典模型之一,基于翻译距离的模型!
该模型将关系看作头实体到尾实体的翻译。
所以它的损失函数(欧式距离的相反数)
示例代码
import torch
import torch.nn as nn
import torch.optim as optim
# TransE模型
class TransE(nn.Module):
def __init__(self, num_entities, num_relations, embedding_dim):
super(TransE, self).__init__()
self.entity_embeddings = nn.Embedding(num_entities, embedding_dim)
self.relation_embeddings = nn.Embedding(num_relations, embedding_dim)
self.embedding_dim = embedding_dim
# 初始化嵌入向量
nn.init.xavier_uniform_(self.entity_embeddings.weight.data)
nn.init.xavier_uniform_(self.relation_embeddings.weight.data)
def forward(self, head, relation, tail):
head_emb = self.entity_embeddings(head)
relation_emb = self.relation_embeddings(relation)
tail_emb = self.entity_embeddings(tail)
#经典损失函数计算!计算三元组的得分
#norm函数计算范数,p=1表示计算1范数,dim=1表示计算维度为1的范数
score = torch.norm(head_emb + relation_emb - tail_emb, p=1, dim=1)
return score
# 损失函数
def transE_loss(pos_score, neg_score, margin=1.0):
return torch.mean(torch.relu(pos_score - neg_score + margin))
# 数据示例(假设已经编码为整数)
# 这里的实体和关系已经被转换成了数字id
triples = [(0, 0, 1), (2, 1, 3), (0, 2, 2)]
negative_triples = [(1, 0, 2), (3, 1, 0), (2, 2, 1)]
# 超参数设置
num_entities = 4
num_relations = 3
embedding_dim = 50
learning_rate = 0.001
num_epochs = 100
# 初始化模型
model = TransE(num_entities, num_relations, embedding_dim)
optimizer = optim.Adam(model.parameters(), lr=learning_rate)
# 训练过程
for epoch in range(num_epochs):
model.train()
#pytorch中一样的数据转为张量
pos_triples = torch.LongTensor(triples)
neg_triples = torch.LongTensor(negative_triples)
# 正例得分
pos_score = model(pos_triples[:, 0], pos_triples[:, 1], pos_triples[:, 2])
# 负例得分
neg_score = model(neg_triples[:, 0], neg_triples[:, 1], neg_triples[:, 2])
# 计算损失
loss = transE_loss(pos_score, neg_score)
# 反向传播和优化
optimizer.zero_grad()
loss.backward()
optimizer.step()
if epoch % 10 == 0:
print(f"Epoch {epoch}, Loss: {loss.item()}")
print("Training complete!")
一些解释说明
nn.Embedding(num_entities, embedding_dim)
是 PyTorch 中用于生成嵌入矩阵的一个模块。它通常用于将离散的整数索引(例如实体或词汇的 ID)映射到一个连续的向量空间中。
-
num_entities
: 表示要嵌入的元素的数量。例如,如果你有 100 个不同的实体(或词汇),那么num_entities
就是 100。这相当于嵌入矩阵的行数。 -
embedding_dim
: 表示嵌入向量的维度,即每个实体(或词汇)将被映射到一个具有embedding_dim
维度的向量。例如,如果embedding_dim
是 50,那么每个实体将被映射到一个 50 维的向量空间中。这相当于嵌入矩阵的列数。
nn.Embedding(4, 3):
计算正例和负例的得分以及损失是TransE模型训练的核心步骤:
首先forward那里norm函数计算范数,p=1表示计算1范数,dim=1表示计算维度为1的范数
1范数就是绝对值范数。
把正例和负例都丢进去:
pos_triples[:, 0]
: 获取所有正例中的头实体的ID。pos_triples[:, 1]
: 获取所有正例中的关系的ID。pos_triples[:, 2]
: 获取所有正例中的尾实体的ID。
- 正例(Positive Example): 在知识图谱中真实存在的三元组(head, relation, tail),表示头实体和尾实体之间确实存在这种关系。
- 负例(Negative Example): 在知识图谱中不存在的虚假三元组,通常是通过对正例进行某种变换(如替换头实体或尾实体)生成的
然后计算损失函数那一套:
损失函数希望正例的得分尽可能小(即头实体加关系后的向量与尾实体的向量非常接近),而负例的得分尽可能大(即头实体加关系后的向量与错误的尾实体的向量相距较远)。margin
是一个超参数,用于控制正例和负例之间的得分差异。
如果正例的得分比负例的得分小 margin
,则损失为零(意味着模型已经正确区分了正负例);否则,损失为正值,需要进一步调整模型参数以缩小正例得分和负例得分之间的差距。
总结
这段代码计算正例和负例的得分,并利用这些得分计算损失。通过最小化这个损失,模型能够学习将合理的三元组映射为较近的向量(得分低),而将不合理的三元组映射为较远的向量(得分高),从而有效地进行知识图谱的推理。
transH模型
对经典的TransE模型的扩展。TransH模型通过引入关系特定的超平面,解决了TransE模型在处理复杂关系(如多对多关系)时的不足。
TransH假设每个关系 r都对应一个超平面,实体在这个超平面上被投影,然后再在这个超平面上进行平移操作。这一改进使得模型能够更灵活地处理多对多关系,从而提高嵌入的表现力。
投影操作的目的是将实体嵌入向量移到关系对应的超平面上,使得实体在不同的关系超平面上有不同的表示。这样,同一个实体在不同关系下可以有不同的语义表现。
transR模型
通过引入关系特定的映射矩阵,TransR能够更好地处理多对多关系和多样的关系模式。
核心是把实体和关系分开!!!
每个实体和关系都被嵌入到不同的向量空间中。实体首先在实体空间中表示,然后通过一个关系特定的转换矩阵映射到关系空间,在关系空间中进行平移操作。
后续还有transD模型,是transR模型的进一步改进,这里不赘述。
RGCN模型
专门用于处理异构图(Heterogeneous Graphs),即具有不同类型节点和边的图!
它为每种关系类型引入了独立的参数,从而能够处理不同关系类型的边,并将它们聚合到目标节点的表示中。
每种关系类型都有独立的权重矩阵!损失函数计算时考虑关系和其权重矩阵!(通过为每种关系类型引入独立的权重矩阵,RGCN能够灵活地捕捉不同关系类型的特征。)
缺点太明显:参数太多,极其容易过拟合(需要正则化)
示例代码
import torch
import torch.nn as nn
import torch.nn.functional as F
import dgl
from dgl.nn import RelGraphConv
class RGCN(nn.Module):
def __init__(self, h_dim, out_dim, num_rels, num_bases=-1, num_hidden_layers=1):
super(RGCN, self).__init__()
self.layers = nn.ModuleList()
self.layers.append(RelGraphConv(h_dim, h_dim, num_rels, num_bases, activation=F.relu, self_loop=True, dropout=0.1))
for _ in range(num_hidden_layers):
self.layers.append(RelGraphConv(h_dim, h_dim, num_rels, num_bases, activation=F.relu, self_loop=True, dropout=0.1))
self.layers.append(RelGraphConv(h_dim, out_dim, num_rels, num_bases, activation=None, self_loop=True))
def forward(self, g, inputs):
h = inputs
for layer in self.layers:
h = layer(g, h, g.edata['rel_type'])
return h
# 示例使用
num_nodes = 100
h_dim = 16
out_dim = 8
num_rels = 4
g = dgl.heterograph({
('node', 'rel_type1', 'node'): (torch.randint(0, num_nodes, (500,)), torch.randint(0, num_nodes, (500,))),
('node', 'rel_type2', 'node'): (torch.randint(0, num_nodes, (300,)), torch.randint(0, num_nodes, (300,))),
('node', 'rel_type3', 'node'): (torch.randint(0, num_nodes, (400,)), torch.randint(0, num_nodes, (400,))),
('node', 'rel_type4', 'node'): (torch.randint(0, num_nodes, (200,)), torch.randint(0, num_nodes, (200,))),
})
node_features = torch.randn(num_nodes, h_dim)
model = RGCN(h_dim, out_dim, num_rels)
output = model(g, node_features)
print(output)
MetaPath2Vec模型
元路径思想!
MetaPath2Vec 的核心思想是通过“元路径”(Meta-path)的方式,在异构网络中生成节点的游走路径,然后利用这些路径训练一个 Skip-gram 模型,以学习节点的表示。
Skip-gram 模型是一种用于词向量学习的神经网络模型,是 Word2Vec 的两个模型之一(另一个是 CBOW 模型)。
Skip-gram 模型的核心思想是基于分布假设:相似的词会出现在相似的上下文中。因此,通过学习一个词与其上下文词的共现关系,Skip-gram 能够生成捕捉词语语义的向量表示。
示例代码
import torch
import torch.nn as nn
import torch.optim as optim
import dgl
from dgl.nn.pytorch import metapath2vec
# MetaPath2Vec 模型定义
class MetaPath2Vec(nn.Module):
def __init__(self, num_nodes, embedding_dim):
super(MetaPath2Vec, self).__init__()
self.embedding = nn.Embedding(num_nodes, embedding_dim)
self.metapath2vec = metapath2vec.MetaPath2Vec(embedding_dim, embedding_dim, num_layers=1)
def forward(self, g, metapath):
# Compute node embeddings
node_embeddings = self.embedding.weight
# Compute metapath embeddings
metapath_embeddings = self.metapath2vec(g, node_embeddings, metapath)
return metapath_embeddings
# 示例数据和训练过程
def main():
# 创建一个简单的异构图
g = dgl.heterograph({
('user', 'follows', 'user'): (torch.tensor([0, 1, 2]), torch.tensor([1, 2, 0])),
('user', 'likes', 'item'): (torch.tensor([0, 1, 2]), torch.tensor([0, 1, 2])),
('item', 'liked_by', 'user'): (torch.tensor([0, 1, 2]), torch.tensor([0, 1, 2])),
})
# 定义模型和优化器
num_nodes = g.num_nodes('user')
embedding_dim = 10
model = MetaPath2Vec(num_nodes, embedding_dim)
optimizer = optim.Adam(model.parameters(), lr=0.01)
# 定义元路径
metapath = [('user', 'follows', 'user'), ('user', 'likes', 'item'), ('item', 'liked_by', 'user')]
# 训练模型
model.train()
for epoch in range(100):
optimizer.zero_grad()
# 获取 metapath 嵌入
embeddings = model(g, metapath)
# 这里的损失函数需要根据具体任务定义,这里仅作示例
loss = torch.norm(embeddings) # 简单的损失函数作为示例
loss.backward()
optimizer.step()
print(f'Epoch {epoch}, Loss: {loss.item()}')
if __name__ == "__main__":
main()
一些解释说明
dgl里面带了实现类,直接调用即可
MAGNN模型
有效地聚合和利用多种元路径(Meta-path)信息来学习节点的表示。MAGNN 被设计用来解决在异构信息网络中,由于节点和边的多样性带来的复杂关系建模问题。
核心:MAGNN 的核心思想是通过聚合不同元路径下的节点邻居信息,来生成节点的多方面表示,并将这些表示结合起来,形成最终的节点嵌入。与传统的基于元路径的模型(如 MetaPath2Vec)不同,MAGNN 能够同时处理多个元路径,并且在节点表示的生成过程中充分利用了图的结构信息。
主要操作
元路径实例化:首先通过特定的元路径实例化生成一组节点序列,这些序列表示节点在不同元路径下的多种关系!每个元路径对应的实例化序列,称为元路径实例。
元路径实例的内聚合:针对每一条指定的元路径,进行内部特征的聚合。具体方法包括基于注意力机制的加权求和、平均等。这一步聚合可以捕捉到元路径内部节点之间的依赖关系。
元路径间聚合:经典!不同的元路径提供了节点之间的不同视角。MAGNN 在进行内聚合后,还要对所有元路径的结果进行聚合,得到节点的最终表示!
通过注意力机制(或其他聚合方式)来聚合不同元路径下的节点表示,进而综合不同的语义信息。
示例代码
总结
MAGNN 是一种强大的异构信息网络表示学习模型,通过对多种元路径的聚合,有效地捕捉了异构网络中的复杂关系。它利用内聚合和间聚合的双层架构,结合注意力机制,实现了对不同元路径信息的高效整合。MAGNN 在节点分类、链接预测等任务中展示了优异的性能,适合应用于各种异构网络分析任务。
HAN模型
示例代码
import torch
import torch.nn as nn
import torch.nn.functional as F
import dgl
import dgl.nn as dglnn
from dgl.dataloading import NodeDataLoader
class MAGNNLayer(nn.Module):
def __init__(self, in_feats, out_feats, metapaths, num_heads=1):
super(MAGNNLayer, self).__init__()
#传入元路径列表!(描述图中节点之间的特定关系序列。)
self.metapaths = metapaths
#输入特征的维度乘以元路径的数量,作为全连接层的输入维度。
#输出特征的维度乘以注意力头的数量,作为全连接层的输出维度。
self.attn_fc = nn.Linear(in_feats * len(metapaths), out_feats * num_heads, bias=False)
self.out_feats = out_feats
self.num_heads = num_heads
def forward(self, G, h):
# 初始化元路径特征列表,存储每个元路径的聚合特征。
metapath_h = []
for metapath in self.metappaths:
# h_metapath,用于存储当前元路径中每个关系的聚合特征
h_metapath = []
for u, v in metapath:
#对于元路径中的每对关系 (u, v),计算节点 v 的特征在维度1上的均值,并将其添加到 h_metapath 列表中。
h_metapath.append(h[v].mean(dim=1))
#拼接起来,得到当前元路径的聚合特征
metapath_h.append(torch.cat(h_metapath, dim=1))
# 拼接所有元路径的聚合特征
h_cat = torch.cat(metapath_h, dim=1)
# 使用注意力机制进行跨元路径聚合
h_attn = self.attn_fc(h_cat)
h_attn = h_attn.view(h_attn.shape[0], self.num_heads, self.out_feats)
h_attn = F.elu(h_attn.sum(dim=1)) # Apply ELU non-linearity
return h_attn
class MAGNN(nn.Module):
#模型由多个 MAGNNLayer 层组成
'''num_metapaths: 元路径的数量。
in_feats: 输入特征的维度。
hidden_feats: 隐藏层特征的维度。
out_feats: 输出特征的维度。
num_heads: 注意力头的数量,默认为1。
num_layers: 层的数量,默认为2'''
def __init__(self, num_metapaths, in_feats, hidden_feats, out_feats, num_heads=1, num_layers=2):
super(MAGNN, self).__init__()
self.layers = nn.ModuleList()
#添加输入层
self.layers.append(MAGNNLayer(in_feats, hidden_feats, num_metapaths, num_heads))
#添加隐藏层
for _ in range(num_layers - 2):
self.layers.append(MAGNNLayer(hidden_feats, hidden_feats, num_metapaths, num_heads))
#添加输出层
self.layers.append(MAGNNLayer(hidden_feats, out_feats, num_metapaths, num_heads))
#每一层都这样!!所以逐层前向传播
def forward(self, G, h):
for layer in self.layers:
h = layer(G, h)
return h
# 假设我们有一个异构图 G,和节点特征 h,以及元路径定义 metapaths
G = ... # 预定义的异构图
h = ... # 节点特征矩阵
metapaths = [...] # 元路径列表,包含多个元路径的边列表
# 创建 MAGNN 模型
model = MAGNN(num_metapaths=len(metapaths), in_feats=h.shape[1], hidden_feats=64, out_feats=32)
# 进行前向传播
node_embeddings = model(G, h)
一些解释说明
首先定义MAGNNLayer,为一层的传播模型,里面包含简单的全连接层,对元路径上的节点关系进行信息聚合(一个范式!)
MAGNN: 由多个MAGNNLayer
组成的模型。构造函数初始化了输入层、若干隐藏层和输出层。每一层通过MAGNNLayer
进行跨元路径的特征聚合和转换。
KGCN模型
关于推荐系统!KGCN(Knowledge Graph Convolutional Network)是一种利用知识图谱(Knowledge Graph)增强推荐系统的图神经网络模型。KGCN 的核心思想是通过将知识图谱中的实体和关系信息引入到推荐系统中,从而更好地捕捉用户和物品之间的复杂关系,提高推荐的准确性。
核心操作:在知识图谱上进行卷积!!!
KGCN 通过遍历与该实体直接相连的邻居实体,来聚合它们的特征信息。为了捕捉更深层次的语义信息,KGCN 通常会堆叠多个卷积层。在每一层中,KGCN 聚合来自上一层的邻居信息,并逐层更新实体的表示。
示例代码
import torch
import torch.nn as nn
import torch.nn.functional as F
import dgl
import dgl.nn as dglnn
import numpy as np
class KGCNLayer(nn.Module):
def __init__(self, in_feats, out_feats):
super(KGCNLayer, self).__init__()
self.fc = nn.Linear(in_feats, out_feats)
#注意力机制!
self.attn_fc = nn.Linear(2 * out_feats, 1)
def edge_attention(self, edges):
#经典注意力机制的处理
z2 = torch.cat([edges.src['z'], edges.dst['z']], dim=1)
a = self.attn_fc(z2)
return {'e': F.leaky_relu(a)}
def message_func(self, edges):
return {'z': edges.src['z'], 'e': edges.data['e']}
def reduce_func(self, nodes):
alpha = F.softmax(nodes.mailbox['e'], dim=1)
h = torch.sum(alpha * nodes.mailbox['z'], dim=1)
return {'h': h}
def forward(self, g, h):
z = self.fc(h)
g.ndata['z'] = z
g.apply_edges(self.edge_attention)
g.update_all(self.message_func, self.reduce_func)
return g.ndata.pop('h')
class KGCN(nn.Module):
def __init__(self, in_feats, hidden_feats, out_feats, num_layers=2):
super(KGCN, self).__init__()
self.layers = nn.ModuleList()
#输入层
self.layers.append(KGCNLayer(in_feats, hidden_feats))
#隐藏层
for _ in range(num_layers - 2):
self.layers.append(KGCNLayer(hidden_feats, hidden_feats))
self.layers.append(KGCNLayer(hidden_feats, out_feats))
def forward(self, g, h):
for layer in self.layers:
h = layer(g, h)
return h
# 示例数据(用户-物品图)
# 假设用户和物品已经编码为整数索引
user_item_graph = dgl.heterograph({
('user', 'interacts', 'item'): (torch.tensor([0, 1, 2]), torch.tensor([1, 2, 3])),
('item', 'interacted_by', 'user'): (torch.tensor([1, 2, 3]), torch.tensor([0, 1, 2]))
})
# 示例数据(知识图谱)
# 假设实体已经编码为整数索引
kg_graph = dgl.heterograph({
('entity', 'related_to', 'entity'): (torch.tensor([0, 1, 2]), torch.tensor([1, 2, 3]))
})
# 假设实体和物品共享相同的嵌入
num_entities = 4
embedding_dim = 8
entity_embeddings = nn.Embedding(num_entities, embedding_dim)
# 创建 KGCN 模型
in_feats = embedding_dim
hidden_feats = 16
out_feats = 8
kgcn_model = KGCN(in_feats, hidden_feats, out_feats)
# 初始化特征
entity_features = entity_embeddings(torch.arange(num_entities))
# 在知识图谱上进行前向传播
kg_embeddings = kgcn_model(kg_graph, entity_features)
# 将知识图谱的嵌入应用于用户-物品图
user_embeddings = kg_embeddings[user_item_graph.nodes['user'].data[dgl.NID]]
item_embeddings = kg_embeddings[user_item_graph.nodes['item'].data[dgl.NID]]
# 计算用户与物品的交互得分
scores = torch.sum(user_embeddings * item_embeddings, dim=1)
# 显示得分
print(scores)
一些解释和说明
标准的DGL书写范式! 注意注意力机制的使用。