协同过滤与NeuralCF
- 协同过滤是推荐算法中最经典的,但是随着技术的发展,协同过滤相比深度学习模型的弊端就日益显现出来了,因为它是通过直接利用非常稀疏的共现矩阵进行预测的,所以模型的泛化能力非常弱,遇到历史行为非常少的用户,就没法产生准确的推荐结果了。虽然可以通过矩阵分解算法可以提高泛化能力。但是矩阵分解中的计算主要是以内积的方式对用户及物品向量进行交叉计算,所以拟合能力较弱。
- 2017 年,新加坡国立的研究者就使用深度学习网络来改进了传统的协同过滤算法,取名NeuralCF(神经网络协同过滤)。NeuralCF大大提高了协同过滤算法的泛化能力和拟合能力。
NeuralCF模型的结构
-
协同过滤是利用用户和物品之间的交互行为历史,构建出一个像左图一样的共现矩阵。在共现矩阵的基础上,利用每一行的用户向量相似性,找到相似用户,再利用相似用户喜欢的物品进行推荐。
-
矩阵分解则进一步加强了协同过滤的泛化能力,它把协同过滤中的共现矩阵分解成了用户矩阵和物品矩阵,从用户矩阵中提取出用户隐向量,从物品矩阵中提取出物品隐向量,再利用它们之间的内积相似性进行推荐排序。矩阵分解神经网络化之后得到下图:
-
把矩阵分解神经网络化之后,把它跟Embedding+MLP和Wide&Deep模型做对比,就会发现它的弱点:矩阵分解在Embedding层之上操作过于简单,就是直接利用内积得出最终结果。这会导致特征之间还没有充分交叉就直接输出结果,模型会有欠拟合的风险。NeuralCF对矩阵分解进行了改进,它的结构图如下:
-
如上图所示NeuralCF用一个多层的神经网络替代掉了原来简单的点积操作。这样就可以让用户和物品隐向量之间进行充分的交叉,提高模型整体的拟合能力。
NeuralCF双塔模型
-
NeuralCF 的模型结构之中,可以把模型分成用户侧模型和物品侧模型两部分,然后用互操作层把这两部分联合起来,产生最后的预测得分。
-
这里的用户侧模型结构和物品侧模型结构,可以是Embedding层,也可以是神经网络结构,最后的互操作层可以是点积操作,也可以是MLP结构。但只要是这种物品侧模型 + 用户侧模型 + 互操作层的模型结构,我们把它统称为“双塔模型”结构。如下图所示:
-
双塔模型相比于Embedding MLP和Wide&Deep的优势是,它易上线、易服务。因为物品塔的输入特征向量是x,经过变换生成了向量u(x),u(x)物品的Embedding向量。同理,v(y)是用户y的Embedding 向量,将u(x)和v(y)存入特征数据库,在做线上服务的时候,只要把 u(x) 和 v(y) 取出来,再对它们做简单的互操作层运算就可以得出最后的模型预估结果。
-
NerualCF只利用了用户ID作为“用户塔”的输入特征,物品ID作为“物品塔”的输入特征。其实完全可以把其他用户和物品相关的特征也分别放入用户塔和物品塔,让模型能够学到的信息更全面。例如YouTube构建用于召回层的双塔模型时,就分别在用户侧和物品侧输入了多种不同的特征,如下图所示:
-
YouTube 召回双塔模型的用户侧特征包括了用户正在观看的视频 ID、频道 ID(图中的 seed features)、该视频的观看数、被喜欢的次数,以及用户历史观看过的视频ID等等。物品侧的特征包括了候选视频的 ID、频道 ID、被观看次数、被喜欢次数等等。在经过了多层ReLU神经网络的学习之后,双塔模型最终通过softmax输出层连接两部分,输出最终预测分数。如下图所示:
NeuralCF的TensorFlow实现
经典NeuralCF相关代码
- 读取数据,定义特征,训练模型等实现代码之前已经实现,本章节主要是从模型定义来进行代码实现:
- 生成NeuralCF模型的函数,共有四个输入分别是:
- feature_inputs(所有模型的输入);
- item_feature_columns(物品侧特征);
- user_feature_columns(用户侧特征);
- hidden_units(隐藏层单元)
- DenseFeatures层创建好用户侧和物品侧输入层之后,再利用concatenate层将二者连接起来,然后输入多层神经网络进行训练。如果想要定义多层神经网络的层数和神经元数量,可以设置hidden_units数组。具体代码如下:
# 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相关代码
- 多层神经网络操作放到了物品塔和用户塔内部,让塔内的特征进行充分交叉,最后使用内积层作为物品塔和用户塔的交互层。具体代码如下:
def neural_cf_model_2(feature_inputs, item_feature_columns, user_feature_columns, hidden_units):
# 物品侧输入特征层
item_tower = tf.keras.layers.DenseFeatures(item_feature_columns)(feature_inputs)
# 物品塔结构
for num_nodes in hidden_units:
item_tower = tf.keras.layers.Dense(num_nodes, activation='relu')(item_tower)
# 用户侧输入特征层
user_tower = tf.keras.layers.DenseFeatures(user_feature_columns)(feature_inputs)
# 用户塔结构
for num_nodes in hidden_units:
user_tower = tf.keras.layers.Dense(num_nodes, activation='relu')(user_tower)
# 使用内积操作交互物品塔和用户塔,产生最后输出
output = tf.keras.layers.Dot(axes=1)([item_tower, user_tower])
# 定义keras模型
neural_cf_model = tf.keras.Model(feature_inputs, output)
return neural_cf_model