前置知识:
矩阵分解MF: 把user–item交互矩阵分为两个子矩阵,用两个子矩阵相乘来重构关联矩阵,优化目标是使重构矩阵和真实矩阵之间的误差最小。常用的损失函数是均方误差。通过这种方式得到二者的向量学习到用户商品潜在的关联信息。
NCF
NCF通过对用户和商品取embedding,对embedding进行交互,通过Neural CF Layers(若干全连接层),通过Output Layer得到最终的结果,其创新之处在于利用神经网络去拟合,逼近真实分布,取代了之前MF使用的内积的操作,对高维特征的学习能力更强
Input Layer:
输入层是根据用户、项目的ID顺序得到的独热编码,作为输入向量进行模型
Embedding layer:
将稀疏的用户和物品向量映射为稠密的embedding向量
Neural CF layer 和 output layer:
将用户embedding和商品embedding拼接起来后通过若干全连接层得到最终的结果
文中给了三个NCF实例
- Generalized Matrix Factorization (GMF)
- Multi-Layer Perceptron (MLP)
- Fusion of GMF and MLP
模型整体结构如下:
与前文的区别是,GMF Layer 为用户和商品embedding向量的内积,将GMF Layer 与 MLP的结果拼接起来得到最终通过激活函数得到最终的结果
Fusion of GMF and MLP
简单做法:共用一个embedding层
不足:针对一些数据集,两种模型的最优embedding size相差较多,这样混合模型的效果就会受影响
本文:GMF和MLP使用不同的embedding
输出层:
本文中这个系数取0.5
GMF
从这里可以看出当a_out设置为等值函数f(x) = x,h设为都为1的向量,那么GMF模型就会退化变成MF模型,所以NCF模型可以泛化MF模型,MF模型是NCF模型基于线性关系的一个特例。
MLP
公式如上文Neural CF layer 和 output layer
NCF模型基于paddle的代码实现
class NCF(paddle.nn.Layer):
def __init__(self,
embedding_dim = 16,
vocab_map = None,
loss_fun = 'nn.BCELoss()'):
super(NCF, self).__init__()
self.embedding_dim = embedding_dim
self.vocab_map = vocab_map
self.loss_fun = eval(loss_fun) # self.loss_fun = paddle.nn.BCELoss()
self.user_emb_layer = nn.Embedding(self.vocab_map['user_id'],
self.embedding_dim)
self.item_emb_layer = nn.Embedding(self.vocab_map['item_id'],
self.embedding_dim)
self.mlp = nn.Sequential(
nn.Linear(2*self.embedding_dim,self.embedding_dim),
nn.ReLU(),
nn.BatchNorm1D(self.embedding_dim),
nn.Linear(self.embedding_dim,1),
nn.Sigmoid()
)
def forward(self,data):
user_emb = self.user_emb_layer(data['user_id']) # [batch,emb]
item_emb = self.item_emb_layer(data['item_id']) # [batch,emb]
mlp_input = paddle.concat([user_emb, item_emb],axis=-1).squeeze(1)
y_pred = self.mlp(mlp_input)
if 'label' in data.keys():
loss = self.loss_fun(y_pred.squeeze(),data['label'])
output_dict = {'pred':y_pred,'loss':loss}
else:
output_dict = {'pred':y_pred}
return output_dict