推荐系统遇上深度学习(二十八)--知识图谱与推荐系统结合之MKR模型原理及实现

知识图谱特征学习在推荐系统中的应用步骤大致有以下三种方式:

依次训练的方法主要有:Deep Knowledge-aware Network(DKN)
联合训练的方法主要有:Ripple Network
交替训练主要采用multi-task的思路,主要方法有:Multi-task Learning for KG enhanced Recommendation (MKR)

本文先来介绍交替训练的方法MKR。

网上没有找到相关的论文,只有在一篇帖子里有所介绍,github上可以找到源代码进行学习。

1、MKR原理介绍

由于推荐系统中的物品和知识图谱中的实体存在重合,因此可以采用多任务学习的框架,将推荐系统和知识图谱特征学习视为两个分离但是相关的任务,进行交替式的学习。

MKR的模型框架如下图,其中左侧是推荐系统任务,右侧是知识图谱特征学习任务。推荐部分的输入是用户和物品的特征表示,点击率的预估值作为输出。知识图谱特征学习部分使用的是三元组的头节点和关系作为输入,预测的尾节点作为输出:

由于推荐系统中的物品和知识图谱中的实体存在重合,所以两个任务并非相互独立。所以作者在两个任务中设计了交叉特征共享单元(cross-feature-sharing units)作为两者的连接纽带。

交叉特征共享单元是一个可以让两个任务交换信息的???。由于物品向量和实体向量实际上是对同一个对象的两种描述,他们之间的信息交叉共享可以让两者都获得来自对方的额外信息,从而弥补了自身的信息稀疏性的不足,其结构如下:

关于这个交叉单元具体实现,大家可以参照代码进行理解。

最后是损失函数部分,由于是交替训练的方式,所以在训练时首先固定推荐系统??榈牟问?,训练知识图谱特征学习??榈牟问蝗缓蠊潭ㄖ锻计滋卣餮澳?榈牟问盗吠萍鱿低衬?榈牟问?/p>

推荐系统??槭堑慊髀试す滥P停鹗Ш嵌允鹗Ъ觢2正则项;知识图谱特征学习??橄Mげ獾玫降膖ail向量和真实的tail向量相近,因此首先计算二者的内积(内积可近似表示向量之间的余弦相似度),内积经过sigmoid之后取相反数,再加上l2正则项,即得到了知识图谱特征学习模块的损失。关于损失的计算,我们在代码里可以更清楚的看到。

2、MKR模型tensorflow实现

本文的代码地址为:https://github.com/princewen/tensorflow_practice/tree/master/recommendation/Basic-MKR-Demo
参考代码地址为:https://github.com/hwwang55/MKR
数据下载地址为:https://pan.baidu.com/s/1uHkQXK_ozAgBWcMUMzOfZQ 密码:qw30

在对数据进行预处理后,我们得到了两个文件:kg_final.txt和rating_final.txt

rating_final.txt数据形式如下,三列分别是user-id,item-id以及label(0是通过负采样得到的,正负样本比例为1:1)。

kg_final.txt格式如下,三类分别代表h,r,t(这里entity和item用的是同一套id):

好了,接下来我们重点介绍一下我们的MKR框架的构建。

模型输入

模型输入有以下几部分:用户的id、物品的id、推荐系统部分的label、知识图谱三元组的head、relation、tail的对应id:

def _build_inputs(self):
    self.user_indices = tf.placeholder(tf.int32,[None],'user_indices')
    self.item_indices = tf.placeholder(tf.int32,[None],'item_indices')
    self.labels = tf.placeholder(tf.float32,[None],'labels')
    self.head_indices = tf.placeholder(tf.int32,[None],'head_indices')
    self.tail_indices = tf.placeholder(tf.int32,[None],'tail_indices')
    self.relation_indices = tf.placeholder(tf.int32,[None],'relation_indices')

低层网络构建

低层网络指下面的部分:


可以看到,user_id、item_id、head_id以及relation_id首先转换为对应的embedding,user_id和relation_id经由多层神经网络向上传播、而head_id和item_id经过交叉单元进行传播。

def _build_low_layers(self,args):
    self.user_emb_matrix = tf.get_variable('user_emb_matrix', [self.n_user, args.dim])
    self.item_emb_matrix = tf.get_variable('item_emb_matrix', [self.n_item, args.dim])
    self.entity_emb_matrix = tf.get_variable('entity_emb_matrix', [self.n_entity, args.dim])
    self.relation_emb_matrix = tf.get_variable('relation_emb_matrix', [self.n_relation, args.dim])

    # [batch_size, dim]
    self.user_embeddings = tf.nn.embedding_lookup(self.user_emb_matrix, self.user_indices)
    self.item_embeddings = tf.nn.embedding_lookup(self.item_emb_matrix, self.item_indices)
    self.head_embeddings = tf.nn.embedding_lookup(self.entity_emb_matrix, self.head_indices)
    self.relation_embeddings = tf.nn.embedding_lookup(self.relation_emb_matrix, self.relation_indices)
    self.tail_embeddings = tf.nn.embedding_lookup(self.entity_emb_matrix, self.tail_indices)

    for _ in range(args.L):
        user_mlp = Dense(input_dim=args.dim,output_dim=args.dim)
        tail_mlp = Dense(input_dim=args.dim,output_dim = args.dim)
        cc_unit = CrossCompressUnit(args.dim)

        self.user_embeddings = user_mlp(self.user_embeddings)
        self.item_embeddings,self.head_embeddings = cc_unit([self.item_embeddings,self.head_embeddings])
        self.tail_embeddings = tail_mlp(self.tail_embeddings)

        self.vars_rs.extend(user_mlp.vars)
        self.vars_rs.extend(cc_unit.vars)
        self.vars_kge.extend(tail_mlp.vars)
        self.vars_kge.extend(cc_unit.vars)

接下来,我们来看一下交叉单元的代码:

v,e = inputs

v = tf.expand_dims(v,dim=2)
e = tf.expand_dims(e,dim=1)


# [batch_size, dim, dim]
c_matrix = tf.matmul(v, e)
c_matrix_transpose = tf.transpose(c_matrix, perm=[0, 2, 1])

# [batch_size * dim, dim]
c_matrix = tf.reshape(c_matrix, [-1, self.dim])
c_matrix_transpose = tf.reshape(c_matrix_transpose, [-1, self.dim])

v_output = tf.reshape(tf.matmul(c_matrix,self.weight_vv) + tf.matmul(c_matrix_transpose,self.weight_ev),[-1,self.dim]) + self.bias_v

e_output = tf.reshape(tf.matmul(c_matrix, self.weight_ve) + tf.matmul(c_matrix_transpose, self.weight_ee),
                      [-1, self.dim]) + self.bias_e

return v_output,e_output

item对应的embedding用v表示,head对应的embedding用e表示,二者初始情况下都是batch * dim大小的。过程如下:
1、v扩展成三维batch * dim * 1,e扩展成三维batch * 1 * dim,随后二者进行矩阵相乘v * e,我们知道三维矩阵相乘实际上是后两维进行运算,因此得到c_matrix的大小为 batch * dim * dim
2、对得到的c_matrix进行转置,得到c_matrix_transpose,大小为batch * dim * dim。这相当于将e扩展成三维batch * dim * 1,v扩展成三维batch * 1 * dim,随后二者进行矩阵相乘e * v。这是两种不同的特征交叉方式。
3、对c_matrix和c_matrix_transpose 进行reshape操作,变为(batch * dim ) * dim的二维矩阵
4、定义两组不同的参数和偏置,分别得到交叉后的v_output和e_output.

高层网络构建

高层网络指下面的部分:

对于推荐部分,可以采用内积直接得到CTR的预估值,也可以经过多层神经网络得到预估值;对于知识图谱部分,将head和relation对应的向量进行拼接,经过多层神经网络,得到一个tail对应向量的预估值,并与真实的tail向量计算内积。代码如下:

def _build_high_layers(self,args):
    #RS
    use_inner_product = True
    if use_inner_product:
        self.scores = tf.reduce_sum(self.user_embeddings*self.item_embeddings,axis=1)
    else:
        self.user_item_concat = tf.concat([self.user_embeddings,self.item_embeddings],axis=1)
        for _ in range(args.H - 1):
            rs_mlp = Dense(input_dim = args.dim * 2 , output_dim = args.dim * 2)
            self.user_item_concat = rs_mlp(self.user_item_concat)
            self.vars_rs.extend(rs_mlp.vars)

        rs_pred_mlp = Dense(input_dim=args.dim * 2,output_dim=1)
        self.scores = tf.squeeze(rs_pred_mlp(self.user_item_concat))
        self.vars_rs.extend(rs_pred_mlp)

    self.scores_normalized = tf.nn.sigmoid(self.scores)

    #KGE
    self.head_relation_concat = tf.concat([self.head_embeddings,self.relation_embeddings],axis=1)
    for _ in range(args.H - 1):
        kge_mlp = Dense(input_dim=args.dim * 2,output_dim = args.dim * 2)
        self.head_relation_concat = kge_mlp(self.head_relation_concat)
        self.vars_kge.extend(kge_mlp.vars)

    kge_pred_mlp = Dense(input_dim=args.dim * 2,output_dim = args.dim)
    self.tail_pred = kge_pred_mlp(self.head_relation_concat)
    self.vars_kge.extend(kge_pred_mlp.vars)
    self.tail_pred = tf.nn.sigmoid(self.tail_pred)

    self.scores_kge = tf.nn.sigmoid(tf.reduce_sum(self.tail_embeddings * self.tail_pred,axis=1))
    #self.rmse = tf.reduce_mean(tf.sqrt(tf.reduce_sum(tf.square(self.tail_embeddings - self.tail_pred),axis=1) / args.dim))

定义损失

推荐系统部分的损失是对数损失加l2正则项:

# RS
self.base_loss_rs = tf.reduce_mean(
    tf.nn.sigmoid_cross_entropy_with_logits(labels=self.labels, logits=self.scores))
self.l2_loss_rs = tf.nn.l2_loss(self.user_embeddings) + tf.nn.l2_loss(self.item_embeddings)
for var in self.vars_rs:
    self.l2_loss_rs += tf.nn.l2_loss(var)
self.loss_rs = self.base_loss_rs + self.l2_loss_rs * args.l2_weight

知识图谱特征学习模块用上一步计算的scores_kge的相反数再加上l2正则项:

# KGE
self.base_loss_kge = -self.scores_kge
self.l2_loss_kge = tf.nn.l2_loss(self.head_embeddings) + tf.nn.l2_loss(self.tail_embeddings)
for var in self.vars_kge:
    self.l2_loss_kge += tf.nn.l2_loss(var)
self.loss_kge = self.base_loss_kge + self.l2_loss_kge * args.l2_weight

参考文献

1、http://baijiahao.baidu.com/s?id=1602210213239784098&wfr=spider&for=pc

?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352

推荐阅读更多精彩内容