Uczenie_maszynowe_Systemy_R.../P5. Graph-based.ipynb
2020-06-14 22:23:50 +02:00

569 KiB
Raw Blame History

Self made RP3-beta

pip install surprise
Collecting surprise
  Downloading https://files.pythonhosted.org/packages/61/de/e5cba8682201fcf9c3719a6fdda95693468ed061945493dea2dd37c5618b/surprise-0.1-py2.py3-none-any.whl
Collecting scikit-surprise
[?25l  Downloading https://files.pythonhosted.org/packages/f5/da/b5700d96495fb4f092be497f02492768a3d96a3f4fa2ae7dea46d4081cfa/scikit-surprise-1.1.0.tar.gz (6.4MB)
     |████████████████████████████████| 6.5MB 2.8MB/s 
[?25hRequirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.6/dist-packages (from scikit-surprise->surprise) (0.15.1)
Requirement already satisfied: numpy>=1.11.2 in /usr/local/lib/python3.6/dist-packages (from scikit-surprise->surprise) (1.18.5)
Requirement already satisfied: scipy>=1.0.0 in /usr/local/lib/python3.6/dist-packages (from scikit-surprise->surprise) (1.4.1)
Requirement already satisfied: six>=1.10.0 in /usr/local/lib/python3.6/dist-packages (from scikit-surprise->surprise) (1.12.0)
Building wheels for collected packages: scikit-surprise
  Building wheel for scikit-surprise (setup.py) ... [?25l[?25hdone
  Created wheel for scikit-surprise: filename=scikit_surprise-1.1.0-cp36-cp36m-linux_x86_64.whl size=1675402 sha256=0402e186e04cc281f74955fc53a5f90361c98d08599a8e72cc36eb0b55dafd8a
  Stored in directory: /root/.cache/pip/wheels/cc/fa/8c/16c93fccce688ae1bde7d979ff102f7bee980d9cfeb8641bcf
Successfully built scikit-surprise
Installing collected packages: scikit-surprise, surprise
Successfully installed scikit-surprise-1.1.0 surprise-0.1
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, 6376.09it/s]
RMSE MAE precision recall F_1 F_05 precision_super recall_super NDCG mAP MRR LAUC HR 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 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]
943it [00:00, 6334.35it/s]
 12%|█▎        | 1/8 [00:11<01:19, 11.38s/it]
0it [00:00, ?it/s]
943it [00:00, 6365.04it/s]
 25%|██▌       | 2/8 [00:22<01:07, 11.33s/it]
0it [00:00, ?it/s]
943it [00:00, 6323.80it/s]
 38%|███▊      | 3/8 [00:34<00:56, 11.37s/it]
0it [00:00, ?it/s]
943it [00:00, 6484.87it/s]
 50%|█████     | 4/8 [00:45<00:45, 11.32s/it]
0it [00:00, ?it/s]
943it [00:00, 6172.18it/s]
 62%|██████▎   | 5/8 [00:56<00:33, 11.27s/it]
0it [00:00, ?it/s]
943it [00:00, 6405.62it/s]
 75%|███████▌  | 6/8 [01:07<00:22, 11.32s/it]
0it [00:00, ?it/s]
943it [00:00, 6547.37it/s]
 88%|████████▊ | 7/8 [01:19<00:11, 11.35s/it]
0it [00:00, ?it/s]
943it [00:00, 6573.96it/s]
100%|██████████| 8/8 [01:30<00:00, 11.31s/it]
Alpha RMSE MAE precision recall F_1 F_05 precision_super recall_super NDCG mAP MRR LAUC HR 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 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 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 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 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 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 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 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.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]
943it [00:00, 6498.11it/s]
 10%|█         | 1/10 [00:11<01:44, 11.61s/it]
0it [00:00, ?it/s]
943it [00:00, 6408.36it/s]
 20%|██        | 2/10 [00:23<01:32, 11.61s/it]
0it [00:00, ?it/s]
943it [00:00, 6396.07it/s]
 30%|███       | 3/10 [00:34<01:20, 11.54s/it]
0it [00:00, ?it/s]
943it [00:00, 5925.94it/s]
 40%|████      | 4/10 [00:46<01:09, 11.58s/it]
0it [00:00, ?it/s]
943it [00:00, 5799.00it/s]
 50%|█████     | 5/10 [00:57<00:57, 11.46s/it]
0it [00:00, ?it/s]
943it [00:00, 6089.68it/s]
 60%|██████    | 6/10 [01:08<00:45, 11.37s/it]
0it [00:00, ?it/s]
943it [00:00, 5271.79it/s]
 70%|███████   | 7/10 [01:20<00:34, 11.43s/it]
0it [00:00, ?it/s]
943it [00:00, 6396.94it/s]
 80%|████████  | 8/10 [01:31<00:22, 11.32s/it]
0it [00:00, ?it/s]
943it [00:00, 6464.53it/s]
 90%|█████████ | 9/10 [01:42<00:11, 11.29s/it]
0it [00:00, ?it/s]
943it [00:00, 6338.37it/s]
100%|██████████| 10/10 [01:53<00:00, 11.38s/it]
Beta RMSE MAE precision recall F_1 F_05 precision_super recall_super NDCG mAP MRR LAUC HR 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 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 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 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 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.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.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.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.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.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.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
10631 190 5 Courage Under Fire (1996) Drama, War
19196 190 5 Scream (1996) Horror, Thriller
5262 190 5 Jerry Maguire (1996) Drama, Romance
46114 190 5 L.A. Confidential (1997) Crime, Film-Noir, Mystery, Thriller
40446 190 5 Titanic (1997) Action, Drama, Romance
45123 190 4 Tomorrow Never Dies (1997) Action, Romance, Thriller
33331 190 4 Game, The (1997) Mystery, Thriller
34493 190 4 Devil's Own, The (1997) Action, Drama, Thriller, War
39439 190 4 Sleepers (1996) Crime, Drama
40264 190 4 Rainmaker, The (1997) Drama
461 190 4 Heat (1995) Action, Crime, Thriller
30171 190 4 Star Trek: First Contact (1996) Action, Adventure, Sci-Fi
47145 190 4 Primal Fear (1996) Drama, Thriller
55123 190 4 Long Kiss Goodnight, The (1996) Action, Thriller
58856 190 4 Wedding Singer, The (1998) Comedy, Romance
user rec_nb title genres
84 190.0 1 Star Wars (1977) Action, Adventure, Romance, Sci-Fi, War
1019 190.0 2 English Patient, The (1996) Drama, Romance, War
2704 190.0 3 Return of the Jedi (1983) Action, Adventure, Romance, Sci-Fi, War
3106 190.0 4 Toy Story (1995) Animation, Children's, Comedy
3611 190.0 5 Conspiracy Theory (1997) Action, Mystery, Romance, Thriller
2268 190.0 6 Godfather, The (1972) Action, Crime, Drama
8825 190.0 7 Men in Black (1997) Action, Adventure, Comedy, Sci-Fi
6263 190.0 8 Good Will Hunting (1997) Drama
8307 190.0 9 Leaving Las Vegas (1995) Drama, Romance
4369 190.0 10 Dead Man Walking (1995) Drama

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'

import evaluation_measures as ev

model=RP3Beta()
model.fit(train_ui, alpha=0.6, beta=0.4)  # wartości alpha 0.6, beta 0.4 mają mniejszy RMSE, MAE, większą precyzję i niedużo większy recall
model_reco=pd.DataFrame(model.recommend(user_code_id, item_code_id, topK=10))
model_reco.to_csv('Recommendations generated/ml-100k/Self_RP3Beta_reco.csv', index=False, header=False)
estimations_df=pd.DataFrame(model.estimate(user_code_id, item_code_id, test_ui))
estimations_df.to_csv('Recommendations generated/ml-100k/Self_RP3Beta_estimations.csv', index=False, header=False)
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, 7119.46it/s]
RMSE MAE precision recall F_1 F_05 precision_super recall_super NDCG mAP MRR LAUC HR Reco in test Test coverage Shannon Gini
0 3.626913 3.450842 0.309862 0.208881 0.203915 0.23761 0.228541 0.263796 0.375901 0.239086 0.621655 0.602052 0.889714 1.0 0.105339 4.209113 0.964697
import evaluation_measures as ev

model=RP3Beta()
model.fit(train_ui, alpha=1, beta=0)    # alpha, 1 beta 0
model_reco=pd.DataFrame(model.recommend(user_code_id, item_code_id, topK=10))
model_reco.to_csv('Recommendations generated/ml-100k/Self_RP3Beta_reco2.csv', index=False, header=False)
estimations_df=pd.DataFrame(model.estimate(user_code_id, item_code_id, test_ui))
estimations_df.to_csv('Recommendations generated/ml-100k/Self_RP3Beta_estimations2.csv', index=False, header=False)
reco=np.loadtxt('Recommendations generated/ml-100k/Self_RP3Beta_reco2.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, 6878.29it/s]
RMSE MAE precision recall F_1 F_05 precision_super recall_super NDCG mAP MRR LAUC HR 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 1.0 0.077201 3.875892 0.974947

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'