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. 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

你可能感兴趣的:(python,开发语言,后端)