阅读时间

大约10分钟。

Neural Collaborative Filterring

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的长度和神经网络的层数是可以提升训练效果的。

NCF实战

本文的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.elu

if 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_logits

if 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 0

def 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,最好能够手敲一遍来理解其原理哟!

参考文章

https://www.comp.nus.edu.sg/~xiangnan/papers/ncf.pdf
https://www.cnblogs.com/HolyShine/p/6728999.html

原文链接:https://mp.weixin.qq.com/s/vXF3gJT_8LQOa67I7vvI5Q

查阅更为简洁方便的分类文章以及最新的课程、产品信息,请移步至全新呈现的“LeadAI学院官网”:

www.leadai.org

请关注人工智能LeadAI公众号,查看更多专业文章

大家都在看

LSTM模型在问答系统中的应用

基于TensorFlow的神经网络解决用户流失概览问题

最全常见算法工程师面试题目整理(一)

最全常见算法工程师面试题目整理(二)

TensorFlow从1到2 | 第三章 深度学习革命的开端:卷积神经网络

装饰器 | Python高级编程

今天不如来复习下Python基础

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

  1. 推荐系统遇上深度学习,9篇阿里推荐论文汇总!

    作者 | 石晓文 转载自小小挖掘机(ID: wAIsjwj) 业界常用的推荐系统主要分为两个阶段,召回阶段和精排阶段,当然有时候在最后还会接一些打散或者探索的规则,这点咱们就不考虑了. 前面九篇文章中 ...

  2. 推荐系统遇上深度学习(三十九)-推荐系统中召回策略演进!

    推荐系统中的核心是从海量的商品库挑选合适商品最终展示给用户.由于商品库数量巨大,因此常见的推荐系统一般分为两个阶段,即召回阶段和排序阶段.召回阶段主要是从全量的商品库中得到用户可能感兴趣的一小部分候选 ...

  3. 推荐系统遇上深度学习(八十七)-[阿里]基于搜索的用户终身行为序列建模

    本文介绍的论文是<Search-based User Interest Modeling with Lifelong Sequential Behavior Data for Click-Thr ...

  4. 知识图谱论文阅读(八)【转】推荐系统遇上深度学习(二十六)--知识图谱与推荐系统结合之DKN模型原理及实现

    学习的博客: 推荐系统遇上深度学习(二十六)–知识图谱与推荐系统结合之DKN模型原理及实现 知识图谱特征学习的模型分类汇总 知识图谱嵌入(KGE):方法和应用的综述 论文: Knowledge Gra ...

  5. 推荐系统遇上深度学习(九十二)-[腾讯]RecSys2020最佳长论文-多任务学习模型PLE

    今天介绍的是腾讯提出的一种新的多任务学习个性化推荐模型,该论文荣获了RecSys2020最佳长论文奖,一起来学习下! 1.背景 多任务学习通过在一个模型中同时学习多个不同的目标,如CTR和CVR,最近 ...

  6. 【回顾】推荐系统中基于深度学习的混合协同过滤模型

    近些年,深度学习在语音识别.图像处理.自然语言处理等领域都取得了很大的突破与成就.相对来说,深度学习在推荐系统领域的研究与应用还处于早期阶段. 携程在深度学习与推荐系统结合的领域也进行了相关的研究与应 ...

  7. 推荐系统遇上深度学习(二十二):DeepFM升级版XDeepFM模型强势来袭!

    今天我们要学习的模型是xDeepFM模型,论文地址为:https://arxiv.org/abs/1803.05170.文中包含我个人的一些理解,如有不对的地方,欢迎大家指正!废话不多说,我们进入正题 ...

  8. 推荐系统遇上深度学习(十五)--强化学习在京东推荐中的探索

    强化学习在各个公司的推荐系统中已经有过探索,包括阿里.京东等.之前在美团做过的一个引导语推荐项目,背后也是基于强化学习算法.本文,我们先来看一下强化学习是如何在京东推荐中进行探索的. 本文来自于pap ...

  9. 推荐系统遇上深度学习(二十)--探秘阿里之完整空间多任务模型ESSM

    笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值,找寻数据的秘密,笔者认为,数据的价值不仅仅只体现在企业中,个人也可以体会到数据的魅力,用技术力量探索行为密码,让大数据 ...

最新文章

  1. PostgreSQL技术周刊第8期:用PostgreSQL 做实时高效搜索引擎
  2. VScode+SSH Remote多级连跳配置
  3. dos 删除文件夹 rd
  4. [BZOJ2095]Bridges
  5. Linux下Web网站压力测试工具Webbench
  6. git 源代码自动检查_检查提交(git log,git show)《 Nest.js 应用案例:源代码管理 》...
  7. kbengine连接mysql报错
  8. 力扣90. 子集 II(JavaScript)
  9. 高阶函数-sort()与sorted() (三分钟读懂)
  10. 实现TeX的算法:回首编程技术的过去三十年
  11. Opencv笔记(四)——绘图函数
  12. [2] ET框架初养成 mac OS第一次启动Demo
  13. Unity优化百科(UWA 博客目录)
  14. 惠普打印机显示服务器脱机,打印机脱机解决办法 HP打印机出现脱机故障的解决办法...
  15. Linux基础操作命令
  16. 站内信“数据库设计思路”
  17. Android-Handle详解
  18. XP体系正式退役 电脑迷自述我和XP那10年
  19. App测试的方法和思路有哪些?一篇文章告诉你答案
  20. 安卓日程表毕业设计源码

热门文章

  1. python用circle画多边形_pythonopencv圆、椭圆与任意多边形的绘制
  2. 2016全国计算机二级题,2016全国计算机二级考生试题及答案
  3. unix linux系统版本,怎么查看UNIX系统版本?
  4. stm32 工业按键检测_基于STM32芯片的能谱仪设计
  5. java流数据base64,Base64数据的流解码
  6. xstream不映射字段_用xstream 将xml映射为类对象遇到的问题
  7. python提供两个对象身份比较操作符_标准类型对象比较操作符
  8. oracle集,oracle(集合门类)
  9. Html input 标签
  10. 【BZOJ】1756: Vijos1083 小白逛公园(线段树)