[PYTHON] J'ai appliqué LightFM à Movielens

En recherchant des bibliothèques liées à Factoriazation Machines, je suis tombé sur une bibliothèque appelée LightFM, alors j'ai essayé de l'utiliser.

Finalement, j'aimerais l'appliquer à mon propre jeu de données, mais cette fois je vais l'appliquer à Movielens (jeu de données de recommandation de films) pour m'habituer à utiliser LightFM.

Dépôt LightFM GitHub - lyst/lightfm: A Python implementation of LightFM, a hybrid recommendation algorithm.

Différence entre Light FM et FM

LightFM est attaché à FM, mais ce n'est pas une bibliothèque de machines de factorisation.

Lors de la lecture de [Paper] de LightFM (https://arxiv.org/abs/1507.08439), les auteurs déclarent que "LightFM est un cas particulier de FM".

Je cherchais une implémentation python de FM, donc j'étais un peu déçu, mais quand je l'ai lu, j'avais encore un modèle qui semblait résoudre la tâche que je voulais essayer, alors j'ai décidé d'essayer ceci. (Et beaucoup de tutoriels et de documentation)

En FM normale, diverses fonctionnalités peuvent être utilisées pour l'entrée en tant que fonctionnalités de contexte en plus de l'ID utilisateur et de l'ID d'élément, et chaque intégration est prise et tous les produits internes sont pris pour générer la somme.

f(x)=w_0 + \sum_{i=1}^d w_ix_i + \sum_{i,j}\langle \boldsymbol v_i, \boldsymbol 
 v_j \rangle x_ix_j

Prenez le produit interne de l'intégration de l'utilisateur et de l'incorporation d'éléments, le produit interne des fonctionnalités contextuelles, etc.

LightFM peut partiellement inclure des fonctionnalités de contexte telles que FM, mais tout ne peut pas être inclus.

La fonctionnalité à ajouter est forcément une fonctionnalité utilisateur ou une fonctionnalité d'élément.

En ce qui concerne le produit interne, la fonctionnalité de l'utilisateur ne prend que le produit interne avec la fonctionnalité de l'article. Il ne prend pas le produit interne des fonctionnalités utilisateur.

f(i, u)=  \langle \boldsymbol p_i,  \boldsymbol q_u \rangle + b_i +b_u 

pourtant

 \boldsymbol p_i = \sum_{j \in f_i}\boldsymbol e_{j}^I
  \boldsymbol q_u = \sum_{j \in f_u}\boldsymbol e_{j}^U
  b_i = \sum_{j \in f_i}b_{j}^U
  b_u = \sum_{j \in f_u} b_{j}^U

est.

Si vous ne donnez pas les fonctionnalités de l'utilisateur ou de l'élément, cela correspond à la forme de la factorisation matricielle.

Les pertes supportées par LightFM sont BPR, WARP, warp-kos et perte logistique. Les trois premiers sont des pertes de classement et conviennent à l'apprentissage de classement avec rétroaction implicite.

J'ai écrit un article sur BPR ici, alors n'hésitez pas à me contacter!

[Introduction] BPR: classement bayésien personnalisé d'après les commentaires implicites (UAI 2009)

Bougeons

Obtenir l'ensemble de données Movielens

Movielens est un ensemble de données de recommandations de films et est souvent utilisé dans des expériences dans ce domaine. Cette fois, je me concentre sur une taille relativement petite.

LightFM fournit gentiment une classe pour charger Movielens, alors utilisons-la.

(L'installation de LightFM peut être saisie avec pip ou conda. Omis)

from lightfm.datasets import fetch_movielens
data = fetch_movielens()

data.keys()

Puis

dict_keys(["train", "test",  "item_features", "item_feature_labels", "item_labels"])

Est revenu.

train et test sont notés dans la matrice (n_user, n_item). S'il n'y a pas de note, 0 est inclus.

Je pensais que item_features est une matrice de (n_item, n_features) qui représente la caractéristique de chaque élément par 01 ... mais quand je regarde le contenu, cela ressemble à une matrice carrée et une matrice unitaire. est.

En outre, le document indique que item_feature_label contient le nom de chaque fonctionnalité et item_labels contient le nom de l'élément, et il dit que ce sont des tableaux de (n_feature,) et (n_item,), respectivement, mais regardez le contenu. Tous les deux

array(['Toy Story (1995)', 'GoldenEye (1995)', 'Four Rooms (1995)', ...,
       'Sliding Doors (1998)', 'You So Crazy (1994)',
       'Scream of Stone (Schrei aus Stein) (1991)'], dtype=object)

était. Tous sont des noms d'articles.

Peut-être que ce didacticiel fonctionnera sans la fonctionnalité d'élément (c'est-à-dire avec la factorisation matricielle).

Partie d'apprentissage

Cliquez ici pour la partie qui entraîne réellement le modèle.

train = data["train"]
test = data["test"]

model = LightFM(no_components=10,learning_rate=0.05, loss='bpr')
model.fit(train, epochs=10)

train_precision = precision_at_k(model, train, k=10).mean()
test_precision = precision_at_k(model, test, k=10).mean()

train_auc = auc_score(model, train).mean()
test_auc = auc_score(model, test).mean()

print('Precision: train %.2f, test %.2f.' % (train_precision, test_precision))
print('AUC: train %.2f, test %.2f.' % (train_auc, test_auc))

Vous pouvez spécifier la dimension d'intégration avec no_components. Cette fois 10.

Chez model fit et auc

model.fit(train, item_features=data["item_features"], epochs=10)
auc_score(model, train, item_features=data["item_features"]).mean()

Vous pouvez mettre item_features comme ceci.

Cependant, cette fois, item_features contient uniquement l'ID d'élément, donc le résultat ne doit pas changer.

item_Aucune fonctionnalité
Precision: train 0.59, test 0.10.
AUC: train 0.90, test 0.86.

item_avec fonctionnalité
Precision: train 0.59, test 0.10.
AUC: train 0.89, test 0.86.

J'ai pensé, mais c'est légèrement différent ... Est-ce une erreur?

En regardant la source LightFM, si item_features n'est pas spécifié, les fonctionnalités d'élément sont créées avec la matrice d'unité de (n_item, n_item), donc les deux éléments ci-dessus doivent se comporter de la même manière.

Obtenez l'intégration

Vous pouvez obtenir l'intégration après avoir appris comme suit.

user_embedding=model.user_embeddings
item_embedding=model.item_embeddings

user_embedding est (n_user_features, no_components) item_embedding est (n_item_features, no_components)

Array.

Comme je l'ai découvert en accédant à la source plus tôt, si les fonctionnalités n'étaient pas spécifiées, l'intégration de l'ID utilisateur / élément a été obtenue.

predict

Enfin, il y a eu une petite pierre d'achoppement dans Predict, alors prenez-en note.

Étant donné user_id et item_id, il calculera le score pour cette paire. Pour donner un identifiant, vous pouvez d'abord donner plusieurs identifiants d'élément pour chaque utilisateur.

model.predict(user_ids=0,item_ids=[1,3,4])

Ensuite, le score pour l'élément1,3,4 de l'utilisateur0 sera calculé. Je pense qu'il existe de nombreuses situations où le classement est créé pour chaque utilisateur, donc cette méthode d'écriture est pratique.

Il y a quelques points à noter à propos de plusieurs identifiants d'utilisateur et de plusieurs paires d'identifiants d'élément,

model.predict(user_ids=[4,3,1],item_ids=[1,3,4])

Si vous le faites, vous obtiendrez une erreur d'assertion.

Correctement

import numpy as np
model.predict(user_ids=np.array([4,3,1]),item_ids=[1,3,4])

Lors de la lecture de la source, si l'ID utilisateur n'est pas un tableau numpy, le processus de répétition du nombre d'ID d'élément est inclus afin de faire correspondre le nombre avec les ID d'élément. Si l'ID utilisateur est int (lors du calcul du score pour plusieurs éléments par utilisateur mentionné précédemment), les longueurs seront les mêmes, mais lorsque user_ids est donné sous forme de liste, elle sera répétée et la longueur sera la même. Ne sera pas aligné. C'est un piège ...

Je devrais décider si je dois répéter par "Les user_ids peuvent-ils calculer len?" Quelqu'un vous donnera une liste ...

À propos de la perte

BPR et WARP ont été mis en œuvre afin que ceux qui ont une note positive soient considérés comme des commentaires positifs. Il semble que vous n'ayez pas à le fixer à 1 ici et à le donner.

Il a été écrit que la perte logistique peut être utilisée lorsqu'il y a des rétroactions +1 et -1, Cela a fonctionné même si je donnais la matrice de notation telle quelle. (Je ne pouvais pas tout à fait comprendre ce que je faisais à l'intérieur rien qu'en regardant la source)

logistic


Precision: train 0.43, test 0.08.
AUC: train 0.87, test 0.84.

bpr(Republier)


Precision: train 0.59, test 0.10.
AUC: train 0.90, test 0.86.

Cependant, vous pouvez voir qu'il ne convient pas comme indice de classement.

en conclusion

Comme mentionné ci-dessus, je pense que le traitement de base peut être effectué maintenant.

Je suis heureux que les didacticiels et la documentation soient faciles à comprendre et que plusieurs pertes soient prises en charge.

Je l'ai écrit brièvement, mais il est également pratique que des indicateurs d'évaluation tels que AUC et Precision @ k soient également fournis.

Ensuite, j'appliquerai enfin LightFM à mon propre jeu de données!

Merci d'avoir lu jusqu'au bout.

Recommended Posts

J'ai appliqué LightFM à Movielens
J'ai commencé à analyser
J'ai essayé de déboguer.
J'ai essayé d'apprendre PredNet
J'ai essayé d'organiser SVM.
J'ai parlé à Raspberry Pi
J'ai essayé d'implémenter PCANet
Introduction à l'optimisation non linéaire (I)
Je veux résoudre SUDOKU
J'ai essayé de réintroduire Linux
J'ai essayé de présenter Pylint
J'ai essayé de résumer SparseMatrix
jupyter je l'ai touché
J'ai essayé d'implémenter StarGAN (1)
J'ai essayé d'implémenter Deep VQE
J'ai essayé de créer l'API Quip
J'ai essayé de toucher Python (installation)
Je veux comprendre à peu près systemd
J'ai essayé d'expliquer l'ensemble de données de Pytorch
J'ai essayé l'authentification vocale Watson (Speech to Text)
J'ai essayé de calculer l'intégrale de probabilité (I à l'intégrale)
J'ai touché l'API de Tesla
Je voulais faire évoluer cGAN vers ACGAN
Je veux gratter des images et les former
J'ai essayé de m'organiser à propos de MCMC.
Je veux faire ○○ avec les Pandas
J'ai essayé d'implémenter Realness GAN
Je veux copier l'annotation de yolo
Je veux déboguer avec Python
Chaîne de hachage que je voulais éviter (1)
J'ai essayé de déplacer le ballon
J'étais accro au multitraitement + psycopg2
J'ai essayé d'estimer la section.
Une histoire à laquelle j'étais accro à essayer d'installer LightFM sur Amazon Linux