rgcn.py 5.4 KB
Newer Older
Z
Zhong Hui 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import paddle
import pgl
import numpy as np
Z
Zhong Hui 已提交
18 19
import paddle.fluid as fluid
from paddle.fluid.contrib import summary
Z
Zhong Hui 已提交
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41


def rgcn_conv(graph_wrapper,
              feature,
              hidden_size,
              edge_types,
              name="rgcn_conv"):
    def __message(src_feat, dst_feat, edge_feat):
        """send function
        """
        return src_feat['h']

    def __reduce(feat):
        """recv function
        """
        return fluid.layers.sequence_pool(feat, pool_type='average')

    if not isinstance(edge_types, list):
        edge_types = [edge_types]

    output = None
    for i in range(len(edge_types)):
Z
Zhong Hui 已提交
42
        tmp_feat = fluid.layers.fc(
Z
Zhong Hui 已提交
43 44
            feature,
            size=hidden_size,
Z
Zhong Hui 已提交
45 46
            param_attr=fluid.ParamAttr(name='%s_node_fc_%s' %
                                       (name, edge_types[i].split("2")[0])),
Z
Zhong Hui 已提交
47 48
            act=None)
        if output is None:
Z
Zhong Hui 已提交
49
            output = fluid.layers.zeros_like(tmp_feat)
Z
Zhong Hui 已提交
50 51 52
        msg = graph_wrapper[edge_types[i]].send(
            __message, nfeat_list=[('h', tmp_feat)])
        neigh_feat = graph_wrapper[edge_types[i]].recv(msg, __reduce)
Z
Zhong Hui 已提交
53 54 55 56 57
        # The weight of FC should be the same for the same type of node
        # The edge type str should be `A2B`(from type A to type B)
        neigh_feat = fluid.layers.fc(
            neigh_feat,
            size=hidden_size,
Z
Zhong Hui 已提交
58 59
            param_attr=fluid.ParamAttr(name='%s_edge_fc_%s' %
                                       (name, edge_types[i])),
Z
Zhong Hui 已提交
60
            act=None)
Z
Zhong Hui 已提交
61
        # TODO: the tmp_feat and neigh_feat should be add togather.
Z
Zhong Hui 已提交
62
        output = output + neigh_feat * tmp_feat
Z
Zhong Hui 已提交
63

Z
Zhong Hui 已提交
64 65 66 67
    return output


class RGCNModel:
Z
Zhong Hui 已提交
68 69 70 71 72
    def __init__(self, graph_wrapper, num_layers, hidden_size, num_class,
                 edge_types):
        self.graph_wrapper = graph_wrapper
        self.num_layers = num_layers
        self.hidden_size = hidden_size
Z
Zhong Hui 已提交
73
        self.num_class = num_class
Z
Zhong Hui 已提交
74
        self.edge_types = edge_types
Z
Zhong Hui 已提交
75 76

    def forward(self, feat):
Z
Zhong Hui 已提交
77
        for i in range(self.num_layers - 1):
Z
Zhong Hui 已提交
78
            feat = rgcn_conv(
Z
Zhong Hui 已提交
79
                self.graph_wrapper,
Z
Zhong Hui 已提交
80 81 82 83 84 85 86
                feat,
                self.hidden_size,
                self.edge_types,
                name="rgcn_%d" % i)
            feat = fluid.layers.relu(feat)
            feat = fluid.layers.dropout(feat, dropout_prob=0.5)
        feat = rgcn_conv(
Z
Zhong Hui 已提交
87
            self.graph_wrapper,
Z
Zhong Hui 已提交
88 89 90
            feat,
            self.num_class,
            self.edge_types,
Z
Zhong Hui 已提交
91
            name="rgcn_%d" % (self.num_layers - 1))
Z
Zhong Hui 已提交
92 93 94
        return feat


Z
Zhong Hui 已提交
95
def cross_entropy_loss(logit, label):
Z
Zhong Hui 已提交
96 97 98
    loss = fluid.layers.softmax_with_cross_entropy(logit, label)
    loss = fluid.layers.mean(loss)
    acc = fluid.layers.accuracy(fluid.layers.softmax(logit), label)
Z
Zhong Hui 已提交
99
    return loss, acc
Z
Zhong Hui 已提交
100 101 102 103 104


if __name__ == "__main__":
    num_nodes = 4
    num_class = 2
Z
Zhong Hui 已提交
105 106 107 108
    feat_dim = 16
    hidden_size = 16
    num_layers = 2

Z
Zhong Hui 已提交
109 110 111 112 113 114
    node_types = [(0, 'user'), (1, 'user'), (2, 'item'), (3, 'item')]
    edges = {
        'U2U': [(0, 1), (1, 0)],
        'U2I': [(1, 2), (0, 3), (1, 3)],
        'I2I': [(2, 3), (3, 2)],
    }
Z
Zhong Hui 已提交
115
    node_feat = {'feature': np.random.randn(4, feat_dim)}
Z
Zhong Hui 已提交
116 117
    edges_feat = {
        'U2U': {
Z
Zhong Hui 已提交
118
            'h': np.random.randn(2, feat_dim)
Z
Zhong Hui 已提交
119 120
        },
        'U2I': {
Z
Zhong Hui 已提交
121
            'h': np.random.randn(3, feat_dim)
Z
Zhong Hui 已提交
122 123
        },
        'I2I': {
Z
Zhong Hui 已提交
124
            'h': np.random.randn(2, feat_dim)
Z
Zhong Hui 已提交
125 126 127 128 129 130 131 132
        },
    }
    g = pgl.heter_graph.HeterGraph(
        num_nodes=num_nodes,
        edges=edges,
        node_types=node_types,
        node_feat=node_feat,
        edge_feat=edges_feat)
Z
Zhong Hui 已提交
133

Z
Zhong Hui 已提交
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
    train_program = fluid.Program()
    startup_program = fluid.Program()
    test_program = fluid.Program()

    with fluid.program_guard(train_program, startup_program):
        label = fluid.layers.data(shape=[-1], dtype="int64", name='label')
        label = fluid.layers.reshape(label, [-1, 1])
        label.stop_gradient = True
        gw = pgl.heter_graph_wrapper.HeterGraphWrapper(
            name="heter_graph",
            edge_types=g.edge_types_info(),
            node_feat=g.node_feat_info(),
            edge_feat=g.edge_feat_info())

        feat = fluid.layers.create_parameter(
Z
Zhong Hui 已提交
149
            shape=[num_nodes, feat_dim], dtype='float32')
Z
Zhong Hui 已提交
150

Z
Zhong Hui 已提交
151 152 153 154 155
        model = RGCNModel(gw, num_layers, num_class, hidden_size,
                          g.edge_types_info())
        logit = model.forward(feat)
        loss, acc = cross_entropy_loss(logit, label)
        opt = fluid.optimizer.SGD(learning_rate=0.1)
Z
Zhong Hui 已提交
156 157
        opt.minimize(loss)

Z
Zhong Hui 已提交
158 159
    summary(train_program)
    place = fluid.CPUPlace()
Z
Zhong Hui 已提交
160 161
    exe = fluid.Executor(place)
    exe.run(startup_program)
Z
Zhong Hui 已提交
162

Z
Zhong Hui 已提交
163 164 165 166 167
    feed_dict = gw.to_feed(g)
    feed_dict['label'] = np.array([1, 0, 1, 1]).astype('int64')
    for i in range(100):
        res = exe.run(train_program,
                      feed=feed_dict,
Z
Zhong Hui 已提交
168 169
                      fetch_list=[loss.name, acc.name])
        print("STEP: %d  LOSS: %f  ACC: %f" % (i, res[0], res[1]))