Recommandation par filtrage hybride
Le filtrage hybride consiste à combiner les techniques de recommandation basées sur le contenu avec celles basées sur le filtrage collaboratif. Beaucoup d'approches sont possibles. Nous en décrivons une ci-dessous que nous mettons en &eolig;uvre.
Le problème et les données
Le problème
Nous reprenons les jeux de données MovieLens utilisés dans le TP sur recommandation par filtrage collaboratif. Nous rappelons les éléments qui suivent.
Chaque utilisateur est décrit par quelques attributs :
- un numéro d'identification,
- son âge,
- son sexe,
- son métier,
- son code postal (USA).
Chaque film est décrit par :
- un numéro d'identification,
- son titre,
- sa date de sortie au cinéma,
- son genre. Le genre peut-être : inconnu, Action, Aventure,
Animation, Enfants, Comédie, Crime, Documentaire, Drame, Fantaisie,
Film-Noir, Horreur, Musical, Mystère, Romance, Science-Fiction,
Suspense, Guerre, Western. Chaque genre est indiqué par une valeur
booléenne selon qu'il s'applique ou non à la donnée.
On dispose alors d'une liste de notes sous la forme :
- identifiant d'utilisateur,
- identifiant de film,
- note attribuée par cet utilisateur à ce film,
- information de date : nombre de secondes écoulées depuis le
1/1/1970.
Dans le plus petit jeu de données, il y a 100.000 notes, provenant de
943 utilisateurs pour 1682 films.
Nous allons utiliser à la fois les attributs des données et les notes. Nous allons considérer un problème qui consiste à prédire la note attribuée par un utilisateur à un film. Il s'agît alors d'un problème de classification supervisée dans lequel il y a 5 classes, une classe par note possible.
Les numéros d'identification de l'utilisateur et de l'item sont sans intérêt ; pour l'item, nous ne retiendrons que les attributs spécifiant le genre du film.
Apprentissage supervisé des notes
Construction du jeu d'apprentissage et du jeu de test
Nous devons construire le jeu de données permettant l'apprentissage des notes. Pour cela, un exemple est constitué d'un triplet (utilisateur, item, note). Un exemple est donc constitué des attributs de l'utilisateur et de l'item, la classe étant la note.
Construire le jeu d'apprentissage à partir des données présentes dans u1.base et un jeu de test avec les données présentes dans u1.test.
Les deux jeux de données doivent avoir les mêmes attributs.
Ils sont placés dans deux matrices.
On convient que la classe est dans la dernière colonne (celle de numéro ncol (jeu)).
Pour une raison purement technique, la classe (la note) doit avoir une valeur dont la plus petite est 0. La classe doit donc être la note - 1 et non pas la note. Faites-le.
Apprentissage supervisée
Il y a une multitude d'algorithmes utilisables pour réaliser une tâche d'apprentissage supervisée. Les méthodes d'ensemble sont connues pour leur excellente performance. Aussi, nous allons utiliser l'une d'entre-elles connue sous le nom de forêt aléatoire : une forêt aléatoire est un ensemble d'arbres de décision. Il y a plusieurs manières de construire un arbre de décision et plusieurs manières de combiner des arbres pour prédire la classe d'une donnée. Nous allons utiliser la méthode extreme gradient boosting tree qui est disponible dans R. Elle est très performante, très rapide et très facile à utiliser. On peut l'utiliser pour réaliser une tâche de classification binaire, ou à plus de 2 classes, une tâche de régression linéaire ou logistique, ou une tâche de ranking.
- faire
library (xgboost)
- supposons que le jeu d'entrainement soit dans une matrice nommée train dont la dernière colonne contient les étiquettes
- supposons que le jeu de test soit dans une matrice nommée test dont la dernière colonne contient les étiquettes
- faire
dtrain <- xgb.DMatrix (data = train [, -ncol (train)], label = train [, ncol (train)])
.
Cette instruction construit un objet à partir des exemples, en séparant leurs attributs de leurs étiquettes. La notation train [, -ncol (train)]
indique que l'on prend toutes les colonnes sauf la dernière (qui est numérotée ncol (train)
) : donc les données sont dans toutes les colonnes sauf la dernière, et les étiquettes sont dans la dernière. Bien entendu, les étiquettes peuvent se trouver dans n'importe quelle colonne de train : ce qui importe est de spécifier la bonne colonne ici.
- faire de même pour les exemples de test :
dtest <- xgb.DMatrix (data = test [, -ncol (test)], label = test [, ncol (test)])
- et on peut lancer l'apprentissage par :
model <- xgb.train (data = dtrain, nround = 200, watchlist = list (train = dtrain, test = dtest), params = list (objective = "multi:softmax", num.class = 5))
Cette commande indique les éléments suivant :
- data = dtrain : on apprend avec les exemples contenus dans train
- nround = 200 : on exécute 200 itérations de l'algorithme
- watchlist = list (train = dtrain, test = dtest) : indique le jeu d'entraînement et le jeu de données utilisé pendant l'entraînement
- params = list (objective = "multi:softmax", num.class = 5) est une liste de paramètres et indique deux choses :
- objective = "multi:softmax" : il s'agît d'un problème de classification supervisée ayant plus de 2 classes
- num.class = 5 : il y a 5 classes
Remarque : si vous obtenez un message du genre :
Erreur dans xgb.iter.update(bst$handle, dtrain, i - 1, obj) :
SoftmaxMultiClassObj: label must be in [0, num_class), num_class=5 but found 5 in label
c'est que vous n'avez pas respecté la règle que les étiquettes doivent être numérotées de 0 à n - 1 (et non de 1 à n).
- cette commande prend un peu de temps à s'exécuter. Pour faire patienter et montrer qu'elle est en pleine action, elle affiche l'erreur de classification obtenue à chaque itération sur le jeu d'entraînement et sur le jeu de test. Par exemple :
[11] train-merror:0.609300 test-merror:0.629650
indique qu'à la 11è itération, l'erreur de classification sur le jeu d'entraînement est 0.609300, celle sur le jeu de test est 0.629650.
Prédiction des notes
- quand le modèle est appris, on peut prédire la classe de nouvelles données. Si on veut prédire la classe des données de test, on fait :
prediction.model <- predict (model, test [, -ncol (test)])
.
prediction.model contient ensuite un vecteur indiquant la classe prédite pour chacune des données. Ici, c'est donc un vecteur de 20000 valeurs.
- en utilisant prediction.model, calculer le taux d'erreur sur le jeu de test. Vous devez retrouver la valeur indiquée à la dernière ligne de l'exécution de xgb.train.
- vérifier également le taux d'erreur sur le jeu d'entraînement.
Recommandation
- Écrire une fonction qui recommande 5 films qu'il n'a pas notés à un utilisateur identifié par son numéro.
- Un nouvel utilisateur se présente sur le site : que lui recommendez-vous ?
Si vous avez fait tout ce qui précède
Je propose ici des sujets pour aller plus loin.
- un utilisateur veut que les items recommendés lui plaisent. Aussi, ne s'intéresse-t-il qu'aux items notés 4 ou 5. Aussi, peut-on considérer non pas 5 notes possibles, mais une étiquette disant intéressant (correspondant aux notes 4 ou 5) et pas intéressant (correspondant à 1, 2 ou 3). Mettez cette idée en œuvre. L'erreur de classification est-elle meilleure ?
Refaire la même en considérant que les produits intéressant ont été notés 3, 4 ou 5 et les non intéressants ont été notés 1 ou 2.
L'une ou l'autre de ces deux approches donne-t-elle de meilleurs résultat ?
- en général, les utilisateurs notent les items qui leur plaisent et ne notent pas les produits qui ne leur plaisent pas. Cela crée un distribution particulière des notes, avec une sur-représentation des bonnes notes.
Constatez ce phénomène sur le jeu de données.
Pensez-vous que cela soit un problème ? Si non, pourquoi ? Si oui, pourquoi et comment essayer d'y remédier ?