推荐系统论文回顾:神经协同过滤理解与实现

2024-06-21 09:08

本文主要是介绍推荐系统论文回顾:神经协同过滤理解与实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

点击上方“AI公园”,关注公众号,选择加“星标“或“置顶”


作者:Kung-Hsiang, Huang (Steeve)

编译:ronghuaiyang

导读

今天给大家回顾一篇论文,神经协同过滤,看名字就知道,神经网络版本的协同过滤,推荐算法的经典的方法之一。

神经协同过滤(NCF)是新加坡国立大学、哥伦比亚大学、山东大学、德州农工大学于 2017 年共同发表的一篇论文。利用神经网络的灵活性、复杂性和非线性,建立了一个推荐系统。证明了传统的推荐系统矩阵分解是神经协同过滤的一个特例。此外,它还表明 NCF 在两个公共数据集中的表现优于最先进的模型。本文将解释 NCF 的概念,并演示如何在 Pytorch 中实现它。

必备知识点

在深入研究论文之前,你应该知道什么是推荐系统,以及一些基本的推荐系统。

矩阵分解

我们从矩阵分解开始。它将效用矩阵分解为两个子矩阵。在预测过程中,我们将两个子矩阵相乘来重建预测的效用矩阵。对效用矩阵进行因式分解,使两者相乘的损失与真实效用矩阵的损失最小化。一个常用的损失函数是均方误差。

本质上,每个用户和物品都被投射到潜空间中,由潜向量表示。两个潜向量越相似,对应的用户偏好越相关。由于我们将效用矩阵分解成相同的潜在空间,所以我们可以用余弦相似度或点积来度量任意两个潜向量的相似度。实际上,每个用户/物品的预测是通过对应的潜向量的点积计算出来的。

预测等于潜向量的内积

然而,本文认为点积限制了用户和物品潜向量的表达。让我们考虑下面的情况。我们首先关注效用矩阵的前三行。

设 S{x,y}表示用户 x 与用户 y 之间的相似度,通过计算用户 1、2、3 之间的余弦相似度,可知 S{2, 3} > S{1, 2} > S{1, 3}。在不失一般性的情况下,我们将用户映射到一个二维的潜空间,如下所示。

现在,我们考虑用户 4。通过与其他算法的相似性比较,我们得到了 S{1,4} > S{3,4} > S{2,4}。但是,无论我们把潜向量 P4 放在 P1 的左右,它都必然会比 P3 更接近 P2。

因此,这个例子显示了内积在充分模拟用户和项目在潜在空间中的交互方面的局限性。

神经协同过滤

本文提出了如下图所示的神经协同滤波方法。在输入层,用户和物品是独热编码的。然后,通过相应的嵌入层映射到隐藏空间。神经网络 FC 层可以是任何类型的神经元连接。例如,多层感知器可以放在这里。该模型具有神经 CF 层的复杂连接和非线性,能够较好地估计潜空间中用户与物品之间的复杂交互作用。

NCF结构

那么,NCF 是如何成为泛化版本的矩阵分解的呢?让我在下图中给你展示。我们首先用乘法层替换神经 CF 层,乘法层对两个输入执行元素级的乘法。然后利用线性激活函数,将乘法层到输出层的权值设为 K×1 维的固定单位矩阵(全一矩阵)。

然后,我们有以下方程。

未被发现的交互 ŷ_ui 表示预测值(u, i)进入了重建的效用矩阵。L 为线性激活函数,⊙ 为元素乘操作。p_u 和 q_i 分别是用户和项目的潜在向量,J 是维数(K,1)的单位矩阵。因为 J 是一个单位矩阵,所以在这个线性函数里面变成了潜向量 p_u 和 q_i 的内积。此外,由于线性函数的输入和输出是相同的,所以可以归结为最后一行。预测标签是对应用户和物品的潜向量的内积。这个方程与矩阵分解部分所示的方程相同。从而证明了矩阵分解是 NCF 的一个特例。

NeuMF

为了引入额外的非线性能力,提出的最终模型 NeuMF 除了广义矩阵分解(GMP)层外,还包括一个多层感知器(MLP)模块。

GMF 和 MLP 模块的输出连接到 sigmoid 激活输出层。

性能

本文对 NCF 模型和其他模型进行了评价。也就是说,每个用户的最后一次交互被保留下来进行评估。考虑两个评估指标,命中率@10 和 NDCG@10。命中率@K 表示每个用户获得 10 个推荐的预测命中率。假设我们为每个用户推荐 10 个项目,10 个用户中的 4 个与我们推荐的项目进行交互,然后点击 Ratio@10=0.4。另一方面,NDCG 可以被看作是命中率的扩展,它还考虑了命中的顺序。这意味着如果你的命中发生在较高的推荐上,NDCG 将更高。

性能比较如下图所示。在所有情况下,NeuMF 的表现都优于其他模型。

此外,本文还证明了对 NeuMF 各个模块进行预训练的有效性。分别训练 GMF 和 MLP 后,将训练后的 GMF 和 MLP 的权重设置为 NeuMF 的初始化。

实现

在本节中,我将向你展示如何在 Pytorch 中轻松实现 NeuMF。你只需为每个模型实现两个函数,__init__函数指定模型结构,而forward()函数定义如何将输入张量进行前向传播。

def __init__(self, config):super(NeuMF, self).__init__()#mf partself.embedding_user_mf = torch.nn.Embedding(num_embeddings=self.num_users, embedding_dim=self.latent_dim_mf)self.embedding_item_mf = torch.nn.Embedding(num_embeddings=self.num_items, embedding_dim=self.latent_dim_mf)#mlp partself.embedding_user_mlp = torch.nn.Embedding(num_embeddings=self.num_users, embedding_dim=self.latent_dim_mlp)self.embedding_item_mlp = torch.nn.Embedding(num_embeddings=self.num_items, embedding_dim=self.latent_dim_mlp)self.fc_layers = torch.nn.ModuleList()for idx, (in_size, out_size) in enumerate(zip(config['layers'][:-1], config['layers'][1:])):self.fc_layers.append(torch.nn.Linear(in_size, out_size))self.logits = torch.nn.Linear(in_features=config['layers'][-1] + config['latent_dim_mf']  , out_features=1)self.sigmoid = torch.nn.Sigmoid()

研究结果表明,采用单独的嵌入层,MLP 和 GMF 的嵌入效果更好。因此,我们为这两个模块定义了指定的嵌入层。此外,ModuleList 还用于构建多层感知器。

def forward(self, user_indices, item_indices, titles):user_embedding_mlp = self.embedding_user_mlp(user_indices)item_embedding_mlp = self.embedding_item_mlp(item_indices)user_embedding_mf = self.embedding_user_mf(user_indices)item_embedding_mf = self.embedding_item_mf(item_indices)#### mf partmf_vector =torch.mul(user_embedding_mf, item_embedding_mf)mf_vector = torch.nn.Dropout(self.config.dropout_rate_mf)(mf_vector)#### mlp partmlp_vector = torch.cat([user_embedding_mlp, item_embedding_mlp], dim=-1)  # the concat latent vectorfor idx, _ in enumerate(range(len(self.fc_layers))):mlp_vector = self.fc_layers[idx](mlp_vector "idx")mlp_vector = torch.nn.ReLU()(mlp_vector)mlp_vector = torch.nn.Dropout(self.config.dropout_rate_mlp)(mlp_vector)vector = torch.cat([mlp_vector, mf_vector], dim=-1)logits = self.logits(vector)output = self.sigmoid(logits)return output

forward()函数相当简单。我们只是让用户和物品索引在定义的网络中流动。特别要注意的是,我在 GMP 和 MLP 模块的末尾添加了 Dropout 层,因为我发现这有助于网络的正则化和性能的提高。

—END—

英文原文:https://towardsdatascience.com/paper-review-neural-collaborative-filtering-explanation-implementation-ea3e031b7f96

请长按或扫描二维码关注本公众号

喜欢的话,请给我个好看吧

这篇关于推荐系统论文回顾:神经协同过滤理解与实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1080799

相关文章

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

Python实现字典转字符串的五种方法

《Python实现字典转字符串的五种方法》本文介绍了在Python中如何将字典数据结构转换为字符串格式的多种方法,首先可以通过内置的str()函数进行简单转换;其次利用ison.dumps()函数能够... 目录1、使用json模块的dumps方法:2、使用str方法:3、使用循环和字符串拼接:4、使用字符

深入理解Mysql OnlineDDL的算法

《深入理解MysqlOnlineDDL的算法》本文主要介绍了讲解MysqlOnlineDDL的算法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小... 目录一、Online DDL 是什么?二、Online DDL 的三种主要算法2.1COPY(复制法)

Linux下利用select实现串口数据读取过程

《Linux下利用select实现串口数据读取过程》文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设... 目录示例代码(使用select实现)代码解释总结在 linux 系统里,我们可以借助 select、

Linux挂载linux/Windows共享目录实现方式

《Linux挂载linux/Windows共享目录实现方式》:本文主要介绍Linux挂载linux/Windows共享目录实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录文件共享协议linux环境作为服务端(NFS)在服务器端安装 NFS创建要共享的目录修改 NFS 配

通过React实现页面的无限滚动效果

《通过React实现页面的无限滚动效果》今天我们来聊聊无限滚动这个现代Web开发中不可或缺的技术,无论你是刷微博、逛知乎还是看脚本,无限滚动都已经渗透到我们日常的浏览体验中,那么,如何优雅地实现它呢?... 目录1. 早期的解决方案2. 交叉观察者:IntersectionObserver2.1 Inter