XSimGCL: Towards Extremely Simple GraphContrastive Learning for Recommendation 论文+代码解读
及时跟进接续上一篇的工作,在对比学习的过程中, 图的增强仅起到次要作用. 通过优化CL损失函数, 能够获得更为均衡的表示分布. 在表示空间中引入有向随机噪声后, 并结合多样的数据增强策略与对比机制, 所提出的方法显著提升了推荐系统的性能水平.
SimGCL:是否有必要实施图增强?一种高效的图对比学习方法用于推荐系统 论文代码解析_只想做个咸鱼的博客-博客
一、前言
这一篇是上一篇的延续,更进一步简化,大致差不多,细节做了更好的改进

我们开发了一种高度简洁的图对比学习算法(XSimGCL),旨在为推荐系统提供高效的解决方案。该算法舍弃了传统的无效图增强技术,并采用了一种高效、可靠的噪声增强机制来生成两个互补的表征。
1、介绍
由于CL通过从未标记的数据提取通用特征的能力成为解决数据稀疏性问题的关键手段之一,从而推动了推荐技术的进步.该方法主要包含两个步骤:首先,通过应用结构破坏手段(如边/节点以特定比例被移除)来增强原始用户-项目二分图;接着,在联合学习框架下最大化来自各次图形增强所提取表示的一致性.

问题1: 基于CL的推荐模型真的需要图形增强吗?
在联合优化过程中使用的对比度损失函数InfoNCE具有关键的重要性,在于其相较于通过图形增强技术提升性能而言,在不考虑图形增强的情况下能够实现更为均匀地分布用户与项目的表示。通过优化这种对比度损失函数,在不考虑图形增强的情况下能够实现更为均匀地分布用户与项目的表示。即便不采用图形增强技术,在这种情况下也能有效避免因广泛使用的偏见而导致的问题,并有助于提升对长尾项目的关注。
问题2: 是否有更有效和高效的增强方法?
SimGCL舍弃了低效图形增强方案,并通过引入统一噪声源促进学习表示的质量提升。这种基于噪声的数据增强方式能够使得嵌入空间呈现更为均匀且规则化的分布特征。值得注意的是,在这一过程中我们实现了对表征一致性的有效平衡调节:一方面可以通过调节噪声强度来平衡一致性与多样性之间的关系;另一方面也能根据具体需求灵活控制表征的空间扩展程度。经过这些设计优化后,在推荐系统性能指标方面 SimGCL较之传统基于图形增强的方法展现出显著的优势
可以得出两个结论:
(1)图形增强表现出显著的效果;然而其预设目标并未达到效果;其性能提升主要归因于对比度损失InfoNCE;这有助于理解为何即使在非常稀疏的图形增强情况下也能获得有用的信息。
并非所有图形增强都能带来正面效果;选择有效的技术通常需要进行长时间的测试与验证。可能的原因在于某些图形增强方法可能导致严重的变形。例如,在节点丢失的情况下,可能会导致关键节点(如hub)及其相关连接边被删除,并将相关的子图分割成不连通的部分。
由于这样的图扩充与原始图之间几乎没有可学习的不变性,则促进它们之间的一致性是不合理的
2、提出SimGCL的不足
核心部分来了,将右侧对比直接合并到左侧,使结构更简单!!!

SimGCL由于其复杂的架构显得略显功能不足。在仅用于推荐任务的正向与反向传播之外,并需为小批量对比任务分别进行两次正向与反向传播的操作
这项工作中,提出了一种非常简单的图形对比学习方法(XSimGCL)供推荐。
XSimGCL不仅沿用了SimGCL基于噪声的提升,并且通过优化传播机制显著减少了计算复杂度。如图2所示,在一个小批量处理中,XSimGCL共用了一个完整的正向与反向传播流程,而不是分别设置了独立的通道。具体而言,两个模型均采用相同的输入:初始嵌入与邻接矩阵
主要区别在于,在比较所学的两个最终表示类型时,SimGCL采用了不同的噪声处理,并结合普通表示方法进行推荐,而XSimGCL则在两个任务上采用了相同的扰动处理方式,并将跨层对比机制取代了SimGCL中的顶层对比方法
二、对比推荐与图形增强
下面这部分涉及到了CL的相关知识及其表现形式,请参考前面讨论过的相关材料)。
1、传统的CL
研究人员认为,在部分结构扰动保持不变的情况下(即对部分结构扰动保持不变),学习表示被视为高质量的

前面的就是我们常用的BPR损失了

后面的是InfoNCE损失了,CL任务起着辅助作用,其效果由超参数λ调节

其中i和j是批次B中的用户/项目,

和

由两种不同的基于丢包的情况下的图形增强学习遵循L2规范化表示(F.normalize),其中温度参数τ被设定为大于零(如0.2),用于控制样本惩罚强度的大小。InfoNCE损失促进了这一效果。

和

之间的一致性,它们是彼此的正样本,同时将

和

在两个实体之间的一致性被最小化,在这种情况下它们彼此互为负样本。优化InfoNCE损失等价于最大限度地降低相互信息的下限。
SGL使用LightGCN[28]作为其编码器,其消息传递过程 定义为:

2、特征/密度估计之间的明显对比

通过图中可以看出LightGCN与基于CL的推荐模型在特征/密度估计方面呈现出明显的对比关系。在左侧列中,LightGCN能够有效提取高度聚集化的特征,并展现出急剧上升和下降的密度曲线。值得注意的是,在热用户与热物品之间存在相似分布特征,在冷用户群体中则主要集中在热物品附近;仅有少数用户会出现在冷物品区域。从技术层面而言,这一现象表明该方法存在一定的偏向性方式,并可能导致模型持续向大多数用户提供热门项目以生成常规化的推荐结果
我们认为,这两个问题可能会导致这种偏颇的分布。
一个是在推荐系统中,一小部分项目通常会导致大多数交互,
另一个问题是臭名远扬的过度光滑问题,它导致嵌入在局部区域表现出相似性,进而强化了马太效应。
与之相比,在第二、第三列中,SGL变体学习的特征分布更加均匀;其密度估计曲线则相对不够清晰。无论是否采用了图形增强技术,作为参考,在第四列中我们仅通过优化SGL-ED中的InfoNCE损失来绘制所学特征。若未考虑Lrec的影响,则所学特征近乎呈均匀分布。
真正原因在于数据分布的均匀性得到显著提升 。这种机制不仅有效降低了流行偏见的影响,并且促进了长尾项的表现。研究结果表明,在更均匀的数据分布下(即更均衡地利用数据特征),模型能够更好地保持节点特性和泛化能力。这些发现进一步证实了所提出的SGL-WA方法在实际应用中的卓越性能表现。值得注意的是,在有限范围内追求一致性和性能之间的正相关是必要的策略。
3、统一才是真正重要的
揭示了对比损失InfoNCE是关键。InfoNCE损失的预训练强化了两个特性:
正对特征的对齐,以及单位超球面上标准化特征分布的一致性。
为了降低不同节点表示之间的余弦相似性而优化CL损失
三、极简对比学习
1、基于噪声的增强
基于此研究结果表明,在一定范围内调节学习表征的一致性有助于提升对比推荐模型的效果。
然而调节图形结构以实现可控制均匀性的难度较高。
因此我们将研究重点转向嵌入空间中的优化问题。
受到对抗示例生成方法的影响,在表示中加入随机噪声是一种有效的方法。


通过引入缩放后的噪声向量到ei后, 从而使其旋转了两个微小的角度(如图4所示θ1和θ2). 每个这样的旋转都与ei的变化相关联, 并生成一个增强表示.

和

注
2、极简对比学习模型

新体系结构仅在小型批处理计算中进行一次前向和反向传递。我们将其命名为XSimGCL,这是极简单图形对比学习的缩写.XSimGCL的联合损失公式为:

这里

用于比较最终层的层次结构。对比两个中间层级是可以选择的;实验结果表明,在包含最后一层时可以获得最佳性能
在XSimGCL中,我们可以通过更改

的值来明确控制扩展表示与原始表示的偏差程度。直觉上,一个更大的

会引导表示趋于更加一致的分布。这是因为,在优化对比度损失的过程中,加入的噪声会作为梯度的一部分传递下去。由于噪声是从均匀分布中抽取的样本,在优化过程中对原始表示施加了正则化处理。为了验证这一结论的有效性与可行性。
核心还是下面的这个图:
实验结果

四、pytorch代码
1、数据集介绍

模型实现:
def forward(self, perturbed=False):
ego_embeddings = torch.cat([self.embedding_dict['user_emb'], self.embedding_dict['item_emb']], 0) #7297*64
all_embeddings = []
all_embeddings_cl = ego_embeddings
for k in range(self.n_layers):
ego_embeddings = torch.sparse.mm(self.sparse_norm_adj, ego_embeddings)#7297*64
if perturbed:
#2random_noise = torch.rand_like(ego_embeddings).cuda()
random_noise = torch.rand_like(ego_embeddings)
ego_embeddings += torch.sign(ego_embeddings) * F.normalize(random_noise, dim=-1) * self.eps
all_embeddings.append(ego_embeddings) #list 2
if k==self.layer_cl-1: #存储第一次聚合的加噪声embedding
all_embeddings_cl = ego_embeddings
final_embeddings = torch.stack(all_embeddings, dim=1) #7297*2*64
final_embeddings = torch.mean(final_embeddings, dim=1) #7297*64
user_all_embeddings, item_all_embeddings = torch.split(final_embeddings, [self.data.user_num, self.data.item_num]) #得到加噪声的 embedding聚合结果(final embed)
user_all_embeddings_cl, item_all_embeddings_cl = torch.split(all_embeddings_cl, [self.data.user_num, self.data.item_num]) #得到加噪声的 第一层embedding聚合结果(first embed)
if perturbed:
return user_all_embeddings, item_all_embeddings,user_all_embeddings_cl, item_all_embeddings_cl
return user_all_embeddings, item_all_embeddings

先BPR损失函数过一下
rec_user_emb, rec_item_emb, cl_user_emb, cl_item_emb = model(True)
user_emb, pos_item_emb, neg_item_emb = rec_user_emb[user_idx], rec_item_emb[pos_idx], rec_item_emb[neg_idx]
rec_loss = bpr_loss(user_emb, pos_item_emb, neg_item_emb)
def bpr_loss(user_emb, pos_item_emb, neg_item_emb):
pos_score = torch.mul(user_emb, pos_item_emb).sum(dim=1) #2048
neg_score = torch.mul(user_emb, neg_item_emb).sum(dim=1) # 2048
loss = -torch.log(10e-8 + torch.sigmoid(pos_score - neg_score))# 2048
return torch.mean(loss)
然后InfoNCE损失函数
cl_loss = self.cl_rate * self.cal_cl_loss([user_idx,pos_idx],rec_user_emb,cl_user_emb,rec_item_emb,cl_item_emb)
def cal_cl_loss(self, idx, user_view1,user_view2,item_view1,item_view2): # xx rec_user cl_user rec_item cl_item
#u_idx = torch.unique(torch.Tensor(idx[0]).type(torch.long)).cuda()
#i_idx = torch.unique(torch.Tensor(idx[1]).type(torch.long)).cuda()
u_idx = torch.unique(torch.Tensor(idx[0]).type(torch.long))
i_idx = torch.unique(torch.Tensor(idx[1]).type(torch.long))
user_cl_loss = InfoNCE(user_view1[u_idx], user_view2[u_idx], self.temp)
item_cl_loss = InfoNCE(item_view1[i_idx], item_view2[i_idx], self.temp)
return user_cl_loss + item_cl_loss
def InfoNCE(view1, view2, temperature):
view1, view2 = F.normalize(view1, dim=1), F.normalize(view2, dim=1) # 标准化 第一个加了干扰的 eu1 第二个加了干扰的eu2
pos_score = (view1 * view2).sum(dim=-1) #1563
pos_score = torch.exp(pos_score / temperature) #分子
ttl_score = torch.matmul(view1, view2.transpose(0, 1))
ttl_score = torch.exp(ttl_score / temperature).sum(dim=1) # 分母 1563
cl_loss = -torch.log(pos_score / ttl_score) #1563
return torch.mean(cl_loss)

batch_loss = rec_loss + l2_reg_loss(self.reg, user_emb, pos_item_emb) + cl_loss
总结
对推荐系统中图结构CL(Graph Contrastive Learning)进行了深入回顾,并探讨其在提升图推荐性能方面的作用机制。经研究发现,在提升性能方面起着决定性作用的是InfoNCE损失函数,在这一过程中发挥重要作用的是图形预处理技术。通过优化该损失函数可以显著改善节点表示的空间分布质量,在推荐场景中促进更均衡地覆盖长尾类别项目的一种高效噪声增强策略能够在保持模型稳定性的基础上实现更好的表示效果,并特别适用于大规模图结构化数据处理任务的新框架框架XSimGCL
