训练管道

此功能处于 Alpha 阶段。有关功能分级的更多信息,请参阅 API 分级

训练模式 gds.alpha.pipeline.nodeRegression.train 负责数据拆分、特征提取、模型选择、训练以及存储模型以供将来使用。运行此模式会生成一个类型为 NodeRegression 的回归模型,该模型随后会被存储在 模型目录 (model catalog) 中。回归模型可以应用于图,以预测新节点的属性值。

更准确地说,训练过程如下:

  1. 将根据 添加节点属性 所定义的节点属性步骤应用于整个图。每个步骤的图过滤器由 contextNodeLabels + targetNodeLabelscontextRelationships + relationshipTypes 组成。

  2. targetNodeLabels 过滤器应用于该图。

  3. 选择要用作特征的节点属性,具体如 添加特征 所述。

  4. 将输入图拆分为两部分:训练图和测试图。这在 配置节点拆分 中进行了描述。这些图在内部进行管理,且仅在训练期间存在。

  5. 使用分层 k 折交叉验证对训练图中的节点进行拆分。折数 k 的配置如 配置节点拆分 中所述。

  6. 参数空间 中定义的每个模型候选者都会在每个训练集上进行训练,并针对每一折在相应的验证集上进行评估。评估使用指定的首要指标 (primary metric)。

  7. 根据首要指标的平均得分最高,选择表现最好的模型。

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

  9. 评估胜出模型在整个训练图以及测试图上的表现。

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

  11. 将胜出的模型注册到 模型目录 中。

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

指标 (Metrics)

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

  • MEAN_SQUARED_ERROR(均方误差)

  • ROOT_MEAN_SQUARED_ERROR(均方根误差)

  • MEAN_ABSOLUTE_ERROR(平均绝对误差)

训练期间可以指定多个指标,但只有第一个指定的指标——即 primary(首要)指标——用于模型评估,所有指标的结果都会呈现在训练结果中。

语法

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

graphName

字符串

不适用

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

配置

Map

{}

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

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

pipeline

字符串

不适用

要执行的流水线名称。

targetNodeLabels(目标节点标签)

字符串列表

['*']

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

relationshipTypes

字符串列表

['*']

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

concurrency

整数

4 [1]

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

jobId

字符串

内部生成

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

logProgress

布尔值

true

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

targetProperty

字符串

不适用

节点的预测目标属性。必须是 Integer(整数)或 Float(浮点数)类型。

metrics

字符串列表

不适用

用于评估模型的 指标

randomSeed

整数

不适用

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

modelName

字符串

不适用

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

storeModelToDisk

布尔值

false

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

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

表 3. 结果
名称 类型 描述

trainMillis

整数

用于训练的毫秒数。

modelInfo

Map

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

modelSelectionStats

Map

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

配置

Map

用于训练过程的配置。

modelInfo 也可以稍后通过使用 模型列表过程 获取。modelInfo 返回字段包含以下算法特定的子字段

表 4. 模型信息字段
名称 类型 描述

bestParameters

Map

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

metrics

Map

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

nodePropertySteps

映射列表

在流水线内产生节点属性的算法。

featureProperties

字符串列表

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

modelInfo 的结构为

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

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

例如,每个模型候选的表现如何会以 info 日志级别记录,并最终进入数据库的 neo4j.log 文件中。

有些信息仅以 debug 日志级别记录,并最终进入数据库的 debug.log 文件中。例如模型候选训练期间(模型选择阶段)特定于训练方法的元数据(如逻辑回归的每 epoch 损失)。请注意,此特定数据不会由过程调用返回。

示例

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

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

在本节中,我们将展示在一个具体图上运行节点回归训练管道的示例。其目的是说明结果的样子,并为如何在实际环境中使用该模型提供指南。我们将使用一个包含少量代表房屋的节点的小图来进行演示。在我们的示例中,我们要预测房屋的 price(价格)。示例图如下所示

node property pipeline graph
以下 Cypher 语句将在 Neo4j 数据库中创建示例图:
CREATE
  (gold:House {color: 'Gold', sizePerStory: [15.5, 23.6, 33.1], price: 99.99}),
  (red:House {color: 'Red', sizePerStory: [15.5, 23.6, 100.0], price: 149.99}),
  (blue:House {color: 'Blue', sizePerStory: [11.3, 35.1, 22.0], price: 77.77}),
  (green:House {color: 'Green', sizePerStory: [23.2, 55.1, 0.0], price: 80.80}),
  (gray:House {color: 'Gray', sizePerStory: [34.3, 24.0, 0.0],  price: 57.57}),
  (black:House {color: 'Black', sizePerStory: [71.66, 55.0, 0.0], price: 140.14}),
  (white:House {color: 'White', sizePerStory: [11.1, 111.0, 0.0], price: 122.22}),
  (teal:House {color: 'Teal', sizePerStory: [80.8, 0.0, 0.0], price: 80.80}),
  (beige:House {color: 'Beige', sizePerStory: [106.2, 0.0, 0.0], price: 110.11}),
  (magenta:House {color: 'Magenta', sizePerStory: [99.9, 0.0, 0.0], price: 100.00}),
  (purple:House {color: 'Purple', sizePerStory: [56.5, 0.0, 0.0], price: 60.00}),
  (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 属性以用作模型特征,并将 price 属性用作目标特征。

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

训练 (Train)

在接下来的示例中,我们将演示如何在此图上运行节点回归训练管道。我们将训练一个模型来预测房屋的价格,该模型基于其 sizePerStory 属性。管道的配置是运行上一页示例的结果。

以下将使用流水线训练一个模型:
CALL gds.alpha.pipeline.nodeRegression.train('myGraph', {
  pipeline: 'pipe',
  targetNodeLabels: ['House'],
  modelName: 'nr-pipeline-model',
  targetProperty: 'price',
  randomSeed: 25,
  concurrency: 1,
  metrics: ['MEAN_SQUARED_ERROR']
}) YIELD modelInfo
RETURN
  modelInfo.bestParameters AS winningModel,
  modelInfo.metrics.MEAN_SQUARED_ERROR.train.avg AS avgTrainScore,
  modelInfo.metrics.MEAN_SQUARED_ERROR.outerTrain AS outerTrainScore,
  modelInfo.metrics.MEAN_SQUARED_ERROR.test AS testScore
表 5. 结果
winningModel avgTrainScore outerTrainScore testScore

{maxDepth=2147483647, methodName="RandomForest", minLeafSize=1, minSplitSize=2, numberOfDecisionTrees=5, numberOfSamplesRatio=1.0}

658.1848249523812

1188.6296009999999

1583.5897253333333

在这里我们可以观察到,拥有 5 棵决策树的 RandomForest 候选者在训练阶段表现最好。请注意,这只是一个在极小图上的玩具示例。为了获得更高的测试分数,我们可能需要使用更好的特征、更大的图或不同的模型配置。

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

在上面的例子中,我们投影了一个没有关系的 House 子图,并将其用于训练和测试。原始图中的许多信息未被使用。我们可能希望利用更多的节点和关系类型来生成节点属性(以及链接特征),并研究这是否能改善节点回归。我们可以通过在添加节点属性步骤时传入 contextNodeLabels 和 contextRelationshipTypes 来做到这一点。

以下语句将通过 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 { .sizePerStory, .price },
    relationshipType: 'PAINTED'
  },
  { undirectedRelationshipTypes: ['PAINTED'] }
)

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

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

CALL gds.alpha.pipeline.nodeRegression.create('pipe-with-context')

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

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

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

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

并通过添加随机森林模型候选者来完成管道设置

CALL gds.alpha.pipeline.nodeRegression.addRandomForest('pipe-with-context', {numberOfDecisionTrees: 5})

现在我们准备调用新创建管道的训练。

以下内容将使用配置了上下文的管道来训练模型
CALL gds.alpha.pipeline.nodeRegression.train('paintingGraph', {
  pipeline: 'pipe-with-context',
  targetNodeLabels: ['House'],
  modelName: 'nr-pipeline-model-contextual',
  targetProperty: 'price',
  randomSeed: 25,
  concurrency: 1,
  metrics: ['MEAN_SQUARED_ERROR']
}) YIELD modelInfo
RETURN
  modelInfo.bestParameters AS winningModel,
  modelInfo.metrics.MEAN_SQUARED_ERROR.train.avg AS avgTrainScore,
  modelInfo.metrics.MEAN_SQUARED_ERROR.outerTrain AS outerTrainScore,
  modelInfo.metrics.MEAN_SQUARED_ERROR.test AS testScore
表 6. 结果
winningModel avgTrainScore outerTrainScore testScore

{maxDepth=2147483647, methodName="RandomForest", minLeafSize=1, minSplitSize=2, numberOfDecisionTrees=5, numberOfSamplesRatio=1.0}

758.087008266667

837.5558960000001

1192.523748

我们可以看到,结果表明与前一节中的 nr-pipeline-model 相比,随机森林模型的均方误差更低。这一变化是因为嵌入考虑了更多的上下文信息。虽然这是一个玩具示例,但额外的上下文有时可以为管道步骤提供有价值的信息,从而产生更好的性能。