"""LightGCN (+slate softmax on logged events) on the TRAIN-ONLY click graph at cutoff X_k. python gcn.py K TAG [--d 32 --L 2 --l2 1e-4 --steps 600 --lr 5e-3 --seed 0 --mon val25 --every 100 --slate 1.0 --bpr 1.0] Saves out/emb_{TAG}_k{K}_s{seed}.npz (U,V final-propagated at each checkpoint step list in --save) .""" import warnings; warnings.filterwarnings('ignore') import sys,os,time,argparse,numpy as np,torch; sys.dont_write_bytecode=True from sklearn.metrics import roc_auc_score ap=argparse.ArgumentParser(); ap.add_argument('k',type=int); ap.add_argument('tag'); ap.add_argument('--d',type=int,default=32); ap.add_argument('--L',type=int,default=2) ap.add_argument('--l2',type=float,default=1e-4); ap.add_argument('--steps',type=int,default=600); ap.add_argument('--lr',type=float,default=5e-3); ap.add_argument('--seed',type=int,default=0) ap.add_argument('--mon',default=''); ap.add_argument('--every',type=int,default=100); ap.add_argument('--slate',type=float,default=1.0); ap.add_argument('--bpr',type=float,default=1.0) ap.add_argument('--bs',type=int,default=16384); ap.add_argument('--drop',type=float,default=0.0); ap.add_argument('--save',default=''); ap.add_argument('--thr',type=int,default=2) a=ap.parse_args(); torch.set_num_threads(a.thr); O='/workspace/huawei/v21graph/out/'; t0=time.time() NU,NI=np.load(O+'dims.npy'); g=np.load(O+f'g_{a.k}.npz'); cu,ci=g['cu'].astype(np.int64),g['ci'].astype(np.int64); nX=int(g['nX']) key=np.unique(cu*NI+ci); eu=key//NI; ei=key%NI; E_=len(key) # logged slates (train events with ts cached by prep as slates.npz sl=np.load(O+'slates.npz'); keep=sl['e']0: m=torch.rand(E_)>a.drop; Ad=adj(eu[m.numpy()],ei[m.numpy()]) else: Ad=A z=prop(emb,Ad); U=z[:NU]; V=z[NU:]; loss=0. if a.bpr>0: b=torch.randint(0,E_,(a.bs,)); u=EU[b]; p=EI[b]; n=torch.multinomial(pop,a.bs,replacement=True) loss=loss+a.bpr*torch.nn.functional.softplus((U[u]*V[n]).sum(1)-(U[u]*V[p]).sum(1)).mean() if a.slate>0: b=torch.randint(0,len(SU),(a.bs//2,)); u=SU[b]; c=SC[b]; y=SY[b] # y in 0..9 (event has a click) else skipped ok=y>=0; u,c,y=u[ok],c[ok],y[ok] sc=(U[u][:,None,:]*V[c]).sum(-1); loss=loss+a.slate*torch.nn.functional.cross_entropy(sc,y) reg=a.l2*(emb[:NU].pow(2).sum(1).mean()+emb[NU:].pow(2).sum(1).mean()); (loss+reg).backward(); opt.step() if mon is not None and step%a.every==0: monitor(step) if step in saves: with torch.no_grad(): z=prop(emb,A); res[f'U{step}']=z[:NU].numpy().astype(np.float32); res[f'V{step}']=z[NU:].numpy().astype(np.float32) if res: np.savez(O+f'emb_{a.tag}_k{a.k}_s{a.seed}.npz',**res) print('done',time.time()-t0)