GraphAwareTM
Christophe Willemsen
graphaware.com
@graph_aware
Recommandations avec Neo4j
Construction d’un moteur de recommandation haute-performance
GraphAwareTM
Rapide introductionPourquoi les graphes ?Challenges Business et TechniquesGraphAware Recommendation Engine
A propos
GraphAwareTM
Articles que vous pourriez lireLivres que vous pourriez acheterPersonnes que vous pourriez connaitrePersonnes que vous pourriez rencontrerPersonnes auxquelles vous pourriez présentervotre produit…
Recommandation
GraphAwareTM
Approche contenu (caractéristiques)Filtrage collaboratif (relations utilisateur <-> produit)
Recommandation
GraphAwareTM
Les caractéristiques tout comme les relations peuvent être représentées naturellement dans un graphe.
Bonnes nouvelles
GraphAwareTM
Exemple
IS_OF_GENRE
title: “Love Actually”
Movie
name: “Bob”
User
name: “Comedy”
Genre
RATEDrating: 5
name: “Alice”
Username:
“Romance”
Genre
title: “American Pie”
Movie
IS_OF_GENRE
IS_OF_GENRE
RATEDrating: 5
INTERESTED_IN
rating: 5RATED
GraphAwareTM
Facile à comprendreModélisation naturelleFlexible (schema-free)Temps de requête ultra-rapides
Graphes (Neo4j)
GraphAwareTM
Bien pour un PoC rapideBien pour des petits jeux de donnéesBien pour des logiques relativement simples
Cypher
GraphAwareTM
CypherMATCH (u:User)-[:LIKED]->(m:Movie), (m)<-[:LIKED]-(another:User), (another)-[:LIKED]->(reco:Movie)
WHERE NOT (u)-[:LIKED|DISLIKED]->(reco)
RETURN reco;
GraphAwareTM
Les spécificités d’un moteur de recommandation pour le monde réel sont souvent beaucoup plus complexes.
La réalité
GraphAwareTM
Imaginez que vous construisez la fonction ”personnes que vous pourriez connaître” sur LinkedIn.
Exemple
GraphAwareTM
Après une session de brainstorming, votre équipe définit de la façon suivante comment trouver des personnes que vous pourriez connaître:
Exemple
GraphAwareTM
Contacts en communAmis Facebook en communsContacts email/mobile en communTous les autres contacts email/mobileTravaillé pour la même sociétéEtudié à la même écolePartage le même intérêtVit dans la même ville
Personnes que vous pourriez connaître
GraphAwareTM
Mais c’est seulement le début!
Retournons en arrière et améliorons tout cela.
GraphAwareTM
Plus de contacts en commun = meilleure chance?Même ville / école / société = la taille compte?Qu’en est-t-il des emails qui ne représentent pas une personne ?Et les personnes déjà connectées? Et celles en attente… Et celles rejetées…Et celles ignorées répétivement…
Personnes que vous pourriez connaître
GraphAwareTM
Trouver des “choses” à recommanderServir les recommandations les plus rélévantesMesurer la qualité des recommandationsTime to market / coût du développement
Challenges Business
GraphAwareTM
Performance (real-time!)
Challenges Techniques
GraphAwareTM
Mauvaise Recommandation
GraphAwareTM
Mauvaise Recommandation
GraphAwareTM
Performance (real-time!)SimplicitéFlexibilité
Challenges Techniques
GraphAwareTM
Alors on a sorti une architecture de base open-source d’un moteur de recommandation qui vous aidera à résoudre tous ces challenges.
On l’a fait
GraphAwareTM
plugin Neo4j (utilise GraphAware Framework)vous devez utiliser un langage JVMarchitecture dogmatiqueultra rapideultra flexiblegère toute l’encapsulation
Moteur de recommandation
GraphAwareTM
Un moteur par “raison” de recommandation (core logic)Le moteur effectuée une traversée dans le graphe pour trouver des produits/personnes/..Les moteurs sont assemblés ensemble dans un moteur de haut-niveau
Décisions d’architecture
GraphAwareTM
Exemple
IS_OF_GENRE
title: “Love Actually”
Movie
name: “Bob”
User
name: “Comedy”
Genre
RATEDrating: 5
name: “Alice”
Username:
“Romance”
Genre
title: “American Pie”
Movie
IS_OF_GENRE
IS_OF_GENRE
RATEDrating: 5
INTERESTED_IN
rating: 5RATED
GraphAwareTM
Un moteur par “raison” de recommandation (core logic)Le moteur effectuée une traversée dans le graphe pour trouver des produits/personnes/..Les moteurs sont assemblés ensemble dans un moteur de haut-niveauLes produits trouvés plusieurs fois sont plus rélévantsLa rélévance dépend de la vitesse à laquelle un produit a été trouvé
Décisions d’architecture
GraphAwareTM
Exemple
IS_OF_GENRE
title: “Love Actually”
Movie
name: “Bob”
User
name: “Comedy”
Genre
RATEDrating: 5
name: “Alice”
Username:
“Romance”
Genre
title: “American Pie”
Movie
IS_OF_GENRE
IS_OF_GENRE
RATEDrating: 5
INTERESTED_IN
rating: 5RATED
GraphAwareTM
Un moteur par “raison” de recommandation (core logic)Le moteur effectuée une traversée dans le graphe pour trouver des produits/personnes/..Les moteurs sont assemblés ensemble dans un moteur de haut-niveauLes produits trouvés plusieurs fois sont plus rélévantsLa rélévance dépend de la vitesse à laquelle un produit a été trouvéProduits qui ne doivent pas être recommandés
Décisions d’architecture
GraphAwareTM
Example
IS_OF_GENRE
title: “Love Actually”
Movie
name: “Bob”
User
name: “Comedy”
Genre
RATEDrating: 5
name: “Alice”
Username:
“Romance”
Genre
title: “American Pie”
Movie
IS_OF_GENRE
IS_OF_GENRE
RATEDrating: 5
INTERESTED_IN
rating: 5RATED
GraphAwareTM
Input -> Engine -> RecommandationsScores et Score TransformersBlacklistsFiltersPost-processorsContext (combien, en combien de temps,…?)Loggers
Architecture
GraphAwareTM
En 5 minutes, on va construire un moteur de recommandation qui vous proposera des personnes avec qui vous pourriez vous connecter.
Let’s code
GraphAwareTM
0) Modèle
GraphAwareTM
1) Découverte
GraphAwareTM
public class FriendsInCommon extends SomethingInCommon { @Override public String name() { return "friendsInCommon"; } @Override protected RelationshipType getType() { return FRIEND_OF; } @Override protected Direction getDirection() { return BOTH; }}
GraphAwareTM
2) Score
GraphAwareTM
public class FriendsInCommon extends SomethingInCommon {
@Override protected ScoreTransformer scoreTransformer() { return new ParetoScoreTransformer(100, 10); } @Override public String name() { return "friendsInCommon"; } @Override protected RelationshipType getType() { return FRIEND_OF; } @Override protected Direction getDirection() { return BOTH; }}
GraphAwareTM
3) Post-Process
GraphAwareTM
public class RewardSameLocation extends RewardSomethingShared { @Override protected RelationshipType type() { return LIVES_IN; } @Override protected Direction direction() { return OUTGOING; } @Override protected float scoreValue(Node reco, Node in, Node shared) { return 10; } @Override protected String scoreName() { return "sameLocation"; }}
GraphAwareTM
public class RewardSameLabels implements PostProcessor<Node, Node> { @Override public void postProcess(Recommendations<Node> out, Node in) { Label[] inLabels = toArray(in.getLabels()); for (Recommendation<Node> reco : out.get()) { if (Arrays.equals(inLabels, toArray(reco.getItem().getLabels()))) { reco.add("sameGender", 10); } } }}
GraphAwareTM
4) Filter
GraphAwareTM
... @Override protected List<BlacklistBuilder<Node, Node>> blacklistBuilders() { return asList( new ExistingRelationshipBlacklistBuilder(FRIEND_OF, BOTH) ); } @Override protected List<Filter<Node, Node>> filters() { return asList( new ExcludeSelf() ); }
GraphAwareTM
5) Assemble
GraphAwareTM
public final class FriendsComputingEngine extends Neo4jTopLevelDelegatingEngine { @Override protected List<RecommendationEngine<Node, Node>> engines() { return Arrays.<RecommendationEngine<Node, Node>>asList( new FriendsInCommon(), new RandomPeople() ); } @Override protected List<PostProcessor<Node, Node>> postProcessors() { return Arrays.asList( new RewardSameLabels(), new RewardSameLocation(), new PenalizeAgeDifference() ); } @Override protected List<BlacklistBuilder<Node, Node>> blacklistBuilders() { return Arrays.asList( new ExistingRelationshipBlacklistBuilder(FRIEND_OF, BOTH) ); } @Override protected List<Filter<Node, Node>> filters() { return Arrays.<Filter<Node, Node>>asList( new ExcludeSelf() ); }}
GraphAwareTM
?) Precompute
GraphAwareTM
public final class FriendsComputingEngine extends Neo4jTopLevelDelegatingEngine { public FriendsComputingEngine() { super(new FriendsContextFactory()); } @Override protected List<RecommendationEngine<Node, Node>> engines() { return asList( new FriendsInCommon(), new RandomPeople() ); } @Override protected List<PostProcessor<Node, Node>> postProcessors() { return asList( new RewardSameLabels(), new RewardSameLocation(), new PenalizeAgeDifference() ); } @Override public ParticipationPolicy<Node, Node> participationPolicy(Context<Node, Node> context) { return ParticipationPolicy.IF_MORE_RESULTS_NEEDED; }}
GraphAwareTM
public final class FriendsRecommendationEngine extends Neo4jTopLevelDelegatingEngine { @Override protected List<RecommendationEngine<Node, Node>> engines() { return Arrays.<RecommendationEngine<Node, Node>>asList( new Neo4jPrecomputedEngine(), new FriendsComputingEngine() ); } @Override protected List<BlacklistBuilder<Node, Node>> blacklistBuilders() { return Arrays.asList( new ExistingRelationshipBlacklistBuilder(FRIEND_OF, BOTH) ); } @Override protected List<Filter<Node, Node>> filters() { return Arrays.<Filter<Node, Node>>asList( new ExcludeSelf() ); }}
GraphAwareTM
6) Log
GraphAwareTM
public final class FriendsRecommendationEngine extends Neo4jTopLevelDelegatingEngine { @Override protected List<RecommendationEngine<Node, Node>> engines() { return Arrays.<RecommendationEngine<Node, Node>>asList( new Neo4jPrecomputedEngine(), new FriendsComputingEngine() ); } @Override protected List<BlacklistBuilder<Node, Node>> blacklistBuilders() { return Arrays.asList( new ExistingRelationshipBlacklistBuilder(FRIEND_OF, BOTH) ); } @Override protected List<Filter<Node, Node>> filters() { return Arrays.<Filter<Node, Node>>asList( new ExcludeSelf() ); } @Override protected List<Logger<Node, Node>> loggers() { return Arrays.asList( new Slf4jRecommendationLogger<Node, Node>(), new Slf4jStatisticsLogger<Node, Node>() ); }}
GraphAwareTM
7) Test
GraphAwareTM
List<Recommendation<Node>> reco = recommendationEngine.recommend(getPersonByName(“Adam"), 2);String expected = "(Vince {total:19.338144," + "ageDifference:-5.527864," + "friendsInCommon:14.866008," + "sameGender:10.0})," + "(Luanne {total:11.553411," + "ageDifference:-3.312597," + "friendsInCommon:14.866008})"; assertEquals(expected, toString(reco));
GraphAwareTM
Trouver des “choses” à recommanderServir les recommandations les plus rélévantesMesurer la qualité des recommandationsTime to market / coût du développement
Challenges Business
GraphAwareTM
Performance (real-time!)SimplicitéFlexibilité
Challenges Techniques
GraphAwareTM
A vous !<dependencies>
...
<dependency> <groupId>com.graphaware.neo4j</groupId> <artifactId>recommendation-engine</artifactId> <version>2.2.0.30.6</version> </dependency>
...
<dependencies>
GraphAwareTM
Habilité intégrée pour pré-computer les recommandationsAutres classes intégréesTime-based ParticipationPolicy Raisons for RecommendationContribuez !https://github.com/graphaware/neo4j-reco
Il y a encore plus !
GraphAwareTM
Algorithmes intégrésIntégration avec des compute enginesMachine learning
Futur
GraphAwareTM
Le Framework GraphAware rend facile la conception, le test et le déploiement de fonctionnalités pour Neo4j, aussi bien génériques que spécifiques à un domaine.
GraphAware Framework
GraphAwareTM
GraphUnit& RestTest RelCount WarmUp Schema (wip) Recommendation
Engine
GraphAware Framework
ChangeFeed UUID TimeTree Algorithms NodeRank
GraphAwareTM
Open Source (GPL)ActifProduction ReadyGithub: github.com/graphawareWeb: graphaware.comMaven Central
GraphAware Framework
GraphAwareTM
EssayezDonnez-nous du feedbackContribuezConcevez vos propres modulesContactez-nous pour du support/consultance
GraphAware Framework
Questions ?
Merci !
GraphAwareTM
Christophe Willemsen
Twitter: @ikwattro
Github: @ikwattro