NCF知识总结代码实现

  • 1. NeuralCF 模型的结构
    • 1.1 回顾CF和MF
    • 1.2 NCF 模型结构
    • 1.3 NeuralCF 模型的扩展---双塔模型
  • 2. NCF代码实现
    • 2.1 tensorflow
    • 2.2 pytorch

NeuralCF:如何用深度学习改造协同过滤?

随着技术的发展,协同过滤相比深度学习模型的弊端就日益显现,因为它是通过直接利用非常稀疏的共现矩阵进行预测的,所以模型的泛化能力非常弱,遇到历史行为非常少的用户,就没法产生准确的推荐结果。

虽然,可以通过矩阵分解算法增强它的泛化能力,但因为矩阵分解是利用非常简单的内积方式来处理用户向量和物品向量的交叉问题的,所以,它的拟合能力也比较弱。

2017 年,新加坡国立的研究者就使用深度学习网络来改进了传统的协同过滤算法,取名 NeuralCF(神经网络协同过滤)。NeuralCF 大大提高了协同过滤算法的泛化能力和拟合能力,让这个经典的推荐算法又重新在深度学习时代焕发生机。

1. NeuralCF 模型的结构

1.1 回顾CF和MF

先来简单回顾一下协同过滤和矩阵分解的原理。协同过滤是利用用户和物品之间的交互行为历史,构建出一个像图左一样的共现矩阵。在共现矩阵的基础上,利用每一行的用户向量相似性,找到相似用户,再利用相似用户喜欢的物品进行推荐。
矩阵分解则进一步加强了协同过滤的泛化能力,它把协同过滤中的共现矩阵分解成了用户矩阵和物品矩阵,从用户矩阵中提取出用户隐向量,从物品矩阵中提取出物品隐向量,再利用它们之间的内积相似性进行推荐排序。

如果用神经网络的思路来理解矩阵分解,它的结构图就是图 2 这样的。

图 2 中的输入层是由用户 ID 和物品 ID 生成的 One-hot 向量,Embedding 层是把 One-hot 向量转化成稠密的 Embedding 向量表达,这部分就是矩阵分解中的用户隐向量和物品隐向量。输出层使用了用户隐向量和物品隐向量的内积作为最终预测得分,之后通过跟目标得分对比,进行反向梯度传播,更新整个网络。

把矩阵分解神经网络化之后,把它跟 Embedding+MLP 以及 Wide&Deep 模型做对比,我们可以一眼看出网络中的薄弱环节:矩阵分解在 Embedding 层之上的操作好像过于简单了,就是直接利用内积得出最终结果。这会导致特征之间还没有充分交叉就直接输出结果,模型会有欠拟合的风险。

1.2 NCF 模型结构

针对矩阵分解的弱点,NeuralCF 对矩阵分解进行了改进,它的结构图是图 3 这样的。


NeuralCF 用一个多层的神经网络替代掉了原来简单的点积操作。这样就可以让用户和物品隐向量之间进行充分的交叉,提高模型整体的拟合能力。

1.3 NeuralCF 模型的扩展—双塔模型

NeuralCF 的模型结构之中,蕴含了一个非常有价值的思想,就是我们可以把模型分成用户侧模型和物品侧模型两部分,然后用互操作层把这两部分联合起来,产生最后的预测得分。

这里的用户侧模型结构和物品侧模型结构,可以是简单的 Embedding 层,也可以是复杂的神经网络结构,最后的互操作层可以是简单的点积操作,也可以是比较复杂的 MLP 结构。但只要是这种物品侧模型 + 用户侧模型 + 互操作层的模型结构,我们把它统称为“双塔模型”结构。


对于 NerualCF 来说,它只利用了用户 ID 作为“用户塔”的输入特征,用物品 ID 作为“物品塔”的输入特征。事实上,我们完全可以把其他用户和物品相关的特征也分别放入用户塔和物品塔,让模型能够学到的信息更全面。比如说,YouTube 在构建用于召回层的双塔模型时,就分别在用户侧和物品侧输入了多种不同的特征。


YouTube 召回双塔模型的用户侧特征包括了用户正在观看的视频 ID、频道 ID(图中的 seed features)、该视频的观看数、被喜欢的次数,以及用户历史观看过的视频 ID 等等。物品侧的特征包括了候选视频的 ID、频道 ID、被观看次数、被喜欢次数等等。在经过了多层 ReLU 神经网络的学习之后,双塔模型最终通过 softmax 输出层连接两部分,输出最终预测分数。

这个双塔模型相比Embedding MLP 和 Wide&Deep 有优势:在实际工作中,双塔模型最重要的优势就在于它易上线、易服务。

注意看物品塔和用户塔最顶端的那层神经元,那层神经元的输出其实就是一个全新的物品 Embedding 和用户 Embedding。拿图 4 来说,物品塔的输入特征向量是 x,经过物品塔的一系列变换,生成了向量 u(x),那么这个 u(x) 就是这个物品的 Embedding 向量。同理,v(y) 是用户 y 的 Embedding 向量,这时,我们就可以把 u(x) 和 v(y) 存入特征数据库,这样一来,线上服务的时候,我们只要把 u(x) 和 v(y) 取出来,再对它们做简单的互操作层运算就可以得出最后的模型预估结果了。

所以使用双塔模型,不用把整个模型都部署上线,只需要预存物品塔和用户塔的输出,以及在线上实现互操作层就可以了。如果这个互操作层是点积操作,那么这个实现可以说没有任何难度,这是实际应用中非常容易落地的,这也正是双塔模型在业界巨大的优势所在。

2. NCF代码实现

2.1 tensorflow

NeuralCF模型部分的实现


# neural cf model arch two. only embedding in each tower, then MLP as the interaction layers
def neural_cf_model_1(feature_inputs, item_feature_columns, user_feature_columns, hidden_units):# 物品侧特征层item_tower = tf.keras.layers.DenseFeatures(item_feature_columns)(feature_inputs)# 用户侧特征层user_tower = tf.keras.layers.DenseFeatures(user_feature_columns)(feature_inputs)# 连接层及后续多层神经网络interact_layer = tf.keras.layers.concatenate([item_tower, user_tower])for num_nodes in hidden_units:interact_layer = tf.keras.layers.Dense(num_nodes, activation='relu')(interact_layer)# sigmoid单神经元输出层output_layer = tf.keras.layers.Dense(1, activation='sigmoid')(interact_layer)# 定义keras模型neural_cf_model = tf.keras.Model(feature_inputs, output_layer)return neural_cf_model

代码中定义的生成 NeuralCF 模型的函数,接收了四个输入变量。其中 feature_inputs 代表着所有的模型输入, item_feature_columns 和 user_feature_columns 分别包含了物品侧和用户侧的特征。在训练时,如果只在 item_feature_columns 中放入 movie_id ,在 user_feature_columns 放入 user_id, 就是NeuralCF的经典实现了。

通过 DenseFeatures 层创建好用户侧和物品侧输入层之后,再利用 concatenate 层将二者连接起来,然后输入多层神经网络进行训练。如果想要定义多层神经网络的层数和神经元数量,可以通过设置 hidden_units 数组来实现。

2.2 pytorch

#GMF层
class GMF(nn.Module):def __init__(self,embedding_dim):super(GMF, self).__init__()self.embedding_dim = embedding_dimself.fc = nn.Linear(self.embedding_dim,self.embedding_dim)def forward(self, user_emb, item_emb):out = self.fc(user_emb*item_emb).sigmoid()return out#MLP
class MLP_Layer(nn.Module):def __init__(self,input_dim,output_dim=None,hidden_units=[],hidden_activations="ReLU",final_activation=None,dropout_rates=0,batch_norm=False,use_bias=True):super(MLP_Layer, self).__init__()dense_layers = []if not isinstance(dropout_rates, list):dropout_rates = [dropout_rates] * len(hidden_units)if not isinstance(hidden_activations, list):hidden_activations = [hidden_activations] * len(hidden_units)hidden_activations = [set_activation(x) for x in hidden_activations]hidden_units = [input_dim] + hidden_unitsfor idx in range(len(hidden_units) - 1):dense_layers.append(nn.Linear(hidden_units[idx], hidden_units[idx + 1], bias=use_bias))if batch_norm:dense_layers.append(nn.BatchNorm1d(hidden_units[idx + 1]))if hidden_activations[idx]:dense_layers.append(hidden_activations[idx])if dropout_rates[idx] > 0:dense_layers.append(nn.Dropout(p=dropout_rates[idx]))if output_dim is not None:dense_layers.append(nn.Linear(hidden_units[-1], output_dim, bias=use_bias))if final_activation is not None:dense_layers.append(set_activation(final_activation))self.dnn = nn.Sequential(*dense_layers)  # * used to unpack listdef forward(self, inputs):return self.dnn(inputs)def set_device(gpu=-1):if gpu >= 0 and torch.cuda.is_available():os.environ["CUDA_VISIBLE_DEVICES"] = str(gpu)device = torch.device(f"cuda:{gpu}")else:device = torch.device("cpu")return devicedef set_activation(activation):if isinstance(activation, str):if activation.lower() == "relu":return nn.ReLU()elif activation.lower() == "sigmoid":return nn.Sigmoid()elif activation.lower() == "tanh":return nn.Tanh()else:return getattr(nn, activation)()else:return activationdef get_dnn_input_dim(enc_dict,embedding_dim):num_sparse = 0num_dense = 0for col in enc_dict.keys():if 'min' in enc_dict[col].keys():num_dense+=1elif 'vocab_size' in enc_dict[col].keys():num_sparse+=1return num_sparse*embedding_dim+num_densedef get_linear_input(enc_dict,data):res_data = []for col in enc_dict.keys():if 'min' in enc_dict[col].keys():res_data.append(data[col])res_data = torch.stack(res_data,axis=1)return res_data
# NCF 模型
class NCF(nn.Module):def __init__(self,embedding_dim1=16, # GMF 对应的Embedding层embedding_dim2=32, # MLP 对应的Embedding层hidden_units=[64, 32, 16],loss_fun = 'torch.nn.BCELoss()',enc_dict=None):super(NCF, self).__init__()self.embedding_dim1 = embedding_dim1 # GMF Embself.embedding_dim2 = embedding_dim2 # MLP Embself.hidden_units = hidden_unitsself.loss_fun = eval(loss_fun)self.enc_dict = enc_dict# GMFself.user_emb_layer1 = nn.Embedding(self.enc_dict['user_id']['vocab_size'],self.embedding_dim1)self.item_emb_layer1 = nn.Embedding(self.enc_dict['item_id']['vocab_size'],self.embedding_dim1)# MLPself.user_emb_layer2 = nn.Embedding(self.enc_dict['user_id']['vocab_size'],self.embedding_dim2)self.item_emb_layer2 = nn.Embedding(self.enc_dict['item_id']['vocab_size'],self.embedding_dim2)self.gmf = GMF(self.embedding_dim1)self.mlp = MLP_Layer(input_dim=self.embedding_dim2*2, hidden_units=self.hidden_units,hidden_activations='relu', dropout_rates=0)# GMF:[batch,Emb1] MLP:[batch,hidden_units[-1]]-> FC的输入维度:self.embedding_dim1 + self.hidden_units[-1]self.fc = nn.Linear(self.embedding_dim1 + self.hidden_units[-1],1)def forward(self, data):# GMFuser_emb1 = self.user_emb_layer1(data['user_id'])item_emb1 = self.item_emb_layer1(data['item_id'])# MLPuser_emb2 = self.user_emb_layer2(data['user_id'])item_emb2 = self.item_emb_layer2(data['item_id'])# GMFgmf_out = self.gmf(user_emb1, item_emb1)# MLPmlp_input = torch.cat([user_emb2,item_emb2],axis=-1)mlp_out = self.mlp(mlp_input)#输出final_input = torch.cat([gmf_out,mlp_out],axis=-1)y_pred = self.fc(final_input).sigmoid()loss = self.loss_fun(y_pred.squeeze(-1),data['label'])output_dict = {'pred':y_pred,'loss':loss}return output_dict

推荐算法——NCF知识总结代码实现相关推荐

  1. 【推荐算法】知识驱动的智能化推荐算法(附交流视频和PPT下载链接)

    今天给大家带来中国人民大学赵鑫老师在2020年5月10日举办的"推荐系统前沿进展"系列学术沙龙活动中所做的分享<知识驱动的智能化推荐算法研究进展汇报>,在本次分享中,赵 ...

  2. 阿里天池大数据之移动推荐算法大赛总结及代码全公布

    移动推荐算法比赛已经结束了一个多星期了,现在写一篇文章来回顾一下自己的参赛历程. 首先,对不了解这个比赛的同学们介绍一下这个比赛(引用自官网): 赛题简介 2014年是阿里巴巴集团移动电商业务快速发展 ...

  3. python协同过滤可以预测吗_基于用户的协同过滤推荐算法原理-附python代码实现...

    在推荐系统众多方法中,基于用户的协同过滤推荐算法是最早诞生的,原理也较为简单.该算法1992年提出并用于邮件过滤系统,两年后1994年被 GroupLens 用于新闻过滤.一直到2000年,该算法都是 ...

  4. 基于图神经网络的异构图表示学习和推荐算法研究(完整代码+数据)

    基于图神经网络的异构图表示学习和推荐算法研究.包含基于对比学习的关系感知异构图神经网络(Relation-aware Heterogeneous Graph Neural Network with C ...

  5. 【恋上数据结构】排序算法前置知识及代码环境准备

    排序准备工作 何为排序? 何为稳定性? 何为原地算法? 时间复杂度的知识 写排序算法前的准备 项目结构 Sort.java Asserts.java Integers.java Times.java ...

  6. 推荐算法工程师的成长之道

    作者 | gongyouliu 来源 | 大数据与人工智能(ID: ai-big-data) 本文,作者会基于自己的实践经验讲述推荐算法工程师的成长之道,这里的"道"有发展路径和道 ...

  7. 蚂蚁金服核心技术:百亿特征实时推荐算法揭秘

    小叽导读:文章提出一整套创新算法与架构,通过对TensorFlow底层的弹性改造,解决了在线学习的弹性特征伸缩和稳定性问题,并以GroupLasso和特征在线频次过滤等自研算法优化了模型稀疏性.在支付 ...

  8. 推荐算法工程师成长2:排序模块

    开一个系列,主题是推荐算法工程师成长路径.目标是希望填补书本上的机器学习理论与业界推荐算法工程师知识体系上的gap,了解一些业界模块的通用玩法.目标群体是针对以下用户: 有一些代码和机器学习基础,但是 ...

  9. 【推荐算法】双塔模型介绍

    双塔模型的结构不仅在推荐领域的召回和粗排环节中被广泛采用:而且在其它领域,如文档检索.问答系统等都有它的应用场景. 我们常说的双塔模型的结构,并不是一个固定不变的网络,而是一种模型构造思路:即把模型分 ...

最新文章

  1. javascrip --- 构造函数的继承
  2. LAMP搭建之三:php编译安装
  3. 如何在24行JavaScript中实现Redux
  4. linux shell的here document用法(cat EOF)
  5. 数学倒底有没有绝对的严格性和形式化?
  6. PyQt5-Label背景颜色和文本颜色的设置
  7. 动态ARP检测原理及应用
  8. 顶级域名、一级域名、二级域名、三级域名的区别
  9. 显卡排行榜天梯图2022年9月 笔记本显卡排行榜天梯图2022
  10. mysql怎么画继承_UML类图(上):类、继承和实现
  11. 盘点世界十大软件外包公司排名是哪些公司
  12. 中国方言地图的总结与展望
  13. Linux下sqlite3移植与编程
  14. 微信公众平台——用户管理
  15. 简单的实现app界面劫持和后台监控
  16. idea连接数据库Server returns invalid timezone. Go to ‘Advanced‘ tab and set ‘serverTimezon‘
  17. 奥维没有了gg地图后,正确的打开方式
  18. 南方科技大学深港微电子学院夏令营
  19. Q1营收超预期但仍呈负增长,携程究竟在哪里“丢了分”?
  20. 深度GHOST XP快速装机版V6.5

热门文章

  1. 车牌识别停车系统无法链接服务器,停车场车牌识别系统识别不了的处理方法有哪些?...
  2. matlab多种分配方案_分配问题匈牙利算法的Matlab实现
  3. 智能工厂:怎样服装ERP软件的价格距离这么大?
  4. 2021年中国海图记录器市场趋势报告、技术动态创新及2027年市场预测
  5. 可视化地图__公司收集
  6. 中国电信超 2 亿用户信息被卖,售价低至 0.01 元/条!个人信息安全何在?
  7. Android7.1 Kyguard界面灭屏时间分析
  8. 致远SPM解决方案之公务车辆管理
  9. 机器学习面试1000题 1 - 255
  10. Python面向对象,从农药到吃鸡 bilibili视频笔记