"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m512.4/512.4 KB\u001b[0m \u001b[31m8.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hRequirement already satisfied: typing-extensions in /usr/local/lib/python3.8/dist-packages (from torchmetrics) (4.4.0)\n",
"Requirement already satisfied: numpy>=1.17.2 in /usr/local/lib/python3.8/dist-packages (from torchmetrics) (1.21.6)\n",
"Requirement already satisfied: torch>=1.8.1 in /usr/local/lib/python3.8/dist-packages (from torchmetrics) (1.13.0+cu116)\n",
"Requirement already satisfied: packaging in /usr/local/lib/python3.8/dist-packages (from torchmetrics) (21.3)\n",
"Requirement already satisfied: pyparsing!=3.0.5,>=2.0.2 in /usr/local/lib/python3.8/dist-packages (from packaging->torchmetrics) (3.0.9)\n",
"Installing collected packages: torchmetrics\n",
"Successfully installed torchmetrics-0.11.0\n",
"DGL installed!\n"
"DGL installed!\n"
]
]
}
}
...
@@ -87,6 +76,8 @@
...
@@ -87,6 +76,8 @@
"A [**hypergraph**](https://en.wikipedia.org/wiki/Hypergraph) consists of *nodes* and *hyperedges*. Contrary to edges in graphs, a *hyperedge* can connect arbitrary number of nodes. For instance, the following figure shows a hypergraph with 11 nodes and 5 hyperedges drawn in different colors.\n",
"A [**hypergraph**](https://en.wikipedia.org/wiki/Hypergraph) consists of *nodes* and *hyperedges*. Contrary to edges in graphs, a *hyperedge* can connect arbitrary number of nodes. For instance, the following figure shows a hypergraph with 11 nodes and 5 hyperedges drawn in different colors.\n",
"Hypergraphs are particularly useful when the relationships between data points within the dataset is not binary. For instance, more than two products can be co-purchased together in an e-commerce system, so the relationship of co-purchase is $n$-ary rather than binary, and therefore it is better described as a hypergraph rather than a normal graph.\n",
"\n",
"A hypergraph is usually characterized by its *incidence matrix* $H$, whose rows represent nodes and columns represent hyperedges. An entry $H_{ij}$ is 1 if hyperedge $j$ includes node $i$, or 0 otherwise. For example, the hypergraph in the figure above can be characterized by a $11 \\times 5$ matrix as follows:\n",
"A hypergraph is usually characterized by its *incidence matrix* $H$, whose rows represent nodes and columns represent hyperedges. An entry $H_{ij}$ is 1 if hyperedge $j$ includes node $i$, or 0 otherwise. For example, the hypergraph in the figure above can be characterized by a $11 \\times 5$ matrix as follows:\n",
"* $H \\in \\mathbb{R}^{N \\times M}$ is the incidence matrix of hypergraph with $N$ nodes and $M$ hyperedges.\n",
"* $H \\in \\mathbb{R}^{N \\times M}$ is the incidence matrix of hypergraph with $N$ nodes and $M$ hyperedges.\n",
"* $D_v \\in \\mathbb{R}^{N \\times N}$ is a diagonal matrix representing node degrees, whose $i$-th diagonal element is $\\sum_{j=1}^M H_{ij}$.\n",
"* $D_v \\in \\mathbb{R}^{N \\times N}$ is a diagonal matrix representing node degrees, whose $i$-th diagonal element is $\\sum_{j=1}^M H_{ij}$.\n",
"* $D_e \\in \\mathbb{R}^{M \\times M}$ is a diagonal matrix representing hyperedge degrees, whose $j$-th diagonal element is $\\sum_{i=1}^N H_{ij}$.\n",
"* $D_e \\in \\mathbb{R}^{M \\times M}$ is a diagonal matrix representing hyperedge degrees, whose $j$-th diagonal element is $\\sum_{i=1}^N H_{ij}$.\n",
"* $B \\in \\mathbb{R}^{M \\times M}$ is a diagonal matrix representing the hyperedge weights, whose $j$-th diagonal element is the weight of $j$-th hyperedge. In our example, $B$ is an identity matrix."
"* $B \\in \\mathbb{R}^{M \\times M}$ is a diagonal matrix representing the hyperedge weights, whose $j$-th diagonal element is the weight of $j$-th hyperedge. In our example, $B$ is an identity matrix.\n",
" # Compute Laplacian from the equation above.\n",
" W = dglsp.identity((n_edges, n_edges))\n",
" self.L = D_v_invsqrt @ H @ B @ D_e_inv @ H.T @ D_v_invsqrt\n",
" self.laplacian = D_V_invsqrt @ H @ W @ D_E_inv @ H.T @ D_V_invsqrt\n",
"\n",
"\n",
" def forward(self, X):\n",
" def forward(self, X):\n",
" X = self.laplacian @ self.Theta1(self.dropout(X))\n",
" X = self.L @ self.W1(self.dropout(X))\n",
" X = F.relu(X)\n",
" X = F.relu(X)\n",
" X = self.laplacian @ self.Theta2(self.dropout(X))\n",
" X = self.L @ self.W2(self.dropout(X))\n",
" return X\n",
" return X"
],
"metadata": {
"id": "58WnPtPvT2mx"
},
"execution_count": 4,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"## Loading Data\n",
"\n",
"We use Cora citation network in our example. But instead of using the original \"cite\" relationship between papers, we consider the \"co-cite\" relationship between papers. We build a hypergraph from the original citation network where for each paper we construct a hyperedge that includes all the other papers it cited, as well as the paper itself.\n",
"Note that a hypergraph constructed this way has an incidence matrix exactly identical to the adjacency matrix of the original graph (plus an identity matrix for self-loops). This is because each hyperedge has a one-to-one correspondence to each paper. So we can directly take the graph's adjacency matrix and add an identity matrix to it, and we use it as the hypergraph's incidence matrix."
],
"metadata": {
"id": "bPrOHVaGwUD0"
}
},
{
"cell_type": "code",
"source": [
"def load_data():\n",
" dataset = CoraGraphDataset()\n",
"\n",
" graph = dataset[0]\n",
" src, dst = graph.edges()\n",
" H = dglsp.from_coo(dst, src)\n",
" H = H + dglsp.identity(H.shape)\n",
"\n",
" X = graph.ndata[\"feat\"]\n",
" Y = graph.ndata[\"label\"]\n",
" train_mask = graph.ndata[\"train_mask\"]\n",
" val_mask = graph.ndata[\"val_mask\"]\n",
" test_mask = graph.ndata[\"test_mask\"]\n",
" return H, X, Y, dataset.num_classes, train_mask, val_mask, test_mask"
],
"metadata": {
"id": "qI0j1J9pwTFg"
},
"execution_count": 5,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"## Training and Evaluation\n",
"\n",
"\n",
"Now we can write the training and evaluation functions as follows."
],
"metadata": {
"id": "--rq1-r7wMST"
}
},
{
"cell_type": "code",
"source": [
"def train(model, optimizer, X, Y, train_mask):\n",
"def train(model, optimizer, X, Y, train_mask):\n",
" model.train()\n",
" model.train()\n",
" Y_hat = model(X)\n",
" Y_hat = model(X)\n",
...
@@ -275,35 +329,11 @@
...
@@ -275,35 +329,11 @@
" return val_acc, test_acc\n",
" return val_acc, test_acc\n",
"\n",
"\n",
"\n",
"\n",
"def load_data():\n",
"H, X, Y, num_classes, train_mask, val_mask, test_mask = load_data()\n",