最小推荐系统:Neural Collaborative Filtering (NeuralCF)

和自甴很熟 提交于 2020-07-25 10:31:10

推荐系统算法中有两个根本性的思路,一是用户和条目表征,二是特征交叉。在传统算法中,隐语义模型从用户和条目表征(Embedding, 或称为“嵌入”)出发,以用户表征向量与条目表征向量的乘(内积)表示用户对条目的喜好。而因子分解机(FM)则致力于解决特征交叉问题。在上一篇中的AutoRec则是首个使用深度学习从用户和条目表征的角度解决问题的方案。当然,其结构过于简单,没有应对特征交叉的能力,模型本身的非线性能力也不足。而NeuralCF[1]是第一个深度学习下的同时处理这两个问题的模型。

NeuralCF网络可以分解为两个子网络,一个被称为Generalized Matrix Factorization (GMF, 广义矩阵分解),另一个是Multi-Layer Perceptron (MLP, 多层感知机). 这两个子网络都包含用户和条目的表征部分:

embedding_user = torch.nn.Embedding(num_embeddings=num_users, embedding_dim=latent_dim)
embedding_item = torch.nn.Embedding(num_embeddings=num_items, embedding_dim=latent_dim)

自然地,就有两个问题:1)如何优化表征 以及2)如何做特征交叉。

对于问题2),是使用直接的表征向量内积来实现的:

user_embedding = embedding_user(user)
item_embedding = embedding_item(item)
element_product = torch.mul(user_embedding, item_embedding)

也就是 \phi_1\left( \mathbf{p}_u , \mathbf{q}_i \right) = \mathbf{p}_u \odot \mathbf{q}_i , 其中 \odot 为向量内积

然后使用优化方法解决问题1),这同样也包括两个部分,对于GMF部分,内积向量被输入到全连接层,然后通过softmax进行预估,即

\hat{y}_{ui}= \mathop{softmax} \left( \mathbf{h}^{\mathbf{T}} \left( \mathbf{p}_u \odot \mathbf{q}_i  \right)\right)

优化目标: \mathop{min}\sum\lVert y_{ui} -\hat{y}_{ui}  \rVert (1). 可见,单独拿出来看,GMF部分几乎完全等价于隐语义模型(LFM).

对于MLP部分,先载入GMF训练好的用户和条目表征权重,然后直接cancat用户和条目表征向量,输入多层感知机网络,使用非线性激活函数进行优化:

\mathbf{z}_1 = \phi_1\left( \mathbf{p}_u , \mathbf{q}_i \right)

\phi_2\left( \mathbf{z}_1  \right) = \mathop{relu_2}\left(  \mathbf{W}_2^T\mathbf{z}_1 + \mathbf{b}_2\right)

......

\phi_L\left( \mathbf{z}_{L-1}  \right) = \mathop{relu_L}\left(  \mathbf{W}_L^T\mathbf{z}_{L-1} + \mathbf{b}_L\right)

\hat{y}_{ui}=\sigma \left( \mathbf{h}^T\phi_L\left( \mathbf{z}_{L-1}  \right) \right)

user_embedding = embedding_user(user)
item_embedding = embedding_item(item)
vector = torch.cat([user_embedding, item_embedding], dim=-1)
#载入预训练表征权重:
embedding_user.weight.data = gmf_model.embedding_user.weight.data
embedding_item.weight.data = gmf_model.embedding_item.weight.data

优化目标同(1)式。

以上按顺序分别训练GMF和MLP, 然后载入它们的权重,进行整体网络的优化。在这一步的优化过程中,GMF和MLP部分的用户和条目表征分开:

这一点从代码上理解更直观:

embedding_user_mlp = torch.nn.Embedding(num_embeddings=num_users, embedding_dim=latent_dim_mlp)
embedding_item_mlp = torch.nn.Embedding(num_embeddings=num_items, embedding_dim=latent_dim_mlp)
embedding_user_mf = torch.nn.Embedding(num_embeddings=num_users, embedding_dim=latent_dim_mf)
embedding_item_mf = torch.nn.Embedding(num_embeddings=num_items, embedding_dim=latent_dim_mf)
#载入预训练表征权重:
embedding_user_mlp.weight.data = mlp_model.embedding_user.weight.data
embedding_item_mlp.weight.data = mlp_model.embedding_item.weight.data
embedding_user_mf.weight.data = gmf_model.embedding_user.weight.data
embedding_item_mf.weight.data = gmf_model.embedding_item.weight.data
#forward:
user_embedding_mlp = embedding_user_mlp(user)
item_embedding_mlp = embedding_item_mlp(item)
user_embedding_mf = embedding_user_mf(user)
item_embedding_mf = embedding_item_mf(item)
mlp_vector = torch.cat([user_embedding_mlp, item_embedding_mlp], dim=-1)
mf_vector =torch.mul(user_embedding_mf, item_embedding_mf)
for idx, _ in enumerate(range(len(self.fc_layers))):
    mlp_vector = self.fc_layers[idx](mlp_vector)
    mlp_vector = torch.nn.ReLU()(mlp_vector)

vector = torch.cat([mlp_vector, mf_vector]

上面部分也可以不使用GMF和MLP的预训练权重,而直接从头开始训练。当然,也可以对GMF和MLP部分使用同样的用户和条目表征。不过这样做的预测效果更差,这也是符合直觉和逻辑的。

以今天视角来看,NeuralCF所使用的一些操作不过是网络设计中的常规做法。但它实际上是一个深度学习框架下基础性推荐算法结构,对用户和条目表征和特征交叉都做出了针对性的处理,以此为基础可以做出很多优化。

参考

  1. ^He, X., Liao, L., Zhang, H., Nie, L., Hu, X., & Chua, T. (2017). Neural Collaborative Filtering. the web conference.
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!