---
# System prepended metadata

title: Untitled

---

import torch
import pandas as pd
pd.set_option('display.max_colwidth', None)
from sklearn import preprocessing as pp
from sklearn.model_selection import train_test_split
import scipy.sparse as sp
import numpy as np
import random
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
import time
import matplotlib.pyplot as plt
from tqdm.notebook import tqdm

------
torch.__version__
print(torch.cuda.is_available())
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)

------

df = pd.read_csv("initial_Train.csv")

---
df = df.sort_values(by = ['timestamp'])
train_, test_ = train_test_split(df.values, test_size=0.3, shuffle=False)
train_ = pd.DataFrame(train_, columns = names )
test_ = pd.DataFrame(test_, columns = names)
print("Train Size  : ", len(train_))
print("Test Size : ", len (test_

import numpy
from numpy.compat import pickle 
le_user = pp.LabelEncoder()
le_item = pp.LabelEncoder()
train_['user_id_idx'] = le_user.fit_transform(train_['userId'].values)
train_['item_id_idx'] = le_item.fit_transform(train_['movieId'].values)
output_user = open('user_encoder.pkl', 'wb')
output_item = open('item_encodesr.pkl', 'wb')
pickle.dump(le_user, output_user)
pickle.dump(le_item, output_item)
output_item.close()
output_user.close()

train_user_ids = train_['userId'].unique()
train_item_ids = train_['movieId'].unique()





test_ = test_[(test_['userId'].isin(train_user_ids))&(test_['movieId'].isin(train_item_ids))]


print(len(test_))
print(len(train_))

test_['user_id_idx'] = le_user.transform(test_['userId'].values)
test_['item_id_idx'] = le_item.transform(test_['movieId'].values)

n_users = train_['user_id_idx'].nunique()
n_items = train_['item_id_idx'].nunique()
print("Number of Unique Users : ", n_users)
print("Number of unique Items : ", n_items)
print("SUM : ",n_users + n_items )
n_users_ = test_['user_id_idx'].nunique()
n_items_ = test_['item_id_idx'].nunique()
print("Number of Unique Users : ", n_users_)
print("Number of unique Items : ", n_items_)
print("SUM : ",n_users_ + n_items_ )


latent_dim = 64
n_layers = 3  
item_eb = nn.Embedding(num_embeddings=n_items, embedding_dim=latent_dim)
nn.init.normal_(item_eb.weight, std=0.1)

torch.save(item_eb.weight, 'initial_item_Embed0.pt')
```
def convert_to_sparse_tensor(dok_mtrx):
    
    dok_mtrx_coo = dok_mtrx.tocoo().astype(np.float32)
    values = dok_mtrx_coo.data
    indices = np.vstack((dok_mtrx_coo.row, dok_mtrx_coo.col))

    i = torch.LongTensor(indices)
    v = torch.FloatTensor(values)
    shape = dok_mtrx_coo.shape

    dok_mtrx_sparse_tensor = torch.sparse.FloatTensor(i, v, torch.Size(shape))

    return dok_mtrx_sparse_tensor.to(device)
```

```
from sklearn import metrics
from sklearn.preprocessing import label_binarize
def get_metrics(user_Embed_wts, item_Embed_wts, n_users, n_items, train_data, test_data, K):
    test = sp.dok_matrix((n_users, n_items), dtype = np.float32)
    test[test_data['user_id_idx'], test_data['item_id_idx']] = 1.0
    test = test.toarray()
    user_Embedding = nn.Embedding(user_Embed_wts.size()[0], user_Embed_wts.size()[1], _weight = user_Embed_wts)
    item_Embedding = nn.Embedding(item_Embed_wts.size()[0], item_Embed_wts.size()[1], _weight = item_Embed_wts)

    test_user_ids = torch.LongTensor(test_data['user_id_idx'].unique())

    relevance_score = torch.matmul(user_Embed_wts.to(device), torch.transpose(item_Embed_wts,0, 1).to(device))
    itms = test_data['item_id_idx'].unique()
    usrs = test_data['user_id_idx'].unique()


    
    R = sp.dok_matrix((n_users, n_items), dtype = np.float32)
    R[train_data['user_id_idx'], train_data['item_id_idx']] = 1.0
    
    R_tensor = convert_to_sparse_tensor(R)
    R_tensor_dense = R_tensor.to_dense()

    R_tensor_dense = R_tensor_dense*(-np.inf)
    R_tensor_dense = torch.nan_to_num(R_tensor_dense, nan=0.0)
    

    relevance_score = relevance_score.to(device) + R_tensor_dense.to(device)
    # print(torch.topk(relevance_score, K))
    topk_relevance_score = torch.topk(relevance_score, K).values
    # print(len(topk_relevance_score))
    topk_relevance_indices = torch.topk(relevance_score, K).indices
    # print(topk_relevance_indices)
    #covert topk_relevance_indices to cpu
    topk_relevance_indices = topk_relevance_indices.cpu()
    topk_relevance_indices_df = pd.DataFrame(topk_relevance_indices.numpy(),columns =['top_indx_'+str(x+1) for x in range(K)])
    # print(topk_relevance_indices_df)
    topk_relevance_indices_df['user_ID'] = topk_relevance_indices_df.index
    #
    topk_relevance_indices_df['top_rlvnt_itm'] = topk_relevance_indices_df[['top_indx_'+str(x+1) for x in range(K)]].values.tolist()
    topk_relevance_indices_df = topk_relevance_indices_df[['user_ID','top_rlvnt_itm']]
    # print(topk_relevance_indices_df)
    test_interacted_items = test_data.groupby('user_id_idx')['item_id_idx'].apply(list).reset_index()
    
    metrics_df = pd.merge(test_interacted_items,topk_relevance_indices_df, how= 'left', left_on = 'user_id_idx',right_on = ['user_ID'])
    # print(metrics_df)
    metrics_df['intrsctn_itm'] = [list(set(a).intersection(b)) for a, b in zip(metrics_df.item_id_idx, metrics_df.top_rlvnt_itm)]
   

    metrics_df['recall'] = metrics_df.apply(lambda x : len(x['intrsctn_itm'])/len(x['item_id_idx']), axis = 1) 
    metrics_df['precision'] = metrics_df.apply(lambda x : len(x['intrsctn_itm'])/K, axis = 1)
    # print(metrics_df)
    # metrics_df['auc'] = metrics_df.apply(lambda x : metrics.roc_auc_score([1]*len(x['intrsctn_itm']) + [0]*(K-len(x['intrsctn_itm'])), x['intrsctn_itm'] + [0]*(K-len(x['intrsctn_itm']))), axis = 1)


    def get_hit_list(item_id_idx, top_rlvnt_itm):
        return [1 if x in set(item_id_idx) else 0 for x in top_rlvnt_itm ]

    metrics_df['hit_list'] = metrics_df.apply(lambda x : get_hit_list(x['item_id_idx'], x['top_rlvnt_itm']), axis = 1)


    usert = test_data['user_id_idx'].unique()
    # print(intrsctn_itm)
    def get_auc(test, relevance_score, usert):
        relevance_score = relevance_score.cpu()
        auc = []
        for i in usert:
            a = metrics.roc_auc_score(test[i], relevance_score[i])
            auc.append(a)
        return np.mean(auc)
            
            
    def get_dcg_idcg(item_id_idx, hit_list):
        idcg  = sum([1 / np.log1p(idx+1) for idx in range(min(len(item_id_idx),len(hit_list)))])
        dcg =  sum([hit / np.log1p(idx+1) for idx, hit in enumerate(hit_list)])
        return dcg/idcg

    def get_cumsum(hit_list):
        return np.cumsum(hit_list)

    def get_map(item_id_idx, hit_list, hit_list_cumsum):
        return sum([hit_cumsum*hit/(idx+1) for idx, (hit, hit_cumsum) in enumerate(zip(hit_list, hit_list_cumsum))])/len(item_id_idx)
  

    metrics_df['ndcg'] = metrics_df.apply(lambda x : get_dcg_idcg(x['item_id_idx'], x['hit_list']), axis = 1)
    metrics_df['hit_list_cumsum'] = metrics_df.apply(lambda x : get_cumsum(x['hit_list']), axis = 1)
    
    metrics_df['map'] = metrics_df.apply(lambda x : get_map(x['item_id_idx'], x['hit_list'], x['hit_list_cumsum']), axis = 1)
    a_m = get_auc(test, relevance_score, usert)

    return  a_m, metrics_df['recall'].mean(), metrics_df['precision'].mean(), metrics_df['ndcg'].mean(), metrics_df['map'].mean() 
```
```
class LightGCN(nn.Module):
    def __init__(self, data, n_users, n_items, n_layers, latent_dim):
        super(LightGCN, self).__init__()
        self.data = data
        self.n_users = n_users
        self.n_items = n_items
        self.n_layers = n_layers
        self.latent_dim = latent_dim
        self.init_embedding()
        self.norm_adj_mat_sparse_tensor = self.get_A_tilda()

    def init_embedding(self):
  
        self.embedding_user = nn.Embedding(
            num_embeddings=self.n_users, embedding_dim=self.latent_dim)
  
        self.embedding_item = torch.load('initial_item_Embed0.pt')
        nn.init.normal_(self.embedding_user.weight, std=0.1)
   
        self.E0 = torch.cat((self.embedding_user.weight.to(device), self.embedding_item.to(device)), 0)
  
        self.E0 = nn.Parameter(self.E0).to(device)

    def get_A_tilda(self):
        R = sp.dok_matrix((self.n_users, self.n_items), dtype = np.float32)
        R[self.data['user_id_idx'], self.data['item_id_idx']] = 1.0

        adj_mat = sp.dok_matrix(
                (self.n_users + self.n_items, self.n_users + self.n_items), dtype=np.float32
            )
        adj_mat = adj_mat.tolil()
        R = R.tolil()

        adj_mat[: self.n_users, self.n_users :] = R
        adj_mat[self.n_users :, : self.n_users] = R.T
        adj_mat = adj_mat.todok()

        rowsum = np.array(adj_mat.sum(1))
        d_inv = np.power(rowsum + 1e-9, -0.5).flatten()
        d_inv[np.isinf(d_inv)] = 0.0
        d_mat_inv = sp.diags(d_inv)
        norm_adj_mat = d_mat_inv.dot(adj_mat)
        norm_adj_mat = norm_adj_mat.dot(d_mat_inv)
        
        norm_adj_mat_coo = norm_adj_mat.tocoo().astype(np.float32)
        values = norm_adj_mat_coo.data
        indices = np.vstack((norm_adj_mat_coo.row, norm_adj_mat_coo.col))

        i = torch.LongTensor(indices)
        v = torch.FloatTensor(values)
        shape = norm_adj_mat_coo.shape

        norm_adj_mat_sparse_tensor = torch.sparse.FloatTensor(i, v, torch.Size(shape))

        return norm_adj_mat_sparse_tensor.to(device)
    
    def propagate_through_layers(self):
        all_layer_embedding = [self.E0]
        E_lyr = self.E0

        for layer in range(self.n_layers):
            E_lyr = torch.sparse.mm(self.norm_adj_mat_sparse_tensor, E_lyr)
            all_layer_embedding.append(E_lyr)
            

        all_layer_embedding = torch.stack(all_layer_embedding)
        mean_layer_embedding = torch.mean(all_layer_embedding, axis = 0)

        final_user_Embed, final_item_Embed = torch.split(mean_layer_embedding, [self.n_users, self.n_items])
        initial_user_Embed, initial_item_Embed = torch.split(self.E0, [self.n_users, self.n_items])

        return final_user_Embed, final_item_Embed, initial_user_Embed, initial_item_Embed

    def forward(self, users, pos_items, neg_items):
        final_user_Embed, final_item_Embed, initial_user_Embed, initial_item_Embed = self.propagate_through_layers()

        users_emb, pos_emb, neg_emb = final_user_Embed[users], final_item_Embed[pos_items], final_item_Embed[neg_items]
        userEmb0,  posEmb0, negEmb0 = initial_user_Embed[users], initial_item_Embed[pos_items], initial_item_Embed[neg_items]

        return users_emb, pos_emb, neg_emb, userEmb0,  posEmb0, negEmb0
```

```
train_ = train_.sort_values(by = ['user_id_idx'])
train_ = train_.reset_index(drop = True)
test_ = test_.sort_values(by = ['user_id_idx'])
```


```
def bpr_loss(users, users_emb, pos_emb, neg_emb, userEmb0,  posEmb0, negEmb0):
  
    reg_loss = (1/2)*(userEmb0.norm().pow(2) + 
                    posEmb0.norm().pow(2)  +
                    negEmb0.norm().pow(2))/float(len(users))
    pos_scores = torch.mul(users_emb, pos_emb)
    pos_scores = torch.sum(pos_scores, dim=1)
    neg_scores = torch.mul(users_emb, neg_emb)
    neg_scores = torch.sum(neg_scores, dim=1)
        
    loss = torch.mean(torch.nn.functional.softplus(neg_scores - pos_scores))
        
    return loss, reg_loss
```

user_id_idx_end = train_.groupby('user_id_idx').tail(1).index


```
def data_loader(data, batch_size, n_usr, n_itm):
  
    interected_items_df = data.groupby('user_id_idx')['item_id_idx'].apply(list).reset_index()
    def sample_neg(x):
        while True:
            neg_id = random.randint(0, n_itm - 1)
            if neg_id not in x:
                return neg_id

    indices = [x for x in range(n_usr)]
    
    if n_usr < batch_size:
        users = [random.choice(indices) for _ in range(batch_size)]
    else:
        users = random.sample(indices, batch_size)

    users.sort()
    users_df = pd.DataFrame(users,columns = ['users'])
    interected_items_df = pd.merge(interected_items_df, users_df, how = 'right', left_on = 'user_id_idx', right_on = 'users')
    pos_items = interected_items_df['item_id_idx'].apply(lambda x : random.choice(x)).values
    neg_items = interected_items_df['item_id_idx'].apply(lambda x: sample_neg(x)).values
    
    users = np.array(list(users))
    users = torch.from_numpy(users)

    pos_items = np.array(list(pos_items))
    pos_items = torch.from_numpy(pos_items)

    neg_items = np.array(list(neg_items))
    neg_items = torch.from_numpy(neg_items)
      

    return users.type(torch.long), pos_items.type(torch.long), neg_items.type(torch.long)
```

```
EPOCHS = 10
BATCH_SIZE = 1024
DECAY = 0.0001
K = 5
```


```
from sklearn.utils import column_or_1d
from sklearn.preprocessing import LabelEncoder
from sklearn.utils import shuffle
class MyLabelEncoder(LabelEncoder):

    def fit(self, y):
        y = column_or_1d(y, warn=True)
        self.classes_ = pd.Series(y).unique()
        #shuffle the classes
        self.classes_ = shuffle(self.classes_)
        print(self.classes_)
        return self
```

```
import json
import math
import copy
a = []
r = []
p = []
n = []
m = []
time_ = []
for s in range(10):
    item_eb = nn.Embedding(num_embeddings=n_items, embedding_dim=latent_dim)
    nn.init.normal_(item_eb.weight, std=0.1)
    torch.save(item_eb.weight, 'initial_item_Embed0.pt')
    le = MyLabelEncoder()
    le.fit(train_['userId'].values)
    train_['user_id_idx'] = le.transform(train_['userId'])
    test_['user_id_idx'] = le.transform(test_['userId'])
    train_ = train_.sort_values(by = ['user_id_idx'])
    train_ = train_.reset_index(drop = True)
    user_id_idx_end = train_.groupby('user_id_idx').tail(1).index
    st = time.time()
    l = time.time()
    for t in range(1):
        i = 0 
        user_e = []
        item_e = 0
        begin = 0 #index of the first user
        number_of_users = 6583
        end = number_of_users - 1 #index of the last user
        # if t == 1:
        #     train_.iloc[:]['user_id_idx'] -= train_.iloc[-1]['user_id_idx']
        #     train_.loc[train_['user_id_idx']<0, 'user_id_idx'] *= -1
        #     train_ = train_.sort_values(by = ['user_id_idx'])
        #     train_ = train_.reset_index(drop = True)
        #     user_id_idx_end = train_.groupby('user_id_idx').tail(1).index


        while(begin != len(train_)):

            loss_list_epoch = []
            MF_loss_list_epoch = []
            reg_loss_list_epoch = []
            recall_list = []
            precision_list = []
            ndcg_list = []
            map_list = []
            train_time_list = []
            eval_time_list = []
    
            temp = user_id_idx_end[end]
            print(i)
            print('\n')
            print('begin: ', begin)
            print('\n')
            print('temp: ', temp)
            print('\n')
            print('number_of_users: ', number_of_users)     
            train = copy.deepcopy(train_.iloc[begin:temp+1])
            train.iloc[:]['user_id_idx'] -= train.iloc[0]['user_id_idx']
            test_user_dic = dict(zip(train['userId'].unique(), train['user_id_idx'].unique()))
            #save dictionary as pkl file
            with open('onehot_user_dic+'+str(i)+'.pkl', 'wb') as f:
                pickle.dump(test_user_dic, f)

            test = copy.deepcopy(test_[test_['userId'].isin(test_user_dic.keys())])
            test['user_id_idx'] = test.iloc[:]['userId'].map(test_user_dic)
            lightGCN = LightGCN(train, number_of_users, n_items, n_layers, latent_dim)
            begin = temp + 1

            
            for epoch in tqdm(range(EPOCHS)):
               
                n_batch = int(len(train)/BATCH_SIZE)
            
                final_loss_list = []
                MF_loss_list = []
                reg_loss_list = []      
                best_ndcg = -1
            
                train_start_time = time.time()

                model = lightGCN.to(device)
            


                optimizer = torch.optim.Adam(lightGCN.parameters(), lr = 0.005)
                model.train()
       
                for batch_idx in range(n_batch):
                    if(time.time()-l >= 362.8118):
                        print("--------------------------------------------break--------------------------------------------------")
                        break
                    optimizer.zero_grad()
                    users, pos_items, neg_items = data_loader(train, BATCH_SIZE, number_of_users, n_items)
                    
                    users_emb, pos_emb, neg_emb, userEmb0,  posEmb0, negEmb0 = lightGCN.forward(users.to(device), pos_items.to(device), neg_items.to(device))
                
                    mf_loss, reg_loss = bpr_loss(users, users_emb, pos_emb, neg_emb, userEmb0,  posEmb0, negEmb0)

                    reg_loss = DECAY * reg_loss
                    final_loss = mf_loss + reg_loss
                    if batch_idx % 100 == 0:
                        print("final_loss: ", final_loss)
                    final_loss.backward()
                    optimizer.step()

                    final_loss_list.append(final_loss.item())
                    MF_loss_list.append(mf_loss.item())
                    reg_loss_list.append(reg_loss.item())    
                    
                train_end_time = time.time()
                train_time = train_end_time - train_start_time
            
                model.eval()
                with torch.no_grad():
                    final_user_Embed, final_item_Embed, initial_user_Embed,initial_item_Embed = lightGCN.propagate_through_layers()
                    auc, test_topK_recall,  test_topK_precision, test_topK_ndcg, test_topK_map  = get_metrics(final_user_Embed.to(device), final_item_Embed.to(device), number_of_users, n_items, train, test, K)


                if test_topK_ndcg > best_ndcg:
                    best_ndcg = test_topK_ndcg
                    # torch.save(final_user_Embed, 'final_user_Embed' +str(i)+ '.pt')
                    torch.save(final_item_Embed, 'final_item_Embed' +str(i)+ '.pt')
                    # torch.save(initial_user_Embed, 'initial_user_Embed' +str(i)+ '.pt')
                    torch.save(initial_item_Embed, 'initial_item_Embed0.pt')
                    # torch.save(lightGCN.state_dict(),'pre_LightGCN_20M.pth')    
                    user_final_embedding = final_user_Embed
                    item_final_embedding = final_item_Embed
                

                eval_time = time.time() - train_end_time

                loss_list_epoch.append(round(np.mean(final_loss_list),4))
                MF_loss_list_epoch.append(round(np.mean(MF_loss_list),4))
                reg_loss_list_epoch.append(round(np.mean(reg_loss_list),4))

                recall_list.append(round(test_topK_recall,4))
                precision_list.append(round(test_topK_precision,4))
                ndcg_list.append(round(test_topK_ndcg,4))
                map_list.append(round(test_topK_map,4))
            if ((end + number_of_users) <= len(user_id_idx_end)-1):
                temp = user_id_idx_end[end]#index of the last user
                end += number_of_users
            else:
                print('-------------------------------------------')
                number_of_users = len(user_id_idx_end) - end - 1
                temp = user_id_idx_end[-1]   
                end = len(user_id_idx_end)- 1

            # user_final_embedding =torch.flip(user_final_embedding, [0])
            user_e.append(user_final_embedding)
            
            # print("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXuser_e:",(user_e))
            # torch.cat(user_e, dim=0)
            item_e = item_final_embedding
            i += 1

            print("Last Epoch's Test Data Recall -> ", recall_list[-1])
            print("Last Epoch's Test Data Precision -> ", precision_list[-1])
            print("Last Epoch's Test Data F1 score -> ", (2*precision_list[-1]*recall_list[-1])/(precision_list[-1]+recall_list[-1]))
            print("Last Epoch's Test Data NDCG -> ", ndcg_list[-1])
            print("Last Epoch's Test Data MAP -> ", map_list[-1])
            print("Last Epoch's Train Data Loss -> ", loss_list_epoch[-1])
            print("---------------------------------------------------------------------------------------------------------------------------")
    # user_e.reverse()
    user_e = torch.cat(user_e, dim=0)
    # user_e = torch.flip(user_e, [0])
    auc, test_topK_recall,  test_topK_precision, test_topK_ndcg, test_topK_map  = get_metrics(user_e.to('cpu'), item_e.to('cpu'), n_users, n_items, train_, test_, K=5)
    a.append(round(auc,4))
    r.append(round(test_topK_recall,4))
    p.append(round(test_topK_precision,4))
    n.append(round(test_topK_ndcg,4))
    m.append(round(test_topK_map,4))
    time_.append(time.time() - st)
    print(n)
    
```
a = numpy.array(a)
r = numpy.array(r)
p = numpy.array(p)
n = numpy.array(n)
t = numpy.array(time_)
m = numpy.array(m)
a_mean = np.mean(a)
t_mean = np.mean(t)
r_mean = np.mean(r)
p_mean = np.mean(p)
n_mean = np.mean(n)
m_mean = np.mean(m)
a_std = np.std(a)
r_std = np.std(r)
p_std = np.std(p)
n_std = np.std(n)
m_std = np.std(m)
print("Time: ", t_mean)
print("AUC: ", a_mean, a_std)
print("Recall: ", r_mean, r_std)
print("Precision: ", p_mean, p_std)
print("NDCG: ", n_mean, n_std)
print("MAP: ", m_mean, m_std)
```

```
user_e = torch.cat(user_e, 0)
test_topK_recall,  test_topK_precision, test_topK_ndcg, test_topK_map  = get_metrics(user_e.to('cpu'), item_e.to('cpu'), n_users, n_items, train_, test_, K=5)
print("test_topK_recall: ", test_topK_recall)
print("test_topK_precision: ", test_topK_precision)
print("test_topK_ndcg: ", test_topK_ndcg)
print("test_topK_map: ", test_topK_map)

```