sgc_reddit.py 3.32 KB
Newer Older
Tianyi's avatar
Tianyi committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"""
This code was modified from the GCN implementation in DGL examples.
Simplifying Graph Convolutional Networks
Paper: https://arxiv.org/abs/1902.07153
Code: https://github.com/Tiiiger/SGC
SGC implementation in DGL.
"""
import argparse, time, math
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import dgl.function as fn
from dgl import DGLGraph
from dgl.data import register_data_args, load_data
16
from dgl.nn.pytorch.conv import SGConv
Tianyi's avatar
Tianyi committed
17

18
19
def normalize(h):
    return (h-h.mean(0))/h.std(0)
Tianyi's avatar
Tianyi committed
20

21
def evaluate(model, features, graph, labels, mask):
Tianyi's avatar
Tianyi committed
22
23
    model.eval()
    with torch.no_grad():
24
        logits = model(graph, features)[mask] # only compute the evaluation set
Tianyi's avatar
Tianyi committed
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
        labels = labels[mask]
        _, indices = torch.max(logits, dim=1)
        correct = torch.sum(indices == labels)
        return correct.item() * 1.0 / len(labels)

def main(args):
    # load and preprocess dataset
    args.dataset = "reddit-self-loop"
    data = load_data(args)
    features = torch.FloatTensor(data.features)
    labels = torch.LongTensor(data.labels)
    train_mask = torch.ByteTensor(data.train_mask)
    val_mask = torch.ByteTensor(data.val_mask)
    test_mask = torch.ByteTensor(data.test_mask)
    in_feats = features.shape[1]
    n_classes = data.num_labels
    n_edges = data.graph.number_of_edges()
    print("""----Data statistics------'
      #Edges %d
      #Classes %d
      #Train samples %d
      #Val samples %d
      #Test samples %d""" %
          (n_edges, n_classes,
           train_mask.sum().item(),
           val_mask.sum().item(),
           test_mask.sum().item()))

    if args.gpu < 0:
        cuda = False
    else:
        cuda = True
        torch.cuda.set_device(args.gpu)
        features = features.cuda()
        labels = labels.cuda()
        train_mask = train_mask.cuda()
        val_mask = val_mask.cuda()
        test_mask = test_mask.cuda()

    # graph preprocess and calculate normalization factor
    g = DGLGraph(data.graph)
    n_edges = g.number_of_edges()
    # normalization
    degs = g.in_degrees().float()
    norm = torch.pow(degs, -0.5)
    norm[torch.isinf(norm)] = 0
    if cuda: norm = norm.cuda()
    g.ndata['norm'] = norm.unsqueeze(1)

    # create SGC model
75
76
77
    model = SGConv(in_feats, n_classes, k=2, cached=True, bias=True, norm=normalize)
    if args.gpu >= 0:
        model = model.cuda()
Tianyi's avatar
Tianyi committed
78
79
80
81
82
83
84

    # use optimizer
    optimizer = torch.optim.LBFGS(model.parameters())

    # define loss closure
    def closure():
        optimizer.zero_grad()
85
        output = model(g, features)[train_mask]
Tianyi's avatar
Tianyi committed
86
87
88
89
90
91
92
        loss_train = F.cross_entropy(output, labels[train_mask])
        loss_train.backward()
        return loss_train

    # initialize graph
    for epoch in range(args.n_epochs):
        model.train()
93
        optimizer.step(closure)
Tianyi's avatar
Tianyi committed
94

95
    acc = evaluate(model, features, g, labels, test_mask)
Tianyi's avatar
Tianyi committed
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
    print("Test Accuracy {:.4f}".format(acc))


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='SGC')
    register_data_args(parser)
    parser.add_argument("--gpu", type=int, default=-1,
            help="gpu")
    parser.add_argument("--bias", action='store_true', default=False,
            help="flag to use bias")
    parser.add_argument("--n-epochs", type=int, default=2,
            help="number of training epochs")
    args = parser.parse_args()
    print(args)

    main(args)