NewBeeNLP原创出品 公众号专栏作者@上杉翔二 悠闲会 · 信息检索
本篇文章follow一些 Graph in Rec 的文章,以前博主整理过的系列可以见:
来自WWW2021的文章,探讨推荐系统中的过平滑问题。从何向南大佬的NGCF开始一直强调的就是高阶邻居的协作信号是可以学习良好的用户和项目嵌入。虽然GCN容易「过平滑」(即叠加更多层时,节点嵌入变得更加相似,最终无法区分,导致性能下降),也可以用一些方法来缓解如LightGCN和LR-GCN模型,但作者认为他们忽略了一个很重要的问题:「用户的嵌入学习也可以涉及到与用户没有共同兴趣的高阶邻域用户。」 所以多层图卷积会使不同兴趣的用户具有相似的嵌入性。
应对这一点,作者提出一种新的「兴趣感知消息传递GCN (IMP-GCN)」,该模型将用户及其交互项分组到不同的子图中,在子图中进行高阶图卷积 。而这里的子图是由具有相似兴趣的用户及其交互项组成的。
子图由无监督的子图生成模块生成,该模块集成了用户特征和图结构,以识别具有相似兴趣的用户,然后通过保留这些用户及其交互项来构造子图。为此,其可以过滤掉在高阶图卷积运算中的负信息传播,从而通过叠加更多的图卷积层来保持用户的唯一性。
模型图如上,简单来看就是一阶和多阶子图的融合,最后combination之后做预测。
最后经过图卷积后item?的最终表示是它在不同子图s中学习到的嵌入的组合:
将获得的用户特征转换为具有2层神经网络去分类即可:
Uo即表示了用户所属的组/子图。值得注意的是,这里将用户分为不同的组是一种无监督的方法,不需要真实标签。此处博主个人的理解是,有相似embedding的用户将生成的预测向量,所以会被归类为同一组。
补一下前文说到的LR-GCN,来自AAAI2020。LR-GCN也始于两个问题:
针对这两个问题,作者提出残差图卷积方法重新研究了基于图的CF模型(Linear Residual Graph Convolutional Collaborative Filtering)。模型图如上,其实解决方案已经十分的明显了,在每一步的传播中进行残差连接,并且没有非线性...
其中d是度,R是邻居。
简要看看关键代码吧:一些注意点已经备注在代码里
def forward(self, user, item_i, item_j):
#得到embedding
users_embedding=self.embed_user.weight
items_embedding=self.embed_item.weight
#直接开始gcn,这里直接实现上面的那个公式,1无非线性2残差也是在这里一起做的,先user,再item。
#然后尝试搭建多层的GCN。
gcn1_users_embedding = (torch.sparse.mm(self.user_item_matrix, items_embedding) + users_embedding.mul(self.d_i_train))#*2. #+ users_embedding
gcn1_items_embedding = (torch.sparse.mm(self.item_user_matrix, users_embedding) + items_embedding.mul(self.d_j_train))#*2. #+ items_embedding
gcn2_users_embedding = (torch.sparse.mm(self.user_item_matrix, gcn1_items_embedding) + gcn1_users_embedding.mul(self.d_i_train))#*2. + users_embedding
gcn2_items_embedding = (torch.sparse.mm(self.item_user_matrix, gcn1_users_embedding) + gcn1_items_embedding.mul(self.d_j_train))#*2. + items_embedding
gcn3_users_embedding = (torch.sparse.mm(self.user_item_matrix, gcn2_items_embedding) + gcn2_users_embedding.mul(self.d_i_train))#*2. + gcn1_users_embedding
gcn3_items_embedding = (torch.sparse.mm(self.item_user_matrix, gcn2_users_embedding) + gcn2_items_embedding.mul(self.d_j_train))#*2. + gcn1_items_embedding
# gcn4_users_embedding = (torch.sparse.mm(self.user_item_matrix, gcn3_items_embedding) + gcn3_users_embedding.mul(self.d_i_train))#*2. + gcn1_users_embedding
# gcn4_items_embedding = (torch.sparse.mm(self.item_user_matrix, gcn3_users_embedding) + gcn3_items_embedding.mul(self.d_j_train))#*2. + gcn1_items_embedding
gcn_users_embedding= torch.cat((users_embedding,gcn1_users_embedding,gcn2_users_embedding,gcn3_users_embedding),-1)#+gcn4_users_embedding
gcn_items_embedding= torch.cat((items_embedding,gcn1_items_embedding,gcn2_items_embedding,gcn3_items_embedding),-1)#+gcn4_items_embedding#
#然后实现bpr的loss计算,即先得到i和j
user = F.embedding(user,gcn_users_embedding)
item_i = F.embedding(item_i,gcn_items_embedding)
item_j = F.embedding(item_j,gcn_items_embedding)
# # pdb.set_trace()
prediction_i = (user * item_i).sum(dim=-1)
prediction_j = (user * item_j).sum(dim=-1)
#再相减,补上一个L2
# loss=-((rediction_i-prediction_j).sigmoid())**2#self.loss(prediction_i,prediction_j)#.sum()
l2_regulization = 0.01*(user**2+item_i**2+item_j**2).sum(dim=-1)
# l2_regulization = 0.01*((gcn1_users_embedding**2).sum(dim=-1).mean()+(gcn1_items_embedding**2).sum(dim=-1).mean())
loss2= -((prediction_i - prediction_j).sigmoid().log().mean())
# loss= loss2 + l2_regulization
loss= -((prediction_i - prediction_j)).sigmoid().log().mean() +l2_regulization.mean()
# pdb.set_trace()
return prediction_i, prediction_j,loss,loss2