GraphSAGE
此功能在 Aura 图分析无服务器中不可用。 |
此功能处于 Beta 阶段。有关功能层级的更多信息,请参阅 API 层级。
词汇表
- 有向
-
有向特性。该算法在有向图上定义良好。
- 有向
-
有向特性。该算法忽略图的方向。
- 有向
-
有向特性。该算法不在有向图上运行。
- 无向
-
无向特性。该算法在无向图上定义良好。
- 无向
-
无向特性。该算法忽略图的无向性。
- 异构节点
-
异构节点完全支持。该算法能够区分不同类型的节点。
- 异构节点
-
异构节点允许。该算法对所有选定的节点一视同仁,无论其标签如何。
- 异构关系
-
异构关系完全支持。该算法能够区分不同类型的关系。
- 异构关系
-
异构关系允许。该算法对所有选定的关系一视同仁,无论其类型如何。
- 加权关系
-
加权特性。该算法支持将关系属性用作权重,通过 relationshipWeightProperty 配置参数指定。
- 加权关系
-
加权特性。该算法将每个关系视为同等重要,忽略任何关系权重的值。
GraphSAGE 是一种用于计算节点嵌入的归纳算法。GraphSAGE 使用节点特征信息在未见过的节点或图上生成节点嵌入。该算法不为每个节点训练单独的嵌入,而是学习一个函数,通过从节点的局部邻域采样和聚合特征来生成嵌入。
该算法专为无向图定义。 |
有关此算法的更多信息,请参阅
注意事项
孤立节点
如果您正在嵌入一个包含孤立节点的图,GraphSAGE 中的聚合步骤只能从节点本身提取信息。当该节点的所有属性均为 0.0
且激活函数为 ReLU 时,这会导致该节点的所有元素均为零向量。然而,由于 GraphSAGE 使用 L2 范数对节点嵌入进行归一化,而零向量无法归一化,因此在这种特殊情况下,我们将全零嵌入分配给此类节点。在为孤立节点生成全零嵌入的情况下,这可能会对最近邻或其他相似度算法等下游任务产生影响。在运行 GraphSAGE 之前,更合适的做法是过滤掉这些断开连接的节点。
图预采样以减少时间和内存
由于在大型图上训练 GraphSAGE 模型可能需要大量时间和内存,因此在训练之前采样一个较小的子图,然后在该子图上进行训练会很有帮助。由于 GraphSAGE 具有归纳性,训练好的模型仍然可以应用于预测完整图(或其他图)上的嵌入。要采样一个结构上有代表性的子图,请参阅 带重启的随机游走采样。
调优参数
一般来说,参数调优很大程度上取决于具体数据集。
聚合器
聚合器定义了如何结合节点的嵌入和来自上一层采样的邻居嵌入。GDS 支持 Mean
和 Pool
聚合器。
Mean
更简单,需要更少内存,计算速度更快。Pool
更复杂,可以编码更丰富的邻域信息。
样本大小
每个样本大小代表一个隐藏层,其输出大小等于嵌入维度。该层使用给定的聚合器和激活函数。更多的层会导致更远的邻居被考虑用于节点的嵌入。第 N
层使用距离 <\= N
的采样邻居嵌入作为第 N -1
层的输入。层数越多,所需的内存和计算时间就越高。
样本大小 n
意味着我们尝试从一个节点最多采样 n
个邻居。更高的样本大小也需要更多的内存和计算时间。
批处理大小
此参数定义了单个批次中分组的训练示例数量。对于每个训练示例,我们还将采样一个正例和一个负例。梯度使用 concurrency
数量的线程在批次上并行计算。
批处理大小不影响模型质量,但可用于调整训练速度。更大的批处理大小会增加计算的内存消耗。
迭代周期
此参数定义了训练的最大迭代周期数。在每个迭代周期之前,会为每个层采样新邻居,如 样本大小 中所指定。无论模型质量如何,训练都将在这么多迭代周期后终止。请注意,如果损失收敛(参见 容差),训练也可以提前停止。
设置此参数有助于限制模型的训练时间。限制计算预算可以起到正则化和缓解过拟合的作用,过拟合在大量迭代周期下会成为风险。
因为每个迭代周期都会重新采样邻居,所以多个迭代周期可以避免在特定邻域上过拟合。
批处理采样比率
此参数定义了单次迭代要采样的批次数量。
采样批次越多,梯度计算越准确。但是,更多的批次也会增加每次迭代的运行时间。
通常,建议确保使用的批次数量至少与定义的 concurrency
相同。
语法
CALL gds.beta.graphSage.train(
graphName: String,
configuration: Map
) YIELD
modelInfo: Map,
configuration: Map,
trainMillis: Integer
名称 | 类型 | 默认值 | 可选 | 描述 |
---|---|---|---|---|
graphName |
字符串 |
|
否 |
目录中存储的图的名称。 |
configuration |
映射 |
|
是 |
用于算法特定配置和/或图过滤的配置。 |
名称 | 类型 | 默认值 | 可选 | 描述 |
---|---|---|---|---|
modelName |
字符串 |
|
否 |
要训练的模型名称,在模型目录中必须不存在。 |
featureProperties |
字符串列表 |
|
否 |
应作为输入特征的节点属性名称。所有属性名称必须存在于投影图中,并且类型为 Float 或 List of Float。 |
字符串列表 |
|
是 |
使用给定节点标签过滤命名图。将包含具有任何给定标签的节点。 |
|
字符串列表 |
|
是 |
使用给定关系类型过滤命名图。将包含具有任何给定类型的关系。 |
|
整数 |
|
是 |
用于运行算法的并发线程数。 |
|
字符串 |
|
是 |
可用于更轻松地跟踪算法进度的 ID。 |
|
布尔值 |
|
是 |
如果禁用,将不会记录进度百分比。 |
|
embeddingDimension |
整数 |
|
是 |
生成的节点嵌入及其隐藏层表示的维度。 |
aggregator |
字符串 |
|
是 |
层将使用的聚合器。支持的值为 "Mean" 和 "Pool"。 |
activationFunction |
字符串 |
|
是 |
模型架构中要使用的激活函数。支持的值为 "Sigmoid" 和 "ReLu"。 |
sampleSizes |
整数列表 |
|
是 |
整数值列表,列表的大小决定了层数,值决定了层将采样多少个节点。 |
projectedFeatureDimension |
整数 |
|
是 |
投影 |
batchSize |
整数 |
|
是 |
每个批次的节点数。 |
浮点数 |
|
是 |
用于迭代周期提前收敛的容差,在每次迭代后检查。 |
|
learningRate |
浮点数 |
|
是 |
学习率决定了每次迭代中朝损失函数最小值移动的步长。 |
epochs |
整数 |
|
是 |
遍历图的次数。 |
整数 |
|
是 |
每个迭代周期的最大迭代次数。每次迭代都会更新权重。 |
|
batchSamplingRatio |
浮点数 |
|
是 |
每次权重更新要考虑的批次采样比率。默认情况下,每个线程评估一个批次。 |
searchDepth |
整数 |
|
是 |
随机游走的最大深度,用于采样附近节点进行训练。 |
negativeSampleWeight |
整数 |
|
是 |
负样本的权重。 |
字符串 |
|
是 |
用作权重的关系属性名称。如果未指定,算法将运行无权重模式。 |
|
randomSeed |
整数 |
|
是 |
用于控制嵌入计算中随机性的随机种子。 |
penaltyL2 |
浮点数 |
|
是 |
l2 惩罚项对损失函数的影响。 |
storeModelToDisk |
布尔值 |
|
是 |
训练后自动将模型存储到磁盘。 |
名称 | 类型 | 描述 |
---|---|---|
|
映射 |
训练模型的详细信息。 |
|
映射 |
用于运行过程的配置。 |
|
整数 |
训练模型所需毫秒数。 |
名称 | 类型 | 描述 |
---|---|---|
|
字符串 |
训练模型的名称。 |
|
字符串 |
训练模型的类型。始终为 |
|
映射 |
与运行训练相关的指标,详细信息见下表。 |
名称 | 类型 | 描述 |
---|---|---|
|
整数 |
训练期间运行的迭代周期数。 |
|
列表 |
每个迭代周期后每个节点的平均损失。 |
|
浮点数列表的列表 |
每个迭代周期中,每次迭代后每个节点的平均损失。 |
|
布尔值 |
指示训练是否已收敛。 |
CALL gds.beta.graphSage.stream(
graphName: String,
configuration: Map
) YIELD
nodeId: Integer,
embedding: List
名称 | 类型 | 默认值 | 可选 | 描述 |
---|---|---|---|---|
graphName |
字符串 |
|
否 |
目录中存储的图的名称。 |
configuration |
映射 |
|
是 |
用于算法特定配置和/或图过滤的配置。 |
名称 | 类型 | 默认值 | 可选 | 描述 |
---|---|---|---|---|
modelName |
字符串 |
|
否 |
模型目录中 GraphSAGE 模型的名称。 |
字符串列表 |
|
是 |
使用给定节点标签过滤命名图。将包含具有任何给定标签的节点。 |
|
字符串列表 |
|
是 |
使用给定关系类型过滤命名图。将包含具有任何给定类型的关系。 |
|
整数 |
|
是 |
用于运行算法的并发线程数。 |
|
字符串 |
|
是 |
可用于更轻松地跟踪算法进度的 ID。 |
|
布尔值 |
|
是 |
如果禁用,将不会记录进度百分比。 |
|
batchSize |
整数 |
|
是 |
每个批次的节点数。 |
名称 | 类型 | 描述 |
---|---|---|
|
整数 |
Neo4j 节点 ID。 |
|
浮点数列表 |
计算出的节点嵌入。 |
CALL gds.beta.graphSage.mutate(
graphName: String,
configuration: Map
)
YIELD
nodeCount: Integer,
nodePropertiesWritten: Integer,
preProcessingMillis: Integer,
computeMillis: Integer,
mutateMillis: Integer,
configuration: Map
名称 | 类型 | 默认值 | 可选 | 描述 |
---|---|---|---|---|
graphName |
字符串 |
|
否 |
目录中存储的图的名称。 |
configuration |
映射 |
|
是 |
用于算法特定配置和/或图过滤的配置。 |
名称 | 类型 | 默认值 | 可选 | 描述 |
---|---|---|---|---|
modelName |
字符串 |
|
否 |
模型目录中 GraphSAGE 模型的名称。 |
mutateProperty |
字符串 |
|
否 |
GDS 图中要写入嵌入的节点属性。 |
字符串列表 |
|
是 |
使用给定节点标签过滤命名图。 |
|
字符串列表 |
|
是 |
使用给定关系类型过滤命名图。 |
|
整数 |
|
是 |
用于运行算法的并发线程数。 |
|
字符串 |
|
是 |
可用于更轻松地跟踪算法进度的 ID。 |
|
batchSize |
整数 |
|
是 |
每个批次的节点数。 |
名称 | 类型 | 描述 |
---|---|---|
nodeCount |
整数 |
处理的节点数量。 |
nodePropertiesWritten |
整数 |
写入的节点属性数量。 |
preProcessingMillis |
整数 |
预处理数据所需毫秒数。 |
computeMillis |
整数 |
运行算法所需毫秒数。 |
mutateMillis |
整数 |
将结果数据写回投影图所需毫秒数。 |
configuration |
映射 |
用于运行算法的配置。 |
CALL gds.beta.graphSage.write(
graphName: String,
configuration: Map
)
YIELD
nodeCount: Integer,
nodePropertiesWritten: Integer,
preProcessingMillis: Integer,
computeMillis: Integer,
writeMillis: Integer,
configuration: Map
名称 | 类型 | 默认值 | 可选 | 描述 |
---|---|---|---|---|
graphName |
字符串 |
|
否 |
目录中存储的图的名称。 |
configuration |
映射 |
|
是 |
用于算法特定配置和/或图过滤的配置。 |
名称 | 类型 | 默认值 | 可选 | 描述 |
---|---|---|---|---|
modelName |
字符串 |
|
否 |
模型目录中 GraphSAGE 模型的名称。 |
字符串列表 |
|
是 |
使用给定节点标签过滤命名图。将包含具有任何给定标签的节点。 |
|
字符串列表 |
|
是 |
使用给定关系类型过滤命名图。将包含具有任何给定类型的关系。 |
|
整数 |
|
是 |
用于运行算法的并发线程数。 |
|
字符串 |
|
是 |
可用于更轻松地跟踪算法进度的 ID。 |
|
布尔值 |
|
是 |
如果禁用,将不会记录进度百分比。 |
|
整数 |
|
是 |
用于将结果写入 Neo4j 的并发线程数。 |
|
字符串 |
|
否 |
Neo4j 数据库中要写入嵌入的节点属性。 |
|
batchSize |
整数 |
|
是 |
每个批次的节点数。 |
名称 | 类型 | 描述 |
---|---|---|
nodeCount |
整数 |
处理的节点数量。 |
nodePropertiesWritten |
整数 |
写入的节点属性数量。 |
preProcessingMillis |
整数 |
预处理数据所需毫秒数。 |
computeMillis |
整数 |
运行算法所需毫秒数。 |
writeMillis |
整数 |
将结果数据写回 Neo4j 所需毫秒数。 |
configuration |
映射 |
用于运行算法的配置。 |
示例
以下所有示例都应在空数据库中运行。 示例通常使用 Cypher 投影。原生投影将在未来版本中弃用。 |
在本节中,我们将展示在具体图上运行 GraphSAGE 算法的示例。目的是说明结果如何,并提供在实际设置中如何使用该算法的指南。我们将在一个由少数节点以特定模式连接的小型朋友网络图上进行此操作。示例图如下所示
CREATE
// Persons
( dan:Person {name: 'Dan', age: 20, heightAndWeight: [185, 75]}),
(annie:Person {name: 'Annie', age: 12, heightAndWeight: [124, 42]}),
( matt:Person {name: 'Matt', age: 67, heightAndWeight: [170, 80]}),
( jeff:Person {name: 'Jeff', age: 45, heightAndWeight: [192, 85]}),
( brie:Person {name: 'Brie', age: 27, heightAndWeight: [176, 57]}),
( elsa:Person {name: 'Elsa', age: 32, heightAndWeight: [158, 55]}),
( john:Person {name: 'John', age: 35, heightAndWeight: [172, 76]}),
(dan)-[:KNOWS {relWeight: 1.0}]->(annie),
(dan)-[:KNOWS {relWeight: 1.6}]->(matt),
(annie)-[:KNOWS {relWeight: 0.1}]->(matt),
(annie)-[:KNOWS {relWeight: 3.0}]->(jeff),
(annie)-[:KNOWS {relWeight: 1.2}]->(brie),
(matt)-[:KNOWS {relWeight: 10.0}]->(brie),
(brie)-[:KNOWS {relWeight: 1.0}]->(elsa),
(brie)-[:KNOWS {relWeight: 2.2}]->(jeff),
(john)-[:KNOWS {relWeight: 5.0}]->(jeff)
MATCH (source:Person)
OPTIONAL MATCH (source:Person)-[r:KNOWS]->(target:Person)
RETURN gds.graph.project(
'persons',
source,
target,
{
sourceNodeLabels: labels(source),
targetNodeLabels: labels(target),
sourceNodeProperties: source { .age, .heightAndWeight },
targetNodeProperties: target { .age, .heightAndWeight },
relationshipType: type(r),
relationshipProperties: r { .relWeight }
},
{ undirectedRelationshipTypes: ['KNOWS'] }
)
该算法专为无向图定义。 |
训练
在生成节点嵌入之前,我们需要训练一个模型并将其存储在模型目录中。下面是一个如何操作的示例。
featureProperties 配置参数中指定的名称必须存在于投影图中。 |
CALL gds.beta.graphSage.train(
'persons',
{
modelName: 'exampleTrainModel',
featureProperties: ['age', 'heightAndWeight'],
aggregator: 'mean',
activationFunction: 'sigmoid',
randomSeed: 1337,
sampleSizes: [25, 10]
}
) YIELD modelInfo as info
RETURN
info.modelName as modelName,
info.metrics.didConverge as didConverge,
info.metrics.ranEpochs as ranEpochs,
info.metrics.epochLosses as epochLosses
modelName | didConverge | ranEpochs | epochLosses |
---|---|---|---|
"exampleTrainModel" |
true |
1 |
[26.5784954435] |
由于权重变量的随机初始化,不同运行的结果可能会有所不同。 |
查看结果,我们可以得出以下结论:训练在单个迭代周期后收敛,损失几乎相同。调整算法参数,例如尝试不同的 sampleSizes
、searchDepth
、embeddingDimension
或 batchSize
可以改善损失。对于不同的数据集,GraphSAGE 可能需要不同的训练参数才能生成好的模型。
训练好的模型会自动注册到 模型目录 中。
使用多个节点标签进行训练
在本节中,我们将介绍如何在具有多个标签的图上进行训练。不同的标签可能具有不同的属性集。为了在此类图上运行,GraphSAGE 以多标签模式运行,其中特征属性被投影到一个公共特征空间中。因此,所有节点在投影后都具有相同维度的特征向量。
标签的投影是线性的,由一个权重矩阵给出。每个标签的权重与 GraphSAGE 模型的其他权重一起学习。
在多标签模式下,在通常的聚合层之前应用以下内容
-
表示标签的属性被添加到该标签的特征属性中
-
每个标签的特征属性被投影到一个共享维度的特征向量中
投影特征维度通过 projectedFeatureDimension
配置,指定此参数即可启用多标签模式。
用于标签的特征属性是 featureProperties
配置参数中存在于该标签的图中的属性。在多标签模式下,不再要求所有标签都具有所有指定的属性。
示例
为了演示带有多个标签的 GraphSAGE,我们在示例图中添加了乐器以及人与乐器之间类型为 LIKE
的关系。
MATCH
(dan:Person {name: "Dan"}),
(annie:Person {name: "Annie"}),
(matt:Person {name: "Matt"}),
(brie:Person {name: "Brie"}),
(john:Person {name: "John"})
CREATE
(guitar:Instrument {name: 'Guitar', cost: 1337.0}),
(synth:Instrument {name: 'Synthesizer', cost: 1337.0}),
(bongos:Instrument {name: 'Bongos', cost: 42.0}),
(trumpet:Instrument {name: 'Trumpet', cost: 1337.0}),
(dan)-[:LIKES]->(guitar),
(dan)-[:LIKES]->(synth),
(dan)-[:LIKES]->(bongos),
(annie)-[:LIKES]->(guitar),
(annie)-[:LIKES]->(synth),
(matt)-[:LIKES]->(bongos),
(brie)-[:LIKES]->(guitar),
(brie)-[:LIKES]->(synth),
(brie)-[:LIKES]->(bongos),
(john)-[:LIKES]->(trumpet)
MATCH (source:Person)-[r:LIKES]->(target:Instrument)
RETURN gds.graph.project(
'persons_with_instruments',
source,
target,
{
sourceNodeLabels: labels(source),
sourceNodeProperties: source { .age, .heightAndWeight },
targetNodeLabels: labels(target),
targetNodeProperties: target { .cost },
relationshipType: type(r),
relationshipProperties: r { .relWeight }
},
{ undirectedRelationshipTypes: ['LIKES'] }
)
现在我们可以通过指定 projectedFeatureDimension
参数,在该图上以多标签模式运行 GraphSAGE。多标签 GraphSAGE 消除了内存图中每个节点必须具有所有 featureProperties
的要求。但是,每个标签的投影是独立的,即使两个标签具有相同的 featureProperty
,在投影之前它们也被视为不同的特征。projectedFeatureDimension
应等于特征数组的最大长度。在我们的示例中,人物具有 age
(1) 和 heightAndWeight
(2),总长度为 3。乐器只有 cost
,长度为 1。因此,projectedFeatureDimension
应设置为 3。对于每个节点,其唯一的标签属性都使用标签特定的投影投射到维度为 projectedFeatureDimension
的向量空间中。请注意,cost
特征仅为乐器节点定义,而 age
和 heightAndWeight
仅为人物定义。
CALL gds.beta.graphSage.train(
'persons_with_instruments',
{
modelName: 'multiLabelModel',
featureProperties: ['age', 'heightAndWeight', 'cost'],
projectedFeatureDimension: 3
}
)
使用关系权重进行训练
GraphSAGE 实现支持使用关系权重进行训练。节点之间更大的关系权重表示这些节点应具有更相似的嵌入值。
CALL gds.beta.graphSage.train(
'persons',
{
modelName: 'weightedTrainedModel',
featureProperties: ['age', 'heightAndWeight'],
relationshipWeightProperty: 'relWeight',
nodeLabels: ['Person'],
relationshipTypes: ['KNOWS']
}
)
当图中没有节点属性时进行训练
以下示例说明了如何在 mutate
模式下调用度中心性算法,然后将变异后的属性用作 GraphSAGE 训练的特征。为了本示例的目的,我们将使用 Persons
图,但不会将任何属性加载到内存图。
MATCH (source:Person)-[r:KNOWS]->(target:Person)
RETURN gds.graph.project(
'noPropertiesGraph',
source,
target,
{},
{ undirectedRelationshipTypes: ['*'] }
)
CALL gds.degree.mutate(
'noPropertiesGraph',
{
mutateProperty: 'degree'
}
) YIELD nodePropertiesWritten
CALL gds.beta.graphSage.train(
'noPropertiesGraph',
{
modelName: 'myModel',
featureProperties: ['degree']
}
)
YIELD trainMillis
RETURN trainMillis
gds.degree.mutate
将为内存图中的每个节点创建一个新的节点属性 degree
,然后可以在 GraphSAGE.train
模式中将其用作 featureProperty
。
使用单独的算法生成特征属性对于捕获图拓扑属性也非常有用。 |
流式传输
要生成嵌入并将其流回客户端,我们可以使用流模式。我们必须首先训练一个模型,我们使用 gds.beta.graphSage.train
过程来完成此操作。
CALL gds.beta.graphSage.train(
'persons',
{
modelName: 'graphSage',
featureProperties: ['age', 'heightAndWeight'],
embeddingDimension: 3,
randomSeed: 19
}
)
一旦我们训练了一个模型(名为 'graphSage'
),我们就可以使用它来生成和流式传输嵌入。
CALL gds.beta.graphSage.stream(
'persons',
{
modelName: 'graphSage'
}
)
YIELD nodeId, embedding
RETURN gds.util.asNode(nodeId).name AS person, embedding
ORDER BY person, embedding
人员 | embedding |
---|---|
"Annie" |
[0.5285002573, 0.4682181872, 0.7081378445] |
"Brie" |
[0.5285002574, 0.4682181872, 0.7081378445] |
"Dan" |
[0.5285002573, 0.4682181872, 0.7081378445] |
"Elsa" |
[0.5285002574, 0.4682181872, 0.7081378444] |
"Jeff" |
[0.5285002573, 0.4682181872, 0.7081378445] |
"John" |
[0.5285002573, 0.4682181872, 0.7081378445] |
"Matt" |
[0.5285002573, 0.4682181872, 0.7081378445] |
由于权重变量的随机初始化,不同运行的结果可能会略有不同。 |
变异
作为流示例一部分训练的 模型 可以重复使用,以使用该过程的 mutate
模式将结果写入内存图。下面是如何实现这一点的示例。
CALL gds.beta.graphSage.mutate(
'persons',
{
mutateProperty: 'inMemoryEmbedding',
modelName: 'graphSage'
}
) YIELD
nodeCount,
nodePropertiesWritten
nodeCount | nodePropertiesWritten |
---|---|
7 |
7 |
写入
作为流示例一部分训练的 模型 可以重复使用,以将结果写入 Neo4j。下面是如何实现这一点的示例。
CALL gds.beta.graphSage.write(
'persons',
{
writeProperty: 'embedding',
modelName: 'graphSage'
}
) YIELD
nodeCount,
nodePropertiesWritten
nodeCount | nodePropertiesWritten |
---|---|
7 |
7 |