GraphSAGE
此功能处于 beta 层级。有关功能层级的更多信息,请参见 API 层级。
词汇表
- 有向
-
有向特性。该算法在有向图上定义良好。
- 有向
-
有向特性。该算法忽略图的方向。
- 有向
-
有向特性。该算法不在有向图上运行。
- 无向
-
无向特性。该算法在无向图上定义良好。
- 无向
-
无向特性。该算法忽略图的无向性。
- 异构节点
-
异构节点 完全支持。该算法能够区分不同类型的节点。
- 异构节点
-
异构节点 允许。该算法无论节点标签如何,都会以相同的方式处理所有选定的节点。
- 异构关系
-
异构关系 完全支持。该算法能够区分不同类型的关系。
- 异构关系
-
异构关系 允许。该算法无论关系类型如何,都会以相同的方式处理所有选定的关系。
- 加权关系
-
加权特性。该算法支持使用关系属性作为权重,通过 relationshipWeightProperty 配置参数指定。
- 加权关系
-
加权特性。该算法将每个关系视为同等重要,丢弃任何关系权重的值。
GraphSAGE 是一种用于计算节点嵌入的归纳算法。GraphSAGE 使用节点特征信息来生成看不见的节点或图上的节点嵌入。该算法不是为每个节点训练单个嵌入,而是学习一个函数,该函数通过对节点的局部邻域进行采样和聚合特征来生成嵌入。
该算法针对无向图定义。 |
有关此算法的更多信息,请参见
注意事项
孤立节点
如果您嵌入的图包含孤立节点,则 GraphSAGE 中的聚合步骤只能从节点本身获取信息。当该节点的所有属性均为 0.0
且激活函数为 ReLU 时,会导致该节点的所有向量均为零。但是,由于 GraphSAGE 使用 L2 范数对节点嵌入进行归一化,而零向量无法归一化,因此在这些特殊情况下,我们为这些节点分配所有零嵌入。在您为孤儿节点生成所有零嵌入的情况下,可能会对下游任务(例如最近邻或其他相似性算法)产生影响。在运行 GraphSAGE 之前,最好过滤掉这些断开连接的节点。
图预采样以减少时间和内存
由于训练 GraphSAGE 模型在大型图上可能需要大量时间和内存,因此在训练之前对较小的子图进行采样,然后在该子图上进行训练可能会有所帮助。训练后的模型仍然可以应用于预测完整图(或其他图)上的嵌入,因为 GraphSAGE 是归纳式的。要采样结构上有代表性的子图,请参见带重启的随机游走采样。
调整参数
一般来说,调整参数非常依赖于特定的数据集。
聚合器
聚合器定义了如何组合节点的嵌入和上一层中采样的邻居嵌入。GDS 支持 Mean
和 Pool
聚合器。
Mean
更简单,需要更少的内存,计算速度更快。Pool
更复杂,可以编码更丰富的邻域。
样本大小
每个样本大小代表一个隐藏层,其输出大小等于嵌入维度。该层使用给定的聚合器和激活函数。更多层会导致为节点的嵌入考虑更多距离更远的邻居。第 N
层使用第 N -1
层中距离 <\= N
的采样邻居嵌入。层数越多,内存和计算时间就越高。
样本大小 n
表示我们尝试从节点中最多采样 n
个邻居。较高的样本大小也需要更多的内存和计算时间。
批次大小
此参数定义了在单个批次中分组的训练示例数量。对于每个训练示例,我们还将采样一个正例和一个负例。使用 concurrency
个线程同时计算批次上的梯度。
批次大小不会影响模型质量,但可以用于调整训练速度。较大的批次大小会增加计算的内存消耗。
时期
设置此参数对于限制模型的训练时间可能会有用。限制计算预算可以起到正则化的作用,并减轻过度拟合的风险,过度拟合在大量时期后会成为风险。
由于每个时期都会重新采样邻居,因此多个时期可以避免对特定邻域进行过度拟合。
批次采样率
此参数定义了为单个迭代采样的批次数量。
采样的批次越多,梯度计算就越准确。但是,更多的批次也会增加每次迭代的运行时间。
一般来说,建议确保至少使用与定义的 concurrency
相同数量的批次。
语法
CALL gds.beta.graphSage.train(
graphName: String,
configuration: Map
) YIELD
modelInfo: Map,
configuration: Map,
trainMillis: Integer
名称 | 类型 | 默认 | 可选 | 描述 |
---|---|---|---|---|
graphName |
字符串 |
|
否 |
存储在目录中的图的名称。 |
configuration |
映射 |
|
是 |
针对算法特性的配置和/或图过滤。 |
名称 | 类型 | 默认 | 可选 | 描述 |
---|---|---|---|---|
modelName |
字符串 |
|
否 |
要训练的模型的名称,在模型目录中不存在。 |
featureProperties |
字符串列表 |
|
否 |
应作为输入特征使用的节点属性的名称。所有属性名称都必须存在于投影图中,并且类型为浮点数或浮点数列表。 |
字符串列表 |
|
是 |
使用给定的节点标签过滤命名图。包含具有任何给定标签的节点。 |
|
字符串列表 |
|
是 |
使用给定的关系类型过滤命名图。包含具有任何给定类型的关系。 |
|
整数 |
|
是 |
用于运行算法的并发线程数。 |
|
字符串 |
|
是 |
一个 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] |
由于权重变量的随机初始化,结果可能在不同的运行之间有所不同。 |
从结果中我们可以得出以下结论,训练在单个 epoch 后收敛,损失几乎相同。调整算法参数,例如尝试不同的 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
。
使用单独的算法生成 featureProperties 对于捕获图拓扑属性也非常有用。 |
流
要生成嵌入并将其流回客户端,我们可以使用流模式。我们必须首先训练一个模型,这可以通过 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
person | 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 |