LightGCN 代码解析

代码地址

整体流程:

  1. 构建dataset类:处理数据集,构建m_item, UserItemNet等统计量
  2. 实例化LightGCN模型(继承自nn.module):初始化embedding、设置激活函数、调用dataset中的SparseGraph函数(构建邻接矩阵、度矩阵)
  3. 指定loss,BPRLoss()
  4. 调用Procedure.Test & Procedure.BPR_train_original进行测试和训练。① 采样 (u,i,j) ,正负采样都是随机选的。② 根据LightGCN的聚合公式 ( D − 1 / 2 A D − 1 / 2 ) e i (D^{-1/2}AD^{-1/2})e_i (D1/2AD1/2)ei,得到聚合三次后(u,i,j)的embedding③ 传入embedding,通过bpr_loss()计算loss。
    理解成,LightGCN就是通过信息传播聚合得到embedding,然后采集正负样本,使用bpr_loss训练就好了。

1. import过程

1.1 world

这部分是获取参数、路径:

  1. 获取路径,记得将ROOT_PATH = "/Users/gus/Desktop/light-gcn"换成当前路径:ROOT_PATH = os.path.dirname(__file__)
  2. args = parse_args()获取参数,存到config字典中
  3. 定义了cprint,相当于高光输出。

1.2 utils

这部分指定评价指标

  1. 检查是否含有cpp文件
  2. 评价指标都在里面哦

1.3 register

  1. 输出参数
  2. 定义MODELS
MODELS = {
   'mf': model.PureMF,
   'lgn': model.LightGCN
}

2. 指定模型和损失函数

传入初始化参数(config和dataset),进行实例化

Recmodel = register.MODELS[world.model_name](world.config, dataset) 
Recmodel = Recmodel.to(world.device)
bpr = utils.BPRLoss(Recmodel, world.config)
  1. 初始化LightGCN时,就构建好了邻接矩阵什么的

3. 训练过程

3.1 测试

每隔十轮就进行测试 Procedure.Test(dataset, Recmodel, epoch, w, world.config['multicore'])

  1. 调用rating = Recmodel.getUsersRating(batch_users_gpu)得到用户对所有物品的评分(两个embedding相乘)
  2. 修改历史交互物品的评分(不会被选入TopN):rating[exclude_index, exclude_items] = -(1<<10)
  3. 传入真实值和预测值,查看各个指标结果:test_one_batch(x);调用了utils中的函数。

3.2 训练

调用Procedure进行训练:output_information = Procedure.BPR_train_original(dataset, Recmodel, bpr, epoch, neg_k=Neg_k,w=w)

  1. 先进行正负样本采样
  2. 调用utils.stageOne计算loss和更新
  3. 调用all_users, all_items = self.computer()获取embedding;初始embedding是随机的,经过三次聚合得到最终的embedding
  4. 根据bpr loss计算损失
  5. 使用Adam进行更新

函数用法:

  1. os.path.join()用法: 获取当前目录,并组合成新目录 CODE_PATH = join(ROOT_PATH, 'code')
  2. argparse 命令行选项、参数和子命令解析器。
    import argparse导包
    parser = argparse.ArgumentParser()创建对象
    parser.add_argument() 添加值
    args = parse_args()进行解析,之后调用参数直接args.xx就好了
  3. simplefilter是一个模块,它提供了构建卷积分类网络所需的工具
  4. strip() 删除空格/规定字符,split()拆分
l = '1 2 3 4 5 6 '
l=l.strip()
l=l.split(' ')
print(l)# ['1', '2', '3', '4', '5', '6']
  1. @property:是一种装饰器,将方法变成属性调用
  2. dict.get(key, default=None)返回指定键的值,如果键不在字典中返回默认值 None 或者设置的默认值。
  3. numpy.random.randint(low, high=None, size=None, dtype='l')指定上下界和size
  4. np.unique( )的用法 该函数是去除数组中的重复数字,并进行排序之后输出。
import numpy as np
a = [1,2,3,3,5,1,6,2,4]
print(np.unique(a)) # [1 2 3 4 5 6]
  1. csr_matrix构造稀疏矩阵;indices为[1,4,12,34,1,3,56,12,45,10];indptr是[ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10](非零的位置)
res = csr_matrix((np.ones(10), ([1,2,3,4,5,6,7,8,9,10], [1,4,12,34,1,3,56,12,45,10])), shape=(11, 57))
# 输出:
out:  
  (1, 1)	1.0
  (2, 4)	1.0
  (3, 12)	1.0
  (4, 34)	1.0
  (5, 1)	1.0
  ......
  1. nonzero函数是numpy中用于得到数组array中非零元素的位置(数组索引)的函数。
print(np.nonzero([0,0,3,0,3,5]))
# output: (array([2, 4, 5], dtype=int64),)
  1. pprint用于打印复杂的数据结构对象,例如多层嵌套的列表、元组和字典等。
  2. sp.dok_matrix:采用字典来记录矩阵中不为0的元素(有“稀疏字典”那味)这篇文章解释了各种稀疏矩阵的构建方法和区别(coo, csr, dok)
from scipy.sparse import dok_matrix
S = dok_matrix((3, 3), dtype=np.float32)
for i in range(3):
    for j in range(3):
        S[i, j] = i + j    # Update element
print(S)     
>>> output:
  (0, 1)        1.0
  (0, 2)        2.0
  (1, 0)        1.0
  (1, 1)        2.0
  (1, 2)        3.0
  (2, 0)        2.0
  (2, 1)        3.0
  (2, 2)        4.0  
_row  = np.array([0, 3, 1, 0])
_col  = np.array([0, 3, 1, 2])
_data = np.array([4, 5, 7, 9])
# 稀疏矩阵存储方式 (row, col, data)
coo = coo_matrix((_data, (_row, _col)), shape=(4, 4), dtype=np.int)
print(coo)
#   (0, 0)        4
#   (3, 3)        5
#   (1, 1)        7
#   (0, 2)        9

# 按行压缩的稀疏矩阵存储方式 indptr, indices, data
csr = csr_matrix((_data, (_row, _col)), shape=(4, 4), dtype=np.int)
print(csr)
#   (0, 0)        4
#   (0, 2)        9
#   (1, 1)        7
#   (3, 3)        5
  1. torch.nn.Embedding(num_embeddings=self.num_users, embedding_dim=self.latent_dim) 构建embedding;这个模块常用来保存词嵌入和用下标检索它们。
emb = nn.Embedding(num_embeddings=5,embedding_dim=3)
user = torch.tensor(([1,2,3],[2,1,0]))
print(emb(user))
>>>output:
tensor([[[-0.7928, -0.7821,  0.3877],
         [ 0.2589, -0.0153,  1.4402],
         [ 0.1919,  0.8395,  1.2728]],

        [[ 0.2589, -0.0153,  1.4402],
         [-0.7928, -0.7821,  0.3877],
         [ 0.9853,  0.6987, -2.0332]]], grad_fn=<EmbeddingBackward>)
  1. torch.nn.init.normal(tensor, mean=0, std=1)从给定均值和标准差的正态分布N(mean, std)中生成值,填充输入的张量或变量。
  2. def shuffle(*arrays, **kwargs):表示不确定具体参数个数,前者是tuple类型,后者是dict类型
def add(*num):
    res = 0
    for i in num:
        res += i
    return res   

print(add(1,2,3))
def infor(**diec_args):
    res = 0
    for i,j in diec_args.items():
        print(i,":",j)
#注意输入格式 k1=v1,k2=v2   
infor(name='shiqi',sex='girl')
  1. 带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
  2. torch.topk(rating, k=max_K)用来求tensor中某个dim的前k大或者前k小的值以及对应的index
  3. assert expression用于判断一个表达式,在表达式条件为 false 的时候触发异常。
# 相当于:
if not expression:
    raise AssertionError
# 用法
assert 1<2  # 不会报错
assert 1>2  # 会报错AssertionError
  1. with结构,基本思想是with所求值的对象必须有一个__enter__()方法,一个__exit__()方法。紧跟with后面的语句被求值后,返回对象的__enter__()方法被调用,这个方法的返回值将被赋值给as后面的变量。当with后面的代码块全部被执行完之后,将调用前面返回对象的__exit__()方法。下面这个例子是计算运行时间的:
 with timer(name="Sample"):
        S = utils.UniformSample_original(dataset)
 time_info = timer.dict()        
    def __enter__(self):
        self.start = timer.time()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
         timer.NAMED_TAPE[self.named] += timer.time() - self.start
         
    def dict(select_keys=None):
        hint = "|"
		for key, value in timer.NAMED_TAPE.items(): 
            hint = hint + f"{key}:{value:.2f}|"
        return hint
  • 8
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
LightGCN是一种将图卷积神经网络应用于推荐系统的算法,它是对神经图协同过滤(NGCF)算法的优化和改进。NGCF是基于图卷积网络(GCN)的协同过滤算法,但它在GCN的特征转换和非线性激活过程上存在一些问题。为了解决这些问题,LightGCN简化了标准GCN的设计,使其更适用于推荐任务。 具体来说,LightGCN的原理是通过在用户-物品交互图上进行图卷积操作来学习用户和物品的嵌入表示。它采用了一种简化的图卷积操作,即将用户和物品的嵌入向量相加而不进行特征转换和非线性激活。这种简化的操作可以减少模型的复杂性和计算量,同时保持了推荐任务中用户和物品之间的关系。 关于代码实现,可以参考相关的开源代码库或文献中提供的实现。\[2\] #### 引用[.reference_title] - *1* [推荐系统笔记(五):lightGCN算法原理与背景](https://blog.csdn.net/qq_46006468/article/details/126060701)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [经典图推荐系统论文LightGCN算法及代码简介](https://blog.csdn.net/Blueghost19/article/details/129665502)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值