-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Update README * Update * Update * Update * Update * linebreak * Add figure * Resize * Update * Update * Update * Update * Update * Update tutorial * Update * Update * Update * Update * center image * Update * Overview * Update * Update * Update * Update * Update * Update * Finish tutorial1 * Update * Update * Update * Update * Update * Update * Update * Update * Update * Fix * Update * Update * Update * Update * Update * Update * Update * Update * Update * Update * Update
- Loading branch information
Showing
28 changed files
with
619 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,62 @@ | ||
# GNNLens2 | ||
<img src="resources/logo.png" width=30% height=30%> | ||
|
||
<img src="resources/README.png"> | ||
|
||
GNNLens2 is an interactive visualization tool for graph neural networks (GNN). It allows seamless integration with [deep graph library (DGL)](https://github.com/dmlc/dgl) and can meet your various visualization requirements for presentation, analysis and model explanation. It is an open source version of [GNNLens](https://arxiv.org/abs/2011.11048) with simplification and extension. | ||
|
||
## Installation | ||
|
||
### Requirements | ||
|
||
- [PyTorch](https://pytorch.org/) | ||
- [DGL](https://www.dgl.ai/pages/start.html) | ||
- Flask-CORS | ||
|
||
You can install Flask-CORS with | ||
|
||
```bash | ||
pip install -U flask-cors | ||
``` | ||
|
||
### Installation for the latest stable version | ||
|
||
```bash | ||
pip install gnnlens | ||
``` | ||
|
||
### Installation from source | ||
|
||
If you want to try experimental features, you can install from source as follows: | ||
|
||
```bash | ||
git clone https://github.com/dmlc/GNNLens2.git | ||
cd GNNLens2/python | ||
python setup.py install | ||
``` | ||
|
||
### Verifying successful installation | ||
|
||
Once you have installed the package, you can verify the success of installation with | ||
|
||
```python | ||
import gnnlens | ||
|
||
print(gnnlens.__version__) | ||
# 0.1.0 | ||
``` | ||
|
||
## Tutorials | ||
|
||
We provide a set of tutorials to get you started with the library: | ||
- [Tutorial 1: Graph structure](resources/tutorials/tutorial_1_graph.md) | ||
- [Tutorial 2: Ground truth and predicted node labels](resources/tutorials/tutorial_2_nlabel.md) | ||
- [Tutorial 3: Edge weights and attention](resources/tutorials/tutorial_3_eweight.md) | ||
- [Tutorial 4: Weighted subgraphs and explanation methods](resources/tutorials/tutorial_4_subgraph.md) | ||
|
||
## Team | ||
|
||
**HKUST VisLab**: [Zhihua Jin](jnzhihuoo1), [Huamin Qu](http://huamin.org/) | ||
|
||
**AWS Shanghai AI Lab**: [Mufei Li](https://github.com/mufeili), [Wanru Zhao](https://github.com/Ryan0v0) (work done during internship), [Jian Zhang](https://github.com/zhjwy9343), [Minjie Wang](https://jermainewang.github.io/) | ||
|
||
**SMU**: [Yong Wang](http://yong-wang.org/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Licensed to the Apache Software Foundation (ASF) under one | ||
# or more contributor license agreements. See the NOTICE file | ||
# distributed with this work for additional information | ||
# regarding copyright ownership. The ASF licenses this file | ||
# to you 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. | ||
|
||
# current version | ||
__version__ = '0.1.0' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
# Tutorial 1: Graph structure | ||
|
||
Graph structure plays a critical role in developing a GNN. You may want to visualize the whole graph to roughly understand the sparsity of it and if it has subgraphs of a particular pattern. | ||
|
||
GNNLens2 allows you to do it using a simple API with very little effort. | ||
|
||
## Data preparation | ||
|
||
First we load DGL’s built-in Cora and Citeseer dataset and retrieve their graph structures. | ||
|
||
```python | ||
from dgl.data import CoraGraphDataset, CiteseerGraphDataset | ||
|
||
cora_dataset = CoraGraphDataset() | ||
cora_graph = cora_dataset[0] | ||
citeseer_dataset = CiteseerGraphDataset() | ||
citeseer_graph = citeseer_dataset[0] | ||
``` | ||
|
||
Next, we need to dump the graph structures to a local file that GNNLens2 can read. GNNLens2 provides a built-in class `Writer` for this purpose. You can add an arbitrary number of graphs, one at a time. | ||
|
||
Once you finish adding data, you need to call **writer.close()**. | ||
|
||
```python | ||
from gnnlens import Writer | ||
|
||
# Specify the path to create a new directory for dumping data files. | ||
writer = Writer('tutorial_graph') | ||
writer.add_graph(name='Cora', graph=cora_graph) | ||
writer.add_graph(name='Citeseer', graph=citeseer_graph) | ||
# Finish dumping | ||
writer.close() | ||
``` | ||
|
||
## Launch GNNLens2 | ||
|
||
To launch GNNLens2, run the following command line. | ||
|
||
```bash | ||
gnnlens --logdir tutorial_graph | ||
``` | ||
|
||
By entering `localhost:7777` in your web browser address bar, you can see the GNNLens2 interface like below. `7777` is the default port GNNLens2 uses. You can specify an alternative one by adding `--port xxxx` after the command line and change the address in the web browser accordingly. | ||
|
||
## GNNLens2 Interface | ||
|
||
<p align="center"> | ||
<img width=80% height=80% src="../figures/tutorial_1/empty_interface.png" /> | ||
</p> | ||
|
||
The interface is empty as no graph is selected. The control panel on the left has multiple selectors for users to make selections. The first selector is the graph selector. You can click it and select a graph to visualize from the drop-down list. The options in the drop-down list are the names you passed to `add_graph`. | ||
|
||
<p align="center"> | ||
<img src="../figures/tutorial_1/control_panel.png" /> | ||
</p> | ||
|
||
After you select a graph, GNNLens2 will plot the corresponding graph as below. GNNLens2 determines the graph layout (node positions) on the fly using a force-directed graph drawing algorithm. The algorithm simulates the physical forces on nodes. The simulation stops when you click the “Stop Simulation” button and starts when you click the same button again. | ||
|
||
<p align="center"> | ||
<img width=80% height=80% src="../figures/tutorial_1/stop_simulation.png" /> | ||
</p> | ||
|
||
For a large graph, you can view different parts of it by clicking on the overview box at the lower-right corner. | ||
|
||
<p align="center"> | ||
<img width=80% height=80% src="../figures/tutorial_1/overview.png" /> | ||
</p> | ||
|
||
You can drag the graph by pressing and holding the mouse button. The figure below is the result of dragging the graph to the right. | ||
|
||
<p align="center"> | ||
<img width=80% height=80% src="../figures/tutorial_1/drag.png" /> | ||
</p> | ||
|
||
You can also zoom in or out on the graph. The figure below is the result of zooming in on the graph. | ||
|
||
<p align="center"> | ||
<img width=80% height=80% src="../figures/tutorial_1/zoom.png" /> | ||
</p> | ||
|
||
As you move the cursor to a particular node, GNNLens2 will display its node ID and highlight its one-hop neighborhood. | ||
|
||
<p align="center"> | ||
<img width=80% height=80% src="../figures/tutorial_1/hover_node.png" /> | ||
</p> | ||
|
||
If you want to examine a subgraph centered at a particular node, you can simply click on it. GNNLens2 will then display its two-hop subgraph by default and the node you clicked on will be highlighted. You can click the overview box to put the subgraph in the center. | ||
|
||
<p align="center"> | ||
<img width=80% height=80% src="../figures/tutorial_1/two_hop.png" /> | ||
</p> | ||
|
||
You can switch the subgraph option in the “Subgraph” drop-down list. | ||
|
||
<p align="center"> | ||
<img width=80% height=80% src="../figures/tutorial_1/subgraph_options.png" /> | ||
</p> | ||
|
||
To terminate GNNLens2, use `ctrl + c`. | ||
|
||
## Next | ||
|
||
So far, we've seen how to visualize a graph structure. Now let us look at how to [use node labels to color nodes in visualization](./tutorial_2_nlabel.md). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
# Tutorial 2: Ground truth and predicted node labels | ||
|
||
The nodes in a graph can be associated with a label like node type or node class. For the task of multiclass node classification, you can have ground truth node labels and node labels predicted from different models. GNNLens2 allows coloring nodes based on node labels in graph visualization and comparing node labels from different sources. | ||
|
||
## Data preparation | ||
|
||
First, we load DGL’s built-in Cora dataset and retrieve its graph structure, node labels (classes) and number of node classes. | ||
|
||
```python | ||
from dgl.data import CoraGraphDataset | ||
|
||
dataset = CoraGraphDataset() | ||
graph = dataset[0] | ||
nlabels = graph.ndata['label'] | ||
num_classes = dataset.num_classes | ||
``` | ||
|
||
We dump them to a local file that GNNLens2 can read. Compared with [the previous section](./tutorial_1_graph.md), we additionally dump the node classes and the number of node classes. | ||
|
||
```python | ||
from gnnlens import Writer | ||
|
||
# Specify the path to create a new directory for dumping data files. | ||
writer = Writer('tutorial_nlabel') | ||
writer.add_graph(name='Cora', graph=graph, | ||
nlabels=nlabels, num_nlabel_types=num_classes) | ||
``` | ||
|
||
Next, we train two graph convolutional networks (GCN) for node classification, `GCN_L1` (GCN with one layer) and `GCN_L2` (GCN with two layers). Once trained, we retrieve the predicted node classes and dump them to local files | ||
|
||
```python | ||
import torch | ||
import torch.nn as nn | ||
import torch.nn.functional as F | ||
from dgl.nn.pytorch import GraphConv | ||
|
||
# Define a class for GCN | ||
class GCN(nn.Module): | ||
def __init__(self, | ||
in_feats, | ||
num_classes, | ||
num_layers): | ||
super(GCN, self).__init__() | ||
self.layers = nn.ModuleList() | ||
self.layers.append(GraphConv(in_feats, num_classes)) | ||
for _ in range(num_layers - 1): | ||
self.layers.append(GraphConv(num_classes, num_classes)) | ||
|
||
def forward(self, g, h): | ||
for layer in self.layers: | ||
h = layer(g, h) | ||
return h | ||
|
||
# Define a function to train a GCN with the specified number of layers | ||
# and return the predictions | ||
def train_gcn(g, num_layers, num_classes): | ||
features = g.ndata['feat'] | ||
labels = g.ndata['label'] | ||
train_mask = g.ndata['train_mask'] | ||
model = GCN(in_feats=features.shape[1], | ||
num_classes=num_classes, | ||
num_layers=num_layers) | ||
loss_func = nn.CrossEntropyLoss() | ||
optimizer = torch.optim.Adam(model.parameters(), lr=1e-2) | ||
|
||
num_epochs = 200 | ||
model.train() | ||
for _ in range(num_epochs): | ||
logits = model(g, features) | ||
loss = loss_func(logits[train_mask], labels[train_mask]) | ||
optimizer.zero_grad() | ||
loss.backward() | ||
optimizer.step() | ||
|
||
model.eval() | ||
predictions = model(g, features) | ||
_, predicted_classes = torch.max(predictions, dim=1) | ||
return predicted_classes | ||
|
||
print("Training GCN with one layer...") | ||
predictions_one_layer = train_gcn(graph, num_layers=1, num_classes=num_classes) | ||
print("Training GCN with two layers...") | ||
predictions_two_layers = train_gcn(graph, num_layers=2, num_classes=num_classes) | ||
# Dump the predictions to local files | ||
writer.add_model(graph_name='Cora', model_name='GCN_L1', | ||
nlabels=predictions_one_layer) | ||
writer.add_model(graph_name='Cora', model_name='GCN_L2', | ||
nlabels=predictions_two_layers) | ||
# Finish dumping | ||
writer.close() | ||
``` | ||
|
||
## Launch GNNLens2 | ||
|
||
To launch GNNLens2, run the following command line. | ||
|
||
```bash | ||
gnnlens --logdir tutorial_nlabel | ||
``` | ||
|
||
By entering `localhost:7777` in your web browser address bar, you can see the GNNLens2 interface. `7777` is the default port GNNLens2 uses. You can specify an alternative one by adding `--port xxxx` after the command line and change the address in the web browser accordingly. | ||
|
||
## GNNLens2 Interface | ||
|
||
The second selector in the control panel on the left is the nlabel selector. After you select a graph and click the nlabel selector, it will display the available node labels from different sources. The options include `ground_truth` for the ground truth node labels and the model names passed to `add_model` for the model predictions. | ||
|
||
<p align="center"> | ||
<img src="../figures/tutorial_2/nlabel_selector.png" /> | ||
</p> | ||
|
||
You can select an option to color nodes using a source of node labels. The color legend is in the lower left corner. | ||
|
||
<p align="center"> | ||
<img width=80% height=80% src="../figures/tutorial_2/real_color.png" /> | ||
</p> | ||
|
||
The node coloring also applies to subgraphs if you click on a node. | ||
|
||
<p align="center"> | ||
<img width=80% height=80% src="../figures/tutorial_2/subgraph_nlabel.png" /> | ||
</p> | ||
|
||
You can even select multiple options and simultaneously color nodes using multiple sources of node labels. In this case, the circles representing the nodes will be replaced by glyphs. The center of the glyph is colored based on the first selected nlabel source. The outer pie chart will be colored based on the rest nlabel sources in a clockwise direction from the top. This allows a direct comparison among the ground truth node labels and the predicted node labels from various models. | ||
|
||
<p align="center"> | ||
<img width=80% height=80% src="../figures/tutorial_2/glyph.png" /> | ||
</p> | ||
|
||
To terminate GNNLens2, use `ctrl + c`. | ||
|
||
## Next | ||
|
||
So far, we've seen how to visualize node labels. Now let us look at how to [use edge weights in visualization](./tutorial_3_eweight.md). |
Oops, something went wrong.