chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
from .dhcf import DHCF
|
||||
from .hgnn import HGNN
|
||||
from .hgnnp import HGNNP
|
||||
from .hnhn import HNHN
|
||||
from .hwnn import HWNN
|
||||
from .hypergcn import HyperGCN
|
||||
from .setgnn import SetGNN
|
||||
from .unignn import UniGAT
|
||||
from .unignn import UniGCN
|
||||
from .unignn import UniGIN
|
||||
from .unignn import UniSAGE
|
||||
@@ -0,0 +1,95 @@
|
||||
from typing import Tuple
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
|
||||
from easygraph.classes import Hypergraph
|
||||
|
||||
|
||||
class DHCF(nn.Module):
|
||||
r"""The DHCF model proposed in `Dual Channel Hypergraph Collaborative Filtering <https://dl.acm.org/doi/10.1145/3394486.3403253>`_ paper (KDD 2020).
|
||||
|
||||
.. note::
|
||||
|
||||
The user and item embeddings and trainable parameters are initialized with xavier_uniform distribution.
|
||||
|
||||
Parameters:
|
||||
``num_users`` (``int``): The Number of users.
|
||||
``num_items`` (``int``): The Number of items.
|
||||
``emb_dim`` (``int``): Embedding dimension.
|
||||
``num_layers`` (``int``): The Number of layers. Defaults to ``3``.
|
||||
``drop_rate`` (``float``): The dropout probability. Defaults to ``0.5``.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
num_users: int,
|
||||
num_items: int,
|
||||
emb_dim: int,
|
||||
num_layers: int = 3,
|
||||
drop_rate: float = 0.5,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.num_users, self.num_items = num_users, num_items
|
||||
self.num_layers = num_layers
|
||||
self.drop_rate = drop_rate
|
||||
self.u_embedding = nn.Embedding(num_users, emb_dim)
|
||||
self.i_embedding = nn.Embedding(num_items, emb_dim)
|
||||
self.W_gc, self.W_bi = nn.ModuleList(), nn.ModuleList()
|
||||
for _ in range(self.num_layers):
|
||||
self.W_gc.append(nn.Linear(emb_dim, emb_dim))
|
||||
self.W_bi.append(nn.Linear(emb_dim, emb_dim))
|
||||
self.reset_parameters()
|
||||
|
||||
def reset_parameters(self):
|
||||
r"""Initialize learnable parameters."""
|
||||
nn.init.xavier_uniform_(self.u_embedding.weight)
|
||||
nn.init.xavier_uniform_(self.i_embedding.weight)
|
||||
for W_gc, W_bi in zip(self.W_gc, self.W_bi):
|
||||
nn.init.xavier_uniform_(W_gc.weight)
|
||||
nn.init.xavier_uniform_(W_bi.weight)
|
||||
nn.init.constant_(W_gc.bias, 0)
|
||||
nn.init.constant_(W_bi.bias, 0)
|
||||
|
||||
def forward(
|
||||
self, hg_ui: Hypergraph, hg_iu: Hypergraph
|
||||
) -> Tuple[torch.Tensor, torch.Tensor]:
|
||||
r"""The forward function.
|
||||
|
||||
Parameters:
|
||||
``hg_ui`` (``eg.Hypergraph``): The hypergraph structure that users as vertices.
|
||||
``hg_iu`` (``eg.Hypergraph``): The hypergraph structure that items as vertices.
|
||||
"""
|
||||
u_embs = self.u_embedding.weight
|
||||
i_embs = self.i_embedding.weight
|
||||
all_embs = torch.cat([u_embs, i_embs], dim=0)
|
||||
|
||||
embs_list = [all_embs]
|
||||
for _idx in range(self.num_layers):
|
||||
u_embs, i_embs = torch.split(
|
||||
all_embs, [self.num_users, self.num_items], dim=0
|
||||
)
|
||||
# ==========================================================================================
|
||||
# Two JHConv Layers for users and items, respectively.
|
||||
u_embs = hg_ui.smoothing_with_HGNN(u_embs)
|
||||
i_embs = hg_iu.smoothing_with_HGNN(i_embs)
|
||||
g_embs = torch.cat([u_embs, i_embs], dim=0)
|
||||
sum_embs = F.leaky_relu(
|
||||
self.W_gc[_idx](g_embs) + g_embs, negative_slope=0.2
|
||||
)
|
||||
# ==========================================================================================
|
||||
|
||||
bi_embs = all_embs * g_embs
|
||||
bi_embs = F.leaky_relu(self.W_bi[_idx](bi_embs), negative_slope=0.2)
|
||||
|
||||
all_embs = sum_embs + bi_embs
|
||||
all_embs = F.dropout(all_embs, p=self.drop_rate, training=self.training)
|
||||
all_embs = F.normalize(all_embs, p=2, dim=1)
|
||||
|
||||
embs_list.append(all_embs)
|
||||
embs = torch.stack(embs_list, dim=1)
|
||||
embs = torch.mean(embs, dim=1)
|
||||
|
||||
u_embs, i_embs = torch.split(embs, [self.num_users, self.num_items], dim=0)
|
||||
return u_embs, i_embs
|
||||
@@ -0,0 +1,70 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
|
||||
|
||||
class DHNE(nn.Module):
|
||||
r"""The DHNE model proposed in `Structural Deep Embedding for Hyper-Networks <https://arxiv.org/abs/1711.10146>`_ paper (AAAI 2018).
|
||||
|
||||
Parameters:
|
||||
``dim_feature`` (``int``): : feature dimension list ( len = 3)
|
||||
``embedding_size`` (``int``): :The embedding dimension size
|
||||
``hidden_size`` (``int``): The hidden full connected layer size.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, dim_feature, embedding_size, hidden_size):
|
||||
super(DHNE, self).__init__()
|
||||
self.dim_feature = dim_feature
|
||||
self.embedding_size = embedding_size
|
||||
self.hidden_size = hidden_size
|
||||
self.encode0 = nn.Sequential(
|
||||
nn.Linear(
|
||||
in_features=self.dim_feature[0], out_features=self.embedding_size[0]
|
||||
)
|
||||
)
|
||||
self.encode1 = nn.Sequential(
|
||||
nn.Linear(
|
||||
in_features=self.dim_feature[1], out_features=self.embedding_size[1]
|
||||
)
|
||||
)
|
||||
self.encode2 = nn.Sequential(
|
||||
nn.Linear(
|
||||
in_features=self.dim_feature[2], out_features=self.embedding_size[2]
|
||||
)
|
||||
)
|
||||
self.decode_layer0 = nn.Linear(
|
||||
in_features=self.embedding_size[0], out_features=self.dim_feature[0]
|
||||
)
|
||||
self.decode_layer1 = nn.Linear(
|
||||
in_features=self.embedding_size[1], out_features=self.dim_feature[1]
|
||||
)
|
||||
self.decode_layer2 = nn.Linear(
|
||||
in_features=self.embedding_size[2], out_features=self.dim_feature[2]
|
||||
)
|
||||
|
||||
self.hidden_layer = nn.Linear(
|
||||
in_features=sum(self.embedding_size), out_features=self.hidden_size
|
||||
)
|
||||
self.ouput_layer = nn.Linear(in_features=self.hidden_size, out_features=1)
|
||||
|
||||
def forward(self, input0, input1, input2):
|
||||
input0 = self.encode0(input0)
|
||||
input0 = torch.tanh(input0)
|
||||
decode0 = self.decode_layer0(input0)
|
||||
decode0 = torch.sigmoid(decode0)
|
||||
|
||||
input1 = self.encode1(input1)
|
||||
input1 = torch.tanh(input1)
|
||||
decode1 = self.decode_layer1(input1)
|
||||
decode1 = torch.sigmoid(decode1)
|
||||
|
||||
input2 = self.encode2(input2)
|
||||
input2 = torch.tanh(input2)
|
||||
decode2 = self.decode_layer2(input2)
|
||||
decode2 = torch.sigmoid(decode2)
|
||||
|
||||
merged = torch.tanh(torch.cat((input0, input1, input2), dim=1))
|
||||
merged = self.hidden_layer(merged)
|
||||
merged = self.ouput_layer(merged)
|
||||
merged = torch.sigmoid(merged)
|
||||
return [decode0, decode1, decode2, merged]
|
||||
@@ -0,0 +1,45 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
|
||||
from easygraph.nn import HGNNConv
|
||||
|
||||
|
||||
class HGNN(nn.Module):
|
||||
r"""The HGNN model proposed in `Hypergraph Neural Networks <https://arxiv.org/pdf/1809.09401>`_ paper (AAAI 2019).
|
||||
|
||||
Parameters:
|
||||
``in_channels`` (``int``): :math:`C_{in}` is the number of input channels.
|
||||
``hid_channels`` (``int``): :math:`C_{hid}` is the number of hidden channels.
|
||||
``num_classes`` (``int``): The Number of class of the classification task.
|
||||
``use_bn`` (``bool``): If set to ``True``, use batch normalization. Defaults to ``False``.
|
||||
``drop_rate`` (``float``, optional): Dropout ratio. Defaults to 0.5.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
in_channels: int,
|
||||
hid_channels: int,
|
||||
num_classes: int,
|
||||
use_bn: bool = False,
|
||||
drop_rate: float = 0.5,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.layers = nn.ModuleList()
|
||||
self.layers.append(
|
||||
HGNNConv(in_channels, hid_channels, use_bn=use_bn, drop_rate=drop_rate)
|
||||
)
|
||||
self.layers.append(
|
||||
HGNNConv(hid_channels, num_classes, use_bn=use_bn, is_last=True)
|
||||
)
|
||||
|
||||
def forward(self, X: torch.Tensor, hg: "Hypergraph") -> torch.Tensor:
|
||||
r"""The forward function.
|
||||
|
||||
Parameters:
|
||||
``X`` (``torch.Tensor``): Input vertex feature matrix. Size :math:`(N, C_{in})`.
|
||||
``hg`` (``eg.Hypergraph``): The hypergraph structure that contains :math:`N` vertices.
|
||||
"""
|
||||
|
||||
for layer in self.layers:
|
||||
X = layer(X, hg)
|
||||
return X
|
||||
@@ -0,0 +1,44 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
|
||||
from easygraph.nn import HGNNPConv
|
||||
|
||||
|
||||
class HGNNP(nn.Module):
|
||||
r"""The HGNN :sup:`+` model proposed in `HGNN+: General Hypergraph Neural Networks <https://ieeexplore.ieee.org/document/9795251>`_ paper (IEEE T-PAMI 2022).
|
||||
|
||||
Parameters:
|
||||
``in_channels`` (``int``): :math:`C_{in}` is the number of input channels.
|
||||
``hid_channels`` (``int``): :math:`C_{hid}` is the number of hidden channels.
|
||||
``num_classes`` (``int``): The Number of class of the classification task.
|
||||
``use_bn`` (``bool``): If set to ``True``, use batch normalization. Defaults to ``False``.
|
||||
``drop_rate`` (``float``, optional): Dropout ratio. Defaults to ``0.5``.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
in_channels: int,
|
||||
hid_channels: int,
|
||||
num_classes: int,
|
||||
use_bn: bool = False,
|
||||
drop_rate: float = 0.5,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.layers = nn.ModuleList()
|
||||
self.layers.append(
|
||||
HGNNPConv(in_channels, hid_channels, use_bn=use_bn, drop_rate=drop_rate)
|
||||
)
|
||||
self.layers.append(
|
||||
HGNNPConv(hid_channels, num_classes, use_bn=use_bn, is_last=True)
|
||||
)
|
||||
|
||||
def forward(self, X: torch.Tensor, hg: "eg.Hypergraph") -> torch.Tensor:
|
||||
r"""The forward function.
|
||||
|
||||
Parameters:
|
||||
``X`` (``torch.Tensor``): Input vertex feature matrix. Size :math:`(N, C_{in})`.
|
||||
``hg`` (``eg.Hypergraph``): The hypergraph structure that contains :math:`N` vertices.
|
||||
"""
|
||||
for layer in self.layers:
|
||||
X = layer(X, hg)
|
||||
return X
|
||||
@@ -0,0 +1,44 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
|
||||
from easygraph.nn import HNHNConv
|
||||
|
||||
|
||||
class HNHN(nn.Module):
|
||||
r"""The HNHN model proposed in `HNHN: Hypergraph Networks with Hyperedge Neurons <https://arxiv.org/pdf/2006.12278.pdf>`_ paper (ICML 2020).
|
||||
|
||||
Parameters:
|
||||
``in_channels`` (``int``): :math:`C_{in}` is the number of input channels.
|
||||
``hid_channels`` (``int``): :math:`C_{hid}` is the number of hidden channels.
|
||||
``num_classes`` (``int``): The Number of class of the classification task.
|
||||
``use_bn`` (``bool``): If set to ``True``, use batch normalization. Defaults to ``False``.
|
||||
``drop_rate`` (``float``, optional): Dropout ratio. Defaults to ``0.5``.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
in_channels: int,
|
||||
hid_channels: int,
|
||||
num_classes: int,
|
||||
use_bn: bool = False,
|
||||
drop_rate: float = 0.5,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.layers = nn.ModuleList()
|
||||
self.layers.append(
|
||||
HNHNConv(in_channels, hid_channels, use_bn=use_bn, drop_rate=drop_rate)
|
||||
)
|
||||
self.layers.append(
|
||||
HNHNConv(hid_channels, num_classes, use_bn=use_bn, is_last=True)
|
||||
)
|
||||
|
||||
def forward(self, X: torch.Tensor, hg: "eg.Hypergraph") -> torch.Tensor:
|
||||
r"""The forward function.
|
||||
|
||||
Parameters:
|
||||
``X`` (``torch.Tensor``): Input vertex feature matrix. Size :math:`(N, C_{in})`.
|
||||
``hg`` (``eg.Hypergraph``): The hypergraph structure that contains :math:`N` vertices.
|
||||
"""
|
||||
for layer in self.layers:
|
||||
X = layer(X, hg)
|
||||
return X
|
||||
@@ -0,0 +1,60 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
|
||||
from easygraph.nn import HWNNConv
|
||||
|
||||
|
||||
class HWNN(nn.Module):
|
||||
r"""The HWNN model proposed in `Heterogeneous Hypergraph Embedding for Graph Classification <https://arxiv.org/abs/2010.10728>`_ paper (WSDM 2021).
|
||||
|
||||
Parameters:
|
||||
``in_channels`` (``int``): Number of input feature channels. :math:`C_{in}` is the dimension of input features.
|
||||
``num_classes`` (``int``): Number of target classes for classification.
|
||||
``ncount`` (``int``): Total number of nodes in the hypergraph.
|
||||
``hyper_snapshot_num`` (``int``, optional): number of sementic snapshots for the given heterogeneous hypergraph.
|
||||
``hid_channels`` (``int``, optional): Number of hidden units. :math:`C_{hid}` is the dimension of hidden representations. Defaults to 128.
|
||||
``drop_rate`` (``float``, optional): Dropout probability for regularization. Defaults to 0.01.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
in_channels: int,
|
||||
num_classes: int,
|
||||
ncount: int,
|
||||
hyper_snapshot_num: int = 1,
|
||||
hid_channels: int = 128,
|
||||
drop_rate: float = 0.01,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.drop_rate = drop_rate
|
||||
self.convolution_1 = HWNNConv(
|
||||
in_channels, hid_channels, ncount, K1=3, K2=3, approx=True
|
||||
)
|
||||
self.convolution_2 = HWNNConv(
|
||||
hid_channels, num_classes, ncount, K1=3, K2=3, approx=True
|
||||
)
|
||||
self.par = torch.nn.Parameter(torch.Tensor(hyper_snapshot_num))
|
||||
torch.nn.init.uniform_(self.par, 0, 0.99)
|
||||
|
||||
def forward(self, X: torch.Tensor, hgs: list) -> torch.Tensor:
|
||||
r"""The forward function.
|
||||
|
||||
Parameters:
|
||||
``X`` (``torch.Tensor``): Input vertex feature matrix. Size :math:`(N, C_{in})`.
|
||||
``hg`` (``eg.Hypergraph``): The hypergraph structure that contains :math:`N` vertices.
|
||||
``hgs`` (``list`` of ``Hypergraph``): A list of hypergraph structures whcih stands for snapshots.
|
||||
"""
|
||||
channel = []
|
||||
hyper_snapshot_num = len(hgs)
|
||||
for snap_index in range(hyper_snapshot_num):
|
||||
hg = hgs[snap_index]
|
||||
Y = F.relu(self.convolution_1(X, hg))
|
||||
Y = F.dropout(Y, self.drop_rate)
|
||||
Y = self.convolution_2(Y, hg)
|
||||
Y = F.log_softmax(Y, dim=1)
|
||||
channel.append(Y)
|
||||
X = torch.zeros_like(channel[0])
|
||||
for ind in range(hyper_snapshot_num):
|
||||
X = X + self.par[ind] * channel[ind]
|
||||
return X
|
||||
@@ -0,0 +1,67 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
|
||||
from easygraph.classes import Hypergraph
|
||||
from easygraph.nn import HyperGCNConv
|
||||
|
||||
|
||||
class HyperGCN(nn.Module):
|
||||
r"""The HyperGCN model proposed in `HyperGCN: A New Method of Training Graph Convolutional Networks on Hypergraphs <https://papers.nips.cc/paper/2019/file/1efa39bcaec6f3900149160693694536-Paper.pdf>`_ paper (NeurIPS 2019).
|
||||
|
||||
Parameters:
|
||||
``in_channels`` (``int``): :math:`C_{in}` is the number of input channels.
|
||||
``hid_channels`` (``int``): :math:`C_{hid}` is the number of hidden channels.
|
||||
``num_classes`` (``int``): The Number of class of the classification task.
|
||||
``use_mediator`` (``str``): Whether to use mediator to transform the hyperedges to edges in the graph. Defaults to ``False``.
|
||||
``fast`` (``bool``): If set to ``True``, the transformed graph structure will be computed once from the input hypergraph and vertex features, and cached for future use. Defaults to ``True``.
|
||||
``drop_rate`` (``float``, optional): Dropout ratio. Defaults to 0.5.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
in_channels: int,
|
||||
hid_channels: int,
|
||||
num_classes: int,
|
||||
use_mediator: bool = False,
|
||||
use_bn: bool = False,
|
||||
fast: bool = True,
|
||||
drop_rate: float = 0.5,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.fast = fast
|
||||
self.cached_g = None
|
||||
self.with_mediator = use_mediator
|
||||
self.layers = nn.ModuleList()
|
||||
self.layers.append(
|
||||
HyperGCNConv(
|
||||
in_channels,
|
||||
hid_channels,
|
||||
use_mediator,
|
||||
use_bn=use_bn,
|
||||
drop_rate=drop_rate,
|
||||
)
|
||||
)
|
||||
self.layers.append(
|
||||
HyperGCNConv(
|
||||
hid_channels, num_classes, use_mediator, use_bn=use_bn, is_last=True
|
||||
)
|
||||
)
|
||||
|
||||
def forward(self, X: torch.Tensor, hg: "eg.Hypergraph") -> torch.Tensor:
|
||||
r"""The forward function.
|
||||
|
||||
Parameters:
|
||||
``X`` (``torch.Tensor``): Input vertex feature matrix. Size :math:`(N, C_{in})`.
|
||||
``hg`` (``eg.Hypergraph``): The hypergraph structure that contains :math:`N` vertices.
|
||||
"""
|
||||
if self.fast:
|
||||
if self.cached_g is None:
|
||||
self.cached_g = Hypergraph.from_hypergraph_hypergcn(
|
||||
hg, X, self.with_mediator
|
||||
)
|
||||
for layer in self.layers:
|
||||
X = layer(X, hg, self.cached_g)
|
||||
else:
|
||||
for layer in self.layers:
|
||||
X = layer(X, hg)
|
||||
return X
|
||||
@@ -0,0 +1,289 @@
|
||||
from collections import Counter
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
|
||||
from easygraph.nn.convs.common import MLP
|
||||
from easygraph.nn.convs.hypergraphs.halfnlh_conv import HalfNLHconv
|
||||
from torch.nn import Linear
|
||||
|
||||
|
||||
__all__ = ["SetGNN"]
|
||||
|
||||
|
||||
class SetGNN(nn.Module):
|
||||
r"""The SetGNN model proposed in `YOU ARE ALLSET: A MULTISET LEARNING FRAMEWORK FOR HYPERGRAPH NEURAL NETWORKS <https://openreview.net/pdf?id=hpBTIv2uy_E>`_ paper (ICLR 2022).
|
||||
|
||||
Parameters:
|
||||
``num_features`` (``int``): : The dimension of node features.
|
||||
``num_classes`` (``int``): The Number of class of the classification task.
|
||||
``Classifier_hidden`` (``int``): Decoder hidden units.
|
||||
``Classifier_num_layers`` (``int``): Layers of decoder.
|
||||
``MLP_hidden`` (``int``): Encoder hidden units.
|
||||
``MLP_num_layers`` (``int``): Layers of encoder.
|
||||
``dropout`` (``float``, optional): Dropout ratio. Defaults to 0.5.
|
||||
``aggregate`` (``str``): The aggregation method. Defaults to ``add``
|
||||
``normalization`` (``str``): The normalization method. Defaults to ``ln``
|
||||
``deepset_input_norm`` (``bool``): Defaults to True.
|
||||
``heads`` (``int``): Defaults to 1
|
||||
`PMA`` (``bool``): Defaults to True
|
||||
`GPR`` (``bool``): Defaults to False
|
||||
`LearnMask`` (``bool``): Defaults to False
|
||||
`norm`` (``Tensor``): The weight for edges in bipartite graphs, correspond to data.edge_index
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
num_features,
|
||||
num_classes,
|
||||
Classifier_hidden=64,
|
||||
Classifier_num_layers=2,
|
||||
MLP_hidden=64,
|
||||
MLP_num_layers=2,
|
||||
All_num_layers=2,
|
||||
dropout=0.5,
|
||||
aggregate="mean",
|
||||
normalization="ln",
|
||||
deepset_input_norm=True,
|
||||
heads=1,
|
||||
PMA=True,
|
||||
GPR=False,
|
||||
LearnMask=False,
|
||||
norm=None,
|
||||
self_loop=True,
|
||||
):
|
||||
super(SetGNN, self).__init__()
|
||||
"""
|
||||
args should contain the following:
|
||||
V_in_dim, V_enc_hid_dim, V_dec_hid_dim, V_out_dim, V_enc_num_layers, V_dec_num_layers
|
||||
E_in_dim, E_enc_hid_dim, E_dec_hid_dim, E_out_dim, E_enc_num_layers, E_dec_num_layers
|
||||
All_num_layers,dropout
|
||||
!!! V_in_dim should be the dimension of node features
|
||||
!!! E_out_dim should be the number of classes (for classification)
|
||||
"""
|
||||
|
||||
# Now set all dropout the same, but can be different
|
||||
self.All_num_layers = All_num_layers
|
||||
self.dropout = dropout
|
||||
self.aggr = aggregate
|
||||
self.NormLayer = normalization
|
||||
self.InputNorm = deepset_input_norm
|
||||
self.GPR = GPR
|
||||
self.LearnMask = LearnMask
|
||||
# Now define V2EConvs[i], V2EConvs[i] for ith layers
|
||||
# Currently we assume there's no hyperedge features, which means V_out_dim = E_in_dim
|
||||
# If there's hyperedge features, concat with Vpart decoder output features [V_feat||E_feat]
|
||||
self.V2EConvs = nn.ModuleList()
|
||||
self.E2VConvs = nn.ModuleList()
|
||||
self.bnV2Es = nn.ModuleList()
|
||||
self.bnE2Vs = nn.ModuleList()
|
||||
self.edge_index = None
|
||||
self.self_loop = self_loop
|
||||
if self.LearnMask:
|
||||
self.Importance = nn.Parameter(torch.ones(norm.size()))
|
||||
|
||||
if self.All_num_layers == 0:
|
||||
self.classifier = MLP(
|
||||
in_channels=num_features,
|
||||
hidden_channels=Classifier_hidden,
|
||||
out_channels=num_classes,
|
||||
num_layers=Classifier_num_layers,
|
||||
dropout=self.dropout,
|
||||
normalization=self.NormLayer,
|
||||
InputNorm=False,
|
||||
)
|
||||
else:
|
||||
self.V2EConvs.append(
|
||||
HalfNLHconv(
|
||||
in_dim=num_features,
|
||||
hid_dim=MLP_hidden,
|
||||
out_dim=MLP_hidden,
|
||||
num_layers=MLP_num_layers,
|
||||
dropout=self.dropout,
|
||||
normalization=self.NormLayer,
|
||||
InputNorm=self.InputNorm,
|
||||
heads=heads,
|
||||
attention=PMA,
|
||||
)
|
||||
)
|
||||
self.bnV2Es.append(nn.BatchNorm1d(MLP_hidden))
|
||||
self.E2VConvs.append(
|
||||
HalfNLHconv(
|
||||
in_dim=MLP_hidden,
|
||||
hid_dim=MLP_hidden,
|
||||
out_dim=MLP_hidden,
|
||||
num_layers=MLP_num_layers,
|
||||
dropout=self.dropout,
|
||||
normalization=self.NormLayer,
|
||||
InputNorm=self.InputNorm,
|
||||
heads=heads,
|
||||
attention=PMA,
|
||||
)
|
||||
)
|
||||
self.bnE2Vs.append(nn.BatchNorm1d(MLP_hidden))
|
||||
for _ in range(self.All_num_layers - 1):
|
||||
self.V2EConvs.append(
|
||||
HalfNLHconv(
|
||||
in_dim=MLP_hidden,
|
||||
hid_dim=MLP_hidden,
|
||||
out_dim=MLP_hidden,
|
||||
num_layers=MLP_num_layers,
|
||||
dropout=self.dropout,
|
||||
normalization=self.NormLayer,
|
||||
InputNorm=self.InputNorm,
|
||||
heads=heads,
|
||||
attention=PMA,
|
||||
)
|
||||
)
|
||||
self.bnV2Es.append(nn.BatchNorm1d(MLP_hidden))
|
||||
self.E2VConvs.append(
|
||||
HalfNLHconv(
|
||||
in_dim=MLP_hidden,
|
||||
hid_dim=MLP_hidden,
|
||||
out_dim=MLP_hidden,
|
||||
num_layers=MLP_num_layers,
|
||||
dropout=self.dropout,
|
||||
normalization=self.NormLayer,
|
||||
InputNorm=self.InputNorm,
|
||||
heads=heads,
|
||||
attention=PMA,
|
||||
)
|
||||
)
|
||||
self.bnE2Vs.append(nn.BatchNorm1d(MLP_hidden))
|
||||
|
||||
if self.GPR:
|
||||
self.MLP = MLP(
|
||||
in_channels=num_features,
|
||||
hidden_channels=MLP_hidden,
|
||||
out_channels=MLP_hidden,
|
||||
num_layers=MLP_num_layers,
|
||||
dropout=self.dropout,
|
||||
normalization=self.NormLayer,
|
||||
InputNorm=False,
|
||||
)
|
||||
self.GPRweights = Linear(self.All_num_layers + 1, 1, bias=False)
|
||||
self.classifier = MLP(
|
||||
in_channels=MLP_hidden,
|
||||
hidden_channels=Classifier_hidden,
|
||||
out_channels=num_classes,
|
||||
num_layers=Classifier_num_layers,
|
||||
dropout=self.dropout,
|
||||
normalization=self.NormLayer,
|
||||
InputNorm=False,
|
||||
)
|
||||
else:
|
||||
self.classifier = MLP(
|
||||
in_channels=MLP_hidden,
|
||||
hidden_channels=Classifier_hidden,
|
||||
out_channels=num_classes,
|
||||
num_layers=Classifier_num_layers,
|
||||
dropout=self.dropout,
|
||||
normalization=self.NormLayer,
|
||||
InputNorm=False,
|
||||
)
|
||||
|
||||
def generate_edge_index(self, dataset, self_loop=False):
|
||||
edge_list = dataset["edge_list"]
|
||||
e_ind = 0
|
||||
edge_index = [[], []]
|
||||
for e in edge_list:
|
||||
for n in e:
|
||||
edge_index[0].append(n)
|
||||
edge_index[1].append(e_ind)
|
||||
e_ind += 1
|
||||
edge_index = torch.tensor(edge_index).type(torch.LongTensor)
|
||||
if self_loop:
|
||||
hyperedge_appear_fre = Counter(edge_index[1].numpy())
|
||||
skip_node_lst = []
|
||||
for edge in hyperedge_appear_fre:
|
||||
if hyperedge_appear_fre[edge] == 1:
|
||||
skip_node = edge_index[0][torch.where(edge_index[1] == edge)[0]]
|
||||
skip_node_lst.append(skip_node)
|
||||
num_nodes = dataset["num_vertices"]
|
||||
new_edge_idx = len(edge_index[1]) + 1
|
||||
new_edges = torch.zeros(
|
||||
(2, num_nodes - len(skip_node_lst)), dtype=edge_index.dtype
|
||||
)
|
||||
tmp_count = 0
|
||||
for i in range(num_nodes):
|
||||
if i not in skip_node_lst:
|
||||
new_edges[0][tmp_count] = i
|
||||
new_edges[1][tmp_count] = new_edge_idx
|
||||
new_edge_idx += 1
|
||||
tmp_count += 1
|
||||
|
||||
edge_index = torch.Tensor(edge_index).type(torch.LongTensor)
|
||||
edge_index = torch.cat((edge_index, new_edges), dim=1)
|
||||
_, sorted_idx = torch.sort(edge_index[0])
|
||||
edge_index = torch.Tensor(edge_index[:, sorted_idx]).type(torch.LongTensor)
|
||||
|
||||
return edge_index
|
||||
|
||||
def reset_parameters(self):
|
||||
for layer in self.V2EConvs:
|
||||
layer.reset_parameters()
|
||||
for layer in self.E2VConvs:
|
||||
layer.reset_parameters()
|
||||
for layer in self.bnV2Es:
|
||||
layer.reset_parameters()
|
||||
for layer in self.bnE2Vs:
|
||||
layer.reset_parameters()
|
||||
self.classifier.reset_parameters()
|
||||
if self.GPR:
|
||||
self.MLP.reset_parameters()
|
||||
self.GPRweights.reset_parameters()
|
||||
if self.LearnMask:
|
||||
nn.init.ones_(self.Importance)
|
||||
|
||||
def forward(self, data):
|
||||
"""
|
||||
The data should contain the follows
|
||||
data.x: node features
|
||||
data.edge_index: edge list (of size (2,|E|)) where data.edge_index[0] contains nodes and data.edge_index[1] contains hyperedges
|
||||
!!! Note that self loop should be assigned to a new (hyper)edge id!!!
|
||||
!!! Also note that the (hyper)edge id should start at 0 (akin to node id)
|
||||
data.norm: The weight for edges in bipartite graphs, correspond to data.edge_index
|
||||
!!! Note that we output final node representation. Loss should be defined outside.
|
||||
"""
|
||||
if self.edge_index is None:
|
||||
self.edge_index = self.generate_edge_index(data, self.self_loop)
|
||||
# print("generate_edge_index:", self.edge_index.shape)
|
||||
x, edge_index = data["features"], self.edge_index
|
||||
if data["weight"] == None:
|
||||
norm = torch.ones(edge_index.size()[1])
|
||||
else:
|
||||
norm = data["weight"]
|
||||
|
||||
if self.LearnMask:
|
||||
norm = self.Importance * norm
|
||||
|
||||
reversed_edge_index = torch.stack([edge_index[1], edge_index[0]], dim=0)
|
||||
if self.GPR:
|
||||
xs = []
|
||||
xs.append(F.relu(self.MLP(x)))
|
||||
for i, _ in enumerate(self.V2EConvs):
|
||||
x = F.relu(self.V2EConvs[i](x, edge_index, norm, self.aggr))
|
||||
# x = self.bnV2Es[i](x)
|
||||
x = F.dropout(x, p=self.dropout, training=self.training)
|
||||
x = self.E2VConvs[i](x, reversed_edge_index, norm, self.aggr)
|
||||
x = F.relu(x)
|
||||
xs.append(x)
|
||||
# x = self.bnE2Vs[i](x)
|
||||
x = F.dropout(x, p=self.dropout, training=self.training)
|
||||
x = torch.stack(xs, dim=-1)
|
||||
x = self.GPRweights(x).squeeze()
|
||||
x = self.classifier(x)
|
||||
else:
|
||||
x = F.dropout(x, p=0.2, training=self.training) # Input dropout
|
||||
for i, _ in enumerate(self.V2EConvs):
|
||||
x = F.relu(self.V2EConvs[i](x, edge_index, norm, self.aggr))
|
||||
# x = self.bnV2Es[i](x)
|
||||
x = F.dropout(x, p=self.dropout, training=self.training)
|
||||
x = F.relu(self.E2VConvs[i](x, reversed_edge_index, norm, self.aggr))
|
||||
# x = self.bnE2Vs[i](x)
|
||||
x = F.dropout(x, p=self.dropout, training=self.training)
|
||||
x = self.classifier(x)
|
||||
|
||||
return x
|
||||
@@ -0,0 +1,214 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
|
||||
from easygraph.nn import MultiHeadWrapper
|
||||
from easygraph.nn import UniGATConv
|
||||
from easygraph.nn import UniGCNConv
|
||||
from easygraph.nn import UniGINConv
|
||||
from easygraph.nn import UniSAGEConv
|
||||
|
||||
|
||||
__all__ = [
|
||||
"UniGCN",
|
||||
"UniGAT",
|
||||
"UniSAGE",
|
||||
"UniGIN",
|
||||
]
|
||||
|
||||
|
||||
class UniGCN(nn.Module):
|
||||
r"""The UniGCN model proposed in `UniGNN: a Unified Framework for Graph and Hypergraph Neural Networks <https://arxiv.org/pdf/2105.00956.pdf>`_ paper (IJCAI 2021).
|
||||
|
||||
Parameters:
|
||||
``in_channels`` (``int``): :math:`C_{in}` is the number of input channels.
|
||||
``hid_channels`` (``int``): :math:`C_{hid}` is the number of hidden channels.
|
||||
``num_classes`` (``int``): The Number of class of the classification task.
|
||||
``use_bn`` (``bool``): If set to ``True``, use batch normalization. Defaults to ``False``.
|
||||
``drop_rate`` (``float``, optional): Dropout ratio. Defaults to ``0.5``.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
in_channels: int,
|
||||
hid_channels: int,
|
||||
num_classes: int,
|
||||
use_bn: bool = False,
|
||||
drop_rate: float = 0.5,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.layers = nn.ModuleList()
|
||||
self.layers.append(
|
||||
UniGCNConv(in_channels, hid_channels, use_bn=use_bn, drop_rate=drop_rate)
|
||||
)
|
||||
self.layers.append(
|
||||
UniGCNConv(hid_channels, num_classes, use_bn=use_bn, is_last=True)
|
||||
)
|
||||
|
||||
def forward(self, X: torch.Tensor, hg: "eg.Hypergraph") -> torch.Tensor:
|
||||
r"""The forward function.
|
||||
|
||||
Parameters:
|
||||
``X`` (``torch.Tensor``): Input vertex feature matrix. Size :math:`(N, C_{in})`.
|
||||
``hg`` (``eg.Hypergraph``): The hypergraph structure that contains :math:`N` vertices.
|
||||
"""
|
||||
for layer in self.layers:
|
||||
X = layer(X, hg)
|
||||
return X
|
||||
|
||||
|
||||
class UniGAT(nn.Module):
|
||||
r"""The UniGAT model proposed in `UniGNN: a Unified Framework for Graph and Hypergraph Neural Networks <https://arxiv.org/pdf/2105.00956.pdf>`_ paper (IJCAI 2021).
|
||||
|
||||
Parameters:
|
||||
``in_channels`` (``int``): :math:`C_{in}` is the number of input channels.
|
||||
``hid_channels`` (``int``): :math:`C_{hid}` is the number of hidden channels.
|
||||
``num_classes`` (``int``): The Number of class of the classification task.
|
||||
``num_heads`` (``int``): The Number of attention head in each layer.
|
||||
``use_bn`` (``bool``): If set to ``True``, use batch normalization. Defaults to ``False``.
|
||||
``drop_rate`` (``float``): The dropout probability. Defaults to ``0.5``.
|
||||
``atten_neg_slope`` (``float``): Hyper-parameter of the ``LeakyReLU`` activation of edge attention. Defaults to 0.2.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
in_channels: int,
|
||||
hid_channels: int,
|
||||
num_classes: int,
|
||||
num_heads: int,
|
||||
use_bn: bool = False,
|
||||
drop_rate: float = 0.5,
|
||||
atten_neg_slope: float = 0.2,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.drop_layer = nn.Dropout(drop_rate)
|
||||
self.multi_head_layer = MultiHeadWrapper(
|
||||
num_heads,
|
||||
"concat",
|
||||
UniGATConv,
|
||||
in_channels=in_channels,
|
||||
out_channels=hid_channels,
|
||||
use_bn=use_bn,
|
||||
drop_rate=drop_rate,
|
||||
atten_neg_slope=atten_neg_slope,
|
||||
)
|
||||
# The original implementation has applied activation layer after the final layer.
|
||||
# Thus, we donot set ``is_last`` to ``True``.
|
||||
self.out_layer = UniGATConv(
|
||||
hid_channels * num_heads,
|
||||
num_classes,
|
||||
use_bn=use_bn,
|
||||
drop_rate=drop_rate,
|
||||
atten_neg_slope=atten_neg_slope,
|
||||
is_last=False,
|
||||
)
|
||||
|
||||
def forward(self, X: torch.Tensor, hg: "eg.Hypergraph") -> torch.Tensor:
|
||||
r"""The forward function.
|
||||
|
||||
Parameters:
|
||||
``X`` (``torch.Tensor``): Input vertex feature matrix. Size :math:`(N, C_{in})`.
|
||||
``hg`` (``eg.Hypergraph``): The hypergraph structure that contains :math:`N` vertices.
|
||||
"""
|
||||
X = self.drop_layer(X)
|
||||
X = self.multi_head_layer(X=X, hg=hg)
|
||||
X = self.drop_layer(X)
|
||||
X = self.out_layer(X, hg)
|
||||
return X
|
||||
|
||||
|
||||
class UniSAGE(nn.Module):
|
||||
r"""The UniSAGE model proposed in `UniGNN: a Unified Framework for Graph and Hypergraph Neural Networks <https://arxiv.org/pdf/2105.00956.pdf>`_ paper (IJCAI 2021).
|
||||
|
||||
Parameters:
|
||||
``in_channels`` (``int``): :math:`C_{in}` is the number of input channels.
|
||||
``hid_channels`` (``int``): :math:`C_{hid}` is the number of hidden channels.
|
||||
``num_classes`` (``int``): The Number of class of the classification task.
|
||||
``use_bn`` (``bool``): If set to ``True``, use batch normalization. Defaults to ``False``.
|
||||
``drop_rate`` (``float``, optional): Dropout ratio. Defaults to ``0.5``.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
in_channels: int,
|
||||
hid_channels: int,
|
||||
num_classes: int,
|
||||
use_bn: bool = False,
|
||||
drop_rate: float = 0.5,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.layers = nn.ModuleList()
|
||||
self.layers.append(
|
||||
UniSAGEConv(in_channels, hid_channels, use_bn=use_bn, drop_rate=drop_rate)
|
||||
)
|
||||
self.layers.append(
|
||||
UniSAGEConv(hid_channels, num_classes, use_bn=use_bn, is_last=True)
|
||||
)
|
||||
|
||||
def forward(self, X: torch.Tensor, hg: "eg.Hypergraph") -> torch.Tensor:
|
||||
r"""The forward function.
|
||||
|
||||
Parameters:
|
||||
``X`` (``torch.Tensor``): Input vertex feature matrix. Size :math:`(N, C_{in})`.
|
||||
``hg`` (``eg.Hypergraph``): The hypergraph structure that contains :math:`N` vertices.
|
||||
"""
|
||||
for layer in self.layers:
|
||||
X = layer(X, hg)
|
||||
return X
|
||||
|
||||
|
||||
class UniGIN(nn.Module):
|
||||
r"""The UniGIN model proposed in `UniGNN: a Unified Framework for Graph and Hypergraph Neural Networks <https://arxiv.org/pdf/2105.00956.pdf>`_ paper (IJCAI 2021).
|
||||
|
||||
Parameters:
|
||||
``in_channels`` (``int``): :math:`C_{in}` is the number of input channels.
|
||||
``hid_channels`` (``int``): :math:`C_{hid}` is the number of hidden channels.
|
||||
``num_classes`` (``int``): The Number of class of the classification task.
|
||||
``eps`` (``float``): The epsilon value. Defaults to ``0.0``.
|
||||
``train_eps`` (``bool``): If set to ``True``, the epsilon value will be trainable. Defaults to ``False``.
|
||||
``use_bn`` (``bool``): If set to ``True``, use batch normalization. Defaults to ``False``.
|
||||
``drop_rate`` (``float``, optional): Dropout ratio. Defaults to ``0.5``.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
in_channels: int,
|
||||
hid_channels: int,
|
||||
num_classes: int,
|
||||
eps: float = 0.0,
|
||||
train_eps: bool = False,
|
||||
use_bn: bool = False,
|
||||
drop_rate: float = 0.5,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.layers = nn.ModuleList()
|
||||
self.layers.append(
|
||||
UniGINConv(
|
||||
in_channels,
|
||||
hid_channels,
|
||||
eps=eps,
|
||||
train_eps=train_eps,
|
||||
use_bn=use_bn,
|
||||
drop_rate=drop_rate,
|
||||
)
|
||||
)
|
||||
self.layers.append(
|
||||
UniGINConv(
|
||||
hid_channels,
|
||||
num_classes,
|
||||
eps=eps,
|
||||
train_eps=train_eps,
|
||||
use_bn=use_bn,
|
||||
is_last=True,
|
||||
)
|
||||
)
|
||||
|
||||
def forward(self, X: torch.Tensor, hg: "eg.Hypergraph") -> torch.Tensor:
|
||||
r"""The forward function.
|
||||
|
||||
Parameters:
|
||||
``X`` (``torch.Tensor``): Input vertex feature matrix. Size :math:`(N, C_{in})`.
|
||||
``hg`` (``eg.Hypergraph``): The hypergraph structure that contains :math:`N` vertices.
|
||||
"""
|
||||
for layer in self.layers:
|
||||
X = layer(X, hg)
|
||||
return X
|
||||
Reference in New Issue
Block a user