0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

神经协同过滤NCF原理及实战

电子工程师 来源:lp 2019-03-19 15:28 次阅读

1.1 背景

本文讨论的主要是隐性反馈协同过滤解决方案,先来明确两个概念:显性反馈和隐性反馈:

显性反馈行为包括用户明确表示对物品喜好的行为隐性反馈行为指的是那些不能明确反应用户喜好。

举例来说:

很多应用场景,并没有显性反馈的存在。因为大部分用户是沉默的用户,并不会明确给系统反馈“我对这个物品的偏好值是多少”。因此,推荐系统可以根据大量的隐性反馈来推断用户的偏好值。

根据已得到的隐性反馈数据,我们将用户-条目交互矩阵Y定义为:

但是,Yui为1仅代表二者有交互记录,并不代表用户u真的喜欢项目i,同理,u和i没有交互记录也不能代表u不喜欢i。这对隐性反馈的学习提出了挑战,因为它提供了关于用户偏好的噪声信号。虽然观察到的条目至少反映了用户对项目的兴趣,但是未查看的条目可能只是丢失数据,并且这其中存在自然稀疏的负反馈。

在隐性反馈上的推荐问题可以表达为估算矩阵 Y中未观察到的条目的分数问题(这个分数被用来评估项目的排名)。形式上它可以被抽象为学习函数:

为了处理缺失数据,有两种常见的做法:要么将所有未观察到的条目视作负反馈,要么从没有观察到条目中抽样作为负反馈实例。

1.2 矩阵分解及其缺陷

传统的求解方法是矩阵分解(MF,Matrix Factorization),为每个user和item找到一个隐向量,问题变为:

这里的 K表示隐式空间(latent space)的维度。正如我们所看到的,MF模型是用户和项目的潜在因素的双向互动,它假设潜在空间的每一维都是相互独立的并且用相同的权重将它们线性结合。因此,MF可视为隐向量(latent factor)的线性模型。

论文中给出了一个例子来说明这种算法的局限性:

1(a)是user-item交互矩阵,1(b)是用户的隐式空间,论文中强调了两点来理解这张图片:1)MF将user和item分布到同样的隐式空间中,那么两个用户之间的相似性也可以用二者在隐式空间中的向量夹角来确定。

2)使用Jaccard系数来作为真实的用户相似性。通过MF计算的相似性与Jaccard系数计算的相似性也可以用来评判MF的性能。我们先来看看Jaccard系数

上面的示例显示了MF因为使用一个简单的和固定的内积,来估计在低维潜在空间中用户-项目的复杂交互,从而所可能造成的限制。解决该问题的方法之一是使用大量的潜在因子 K (就是隐式空间向量的维度)。然而这可能对模型的泛化能力产生不利的影响(e.g. 数据的过拟合问题),特别是在稀疏的集合上。论文通过使用DNNs从数据中学习交互函数,突破了这个限制。

1.3 NCF

本文先提出了一种通用框架:

针对这个通用框架,论文提出了三种不同的实现,三种实现可以用一张图来说明:

GMF:上图中仅使用GMF layer,就得到了第一种实现方式GMF,GMF被称为广义矩阵分解,输出层的计算公式为:

MLP上图中仅使用右侧的MLP Layers,就得到了第二种学习方式,通过多层神经网络来学习user和item的隐向量。这样,输出层的计算公式为:

NeuMF结合GMF和MLP,得到的就是第三种实现方式,上图是该方式的完整实现,输出层的计算公式为:

1.4 模型实验

论文通过三个角度进行了试验:

RQ1我们提出的NCF方法是否胜过 state-of-the-art 的隐性协同过滤方法?RQ2我们提出的优化框架(消极样本抽样的logloss)怎样为推荐任务服务?RQ3更深的隐藏单元是不是有助于对用户项目交互数据的学习?

使用的数据集:MovieLens 和 Pinterest 两个数据集

评估方案:为了评价项目推荐的性能,论文采用了leave-one-out方法评估,即:对于每个用户,我们将其最近的一次交互作为测试集(数据集一般都有时间戳),并利用余下的培训作为训练集。由于在评估过程中为每个用户排列所有项目花费的时间太多,所以遵循一般的策略,随机抽取100个不与用户进行交互的项目,将测试项目排列在这100个项目中。排名列表的性能由命中率(HR)和归一化折扣累积增益(NDCG)来衡量。同时,论文将这两个指标的排名列表截断为10。如此一来,HR直观地衡量测试项目是否存在于前10名列表中,而NDCG通过将较高分数指定为顶级排名来计算命中的位置。本文计算每个测试用户的这两个指标,并求取了平均分。

Baselines,论文将NCF方法与下列方法进行了比较:ItemPop,ItemKNN,BPR,eALS。

以下是三个结果的贴图,关于试验结果的解读,由于篇幅的原因,大家可以查看原论文。

RQ1试验结果

简单的结论,即NCF效果好于BaseLine模型,如果不好的话论文也不用写了,哈哈。

RQ2试验结果

Figure 6 表示将模型看作一个二分类任务并使用logloss作为损失函数时的训练效果。Figure7 表示采样率对模型性能的影响(横轴是采样率,即负样本与正样本的比例)。

RQ3试验结果

上面的表格设置了两个变量,分别是Embedding的长度K和神经网络的层数,使用类似网格搜索的方式展示了在两个数据集上的结果。增加Embedding的长度和神经网络的层数是可以提升训练效果的。

本文的github地址为:https://github.com/princewen/tensorflow_practice/tree/master/recommendation/Basic-NCF-Demo

本文仅介绍模型相关细节,数据处理部分就不介绍啦。

项目结构如下:

数据输入本文使用了一种新的数据处理方式,不过我们的输入就是三个:userid,itemid以及label,对训练集来说,label是0-1值,对测试集来说,是具体的itemid

def get_data(self):sample = self.iterator.get_next()self.user = sample['user']self.item = sample['item']self.label = tf.cast(sample['label'],tf.float32)

定义初始化方式、损失函数、优化器

def inference(self):""" Initialize important settings """self.regularizer = tf.contrib.layers.l2_regularizer(self.regularizer_rate)if self.initializer == 'Normal': self.initializer = tf.truncated_normal_initializer(stddev=0.01)elif self.initializer == 'Xavier_Normal': self.initializer = tf.contrib.layers.xavier_initializer()else: self.initializer = tf.glorot_uniform_initializer()if self.activation_func == 'ReLU': self.activation_func = tf.nn.reluelif self.activation_func == 'Leaky_ReLU': self.activation_func = tf.nn.leaky_reluelif self.activation_func == 'ELU': self.activation_func = tf.nn.eluif self.loss_func == 'cross_entropy': # self.loss_func = lambda labels, logits: -tf.reduce_sum( # (labels * tf.log(logits) + ( # tf.ones_like(labels, dtype=tf.float32) - labels) * # tf.log(tf.ones_like(logits, dtype=tf.float32) - logits)), 1) self.loss_func = tf.nn.sigmoid_cross_entropy_with_logitsif self.optim == 'SGD': self.optim = tf.train.GradientDescentOptimizer(self.lr, name='SGD')elif self.optim == 'RMSProp': self.optim = tf.train.RMSPropOptimizer(self.lr, decay=0.9, momentum=0.0, name='RMSProp')elif self.optim == 'Adam': self.optim = tf.train.AdamOptimizer(self.lr, name='Adam')

得到embedding值分别得到GMF和MLP的embedding向量,当然也可以使用embedding_lookup方法:

with tf.name_scope('input'):self.user_onehot = tf.one_hot(self.user,self.user_size,name='user_onehot')self.item_onehot = tf.one_hot(self.item,self.item_size,name='item_onehot')with tf.name_scope('embed'):self.user_embed_GMF = tf.layers.dense(inputs = self.user_onehot, units = self.embed_size, activation = self.activation_func, kernel_initializer=self.initializer, kernel_regularizer=self.regularizer, name='user_embed_GMF')self.item_embed_GMF = tf.layers.dense(inputs=self.item_onehot, units=self.embed_size, activation=self.activation_func, kernel_initializer=self.initializer, kernel_regularizer=self.regularizer, name='item_embed_GMF')self.user_embed_MLP = tf.layers.dense(inputs=self.user_onehot, units=self.embed_size, activation=self.activation_func, kernel_initializer=self.initializer, kernel_regularizer=self.regularizer, name='user_embed_MLP')self.item_embed_MLP = tf.layers.dense(inputs=self.item_onehot, units=self.embed_size, activation=self.activation_func, kernel_initializer=self.initializer, kernel_regularizer=self.regularizer, name='item_embed_MLP')

GMFGMF部分就是求两个embedding的内积:

with tf.name_scope("GMF"):self.GMF = tf.multiply(self.user_embed_GMF,self.item_embed_GMF,name='GMF')

MLP

with tf.name_scope("MLP"):self.interaction = tf.concat([self.user_embed_MLP, self.item_embed_MLP], axis=-1, name='interaction')self.layer1_MLP = tf.layers.dense(inputs=self.interaction, units=self.embed_size * 2, activation=self.activation_func, kernel_initializer=self.initializer, kernel_regularizer=self.regularizer, name='layer1_MLP')self.layer1_MLP = tf.layers.dropout(self.layer1_MLP, rate=self.dropout)self.layer2_MLP = tf.layers.dense(inputs=self.layer1_MLP, units=self.embed_size, activation=self.activation_func, kernel_initializer=self.initializer, kernel_regularizer=self.regularizer, name='layer2_MLP')self.layer2_MLP = tf.layers.dropout(self.layer2_MLP, rate=self.dropout)self.layer3_MLP = tf.layers.dense(inputs=self.layer2_MLP, units=self.embed_size // 2, activation=self.activation_func, kernel_initializer=self.initializer, kernel_regularizer=self.regularizer, name='layer3_MLP')self.layer3_MLP = tf.layers.dropout(self.layer3_MLP, rate=self.dropout)

得到预测值

with tf.name_scope('concatenation'):self.concatenation = tf.concat([self.GMF,self.layer3_MLP],axis=-1,name='concatenation')self.logits = tf.layers.dense(inputs= self.concatenation, units = 1, activation=None, kernel_initializer=self.initializer, kernel_regularizer=self.regularizer, name='predict')self.logits_dense = tf.reshape(self.logits,[-1])

测试集构建这里只介绍几行关键的测试集构建代码,整个流程希望大家可以看一下完整的代码。需要明确的一点是,对于测试集,我们的评价不只是对错,还要关注排名,所以测试集的label不是0-1,而是具体的itemid首先,对每个user取最后一行作为测试集的正样本:

with tf.name_scope('concatenation'):self.concatenation = tf.concat([self.GMF,self.layer3_MLP],axis=-1,name='concatenation')self.logits = tf.layers.dense(inputs= self.concatenation, units = 1, activation=None, kernel_initializer=self.initializer, kernel_regularizer=self.regularizer, name='predict')self.logits_dense = tf.reshape(self.logits,[-1])

添加一些负采样的样本, 这里顺序是,1正样本-n负样本-1正样本-n负样本....,每个用户有n+1条数据,便于计算HR和NDCG:

feature_user.append(user)feature_item.append(item)labels_add.append(label)for k in neg_samples:feature_user.append(user)feature_item.append(k)labels_add.append(k)

不打乱测试集的顺序,设置batch的大小为1+n:

dataset = tf.data.Dataset.from_tensor_slices(data)dataset = dataset.batch(test_neg + 1)

计算HR和HDCG

def hr(gt_item, pred_items):if gt_item in pred_items: return 1return 0def ndcg(gt_item, pred_items):if gt_item in pred_items: index = np.where(pred_items == gt_item)[0][0] return np.reciprocal(np.log2(index + 2))return 0

更详细的代码可以参考github,最好能够手敲一遍来理解其原理哟!

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 神经网络
    +关注

    关注

    42

    文章

    4562

    浏览量

    98645
  • 数据集
    +关注

    关注

    4

    文章

    1176

    浏览量

    24340
  • 线性模型
    +关注

    关注

    0

    文章

    8

    浏览量

    7794

原文标题:推荐系统遇上深度学习(十一)--神经协同过滤NCF原理及实战

文章出处:【微信号:atleadai,微信公众号:LeadAI OpenLab】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    请问如何实现协同过滤算法?

    协同过滤算法的原理及实现基于物品的协同过滤算法详解协同过滤算法的原理及实现
    发表于 11-05 06:51

    基于协同过滤的垃圾邮件过滤系统

    针对当前垃圾邮件过滤技术中邮件性质评价难及邮件附件判断难的问题,提出协同过滤模型,设计一个多层次垃圾邮件过滤系统。该系统针对垃圾邮件一般群发给多人的特点,提取
    发表于 04-10 08:39 26次下载

    基于信任因子的协同过滤推荐算法

    传统协同过滤算法最后的预测值是用户最近邻评价的加权平均值,过于强调相似度的作用。除相似度以外,信任也是影响推荐结果的因素之一。该文提出以用户的评价个数和为他人
    发表于 04-13 08:40 22次下载

    基于SVD的Kmeans聚类协同过滤算法王伟

    基于SVD的K_means聚类协同过滤算法_王伟
    发表于 03-17 08:00 0次下载

    一种基于改进信息熵的协同过滤算法_曾建新

    一种基于改进信息熵的协同过滤算法_曾建新
    发表于 03-19 11:46 0次下载

    基于聚类协同过滤推荐算法优化

    作为重要的个性化推荐算法之一,协同过滤推荐算法有其独特的优势,但同时存在数据稀疏性、冷启动和扩展性问题。针对数据稀疏性问题,对项目相似度进行改进,利用基于项目的协同过滤对原始评分矩阵进行填充,以此
    发表于 11-27 10:04 0次下载
    基于聚类<b class='flag-5'>协同过滤</b>推荐算法优化

    基于巴氏系数的协同过滤算法

    推荐系统成功帮助人们解决了信息过载问题,并成功运用于商业领域。推荐系统的核心是推荐算法,协同过滤算法是其中最为广泛使用的协同过滤算法,其优点是它与领域知识无关并且准确性也比其他算法高。可分为
    发表于 11-29 11:47 1次下载

    基于协同过滤推荐方法研究

    协同过滤算法是目前被广泛运用在推荐系统领域的最成功技术之一,但是面对用户数量的快速增长及相应的评分数据的缺失,推荐系统中的数据稀疏性问题也越来越明显,严重地影响着推荐的质量和效率。针对传统协同过滤
    发表于 12-06 10:33 0次下载

    一种基于隐私保护的协同过滤推荐算法

    推荐系统中的用户隐私保护问题是当前的一个研究热点。以推荐系统服务器不可信为前提,提出了一种基于代换加密的隐私保护协同过滤算法。用户在客户端对评分信息进行代换加密并提交给推荐服务器,服务器则根据收集
    发表于 12-06 11:13 0次下载
    一种基于隐私保护的<b class='flag-5'>协同过滤</b>推荐算法

    基于信息熵和时效性的协同过滤推荐

    针对协同过滤推荐算法存在的噪声数据问题,提出了用户信息熵模型。用户信息熵模型结合信息论中信息熵的概念,采用信息熵的大小衡量用户信息的含量,利用用户评分数据得到用户的信息熵,过滤信息熵低的用户,从而
    发表于 12-12 15:50 0次下载

    基于标签主题的协同过滤推荐算法研究

    传统基于标签的推荐算法仅考虑用户的评分信息,导致推荐准确度不高。为解决该问题,提出一种改进的协同过滤推荐算法。对用户一标签矩阵、资源一标签矩阵进行潜在Dirichlet分布建模,发掘推荐系统中的潜在
    发表于 03-07 13:58 0次下载
    基于标签主题的<b class='flag-5'>协同过滤</b>推荐算法研究

    一种协同过滤推荐算法

    协同过滤是至今应用最广泛的推荐技术,已经成功地应用于电子商务、网上学习和新闻媒体等工业界领域,在学术界内也一直受到大量学者关注。协同过滤推荐主要根据用户对资源的历史行为信息,寻找用户或者资源的近邻
    发表于 03-29 15:51 1次下载

    基于显式反馈的改进协同过滤算法研究

    基于显式反馈的协同过滤算法只存在3个变量,其相似度计算方法依赖用户评分数据的显式反馈行为,而未考虑现实推荐场景中存在的隐性因素影响,这决定了协同过滤算法被限制于挖掘用户及商品的偏好,而缺乏挖掘用户
    发表于 04-28 11:30 3次下载
    基于显式反馈的改进<b class='flag-5'>协同过滤</b>算法研究

    结合本体语义和用户属性的改进协同过滤算法

    传统协同过滤推荐算法在处理海量数据时存在数据稀疏性和项目长尾效应,导致推荐精度较低。针对该问题,结合本体语义和用户属性,提出一种改进的协同过滤算法。利用本体计算项目之间的语义相似度,构建项目相似
    发表于 05-25 16:24 1次下载

    PyTorch教程21.6之用于个性化排名的神经协同过滤

    电子发烧友网站提供《PyTorch教程21.6之用于个性化排名的神经协同过滤.pdf》资料免费下载
    发表于 06-06 09:30 0次下载
    PyTorch教程21.6之用于个性化排名的<b class='flag-5'>神经</b><b class='flag-5'>协同过滤</b>