The matrix form of GCN update rule is as follows:
H^{(l+1)}=\tilde{D}^{-\frac{1}{2}}\tilde{A}\tilde{D}^{-\frac{1}{2}}H^{(l)}W^{(l)},
where:
-
H^{(l)}\in\mathbb{R}^{N\times F_l} is the node features after the l-th GCN layer, N is the number of nodes, F_l is the feature size for H^{(l)}. H^{(0)} is the input node features.
-
W^{(l)}\in\mathbb{R}^{F_l\times F_{l+1}} is the learnable weight in the l-th GCN layer.
-
\tilde{A}=A+I, where A is the adjacency matrix.
-
\tilde{D}=\tilde{A}\textbf{1} is the degree matrix corresponding to \tilde{A}, \textbf{1}\in\mathbb{R}^{N\times 1} is a vector whose entries are all 1.
Element-wise, the update rule for a node v is then
H_{v, :}^{(l+1)}=\sum_{u\in\mathcal{N}(v)}\frac{1}{\sqrt{\tilde{d}_u\tilde{d}_{v}}}W^{(l)}H_{u, :}^{(l)}, where \tilde{d}_u=\tilde{D}_{uu}.
degs = graph.in_degrees().float().clamp(min=1)
computes the diagonal of A\textbf{1} (it’s possible that self loops have been added already, which becomes \tilde{A}\textbf{1}). In case there are isolated nodes, it replaces 0-valued entries by 1-valued entries.
norm = th.pow(degs, -0.5)
raises each element in degs
to power -\frac{1}{2}, corresponding to \tilde{D}^{-\frac{1}{2}}.
feat = feat * norm
replaces h^{(l)}_u by \frac{1}{\sqrt{degs[u]}}h^{(l)}_u, corresponding to \frac{1}{\sqrt{\tilde{d_u}}}h^{(l)}_u.