.. _using-graphbolt-ref:
Using GraphBolt to speed up training and inference
==================================================
With GraphStorm ``v0.4``, we introduced support for
`GraphBolt `_
stochastic training. GraphBolt is a new data loading module for DGL that enables faster and more
efficient graph sampling, potentially leading to significant efficiency benefits.
In this guide we'll give an example of how to prepare your data for use with GraphBolt.
Pre-requisites
--------------
To use GraphBolt you need to have a ``dgl >= 2.1.0`` installed. We recommend using DGL 2.3.0
with PyTorch 2.3 for better compatibility with GraphStorm. To install it you can use:
.. code-block:: bash
pip install dgl==2.3.0 -f https://data.dgl.ai/wheels/torch-2.3/repo.html
where you can replace ``torch-2.3`` with the version of PyTorch you have installed.
For more details on DGL installation see the `DGL docs `_.
If using one of the `GraphStorm images `_
for version ``v0.4``, you should already have the environment set up correctly.
To follow along this tutorial you will also need to have installed GraphStorm and cloned
the GraphStorm repository:
.. code-block:: bash
pip install graphstorm
git clone https://github.com/awslabs/graphstorm.git
cd graphstorm
.. note:: Ensure your environment is set up with enough shared memory.
Because DGL uses shared memory to store graph structures and features,
you need to ensure there's enough shared memory configured for your
system. For most Linux systems it's automatically set up as a ``tempfs`` volume
using part of main memory (``df -h | grep shm``). If however you're using a Docker container
to run the example, ensure you're giving the container enough shared memory
by mapping it to the host's shared memory, like:
``docker run -v /dev/shm:/dev/shm -it graphstorm:local-cpu /bin/bash``
Preparing data for use with GraphBolt
-------------------------------------
In order to use GraphBolt for training, we need to first convert our graph data to the GraphBolt
format.
If you are going to create the partitioned graph data using GConstuct or GSPartition, you can
simply provide the additional argument ``--use-graphbolt true`` when creating/partitioning your
graph.
We will be following along the example from :ref:`use-own-data` using the same dataset.
Once we have set up our environment as above, and our current working directory is the
root of the GraphStorm repository we can run:
.. code-block:: bash
python ./examples/acm_data.py --output-path /tmp/acm_raw
As explained in the tutorial, this will create a set of "raw" data files which we
can use as input to GConstuct to create a partitioned graph.
Now let's generate a partitioned graph (with a single partition), along with GraphBolt
data:
.. code-block:: bash
python -m graphstorm.gconstruct.construct_graph \
--conf-file /tmp/acm_raw/config.json \
--num-parts 1 \
--graph-name acm \
--output-dir /tmp/acm_graphbolt \
--use-graphbolt true
The top level data will look exactly the same as when we run the above without setting ``--use-graphbolt true``:
.. code-block:: bash
ls /tmp/acm_graphbolt/
acm.json data_transform_new.json edge_label_stats.json edge_mapping.pt node_label_stats.json node_mapping.pt part0 raw_id_mappings
GraphBolt will create a modified representation of each graph partition:
.. code-block:: bash
ls /tmp/acm_graphbolt/part0
edge_feat.dgl fused_csc_sampling_graph.pt graph.dgl node_feat.dgl
The file ``fused_csc_sampling_graph.pt`` is the new representation that GraphBolt will use during training
to optimize data loading.
GSPartition with GraphBolt
^^^^^^^^^^^^^^^^^^^^^^^^^^
Similar to GConstuct, when running distributed graph partitioning with the GSPartition module you can
provide the ``--use-graphbolt true`` argument to convert the resulting partitions to GraphBolt
at the end of partitioning. You'll need your data to be in DGL's "chunked graph format",
which can be produced by feeding your raw data and GConstuct configuration to
:ref:`GSProcessing `.
.. code-block:: bash
python -m graphstorm.gpartition.dist_partition_graph \
--input-path "${INPUT_PATH}" \
--ip-config ip_list.txt \
--metadata-filename chunked_graph_meta.json \
--num-parts 2 \
--output-path "$DIST_GRAPHBOLT_PATH" \
--ssh-port 2222 \
--use-graphbolt true
Running training and inference tasks with GraphBolt enabled
-----------------------------------------------------------
Now that we have prepared our data we can run a training job, with GraphBolt enabled.
We run the :ref:`same node classification task as in the original guide `
by adding the `--use-graphbolt true` argument to enable GraphBolt:
.. code-block:: bash
python -m graphstorm.run.gs_node_classification \
--part-config /tmp/acm_graphbolt/acm.json \
--num-epochs 30 \
--num-trainers 1 \
--num-servers 1 \
--cf ./examples/use_your_own_data/acm_nc.yaml \
--save-model-path /tmp/acm_nc_graphbolt/models \
--node-feat-name paper:feat author:feat subject:feat \
--use-graphbolt true
Similarly, we can run an inference task with GraphBolt enabled as such:
.. code-block:: bash
python -m graphstorm.run.gs_node_classification \
--inference \
--part-config /tmp/acm_graphbolt/acm.json \
--num-trainers 1 \
--num-servers 1 \
--cf ./examples/use_your_own_data/acm_nc.yaml \
--node-feat-name paper:feat author:feat subject:feat \
--restore-model-path /tmp/acm_nc_baseline/models/epoch-29 \
--save-prediction-path /tmp/acm_nc_graphbolt/predictions \
--use-graphbolt true
Converting existing partitioned graphs to the GraphBolt format
--------------------------------------------------------------
If you have a partitioned graph that you would like to
run training or inference on, using GraphBolt, you can directly convert that
data using our GraphBolt conversion entry point:
.. code-block:: bash
# For testing, let's remove the converted graph file
rm /tmp/acm_graphbolt/part0/fused_csc_sampling_graph.pt
# Now let's run the standalone GraphBolt conversion
python -m graphstorm.gpartition.convert_to_graphbolt \
--metadata-filepath /tmp/acm_graphbolt/acm.json
# We'll see the GraphBolt representation has been re-created
ls /tmp/acm_graphbolt/part0
edge_feat.dgl fused_csc_sampling_graph.pt graph.dgl node_feat.dgl
Using GraphBolt on SageMaker
----------------------------
Before being able to train on SageMaker
we need to ensure our data on S3 have been
converted to the GraphBolt format.
When using GConstruct to process our data
we can include the GraphBolt data conversion in the GConstruct
step as we'll show below.
When using distributed graph construction with GSProcessing and GSPartition,
to prepare data to use with GraphBolt on SageMaker
we need to launch the GraphBolt data conversion step
as a separate SageMaker job, after
the partitioned DGL graph files have been created on S3.
After running your distributed partition SageMaker job as normal using
``sagemaker/launch_partition.py``, you next need to launch the
``sagemaker/launch_graphbolt_convert.py`` script, passing as input
the S3 URI, where the DistDGL partition data is stored by ``launch_partition.py``,
**plus the suffix `dist_graph`** as that's where GSPartition creates the partition files.
For example, if you used ``--output-data-s3 s3://my-bucket/my-part-graph`` for
``sagemaker/launch_partition.py`` you need to use ``--graph-data-s3 s3://my-bucket/my-part-graph/dist_graph``
for ``sagemaker/launch_graphbolt_convert.py``.
Without using GraphBolt a SageMaker job sequence for distributed processing and training
is ``GSProcessing -> GSPartition -> GSTraining``. To use GraphBolt we need to add
a step after partitioning and before training:
``GSProcessing -> GSPartition -> GraphBoltConvert -> GSTraining``.
.. code-block:: bash
cd graphstorm/sagemaker
sagemaker/launch_partition.py \
--graph-data-s3 "s3-uri-where-gsprocessing-data-exist" \
--output-data-s3 "s3-uri-where-gspartition-data-will-be"
# Add other required parameters like --partition-algorithm, --num-instances etc.
# Once the above job succeeds we run the following command to convert the data to GraphBolt format.
# Note the /dist_graph suffix!
sagemaker/launch_graphbolt_convert.py \
--graph-data-s3 "s3-uri-where-gspartition-data-will-be/dist_graph" \
--metadata-filename "metadata.json" # Or .json for gconstruct-ed partitions
If your data are small enough to process on a single SageMaker instance
using ``GConstuct``, you can simply pass the ``--use-graphbolt true`` argument
to the ``GConstruct`` SageMaker launch script and that will create the
necessary GraphBolt files as well.
So the job sequence there remains ``GConstruct -> GSTraining``.
.. code-block:: bash
sagemaker/launch_gconstruct.py \
--graph-data-s3 "s3-uri-where-raw-data-exist" \
--output-data-s3 "s3-uri-where-gspartition-data-will-be" \
--graph-config-file "gconstruct-config.json" \
--use-graphbolt true
If you initially used GConstruct to create the non-GraphBolt DistDGL files,
you'll need to pass in the additional argument ``--metadata-filename``
to ``launch_graphbolt_convert.py``.
Use ``.json`` where the graph name should be the
one you used with GConstruct as shown below:
.. code-block:: bash
# NOTE: we provide 'my-graph' as the graph name
sagemaker/launch_gconstruct.py \
--graph-name my-graph \
--graph-data-s3 "s3-uri-where-raw-data-exist" \
--output-data-s3 "s3-uri-where-gspartition-data-will-be" \
--graph-config-file "gconstruct-config.json" # We don't add --use-graphbolt true
# Once the above job succeeds we run the below to convert the data to GraphBolt
# NOTE: Our metadata file name will be named 'my-graph.json'
sagemaker/launch_graphbolt_convert.py \
--graph-data-s3 "s3-uri-where-gspartition-data-will-be"
--metadata-filename "my-graph.json" # Should be .json
Once the data have been converted to the GraphBolt format you can run your training
and inference jobs as before, passing the additional
argument ``--use-graphbolt`` to the SageMaker launch scripts
to indicate that we want to use GraphBolt during training/inference:
.. code-block:: bash
sagemaker/launch_train.py \
--graph-name my-graph \
--graph-data-s3 "s3-uri-where-gspartition-data-will-be" \
--yaml-s3 "s3-path-to-train-yaml" \
--use-graphbolt true
If you want to test steps locally you can use SageMaker's
[local mode](https://sagemaker.readthedocs.io/en/stable/overview.html#local-mode)
by providing `local` as the instance type in the launch scripts.