训练管道

训练模式 gds.beta.pipeline.linkPrediction.train 负责分割数据、特征提取、模型选择、训练和存储模型以供将来使用。运行此模式会导致类型为 LinkPrediction 的预测模型存储在 模型目录 中,以及训练期间收集的指标。该模型可以 应用 于可能不同的图,从而产生一种预测链接的关系类型,每种链接都具有存储为属性的预测概率。

Visualization of Link Prediction pipeline data flow

更准确地说,该过程将按顺序执行以下步骤

  1. 使用 sourceNodeLabeltargetNodeLabel 应用节点过滤,使用 targetRelationshipType 应用关系过滤。生成的图用作分割的输入。

  2. 根据 配置关系分割 中所述,创建图的关系分割为 testtrainfeature-input 图。这些图是内部管理的,仅在训练期间存在。

  3. 应用节点属性步骤,根据 添加节点属性 添加。每个步骤的图过滤器包括 contextNodeLabels + targetNodeLabel + sourceNodeLabelcontextRelationships + feature-input relationships

  4. 根据 添加链接特征 添加的特征步骤应用于 train 图,这会为每个 train 关系生成一个实例,即特征向量和二进制标签。

  5. 使用分层 k 折交叉验证分割训练实例。可以使用 gds.beta.pipeline.linkPrediction.configureSplit 中的 validationFolds 配置折叠次数 k

  6. 为每个折叠训练由 参数空间 给定的每个模型候选,并在相应的验证集上评估模型。评估使用指定的 指标

  7. 将跨折叠具有最高平均指标的模型声明为获胜者。

  8. 在整个训练集上重新训练获胜模型,并在 traintest 集上对其进行评估。为了在 test 集上进行评估,首先将特征管道应用于 train 集。

  9. 模型目录 中注册获胜模型。

以上步骤描述了该过程的逻辑操作。实现中的实际步骤及其顺序可能有所不同。
一个步骤只能使用输入图中已存在的节点属性或由该步骤之前添加的步骤生成的节点属性。
不支持在同一图上并行执行相同的管道。

语法

在命名图上以训练模式运行链接预测
CALL gds.beta.pipeline.linkPrediction.train(
  graphName: String,
  configuration: Map
) YIELD
  trainMillis: Integer,
  modelInfo: Map,
  modelSelectionStats: Map,
  configuration: Map
表 1. 参数
名称 类型 默认值 可选 描述

graphName

字符串

n/a

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

配置

映射

{}

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

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

modelName

字符串

n/a

要训练的模型的名称,在模型目录中必须不存在。

pipeline

字符串

n/a

要执行的管道的名称。

targetRelationshipType

字符串

n/a

用于训练模型的关系类型的名称。关系类型必须是无向的。

sourceNodeLabel

字符串

'*'

节点标签的名称,训练和测试集中的关系应从 [1] 开始。

targetNodeLabel

字符串

'*'

节点标签的名称,训练和测试集中的关系应以 [1] 结束。

negativeClassWeight

浮点数

1.0

模型评估中负样本的权重。正样本的权重为 1。更多详情请 点击这里

metrics

字符串列表

[AUCPR]

指标 用于评估模型。

randomSeed

整数

n/a

训练过程中使用的随机数生成器的种子。

concurrency

整数

4

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

jobId

字符串

内部生成

一个 ID,可以提供用于更轻松地跟踪训练的进度。

storeModelToDisk

布尔值

false

训练后自动将模型存储到磁盘。

1. 这有助于训练模型预测具有特定标签组合的链接。

表 3. 结果
名称 类型 描述

trainMillis

整数

用于训练的毫秒数。

modelInfo

映射

关于训练和最佳模型的信息。

modelSelectionStats

映射

关于所有模型候选者的评估指标的统计信息。

配置

映射

用于训练过程的配置。

modelInfo 也可以稍后使用 模型列表过程 检索。modelInfo 返回字段具有以下特定于算法的子字段

表 4. modelSelectionStats 的字段
名称 类型 描述

bestParameters

映射

在验证折上根据主要指标平均表现最佳的模型参数。

modelCandidates

列表

映射列表,其中每个映射包含有关一个模型候选者的信息。此信息包括候选者的参数、训练统计信息和验证统计信息。

bestTrial

整数

产生最佳模型的试验。第一个试验的编号为 1。

表 5. modelInfo 的字段
名称 类型 描述

modelName

字符串

已训练模型的名称。

modelType

字符串

已训练模型的类型。

bestParameters

映射

在验证折上根据主要指标平均表现最佳的模型参数。

metrics

映射

从指标描述到针对数据子集评估的最佳模型的指标的映射,请参见下文。

nodePropertySteps

映射列表

在管道中生成节点属性的算法。

linkFeatures

映射列表

将来自端点节点的节点属性组合起来以生成作为管道模型输入的关系(链接)特征的特征步骤。

modelInfo 的结构是

{
    bestParameters: Map,              (1)
    nodePropertySteps: List of Map,
    linkFeatures: List of Map,
    metrics: {                        (2)
        AUCPR: {
            test: Float,              (3)
            outerTrain: Float,        (4)
            train: {                  (5)
                avg: Float,
                max: Float,
                min: Float,
            },
            validation: {             (6)
                avg: Float,
                max: Float,
                min: Float
            }
        }
    }
}
1 得分最高的模型候选者配置。
2 metrics 映射包含每个指标描述(目前仅 AUCPR)的条目以及该指标的对应结果。
3 在测试集上评估最佳模型的数值。
4 在外部训练集上评估最佳模型的数值。
5 train 条目总结了 train 集上的指标结果。
6 validation 条目总结了 validation 集上的指标结果。

在 (3)-(5) 中,如果指标是 OUT_OF_BAG_ERROR,则不会报告这些统计信息。OUT_OF_BAG_ERROR 仅在 (6) 中作为验证指标报告,并且仅当模型是 RandomForest 时。

除了过程产生的数据外,还有大量关于训练的信息在过程进行时被发送到 Neo4j 数据库的日志。

例如,每个模型候选者的表现如何将使用 info 日志级别记录下来,从而最终进入数据库的 neo4j.log 文件。

某些信息仅使用 debug 日志级别记录,因此最终进入数据库的 debug.log 文件。例如,模型候选者训练(在模型选择阶段)期间的训练方法特定元数据(例如逻辑回归的每个时期的损失)。请注意,此特定数据不会由过程调用产生。

示例

在本示例中,我们将创建一个小型图并使用我们迄今为止构建的训练管道。该图是一个小型社会网络,包括人与城市,包括一些关于人们居住地、出生地的信息,以及他们认识的其他人的信息。我们将尝试训练一个模型来预测哪些额外的人可能相互认识。示例图如下所示

Visualization of the example graph
以下 Cypher 语句将在 Neo4j 数据库中创建示例图
CREATE
  (alice:Person {name: 'Alice', age: 38}),
  (michael:Person {name: 'Michael', age: 67}),
  (karin:Person {name: 'Karin', age: 30}),
  (chris:Person {name: 'Chris', age: 52}),
  (will:Person {name: 'Will', age: 6}),
  (mark:Person {name: 'Mark', age: 32}),
  (greg:Person {name: 'Greg', age: 29}),
  (veselin:Person {name: 'Veselin', age: 3}),

  (london:City {name: 'London'}),
  (malmo:City {name: 'Malmo'}),

  (alice)-[:KNOWS]->(michael),
  (michael)-[:KNOWS]->(karin),
  (michael)-[:KNOWS]->(chris),
  (michael)-[:KNOWS]->(greg),
  (will)-[:KNOWS]->(michael),
  (will)-[:KNOWS]->(chris),
  (mark)-[:KNOWS]->(michael),
  (mark)-[:KNOWS]->(will),
  (greg)-[:KNOWS]->(chris),
  (veselin)-[:KNOWS]->(chris),
  (karin)-[:KNOWS]->(veselin),
  (chris)-[:KNOWS]->(karin),

  (alice)-[:LIVES]->(london),
  (michael)-[:LIVES]->(london),
  (karin)-[:LIVES]->(london),
  (chris)-[:LIVES]->(malmo),
  (will)-[:LIVES]->(malmo),

  (alice)-[:BORN]->(london),
  (michael)-[:BORN]->(london),
  (karin)-[:BORN]->(malmo),
  (chris)-[:BORN]->(london),
  (will)-[:BORN]->(malmo),
  (greg)-[:BORN]->(london),
  (veselin)-[:BORN]->(malmo)

有了 Neo4j 中的图,我们现在可以将其投影到图目录中。我们使用针对 Person 节点和 KNOWS 关系的 Cypher 投影来完成此操作。我们还将投影 age 属性,以便在创建链接特征时可以使用它。对于关系,我们必须使用 UNDIRECTED 方向。这是因为链接预测管道仅针对无向图定义。我们忽略了其他节点和关系类型,以便我们的投影是同构的。我们将说明如何在 后续示例 中使用更大的图。

以下语句将使用 Cypher 投影投影图并将其存储在图目录中,名称为 'myGraph'。
MATCH (source:Person)-[r:KNOWS]->(target:Person)
RETURN gds.graph.project(
  'myGraph',
  source,
  target,
  {
    sourceNodeProperties: source { .age },
    targetNodeProperties: target { .age },
    relationshipType: 'KNOWS'
  },
  { undirectedRelationshipTypes: ['KNOWS'] }
)
链接预测模型要求使用 UNDIRECTED 方向为关系创建图。

内存估算

首先,我们将使用 estimate 过程来估计训练管道的成本。估算是为了了解在您的图上训练管道将产生的内存影响。在实际训练管道时,系统将执行估算,并在估算显示运行出内存的可能性非常大时禁止执行。有关更多信息,请参阅 自动估算和执行阻止

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

以下将估计训练管道的内存需求
CALL gds.beta.pipeline.linkPrediction.train.estimate('myGraph', {
  pipeline: 'pipe',
  modelName: 'lp-pipeline-model',
  targetRelationshipType: 'KNOWS'
})
YIELD requiredMemory
表 6. 结果
requiredMemory

"[24 KiB ... 522 KiB]"

训练

现在我们准备实际训练 LinkPrediction 模型。我们必须确保指定 targetRelationshipType 以指示模型仅使用该类型进行训练。对于图 myGraph,实际上没有其他关系类型被投影,但这并非总是如此。

以下将使用管道训练模型
CALL gds.beta.pipeline.linkPrediction.train('myGraph', {
  pipeline: 'pipe',
  modelName: 'lp-pipeline-model',
  metrics: ['AUCPR', 'OUT_OF_BAG_ERROR'],
  targetRelationshipType: 'KNOWS',
  randomSeed: 18
}) YIELD modelInfo, modelSelectionStats
RETURN
  modelInfo.bestParameters AS winningModel,
  modelInfo.metrics.AUCPR.train.avg AS avgTrainScore,
  modelInfo.metrics.AUCPR.outerTrain AS outerTrainScore,
  modelInfo.metrics.AUCPR.test AS testScore,
  [cand IN modelSelectionStats.modelCandidates | cand.metrics.AUCPR.validation.avg] AS validationScores
表 7. 结果
winningModel avgTrainScore outerTrainScore testScore validationScores

{batchSize=100, classWeights=[0.55, 0.45], focusWeight=0.070341817, hiddenLayerSizes=[4, 2], learningRate=0.001, maxEpochs=100, methodName="MultilayerPerceptron", minEpochs=1, patience=2, penalty=0.5, tolerance=0.001}

0.7579365079

0.7

0.6666666667

[0.4305555556, 0.5833333333, 0.4305555556, 0.75]

我们可以看到 MLP 模型配置获胜,并且在测试集上的得分为 0.67。分数计算为 AUCPR 指标,范围为 [0, 1]。如果模型对所有链接的评分高于非链接,则其得分为 1.0,而随机分配评分的模型的平均得分为 0.5。

使用上下文过滤器进行训练

在上面的示例中,我们投影了 Person-KNOWS-Person 子图并将其用于训练和测试。原始图中的许多信息都没有被使用。我们可能希望利用更多节点和关系类型来生成节点属性(和链接特征),并研究它是否会改进链接预测。我们可以通过传入 contextNodeLabelscontextRelationshipTypes 来做到这一点。我们明确地传入 sourceNodeLabeltargetNodeLabel 来指定更窄的节点集,用于训练和测试。

以下语句将使用 Cypher 投影投影完整的图并将其存储在图目录中,名称为 'fullGraph'。

MATCH (source:Person)-[r:KNOWS|LIVES|BORN]->(target:Person|City)
RETURN gds.graph.project(
  'fullGraph',
  source,
  target,
  {
    sourceNodeLabels: labels(source),
    targetNodeLabels: labels(target),
    sourceNodeProperties: source { age: coalesce(source.age, 1) },
    targetNodeProperties: target { age: coalesce(target.age, 1) },
    relationshipType: type(r)
  },
  { undirectedRelationshipTypes: ['KNOWS'] }
)

完整的图包含 2 个节点标签和 3 个关系类型。我们仍然训练 Person-KNOWS-Person 模型,但使用上下文信息 Person-LIVES-City、Person-BORN-City 来生成模型在训练中使用的节点属性。请注意,我们不需要为上下文关系类型使用 UNDIRECTED 方向,因为这些类型将被排除在链接预测训练之外。

首先,我们将创建一个新的管道。

CALL gds.beta.pipeline.linkPrediction.create('pipe-with-context')

接下来,我们将添加具有上下文配置的 nodePropertyStep。

CALL gds.beta.pipeline.linkPrediction.addNodeProperty('pipe-with-context', 'fastRP', {
  mutateProperty: 'embedding',
  embeddingDimension: 256,
  randomSeed: 42,
  contextNodeLabels: ['City'],
  contextRelationshipTypes: ['LIVES', 'BORN']
})

然后,我们将添加链接特征。

CALL gds.beta.pipeline.linkPrediction.addFeature('pipe-with-context', 'hadamard', {
  nodeProperties: ['embedding', 'age']
})

然后,类似地配置数据拆分。

CALL gds.beta.pipeline.linkPrediction.configureSplit('pipe-with-context', {
  testFraction: 0.25,
  trainFraction: 0.6,
  validationFolds: 3
})

然后,我们将添加一个 MLP 模型候选者。

CALL gds.alpha.pipeline.linkPrediction.addMLP('pipe-with-context',
{hiddenLayerSizes: [4, 2], penalty: 1, patience: 2})
以下将使用具有在节点属性步骤中使用的额外上下文信息的管道训练另一个模型
CALL gds.beta.pipeline.linkPrediction.train('fullGraph', {
  pipeline: 'pipe-with-context',
  modelName: 'lp-pipeline-model-filtered',
  metrics: ['AUCPR', 'OUT_OF_BAG_ERROR'],
  sourceNodeLabel: 'Person',
  targetNodeLabel: 'Person',
  targetRelationshipType: 'KNOWS',
  randomSeed: 12
}) YIELD modelInfo, modelSelectionStats
RETURN
  modelInfo.bestParameters AS winningModel,
  modelInfo.metrics.AUCPR.train.avg AS avgTrainScore,
  modelInfo.metrics.AUCPR.outerTrain AS outerTrainScore,
  modelInfo.metrics.AUCPR.test AS testScore,
  [cand IN modelSelectionStats.modelCandidates | cand.metrics.AUCPR.validation.avg] AS validationScores
表 8. 结果
winningModel avgTrainScore outerTrainScore testScore validationScores

{batchSize=100, classWeights=[], focusWeight=0.0, hiddenLayerSizes=[4, 2], learningRate=0.001, maxEpochs=100, methodName="MultilayerPerceptron", minEpochs=1, patience=2, penalty=1.0, tolerance=0.001}

0.832010582

0.6666666667

0.8611111111

[0.75]

正如我们所见,结果实际上是相同的。虽然训练和测试分数在该玩具示例中保持不变,但对于更大的数据集,上下文信息可能会产生更大的影响。