FranKGraphBench 0.0.1a0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. FranKGraphBench-0.0.1a0.dist-info/LICENSE +201 -0
  2. FranKGraphBench-0.0.1a0.dist-info/METADATA +189 -0
  3. FranKGraphBench-0.0.1a0.dist-info/RECORD +70 -0
  4. FranKGraphBench-0.0.1a0.dist-info/WHEEL +6 -0
  5. FranKGraphBench-0.0.1a0.dist-info/entry_points.txt +3 -0
  6. FranKGraphBench-0.0.1a0.dist-info/top_level.txt +2 -0
  7. data_integration/__init__.py +47 -0
  8. data_integration/__main__.py +4 -0
  9. data_integration/dataset.py +270 -0
  10. data_integration/dataset2class.py +34 -0
  11. data_integration/datasets/__init__.py +0 -0
  12. data_integration/datasets/amazon.py +140 -0
  13. data_integration/datasets/book-crossing.py +98 -0
  14. data_integration/datasets/douban-movie.py +75 -0
  15. data_integration/datasets/lastfm.py +215 -0
  16. data_integration/datasets/movielens.py +375 -0
  17. data_integration/datasets/steam.py +98 -0
  18. data_integration/datasets/worker.py +31 -0
  19. data_integration/datasets/yelp.py +156 -0
  20. framework/__init__.py +17 -0
  21. framework/__main__.py +4 -0
  22. framework/dataloader/__init__.py +0 -0
  23. framework/dataloader/dataloader.py +52 -0
  24. framework/dataloader/dataset/__init__.py +0 -0
  25. framework/dataloader/dataset/dataset.py +38 -0
  26. framework/dataloader/edge_splitter/__init__.py +0 -0
  27. framework/dataloader/edge_splitter/edge_splitter.py +163 -0
  28. framework/dataloader/graph/__init__.py +0 -0
  29. framework/dataloader/graph/graph.py +314 -0
  30. framework/dataloader/graph/node.py +80 -0
  31. framework/dataloader/preprocess/__init__.py +0 -0
  32. framework/dataloader/preprocess/methods.py +47 -0
  33. framework/evaluator/__init__.py +0 -0
  34. framework/evaluator/metric.py +39 -0
  35. framework/evaluator/metric2class.py +10 -0
  36. framework/evaluator/metrics/__init__.py +0 -0
  37. framework/evaluator/metrics/map.py +33 -0
  38. framework/evaluator/metrics/ndcg.py +49 -0
  39. framework/experiment.py +120 -0
  40. framework/recommender/__init__.py +0 -0
  41. framework/recommender/model2class.py +46 -0
  42. framework/recommender/models/__init__.py +0 -0
  43. framework/recommender/models/complEx/__init__.py +0 -0
  44. framework/recommender/models/complEx/model.py +193 -0
  45. framework/recommender/models/deep_walk_based/__init__.py +0 -0
  46. framework/recommender/models/deep_walk_based/model.py +149 -0
  47. framework/recommender/models/distMult/__init__.py +0 -0
  48. framework/recommender/models/distMult/model.py +187 -0
  49. framework/recommender/models/ePHEN/__init__.py +0 -0
  50. framework/recommender/models/ePHEN/model.py +159 -0
  51. framework/recommender/models/rESCAL/__init__.py +0 -0
  52. framework/recommender/models/rESCAL/model.py +184 -0
  53. framework/recommender/models/rotatE/__init__.py +0 -0
  54. framework/recommender/models/rotatE/model.py +195 -0
  55. framework/recommender/models/transD/__init__.py +0 -0
  56. framework/recommender/models/transD/model.py +190 -0
  57. framework/recommender/models/transE/__init__.py +0 -0
  58. framework/recommender/models/transE/model.py +194 -0
  59. framework/recommender/models/transH/__init__.py +0 -0
  60. framework/recommender/models/transH/model.py +193 -0
  61. framework/recommender/models/transR/__init__.py +0 -0
  62. framework/recommender/models/transR/model.py +190 -0
  63. framework/recommender/models/tuckER/__init__.py +0 -0
  64. framework/recommender/models/tuckER/model.py +199 -0
  65. framework/recommender/recommender.py +32 -0
  66. framework/recommender/utils/__init__.py +0 -0
  67. framework/recommender/utils/walker.py +185 -0
  68. framework/reporter/__init__.py +0 -0
  69. framework/reporter/report.py +101 -0
  70. framework/utils.py +10 -0
@@ -0,0 +1,120 @@
1
+ import yaml
2
+ import json
3
+ import importlib
4
+ from collections import defaultdict
5
+ from copy import deepcopy
6
+
7
+ from .dataloader.dataloader import load, preprocess, split
8
+ from .recommender.model2class import model2class
9
+ from .evaluator.metric2class import metric2class
10
+ from .reporter.report import Reporter
11
+ from .reporter.report import ExecutionTimesReporter
12
+
13
+ import numpy as np
14
+ import time
15
+ import cpuinfo
16
+ import psutil
17
+ import GPUtil
18
+
19
+ def _load_model_class(name):
20
+ # relative path from root
21
+ module_name = f'framework.recommender.models.{model2class[name]["submodule"]}'
22
+ class_name = model2class[name]['class']
23
+ return module_name, class_name
24
+
25
+ def _load_metric_class(name):
26
+ # relative path from root
27
+ module_name = f'framework.evaluator.{metric2class[name]["submodule"]}'
28
+ class_name = metric2class[name]['class']
29
+ return module_name, class_name
30
+
31
+ def run(config_path):
32
+ config = None
33
+ with open(config_path, 'r') as f:
34
+ config = yaml.safe_load(f)
35
+
36
+ experiment = config['experiment']
37
+
38
+ G = load(**experiment['dataset'])
39
+ preprocess(G, experiment['preprocess'])
40
+ print(f'Final graph: {G.info()}')
41
+
42
+ # Extracting evaluation metrics
43
+ evaluation_config = experiment['evaluation']
44
+ eval_metrics = []
45
+ for metric in evaluation_config['metrics']:
46
+ module_name, class_name = _load_metric_class(metric)
47
+ metric_class = getattr(importlib.import_module(module_name), class_name)
48
+ k = evaluation_config['k']
49
+ relevance_threshold = evaluation_config['relevance_threshold']
50
+ metric_instance = metric_class(k, relevance_threshold)
51
+ eval_metrics.append(metric_instance)
52
+
53
+ # Loop over dataset (specially if its a k-fold split)
54
+ model_metrics = defaultdict(list)
55
+ model_execution_times = defaultdict(list)
56
+ for fold, dataset in enumerate(split(G, **experiment['split'])):
57
+ print(f'{fold+1}-Fold: evaluating model ...')
58
+ print(G.info())
59
+
60
+ for model_config in experiment['models']:
61
+ start_time = time.time()
62
+ # Loading model dinamically
63
+ module_name, class_name = _load_model_class(model_config['name'])
64
+ model = getattr(importlib.import_module(module_name), class_name)
65
+ model = model(model_config['config'], **model_config['parameters'])
66
+
67
+ # Training model
68
+ G_train, ratings_train = dataset.get_train_data()
69
+ print(f'Training model: {model.name()}...')
70
+
71
+ model.train(G_train, ratings_train)
72
+ # Getting recomendations
73
+ # From this data I can evaluate the result for validation and test data
74
+ recs = model.get_recommendations(k=k)
75
+
76
+ ratings_test = dataset.get_test_data()
77
+
78
+ model_execution_times[model.name()].append(time.time() - start_time)
79
+
80
+ metric_vals = []
81
+ for metric in eval_metrics:
82
+ metric_val = metric.eval(ratings_test, recs)
83
+ print(f'{metric.name()}: {metric_val}')
84
+ metric_vals.append(metric_val)
85
+
86
+ print(f'Execution time: {model_execution_times[model.name()][-1]}s')
87
+
88
+ model_metrics[model.name()].append(metric_vals)
89
+
90
+ print('----'*20)
91
+
92
+
93
+ print(f'Metrics final result')
94
+ for model, metrics in model_metrics.items():
95
+ print(f'{model}')
96
+ metrics_mean = np.array(metrics).mean(axis=0)
97
+ metrics_std = np.array(metrics).std(axis=0)
98
+ for idx, (metric_mean, metric_std) in enumerate(zip(metrics_mean, metrics_std)):
99
+ print(f'\t{eval_metrics[idx].name()} mean: {metric_mean} +- {metric_std}')
100
+ print('')
101
+ print(f'Execution times final result')
102
+ for model, execution_times in model_execution_times.items():
103
+ print(f'model: {model}; CPU: {cpuinfo.get_cpu_info()["brand_raw"]}; RAM: {round(psutil.virtual_memory().total/(1024**3))}GB; GPUs: {[gpu.name for gpu in GPUtil.getGPUs()]}')
104
+ execution_times_mean = np.array(execution_times).mean(axis=0)
105
+ execution_times_std = np.array(execution_times).std(axis=0)
106
+ print(f'\tmean: {execution_times_mean}s +- {execution_times_std}s')
107
+
108
+ reporter = Reporter(experiment['report']['file'], eval_metrics)
109
+ reporter.report(model_metrics)
110
+
111
+ if 'execution_times' in experiment['report']:
112
+ et_reporter = ExecutionTimesReporter(experiment['report']['execution_times']['file'])
113
+ et_reporter.report(model_execution_times)
114
+
115
+
116
+
117
+
118
+
119
+
120
+
File without changes
@@ -0,0 +1,46 @@
1
+ model2class = {
2
+ 'deepwalk_based': {
3
+ 'submodule': 'deep_walk_based.model',
4
+ 'class': 'DeepWalkBased'
5
+ },
6
+ 'transE': {
7
+ 'submodule': 'transE.model',
8
+ 'class': 'TransE'
9
+ },
10
+ 'transH': {
11
+ 'submodule': 'transH.model',
12
+ 'class': 'TransH'
13
+ },
14
+ 'transR': {
15
+ 'submodule': 'transR.model',
16
+ 'class': 'TransR'
17
+ },
18
+ 'transD': {
19
+ 'submodule': 'transD.model',
20
+ 'class': 'TransD'
21
+ },
22
+ 'rotatE': {
23
+ 'submodule': 'rotatE.model',
24
+ 'class': 'RotatE'
25
+ },
26
+ 'tuckER': {
27
+ 'submodule': 'tuckER.model',
28
+ 'class': 'TuckER'
29
+ },
30
+ 'distMult': {
31
+ 'submodule': 'distMult.model',
32
+ 'class': 'DistMult'
33
+ },
34
+ 'rESCAL': {
35
+ 'submodule': 'rESCAL.model',
36
+ 'class': 'RESCAL'
37
+ },
38
+ 'complEx': {
39
+ 'submodule': 'complEx.model',
40
+ 'class': 'ComplEx'
41
+ },
42
+ 'ePHEN': {
43
+ 'submodule': 'ePHEN.model',
44
+ 'class': 'EPHEN'
45
+ }
46
+ }
File without changes
File without changes
@@ -0,0 +1,193 @@
1
+ from framework.dataloader.graph.node import UserNode, ItemNode
2
+ from ...recommender import Recommender
3
+
4
+ from pykeen.pipeline import pipeline
5
+ from pykeen.triples import TriplesFactory
6
+ from pykeen.datasets import Dataset
7
+ from sklearn.neighbors import NearestNeighbors
8
+
9
+
10
+ import numpy as np
11
+ import torch
12
+ import typing as t
13
+
14
+ """
15
+ Recommender base class
16
+ """
17
+
18
+
19
+ class ComplEx(Recommender):
20
+ def __init__(
21
+ self,
22
+ config: dict,
23
+ embedding_dim: int = 200,
24
+ entity_initializer=None,
25
+ relation_initializer=None,
26
+ regularizer=None,
27
+ regularizer_kwargs=None,
28
+ epochs: int = 5,
29
+ seed: int = 42,
30
+ evaluation_fallback: bool = True,
31
+ all_recs: bool = False,
32
+ triples: str = "all",
33
+ ):
34
+ super().__init__(config)
35
+ self.embedding_dim = embedding_dim
36
+ self.entity_initializer = entity_initializer
37
+ self.relation_initializer = relation_initializer
38
+ self.regularizer = regularizer
39
+ self.regularizer_kwargs = regularizer_kwargs
40
+ self.epochs = epochs
41
+ self.seed = seed
42
+ self.evaluation_fallback = evaluation_fallback
43
+ self.all_recs = all_recs
44
+ self.triples = triples
45
+ self._triples = None
46
+ self._embedding = {}
47
+
48
+ def name(self):
49
+ text = "ComplEx based model + cosine similarity"
50
+ text += f";embedding_dim={self.embedding_dim};entity_initializer={self.entity_initializer};relation_initializer={self.relation_initializer};regularizer={self.regularizer};epochs={self.epochs};seed={self.seed}"
51
+ return text
52
+
53
+ def train(self, G_train, ratings_train):
54
+ self.G_train = G_train
55
+ if self.triples == "all":
56
+ self._triples = G_train.get_all_triples()
57
+ elif self.triples == "ratings":
58
+ self._triples = G_train.get_ratings_triples()
59
+ self.fit()
60
+
61
+ def get_recommendations(self, k: int = 5) -> t.Dict[UserNode, t.List[ItemNode]]:
62
+ """
63
+ :param k: cutoff recommendation (int)
64
+ :return dict of recommendations for each user
65
+ of type {user1: [item1, item2]}
66
+ """
67
+
68
+ users = list(self.G_train.get_user_nodes())
69
+ items = list(self.G_train.get_item_nodes())
70
+
71
+ users_embeddings = np.array([self._embedding[user] for user in users])
72
+ items_embeddings = np.array([self._embedding[item] for item in items])
73
+
74
+ n_neighbors = self._get_n_neighbors(users, items, k)
75
+ knn = NearestNeighbors(n_neighbors=n_neighbors, metric="cosine")
76
+ knn.fit(items_embeddings)
77
+ rec_indices = knn.kneighbors(users_embeddings, return_distance=False)
78
+
79
+ recommendations = {}
80
+ for user_idx, user in enumerate(users):
81
+ rated_items = self.G_train.get_user_rated_items(user)
82
+ recs = []
83
+ for item_idx in rec_indices[user_idx]:
84
+ item = items[item_idx]
85
+ if item not in rated_items:
86
+ recs.append(item)
87
+ if not self.all_recs and len(recs) == k:
88
+ break
89
+
90
+ recommendations[user] = recs
91
+
92
+ return recommendations
93
+
94
+ def get_user_recommendation(self, user: UserNode, k: int = 5):
95
+ raise NotImplementedError(
96
+ "Override get_user_recommendation() for your model subclass."
97
+ )
98
+
99
+ def fit(self):
100
+ triples = TriplesFactory.from_labeled_triples(
101
+ self._triples[["head", "relation", "tail"]].values,
102
+ create_inverse_triples=False,
103
+ entity_to_id=None,
104
+ relation_to_id=None,
105
+ compact_id=False,
106
+ filter_out_candidate_inverse_relations=True,
107
+ metadata=None,
108
+ )
109
+
110
+ dataset = Dataset.from_tf(triples, ratios=[0.95, 0.05, 0.0])
111
+ entity_to_id = dataset.entity_to_id
112
+
113
+ result = pipeline(
114
+ dataset=dataset,
115
+ model="ComplEx",
116
+ model_kwargs=dict(
117
+ embedding_dim=self.embedding_dim,
118
+ entity_initializer=self.entity_initializer,
119
+ relation_initializer=self.relation_initializer,
120
+ regularizer=self.regularizer,
121
+ regularizer_kwargs=self.regularizer_kwargs,
122
+ ),
123
+ epochs = self.epochs,
124
+ random_seed = self.seed,
125
+ evaluation_fallback= self.evaluation_fallback,
126
+ )
127
+ model = result.model
128
+
129
+ users_iter = self.G_train.get_user_nodes()
130
+ items_iter = self.G_train.get_item_nodes()
131
+
132
+ users = list(users_iter)
133
+ items = list(items_iter)
134
+
135
+ # deal with entities that might have not been embedded
136
+ users_indices, users_no_embedding = [], []
137
+ for user in users_iter:
138
+ try:
139
+ users_indices.append(entity_to_id[user.__str__()])
140
+ except:
141
+ users_no_embedding.append(user)
142
+ users.remove(user)
143
+ continue
144
+ users_indices = torch.LongTensor(users_indices)
145
+
146
+ items_indices, items_no_embedding = [], []
147
+ for item in items_iter:
148
+ try:
149
+ items_indices.append(entity_to_id[item.__str__()])
150
+ except:
151
+ items_no_embedding.append(item)
152
+ items.remove(item)
153
+ continue
154
+ items_indices = torch.LongTensor(items_indices)
155
+
156
+ users_embeddings = (model.entity_representations[0](indices=users_indices).detach().cpu().numpy())
157
+ items_embeddings = (model.entity_representations[0](indices=items_indices).detach().cpu().numpy())
158
+
159
+ concat_users_embeddings, concat_items_embeddings = np.zeros((len(users_embeddings), self.embedding_dim*2)), np.zeros((len(items_embeddings), self.embedding_dim*2))
160
+
161
+ # complex data not supported by sklearn
162
+ for index, ue in enumerate(users_embeddings):
163
+ concat_users_embeddings[index] = np.concatenate((np.real(ue), np.imag(ue)))
164
+ for index, ie in enumerate(items_embeddings):
165
+ concat_items_embeddings[index] = np.concatenate((np.real(ie), np.imag(ie)))
166
+
167
+ if len(users_no_embedding) > 0:
168
+ concat_users_embeddings = np.concatenate((concat_users_embeddings, np.zeros((len(users_no_embedding), self.embedding_dim*2), dtype=concat_users_embeddings.dtype)), axis=0)
169
+ users = users + users_no_embedding
170
+
171
+ if len(items_no_embedding) > 0:
172
+ concat_items_embeddings = np.concatenate((concat_items_embeddings, np.zeros((len(items_no_embedding), self.embedding_dim*2), dtype=concat_items_embeddings.dtype)), axis=0)
173
+ items = items + items_no_embedding
174
+
175
+ for index, user in enumerate(users):
176
+ self._embedding[user] = concat_users_embeddings[index]
177
+
178
+ for index, item in enumerate(items):
179
+ self._embedding[item] = concat_items_embeddings[index]
180
+
181
+ def _get_n_neighbors(self, users, items, top_k):
182
+ n_neighbors = 0
183
+ if self.all_recs:
184
+ n_neighbors = len(items)
185
+ else:
186
+ max_recs = 0
187
+ for user in users:
188
+ rated_items = self.G_train.get_user_rated_items(user)
189
+ if len(rated_items) > max_recs:
190
+ max_recs = len(rated_items)
191
+ n_neighbors = min(max_recs + top_k, len(items))
192
+
193
+ return n_neighbors
@@ -0,0 +1,149 @@
1
+ from framework.dataloader.graph.node import UserNode
2
+ from ...recommender import Recommender
3
+ from ...utils.walker import BiasedRandomWalker
4
+
5
+ import walker
6
+ import numpy as np
7
+ import networkx as nx
8
+ from gensim.models.word2vec import Word2Vec
9
+ from sklearn.neighbors import NearestNeighbors
10
+ from copy import deepcopy
11
+
12
+ from ....dataloader.graph.node import ItemNode
13
+
14
+ class DeepWalkBased(Recommender):
15
+ def __init__(
16
+ self,
17
+ config : dict,
18
+ walk_len : int,
19
+ n_walks : int,
20
+ p : float = 1.0,
21
+ q : float = 1.0,
22
+ embedding_size : int = 64,
23
+ workers : int = 4,
24
+ window_size : int = 3,
25
+ epochs: int = 1,
26
+ learning_rate : float = 0.05,
27
+ min_count : int = 1,
28
+ seed : int = 42,
29
+ all_recs : bool = False
30
+ ):
31
+ super().__init__(config)
32
+ self.walk_len = walk_len
33
+ self.n_walks = n_walks
34
+ self.embedding_size = embedding_size
35
+ self.p, self.q = p, q
36
+ self.workers = workers
37
+ self.window_size = window_size
38
+ self.epochs = epochs
39
+ self.learning_rate = learning_rate
40
+ self.min_count = min_count
41
+ self.seed = seed
42
+ self.all_recs = all_recs
43
+ self._embedding = {}
44
+
45
+ def name(self):
46
+ text = 'Node2Vec based model + cosine similarity'
47
+ text += f';q={self.q};p={self.p};embedding_size={self.embedding_size}'
48
+ return text
49
+
50
+ def train(self, G_train, ratings_train):
51
+ self.G_train = G_train
52
+ self.fit()
53
+
54
+ def get_recommendations(self, k: int = 5):
55
+ # Set doesnt guarantee the order of set elements
56
+ users = list(self.G_train.get_user_nodes())
57
+ items = list(self.G_train.get_item_nodes())
58
+
59
+ users_embeddings = np.array([self._embedding[user] for user in users])
60
+ items_embeddings = np.array([self._embedding[item] for item in items])
61
+
62
+ n_neighbors = self._get_n_neighbors(users, items, k)
63
+ knn = NearestNeighbors(n_neighbors=n_neighbors, metric='cosine')
64
+ knn.fit(items_embeddings)
65
+ rec_indices = knn.kneighbors(users_embeddings, return_distance=False)
66
+
67
+ recommendations = {}
68
+ for user_idx, user in enumerate(users):
69
+ rated_items = self.G_train.get_user_rated_items(user)
70
+ recs = []
71
+ for item_idx in rec_indices[user_idx]:
72
+ item = items[item_idx]
73
+ if item not in rated_items:
74
+ recs.append(item)
75
+ if not self.all_recs and len(recs) == k:
76
+ break
77
+
78
+ recommendations[user] = recs
79
+
80
+ return recommendations
81
+
82
+ def get_user_recommendation(self, user: UserNode, k: int = 5):
83
+ items = self.G_train.get_item_nodes()
84
+ rated_items = self.G_train.get_user_rated_items(user)
85
+ unrated_items = list(items - rated_items)
86
+ user_embedding = self._embedding[user].reshape(1, -1)
87
+ items_embeddings = np.array([self._embedding[item] for item in unrated_items])
88
+ n_neighbors = k if not self.all_recs else len(unrated_items)
89
+ knn = NearestNeighbors(n_neighbors=n_neighbors, metric='cosine')
90
+ knn.fit(items_embeddings)
91
+ rec_indices = knn.kneighbors(user_embedding, return_distance=False)
92
+
93
+ recs = []
94
+ for idx in rec_indices[0]:
95
+ recs.append(unrated_items[idx])
96
+
97
+ return recs
98
+
99
+
100
+ def fit(self):
101
+ graph = self.G_train.convert_node_labels_to_integer()
102
+ self.walks = walker.random_walks(
103
+ graph, n_walks=self.n_walks, walk_len=self.walk_len
104
+ )
105
+ print(f'Random walk shape: {self.walks.shape}')
106
+ self.walks = self.walks.tolist()
107
+
108
+ model = Word2Vec(
109
+ self.walks,
110
+ sg=1,
111
+ hs=1,
112
+ alpha=self.learning_rate,
113
+ epochs=self.epochs,
114
+ vector_size=self.embedding_size,
115
+ window=self.window_size,
116
+ min_count=self.min_count,
117
+ workers=self.workers,
118
+ seed=self.seed
119
+ )
120
+
121
+ for node in graph.nodes():
122
+ self._embedding[graph.nodes[node]['old_label']] = model.wv[node]
123
+
124
+ # self.G_train.convert_back()
125
+
126
+ # def _convert_node_labels_to_integer(self, G):
127
+ # N = G.number_of_nodes()
128
+ # mapping = dict(zip(G.nodes(), range(0, N)))
129
+ # nx.relabel_nodes(G, mapping, copy=False)
130
+ # nx.set_node_attributes(G, {v: k for k, v in mapping.items()}, 'old_label')
131
+
132
+ def _convert_back(self, G):
133
+ mapping = {node: G.nodes[node]['old_label'] for node in G.nodes()}
134
+ nx.relabel_nodes(G, mapping, copy=False)
135
+ nx.set_node_attributes(G, {v: k for k, v in mapping.items()}, 'old_label')
136
+
137
+ def _get_n_neighbors(self, users, items, top_k):
138
+ n_neighbors = 0
139
+ if self.all_recs:
140
+ n_neighbors = len(items)
141
+ else:
142
+ max_recs = 0
143
+ for user in users:
144
+ rated_items = self.G_train.get_user_rated_items(user)
145
+ if len(rated_items) > max_recs:
146
+ max_recs = len(rated_items)
147
+ n_neighbors = min(max_recs + top_k, len(items))
148
+
149
+ return n_neighbors
File without changes