Machine learning pipelines
The Python client has special support for Link prediction pipelines and pipelines for node property prediction. The GDS pipelines are represented as pipeline objects in the GDS Python Client.
Operating pipelines through the client is based entirely on these pipeline objects. This is a more convenient and pythonic API compared to the Cypher procedure API. Once created, the pipeline objects can be passed as arguments to various methods in the Python client, such as the pipeline catalog operations. Additionally, the pipeline objects have convenience methods allowing for inspection of the represented pipeline without explicitly involving the pipeline catalog.
In the examples below we assume that we have an instantiated GraphDataScience object called gds.
Read more about this in Getting started.
Node classification
This section outlines how to use the Python client to build, configure and train a node classification pipeline, as well as how to use the model that training produces for predictions.
Pipeline
To create a new node classification pipeline one would make the following call:
pipe, _ = gds.pipeline.node_classification.create("my-pipe")
where pipe is a pipeline object.
To then go on to build, configure and train the pipeline we would call methods directly on the node classification pipeline object. Below is a description of the methods on such objects:
The node classification pipeline object exposes the following methods to build, configure, train and manage the pipeline:
-
add_node_property— add an algorithm that produces a node property to the pipeline. -
select_features— select node properties to be used as model features. -
configure_split— configure the train-test dataset split. -
add_logistic_regression,add_random_forest,add_mlp— add a model candidate to train during the model selection phase. -
configure_auto_tuning— configure the auto-tuning. -
train— train the pipeline on a graph (returns aNodeClassificationModeland aNodeClassificationPipelineTrainResult);train_estimateestimates the required memory. -
details,name,exists,drop— inspect the pipeline and manage it in the pipeline catalog.
See the NodeClassificationPipeline API reference for the full method signatures.
There are two main differences when comparing the methods above that map to procedures of the Cypher API:
-
As the Python methods are called on the pipeline object, one does not need to provide a name when calling them.
-
Configuration parameters in the Cypher calls are represented by named keyword arguments in the Python method calls.
Another difference is that the train Python call takes a graph object instead of a graph name, and returns a NodeClassificationModel model object that we can run predictions with as well as a NodeClassificationPipelineTrainResult with the metadata from the training.
Please consult the node classification Cypher documentation for information about what kind of input the methods expect.
Example
Below is a small example of how one could configure and train a very basic node classification pipeline. Note that we don’t configure splits explicitly, but rather use the default.
To exemplify this, we introduce a small person graph:
gds.run_cypher(
"""
CREATE
(a:Person {name: "Bob", fraudster: 0}),
(b:Person {name: "Alice", fraudster: 0}),
(c:Person {name: "Eve", fraudster: 1}),
(d:Person {name: "Chad", fraudster: 1}),
(e:Person {name: "Dan", fraudster: 0}),
(f:UnknownPerson {name: "Judy"}),
(a)-[:KNOWS]->(b),
(a)-[:KNOWS]->(c),
(a)-[:KNOWS]->(d),
(b)-[:KNOWS]->(d),
(c)-[:KNOWS]->(d),
(c)-[:KNOWS]->(e),
(d)-[:KNOWS]->(e),
(d)-[:KNOWS]->(f),
(e)-[:KNOWS]->(f)
"""
)
G, project_result = gds.graph.project.native("person_graph", {"Person": {"properties": ["fraudster"]}}, "KNOWS")
assert G.node_labels() == ["Person"]
pipe, _ = gds.pipeline.node_classification.create("my-pipe")
# Add Degree centrality as a property step producing "rank" node properties
pipe.add_node_property("degree", mutate_property="rank")
# Select our "rank" property as a feature for the model training
pipe.select_features("rank")
# Verify that the features to be used in model training are what we expect
feature_properties = pipe.details().feature_properties
assert len(feature_properties) == 1
assert feature_properties[0]["feature"] == "rank"
# Configure the model training to do cross-validation over logistic regression
pipe.add_logistic_regression(tolerance=(0.01, 0.1))
pipe.add_logistic_regression(penalty=1.0)
# Train the pipeline targeting node property "class" as label and "ACCURACY" as only metric
fraud_model, train_result = pipe.train(
G,
model_name="fraud-model",
target_property="fraudster",
metrics=["ACCURACY"],
random_seed=111
)
assert train_result.train_millis >= 0
A model referred to as "fraud-model" in the GDS Model Catalog is produced. In the next section we will go over how to use that model to make predictions.
Model
As we saw in the previous section, node classification models are created when training a node classification pipeline. In addition to inheriting the methods common to all model objects, node classification models have the following methods:
-
metrics,best_parameters,node_property_steps— inspect the training outcome and the pipeline that produced the model. -
predict_stream,predict_mutate,predict_write— predict classes for the input graph’s nodes;predict_estimateestimates the required memory.
See the NodeClassificationModel API reference for the full method signatures.
One can note that the predict methods are indeed very similar to their Cypher counterparts. The three main differences are that:
-
They take a graph object instead of a graph name.
-
They have Python keyword arguments representing the keys of the configuration map.
-
One does not have to provide a "modelName" since the model object used itself have this information.
Example (continued)
We now continue the example above using the node classification model fraud_model we trained there.
# Make sure we indeed obtained an accuracy score
metrics = fraud_model.metrics()
assert "ACCURACY" in metrics
H, project_result = gds.graph.project.native("full_person_graph", ["Person", "UnknownPerson"], "KNOWS")
# Predict on `H` and stream the results with a specific concurrency of 2
predictions = fraud_model.predict_stream(H, concurrency=2)
assert len(predictions) == H.node_count()
Link prediction
This section outlines how to use the Python client to build, configure and train a link prediction pipeline, as well as how to use the model that training produces for predictions.
Pipeline
To create a new link prediction pipeline one would make the following call:
pipe, _ = gds.pipeline.link_prediction.create("my-pipe")
where pipe is a pipeline object.
To then go on to build, configure and train the pipeline we would call methods directly on the link prediction pipeline object. Below is a description of the methods on such objects:
The link prediction pipeline object exposes the following methods to build, configure, train and manage the pipeline:
-
add_node_property— add an algorithm that produces a node property to the pipeline. -
add_feature— add a link feature for model training based on node properties and a feature combiner. -
configure_split— configure the feature-train-test dataset split. -
add_logistic_regression,add_random_forest,add_mlp— add a model candidate to train during the model selection phase. -
configure_auto_tuning— configure the auto-tuning. -
train— train the pipeline on a graph (returns aLinkPredictionModeland aLinkPredictionPipelineTrainResult);train_estimateestimates the required memory. -
details,name,exists,drop— inspect the pipeline and manage it in the pipeline catalog.
See the LinkPredictionPipeline API reference for the full method signatures.
There are two main differences when comparing the methods above that map to procedures of the Cypher API:
-
As the Python methods are called on the pipeline object, one does not need to provide a name when calling them.
-
Configuration parameters in the Cypher calls are represented by named keyword arguments in the Python method calls.
Another difference is that the train Python call takes a graph object instead of a graph name, and returns a LinkPredictionModel model object that we can run predictions with as well as a LinkPredictionPipelineTrainResult with the metadata from the training.
Please consult the link prediction Cypher documentation for information about what kind of input the methods expect.
Example
Below is a small example of how one could configure and train a very basic link prediction pipeline. Note that we don’t configure training parameters explicitly, but rather use the default.
To exemplify this, we introduce a small person graph:
gds.run_cypher(
"""
CREATE
(a:Person {name: "Bob"}),
(b:Person {name: "Alice"}),
(c:Person {name: "Eve"}),
(d:Person {name: "Chad"}),
(e:Person {name: "Dan"}),
(f:Person {name: "Judy"}),
(a)-[:KNOWS]->(b),
(a)-[:KNOWS]->(c),
(a)-[:KNOWS]->(d),
(b)-[:KNOWS]->(d),
(c)-[:KNOWS]->(d),
(c)-[:KNOWS]->(e),
(d)-[:KNOWS]->(e),
(d)-[:KNOWS]->(f),
(e)-[:KNOWS]->(f)
"""
)
G, project_result = gds.graph.project.native("person_graph", "Person", {"KNOWS": {"orientation":"UNDIRECTED"}})
assert G.relationship_types() == ["KNOWS"]
pipe, _ = gds.pipeline.link_prediction.create("lp-pipe")
# Add FastRP as a property step producing "embedding" node properties
pipe.add_node_property("fastRP", embedding_dimension=128, mutate_property="embedding", random_seed=1337)
# Combine our "embedding" node properties with Hadamard to create link features for training
pipe.add_feature("hadamard", node_properties=["embedding"])
# Verify that the features to be used in model training are what we expect
steps = pipe.details().feature_steps
assert len(steps) == 1
# Specify the fractions we want for our dataset split
pipe.configure_split(train_fraction=0.2, test_fraction=0.2, validation_folds=2)
# Add a random forest model with tuning over `max_depth`
pipe.add_random_forest(max_depth=(2, 20))
# Train the pipeline and produce a model named "friend-recommender"
friend_recommender, train_result = pipe.train(
G,
model_name="friend-recommender",
target_relationship_type="KNOWS",
random_seed=42
)
assert train_result.train_millis >= 0
A model referred to as "friend-recommender" in the GDS Model Catalog is produced. In the next section we will go over how to use that model to make predictions.
Model
As we saw in the previous section, link prediction models are created when training a link prediction pipeline. In addition to inheriting the methods common to all model objects, link prediction models have the following methods:
-
metrics,best_parameters,node_property_steps— inspect the training outcome and the pipeline that produced the model. -
predict_stream,predict_mutate— predict links between non-neighboring nodes of the input graph;predict_estimateestimates the required memory.
See the LinkPredictionModel API reference for the full method signatures.
One can note that the predict methods are indeed very similar to their Cypher counterparts. The three main differences are that:
-
They take a graph object instead of a graph name.
-
They have Python keyword arguments representing the keys of the configuration map.
-
One does not have to provide a "modelName" since the model object used itself have this information.
Example (continued)
We now continue the example above using the link prediction model friend_recommender we trained there.
# Make sure we indeed obtained an AUCPR score
metrics = friend_recommender.metrics()
assert "AUCPR" in metrics
# Predict on `G` and mutate it with the relationship predictions
mutate_result = friend_recommender.predict_mutate(G, "PRED_REL", top_n=5)
assert mutate_result.relationships_written >= 0
Node regression
This section outlines how to use the Python client to build, configure and train a node regression pipeline, as well as how to use the model that training produces for predictions.
Pipeline
To create a new node regression pipeline one would make the following call:
pipe, _ = gds.pipeline.node_regression.create("my-pipe")
where pipe is a pipeline object.
To then go on to build, configure and train the pipeline we would call methods directly on the node regression pipeline object. Below is a description of the methods on such objects:
The node regression pipeline object exposes the following methods to build, configure, train and manage the pipeline:
-
add_node_property— add an algorithm that produces a node property to the pipeline. -
select_features— select node properties to be used as model features. -
configure_split— configure the train-test dataset split. -
add_linear_regression,add_random_forest— add a model candidate to train during the model selection phase. -
configure_auto_tuning— configure the auto-tuning. -
train— train the pipeline on a graph (returns aNodeRegressionModeland aNodeRegressionPipelineTrainResult). -
details,name,exists,drop— inspect the pipeline and manage it in the pipeline catalog.
See the NodeRegressionPipeline API reference for the full method signatures.
There are two main differences when comparing the methods above that map to procedures of the Cypher API:
-
As the Python methods are called on the pipeline object, one does not need to provide a name when calling them.
-
Configuration parameters in the Cypher calls are represented by named keyword arguments in the Python method calls.
Another difference is that the train Python call takes a graph object instead of a graph name, and returns a NodeRegressionModel model object that we can run predictions with as well as a NodeRegressionPipelineTrainResult with the metadata from the training.
Please consult the node regression Cypher documentation for information about what kind of input the methods expect.
Example
Below is a small example of how one could configure and train a very basic node regression pipeline. Note that we don’t configure splits explicitly, but rather use the default.
To exemplify this, we introduce a small person graph:
gds.run_cypher(
"""
CREATE
(a:Person {name: "Bob", age: 22}),
(b:Person {name: "Alice", age: 5}),
(c:Person {name: "Eve", age: 53}),
(d:Person {name: "Chad", age: 44}),
(e:Person {name: "Dan", age: 60}),
(f:UnknownPerson {name: "Judy"}),
(a)-[:KNOWS]->(b),
(a)-[:KNOWS]->(c),
(a)-[:KNOWS]->(d),
(b)-[:KNOWS]->(d),
(c)-[:KNOWS]->(d),
(c)-[:KNOWS]->(e),
(d)-[:KNOWS]->(e),
(d)-[:KNOWS]->(f),
(e)-[:KNOWS]->(f)
"""
)
G, project_result = gds.graph.project.native("person_graph", {"Person": {"properties": ["age"]}}, "KNOWS")
assert G.relationship_types() == ["KNOWS"]
pipe, _ = gds.pipeline.node_regression.create("nr-pipe")
# Add Degree centrality as a property step producing "rank" node properties
pipe.add_node_property("degree", mutate_property="rank")
# Select our "rank" property as a feature for the model training
pipe.select_features("rank")
# Verify that the features to be used in model training are what we expect
feature_properties = pipe.details().feature_properties
assert len(feature_properties) == 1
assert feature_properties[0]["feature"] == "rank"
# Configure the model training to do cross-validation over linear regression
pipe.add_linear_regression(tolerance=(0.01, 0.1))
pipe.add_linear_regression(penalty=1.0)
# Train the pipeline targeting node property "age" as label and "MEAN_SQUARED_ERROR" as only metric
age_predictor, train_result = pipe.train(
G,
model_name="age-predictor",
target_property="age",
metrics=["MEAN_SQUARED_ERROR"],
random_seed=42
)
assert train_result.train_millis >= 0
A model referred to as "age-predictor" in the GDS Model Catalog is produced. In the next section we will go over how to use that model to make predictions.
Model
As we saw in the previous section, node regression models are created when training a node regression pipeline. In addition to inheriting the methods common to all model objects, node regression models have the following methods:
-
metrics,best_parameters,node_property_steps— inspect the training outcome and the pipeline that produced the model. -
predict_stream,predict_mutate— predict property values for the input graph’s nodes.
See the NodeRegressionModel API reference for the full method signatures.
One can note that the predict methods are indeed very similar to their Cypher counterparts. The three main differences are that:
-
They take a graph object instead of a graph name.
-
They have Python keyword arguments representing the keys of the configuration map.
-
One does not have to provide a "modelName" since the model object used itself have this information.
Example (continued)
We now continue the example above using the node regression model age_predictor we trained there.
Suppose that we have a new graph H that we want to run predictions on.
# Make sure we indeed obtained an MEAN_SQUARED_ERROR score
metrics = age_predictor.metrics()
assert "MEAN_SQUARED_ERROR" in metrics
H, project_result = gds.graph.project.native("full_person_graph", ["Person", "UnknownPerson"], "KNOWS")
# Predict on `H` and stream the results with a specific concurrency of 2
predictions = age_predictor.predict_stream(H, concurrency=2)
assert len(predictions) == H.node_count()
The pipeline catalog
The primary way to use pipeline objects is for training models.
Additionally, pipeline objects can be used as input to GDS Pipeline Catalog operations.
For instance, supposing we have a pipeline object pipe, we could:
exists_result = gds.pipeline.exists(pipe.name())
if exists_result.exists:
gds.pipeline.drop(pipe.name()) # same as pipe.drop()
A pipeline object that has already been created and is present in the pipeline catalog can be retrieved calling the get method with its name.
For example, we can list from the catalog and use the first pipeline name we find to get a pipeline object representing that pipeline, which will be the NodeClassification pipeline we created in the example above.
list_result = gds.pipeline.list()
first_pipeline_name = list_result[0].pipeline_name
pipe = gds.pipeline.node_classification.get(first_pipeline_name)
assert pipe.name() == "my-pipe"