知识图谱⽤于推荐系统问题(CKE,RippleNet)
Knowledge Graph
知识图谱是⼀种语义图,其结点(node)代表实体(entity)或者概念(concept),边(edge)代表实体/概念之间的各种语义关系(relation)。⼀个知识图谱由若⼲个三元组(h、r、t)组成,其中h和t代表⼀条关系的头结点和尾节点,r代表关系。
引⼊知识图谱进⼊推荐系统领域的优点在于:
精确性(precision)。为物品item引⼊了更多的语义关系,可以深层次地发现⽤户兴趣
多样性(diversity)。提供了不同的关系连接种类,有利于推荐结果的发散,避免推荐结果局限于单⼀类型
可解释性(explainability)。连接⽤户的历史记录和推荐结果,从⽽提⾼⽤户对推荐结果的满意度和接受度,增强⽤户对推荐系统的信任。
但是知识图谱难以与神经⽹络直接结合,所以引出了knowledge reprentation learning,通过学习entity和relation的embedding之后,再嵌⼊到神经⽹络中。embedding⽅法主要可以分为translational dis
tance ⽅法和 mantic matching⽅法两种,前者是学习从头实体到尾实体的空间关系变换(如TransE等系列),后者则是直接⽤神经⽹络对语义相似度进⾏计算。将其结合到推荐⾥⾯⽐较困难的地⽅仍然有:
图简化。如何处理KG带来的多种实体和关系,按需要简化虽然可能会损失部分信息但对效率是必要的,如只对ur-ur或者item-item关系简化⼦图。
多关系传播。KG的特点就是多关系,不过现有可以⽤attention来区分不同关系的重要性,为邻居加权。
⽤户整合(将⾓⾊引⼊图结构)。由于KG是外部信号,但是否也可以将⽤户也融⼊为⼀种实体变成内在产物呢?
⼀般使⽤知识图谱有三种模式,如上图:
依次学习(one-by-one learning)。使⽤知识图谱特征学习得到实体向量和关系向量,然后将这些低维向量(TransR⽅法等),引⼊推荐系统再做后⾯的处理。即只把知识图谱作为⼀个side info,多⼀维特征的处理⽅式。
联合学习(joint learning)。将知识图谱特征学习和推荐算法的⽬标函数结合,使⽤端到端(end-to-e
nd)的⽅法进⾏联合学习。即把知识图谱的损失也纳⼊到最后的损失函数联合训练。
交替学习(alternate learning)。将知识图谱特征学习和推荐算法视为两个分离但⼜相关的任务,使⽤多任务学习(multi-task learning)的框架进⾏交替学习。这样可以让KG和RC在某种程度上融合的更加深⼊。
Collaborative Knowledge ba Embedding (CKE)
发⾃16年KDD,将KG与CF融合做联合训练。
⾸先为了使⽤知识库,作者设计了三个组件分别从结构化知识,⽂本知识和视觉知识中提取语义特征,如上图中的右半部分,知识库的处理分别为:
结构化知识。知识库中的实体以及实体的联系。使⽤TransR提取物品的结构化信息(同时考虑nodes和relations),它的结构如下图,对于每个元组(h,r,t),⾸先将实体空间中的实体向关系r投影得到和,然后使,能够使得头/尾实体在这个关系r下靠近彼此,使得不具有此关系r的实体彼此远离。
⽂本知识。对实体的⽂字性描述。⽤多层降噪⾃编码器提取⽂本表达(SDAE),图中写的是Bayesian SDAE,意思就是让权重,偏置,输出层符合特定的正态分布,对视觉的处理也是⼀样的。
视觉知识。对实体的图⽚描述如海报等。⽤多层卷积⾃编码提取物品视觉表达(SCAE)
最后得到的item的表⽰为offt向量以及结构化知识,⽂本知识,图⽚知识的向量:然后从知识库中提取的特征融合到collabrative filtering 中去,即与左边的⽤户反馈结合起来⼀起做CF进⾏训练就可以了,训练损失函数会⽤pair-wi的偏序优化。
h r t r h +r r ≈t r f (v ,v )=v h t ∣∣v +h r r −v ∣∣t r 2
2
e =j η+j v +j X +,j ∗2L 2Z ,j ∗
2L v
#TransR
def projection_transR_pytorch (original , proj_matrix ):
reference managerent_embedding_size = original .shape [1]
rel_embedding_size = proj_matrix .shape [1] // ent_embedding_size
original = original .view (-1, ent_embedding_size , 1)
#借助⼀个投影矩阵就⾏
proj_matrix = proj_matrix .view (-1, rel_embedding_size , ent_embedding_size )
return torch .matmul (proj_matrix , original ).view (-1, rel_embedding_size )
模型图如下图,对于给定的⽤户u和物品v,如何模拟⽤户兴趣在KG上的传播呢?
作者提出的⽅法就是将知识图谱中的每⼀个实体(h,r,t)都⽤⽤户历史的物品进⾏相似度计算:
v是物品向量,r是关系,h是头节点,三者计算相似度(得到了图⽚中Rh后⾯的绿⾊⽅格)。然后⽤这个权重对该实体中的尾节点t加权就得到了第⼀跳/扩散的结果:
所有跳最后的⽤户特征为所有跳的总和,需要注意的是,Ripple Network中没有对⽤户直接使⽤向量进⾏刻画,⽽是⽤⽤户点击过的物品的向量集合作为其特征(代码中也可以只使⽤最后的o):
实际上求和得到的结果可以视为v在u的⼀跳相关实体中的⼀个响应。该过程可以重复在u的⼆跳、三跳相关实体中进⾏,如此,v在知识图谱上便以V为中⼼逐层向外扩散。最后再⽤⽤户特征计算对物品的相似度得到预测结果:
然后来看⼀下模型类的代码:
这部分的代码分为:数据input,得到嵌⼊特征,依次计算每⼀跳的结果并更新(按照公式依次计算),预测。最后是损失函数(由三部分组成)和训练、测试函数。
class RippleNet (object ):
def __init__(lf , args , n_entity , n_relation ):
lf ._par_args (args , n_entity , n_relation )
lf ._build_inputs ()
p =i softmax (v R h )
in the wayT i i o =u 1p t (h ,r ,t )∈S i i i u 1
∑i i u =o +u 1o ++o u H
y =σ(u v )
T
lf._build_embeddings()
提交英文lf._build_model()
lf._build_loss()
lf._build_train()
def_par_args(lf, args, n_entity, n_relation):
lf.n_entity = n_entity
lf.n_relation = n_relation
lf.dim = args.dim
lf.n_hop = args.n_hop
lf.kge_weight = args.kge_weight
lf.l2_weight = args.l2_weight
lf.lr = args.lr
lf.n_memory = args.n_memory
lf.item_update_mode = args.item_update_mode
lf.using_all_hops = args.using_all_hops
def_build_inputs(lf):
垃圾桶的英文单词
#输⼊有items id,labels和⽤户每⼀跳的ripple t记录
lf.items = tf.placeholder(dtype=tf.int32, shape=[None], name="items")
lf.labels = tf.placeholder(dtype=tf.float64, shape=[None], name="labels")
for hop in range(lf.n_hop):#每⼀跳的结果
tf.placeholder(dtype=tf.int32, shape=[None, lf.n_memory], name="memories_h_"+str(hop)))
become的过去分词
tf.placeholder(dtype=tf.int32, shape=[None, lf.n_memory], name="memories_r_"+str(hop)))
tf.placeholder(dtype=tf.int32, shape=[None, lf.n_memory], name="memories_t_"+str(hop)))
def_build_embeddings(lf):#得到嵌⼊
shape=[lf.n_entity, lf.dim],
ib.layers.xavier_initializer())
#relation连接head和tail所以维度是lf.dim*lf.dim
shape=[lf.n_relation, lf.dim, lf.dim],
ib.layers.xavier_initializer())
i just wanna livedef_build_model(lf):
# transformation matrix for updating item embeddings at the end of each hop
优惠价# 更新item嵌⼊的转换矩阵,这个不⼀定是必要的,可以使⽤直接替换或者加和策略。
# [batch size, dim],得到item的嵌⼊
lf.item_embeddings = bedding_ity_emb_matrix, lf.items)
lf.h_emb_list =[]
lf.r_emb_list =[]
lf.t_emb_list =[]
for i in range(lf.n_hop):#得到每⼀跳的实体,关系嵌⼊list
# [batch size, n_memory, dim]
lf.h_emb_list.bedding_ity_emb_matrix, lf.memories_h[i]))
# [batch size, n_memory, dim, dim]
lf.r_emb_list.bedding_lation_emb_matrix, lf.memories_r[i]))
# [batch size, n_memory, dim]
lf.t_emb_list.bedding_ity_emb_matrix, lf.memories_t[i]))
#按公式计算每⼀跳的结果
o_list = lf._key_addressing()
o_list = lf._key_addressing()
#得到分数
lf.scores = tf.squeeze(lf.predict(lf.item_embeddings, o_list))
哈尔滨托福培训lf.scores_normalized = tf.sigmoid(lf.scores)
def_key_addressing(lf):#得到olist
o_list =[]
for hop in range(lf.n_hop):#依次计算每⼀跳
# [batch_size, n_memory, dim, 1]
h_expanded = tf.expand_dims(lf.h_emb_list[hop], axis=3)
# [batch_size, n_memory, dim],计算Rh,使⽤matmul函数
Rh = tf.squeeze(tf.matmul(lf.r_emb_list[hop], h_expanded), axis=3)
# [batch_size, dim, 1]
v = tf.expand_dims(lf.item_embeddings, axis=2)
# [batch_size, n_memory],然后和v内积计算相似度
probs = tf.squeeze(tf.matmul(Rh, v), axis=2)
# [batch_size, n_memory],softmax输出分数
probs_normalized = tf.nn.softmax(probs)
# [batch_size, n_memory, 1]
probs_expanded = tf.expand_dims(probs_normalized, axis=2)
# [batch_size, dim],然后分配分数给尾节点得到o
o = tf.reduce_sum(lf.t_emb_list[hop]* probs_expanded, axis=1)
负担英语
#更新Embedding表,并且存好o
lf.item_embeddings = lf.update_item_embedding(lf.item_embeddings, o)
o_list.append(o)
return o_list
def update_item_embedding(lf, item_embeddings, o):
#计算完hop之后,更新item的Embedding操作,可以有多种策略
if lf.item_update_mode =="replace":#直接换
item_embeddings = o
elif lf.item_update_mode =="plus":#加到⼀起
item_embeddings = item_embeddings + o
elif lf.item_update_mode =="replace_transform":#⽤前⾯的转换矩阵breatheagain
item_embeddings = tf.matmul(o, lf.transform_matrix)
elif lf.item_update_mode =="plus_transform":#⽤矩阵⽽且再加到⼀起
item_embeddings = tf.matmul(item_embeddings + o, lf.transform_matrix)
el:
rai Exception("Unknown item updating mode: "+ lf.item_update_mode)
return item_embeddings
def predict(lf, item_embeddings, o_list):
y = o_list[-1]#1只⽤olist的最后⼀个向量
if lf.using_all_hops:#2或者使⽤所有向量的相加来代表ur
for i in range(lf.n_hop -1):
y += o_list[i]
# [batch_size],ur和item算内积得到预测值
scores = tf.reduce_sum(item_embeddings * y, axis=1)
return scores
def_build_loss(lf):#损失函数有三部分
#1⽤于推荐的对数损失函数
lf.ba_loss = tf.reduce_sigmoid_cross_entropy_with_logits(labels=lf.labels, logits=lf.scores)) #2知识图谱表⽰的损失函数
lf.kge_loss =0
for hop in range(lf.n_hop):