Skip to content

Commit

Permalink
update 2.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
amazingDD committed Jul 21, 2022
1 parent 3500109 commit a17e6e6
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 184 deletions.
2 changes: 1 addition & 1 deletion daisy/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = 'v2.0.8'
__version__ = 'v2.1.0'
19 changes: 16 additions & 3 deletions daisy/model/AbstractRecommender.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import torch.nn as nn
import torch.optim as optim

from daisy.utils.config import initializer_param_config, initializer_config
from daisy.utils.loss import BPRLoss, TOP1Loss, HingeLoss


Expand All @@ -17,6 +16,20 @@ def __init__(self):
self.lr = 0.01
self.logger = None

self.initializer_param_config = {
'normal': {'mean':0.0, 'std':0.01},
'uniform': {'a':0.0, 'b':1.0},
'xavier_normal': {'gain':1.0},
'xavier_uniform': {'gain':1.0}
}

self.initializer_config = {
'normal': nn.init.normal_,
'uniform': nn.init.uniform_,
'xavier_normal': nn.init.xavier_normal_,
'xavier_uniform': nn.init.xavier_uniform_
}

def calc_loss(self, batch):
raise NotImplementedError

Expand Down Expand Up @@ -55,11 +68,11 @@ def _build_optimizer(self, **kwargs):

def _init_weight(self, m):
if isinstance(m, nn.Linear):
initializer_config[self.initializer](m.weight, **initializer_param_config[self.initializer])
self.initializer_config[self.initializer](m.weight, **self.initializer_param_config[self.initializer])
if m.bias is not None:
nn.init.constant_(m.bias.data, 0.)
elif isinstance(m, nn.Embedding):
initializer_config[self.initializer](m.weight, **initializer_param_config[self.initializer])
self.initializer_config[self.initializer](m.weight, **self.initializer_param_config[self.initializer])
else:
pass

Expand Down
10 changes: 4 additions & 6 deletions daisy/model/NFMRecommender.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
import torch.nn as nn

from daisy.model.AbstractRecommender import GeneralRecommender
from daisy.utils.config import initializer_param_config, initializer_config


class NFM(GeneralRecommender):
def __init__(self, config):
Expand Down Expand Up @@ -93,17 +91,17 @@ def __init__(self, config):
self._init_weight()

def _init_weight(self):
initializer_config[self.initializer](self.embed_user.weight, **initializer_param_config[self.initializer])
initializer_config[self.initializer](self.embed_item.weight, **initializer_param_config[self.initializer])
self.initializer_config[self.initializer](self.embed_user.weight, **self.initializer_param_config[self.initializer])
self.initializer_config[self.initializer](self.embed_item.weight, **self.initializer_param_config[self.initializer])
nn.init.constant_(self.u_bias.weight, 0.0)
nn.init.constant_(self.i_bias.weight, 0.0)

# for deep layers
if self.num_layers > 0: # len(self.layers)
for m in self.deep_layers:
if isinstance(m, nn.Linear):
initializer_config[self.initializer](m.weight, **initializer_param_config[self.initializer])
initializer_config[self.initializer](self.prediction.weight, **initializer_param_config[self.initializer])
self.initializer_config[self.initializer](m.weight, **self.initializer_param_config[self.initializer])
self.initializer_config[self.initializer](self.prediction.weight, **self.initializer_param_config[self.initializer])
else:
nn.init.constant_(self.prediction.weight, 1.0)

Expand Down
3 changes: 1 addition & 2 deletions daisy/model/NGCFRecommender.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import torch.nn.functional as F

from daisy.model.AbstractRecommender import GeneralRecommender
from daisy.utils.config import initializer_config


class NGCF(GeneralRecommender):
Expand Down Expand Up @@ -62,7 +61,7 @@ def __init__(self, config):
self.sparse_norm_adj.to(self.device)

def init_weight(self):
initializer = initializer_config[self.initializer]
initializer = self.initializer_config[self.initializer]

embedding_dict = nn.ParameterDict({
'user_emb': nn.Parameter(initializer(torch.empty(self.n_user, self.emb_size))),
Expand Down
15 changes: 7 additions & 8 deletions daisy/model/NeuMFRecommender.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import torch.nn as nn

from daisy.model.AbstractRecommender import GeneralRecommender
from daisy.utils.config import initializer_param_config, initializer_config


class NeuMF(GeneralRecommender):
Expand Down Expand Up @@ -80,17 +79,17 @@ def __init__(self, config):

def _init_weight(self):
if not self.model == 'NeuMF-pre':
initializer_config[self.initializer](self.embed_user_GMF.weight, **initializer_param_config[self.initializer])
initializer_config[self.initializer](self.embed_item_GMF.weight, **initializer_param_config[self.initializer])
initializer_config[self.initializer](self.embed_user_MLP.weight, **initializer_param_config[self.initializer])
initializer_config[self.initializer](self.embed_item_MLP.weight, **initializer_param_config[self.initializer])
self.initializer_config[self.initializer](self.embed_user_GMF.weight, **self.initializer_param_config[self.initializer])
self.initializer_config[self.initializer](self.embed_item_GMF.weight, **self.initializer_param_config[self.initializer])
self.initializer_config[self.initializer](self.embed_user_MLP.weight, **self.initializer_param_config[self.initializer])
self.initializer_config[self.initializer](self.embed_item_MLP.weight, **self.initializer_param_config[self.initializer])

for m in self.MLP_layers:
if isinstance(m, nn.Linear):
initializer_config[self.initializer](m.weight)
initializer_config[self.initializer](
self.initializer_config[self.initializer](m.weight)
self.initializer_config[self.initializer](
self.predict_layer.weight,
**initializer_param_config[self.initializer])
**self.initializer_param_config[self.initializer])
for m in self.modules():
if isinstance(m, nn.Linear) and m.bias is not None:
m.bias.data.zero_()
Expand Down
110 changes: 1 addition & 109 deletions daisy/utils/config.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,13 @@
import os
import re
import yaml
import torch
import random
import logging
import colorlog
import numpy as np
from colorama import init

import torch
import torch.nn as nn

from daisy.model.KNNCFRecommender import ItemKNNCF
from daisy.model.PureSVDRecommender import PureSVD
from daisy.model.SLiMRecommender import SLiM
from daisy.model.PopRecommender import MostPop
from daisy.model.MFRecommender import MF
from daisy.model.FMRecommender import FM
from daisy.model.Item2VecRecommender import Item2Vec
from daisy.model.NeuMFRecommender import NeuMF
from daisy.model.NFMRecommender import NFM
from daisy.model.NGCFRecommender import NGCF
from daisy.model.VAECFRecommender import VAECF
from daisy.model.EASERecommender import EASE

from daisy.utils.metrics import Precision, Recall, NDCG, MRR, MAP, HR, F1, AUC, Coverage, Diversity, Popularity
from daisy.utils.parser import parse_args
from daisy.utils.utils import ensure_dir, get_local_time

Expand All @@ -34,98 +18,6 @@
'CRITICAL': 'red',
}

tune_params_config = {
'mostpop': [],
'itemknn': ['maxk'],
'puresvd': ['factors'],
'slim': ['alpha', 'elastic'],
'mf': ['num_ng', 'factors', 'lr', 'batch_size', 'reg_1', 'reg_2'],
'fm': ['num_ng', 'factors', 'lr', 'batch_size', 'reg_1', 'reg_2'],
'neumf': ['num_ng', 'factors', 'num_layers', 'dropout', 'lr', 'batch_size', 'reg_1', 'reg_2'],
'nfm': ['num_ng', 'factors', 'num_layers', 'dropout', 'lr', 'batch_size', 'reg_1', 'reg_2'],
'ngcf': ['num_ng', 'factors', 'node_dropout', 'mess_dropout', 'batch_size', 'lr', 'reg_1', 'reg_2'],
'multi-vae': ['latent_dim', 'dropout','batch_size', 'lr', 'anneal_cap'],
'ease': ['reg'],
'item2vec': ['context_window', 'rho', 'lr', 'factors'],
}

param_type_config = {
'num_layers': 'int',
'maxk': 'int',
'factors': 'int',
'alpha': 'float',
'elastic': 'float',
'num_ng': 'int',
'lr': 'float',
'batch_size': 'int',
'reg_1': 'float',
'reg_2': 'float',
'dropout': 'float',
'node_dropout': 'float',
'mess_dropout': 'float',
'latent_dim': 'int',
'anneal_cap': 'float',
'reg': 'float',
'context_window': 'int',
'rho': 'float'
}

metrics_config = {
"recall": Recall,
"mrr": MRR,
"ndcg": NDCG,
"hr": HR,
"map": MAP,
"precision": Precision,
"f1": F1,
"auc": AUC,
"coverage": Coverage,
"diversity": Diversity,
"popularity": Popularity,
}

metrics_name_config = {
"recall": 'Recall',
"mrr": 'MRR',
"ndcg": 'NDCG',
"hr": 'Hit Ratio',
"precision": 'Precision',
"f1": 'F1-score',
"auc": 'AUC',
"coverage": 'Coverage',
"diversity": 'Diversity',
"popularity": 'Average Popularity',
}

model_config = {
'mostpop': MostPop,
'slim': SLiM,
'itemknn': ItemKNNCF,
'puresvd': PureSVD,
'mf': MF,
'fm': FM,
'ngcf': NGCF,
'neumf': NeuMF,
'nfm': NFM,
'multi-vae': VAECF,
'item2vec': Item2Vec,
'ease': EASE,
}

initializer_param_config = {
'normal': {'mean':0.0, 'std':0.01},
'uniform': {'a':0.0, 'b':1.0},
'xavier_normal': {'gain':1.0},
'xavier_uniform': {'gain':1.0}
}

initializer_config = {
'normal': nn.init.normal_,
'uniform': nn.init.uniform_,
'xavier_normal': nn.init.xavier_normal_,
'xavier_uniform': nn.init.xavier_uniform_
}

def init_seed(seed, reproducibility):
'''
init random seed for random functions in numpy, torch, cuda and cudnn
Expand Down
80 changes: 75 additions & 5 deletions daisy/utils/metrics.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,60 @@
import os
import numpy as np
import pandas as pd

metrics_name_config = {
"recall": 'Recall',
"mrr": 'MRR',
"ndcg": 'NDCG',
"hr": 'Hit Ratio',
"precision": 'Precision',
"f1": 'F1-score',
"auc": 'AUC',
"coverage": 'Coverage',
"diversity": 'Diversity',
"popularity": 'Average Popularity',
}

def calc_ranking_results(test_ur, pred_ur, test_u, config):
'''
calculate metrics with prediction results and candidates sets
Parameters
----------
test_ur : defaultdict(set)
groud truths for user in test set
pred_ur : np.array
rank list for user in test set
test_u : list
the user in order from test set
'''
logger = config['logger']
path = config['res_path']
if not os.path.exists(path):
os.makedirs(path)

metric = Metric(config)
res = pd.DataFrame({
'KPI@K': [metrics_name_config[kpi_name] for kpi_name in config['metrics']]
})

common_ks = [1, 5, 10, 20, 30, 50]
if config['topk'] not in common_ks:
common_ks.append(config['topk'])
for topk in common_ks:
if topk > config['topk']:
continue
else:
rank_list = pred_ur[:, :topk]
kpis = metric.run(test_ur, rank_list, test_u)
if topk == 10:
for kpi_name, kpi_res in zip(config['metrics'], kpis):
kpi_name = metrics_name_config[kpi_name]
logger.info(f'{kpi_name}@{topk}: {kpi_res:.4f}')

res[topk] = np.array(kpis)

from daisy.utils.config import metrics_config
return res

class Metric(object):
def __init__(self, config) -> None:
Expand All @@ -13,13 +67,29 @@ def run(self, test_ur, pred_ur, test_u):
res = []
for mc in self.metrics:
if mc == "coverage":
kpi = metrics_config[mc](pred_ur, self.item_num)
kpi = Coverage(pred_ur, self.item_num)
elif mc == "popularity":
kpi = metrics_config[mc](test_ur, pred_ur, test_u, self.item_pop)
kpi = Popularity(test_ur, pred_ur, test_u, self.item_pop)
elif mc == "diversity":
kpi = metrics_config[mc](pred_ur, self.i_categories)
kpi = Diversity(pred_ur, self.i_categories)
elif mc == 'ndcg':
kpi = NDCG(test_ur, pred_ur, test_u)
elif mc == 'mrr':
kpi = MRR(test_ur, pred_ur, test_u)
elif mc == 'recall':
kpi = Recall(test_ur, pred_ur, test_u)
elif mc == 'precision':
kpi = Precision(test_ur, pred_ur, test_u)
elif mc == 'hr':
kpi = HR(test_ur, pred_ur, test_u)
elif mc == 'map':
kpi = MAP(test_ur, pred_ur, test_u)
elif kpi == 'f1':
kpi = F1(test_ur, pred_ur, test_u)
elif kpi == 'auc':
kpi = AUC(test_ur, pred_ur, test_u)
else:
kpi = metrics_config[mc](test_ur, pred_ur, test_u)
raise ValueError(f'Invalid metric name {mc}')

res.append(kpi)

Expand Down
Loading

0 comments on commit a17e6e6

Please sign in to comment.