携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
通过对LightGCN源码进行分析,学习LightGCN的建图的初始化过程以更好的使用该模型,后续建图可结合上一篇构造拉普拉斯矩阵
LightGCN
LightGCN和NGCF一样都是获取协同过滤中的高阶连通信号,本质上还是构建了多个子图,把子图拼接为一个大图的形式进行邻域传播
- LightGCN作为NGCF的优化版,去掉了邻域聚合过程中的特征变换和非线性激活部分
- LightGCN和NGCF一样,其输入只有user和item的id_embedding信息,没有携带更多的特征信息,直接把LightGCN应用到diffnet系列模型上需要考虑处理补充的特征信息问题
LightGCN使用的数据集
以论文中源码给出的yelp2018数据集为例,
通过item_list.txt和user_list.txt中给出的映射表,把用22位字符串表示的user_id和item_id映射为对应的数值型表示。其中item有28048项,user有31668项。交互的分布符合长尾理论的结果,但与原yelp数据集相比缺失了描述user和item分组信息。
最新的yelp数据集中item已经超过了两百万条,如果还是用该方法做映射容易发生丢失等问题。
在train.txt和test.txt中,第一列记录user_id,其余列记录的和该user有交互的item_id列表。
上图为train.txt截选展示
LightGCN加载数据和生成初始图表示的过程
在dataloader.py\Loader\init()中
过程简述:
- 读取train.txt,生成对应的三个array数组:trainUinqueUsers, trainUser, trainItem
- 生成初始的稀疏CSR矩阵:UserItemNet
- 生成拉普拉斯矩阵,用于矩阵传播
源码分析
with open(train_file) as f: # 打开训练集
for l in f.readlines(): # 按行读取
if len(l) > 0:
l = l.strip('\n').split(' ') # 去除前后的换行符,然后根据空格分割
items = [int(i) for i in l[1:]] # 第一列是user_id,后面的是item_id
uid = int(l[0]) # 提取user_id
trainUniqueUsers.append(uid) # 添加到列表中
trainUser.extend([uid] * len(items)) # extend在表尾追加元素
trainItem.extend(items) # 表尾追加元素
self.m_item = max(self.m_item, max(items)) # max_item_id
self.n_user = max(self.n_user, uid) # max_user_id
self.traindataSize += len(items) # size扩大
self.trainUniqueUsers = np.array(trainUniqueUsers) # 转为数组
self.trainUser = np.array(trainUser)
self.trainItem = np.array(trainItem)
步骤1:读取文件生成数组
self.UserItemNet = csr_matrix((np.ones(len(self.trainUser)),(self.trainUser, self.trainItem)),\
shape=(self.n_user, self.m_item))
步骤2:生成CSR稀疏矩阵
scipy.sparse.csr_matrix((data, (row_ind, col_ind)), shape=(x,y))矩阵的压缩表示
数据集中只考虑有无交互而没考虑是隐式或显示交互,可直接用0和1表示有无交互;row和col表示该非0元素的位置,shape为整个矩阵的形状