576 KiB
576 KiB
Self made RP3-beta
import helpers
import pandas as pd
import numpy as np
import scipy.sparse as sparse
from collections import defaultdict
from itertools import chain
import random
import time
import matplotlib.pyplot as plt
train_read=pd.read_csv('./Datasets/ml-100k/train.csv', sep='\t', header=None)
test_read=pd.read_csv('./Datasets/ml-100k/test.csv', sep='\t', header=None)
train_ui, test_ui, user_code_id, user_id_code, item_code_id, item_id_code = helpers.data_to_csr(train_read, test_read)
class RP3Beta():
def fit(self, train_ui, alpha, beta):
"""We weight our edges by user's explicit ratings so if user rated movie high we'll follow that path
with higher probability."""
self.train_ui=train_ui
self.train_iu=train_ui.transpose()
self.alpha = alpha
self.beta = beta
# Define Pui
Pui=sparse.csr_matrix(self.train_ui/self.train_ui.sum(axis=1))
# Define Piu
to_divide=np.vectorize(lambda x: x if x>0 else 1)(self.train_iu.sum(axis=1)) # to avoid dividing by zero
Piu=sparse.csr_matrix(self.train_iu/to_divide)
item_orders=(self.train_ui>0).sum(axis=0)
Pui = Pui.power(self.alpha)
Piu = Piu.power(self.alpha)
P3=Pui*Piu*Pui
P3/=np.power(np.vectorize(lambda x: x if x>0 else 1)(item_orders), self.beta)
self.estimations=np.array(P3)
def recommend(self, user_code_id, item_code_id, topK=10):
top_k = defaultdict(list)
for nb_user, user in enumerate(self.estimations):
user_rated=self.train_ui.indices[self.train_ui.indptr[nb_user]:self.train_ui.indptr[nb_user+1]]
for item, score in enumerate(user):
if item not in user_rated and not np.isnan(score):
top_k[user_code_id[nb_user]].append((item_code_id[item], score))
result=[]
# Let's choose k best items in the format: (user, item1, score1, item2, score2, ...)
for uid, item_scores in top_k.items():
item_scores.sort(key=lambda x: x[1], reverse=True)
result.append([uid]+list(chain(*item_scores[:topK])))
return result
def estimate(self, user_code_id, item_code_id, test_ui):
result=[]
for user, item in zip(*test_ui.nonzero()):
result.append([user_code_id[user], item_code_id[item],
self.estimations[user,item] if not np.isnan(self.estimations[user,item]) else 1])
return result
model=RP3Beta()
model.fit(train_ui, alpha=1, beta=0)
top_n=pd.DataFrame(model.recommend(user_code_id, item_code_id, topK=10))
top_n.to_csv('Recommendations generated/ml-100k/Self_P3_reco.csv', index=False, header=False)
estimations=pd.DataFrame(model.estimate(user_code_id, item_code_id, test_ui))
estimations.to_csv('Recommendations generated/ml-100k/Self_P3_estimations.csv', index=False, header=False)
import evaluation_measures as ev
estimations_df=pd.read_csv('Recommendations generated/ml-100k/Self_P3_estimations.csv', header=None)
reco=np.loadtxt('Recommendations generated/ml-100k/Self_P3_reco.csv', delimiter=',')
ev.evaluate(test=pd.read_csv('./Datasets/ml-100k/test.csv', sep='\t', header=None),
estimations_df=estimations_df,
reco=reco,
super_reactions=[4,5])
943it [00:00, 2167.43it/s]
RMSE | MAE | precision | recall | F_1 | F_05 | precision_super | recall_super | NDCG | mAP | MRR | LAUC | HR | HR2 | Reco in test | Test coverage | Shannon | Gini | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 3.702446 | 3.527273 | 0.282185 | 0.192092 | 0.186749 | 0.21698 | 0.204185 | 0.240096 | 0.339114 | 0.204905 | 0.572157 | 0.593544 | 0.875928 | 0.685048 | 1.0 | 0.077201 | 3.875892 | 0.974947 |
Let's check hiperparameters
Alpha
from tqdm import tqdm
result=[]
for alpha in tqdm([round(i,1) for i in np.arange(0.2,1.6001,0.2)]):
model=RP3Beta()
model.fit(train_ui, alpha=alpha, beta=0)
reco=pd.DataFrame(model.recommend(user_code_id, item_code_id, topK=10))
estimations_df=pd.DataFrame(model.estimate(user_code_id, item_code_id, test_ui))
to_append=ev.evaluate(test=pd.read_csv('./Datasets/ml-100k/test.csv', sep='\t', header=None),
estimations_df=estimations_df,
reco=np.array(reco),
super_reactions=[4,5])
to_append.insert(0, "Alpha", alpha)
result.append(to_append)
result=pd.concat(result)
result
0%| | 0/8 [00:00<?, ?it/s] 0it [00:00, ?it/s][A 280it [00:00, 2775.23it/s][A 526it [00:00, 2666.15it/s][A 943it [00:00, 2520.30it/s][A 12%|██████████▌ | 1/8 [00:24<02:50, 24.33s/it] 0it [00:00, ?it/s][A 247it [00:00, 2452.10it/s][A 494it [00:00, 2452.09it/s][A 943it [00:00, 2484.84it/s][A 25%|█████████████████████ | 2/8 [00:49<02:28, 24.72s/it] 0it [00:00, ?it/s][A 159it [00:00, 1578.44it/s][A 413it [00:00, 1777.94it/s][A 584it [00:00, 1756.96it/s][A 712it [00:00, 1389.14it/s][A 943it [00:00, 1570.48it/s][A 38%|███████████████████████████████▌ | 3/8 [01:15<02:04, 24.87s/it] 0it [00:00, ?it/s][A 229it [00:00, 2273.39it/s][A 544it [00:00, 2476.21it/s][A 943it [00:00, 2626.45it/s][A 50%|██████████████████████████████████████████ | 4/8 [01:39<01:38, 24.58s/it] 0it [00:00, ?it/s][A 232it [00:00, 2303.19it/s][A 472it [00:00, 2244.92it/s][A 618it [00:00, 1927.54it/s][A 943it [00:00, 2102.99it/s][A 62%|████████████████████████████████████████████████████▌ | 5/8 [02:02<01:12, 24.22s/it] 0it [00:00, ?it/s][A 187it [00:00, 1856.41it/s][A 347it [00:00, 1766.98it/s][A 473it [00:00, 1572.31it/s][A 633it [00:00, 1577.13it/s][A 943it [00:00, 1719.13it/s][A 75%|███████████████████████████████████████████████████████████████ | 6/8 [02:24<00:47, 23.62s/it] 0it [00:00, ?it/s][A 268it [00:00, 2660.58it/s][A 534it [00:00, 2658.40it/s][A 943it [00:00, 2583.15it/s][A 88%|█████████████████████████████████████████████████████████████████████████▌ | 7/8 [02:49<00:23, 23.99s/it] 0it [00:00, ?it/s][A 167it [00:00, 1657.84it/s][A 276it [00:00, 1413.02it/s][A 368it [00:00, 1213.79it/s][A 489it [00:00, 1206.38it/s][A 672it [00:00, 1342.97it/s][A 943it [00:00, 1444.56it/s][A 100%|████████████████████████████████████████████████████████████████████████████████████| 8/8 [03:13<00:00, 24.16s/it]
Alpha | RMSE | MAE | precision | recall | F_1 | F_05 | precision_super | recall_super | NDCG | mAP | MRR | LAUC | HR | HR2 | Reco in test | Test coverage | Shannon | Gini | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.2 | 268.177832 | 211.732649 | 0.262672 | 0.166858 | 0.166277 | 0.197184 | 0.187661 | 0.203252 | 0.320910 | 0.196132 | 0.563378 | 0.580866 | 0.850477 | 0.629905 | 1.000000 | 0.060606 | 3.669627 | 0.979636 |
0 | 0.4 | 10.546689 | 7.792373 | 0.268505 | 0.172669 | 0.171569 | 0.202643 | 0.192489 | 0.212653 | 0.326760 | 0.200172 | 0.565148 | 0.583801 | 0.854719 | 0.644751 | 1.000000 | 0.064214 | 3.726996 | 0.978426 |
0 | 0.6 | 3.143988 | 2.948790 | 0.274655 | 0.180502 | 0.177820 | 0.208730 | 0.198176 | 0.222746 | 0.332872 | 0.203290 | 0.568872 | 0.587738 | 0.870626 | 0.657476 | 1.000000 | 0.065657 | 3.785282 | 0.977090 |
0 | 0.8 | 3.670728 | 3.495735 | 0.281972 | 0.189868 | 0.185300 | 0.216071 | 0.203541 | 0.236751 | 0.339867 | 0.206688 | 0.573729 | 0.592432 | 0.874867 | 0.685048 | 1.000000 | 0.070707 | 3.832415 | 0.975998 |
0 | 1.0 | 3.702446 | 3.527273 | 0.282185 | 0.192092 | 0.186749 | 0.216980 | 0.204185 | 0.240096 | 0.339114 | 0.204905 | 0.572157 | 0.593544 | 0.875928 | 0.685048 | 1.000000 | 0.077201 | 3.875892 | 0.974947 |
0 | 1.2 | 3.704441 | 3.529251 | 0.280912 | 0.193633 | 0.187311 | 0.216872 | 0.203004 | 0.240588 | 0.338049 | 0.203453 | 0.571830 | 0.594313 | 0.883351 | 0.681866 | 1.000000 | 0.085859 | 3.910718 | 0.974073 |
0 | 1.4 | 3.704580 | 3.529388 | 0.273595 | 0.190651 | 0.183874 | 0.212183 | 0.199464 | 0.239118 | 0.329550 | 0.195433 | 0.566171 | 0.592793 | 0.871686 | 0.675504 | 1.000000 | 0.107504 | 3.961915 | 0.972674 |
0 | 1.6 | 3.704591 | 3.529399 | 0.263097 | 0.186255 | 0.178709 | 0.205170 | 0.191094 | 0.232920 | 0.317439 | 0.184917 | 0.552349 | 0.590545 | 0.868505 | 0.669141 | 0.999576 | 0.156566 | 4.060156 | 0.969203 |
metrics=list(result.columns[[i not in ['Alpha'] for i in result.columns]])
charts_per_row=6
charts_per_column=3
fig, axes = plt.subplots(nrows=charts_per_row, ncols=charts_per_column,figsize=(18, 7*charts_per_row ))
import itertools
to_iter=[i for i in itertools.product(range(charts_per_row), range(charts_per_column))]
for i in range(len(metrics)):
df=result[['Alpha', metrics[i]]]
df.plot(ax=axes[to_iter[i]], title=metrics[i], x=0, y=1)
Beta
from tqdm import tqdm
result=[]
for beta in tqdm([round(i,1) for i in np.arange(0,1,0.1)]):
model=RP3Beta()
model.fit(train_ui, alpha=1, beta=beta)
reco=pd.DataFrame(model.recommend(user_code_id, item_code_id, topK=10))
estimations_df=pd.DataFrame(model.estimate(user_code_id, item_code_id, test_ui))
to_append=ev.evaluate(test=pd.read_csv('./Datasets/ml-100k/test.csv', sep='\t', header=None),
estimations_df=estimations_df,
reco=np.array(reco),
super_reactions=[4,5])
to_append.insert(0, "Beta", beta)
result.append(to_append)
result=pd.concat(result)
result
0%| | 0/10 [00:00<?, ?it/s] 0it [00:00, ?it/s][A 289it [00:00, 2869.05it/s][A 943it [00:00, 3796.57it/s][A 10%|████████▎ | 1/10 [00:13<02:03, 13.71s/it] 0it [00:00, ?it/s][A 462it [00:00, 4605.96it/s][A 943it [00:00, 3601.05it/s][A 20%|████████████████▌ | 2/10 [00:35<02:09, 16.14s/it] 0it [00:00, ?it/s][A 943it [00:00, 5872.90it/s][A 30%|████████████████████████▉ | 3/10 [00:50<01:50, 15.73s/it] 0it [00:00, ?it/s][A 351it [00:00, 3484.49it/s][A 943it [00:00, 3341.08it/s][A 40%|█████████████████████████████████▏ | 4/10 [01:05<01:33, 15.65s/it] 0it [00:00, ?it/s][A 485it [00:00, 4812.21it/s][A 943it [00:00, 4202.34it/s][A 50%|█████████████████████████████████████████▌ | 5/10 [01:23<01:21, 16.34s/it] 0it [00:00, ?it/s][A 943it [00:00, 4873.77it/s][A 60%|█████████████████████████████████████████████████▊ | 6/10 [01:38<01:04, 16.00s/it] 0it [00:00, ?it/s][A 258it [00:00, 2561.22it/s][A 555it [00:00, 2666.26it/s][A 943it [00:00, 2693.80it/s][A 70%|██████████████████████████████████████████████████████████ | 7/10 [01:57<00:50, 16.73s/it] 0it [00:00, ?it/s][A 943it [00:00, 5416.39it/s][A 80%|██████████████████████████████████████████████████████████████████▍ | 8/10 [02:17<00:35, 17.72s/it] 0it [00:00, ?it/s][A 158it [00:00, 1568.57it/s][A 295it [00:00, 1499.58it/s][A 943it [00:00, 2565.22it/s][A 90%|██████████████████████████████████████████████████████████████████████████▋ | 9/10 [02:33<00:17, 17.21s/it] 0it [00:00, ?it/s][A 262it [00:00, 2600.87it/s][A 572it [00:00, 2728.34it/s][A 943it [00:00, 2883.56it/s][A 100%|██████████████████████████████████████████████████████████████████████████████████| 10/10 [02:51<00:00, 17.16s/it]
Beta | RMSE | MAE | precision | recall | F_1 | F_05 | precision_super | recall_super | NDCG | mAP | MRR | LAUC | HR | HR2 | Reco in test | Test coverage | Shannon | Gini | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.0 | 3.702446 | 3.527273 | 0.282185 | 0.192092 | 0.186749 | 0.216980 | 0.204185 | 0.240096 | 0.339114 | 0.204905 | 0.572157 | 0.593544 | 0.875928 | 0.685048 | 1.000000 | 0.077201 | 3.875892 | 0.974947 |
0 | 0.1 | 3.703312 | 3.528128 | 0.290138 | 0.197597 | 0.192259 | 0.223336 | 0.210944 | 0.246153 | 0.347768 | 0.212034 | 0.581038 | 0.596328 | 0.884411 | 0.695652 | 1.000000 | 0.085137 | 3.957416 | 0.972784 |
0 | 0.2 | 3.703825 | 3.528636 | 0.297137 | 0.201202 | 0.196067 | 0.228169 | 0.218026 | 0.252767 | 0.355655 | 0.219909 | 0.588904 | 0.598160 | 0.886532 | 0.697773 | 1.000000 | 0.094517 | 4.053212 | 0.969980 |
0 | 0.3 | 3.704130 | 3.528939 | 0.303499 | 0.204749 | 0.199901 | 0.232829 | 0.225107 | 0.260797 | 0.363757 | 0.226825 | 0.599969 | 0.599964 | 0.888653 | 0.707317 | 1.000000 | 0.105339 | 4.147779 | 0.966948 |
0 | 0.4 | 3.704313 | 3.529120 | 0.308908 | 0.208811 | 0.203854 | 0.237241 | 0.229614 | 0.266918 | 0.370758 | 0.232673 | 0.609385 | 0.602014 | 0.895016 | 0.718982 | 0.999894 | 0.132035 | 4.259682 | 0.962989 |
0 | 0.5 | 3.704422 | 3.529229 | 0.314316 | 0.211411 | 0.206768 | 0.240986 | 0.237124 | 0.273416 | 0.378307 | 0.239297 | 0.622792 | 0.603327 | 0.903499 | 0.724284 | 0.999046 | 0.168831 | 4.411281 | 0.956648 |
0 | 0.6 | 3.704488 | 3.529295 | 0.314634 | 0.206209 | 0.204818 | 0.240159 | 0.242489 | 0.273850 | 0.376438 | 0.238428 | 0.622042 | 0.600721 | 0.897137 | 0.720042 | 0.996394 | 0.212843 | 4.621938 | 0.945932 |
0 | 0.7 | 3.704528 | 3.529335 | 0.304136 | 0.187298 | 0.191990 | 0.228749 | 0.238305 | 0.256201 | 0.358807 | 0.226808 | 0.593897 | 0.591207 | 0.868505 | 0.693531 | 0.983033 | 0.256854 | 4.898568 | 0.928065 |
0 | 0.8 | 3.704552 | 3.529360 | 0.266384 | 0.147571 | 0.158660 | 0.194838 | 0.214485 | 0.209336 | 0.299850 | 0.184356 | 0.492852 | 0.571152 | 0.803818 | 0.604454 | 0.936373 | 0.341270 | 5.257397 | 0.895882 |
0 | 0.9 | 3.704567 | 3.529375 | 0.162354 | 0.076967 | 0.089233 | 0.114583 | 0.134657 | 0.113253 | 0.160868 | 0.085486 | 0.243590 | 0.535405 | 0.580064 | 0.400848 | 0.800106 | 0.415584 | 5.563910 | 0.857396 |
### import matplotlib.pyplot as plt
metrics=list(result.columns[[i not in ['Beta'] for i in result.columns]])
charts_per_row=6
charts_per_column=3
fig, axes = plt.subplots(nrows=charts_per_row, ncols=charts_per_column,figsize=(18, 7*charts_per_row ))
import itertools
to_iter=[i for i in itertools.product(range(charts_per_row), range(charts_per_column))]
for i in range(len(metrics)):
df=result[['Beta', metrics[i]]]
df.plot(ax=axes[to_iter[i]], title=metrics[i], x=0, y=1)
Check sample recommendations
train=pd.read_csv('./Datasets/ml-100k/train.csv', sep='\t', header=None, names=['user', 'item', 'rating', 'timestamp'])
items=pd.read_csv('./Datasets/ml-100k/movies.csv')
user=random.choice(list(set(train['user'])))
train_content=pd.merge(train, items, left_on='item', right_on='id')
display(train_content[train_content['user']==user][['user', 'rating', 'title', 'genres']]\
.sort_values(by='rating', ascending=False)[:15])
reco = np.loadtxt('Recommendations generated/ml-100k/Self_P3_reco.csv', delimiter=',')
items=pd.read_csv('./Datasets/ml-100k/movies.csv')
# Let's ignore scores - they are not used in evaluation:
reco_users=reco[:,:1]
reco_items=reco[:,1::2]
# Let's put them into one array
reco=np.concatenate((reco_users, reco_items), axis=1)
# Let's rebuild it user-item dataframe
recommended=[]
for row in reco:
for rec_nb, entry in enumerate(row[1:]):
recommended.append((row[0], rec_nb+1, entry))
recommended=pd.DataFrame(recommended, columns=['user','rec_nb', 'item'])
recommended_content=pd.merge(recommended, items, left_on='item', right_on='id')
recommended_content[recommended_content['user']==user][['user', 'rec_nb', 'title', 'genres']].sort_values(by='rec_nb')
user | rating | title | genres | |
---|---|---|---|---|
82 | 200 | 5 | Toy Story (1995) | Animation, Children's, Comedy |
22169 | 200 | 5 | Groundhog Day (1993) | Comedy, Romance |
41229 | 200 | 5 | Star Trek: Generations (1994) | Action, Adventure, Sci-Fi |
40846 | 200 | 5 | Miracle on 34th Street (1994) | Drama |
38062 | 200 | 5 | Beauty and the Beast (1991) | Animation, Children's, Musical |
37534 | 200 | 5 | Aladdin (1992) | Animation, Children's, Comedy, Musical |
36306 | 200 | 5 | Silence of the Lambs, The (1991) | Drama, Thriller |
33862 | 200 | 5 | Independence Day (ID4) (1996) | Action, Sci-Fi, War |
32487 | 200 | 5 | Phenomenon (1996) | Drama, Romance |
32420 | 200 | 5 | Spawn (1997) | Action, Adventure, Sci-Fi, Thriller |
31448 | 200 | 5 | Seven (Se7en) (1995) | Crime, Thriller |
30214 | 200 | 5 | Star Trek: First Contact (1996) | Action, Adventure, Sci-Fi |
27813 | 200 | 5 | Day the Earth Stood Still, The (1951) | Drama, Sci-Fi |
27483 | 200 | 5 | Young Frankenstein (1974) | Comedy, Horror |
27366 | 200 | 5 | Empire Strikes Back, The (1980) | Action, Adventure, Drama, Romance, Sci-Fi, War |
user | rec_nb | title | genres | |
---|---|---|---|---|
2710 | 200.0 | 1 | Return of the Jedi (1983) | Action, Adventure, Romance, Sci-Fi, War |
7001 | 200.0 | 2 | Fargo (1996) | Crime, Drama, Thriller |
2272 | 200.0 | 3 | Godfather, The (1972) | Action, Crime, Drama |
6393 | 200.0 | 4 | Pulp Fiction (1994) | Crime, Drama |
7714 | 200.0 | 5 | Princess Bride, The (1987) | Action, Adventure, Comedy, Romance |
3434 | 200.0 | 6 | Jerry Maguire (1996) | Drama, Romance |
9101 | 200.0 | 7 | Mission: Impossible (1996) | Action, Adventure, Mystery |
4491 | 200.0 | 8 | Air Force One (1997) | Action, Thriller |
2026 | 200.0 | 9 | Back to the Future (1985) | Comedy, Sci-Fi |
8048 | 200.0 | 10 | Monty Python and the Holy Grail (1974) | Comedy |
project task 6: generate recommendations of RP3Beta for hiperparameters found to optimize recall
# use better values than (1,0) for alpha and beta
# if you want you can also modify the model to consider different weights (we took as weights user ratings, maybe take ones or squares of ratings instead)
# save the outptut in 'Recommendations generated/ml-100k/Self_RP3Beta_estimations.csv'
# and 'Recommendations generated/ml-100k/Self_RP3Beta_reco.csv'
model=RP3Beta()
model.fit(train_ui, alpha=1.2, beta=0.5)
top_n=pd.DataFrame(model.recommend(user_code_id, item_code_id, topK=10))
top_n.to_csv('Recommendations generated/ml-100k/Self_RP3Beta_reco.csv', index=False, header=False)
estimations=pd.DataFrame(model.estimate(user_code_id, item_code_id, test_ui))
estimations.to_csv('Recommendations generated/ml-100k/Self_RP3Beta_estimations.csv', index=False, header=False)
import evaluation_measures as ev
estimations_df=pd.read_csv('Recommendations generated/ml-100k/Self_RP3Beta_estimations.csv', header=None)
reco=np.loadtxt('Recommendations generated/ml-100k/Self_RP3Beta_reco.csv', delimiter=',')
ev.evaluate(test=pd.read_csv('./Datasets/ml-100k/test.csv', sep='\t', header=None),
estimations_df=estimations_df,
reco=reco,
super_reactions=[4,5])
943it [00:00, 3493.16it/s]
RMSE | MAE | precision | recall | F_1 | F_05 | precision_super | recall_super | NDCG | mAP | MRR | LAUC | HR | HR2 | Reco in test | Test coverage | Shannon | Gini | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 3.70458 | 3.529388 | 0.302969 | 0.199894 | 0.197705 | 0.231449 | 0.231438 | 0.263787 | 0.362426 | 0.226406 | 0.601293 | 0.597526 | 0.889714 | 0.700954 | 0.996819 | 0.212121 | 4.509878 | 0.951344 |
project task 7 (optional): implement graph-based model of your choice
# for example change length of paths in RP3beta
# save the outptut in 'Recommendations generated/ml-100k/Self_GraphTask_estimations.csv'
# and 'Recommendations generated/ml-100k/Self_GraphTask_reco.csv'