Ce post à pour objectif de défricher le fonctionnement du moteur de recommandation d'Apache Mahout Taste en étudiant les projets d’exemples fournis avec la version 0.7 d'
Apache Mahout (L'API est susceptible d'évoluer en profondeur tant que la MileStone 1 n'est pas atteinte).
<dependency>
<groupId>org.apache.mahout</groupId>
<artifactId>mahout-core</artifactId>
<version>0.7</version>
</dependency>
<dependency>
<groupId>org.apache.mahout</groupId>
<artifactId>mahout-examples</artifactId>
<version>0.7</version>
</dependency>
Concepts
Mahout Taste permet de fournir des recommandation d'
Items à des
User.
Users et
Items étant tous deux représentés par des entiers. Afin de fournir des recommandations, le framework s'appuie sur un
DataModel constitué de préférences. Une préférence est un nombre flottant représentant une affinité entre un
User et un
Item.
Le moteur de recommandation repose sur l'interface
Recommender. Les implémentations de cette interface permettent de :
- Récupérer une liste d'Items recommandées pour un User
- D'estimer l'affinité entre un User et un Item
- De modifier des préférences à la volée
Un exemple simple : ItemAverageRecommender
ItemAverageRecommender est une implémentation de l'interface
Recommender qui estime l'affinité potentielle entre un
User et un
Item comme la moyenne des affinités des
User ayant exprimés une préférence pour pour cet
Item. Elle permet ainsi d'obtenir des recommandations indiférenciées par
User.
Le BookCrossingRecommender
Un peu plus intéressant, le
BookCrossingRecommender est un exemple concret de
Recommender qui s'appuie sur un jeu de données réel :
public final class BookCrossingRecommender implements Recommender {
private final Recommender recommender;
public BookCrossingRecommender(DataModel bcModel) throws TasteException {
UserSimilarity similarity = new CachingUserSimilarity(
new EuclideanDistanceSimilarity(bcModel), bcModel);
UserNeighborhood neighborhood = new NearestNUserNeighborhood(10, 0.2, similarity, bcModel, 0.2);
recommender = new GenericUserBasedRecommender(bcModel, neighborhood, similarity);
}
}
En regardant de plus près le code de ce
BookCrossingRecommender on remarque la présence des concepts suivants :
- UserSimilarity
- qui représente la ressemblance entre deux User.
- UserNeighborhood
- qui représente le voisinage d'un User; les voisins étant déterminés en fonction de leurs ressemblance.
Des implémentations diverses de ces deux concepts sont disponibles par défaut dans Mahout Taste. Le
BookCrossingRecommender utilise la classe
EuclideanDistanceSimilarity comme implémentation de la ressemblance. Celle-ci permet de classer les
Users selon la
distance euclidienne entre leurs affinités communes (chaque
Item pour lequel ils ont chacun exprimé une préférence étant une dimension).
Une fois la notion de ressemblance posée, il est alors possible de définir le voisinage d'un
User. Dans le cas du
BookCrossingRecommender, le voisinage d'un
User est constitué des 10
Users qui lui sont le plus ressemblant.
La classe
NearestNUserNeighborhood permet de fixer un seuil minimal en dessous duquel la ressemblance entre deux
Users leur interdit d'être voisins.
Enfin, le
GenericUserBasedRecommender est utilisé afin de proposer des recommandations à un
User en considérant les
Items ayant l'affinité moyenne la plus grande parmi son voisinage...
Comment choisir (construire) une bonne implémentation
Si Taste propose une API simple permettant de monter rapidement un système de recommandation sur un jeux de données quasi quelconque, comment savoir si la notion de ressemblance retenue est la bonne, si le voisinage n'a pas été choisi trop vaste ou trop restreint et si la méthode de sélection des recommandations est pertinente ?
Quelques requêtes sur Google permettent de se rendre compte que de nombreuses recherches plus ou moins compréhensibles (plutôt moins que plus) ont été menées sur le sujet.
Pour le commun des mortels, Mahout Taste est livré avec un
RecommenderEvaluator sui, comme son nom l'indique, permet d'estimer la qualité d'un
Recommender. A partir d'un
DataModel, le
RecommenderEvaluator va construire le
Recommender à évaluer en utilisant une partie du jeu de données : le
"training percentage". Il va ensuite évaluer le
Recommender sur un sous ensemble du jeu de données appelé
"evaluation percentage" et retourner une
note. Plus la
note retournée est basse, plus le
Recommender a proposé des recommandations proches des préférences réelles de l'
"evaluation percentage". Une
note de 0 indiquant des résultats identiques entre les recommandations du
Recommender et l'
"evaluation percentage".