Commit 8f7abb5f authored by John Andrilla's avatar John Andrilla Committed by Minjie Wang
Browse files

[Doc] Edit for readability (#989)



* Edit for readability

Edit pass for grammar and style. 
* A great value-add would be to provide assumptions and prerequisites in the opening section. This helps readers understand what they need to have in place in order to make use of your tutorial steps.
* The wikidata knowledge graph could be improved with a smaller font for the Zuckerberg circle.

* Update tutorials/hetero/1_basics.py

* Update tutorials/hetero/1_basics.py
Co-Authored-By: default avatarAaron Markham <markhama@amazon.com>

* Update tutorials/hetero/1_basics.py
Co-Authored-By: default avatarAaron Markham <markhama@amazon.com>

* Update tutorials/hetero/1_basics.py
Co-Authored-By: default avatarAaron Markham <markhama@amazon.com>

* Update tutorials/hetero/1_basics.py
Co-Authored-By: default avatarAaron Markham <markhama@amazon.com>

* Update tutorials/hetero/1_basics.py
Co-Authored-By: default avatarAaron Markham <markhama@amazon.com>
parent 409183bc
...@@ -7,96 +7,94 @@ Working with Heterogeneous Graphs in DGL ...@@ -7,96 +7,94 @@ Working with Heterogeneous Graphs in DGL
**Author**: Quan Gan, `Minjie Wang <https://jermainewang.github.io/>`_, Mufei Li, **Author**: Quan Gan, `Minjie Wang <https://jermainewang.github.io/>`_, Mufei Li,
George Karypis, Zheng Zhang George Karypis, Zheng Zhang
Heterogeneous graphs, or **heterographs** for short, are graphs that contain In this tutorial, you learn about:
* Examples of heterogenous graph data and typical applications.
* Creating and manipulating a heterogenous graph in DGL.
* Implementing `Relational-GCN <https://arxiv.org/abs/1703.06103>`_, a popular GNN model,
for heterogenous graph input.
* Training a model to solve a node classification task.
Heterogeneous graphs, or *heterographs* for short, are graphs that contain
different types of nodes and edges. The different types of nodes and edges tend different types of nodes and edges. The different types of nodes and edges tend
to have different types of attributes that are designed to capture the to have different types of attributes that are designed to capture the
characteristics of each node and edge type. Moreoever, within the context of characteristics of each node and edge type. Within the context of
graph neural networks, depending on their complexity, certain node and edge types graph neural networks, depending on their complexity, certain node and edge types
may need to be modeled with representations that have different number of dimensions. might need to be modeled with representations that have a different number of dimensions.
DGL supports graph neural network computations on such heterogeneous graphs, by DGL supports graph neural network computations on such heterogeneous graphs, by
using the heterograph class and its associated API. using the heterograph class and its associated API.
In this tutorial, you will learn:
* Examples of heterogenous graph data and what are the typical applications?
* Create and manipulate a heterograph in DGL.
* Implement `Relational-GCN <https://arxiv.org/abs/1703.06103>`_, a popular GNN model,
for heterograph input.
* Train the model to solve a node classification task.
""" """
############################################################################### ###############################################################################
# Examples of Heterograph # Examples of heterographs
# ----------------------- # -----------------------
# Many real-world graph data represent relations among various types of entities. # Many graph datasets represent relationships among various types of entities.
# In this section, we give several real-world cases that can have their data # This section provides an overview for several graph use-cases that show such relationships
# represented as heterographs. # and can have their data represented as heterographs.
#
# **Citation graph** The `ACM dataset <https://aminer.org/citation>`_ contains two
# million papers, with their authors, publication venues and the other papers
# they cited. This information can be represented as a heterogeneous graph.
#
# Figure 1 depicts several entities in this dataset and the relations among them.
# This graph has three types of entities corresponding to
#
# * Papers,
# #
# * Authors, and # Citation graph
# ~~~~~~~~~~~~~~~
# The Association for Computing Machinery publishes an `ACM dataset <https://aminer.org/citation>`_ that contains two
# million papers, their authors, publication venues, and the other papers
# that were cited. This information can be represented as a heterogeneous graph.
# #
# * Publication venues # The following diagram shows several entities in the ACM dataset and the relationships among them
# (taken from `Shi et al., 2015 <https://arxiv.org/pdf/1511.04854.pdf>`_).
# #
# It also contain three types of edges connecting # .. figure:: https://s3.us-east-2.amazonaws.com/dgl.ai/tutorial/hetero/acm-example.png#
#
# This graph has three types of entities that correspond to papers, authors, and publication venues.
# It also contains three types of edges that connect the following:
# #
# * Authors with papers corresponding to *written-by* relations, # * Authors with papers corresponding to *written-by* relationships
# #
# * Papers with publication venues corresponding to *published-in* relations, and # * Papers with publication venues corresponding to *published-in* relationships
# #
# * Papers with other papers corresponding to *cited-by* relations. # * Papers with other papers corresponding to *cited-by* relationships
# #
# .. figure:: https://s3.us-east-2.amazonaws.com/dgl.ai/tutorial/hetero/acm-example.png
# #
# Figure 1. A heterograph modeling of some of the entities and relations in # Recommender systems
# the ACM dataset (taken from `Shi et al., 2015 <https://arxiv.org/pdf/1511.04854.pdf>`_). # ~~~~~~~~~~~~~~~~~~~~
# The datasets used in recommender systems often contain
# interactions between users and items. For example, the data could include the
# ratings that users have provided to movies. Such interactions can be modeled
# as heterographs.
# #
# **Recommender systems** The datasets used in recommender systems often contain # The nodes in these heterographs will have two types, *users* and *movies*. The edges
# interactions between users and items, such as those corresponding to the
# ratings that users have provided to movies. Such interactions also be modeled
# via heterographs.
#
# The nodes in those heterographs will have two types: *users* and *movies*. The edges
# will correspond to the user-movie interactions. Furthermore, if an interaction is # will correspond to the user-movie interactions. Furthermore, if an interaction is
# marked with a rating, then each rating value could correspond to a different edge type. # marked with a rating, then each rating value could correspond to a different edge type.
# Figure 2 shows an example. # The following diagram shows an example of user-item interactions as a heterograph.
# #
# .. figure:: https://s3.us-east-2.amazonaws.com/dgl.ai/tutorial/hetero/recsys-example.png # .. figure:: https://s3.us-east-2.amazonaws.com/dgl.ai/tutorial/hetero/recsys-example.png
# #
# Figure 2. User-item interactions modeled as a heterograph.
# #
# **Knowledge graph** Knowledge graphs are inherently heterogenous. For example in # Knowledge graph
# Wikidata, Barack Obama (item Q76) is an instance of human, which could be viewed as # ~~~~~~~~~~~~~~~~
# Knowledge graphs are inherently heterogenous. For example, in
# Wikidata, Barack Obama (item Q76) is an instance of a human, which could be viewed as
# the entity class, whose spouse (item P26) is Michelle Obama (item Q13133) and # the entity class, whose spouse (item P26) is Michelle Obama (item Q13133) and
# occupation (item P106) is politician (item Q82955). The relations are shown in Figure 3. # occupation (item P106) is politician (item Q82955). The relationships are shown in the following.
# diagram.
# #
# .. figure:: https://s3.us-east-2.amazonaws.com/dgl.ai/tutorial/hetero/kg-example.png # .. figure:: https://s3.us-east-2.amazonaws.com/dgl.ai/tutorial/hetero/kg-example.png
# #
# Figure 3. Wikidata knowledge graph.
############################################################################### ###############################################################################
# Creating a heterograph in DGL # Creating a heterograph in DGL
# ----------------------------- # -----------------------------
# One can create a heterograph in DGL using the :func:`dgl.heterograph` API. # You can create a heterograph in DGL using the :func:`dgl.heterograph` API.
# The argument to :func:`dgl.heterograph` is a dictionary. The keys are tuples # The argument to :func:`dgl.heterograph` is a dictionary. The keys are tuples
# in the form of ``(srctype, edgetype, dsttype)`` specifying the relation name # in the form of ``(srctype, edgetype, dsttype)`` specifying the relation name
# and the two entity types it connects. We call such tuples *canonical edge # and the two entity types it connects. Such tuples are called *canonical edge
# types*. The values are data to initialize the graph structures, i.e. which # types*. The values are data to initialize the graph structures, that is, which
# nodes the edges actually connect. # nodes the edges actually connect.
# #
# For instance, The following code creates the user-movie rating graph in # For instance, the following code creates the user-item interactions heterograph shown earlier.
# Figure 2.
# Each value of the dictionary is a list of edge tuples. # Each value of the dictionary is a list of edge tuples.
# Nodes are integer IDs starting from zero. Nodes IDs of different types have # Nodes are integer IDs starting from zero. Nodes IDs of different types have
...@@ -108,11 +106,10 @@ ratings = dgl.heterograph( ...@@ -108,11 +106,10 @@ ratings = dgl.heterograph(
('user', '-1', 'movie') : [(2, 1)]}) ('user', '-1', 'movie') : [(2, 1)]})
############################################################################### ###############################################################################
# DGL supports creating a graph from a variety of data sources: # DGL supports creating a graph from a variety of data sources. The following
# code creates the same graph as the above.
# the following codes create the same graph as the above #
# Creating from scipy matrix
# creating from scipy matrix
import scipy.sparse as sp import scipy.sparse as sp
plus1 = sp.coo_matrix(([1, 1, 1], ([0, 0, 1], [0, 1, 0])), shape=(3, 2)) plus1 = sp.coo_matrix(([1, 1, 1], ([0, 0, 1], [0, 1, 0])), shape=(3, 2))
minus1 = sp.coo_matrix(([1], ([2], [1])), shape=(3, 2)) minus1 = sp.coo_matrix(([1], ([2], [1])), shape=(3, 2))
...@@ -120,20 +117,20 @@ ratings = dgl.heterograph( ...@@ -120,20 +117,20 @@ ratings = dgl.heterograph(
{('user', '+1', 'movie') : plus1, {('user', '+1', 'movie') : plus1,
('user', '-1', 'movie') : minus1}) ('user', '-1', 'movie') : minus1})
# creating from networkx graph # Creating from networkx graph
import networkx as nx import networkx as nx
plus1 = nx.DiGraph() plus1 = nx.DiGraph()
plus1.add_nodes_from(['u0', 'u1', 'u2'], bipartite=0) plus1.add_nodes_from(['u0', 'u1', 'u2'], bipartite=0)
plus1.add_nodes_from(['m0', 'm1'], bipartite=1) plus1.add_nodes_from(['m0', 'm1'], bipartite=1)
plus1.add_edges_from([('u0', 'm0'), ('u0', 'm1'), ('u1', 'm0')]) plus1.add_edges_from([('u0', 'm0'), ('u0', 'm1'), ('u1', 'm0')])
# To simplify the example, we reuse the minus1 object. # To simplify the example, reuse the minus1 object.
# This also means that you could use different sources of graph data # This also means that you could use different sources of graph data
# for different relations. # for different relationships.
ratings = dgl.heterograph( ratings = dgl.heterograph(
{('user', '+1', 'movie') : plus1, {('user', '+1', 'movie') : plus1,
('user', '-1', 'movie') : minus1}) ('user', '-1', 'movie') : minus1})
# creating from edge indices # Creating from edge indices
ratings = dgl.heterograph( ratings = dgl.heterograph(
{('user', '+1', 'movie') : ([0, 0, 1], [0, 1, 0]), {('user', '+1', 'movie') : ([0, 0, 1], [0, 1, 0]),
('user', '-1', 'movie') : ([2], [1])}) ('user', '-1', 'movie') : ([2], [1])})
...@@ -141,8 +138,8 @@ ratings = dgl.heterograph( ...@@ -141,8 +138,8 @@ ratings = dgl.heterograph(
############################################################################### ###############################################################################
# Manipulating heterograph # Manipulating heterograph
# ------------------------ # ------------------------
# Let us create a more realistic heterograph using the ACM dataset. First we # You can create a more realistic heterograph using the ACM dataset. To do this, first
# need to download the dataset as follows: # download the dataset as follows:
import scipy.io import scipy.io
import urllib.request import urllib.request
...@@ -156,11 +153,11 @@ print(list(data.keys())) ...@@ -156,11 +153,11 @@ print(list(data.keys()))
############################################################################### ###############################################################################
# The dataset stores node information by their types: ``P`` for paper, ``A`` # The dataset stores node information by their types: ``P`` for paper, ``A``
# for author, ``C`` for conference, ``L`` for subject code, etc. The relations # for author, ``C`` for conference, ``L`` for subject code, and so on. The relationships
# are stored as scipy sparse matrix under key ``XvsY``, where ``X`` and ``Y`` # are stored as SciPy sparse matrix under key ``XvsY``, where ``X`` and ``Y``
# could be any of the node type codes. # could be any of the node type code.
# #
# The following codes print out some statistics about the paper-author relation. # The following code prints out some statistics about the paper-author relationships.
print(type(data['PvsA'])) print(type(data['PvsA']))
print('#Papers:', data['PvsA'].shape[0]) print('#Papers:', data['PvsA'].shape[0])
...@@ -168,21 +165,21 @@ print('#Authors:', data['PvsA'].shape[1]) ...@@ -168,21 +165,21 @@ print('#Authors:', data['PvsA'].shape[1])
print('#Links:', data['PvsA'].nnz) print('#Links:', data['PvsA'].nnz)
############################################################################### ###############################################################################
# Converting this scipy matrix to a heterograph in DGL is straightforward: # Converting this SciPy matrix to a heterograph in DGL is straightforward.
pa_g = dgl.heterograph({('paper', 'written-by', 'author') : data['PvsA']}) pa_g = dgl.heterograph({('paper', 'written-by', 'author') : data['PvsA']})
# equivalent (shorter) API for creating heterograph with two node types: # equivalent (shorter) API for creating heterograph with two node types:
pa_g = dgl.bipartite(data['PvsA'], 'paper', 'written-by', 'author') pa_g = dgl.bipartite(data['PvsA'], 'paper', 'written-by', 'author')
############################################################################### ###############################################################################
# We can easily print out the type names and other structural information. # You can easily print out the type names and other structural information.
print('Node types:', pa_g.ntypes) print('Node types:', pa_g.ntypes)
print('Edge types:', pa_g.etypes) print('Edge types:', pa_g.etypes)
print('Canonical edge types:', pa_g.canonical_etypes) print('Canonical edge types:', pa_g.canonical_etypes)
# Nodes/edges are assigned integer IDs starting from zero and each type has its own counting. # Nodes and edges are assigned integer IDs starting from zero and each type has its own counting.
# To distinguish the nodes/edges of different types, specify the type name as the argument. # To distinguish the nodes and edges of different types, specify the type name as the argument.
print(pa_g.number_of_nodes('paper')) print(pa_g.number_of_nodes('paper'))
# Canonical edge type name can be shortened to only one edge type name if it is # Canonical edge type name can be shortened to only one edge type name if it is
# uniquely distinguishable. # uniquely distinguishable.
...@@ -191,27 +188,27 @@ print(pa_g.number_of_edges('written-by')) ...@@ -191,27 +188,27 @@ print(pa_g.number_of_edges('written-by'))
print(pa_g.successors(1, etype='written-by')) # get the authors that write paper #1 print(pa_g.successors(1, etype='written-by')) # get the authors that write paper #1
# Type name argument could be omitted whenever the behavior is unambiguous. # Type name argument could be omitted whenever the behavior is unambiguous.
print(pa_g.number_of_edges()) # only one edge type, the edge type argument could be omitted print(pa_g.number_of_edges()) # Only one edge type, the edge type argument could be omitted
############################################################################### ###############################################################################
# Homogeneous graph is just a special case of a heterograph with only one type # A homogeneous graph is just a special case of a heterograph with only one type
# of nodes and edges. In this case, all the APIs are exactly the same as in # of node and edge. In this case, all the APIs are exactly the same as in
# :class:`DGLGraph`. # :class:`DGLGraph`.
# paper-citing-paper graph is a homogeneous graph # Paper-citing-paper graph is a homogeneous graph
pp_g = dgl.heterograph({('paper', 'citing', 'paper') : data['PvsP']}) pp_g = dgl.heterograph({('paper', 'citing', 'paper') : data['PvsP']})
# equivalent (shorter) API for creating homogeneous graph # equivalent (shorter) API for creating homogeneous graph
pp_g = dgl.graph(data['PvsP'], 'paper', 'cite') pp_g = dgl.graph(data['PvsP'], 'paper', 'cite')
# All the ntype and etype argument could be omitted because the behavior is unambiguous. # All the ntype and etype arguments could be omitted because the behavior is unambiguous.
print(pp_g.number_of_nodes()) print(pp_g.number_of_nodes())
print(pp_g.number_of_edges()) print(pp_g.number_of_edges())
print(pp_g.successors(3)) print(pp_g.successors(3))
############################################################################### ###############################################################################
# We then create a subset of the ACM graph using the paper-author, paper-paper # Create a subset of the ACM graph using the paper-author, paper-paper,
# and paper-subject relations. Meanwhile, we should also add the reverse # and paper-subject relationships. Meanwhile, also add the reverse
# relations to prepare for the later sections. # relationship to prepare for the later sections.
G = dgl.heterograph({ G = dgl.heterograph({
('paper', 'written-by', 'author') : data['PvsA'], ('paper', 'written-by', 'author') : data['PvsA'],
...@@ -230,9 +227,9 @@ print(G) ...@@ -230,9 +227,9 @@ print(G)
# exist in the network and where the possible links exist. # exist in the network and where the possible links exist.
# #
# DGL provides easy access to the metagraph, which could be visualized using # DGL provides easy access to the metagraph, which could be visualized using
# external tools: # external tools.
# draw the metagraph using graphviz # Draw the metagraph using graphviz.
import pygraphviz as pgv import pygraphviz as pgv
def plot_graph(nxg): def plot_graph(nxg):
ag = pgv.AGraph(strict=False, directed=True) ag = pgv.AGraph(strict=False, directed=True)
...@@ -248,14 +245,14 @@ plot_graph(G.metagraph) ...@@ -248,14 +245,14 @@ plot_graph(G.metagraph)
# ------------------------------------------- # -------------------------------------------
# Some of the typical learning tasks that involve heterographs include: # Some of the typical learning tasks that involve heterographs include:
# #
# * *Node classification/regression*, to predict the class of each node or # * *Node classification and regression* to predict the class of each node or
# estimate a value associated with it. # estimate a value associated with it.
# #
# * *Link prediction*: The task is to predict if there is an edge of a certain # * *Link prediction* to predict if there is an edge of a certain
# type between a pair of nodes, or predict which other nodes a particular # type between a pair of nodes, or predict which other nodes a particular
# node is connected with (and optionally the edge types of such connections). # node is connected with (and optionally the edge types of such connections).
# #
# * *Graph classification/regression*: The task is to assign an entire # * *Graph classification/regression* to assign an entire
# heterograph into one of the target classes or to estimate a numerical # heterograph into one of the target classes or to estimate a numerical
# value associated with it. # value associated with it.
# #
...@@ -268,8 +265,8 @@ plot_graph(G.metagraph) ...@@ -268,8 +265,8 @@ plot_graph(G.metagraph)
# on papers published in three conferences: *KDD*, *ICML*, and *VLDB*. All # on papers published in three conferences: *KDD*, *ICML*, and *VLDB*. All
# the other papers are not labeled, making it a semi-supervised setting. # the other papers are not labeled, making it a semi-supervised setting.
# #
# The following codes extract those papers from the raw dataset and prepare # The following code extracts those papers from the raw dataset and prepares
# the training/validation/testing split. # the training, validation, testing split.
import numpy as np import numpy as np
import torch import torch
...@@ -297,7 +294,7 @@ test_idx = torch.tensor(shuffle[900:]).long() ...@@ -297,7 +294,7 @@ test_idx = torch.tensor(shuffle[900:]).long()
# Relational-GCN on heterograph # Relational-GCN on heterograph
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# We use `Relational-GCN <https://arxiv.org/abs/1703.06103>`_ to learn the # We use `Relational-GCN <https://arxiv.org/abs/1703.06103>`_ to learn the
# representation of nodes in the graph. Its message passing equation is as # representation of nodes in the graph. Its message-passing equation is as
# follows: # follows:
# #
# .. math:: # .. math::
...@@ -305,19 +302,19 @@ test_idx = torch.tensor(shuffle[900:]).long() ...@@ -305,19 +302,19 @@ test_idx = torch.tensor(shuffle[900:]).long()
# h_i^{(l+1)} = \sigma\left(\sum_{r\in \mathcal{R}} # h_i^{(l+1)} = \sigma\left(\sum_{r\in \mathcal{R}}
# \sum_{j\in\mathcal{N}_r(i)}W_r^{(l)}h_j^{(l)}\right) # \sum_{j\in\mathcal{N}_r(i)}W_r^{(l)}h_j^{(l)}\right)
# #
# Breaking down the equation, we see that there are two parts in the # Breaking down the equation, you see that there are two parts in the
# computation: # computation.
# #
# (i) message computation and aggregation within each relation :math:`r`, and # (i) Message computation and aggregation within each relation :math:`r`
# #
# (ii) reduction that merges the results from multiple relations. # (ii) Reduction that merges the results from multiple relationships
# #
# Following this intuition, we perform message passing on a heterograph in # Following this intuition, perform message passing on a heterograph in
# two steps: # two steps.
# #
# (i) per-edge-type message passing, and # (i) Per-edge-type message passing
# #
# (ii) type wise reduction: # (ii) Type wise reduction
import dgl.function as fn import dgl.function as fn
...@@ -350,8 +347,8 @@ class HeteroRGCNLayer(nn.Module): ...@@ -350,8 +347,8 @@ class HeteroRGCNLayer(nn.Module):
return {ntype : G.nodes[ntype].data['h'] for ntype in G.ntypes} return {ntype : G.nodes[ntype].data['h'] for ntype in G.ntypes}
############################################################################### ###############################################################################
# We then create a simple GNN by stacking two ``HeteroRGCNLayer``. Since the # Create a simple GNN by stacking two ``HeteroRGCNLayer``. Since the
# nodes do not have input features, we simply make their embeddings trainable. # nodes do not have input features, make their embeddings trainable.
class HeteroRGCN(nn.Module): class HeteroRGCN(nn.Module):
def __init__(self, G, in_size, hidden_size, out_size): def __init__(self, G, in_size, hidden_size, out_size):
...@@ -376,9 +373,9 @@ class HeteroRGCN(nn.Module): ...@@ -376,9 +373,9 @@ class HeteroRGCN(nn.Module):
############################################################################### ###############################################################################
# Train and evaluate # Train and evaluate
# ~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~
# We then train and evaluate this network. # Train and evaluate this network.
# Create the model. The output has 3 logits for 3 classes. # Create the model. The output has three logits for three classes.
model = HeteroRGCN(G, 10, 10, 3) model = HeteroRGCN(G, 10, 10, 3)
opt = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4) opt = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment