训练管道

此功能处于测试阶段。有关功能层级的更多信息,请参见 API 层级.

训练模式 gds.beta.pipeline.nodeClassification.train 负责分割数据、特征提取、模型选择、训练和存储模型以供将来使用。运行此模式会生成一个类型为 NodeClassification 的分类模型,然后将其存储在 模型目录 中。分类模型可以 应用 于可能不同的图,对节点进行分类。

更确切地说,训练过程如下所示

  1. 根据 添加节点属性 添加的节点属性步骤将应用于图。每个步骤上的图过滤器包含 contextNodeLabels + targetNodeLabelscontextRelationships + relationshipTypes

  2. 对图应用 targetNodeLabels 过滤器。

  3. 选择要作为特征使用的节点属性,如 添加特征 中所指定。

  4. 将输入图拆分为两部分:训练图和测试图。这在 配置节点分割 中有描述。这些图是在内部管理的,只在训练期间存在。

  5. 使用分层 k 折交叉验证分割训练图中的节点。折数 k 可以配置,如 配置节点分割 中所述。

  6. 参数空间 中定义的每个模型候选者都将在每个训练集上进行训练,并在每个折的相应验证集上进行评估。评估使用指定的 主要指标

  7. 根据主要指标的最高平均分数选择性能最佳的模型。

  8. 在整个训练图上重新训练获胜模型。

  9. 评估获胜模型在整个训练图以及测试图上的性能。

  10. 在整个原始图上重新训练获胜模型。

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

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

指标

Neo4j GDS 库中的节点分类模型支持以下评估指标

  • 全局指标

    • F1_WEIGHTED

    • F1_MACRO

    • ACCURACY

    • OUT_OF_BAG_ERROR(仅适用于 RandomForest,仅给出验证和测试分数)

  • 每个类别的指标

    • F1(class=<number>)F1(class=*)

    • PRECISION(class=<number>)PRECISION(class=*)

    • RECALL(class=<number>)RECALL(class=*)

    • ACCURACY(class=<number>)ACCURACY(class=*)

* 是语法糖,用于报告图中每个类别的指标。当使用每个类别的指标时,报告的指标包含类似于 ACCURACY_class_1 的键。

在训练过程中可以指定多个指标,但只使用第一个指定的指标(即 主要 指标)进行评估,所有指标的结果都包含在训练结果中。主要指标不能是 * 扩展,因为无法确定哪个扩展后的指标应该是 主要 指标。

OUT_OF_BAG_ERROR 仅针对 RandomForest 模型计算,并作为多数投票的准确率进行评估,其中每个示例仅考虑在训练期间未使用该示例的树。 每个树使用的训练集比例由配置参数 numberOfSamplesRatio 控制。 OUT_OF_BAG_ERROR 在交叉验证阶段评估时报告为验证分数。 在随机森林模型获胜的情况下,它被报告为基于在整个训练集上重新训练模型的测试分数。

语法

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

graphName

字符串

n/a

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

configuration

映射

{}

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

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

pipeline

字符串

n/a

要执行的管道的名称。

targetNodeLabels

字符串列表

['*']

使用给定的节点标签过滤命名图,以获取要进行训练和评估的节点。

relationshipTypes

字符串列表

['*']

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

concurrency

整数

4

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

targetProperty

字符串

n/a

节点的类。 必须是整数类型。

metrics

字符串列表

n/a

指标 用于评估模型。

randomSeed

整数

n/a

训练期间使用的随机数生成器的种子。

modelName

字符串

n/a

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

jobId

字符串

在内部生成

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

storeModelToDisk

布尔值

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

表 3. 结果
名称 类型 描述

trainMillis

整数

用于训练的毫秒数。

modelInfo

映射

有关训练和获胜模型的信息。

modelSelectionStats

映射

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

configuration

映射

用于训练过程的配置。

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

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

bestParameters

映射

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

modelCandidates

列表

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

bestTrial

整数

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

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

modelName

字符串

已训练模型的名称。

modelType

字符串

已训练模型的类型。

classes

整数列表

整个图上 targetProperty 的不同值的排序列表。

bestParameters

映射

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

metrics

映射

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

nodePropertySteps

映射列表

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

featureProperties

字符串列表

选择作为管道模型输入特征的节点属性。

modelInfo 的结构是

{
    bestParameters: Map,                (1)
    nodePropertySteps: List of Map,
    featureProperties: List of String,
    classes: List of Integer,           (2)
    metrics: {                          (3)
        <METRIC_NAME>: {                (4)
            test: Float,                (5)
            outerTrain: Float,          (6)
            train: {                    (7)
                avg: Float,
                max: Float,
                min: Float,
            },
            validation: {               (8)
                avg: Float,
                max: Float,
                min: Float,
                params: Map
            }
        }
    }
}
1 得分最高的模型候选者配置。
2 整个图上 targetProperty 的不同值的排序列表。
3 metrics 映射包含针对每个指标描述的条目,以及该指标的相应结果。
4 在过程配置中指定的指标名称,例如 F1_MACRORECALL(class=4)
5 针对获胜模型在测试集上的评估的数值。
6 针对获胜模型在外层训练集上的评估的数值。
7 train 条目汇总了 train 集上的指标结果。
8 validation 条目汇总了 validation 集上的指标结果。

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

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

例如,每个模型候选者的表现都以 info 日志级别记录,因此最终会出现在数据库的 neo4j.log 文件中。

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

示例

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

这些示例使用 Cypher 投影 作为规范。 本机投影将在将来的版本中被弃用。

在本节中,我们将展示在具体图上运行节点分类训练管道的示例。 目的是说明结果是什么样子,并提供如何在实际环境中使用模型的指南。 我们将在一个包含少量节点的房屋小图上执行此操作。 这是一个多类分类的例子,class 节点属性的不同值决定了类的数量,在本例中是三个 (0、1 和 2)。 示例图如下所示

node property pipeline graph
以下 Cypher 语句将在 Neo4j 数据库中创建示例图
CREATE
  (gold:House {color: 'Gold', sizePerStory: [15.5, 23.6, 33.1], class: 0}),
  (red:House {color: 'Red', sizePerStory: [15.5, 23.6, 100.0], class: 0}),
  (blue:House {color: 'Blue', sizePerStory: [11.3, 35.1, 22.0], class: 0}),
  (green:House {color: 'Green', sizePerStory: [23.2, 55.1, 0.0], class: 1}),
  (gray:House {color: 'Gray', sizePerStory: [34.3, 24.0, 0.0],  class: 1}),
  (black:House {color: 'Black', sizePerStory: [71.66, 55.0, 0.0], class: 1}),
  (white:House {color: 'White', sizePerStory: [11.1, 111.0, 0.0], class: 1}),
  (teal:House {color: 'Teal', sizePerStory: [80.8, 0.0, 0.0], class: 2}),
  (beige:House {color: 'Beige', sizePerStory: [106.2, 0.0, 0.0], class: 2}),
  (magenta:House {color: 'Magenta', sizePerStory: [99.9, 0.0, 0.0], class: 2}),
  (purple:House {color: 'Purple', sizePerStory: [56.5, 0.0, 0.0], class: 2}),
  (pink:UnknownHouse {color: 'Pink', sizePerStory: [23.2, 55.1, 56.1]}),
  (tan:UnknownHouse {color: 'Tan', sizePerStory: [22.32, 102.0, 0.0]}),
  (yellow:UnknownHouse {color: 'Yellow', sizePerStory: [39.0, 0.0, 0.0]}),

  // richer context
  (schiele:Painter {name: 'Schiele'}),
  (picasso:Painter {name: 'Picasso'}),
  (kahlo:Painter {name: 'Kahlo'}),

  (schiele)-[:PAINTED]->(gold),
  (schiele)-[:PAINTED]->(red),
  (schiele)-[:PAINTED]->(blue),
  (picasso)-[:PAINTED]->(green),
  (picasso)-[:PAINTED]->(gray),
  (picasso)-[:PAINTED]->(black),
  (picasso)-[:PAINTED]->(white),
  (kahlo)-[:PAINTED]->(teal),
  (kahlo)-[:PAINTED]->(beige),
  (kahlo)-[:PAINTED]->(magenta),
  (kahlo)-[:PAINTED]->(purple),
  (schiele)-[:PAINTED]->(pink),
  (schiele)-[:PAINTED]->(tan),
  (kahlo)-[:PAINTED]->(yellow);

有了 Neo4j 中的图,我们现在可以将其投影到图目录中,以便为管道执行做好准备。 我们使用针对 HouseUnknownHouse 标签的 Cypher 投影来执行此操作。 我们还将投影 sizeOfStory 属性以用作模型特征,以及 class 属性以用作目标特征。

以下语句将使用 Cypher 投影投影图,并将其存储在图目录中,名称为 'myGraph'。
MATCH (house:House|UnknownHouse)
RETURN gds.graph.project(
  'myGraph',
  house,
  null,
  {
    sourceNodeLabels: labels(house),
    targetNodeLabels: [],
    sourceNodeProperties: house { .sizePerStory, .class },
    targetNodeProperties: {}
  }
)

内存估算

首先,我们将使用 estimate 过程估算运行算法的成本。 这可以使用任何执行模式来完成。 在此示例中,我们将使用 train 模式。 估算算法有助于了解在图上运行算法会对内存产生什么样的影响。 稍后,当你实际以某种执行模式运行算法时,系统将执行估算。 如果估算表明执行很可能超过其内存限制,则将禁止执行。 要了解有关此方面的更多信息,请参见 自动估算和执行阻止

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

以下操作将估算以训练模式运行算法所需的内存。
CALL gds.beta.pipeline.nodeClassification.train.estimate('myGraph', {
  pipeline: 'pipe',
  targetNodeLabels: ['House'],
  modelName: 'nc-model',
  targetProperty: 'class',
  randomSeed: 2,
  metrics: [ 'ACCURACY' ]
})
YIELD requiredMemory
表 6. 结果
requiredMemory

"[1264 KiB ... 1337 KiB]"

如果节点属性步骤没有实现估算,则该步骤将在估算中被忽略。

训练

在以下示例中,我们将演示在该图上运行节点分类训练管道的过程。 我们将训练一个模型,根据房屋的 sizePerStory 属性预测其所属的类别。

以下操作将使用管道训练模型
CALL gds.beta.pipeline.nodeClassification.train('myGraph', {
  pipeline: 'pipe',
  targetNodeLabels: ['House'],
  modelName: 'nc-pipeline-model',
  targetProperty: 'class',
  randomSeed: 1337,
  metrics: ['ACCURACY', 'OUT_OF_BAG_ERROR']
}) YIELD modelInfo, modelSelectionStats
RETURN
  modelInfo.bestParameters AS winningModel,
  modelInfo.metrics.ACCURACY.train.avg AS avgTrainScore,
  modelInfo.metrics.ACCURACY.outerTrain AS outerTrainScore,
  modelInfo.metrics.ACCURACY.test AS testScore,
  [cand IN modelSelectionStats.modelCandidates | cand.metrics.ACCURACY.validation.avg] AS validationScores
表 7. 结果
winningModel avgTrainScore outerTrainScore testScore validationScores

{batchSize=100, classWeights=[], focusWeight=0.0, learningRate=0.001, maxEpochs=500, methodName="LogisticRegression", minEpochs=1, patience=1, penalty=5.881039654, tolerance=0.001}

1.0

1.0

1.0

[0.8, 0.0, 0.5, 0.9, 0.8]

在这里,我们可以观察到,在训练阶段,具有 5.881 惩罚的模型候选者表现最佳,在训练图和测试图上的 ACCURACY 得分为 1。 这是自动调整找到的一个模型。 这表明该模型对训练图反应良好,并且能够很好地泛化到未见的数据。 请注意,这只是一个针对非常小图的玩具示例。 为了获得更高的测试分数,我们可能需要使用更好的特征、更大的图或不同的模型配置。

为节点属性步骤提供更丰富的上下文

在上面的示例中,我们投影了没有关系的房屋子图,并将其用于训练和测试。 原始图中的许多信息都没有使用。 我们可能希望利用更多节点和关系类型来生成节点属性(和链接特征),并调查它是否会改善节点分类。 我们可以在添加节点属性步骤时传入 contextNodeLabelscontextRelationshipTypes 来做到这一点。

以下语句将使用 Cypher 投影投影包含房屋及其画家信息的信息图,并将其存储在图目录中,名称为 'paintingGraph'。

MATCH (house:House)
OPTIONAL MATCH (painter:Painter)-[r:PAINTED]->(house:House)
RETURN gds.graph.project(
  'paintingGraph',
  painter,
  house,
  {
    sourceNodeLabels: ['Painter'],
    targetNodeLabels: ['House'],
    sourceNodeProperties: {},
    targetNodeProperties: house { .class },
    relationshipType: 'PAINTED'
  },
  { undirectedRelationshipTypes: ['PAINTED'] }
)

我们仍然训练模型来预测每栋房屋的类别,但在生成特征时,除了 House 之外,还使用 PainterPAINTED 作为上下文,以利用完整的图结构。 但是,在特征生成之后,只有 House 节点被视为训练和评估实例,因此只有 House 节点需要具有目标属性 class

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

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

其次,我们添加一个节点属性步骤(在本例中,是节点嵌入),其中 Painter 作为 contextNodeLabels

CALL gds.beta.pipeline.nodeClassification.addNodeProperty('pipe-with-context', 'fastRP', {
embeddingDimension: 64,
iterationWeights: [0, 1],
mutateProperty:'embedding',
contextNodeLabels: ['Painter']
})

我们将嵌入添加到模型中作为特征

CALL gds.beta.pipeline.nodeClassification.selectFeatures('pipe-with-context', ['embedding'])

我们通过添加一个逻辑回归模型候选者来完成管道设置

CALL gds.beta.pipeline.nodeClassification.addLogisticRegression('pipe-with-context')

我们现在可以调用新创建的管道的训练。

以下操作将使用上下文配置的管道训练模型
CALL gds.beta.pipeline.nodeClassification.train('paintingGraph', {
  pipeline: 'pipe-with-context',
  targetNodeLabels: ['House'],
  modelName: 'nc-pipeline-model-contextual',
  targetProperty: 'class',
  randomSeed: 1337,
  metrics: ['ACCURACY']
}) YIELD modelInfo, modelSelectionStats
RETURN
  modelInfo.bestParameters AS winningModel,
  modelInfo.metrics.ACCURACY.train.avg AS avgTrainScore,
  modelInfo.metrics.ACCURACY.outerTrain AS outerTrainScore,
  modelInfo.metrics.ACCURACY.test AS testScore,
  [cand IN modelSelectionStats.modelCandidates | cand.metrics.ACCURACY.validation.avg] AS validationScores
表 8. 结果
winningModel avgTrainScore outerTrainScore testScore validationScores

{batchSize=100, classWeights=[], focusWeight=0.0, learningRate=0.001, maxEpochs=100, methodName="LogisticRegression", minEpochs=1, patience=1, penalty=0.0, tolerance=0.001}

1.0

1.0

1.0

[1.0]

如我们所见,结果表明,画家信息足以完美地对房屋进行分类。 这种变化是由于嵌入考虑了更多上下文信息。 虽然这是一个玩具示例,但额外的上下文有时可以为管道步骤提供有价值的信息,从而导致更好的性能。