1_introduction.py 7.15 KB
Newer Older
1
"""
2
3
Node Classification with DGL
============================
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

GNNs are powerful tools for many machine learning tasks on graphs. In
this introductory tutorial, you will learn the basic workflow of using
GNNs for node classification, i.e. predicting the category of a node in
a graph.

By completing this tutorial, you will be able to

-  Load a DGL-provided dataset.
-  Build a GNN model with DGL-provided neural network modules.
-  Train and evaluate a GNN model for node classification on either CPU
   or GPU.

This tutorial assumes that you have experience in building neural
networks with PyTorch.

(Time estimate: 13 minutes)

"""

24
25
import os
os.environ['DGLBACKEND'] = 'pytorch'
26
27
28
29
import torch
import torch.nn as nn
import torch.nn.functional as F

30
31
import dgl
import dgl.data
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

######################################################################
# Overview of Node Classification with GNN
# ----------------------------------------
#
# One of the most popular and widely adopted tasks on graph data is node
# classification, where a model needs to predict the ground truth category
# of each node. Before graph neural networks, many proposed methods are
# using either connectivity alone (such as DeepWalk or node2vec), or simple
# combinations of connectivity and the node's own features.  GNNs, by
# contrast, offers an opportunity to obtain node representations by
# combining the connectivity and features of a *local neighborhood*.
#
# `Kipf et
# al., <https://arxiv.org/abs/1609.02907>`__ is an example that formulates
# the node classification problem as a semi-supervised node classification
# task. With the help of only a small portion of labeled nodes, a graph
# neural network (GNN) can accurately predict the node category of the
# others.
51
#
52
53
54
55
56
57
58
59
# This tutorial will show how to build such a GNN for semi-supervised node
# classification with only a small number of labels on the Cora
# dataset,
# a citation network with papers as nodes and citations as edges. The task
# is to predict the category of a given paper. Each paper node contains a
# word count vector as its features, normalized so that they sum up to one,
# as described in Section 5.2 of
# `the paper <https://arxiv.org/abs/1609.02907>`__.
60
#
61
62
# Loading Cora Dataset
# --------------------
63
#
64
65
66


dataset = dgl.data.CoraGraphDataset()
67
print("Number of categories:", dataset.num_classes)
68
69
70
71
72


######################################################################
# A DGL Dataset object may contain one or multiple graphs. The Cora
# dataset used in this tutorial only consists of one single graph.
73
#
74
75
76
77
78
79
80
81

g = dataset[0]


######################################################################
# A DGL graph can store node features and edge features in two
# dictionary-like attributes called ``ndata`` and ``edata``.
# In the DGL Cora dataset, the graph contains the following node features:
82
#
83
84
85
86
87
88
89
90
91
92
93
94
# - ``train_mask``: A boolean tensor indicating whether the node is in the
#   training set.
#
# - ``val_mask``: A boolean tensor indicating whether the node is in the
#   validation set.
#
# - ``test_mask``: A boolean tensor indicating whether the node is in the
#   test set.
#
# - ``label``: The ground truth node category.
#
# -  ``feat``: The node features.
95
#
96

97
print("Node features")
98
print(g.ndata)
99
print("Edge features")
100
101
102
103
104
105
print(g.edata)


######################################################################
# Defining a Graph Convolutional Network (GCN)
# --------------------------------------------
106
#
107
108
109
110
# This tutorial will build a two-layer `Graph Convolutional Network
# (GCN) <http://tkipf.github.io/graph-convolutional-networks/>`__. Each
# layer computes new node representations by aggregating neighbor
# information.
111
#
112
113
# To build a multi-layer GCN you can simply stack ``dgl.nn.GraphConv``
# modules, which inherit ``torch.nn.Module``.
114
#
115
116
117

from dgl.nn import GraphConv

118

119
120
121
122
123
class GCN(nn.Module):
    def __init__(self, in_feats, h_feats, num_classes):
        super(GCN, self).__init__()
        self.conv1 = GraphConv(in_feats, h_feats)
        self.conv2 = GraphConv(h_feats, num_classes)
124

125
126
127
128
129
    def forward(self, g, in_feat):
        h = self.conv1(g, in_feat)
        h = F.relu(h)
        h = self.conv2(g, h)
        return h
130
131


132
# Create the model with given dimensions
133
model = GCN(g.ndata["feat"].shape[1], 16, dataset.num_classes)
134
135
136
137
138


######################################################################
# DGL provides implementation of many popular neighbor aggregation
# modules. You can easily invoke them with one line of code.
139
#
140
141
142
143
144


######################################################################
# Training the GCN
# ----------------
145
#
146
# Training this GCN is similar to training other PyTorch neural networks.
147
148
#

149
150
151
152
153
154

def train(g, model):
    optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
    best_val_acc = 0
    best_test_acc = 0

155
156
157
158
159
    features = g.ndata["feat"]
    labels = g.ndata["label"]
    train_mask = g.ndata["train_mask"]
    val_mask = g.ndata["val_mask"]
    test_mask = g.ndata["test_mask"]
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
    for e in range(100):
        # Forward
        logits = model(g, features)

        # Compute prediction
        pred = logits.argmax(1)

        # Compute loss
        # Note that you should only compute the losses of the nodes in the training set.
        loss = F.cross_entropy(logits[train_mask], labels[train_mask])

        # Compute accuracy on training/validation/test
        train_acc = (pred[train_mask] == labels[train_mask]).float().mean()
        val_acc = (pred[val_mask] == labels[val_mask]).float().mean()
        test_acc = (pred[test_mask] == labels[test_mask]).float().mean()

        # Save the best validation accuracy and the corresponding test accuracy.
        if best_val_acc < val_acc:
            best_val_acc = val_acc
            best_test_acc = test_acc

        # Backward
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if e % 5 == 0:
187
188
189
190
191
192
193
194
            print(
                "In epoch {}, loss: {:.3f}, val acc: {:.3f} (best {:.3f}), test acc: {:.3f} (best {:.3f})".format(
                    e, loss, val_acc, best_val_acc, test_acc, best_test_acc
                )
            )


model = GCN(g.ndata["feat"].shape[1], 16, dataset.num_classes)
195
196
197
198
199
200
train(g, model)


######################################################################
# Training on GPU
# ---------------
201
#
202
203
# Training on GPU requires to put both the model and the graph onto GPU
# with the ``to`` method, similar to what you will do in PyTorch.
204
#
205
206
207
208
209
210
# .. code:: python
#
#    g = g.to('cuda')
#    model = GCN(g.ndata['feat'].shape[1], 16, dataset.num_classes).to('cuda')
#    train(g, model)
#
211
212
213
214
215


######################################################################
# What’s next?
# ------------
216
#
217
218
219
220
221
222
223
224
225
# -  :doc:`How does DGL represent a graph <2_dglgraph>`?
# -  :doc:`Write your own GNN module <3_message_passing>`.
# -  :doc:`Link prediction (predicting existence of edges) on full
#    graph <4_link_predict>`.
# -  :doc:`Graph classification <5_graph_classification>`.
# -  :doc:`Make your own dataset <6_load_data>`.
# -  :ref:`The list of supported graph convolution
#    modules <apinn-pytorch>`.
# -  :ref:`The list of datasets provided by DGL <apidata>`.
226
#
227

228

229
# Thumbnail credits: Stanford CS224W Notes
230
# sphinx_gallery_thumbnail_path = '_static/blitz_1_introduction.png'