-
Notifications
You must be signed in to change notification settings - Fork 147
/
HPN.py
136 lines (106 loc) · 4.73 KB
/
HPN.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import dgl
import torch.nn as nn
from . import BaseModel, register_model
from dgl.nn.pytorch.conv import APPNPConv
from ..layers.MetapathConv import MetapathConv
from ..utils.utils import extract_metapaths
from ..layers.macro_layer.SemanticConv import SemanticAttention
@register_model('HPN')
class HPN(BaseModel):
r"""
This model shows an example of using dgl.metapath_reachable_graph on the original heterogeneous
graph.HPN from paper `Heterogeneous Graph Propagation Network
<https://ieeexplore.ieee.org/abstract/document/9428609>`__.
The author did not provide codes. So, we complete it according to the implementation of HAN
.. math::
\mathbf{Z}^{\Phi}=\mathcal{P}_{\Phi}(\mathbf{X})=g_\Phi(f_\Phi(\mathbf{X}))
where :math:`\mathbf{X}` denotes initial feature matrix and :math:`\mathbf{Z^\Phi}` denotes semantic-specific node embedding.
.. math::
\mathbf{H}^{\Phi}=f_\Phi(\mathbf{X})=\sigma(\mathbf{X} \cdot \mathbf{W}^\Phi+\mathbf{b}^{\Phi})
where :math:`\mathbf{H}^{\Phi}` is projected node feature matrix
.. math::
\mathbf{Z}^{\Phi, k}=g_{\Phi}\left(\mathbf{Z}^{\Phi, k-1}\right)=(1-\gamma) \cdot \mathbf{M}^{\Phi} \cdot \mathbf{Z}^{\Phi, k-1}+\gamma \cdot \mathbf{H}^{\Phi}
where :math:`\mathbf{Z}^{\Phi,k}` denotes node embeddings learned by k-th layer semantic propagation mechanism. :math:`\gamma` is a weight scalar which indicates the
importance of characteristic of node in aggregating process.
We use MetapathConv to finish Semantic Propagation and Semantic Fusion.
Parameters
------------
meta_paths : list
contain multiple meta-paths.
category : str
The category means the head and tail node of metapaths.
in_size : int
input feature dimension.
out_size : int
out dimension.
dropout : float
Dropout probability.
k_layer : int
propagation times.
alpha : float
Value of restart probability.
edge_drop : float, optional
The dropout rate on edges that controls the
messages received by each node. Default: ``0``.
"""
@classmethod
def build_model_from_args(cls, args, hg):
if args.meta_paths_dict is None:
meta_paths = extract_metapaths(args.category, hg.canonical_etypes)
else:
meta_paths = args.meta_paths_dict
return cls(meta_paths=meta_paths, category=args.out_node_type,
in_size=args.hidden_dim,
out_size=args.out_dim,
dropout=args.dropout,
k_layer=args.k_layer,
alpha=args.alpha,
edge_drop=args.edge_drop
)
def __init__(self, meta_paths, category, in_size, out_size, dropout, k_layer, alpha, edge_drop):
super(HPN, self).__init__()
self.category = category
self.layers = nn.ModuleList()
self.layers.append(HPNLayer(meta_paths, in_size, dropout, k_layer, alpha, edge_drop))
self.linear = nn.Linear(in_size, out_size)
def forward(self, g, h_dict):
for gnn in self.layers:
h_dict = gnn(g, h_dict)
out_dict = {ntype: self.linear(h_dict[ntype]) for ntype in self.category}
return out_dict
class HPNLayer(nn.Module):
def __init__(self, meta_paths_dict, in_size, dropout, k_layer, alpha, edge_drop):
super(HPNLayer, self).__init__()
# semantic projection function fΦ projects node into semantic space
self.hidden = nn.Sequential(
nn.Linear(in_features=in_size, out_features=in_size, bias=True),
nn.ReLU()
)
self.meta_paths_dict = meta_paths_dict
semantic_attention = SemanticAttention(in_size=in_size)
mods = nn.ModuleDict({mp: APPNPConv(k_layer, alpha, edge_drop) for mp in meta_paths_dict})
self.model = MetapathConv(meta_paths_dict, mods, semantic_attention)
self._cached_graph = None
self._cached_coalesced_graph = {}
def forward(self, g, h_dict):
r"""
Parameters
-----------
g : DGLHeteroGraph
The heterogeneous graph
h : tensor
The input features
Returns
--------
h : tensor
The output features
"""
h_dict = {ntype: self.hidden(h_dict[ntype]) for ntype in h_dict}
if self._cached_graph is None or self._cached_graph is not g:
self._cached_graph = g
self._cached_coalesced_graph.clear()
for mp, mp_value in self.meta_paths_dict.items():
self._cached_coalesced_graph[mp] = dgl.metapath_reachable_graph(
g, mp_value)
h_dict = self.model(self._cached_coalesced_graph, h_dict)
return h_dict