快速随机投影 (Fast Random Projection)

术语表

有向

有向图特性。该算法在有向图上定义良好。

有向

有向图特性。该算法忽略图的方向。

有向

有向图特性。该算法不适用于有向图。

无向

无向图特性。该算法在无向图上定义良好。

无向

无向图特性。该算法忽略图的无向性。

异构节点

异构节点完全支持。该算法具有区分不同类型节点的能力。

异构节点

异构节点允许使用。无论标签如何,该算法对所有选定节点的处理方式相同。

异构关系

异构关系完全支持。该算法具有区分不同类型关系的能力。

异构关系

异构关系允许使用。无论类型如何,该算法对所有选定关系的处理方式相同。

加权关系

加权特性。该算法支持将关系属性用作权重,通过 relationshipWeightProperty 配置参数指定。

加权关系

加权特性。该算法将每种关系视为同等重要,丢弃任何关系权重值。

节点属性

节点属性特性。该算法利用节点属性。

 

FastRP 包含在端到端示例 Jupyter Notebook 中

简介

快速随机投影(Fast Random Projection,简称 FastRP)是一种属于随机投影算法家族的节点嵌入算法。这些算法在理论上由 Johnson-Lindenstrauss 引理支撑,该引理指出,人们可以将 n任意 维度的向量投影到 O(log(n)) 维度,同时仍能近似保留点之间的成对距离。事实上,以随机方式选择的线性投影即可满足此属性。

因此,此类技术允许在保留大部分距离信息的同时进行激进的降维。FastRP 算法作用于图,在这种情况下,我们关注的是保留节点与其邻居之间的相似性。这意味着具有相似邻域的两个节点应被分配相似的嵌入向量。相反,不相似的两个节点不应被分配相似的嵌入向量。

GDS 实现的 FastRP 在多个方面扩展了原始算法[1]

FastRP 算法最初使用一种称为 极稀疏随机投影 的技术为所有节点分配随机向量[2]。从随机向量(节点投影)开始,并对节点邻域进行迭代平均,算法为每个节点 n 构建一个 中间嵌入 序列 e n to the ith。更准确地说,

e n to the ith equals average of e m to the ith minus one

其中 m 遍历 n 的邻居,e n to the zeroeth 是节点的初始随机向量。

节点 n 的嵌入 e n(即算法的输出)是上述向量和嵌入的组合:

e n equals w zero times normalise r n plus sum from i equals 1 to k of w i times normalise e n to the ith

其中 normalize 是将向量除以其 L2 范数 的函数,nodeSelfInfluence 的值是 w zeroiterationWeights 的值是 w 1 comma w 2 comma dot dot dot w k。我们稍后将回到 节点自我影响 部分。

因此,每个节点的嵌入取决于半径等于迭代次数的邻域。通过这种方式,FastRP 在利用图的高阶关系的同时,仍保持高度的可扩展性。

节点属性

大多数现实世界的图都包含存储有关节点及其代表含义的信息的节点属性。GDS 库中的 FastRP 算法通过考虑节点属性的能力扩展了原始的 FastRP 算法。因此,所得的嵌入可以更准确地表示图。

算法的节点属性感知方面通过参数 featurePropertiespropertyRatio 进行配置。featureProperties 中的每个节点属性都与一个维度为 propertyDimension 的随机生成向量相关联,其中 propertyDimension = embeddingDimension * propertyRatio。然后,每个节点初始化为一个大小为 embeddingDimension 的向量,由两部分拼接而成:

  1. 第一部分如同标准 FastRP 算法那样形成,

  2. 第二部分是属性向量的线性组合,使用节点的属性值作为权重。

然后,算法执行与 FastRP 算法相同的逻辑。因此,算法将输出大小为 embeddingDimension 的数组。嵌入中最后 propertyDimension 个坐标捕获了附近节点的属性值信息(下文称为“属性部分”),剩余的坐标(共 embeddingDimension - propertyDimension 个;“拓扑部分”)捕获了附近节点存在的信息。

[0, 1, ...        | ...,   N - 1, N]
 ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
  topology part   |  property part
                  ^
           property ratio

在机器学习流水线中的使用

将 FastRP 生成的节点嵌入作为机器学习管道(如 链接预测管道节点属性预测)中的一个节点属性步骤可能很有用。

为了使机器学习模型能够做出有用的预测,预测期间产生的特征分布必须与模型训练期间产生的特征分布相似,这一点很重要。此外,添加到管道中的节点属性步骤(无论是否为 FastRP)在训练期间以及训练模型进行预测期间都会执行。因此,如果管道包含一个在训练和预测期间产生过于迥异的嵌入的嵌入步骤,就会产生问题。

这对如何将 FastRP 作为节点属性步骤使用有一些影响。通常,如果管道在某个图“g”上使用 FastRP 作为节点属性步骤进行训练,那么所得的训练模型应仅应用于与“g”不太不相似的图。

如果 propertyRatio < 1.0,则进行预测的图中的大多数节点必须与训练期间使用的原始图“g”中的节点(在数据库意义上)相同。这是因为 FastRP 是一种随机算法,在这种情况下,它是根据节点来源的 Neo4j 数据库中的节点 ID 进行播种(seeding)的。

但是,如果 propertyRatio = 1.0,则随机初始节点嵌入仅源自节点属性向量,因此不存在基于节点 ID 的随机播种。

此外,为了使初始随机向量(与使用的 propertyRatio 无关)在多次运行(训练和预测调用)之间保持一致,在将 FastRP 节点属性步骤添加到训练管道时,必须提供 randomSeed 配置参数的值。

总之,如果您设置了随机种子并将 propertyRatio 设置为 1,则 FastRP 是 归纳式 (inductive) 的,因为嵌入仅基于确定性地投影到向量中的节点属性。

调优算法参数

为了改善图上 FastRP 的嵌入质量,可以调优算法参数。这个为您的特定用例和图寻找最佳参数的过程通常称为 超参数调优。我们将逐一介绍配置参数,并解释它们的行为方式。

为了获得统计上稳健的结果,最好保留一个排除在参数调优之外的测试集。选择一组参数值后,可以使用下游机器学习任务在测试集上评估嵌入质量。通过改变参数值并研究机器学习任务的精度,可以推导出最适合具体数据集和用例的参数值。为了构建这样的集合,您可能需要使用图中的专用节点标签来表示不包含测试数据的子图。

嵌入维度

嵌入维度是所生成向量的长度。维度越大,精度越高,但操作成本也越高。

最佳嵌入维度取决于图中的节点数。由于嵌入能够编码的信息量受其维度限制,较大的图往往需要较大的嵌入维度。典型值是 128 - 1024 范围内的 2 的幂。在 105 个节点数量级的图上,至少 256 的值可获得良好的结果,但通常增加维度会改善结果。然而,增加嵌入维度会线性增加内存需求和运行时间。

归一化强度

归一化强度用于控制节点度数如何影响嵌入。使用负值会降低高阶邻居的重要性,而正值则会增加它们的重要性。最佳归一化强度取决于图以及将使用嵌入的任务。在原始论文中,超参数调优是在 [-1,0] 范围内进行的(没有正值),但我们发现有些情况下正归一化强度会产生更好的结果。

迭代权重

迭代权重参数控制两个方面:迭代次数以及它们对最终节点嵌入的相对影响。该参数是一个数字列表,列表中的每个数字代表一次迭代,数字本身即为应用于该次迭代的权重。

在每次迭代中,算法将跨越图中的所有关系进行扩展。这产生了一些含义:

  • 单次迭代时,每个节点嵌入只考虑直接邻居。

  • 两次迭代时,每个节点嵌入考虑直接邻居和二阶邻居。

  • 三次迭代时,每个节点嵌入考虑直接邻居、二阶邻居和三阶邻居。直接邻居可能会在不同迭代中被访问两次。

  • 通常,对应于第 i 次迭代的嵌入包含取决于长度为 i 的路径可达节点的特征。如果图是无向的,则长度为 L 的路径可达的节点也可以通过 L+2k 的长度到达(对于任何整数 k)。

  • 特别是,节点可能会在每次偶数次迭代时回到自身(取决于图中的方向)。

最好在偶数位置和奇数位置至少有一个非零权重。通常建议至少进行几次迭代,例如三次。但是,值过高会考虑远处的节点,这可能没有信息价值,甚至可能产生负面影响。这里的直觉是,随着投影距离节点越远,邻域的特定性就越低。当然,迭代次数越多,完成所需的时间也越长。

节点自我影响

节点自我影响是原始 FastRP 算法的一种变体。

节点嵌入受第 i 次迭代中中间嵌入影响的程度由 iterationWeights 的第 i 个元素控制。这也可以看作是:从节点出发 i 跳(hops)可达的节点的初始随机向量(或投影)对该节点嵌入的影响程度。类似地,nodeSelfInfluence 的行为就像第 0 次迭代的迭代权重,或者节点自身的投影对同一节点嵌入的影响量。

将此参数设置为非零值的一个原因是您的图具有低连通性或存在大量孤立节点。孤立节点结合使用 propertyRatio = 0.0 会导致嵌入全为零。然而,将节点属性与节点自我影响一起使用可以为这些节点产生更有意义的嵌入。这可以被视为在图结构(局部)缺失时产生回退特征。此外,有时节点自身的属性就是有用的特征,即使连通性很高,包含它们也是好的。最后,节点自我影响可用于纯降维,以压缩用于节点分类的节点属性。

如果不使用节点属性,使用 nodeSelfInfluence 也可能会产生积极影响,具体取决于其他设置和问题。

方向

创建图时选择正确的方向可能是影响最大的因素。FastRP 算法旨在与无向图一起工作,我们预计这在大多数情况下都是最佳选择。如果您认为只有传出或传入关系对于预测任务有意义,那么您可能希望分别尝试使用 NATURALREVERSE 方向。

加权图

默认情况下,算法将图关系视为无权重的。您可以使用 relationshipWeightProperty 参数指定关系权重,以指示算法计算相邻嵌入的加权平均值。

语法

本节介绍了在 FastRP 算法的每种执行模式下使用的语法。我们描述的是命名图变体的语法。要了解有关通用语法变体的更多信息,请参阅 语法概述

按模式划分的 FastRP 语法
在命名图上以流模式运行 FastRP。
CALL gds.fastRP.stream(
  graphName: String,
  configuration: Map
) YIELD
  nodeId: Integer,
  embedding: List of Float
表 1. 参数
名称 类型 默认 可选 描述

graphName

字符串

不适用

存储在目录中的图的名称。

配置

Map

{}

算法特定配置和/或图过滤配置。

表 2. 配置
名称 类型 默认 可选 描述

nodeLabels

字符串列表

['*']

使用给定的节点标签过滤命名图。具有任何给定标签的节点都将被包含。

relationshipTypes

字符串列表

['*']

使用给定的关系类型过滤命名图。具有任何给定类型的关系都将被包含。

concurrency

整数

4 [1]

用于运行算法的并发线程数。

jobId

字符串

内部生成

可以提供一个 ID 以更轻松地跟踪算法的进度。

logProgress

布尔值

true

如果禁用,进度百分比将不会被记录。

propertyRatio

浮点数

0.0

属性嵌入维度占总 embeddingDimension 的期望比率。正值要求 featureProperties 非空。

featureProperties

字符串列表

[]

应作为输入特征使用的节点属性名称。所有属性名称必须存在于投影图中,且类型为 Float 或 List of Float。

embeddingDimension

整数

不适用

计算出的节点嵌入的维度。最小值为 1。

iterationWeights

浮点数列表

[0.0, 1.0, 1.0]

包含每次迭代的权重。权重控制来自该迭代的中间嵌入对最终嵌入的贡献程度。

nodeSelfInfluence

浮点数

0.0

控制每个节点的初始随机向量对其最终嵌入的贡献程度。

normalizationStrength

浮点数

0.0

每个节点的初始随机向量按其度数的 normalizationStrength 次方进行缩放。

randomSeed

整数

不适用

用于计算嵌入中所有随机性的随机种子。

relationshipWeightProperty

字符串

null

用于加权随机投影的关系属性名称。如果未指定,算法将以无权重方式运行。

迭代次数等于 iterationWeights 的长度。

要求 iterationWeights 非空或 nodeSelfInfluence 非零。

1. 在 GDS 会话 中,默认值为可用处理器数量。

表 3. 结果
名称 类型 描述

nodeId

整数

节点 ID。

embedding

浮点数列表

FastRP 节点嵌入。

在命名图上以统计模式运行 FastRP。
CALL gds.fastRP.stats(
  graphName: String,
  configuration: Map
) YIELD
  nodeCount: Integer,
  preProcessingMillis: Integer,
  computeMillis: Integer,
  configuration: Map
表 4. 参数
名称 类型 默认 可选 描述

graphName

字符串

不适用

存储在目录中的图的名称。

配置

Map

{}

算法特定配置和/或图过滤配置。

表 5. 配置
名称 类型 默认 可选 描述

nodeLabels

字符串列表

['*']

使用给定的节点标签过滤命名图。具有任何给定标签的节点都将被包含。

relationshipTypes

字符串列表

['*']

使用给定的关系类型过滤命名图。具有任何给定类型的关系都将被包含。

concurrency

整数

4 [2]

用于运行算法的并发线程数。

jobId

字符串

内部生成

可以提供一个 ID 以更轻松地跟踪算法的进度。

logProgress

布尔值

true

如果禁用,进度百分比将不会被记录。

propertyRatio

浮点数

0.0

属性嵌入维度占总 embeddingDimension 的期望比率。正值要求 featureProperties 非空。

featureProperties

字符串列表

[]

应作为输入特征使用的节点属性名称。所有属性名称必须存在于投影图中,且类型为 Float 或 List of Float。

embeddingDimension

整数

不适用

计算出的节点嵌入的维度。最小值为 1。

iterationWeights

浮点数列表

[0.0, 1.0, 1.0]

包含每次迭代的权重。权重控制来自该迭代的中间嵌入对最终嵌入的贡献程度。

nodeSelfInfluence

浮点数

0.0

控制每个节点的初始随机向量对其最终嵌入的贡献程度。

normalizationStrength

浮点数

0.0

每个节点的初始随机向量按其度数的 normalizationStrength 次方进行缩放。

randomSeed

整数

不适用

用于计算嵌入中所有随机性的随机种子。

relationshipWeightProperty

字符串

null

用于加权随机投影的关系属性名称。如果未指定,算法将以无权重方式运行。

迭代次数等于 iterationWeights 的长度。

要求 iterationWeights 非空或 nodeSelfInfluence 非零。

2. 在 GDS 会话 中,默认值为可用处理器数量。

表 6. 结果
名称 类型 描述

nodeCount

整数

处理的节点数。

preProcessingMillis

整数

预处理图的毫秒数。

computeMillis

整数

运行算法的毫秒数。

配置

Map

运行算法所使用的配置。

在命名图上以变更模式运行 FastRP。
CALL gds.fastRP.mutate(
  graphName: String,
  configuration: Map
) YIELD
  nodeCount: Integer,
  nodePropertiesWritten: Integer,
  preProcessingMillis: Integer,
  computeMillis: Integer,
  mutateMillis: Integer,
  configuration: Map
表 7. 参数
名称 类型 默认 可选 描述

graphName

字符串

不适用

存储在目录中的图的名称。

配置

Map

{}

算法特定配置和/或图过滤配置。

表 8. 配置
名称 类型 默认 可选 描述

mutateProperty

字符串

不适用

GDS 图中写入嵌入的节点属性。

nodeLabels

字符串列表

['*']

使用给定的节点标签过滤命名图。

relationshipTypes

字符串列表

['*']

使用给定的关系类型过滤命名图。

concurrency

整数

4

用于运行算法的并发线程数。

logProgress

布尔值

true

如果禁用,进度百分比将不会被记录。

jobId

字符串

内部生成

可以提供一个 ID 以更轻松地跟踪算法的进度。

propertyRatio

浮点数

0.0

属性嵌入维度占总 embeddingDimension 的期望比率。正值要求 featureProperties 非空。

featureProperties

字符串列表

[]

应作为输入特征使用的节点属性名称。所有属性名称必须存在于投影图中,且类型为 Float 或 List of Float。

embeddingDimension

整数

不适用

计算出的节点嵌入的维度。最小值为 1。

iterationWeights

浮点数列表

[0.0, 1.0, 1.0]

包含每次迭代的权重。权重控制来自该迭代的中间嵌入对最终嵌入的贡献程度。

nodeSelfInfluence

浮点数

0.0

控制每个节点的初始随机向量对其最终嵌入的贡献程度。

normalizationStrength

浮点数

0.0

每个节点的初始随机向量按其度数的 normalizationStrength 次方进行缩放。

randomSeed

整数

不适用

用于计算嵌入中所有随机性的随机种子。

relationshipWeightProperty

字符串

null

用于加权随机投影的关系属性名称。如果未指定,算法将以无权重方式运行。

迭代次数等于 iterationWeights 的长度。

要求 iterationWeights 非空或 nodeSelfInfluence 非零。

表 9. 结果
名称 类型 描述

nodeCount

整数

处理的节点数。

nodePropertiesWritten

整数

已写入的节点属性数量。

preProcessingMillis

整数

预处理图的毫秒数。

computeMillis

整数

运行算法的毫秒数。

mutateMillis

整数

向内存中图添加属性的毫秒数。

配置

Map

运行算法所使用的配置。

在命名图上以写入模式运行 FastRP。
CALL gds.fastRP.write(
  graphName: String,
  configuration: Map
) YIELD
  nodeCount: Integer,
  nodePropertiesWritten: Integer,
  preProcessingMillis: Integer,
  computeMillis: Integer,
  writeMillis: Integer,
  configuration: Map
表 10. 参数
名称 类型 默认 可选 描述

graphName

字符串

不适用

存储在目录中的图的名称。

配置

Map

{}

算法特定配置和/或图过滤配置。

表 11. 配置
名称 类型 默认 可选 描述

nodeLabels

字符串列表

['*']

使用给定的节点标签过滤命名图。具有任何给定标签的节点都将被包含。

relationshipTypes

字符串列表

['*']

使用给定的关系类型过滤命名图。具有任何给定类型的关系都将被包含。

concurrency

整数

4 [3]

用于运行算法的并发线程数。

jobId

字符串

内部生成

可以提供一个 ID 以更轻松地跟踪算法的进度。

logProgress

布尔值

true

如果禁用,进度百分比将不会被记录。

writeConcurrency

整数

'concurrency' 的值

用于将结果写入 Neo4j 的并发线程数。

writeProperty

字符串

不适用

Neo4j 数据库中写入嵌入的节点属性。

propertyRatio

浮点数

0.0

属性嵌入维度占总 embeddingDimension 的期望比率。正值要求 featureProperties 非空。

featureProperties

字符串列表

[]

应作为输入特征使用的节点属性名称。所有属性名称必须存在于投影图中,且类型为 Float 或 List of Float。

embeddingDimension

整数

不适用

计算出的节点嵌入的维度。最小值为 1。

iterationWeights

浮点数列表

[0.0, 1.0, 1.0]

包含每次迭代的权重。权重控制来自该迭代的中间嵌入对最终嵌入的贡献程度。

nodeSelfInfluence

浮点数

0.0

控制每个节点的初始随机向量对其最终嵌入的贡献程度。

normalizationStrength

浮点数

0.0

每个节点的初始随机向量按其度数的 normalizationStrength 次方进行缩放。

randomSeed

整数

不适用

用于计算嵌入中所有随机性的随机种子。

relationshipWeightProperty

字符串

null

用于加权随机投影的关系属性名称。如果未指定,算法将以无权重方式运行。

迭代次数等于 iterationWeights 的长度。

要求 iterationWeights 非空或 nodeSelfInfluence 非零。

3. 在 GDS 会话 中,默认值为可用处理器数量。

表 12. 结果
名称 类型 描述

nodeCount

整数

处理的节点数。

nodePropertiesWritten

整数

已写入的节点属性数量。

preProcessingMillis

整数

预处理图的毫秒数。

computeMillis

整数

运行算法的毫秒数。

writeMillis

整数

将结果数据写回 Neo4j 的毫秒数。

配置

Map

运行算法所使用的配置。

示例

以下所有示例应在空数据库中运行。

这些示例均使用 Cypher 投影 作为标准。原生投影将在未来版本中被弃用。

在本节中,我们将展示在具体图上运行 FastRP 节点嵌入算法的示例。目的是说明结果的样子,并为如何在实际环境中使用该算法提供指南。我们将在一个小型社交网络图上进行演示,该图有少数以特定模式连接的节点。示例图如下所示:

Visualization of the example graph
以下 Cypher 语句将在 Neo4j 数据库中创建示例图:
CREATE
  (dan:Person {name: 'Dan', age: 18}),
  (annie:Person {name: 'Annie', age: 12}),
  (matt:Person {name: 'Matt', age: 22}),
  (jeff:Person {name: 'Jeff', age: 51}),
  (brie:Person {name: 'Brie', age: 45}),
  (elsa:Person {name: 'Elsa', age: 65}),
  (john:Person {name: 'John', age: 64}),

  (dan)-[:KNOWS {weight: 1.0}]->(annie),
  (dan)-[:KNOWS {weight: 1.0}]->(matt),
  (annie)-[:KNOWS {weight: 1.0}]->(matt),
  (annie)-[:KNOWS {weight: 1.0}]->(jeff),
  (annie)-[:KNOWS {weight: 1.0}]->(brie),
  (matt)-[:KNOWS {weight: 3.5}]->(brie),
  (brie)-[:KNOWS {weight: 1.0}]->(elsa),
  (brie)-[:KNOWS {weight: 2.0}]->(jeff),
  (john)-[:KNOWS {weight: 1.0}]->(jeff);

此图代表七个彼此认识的人。关系属性 weight 表示两人之间认识程度的强弱。

在 Neo4j 中拥有该图后,我们现在可以将其投影到图目录中,为算法执行做准备。我们使用针对 Person 节点和 KNOWS 关系的 Cypher 投影来完成此操作。对于关系,我们将使用 UNDIRECTED 方向。这是因为据测量,FastRP 算法在无向图中计算出的节点嵌入更具预测性。我们还将添加 weight 关系属性,我们将在运行加权版 FastRP 时使用它。

以下语句将使用 Cypher 投影投影一个图,并将其以名称 'persons' 存储在图目录中。
MATCH (source:Person)-[r:KNOWS]->(target:Person)
RETURN gds.graph.project(
  'persons',
  source,
  target,
  {
    sourceNodeProperties: source { .age },
    targetNodeProperties: target { .age },
    relationshipProperties: r { .weight }
  },
  { undirectedRelationshipTypes: ['*'] }
)

内存估算

首先,我们将使用 estimate 过程来估算运行算法的成本。这可以在任何执行模式下完成。在本示例中,我们将使用 stream 模式。估算算法有助于了解在您的图上运行该算法会产生什么样的内存影响。当您稍后在某个执行模式下实际运行算法时,系统会执行一次估算。如果估算结果显示执行超出内存限制的可能性极高,则禁止执行。要了解更多信息,请参阅 自动估算和执行阻塞

有关 estimate 的更多详细信息,请参阅 内存估算

以下内容将估算运行算法所需的内存
CALL gds.fastRP.stream.estimate('persons', {embeddingDimension: 128})
YIELD nodeCount, relationshipCount, bytesMin, bytesMax, requiredMemory
表 13. 结果
nodeCount relationshipCount bytesMin bytesMax requiredMemory

7

18

11392

11392

"11392 字节"

流 (Stream)

stream 执行模式下,算法返回每个节点的嵌入。这使我们可以直接检查结果或在 Cypher 中对其进行后处理,而不会产生任何副作用。例如,我们可以收集结果并将它们传递给相似度算法。

有关 stream 模式的更多详细信息,请参阅 流模式

以下内容将运行算法并流式传输结果
CALL gds.fastRP.stream('persons',
  {
    embeddingDimension: 4,
    randomSeed: 42
  }
)
YIELD nodeId, embedding
RETURN gds.util.asNode(nodeId).name as person, embedding
ORDER BY person
表 14. 结果
person embedding

"Annie"

[0.51714468, -0.4148067832, -0.5454565287, -1.741045475]

"Brie"

[0.4184039235, -0.4415202737, 0.2315290272, -1.5677155256]

"Dan"

[0.2612129152, -0.6138446331, -0.369674772, -1.7762401104]

"Elsa"

[0.5556756258, -0.3558300138, 0.308482945, -1.5653611422]

"Jeff"

[0.6856793165, -0.3247893453, -0.3811529875, -1.5765502453]

"John"

[1.0, -0.0890870914, -0.4454354346, -0.8908708692]

"Matt"

[0.2737978995, -0.4965225756, -0.3031099439, -1.8122189045]

算法的结果不是非常直观,因为节点嵌入格式是节点在其邻域内的数学抽象,旨在用于机器学习程序。我们可以看到嵌入有四个元素(按照 embeddingDimension 配置),并且数字相对较小(它们都在 [-2, 2] 范围内)。数字的大小由 embeddingDimension、图中的节点数以及 FastRP 对中间嵌入向量执行欧几里得归一化的事实控制。

由于算法的随机性质,结果会在运行之间有所变化。然而,这并不一定意味着两个节点嵌入的成对距离会发生很大变化。

统计 (Stats)

stats 执行模式下,算法返回单行数据,其中包含算法结果的摘要。此执行模式没有任何副作用。它对于通过检查 computeMillis 返回项来评估算法性能非常有用。在下面的示例中,我们将省略返回时间。过程的完整签名可以在 语法部分 中找到。

有关 stats 模式的更多详细信息,请参阅 统计

以下命令将运行算法并以统计和测量值的形式返回结果
CALL gds.fastRP.stats('persons', { embeddingDimension: 8 })
YIELD nodeCount
表 15. 结果
nodeCount

7

stats 模式目前不提供嵌入本身的统计结果。然而,我们可以看到算法已经成功处理了我们示例图中的所有七个节点。

变更 (Mutate)

mutate 执行模式扩展了 stats 模式,具有一个重要的副作用:使用包含该节点嵌入的新节点属性更新命名图。新属性的名称使用强制配置参数 mutateProperty 指定。结果是类似于 stats 的单行摘要,但带有额外指标。当多种算法结合使用时,mutate 模式特别有用。

有关 mutate 模式的更多详细信息,请参阅 修改模式

以下语句将以 mutate 模式运行该算法:
CALL gds.fastRP.mutate(
  'persons',
  {
    embeddingDimension: 8,
    mutateProperty: 'fastrp-embedding'
  }
)
YIELD nodePropertiesWritten
表 16. 结果
nodePropertiesWritten

7

返回的结果类似于 stats 示例。此外,图 'persons' 现在拥有一个节点属性 fastrp-embedding,它存储了每个节点的节点嵌入。要了解如何检查内存中图的新模式,请参阅 列出图

写入 (Write)

write 执行模式扩展了 stats 模式,具有一个重要的副作用:将每个节点的嵌入作为属性写入 Neo4j 数据库。新属性的名称使用强制配置参数 writeProperty 指定。结果是类似于 stats 的单行摘要,但带有额外指标。write 模式支持直接将结果持久化到数据库。

有关 write 模式的更多详细信息,请参阅 写入模式

以下语句将以 write 模式运行该算法:
CALL gds.fastRP.write(
  'persons',
  {
    embeddingDimension: 8,
    writeProperty: 'fastrp-embedding'
  }
)
YIELD nodePropertiesWritten
表 17. 结果
nodePropertiesWritten

7

返回的结果类似于 stats 示例。此外,这七个节点中的每一个现在在 Neo4j 数据库中都有一个新属性 fastrp-embedding,包含该节点的节点嵌入。

加权 (Weighted)

下面是运行加权版算法的示例。

以下内容将运行算法并流式传输结果
CALL gds.fastRP.stream(
  'persons',
  {
    embeddingDimension: 4,
    randomSeed: 42,
    relationshipWeightProperty: 'weight'
  }
)
YIELD nodeId, embedding
RETURN gds.util.asNode(nodeId).name as person, embedding
ORDER BY person
表 18. 结果
person embedding

"Annie"

[0.034561187, -0.2929389477, 0.0952546224, -1.9652962685]

"Brie"

[0.1023679227, -0.2991863489, 0.5466092229, -1.2881529331]

"Dan"

[-0.0909044892, -0.4465829134, 0.3275954127, -1.6877939701]

"Elsa"

[0.0776494294, -0.2621908784, 0.5610812902, -1.2880744934]

"Jeff"

[0.1686269641, -0.2775687575, 0.4166130424, -1.3728146553]

"John"

[0.5247224569, -0.045596078, 0.3423381448, -0.9119215012]

"Matt"

[0.0523263216, -0.3151839674, 0.4781413078, -1.4239065647]

由于算法的初始状态是随机的,因此无法直观地分析关系权重的影响。

使用节点属性作为特征

为了解释使用节点属性的新颖初始化,让我们考虑一个示例,其中 embeddingDimension 为 10,propertyRatio 为 0.2。因此,嵌入属性的维度 propertyDimension 为 2。假设我们有一个标量类型的属性 f1,以及一个存储长度为 2 的数组的属性 f2。这意味着有 3 个特征,我们将它们排序为 f1 后跟 f2 的两个值。对于这三个特征中的每一个,我们采样一个二维随机向量。假设这些向量为 p1=[0.0, 2.4]p2=[-2.4, 0.0]p3=[2.4, 0.0]。现在考虑节点 (n {f1: 0.5, f2: [1.0, -1.0]})。上述线性组合具体为 0.5 * p1 + 1.0 * p2 - 1.0 * p3 = [-4.8, 1.2]。节点 n 的初始随机向量包含前 8 个按原始 FastRP 论文中采样的值,然后是我们计算出的值 -4.81.2,总计 10 个条目。

在下面的示例中,我们再次将嵌入维度设置为 2,但将 propertyRatio 设置为 1,这意味着嵌入仅根据节点属性计算。

以下将运行带有特征属性的 FastRP:
CALL gds.fastRP.stream('persons', {
    randomSeed: 42,
    embeddingDimension: 2,
    propertyRatio: 1.0,
    featureProperties: ['age'],
    iterationWeights: [1.0]
}) YIELD nodeId, embedding
RETURN gds.util.asNode(nodeId).name AS person, embedding
ORDER BY person
表 19. 结果
person embedding

"Annie"

[0.0, -1.0]

"Brie"

[0.0, -0.9999999403953552]

"Dan"

[0.0, -1.0]

"Elsa"

[0.0, -1.0]

"Jeff"

[0.0, -1.0]

"John"

[0.0, -1.0]

"Matt"

[0.0, -0.9999999403953552]

在此示例中,嵌入基于 age 属性。由于对每次迭代(此处仅一次迭代)应用了 L2 归一化,因此所有节点尽管具有不同的年龄值,但仍具有相同的嵌入(舍入误差除外)。


1. Chen, Haochen, Syed Fahad Sultan, Yingtao Tian, Muhao Chen, and Steven Skiena. "Fast and Accurate Network Embeddings via Very Sparse Random Projection." arXiv preprint arXiv:1908.11512 (2019).
2. Achlioptas, Dimitris. "Database-friendly random projections: Johnson-Lindenstrauss with binary coins." Journal of computer and System Sciences 66, no. 4 (2003): 671-687.