项目作者: hscspring

项目描述 :
Memory for Knowledge Graph, using Neo4j. 知识图谱存储与查询。
高级语言: Python
项目地址: git://github.com/hscspring/nlm.git
创建时间: 2019-10-15T01:31:38Z
项目社区:https://github.com/hscspring/nlm

开源协议:MIT License

下载


README

This is a repo focused on NLP memory. Specifically, memorize (store) a node or relationship to the knowledge graph (Actually a Neo4j database instance). And recall (query) a node or relationship from the memory. It’s not only a module, but also a RPC service which can be easily setup.

Here are some scenes:

  • When input is a node or relationship
    • Use several information of a node or relationship to recall a node or a relationship with full information in the memory.
    • Automatically add a node or relationship when there is nothing to recall.
    • Automatically update the properties of a node or relationship when a node or relationship has been recalled.
  • When input is a raw string or a NLU output
    • Automatically extract nodes or relationships from the input.
    • Then do the things above.

The extractor is in development.

Furthermore, recalls are based on nodes (label and name) and relationships (start, end, kind), and their properties are mainly used to sort the results.

中文文档和设计思想:自然语言记忆模块(NLM) | Yam

Setup

IMPORTANT: only support Python3.7+.

  • Step 1: Install dependencies

    1. # do not have pipenv
    2. $ python3 -m venv env
    3. $ source env/bin/activate
    4. $ pip install -r requirements.txt
  • Step 2: Setup a neo4j database

    1. $ docker run --rm -it -p 7475:7474 -p 7688:7687 neo4j

    Here we use another two ports for play and test.

    When the docker has been set up, you should open http://localhost:7475/browser/, modify the port to 7688, input the password neo4j and then change the password to password

  • Step 3: Running the tests

    1. $ pipenv shell
    2. $ pytest

    This step will add 8 nodes and relationships to your Neo4j database.

The document is under ./docs which can be generated by Sphinx, just run make html.

Usage

Module

  1. from py2neo.database import Graph
  2. from nlm import NLMLayer, GraphNode, GraphRelation
  3. mem = NLMLayer(graph=Graph(port=7688),
  4. fuzzy_node=False,
  5. add_inexistence=False,
  6. update_props=False)
  7. ############ Node ############
  8. # recall
  9. node = GraphNode("Person", "AliceThree")
  10. mem(node)
  11. [GraphNode(label='Person', name='AliceThree', props={'age': 22, 'sex': 'male'})]
  12. # add inexistence, here `add_inexistence=True` has covered the NLMLayer config.
  13. new = GraphNode("Person", "Bob")
  14. mem(new, add_inexistence=True)
  15. []
  16. # fuzzy recall
  17. node = GraphNode("Person", "AliceT")
  18. mem(node, fuzzy_node=True)
  19. [GraphNode(label='Person', name='AliceTwo', props={'age': 21, 'occupation': 'teacher'})]
  20. # update property
  21. node = GraphNode("Person", "AliceThree", props={"age": 24})
  22. mem(node, update_props=True)
  23. [GraphNode(label='Person', name='AliceThree', props={'age': 24, 'sex': 'male'})]
  24. # topn
  25. node = GraphNode("Person", "AliceT")
  26. mem(node, fuzzy_node=True, topn=2)
  27. [GraphNode(label='Person', name='AliceTwo', props={'age': 21, 'occupation': 'teacher'}),
  28. GraphNode(label='Person', name='AliceThree', props={'age': 24, 'sex': 'male'})
  29. ]
  30. ############ Relation ############
  31. # recall
  32. start = GraphNode("Person", "AliceThree")
  33. end = GraphNode("Person", "AliceOne")
  34. relation = GraphRelation(start, end, "LOVES")
  35. mem(relation)
  36. [GraphRelation(
  37. start=GraphNode(label='Person', name='AliceThree', props={'age': 22, 'sex': 'male'}),
  38. end=GraphNode(label='Person', name='AliceOne', props={'occupation': 'teacher', 'age': 22, 'sex': 'female'}),
  39. kind='LOVES',
  40. props={'from': 2011, 'roles': 'husband'})
  41. ]
  42. # add inexistence
  43. start = GraphNode("Person", "AliceThree")
  44. end = GraphNode("Person", "Bob")
  45. relation = GraphRelation(start, end, "KNOWS")
  46. mem(relation, add_inexistence=True)
  47. []
  48. # fuzzy recall
  49. start = GraphNode("Person", "AliceTh")
  50. end = GraphNode("Person", "AliceO")
  51. relation = GraphRelation(start, end, "LOVES")
  52. mem(relation, fuzzy_node=True)
  53. [GraphRelation(
  54. start=GraphNode(label='Person', name='AliceThree', props={'age': 24, 'sex': 'male'}),
  55. end=GraphNode(label='Person', name='AliceOne', props={'occupation': 'teacher', 'age': 22, 'sex': 'female'}),
  56. kind='LOVES',
  57. props={'from': 2011, 'roles': 'husband'})
  58. ]
  59. # two nodes, topn
  60. start = GraphNode("Person", "AliceThree")
  61. end = GraphNode("Person", "AliceOne")
  62. relation = GraphRelation(start, end)
  63. mem(relation, topn=3)
  64. [GraphRelation(
  65. start=GraphNode(label='Person', name='AliceThree', props={'age': 24, 'sex': 'male'}),
  66. end=GraphNode(label='Person', name='AliceOne', props={'occupation': 'teacher', 'age': 22, 'sex': 'female'}),
  67. kind='WORK_WITH',
  68. props={'from': 2009, 'roles': 'boss'}),
  69. GraphRelation(
  70. start=GraphNode(label='Person', name='AliceThree', props={'age': 24, 'sex': 'male'}),
  71. end=GraphNode(label='Person', name='AliceOne', props={'occupation': 'teacher', 'age': 22, 'sex': 'female'}),
  72. kind='LOVES',
  73. props={'from': 2011, 'roles': 'husband'})
  74. ]
  75. # update property (relationship)
  76. start = GraphNode("Person", "AliceThree")
  77. end = GraphNode("Person", "Bob")
  78. relation = GraphRelation(start, end, "KNOWS", {"roles": "classmate"})
  79. mem(relation, update_props=True)
  80. [GraphRelation(
  81. start=GraphNode(label='Person', name='AliceThree', props={'age': 24, 'sex': 'male'}),
  82. end=GraphNode(label='Person', name='Bob', props={}),
  83. kind='KNOWS',
  84. props={})
  85. ]
  86. mem(relation)
  87. [GraphRelation(
  88. start=GraphNode(label='Person', name='AliceThree', props={'age': 24, 'sex': 'male'}),
  89. end=GraphNode(label='Person', name='Bob', props={}),
  90. kind='KNOWS',
  91. props={'roles': 'classmate'})
  92. ]
  93. # update property (node + relationship)
  94. start = GraphNode("Person", "AliceThree")
  95. end = GraphNode("Person", "Bob", {"sex": "male"})
  96. relation = GraphRelation(start, end, "KNOWS", {"roles": "friend"})
  97. mem(relation, update_props=True)
  98. [GraphRelation(
  99. start=GraphNode(label='Person', name='AliceThree', props={'age': 24, 'sex': 'male'}),
  100. end=GraphNode(label='Person', name='Bob', props={'sex': 'male'}),
  101. kind='KNOWS',
  102. props={'roles': 'friend'})
  103. ]
  104. start = GraphNode("Person", "AliceThree")
  105. end = GraphNode("Person", "Bob", {"sex": "male"})
  106. relation = GraphRelation(start, end, "STUDY_WITH", {"roles": "classmate"})
  107. mem(relation, update_props=True)
  108. mem(relation)
  109. [GraphRelation(
  110. start=GraphNode(label='Person', name='AliceThree', props={'age': 24, 'sex': 'male'}),
  111. end=GraphNode(label='Person', name='Bob', props={'sex': 'male'}),
  112. kind='STUDY_WITH',
  113. props={'roles': 'classmate'})
  114. ]
  115. mem(GraphRelation(start, end), topn=2)
  116. [GraphRelation(
  117. start=GraphNode(label='Person', name='AliceThree', props={'age': 24, 'sex': 'male'}),
  118. end=GraphNode(label='Person', name='Bob', props={'sex': 'male'}),
  119. kind='STUDY_WITH',
  120. props={'roles': 'classmate'}),
  121. GraphRelation(
  122. start=GraphNode(label='Person', name='AliceThree', props={'age': 24, 'sex': 'male'}),
  123. end=GraphNode(label='Person', name='Bob', props={'sex': 'male'}),
  124. kind='KNOWS',
  125. props={'roles': 'friend'})
  126. ]
  127. ############ RawString and NLU Output ############
  128. # will first extract nodes or relationships, then like the above.
  129. # will coming soon.
  130. ############ Graph ############
  131. mem.labels
  132. frozenset({'Person'})
  133. mem.relationship_types
  134. frozenset({'KNOWS', 'LIKES', 'LOVES', 'STUDY_WITH', 'WORK_WITH'})
  135. mem.nodes_num
  136. 9
  137. mem.relationships_num
  138. 10
  139. mem.nodes
  140. # all nodes generator
  141. mem.relationships
  142. # all relationships generator
  143. mem.query("MATCH (a:Person) RETURN a.age, a.name LIMIT 5")
  144. [{'a.age': 21, 'a.name': 'AliceTwo'},
  145. {'a.age': 23, 'a.name': 'AliceFour'},
  146. {'a.age': 22, 'a.name': 'AliceOne'},
  147. {'a.age': 24, 'a.name': 'AliceFive'},
  148. {'a.age': None, 'a.name': 'Bob'}
  149. ]

Since our mem is actually inherited from the py2neo.Graph, all the functions in the py2neo.Graph can be called through mem. We just make it more convenient and easy to use, especially focus on storage and query.

In addition, when fuzzy_node is True, properties will not be updated. Because the query might be a fuzzy node which does not have the properties we have sent in.

RPC Service

In the gRPC service, you have to have the parameters be set when you are running the serve.

  1. $ python server.py [OPTIONS]
  2. Options:
  3. -fn fuzzy_node
  4. -ai add_inexistence
  5. -up update_props

You could use any programming language in the client side, more detail please read gRPC.

There are total 4 interfaces here:

  • NodeRecall
  • RelationRecall
  • StrRecall
  • NLURecall

The last two is still in development. There is a python client example (client.py) in the repo.

Why

The original intention is to build a memory part for chatbot. We just want the chatbot to automatically memorize the nodes and relationships discovered in dialogue. The input was defined to be the output of NLU (understand) layer. We also want to use the information when the chatbot is responding. So the output was defined to be the input of NLG (generate) layer or NLI (infer) layer. That’s it.

Batch

We have also written an example (under ./batch_example) to add many nodes and relationships in one time. The data comes from QASystemOnMedicalKG, feel free to modify the code to fit your demand.

Changelog

  • 191201 create