GDS 会话(适用于自管理数据库)
1. 先决条件
此笔记本需要有一个可用的 Neo4j 实例,并且您的 Neo4j Aura 租户已启用 GDS 会话功能。请联系您的客户经理启用此功能。
我们还需要安装 graphdatascience
Python 库,版本 1.12a1
或更高版本。
from datetime import timedelta
%pip install "graphdatascience>=1.12a1"
2. Aura API 凭据
GDS 会话通过 Aura API 管理。为了使用 Aura API,我们需要 Aura API 凭据。
使用这些凭据,我们可以创建 GdsSessions
对象,它是管理 GDS 会话的主要入口点。
import os
from graphdatascience.session import GdsSessions, AuraAPICredentials
client_id = os.environ["AURA_API_CLIENT_ID"]
client_secret = os.environ["AURA_API_CLIENT_SECRET"]
# If your account is a member of several tenants, you must also specify the tenant ID to use
tenant_id = os.environ.get("AURA_API_TENANT_ID", None)
sessions = GdsSessions(api_credentials=AuraAPICredentials(client_id, client_secret, tenant_id=tenant_id))
3. 创建一个新的会话
通过调用 sessions.get_or_create()
来创建一个新的会话。作为数据源,我们假设已设置了一个自管理的 Neo4j DBMS 实例,并且可以访问该实例。我们需要将数据库地址、用户名和密码传递给 DbmsConnectionInfo
类。
我们还需要指定会话大小。有关完整列表,请参阅 API 参考文档或手册。
最后,我们需要为我们的会话命名。我们将它命名为 people-and-fruits-sm'。通过调用`get_or_create
并使用相同的会话名称和配置,可以重新连接到现有会话。
我们还将设置会话的生存时间 (TTL)。这确保了我们的会话在 5 小时未使用后自动删除。如果您忘记自己删除会话,这是一个避免产生费用的好方法。
import os
from graphdatascience.session import DbmsConnectionInfo, AlgorithmCategory, CloudLocation
from datetime import timedelta
# Identify the Neo4j DBMS
db_connection = DbmsConnectionInfo(uri=os.environ["NEO4J_URI"], username=os.environ["NEO4J_USER"], password=os.environ["NEO4J_PASSWORD"])
# Specify where to create the GDS session
cloud_location = CloudLocation(provider="gcp", region="europe-west1")
# Create a GDS session!
memory = sessions.estimate(
node_count=20,
relationship_count=50,
algorithm_categories=[AlgorithmCategory.CENTRALITY, AlgorithmCategory.NODE_EMBEDDING],
)
gds = sessions.get_or_create(
# we give it a representative name
session_name="people-and-fruits-sm",
memory=memory,
db_connection=db_connection,
ttl=timedelta(hours=5),
cloud_location=cloud_location,
)
5. 添加数据集
我们假设配置的 Neo4j 数据库实例为空。我们将使用标准 Cypher 添加我们的数据集。
在更现实的场景中,此步骤已经完成,我们只需要连接到现有的数据库。
data_query = """
CREATE
(dan:Person {name: 'Dan', age: 18, experience: 63, hipster: 0}),
(annie:Person {name: 'Annie', age: 12, experience: 5, hipster: 0}),
(matt:Person {name: 'Matt', age: 22, experience: 42, hipster: 0}),
(jeff:Person {name: 'Jeff', age: 51, experience: 12, hipster: 0}),
(brie:Person {name: 'Brie', age: 31, experience: 6, hipster: 0}),
(elsa:Person {name: 'Elsa', age: 65, experience: 23, hipster: 1}),
(john:Person {name: 'John', age: 4, experience: 100, hipster: 0}),
(apple:Fruit {name: 'Apple', tropical: 0, sourness: 0.3, sweetness: 0.6}),
(banana:Fruit {name: 'Banana', tropical: 1, sourness: 0.1, sweetness: 0.9}),
(mango:Fruit {name: 'Mango', tropical: 1, sourness: 0.3, sweetness: 1.0}),
(plum:Fruit {name: 'Plum', tropical: 0, sourness: 0.5, sweetness: 0.8})
CREATE
(dan)-[:LIKES]->(apple),
(annie)-[:LIKES]->(banana),
(matt)-[:LIKES]->(mango),
(jeff)-[:LIKES]->(mango),
(brie)-[:LIKES]->(banana),
(elsa)-[:LIKES]->(plum),
(john)-[:LIKES]->(plum),
(dan)-[:KNOWS]->(annie),
(dan)-[:KNOWS]->(matt),
(annie)-[:KNOWS]->(matt),
(annie)-[:KNOWS]->(jeff),
(annie)-[:KNOWS]->(brie),
(matt)-[:KNOWS]->(brie),
(brie)-[:KNOWS]->(elsa),
(brie)-[:KNOWS]->(jeff),
(john)-[:KNOWS]->(jeff);
"""
# making sure the database is actually empty
assert gds.run_cypher("MATCH (n) RETURN count(n)").squeeze() == 0, "Database is not empty!"
# let's now write our graph!
gds.run_cypher(data_query)
gds.run_cypher("MATCH (n) RETURN count(n) AS nodeCount")
6. 投影图
现在我们已将图导入到我们的数据库,我们可以将其投影到我们的 GDS 会话中。我们使用 gds.graph.project()
端点来做到这一点。
我们正在使用的远程投影查询选择所有 Person
节点及其 LIKES
关系,以及所有 Fruit
节点及其 LIKES
关系。此外,我们还出于说明目的投影节点属性。我们可以使用这些节点属性作为算法的输入,尽管在本笔记本中我们不会这样做。
G, result = gds.graph.project(
"people-and-fruits",
"""
CALL {
MATCH (p1:Person)
OPTIONAL MATCH (p1)-[r:KNOWS]->(p2:Person)
RETURN
p1 AS source, r AS rel, p2 AS target,
p1 {.age, .experience, .hipster } AS sourceNodeProperties,
p2 {.age, .experience, .hipster } AS targetNodeProperties
UNION
MATCH (f:Fruit)
OPTIONAL MATCH (f)<-[r:LIKES]-(p:Person)
RETURN
p AS source, r AS rel, f AS target,
p {.age, .experience, .hipster } AS sourceNodeProperties,
f { .tropical, .sourness, .sweetness } AS targetNodeProperties
}
RETURN gds.graph.project.remote(source, target, {
sourceNodeProperties: sourceNodeProperties,
targetNodeProperties: targetNodeProperties,
sourceNodeLabels: labels(source),
targetNodeLabels: labels(target),
relationshipType: type(rel)
})
""",
)
str(G)
7. 运行算法
现在我们可以对投影的图运行算法。这是使用标准 GDS Python 客户端 API 完成的。还有许多其他教程涵盖了我们在这一步可以做的一些有趣的事情,因此我们将在这里简要介绍。
我们将简单地在图上运行 PageRank 和 FastRP。
print("Running PageRank ...")
pr_result = gds.pageRank.mutate(G, mutateProperty="pagerank")
print(f"Compute millis: {pr_result['computeMillis']}")
print(f"Node properties written: {pr_result['nodePropertiesWritten']}")
print(f"Centrality distribution: {pr_result['centralityDistribution']}")
print("Running FastRP ...")
frp_result = gds.fastRP.mutate(
G,
mutateProperty="fastRP",
embeddingDimension=8,
featureProperties=["pagerank"],
propertyRatio=0.2,
nodeSelfInfluence=0.2,
)
print(f"Compute millis: {frp_result['computeMillis']}")
# stream back the results
gds.graph.nodeProperties.stream(G, ["pagerank", "fastRP"], separate_property_columns=True, db_node_properties=["name"])
8. 写回 Neo4j
GDS 会话的内存中图是从我们指定的 Neo4j 数据库中的数据投影而来的。因此,写回操作将把数据持久化回同一个 Neo4j 数据库。让我们将 PageRank 和 FastRP 算法的结果写回 Neo4j 数据库。
# if this fails once with some error like "unable to retrieve routing table"
# then run it again. this is a transient error with a stale server cache.
gds.graph.nodeProperties.write(G, ["pagerank", "fastRP"])
当然,我们也可以只使用 .write
模式。让我们以写入模式运行 Louvain 来展示
gds.louvain.write(G, writeProperty="louvain")
现在我们可以使用 gds.run_cypher()
方法来查询更新后的图。请注意,run_cypher()
方法将在 Neo4j 数据库上运行查询。
gds.run_cypher(
"""
MATCH (p:Person)
RETURN p.name, p.pagerank AS rank, p.louvain
ORDER BY rank DESC
"""
)
9. 删除会话
现在我们已完成分析,我们可以删除会话。我们产生的结果已写回我们的 Neo4j 数据库,并且不会丢失。如果我们计算了未写回的其他内容,则这些内容将丢失。
删除会话将释放与之关联的所有资源,并停止产生费用。
gds.delete()
# or sessions.delete(session_name="people-and-fruits")
# let's also make sure the deleted session is truly gone:
sessions.list()
# Lastly, let's clean up the database
gds.run_cypher("MATCH (n:Person|Fruit) DETACH DELETE n")