Advertisement

Decision Trees in Neo4j

Software Field Engineer at Relational AI
Sep. 26, 2018
Advertisement

More Related Content

Advertisement
Advertisement

Decision Trees in Neo4j

  1. Decision Trees in Neo4j Build dynamic expert systems and rules engines.
  2. github.com/maxdemarzi About 200 public repositories Max De Marzi Neo4j Field Engineer About Me ! 01 02 03 04 maxdemarzi.com @maxdemarzi
  3. Let’s start with a Sign
  4. Rule for Getting In • Anyone over 21 years old gets in. • On some nights, women 18 years or older get in. • On some nights, men 18 years or older get in. • The last two change dynamically.
  5. Represent in a Graph
  6. Rule Node • expression (String): • (age >= 18) && gender.equals(“female”) • parameter_names(String): • age, gender • parameter_types(String): • Int, String
  7. There was a time… BEFORE
  8. When the Traversal API ruled supreme
  9. https://maxdemarzi.com/2015/09/04/flight-search-with-the-neo4j-traversal-api/ Learn more… or read the docs
  10. The Stored Procedure
  11. @Procedure(name = "com.maxdemarzi.traverse.decision_tree", mode = Mode.READ) @Description("CALL com.maxdemarzi.traverse.decision_tree(tree, facts)") public Stream<PathResult> traverseDecisionTree( @Name("tree") String id, @Name("facts") Map<String, String> facts){ // Which Decision Tree are we interested in? Node tree = db.findNode(Labels.Tree, "id", id); if ( tree != null) { // Find the paths by traversing this graph and the facts given return decisionPath(tree, facts); } return null; } Tree Facts
  12. private Stream<PathResult> decisionPath(Node tree, 
 Map<String, String> facts) { TraversalDescription myTraversal = db.traversalDescription() .depthFirst() .expand(new DecisionTreeExpander(facts)) .evaluator(decisionTreeEvaluator); return myTraversal .traverse(tree) .stream() .map(PathResult::new); } Build a Traversal Description
  13. public class DecisionTreeEvaluator implements PathEvaluator { @Override public Evaluation evaluate(Path path, BranchState branchState) { // If we get to an Answer stop traversing, we found a valid path. if (path.endNode().hasLabel(Labels.Answer)) { return Evaluation.INCLUDE_AND_PRUNE; } else { // If not, continue down this path 
 // to see if there is anything else to find. return Evaluation.EXCLUDE_AND_CONTINUE; } } Build a Path Evaluator
  14. public class DecisionTreeExpander implements PathExpander { private Map<String, String> facts; private ExpressionEvaluator ee = new ExpressionEvaluator(); public DecisionTreeExpander(Map<String, String> facts) { this.facts = facts; ee.setExpressionType(boolean.class); } Build a Path Expander
  15. @Override public Iterable<Relationship> expand(Path path, BranchState branchState) { // If we get to an Answer stop traversing, we found a valid path. if (path.endNode().hasLabel(Labels.Answer)) { return Collections.emptyList(); } // If we have Rules to evaluate, go do that. if (path.endNode() .hasRelationship(Direction.OUTGOING, RelationshipTypes.HAS)) { return path.endNode() .getRelationships(Direction.OUTGOING, RelationshipTypes.HAS); } The expand method
  16. if (path.endNode().hasLabel(Labels.Rule)) { try { if (isTrue(path.endNode())) { return path.endNode() .getRelationships(Direction.OUTGOING, RelationshipTypes.IS_TRUE); } else { return path.endNode() .getRelationships(Direction.OUTGOING, RelationshipTypes.IS_FALSE); } } catch (Exception e) { // Could not continue this way! return Collections.emptyList(); } } The expand method (cont.)
  17. boolean isTrue(Node rule) throws Exception { Map<String, Object> ruleProperties = rule.getAllProperties(); String[] parameterNames = Magic.explode((String) ruleProperties .get("parameter_names")); Class<?>[] parameterTypes = Magic.stringToTypes((String) ruleProperties .get("parameter_types")); The isTrue method
  18. Object[] arguments = new Object[parameterNames.length]; for (int j = 0; j < parameterNames.length; ++j) { arguments[j] = Magic.createObject(parameterTypes[j], facts.get(parameterNames[j])); } ee.setParameters(parameterNames, parameterTypes); ee.cook((String)ruleProperties.get("expression")); return (boolean) ee.evaluate(arguments); The isTrue method (cont.)
  19. Running it
  20. CREATE (tree:Tree { id: 'bar entrance' }) CREATE (over21_rule:Rule { parameter_names: 'age', parameter_types:'int', expression:'age >= 21' }) CREATE (gender_rule:Rule { parameter_names: 'age,gender', parameter_types:'int,String', expression:'(age >= 18) && gender.equals("female")' }) CREATE (answer_yes:Answer { id: 'yes'}) CREATE (answer_no:Answer { id: 'no'}) CREATE (tree)-[:HAS]->(over21_rule) CREATE (over21_rule)-[:IS_TRUE]->(answer_yes) CREATE (over21_rule)-[:IS_FALSE]->(gender_rule) CREATE (gender_rule)-[:IS_TRUE]->(answer_yes) CREATE (gender_rule)-[:IS_FALSE]->(answer_no) Build the Tree
  21. CALL com.maxdemarzi.traverse.decision_tree(
 ‘bar entrance', {gender:'male', age:'20'}) YIELD path RETURN path; Check the Facts Male, 20 years old?
  22. It’s alright…
  23. CALL com.maxdemarzi.traverse.decision_tree(
 ‘bar entrance', {gender:'female', age:’19'}) YIELD path RETURN path; Female, 19 years old? Check the Facts
  24. OMG…
  25. Learn More…
  26. https://maxdemarzi.com/2018/01/14/dynamic-rule-based-decision-trees-in-neo4j/ Details:
  27. What about multiple Options?
  28. https://maxdemarzi.com/2018/01/26/dynamic-rule-based-decision-trees-in-neo4j-part-2/ There is a Part 2
  29. Decision Streams • Paper: 
 https://arxiv.org/pdf/1704.07657.pdf • Authors:
 Dmitry Ignatov and Andrey Ignatov • Implementation: 
 https://github.com/aiff22/Decision-Stream
  30. Thank you!
Advertisement