├── .gitignore ├── deep_gcns_torch ├── gcn_lib │ ├── __init__.py │ ├── dense │ │ ├── __init__.py │ │ ├── torch_nn.py │ │ ├── torch_edge.py │ │ └── torch_vertex.py │ └── sparse │ │ ├── __init__.py │ │ ├── torch_message.py │ │ ├── torch_nn.py │ │ └── torch_edge.py ├── misc │ ├── ppi.png │ ├── intro.png │ ├── pipeline.png │ ├── modelnet_cls.png │ ├── part_sem_seg.png │ ├── deeper_softmax.png │ ├── sem_seg_s3dis.png │ ├── deeper_gcn_intro.png │ └── deeper_power_mean.png ├── examples │ ├── modelnet_cls │ │ ├── __init__.py │ │ ├── README.md │ │ ├── data.py │ │ └── architecture.py │ ├── part_sem_seg │ │ ├── __init__.py │ │ ├── visualize.py │ │ ├── architecture.py │ │ ├── README.md │ │ └── eval.py │ ├── sem_seg_dense │ │ ├── __init__.py │ │ ├── test.py │ │ ├── README.md │ │ ├── architecture.py │ │ └── train.py │ ├── sem_seg_sparse │ │ ├── __init__.py │ │ ├── script │ │ │ ├── test.sh │ │ │ └── train.sh │ │ ├── README.md │ │ ├── test.py │ │ ├── architecture.py │ │ └── train.py │ ├── ogb │ │ ├── README.md │ │ ├── ogbg_mol │ │ │ ├── __init__.py │ │ │ ├── README.md │ │ │ ├── test.py │ │ │ └── args.py │ │ ├── ogbg_ppa │ │ │ ├── __init__.py │ │ │ ├── README.md │ │ │ ├── test.py │ │ │ ├── args.py │ │ │ └── model.py │ │ ├── ogbn_arxiv │ │ │ ├── __init__.py │ │ │ ├── README.md │ │ │ ├── test.py │ │ │ ├── main.py │ │ │ ├── args.py │ │ │ └── model.py │ │ ├── ogbn_products │ │ │ ├── __init__.py │ │ │ ├── README.md │ │ │ ├── test.py │ │ │ ├── args.py │ │ │ └── model.py │ │ ├── ogbn_proteins │ │ │ ├── __init__.py │ │ │ ├── README.md │ │ │ ├── test.py │ │ │ └── args.py │ │ ├── deeper_gcn_env.sh │ │ └── attacks.py │ └── ppi │ │ ├── README.md │ │ └── architecture.py ├── utils │ ├── __init__.py │ ├── metrics.py │ ├── loss.py │ ├── logger.py │ ├── tf_logger.py │ └── ckpt_util.py ├── LICENSE ├── deepgcn_env_install.sh ├── deepgcn.yml └── README.md ├── ogb ├── nodeproppred │ ├── mag │ │ └── README.md │ ├── proteins │ │ └── README.md │ ├── arxiv │ │ ├── README.md │ │ └── mlp.py │ └── products │ │ ├── README.md │ │ └── mlp.py ├── graphproppred │ ├── code │ │ ├── README.md │ │ └── gnn.py │ ├── ppa │ │ ├── README.md │ │ └── gnn.py │ └── mol │ │ ├── README.md │ │ └── gnn.py └── attacks.py ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS* 2 | .idea -------------------------------------------------------------------------------- /deep_gcns_torch/gcn_lib/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /deep_gcns_torch/misc/ppi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devnkong/FLAG/HEAD/deep_gcns_torch/misc/ppi.png -------------------------------------------------------------------------------- /deep_gcns_torch/misc/intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devnkong/FLAG/HEAD/deep_gcns_torch/misc/intro.png -------------------------------------------------------------------------------- /deep_gcns_torch/misc/pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devnkong/FLAG/HEAD/deep_gcns_torch/misc/pipeline.png -------------------------------------------------------------------------------- /deep_gcns_torch/misc/modelnet_cls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devnkong/FLAG/HEAD/deep_gcns_torch/misc/modelnet_cls.png -------------------------------------------------------------------------------- /deep_gcns_torch/misc/part_sem_seg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devnkong/FLAG/HEAD/deep_gcns_torch/misc/part_sem_seg.png -------------------------------------------------------------------------------- /deep_gcns_torch/misc/deeper_softmax.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devnkong/FLAG/HEAD/deep_gcns_torch/misc/deeper_softmax.png -------------------------------------------------------------------------------- /deep_gcns_torch/misc/sem_seg_s3dis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devnkong/FLAG/HEAD/deep_gcns_torch/misc/sem_seg_s3dis.png -------------------------------------------------------------------------------- /deep_gcns_torch/misc/deeper_gcn_intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devnkong/FLAG/HEAD/deep_gcns_torch/misc/deeper_gcn_intro.png -------------------------------------------------------------------------------- /deep_gcns_torch/misc/deeper_power_mean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devnkong/FLAG/HEAD/deep_gcns_torch/misc/deeper_power_mean.png -------------------------------------------------------------------------------- /deep_gcns_torch/gcn_lib/dense/__init__.py: -------------------------------------------------------------------------------- 1 | from .torch_nn import * 2 | from .torch_edge import * 3 | from .torch_vertex import * 4 | 5 | -------------------------------------------------------------------------------- /deep_gcns_torch/gcn_lib/sparse/__init__.py: -------------------------------------------------------------------------------- 1 | from .torch_nn import * 2 | from .torch_edge import * 3 | from .torch_vertex import * 4 | 5 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/modelnet_cls/__init__.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../')) 3 | 4 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/part_sem_seg/__init__.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../')) 3 | 4 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/sem_seg_dense/__init__.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../')) 3 | 4 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/sem_seg_sparse/__init__.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../')) 3 | 4 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/README.md: -------------------------------------------------------------------------------- 1 | # DeeperGCN 2 | 3 | The baseline DeeperGCN models can be found [here](https://github.com/lightaime/deep_gcns_torch/tree/master/examples/ogb). 4 | -------------------------------------------------------------------------------- /deep_gcns_torch/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .ckpt_util import * 2 | # from .data_util import * 3 | from .loss import * 4 | from .metrics import * 5 | from .optim import * 6 | from .tf_logger import * 7 | 8 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbg_mol/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) 4 | sys.path.append(ROOT_DIR) 5 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbg_ppa/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) 4 | sys.path.append(ROOT_DIR) 5 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbn_arxiv/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) 4 | sys.path.append(ROOT_DIR) 5 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbn_products/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) 4 | sys.path.append(ROOT_DIR) 5 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbn_proteins/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) 4 | sys.path.append(ROOT_DIR) 5 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbn_products/README.md: -------------------------------------------------------------------------------- 1 | # ogbn-products 2 | 3 | To train baselines with FLAG in the default setup, run 4 | 5 | python main.py --use_gpu --self_loop --num_layers 14 --gcn_aggr softmax_sg --t 0.1 6 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbg_ppa/README.md: -------------------------------------------------------------------------------- 1 | # ogbg-ppa 2 | 3 | To train baselines with FLAG in the default setup, run 4 | 5 | python main.py --use_gpu --conv_encode_edge --num_layers 28 --gcn_aggr softmax_sg --t 0.01 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/sem_seg_sparse/script/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | conda activate deepgcn 3 | python -u test.py --pretrained_model sem_seg/checkpoints/deepgcn-res-edge-190822_ckpt_50.pth --batch_size 1 --test_path /data/deepgcn/S3DIS 4 | 5 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/sem_seg_sparse/script/train.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | conda activate deepgcn 4 | python -u train.py --multi_gpus --phase train --train_path /data/deepgcn/S3DIS --batch_size 16 5 | echo "===> training loaded Done..." 6 | 7 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbn_arxiv/README.md: -------------------------------------------------------------------------------- 1 | # ogbn-arxiv 2 | 3 | To train baselines with FLAG in the default setup, run 4 | 5 | python main.py --use_gpu --self_loop --num_layers 28 --block res+ --gcn_aggr softmax_sg --t 0.1 6 | 7 | 8 | -------------------------------------------------------------------------------- /ogb/nodeproppred/mag/README.md: -------------------------------------------------------------------------------- 1 | # ogbn-mag 2 | 3 | To train baselines with FLAG in the default setup, run 4 | 5 | **R-GCN+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/nodeproppred/mag). 6 | 7 | python sampler.py 8 | 9 | 10 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbn_proteins/README.md: -------------------------------------------------------------------------------- 1 | # ogbn-proteins 2 | 3 | To train baselines with FLAG in the default setup, run 4 | 5 | python main.py --use_gpu --conv_encode_edge --num_layers 112 --block res+ --gcn_aggr softmax --t 1.0 --learn_t --dropout 0.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /ogb/nodeproppred/proteins/README.md: -------------------------------------------------------------------------------- 1 | # ogbn-proteins 2 | 3 | To train baselines with FLAG in the default setup, run 4 | 5 | **GCN+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/nodeproppred/proteins). 6 | 7 | python gnn.py 8 | 9 | **GraphSAGE+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/nodeproppred/proteins). 10 | 11 | python gnn.py --use-sage 12 | 13 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbg_mol/README.md: -------------------------------------------------------------------------------- 1 | # ogbg-mol 2 | 3 | To train baselines with FLAG in the default setup, run 4 | 5 | **ogbg-molhiv** 6 | 7 | python main.py --use_gpu --conv_encode_edge --num_layers 7 --dataset ogbg-molhiv --block res+ --gcn_aggr softmax --t 1.0 --learn_t --dropout 0.2 --step-size 1e-2 8 | 9 | **ogbg-molpcba** 10 | 11 | python main.py --use_gpu --conv_encode_edge --add_virtual_node --mlp_layers 2 --num_layers 14 --dataset ogbg-molpcba --block res+ --gcn_aggr softmax_sg --t 0.1 --step-size 8e-3 12 | 13 | -------------------------------------------------------------------------------- /deep_gcns_torch/utils/metrics.py: -------------------------------------------------------------------------------- 1 | from math import log10 2 | 3 | 4 | def PSNR(mse, peak=1.): 5 | return 10 * log10((peak ** 2) / mse) 6 | 7 | 8 | class AverageMeter(object): 9 | """Computes and stores the average and current value""" 10 | 11 | def __init__(self): 12 | self.reset() 13 | 14 | def reset(self): 15 | self.val = 0 16 | self.avg = 0 17 | self.sum = 0 18 | self.count = 0 19 | 20 | def update(self, val, n=1): 21 | self.val = val 22 | self.sum += val * n 23 | self.count += n 24 | self.avg = self.sum / self.count 25 | 26 | -------------------------------------------------------------------------------- /ogb/nodeproppred/arxiv/README.md: -------------------------------------------------------------------------------- 1 | # ogbn-arxiv 2 | 3 | To train baselines with FLAG in the default setup, run 4 | 5 | **MLP+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/nodeproppred/arxiv). 6 | 7 | python mlp.py 8 | 9 | **GCN+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/nodeproppred/arxiv). 10 | 11 | python gnn.py 12 | 13 | **GraphSAGE+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/nodeproppred/arxiv). 14 | 15 | python gnn.py --use_sage 16 | 17 | **GAT+FLAG**, the baseline model [here](https://github.com/Espylapiza/dgl/tree/master/examples/pytorch/ogb/ogbn-arxiv). We are using the `GAT(norm. adj.)+labels` version. 18 | 19 | python gat_dgl/gat.py --use-norm --use-labels 20 | -------------------------------------------------------------------------------- /deep_gcns_torch/utils/loss.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn.functional as F 3 | 4 | 5 | class SmoothCrossEntropy(torch.nn.Module): 6 | def __init__(self, smoothing=True, eps=0.2): 7 | super(SmoothCrossEntropy, self).__init__() 8 | self.smoothing = smoothing 9 | self.eps = eps 10 | 11 | def forward(self, pred, gt): 12 | gt = gt.contiguous().view(-1) 13 | 14 | if self.smoothing: 15 | n_class = pred.size(1) 16 | one_hot = torch.zeros_like(pred).scatter(1, gt.view(-1, 1), 1) 17 | one_hot = one_hot * (1 - self.eps) + (1 - one_hot) * self.eps / (n_class - 1) 18 | log_prb = F.log_softmax(pred, dim=1) 19 | 20 | loss = -(one_hot * log_prb).sum(dim=1).mean() 21 | else: 22 | loss = F.cross_entropy(pred, gt, reduction='mean') 23 | 24 | return loss 25 | -------------------------------------------------------------------------------- /ogb/graphproppred/code/README.md: -------------------------------------------------------------------------------- 1 | # ogbg-code 2 | 3 | To train baselines with FLAG in the default setup, run 4 | 5 | **GCN+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/graphproppred/code). 6 | 7 | python main_pyg.py --gnn gcn --step-size 8e-3 8 | 9 | **GCN+V+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/graphproppred/code). 10 | 11 | python main_pyg.py --gnn gcn-virtual --step-size 8e-3 12 | 13 | **GIN+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/graphproppred/code). 14 | 15 | python main_pyg.py --gnn gin --step-size 8e-3 16 | 17 | **GIN+V+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/graphproppred/code). 18 | 19 | python main_pyg.py --gnn gin-virtual --step-size 8e-3 20 | 21 | -------------------------------------------------------------------------------- /ogb/graphproppred/ppa/README.md: -------------------------------------------------------------------------------- 1 | # ogbg-ppa 2 | 3 | To train baselines with FLAG in the default setup, run 4 | 5 | **GCN+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/graphproppred/ppa). 6 | 7 | python main_pyg.py --gnn gcn --step-size 2e-3 8 | 9 | **GCN+V+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/graphproppred/ppa). 10 | 11 | python main_pyg.py --gnn gcn-virtual --step-size 5e-3 12 | 13 | **GIN+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/graphproppred/ppa). 14 | 15 | python main_pyg.py --gnn gin --step-size 8e-3 16 | 17 | **GIN+V+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/graphproppred/ppa). 18 | 19 | python main_pyg.py --gnn gin-virtual --step-size 5e-3 20 | 21 | -------------------------------------------------------------------------------- /ogb/nodeproppred/products/README.md: -------------------------------------------------------------------------------- 1 | # ogbn-products 2 | 3 | To train baselines with FLAG in the default setup, run 4 | 5 | **MLP+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/nodeproppred/products). 6 | 7 | python mlp.py 8 | 9 | **GraphSAGE w/ Neighbor Sampling +FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/nodeproppred/products). 10 | 11 | python sage_ns.py 12 | 13 | **GraphSAGE w/ Cluster +FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/nodeproppred/products). 14 | 15 | python sage_cluster.py 16 | 17 | **GraphSAGE w/ SAINT +FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/nodeproppred/products). 18 | 19 | python sage_saint.py 20 | 21 | **GAT+FLAG**, the baseline model [here](https://github.com/rusty1s/pytorch_geometric/blob/master/examples/ogbn_products_gat.py). 22 | 23 | python gat_ns.py 24 | 25 | 26 | -------------------------------------------------------------------------------- /deep_gcns_torch/utils/logger.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import csv 4 | 5 | 6 | def save_best_result(list_of_dict, file_name, dir_path='best_result'): 7 | if not os.path.exists(dir_path): 8 | os.mkdir(dir_path) 9 | print("Directory ", dir_path, " is created.") 10 | csv_file_name = '{}/{}.csv'.format(dir_path, file_name) 11 | with open(csv_file_name, 'a+') as csv_file: 12 | csv_writer = csv.writer(csv_file) 13 | for _ in range(len(list_of_dict)): 14 | csv_writer.writerow(list_of_dict[_].values()) 15 | 16 | 17 | def create_exp_dir(path, scripts_to_save=None): 18 | if not os.path.exists(path): 19 | os.makedirs(path) 20 | print('Experiment dir : {}'.format(path)) 21 | 22 | if scripts_to_save is not None: 23 | os.mkdir(os.path.join(path, 'scripts')) 24 | for script in scripts_to_save: 25 | dst_file = os.path.join(path, 'scripts', os.path.basename(script)) 26 | shutil.copyfile(script, dst_file) 27 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/modelnet_cls/README.md: -------------------------------------------------------------------------------- 1 | ## [Point cloud classification on ModelNet40](https://arxiv.org/pdf/1910.06849.pdf) 2 | 3 |

4 | 5 |

6 | 7 | ### Train 8 | We train PlainGCN-28 and ResGCN-28 models on one Tesla V100. 9 | For DenseGCN, we use 4 Tesla V100s. 10 | 11 | For training ResGCN-28, run: 12 | ``` 13 | python main.py --phase train --n_blocks 28 --block res --data_dir /path/to/modelnet40 14 | ``` 15 | Just need to set `--data_dir` into your data folder, dataset will be downloaded automatically. 16 | 17 | ### Test 18 | Models can be tested on one 1080Ti. 19 | Our pretrained models are available [Google Drive](https://drive.google.com/drive/folders/1LUWH0V3ZoHNQBylj0u0_36Mx0-UrDh1v?usp=sharing). 20 | 21 | Use the parameter `--pretrained_model` to set a specific pretrained model to load. For example, 22 | 23 | ``` 24 | python main.py --phase test --n_blocks 28 --block res --pretrained_model /path/to/pretrained_model --data_dir /path/to/modelnet40 25 | ``` 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Kezhi Kong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /deep_gcns_torch/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 DeepGCNs.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /deep_gcns_torch/deepgcn_env_install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # make sure command is : source deepgcn_env_install.sh 3 | 4 | # install anaconda3. 5 | # cd ~/ 6 | # wget https://repo.anaconda.com/archive/Anaconda3-2019.07-Linux-x86_64.sh 7 | # bash Anaconda3-2019.07-Linux-x86_64.sh 8 | 9 | 10 | source ~/.bashrc 11 | 12 | # make sure system cuda version is the same with pytorch cuda 13 | # follow the instruction of Pyotrch Geometric: https://pytorch-geometric.readthedocs.io/en/latest/notes/installation.html 14 | export PATH=/usr/local/cuda-10.0/bin:$PATH 15 | export LD_LIBRARY_PATH=/usr/local/cuda-10.0/lib64:$LD_LIBRARY_PATH 16 | 17 | conda create -n deepgcn 18 | conda activate deepgcn 19 | # make sure pytorch version >=1.4.0 20 | conda install -y pytorch=1.4.0 torchvision cudatoolkit=10.0 tensorflow=1.14.0 python=3.7 -c pytorch 21 | 22 | # command to install pytorch geometric, please refer to the official website for latest installation. 23 | # https://pytorch-geometric.readthedocs.io/en/latest/notes/installation.html 24 | CUDA=cu100 25 | pip install torch-scatter==latest+${CUDA} -f https://pytorch-geometric.com/whl/torch-1.4.0.html 26 | pip install torch-sparse==latest+${CUDA} -f https://pytorch-geometric.com/whl/torch-1.4.0.html 27 | pip install torch-spline-conv==latest+${CUDA} -f https://pytorch-geometric.com/whl/torch-1.4.0.html 28 | pip install torch-cluster==1.4.5 29 | pip install torch-geometric 30 | 31 | pip install --upgrade tensorflow-graphics 32 | # install useful modules 33 | pip install requests # sometimes pytorch geometric forget to install it, but it is in need 34 | pip install tqdm 35 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/deeper_gcn_env.sh: -------------------------------------------------------------------------------- 1 | # conda create a new environment 2 | # make sure system cuda version is the same with pytorch cuda 3 | # follow the instruction of Pyotrch Geometric: https://pytorch-geometric.readthedocs.io/en/latest/notes/installation.html 4 | export PATH=/usr/local/cuda-10.1/bin:$PATH 5 | export LD_LIBRARY_PATH=/usr/local/cuda-10.1/lib64:$LD_LIBRARY_PATH 6 | 7 | conda create --name deeper_gcn_env python=3.7 8 | # activate this enviroment 9 | conda activate deeper_gcn_env 10 | 11 | conda install -y pytorch=1.5.0 torchvision cudatoolkit=10.1 -c pytorch 12 | # test if pytorch is installed successfully 13 | python -c "import torch; print(torch.__version__)" 14 | nvcc --version # should be same with that of torch_version_cuda (they should be the same) 15 | python -c "import torch; print(torch.version.cuda)" 16 | 17 | CUDA=cu101 18 | pip install torch-scatter==latest+${CUDA} -f https://pytorch-geometric.com/whl/torch-1.5.0.html 19 | ## install torch-sparse 20 | pip install torch-sparse==latest+${CUDA} -f https://pytorch-geometric.com/whl/torch-1.5.0.html 21 | ## install torch-cluster 22 | pip install torch-cluster==latest+${CUDA} -f https://pytorch-geometric.com/whl/torch-1.5.0.html 23 | ## install torch-spline-conv 24 | pip install torch-spline-conv==latest+${CUDA} -f https://pytorch-geometric.com/whl/torch-1.5.0.html 25 | pip install torch-geometric 26 | 27 | pip install tqdm 28 | pip install ogb 29 | ### check the version of ogb installed, if it is not the latest 30 | python -c "import ogb; print(ogb.__version__)" 31 | # please update the version by running 32 | pip install -U ogb 33 | -------------------------------------------------------------------------------- /ogb/graphproppred/mol/README.md: -------------------------------------------------------------------------------- 1 | # ogbg-molhiv 2 | 3 | To train baselines with FLAG in the default setup, run 4 | 5 | **GCN+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/graphproppred/mol). 6 | 7 | python main_pyg.py --dataset ogbg-molhiv --gnn gcn --step-size 1e-2 8 | 9 | **GCN+V+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/graphproppred/mol). 10 | 11 | python main_pyg.py --dataset ogbg-molhiv --gnn gcn-virtual --step-size 1e-3 12 | 13 | **GIN+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/graphproppred/mol). 14 | 15 | python main_pyg.py --dataset ogbg-molhiv --gnn gin --step-size 5e-3 16 | 17 | **GIN+V+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/graphproppred/mol). 18 | 19 | python main_pyg.py --dataset ogbg-molhiv --gnn gin-virtual --step-size 1e-3 20 | 21 | 22 | # ogbg-molpcba 23 | 24 | To train baselines with FLAG in the default setup, run 25 | 26 | **GCN+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/graphproppred/mol). 27 | 28 | python main_pyg.py --dataset ogbg-molpcba --gnn gcn --step-size 8e-3 29 | 30 | **GCN+V+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/graphproppred/mol). 31 | 32 | python main_pyg.py --dataset ogbg-molpcba --gnn gcn-virtual --step-size 8e-3 33 | 34 | **GIN+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/graphproppred/mol). 35 | 36 | python main_pyg.py --dataset ogbg-molpcba --gnn gin --step-size 8e-3 37 | 38 | **GIN+V+FLAG**, the baseline model [here](https://github.com/snap-stanford/ogb/tree/master/examples/graphproppred/mol). 39 | 40 | python main_pyg.py --dataset ogbg-molpcba --gnn gin-virtual --step-size 8e-3 41 | 42 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbn_products/test.py: -------------------------------------------------------------------------------- 1 | import __init__ 2 | from ogb.nodeproppred import Evaluator 3 | import torch 4 | from torch_geometric.utils import add_self_loops 5 | from args import ArgsInit 6 | from ogb.nodeproppred import PygNodePropPredDataset 7 | from model import DeeperGCN 8 | 9 | 10 | @torch.no_grad() 11 | def test(model, x, edge_index, y_true, split_idx, evaluator): 12 | # test on CPU 13 | model.eval() 14 | out = model(x, edge_index) 15 | 16 | y_pred = out.argmax(dim=-1, keepdim=True) 17 | 18 | train_acc = evaluator.eval({ 19 | 'y_true': y_true[split_idx['train']], 20 | 'y_pred': y_pred[split_idx['train']], 21 | })['acc'] 22 | valid_acc = evaluator.eval({ 23 | 'y_true': y_true[split_idx['valid']], 24 | 'y_pred': y_pred[split_idx['valid']], 25 | })['acc'] 26 | test_acc = evaluator.eval({ 27 | 'y_true': y_true[split_idx['test']], 28 | 'y_pred': y_pred[split_idx['test']], 29 | })['acc'] 30 | 31 | return train_acc, valid_acc, test_acc 32 | 33 | 34 | def main(): 35 | 36 | args = ArgsInit().args 37 | 38 | dataset = PygNodePropPredDataset(name=args.dataset, root=args.data_folder) 39 | graph = dataset[0] 40 | 41 | if args.self_loop: 42 | graph.edge_index = add_self_loops(edge_index=graph.edge_index, 43 | num_nodes=graph.num_nodes)[0] 44 | split_idx = dataset.get_idx_split() 45 | 46 | evaluator = Evaluator(args.dataset) 47 | 48 | args.in_channels = graph.x.size(-1) 49 | args.num_tasks = dataset.num_classes 50 | 51 | print(args) 52 | 53 | model = DeeperGCN(args) 54 | 55 | print(model) 56 | 57 | model.load_state_dict(torch.load(args.model_load_path)['model_state_dict']) 58 | result = test(model, graph.x, graph.edge_index, graph.y, split_idx, evaluator) 59 | print(result) 60 | model.print_params(final=True) 61 | 62 | 63 | if __name__ == "__main__": 64 | main() 65 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/sem_seg_sparse/README.md: -------------------------------------------------------------------------------- 1 | ## [Semantic segmentation of indoor scenes](https://arxiv.org/pdf/1904.03751.pdf) 2 | 3 |

4 | 5 |

6 | 7 | Sem_seg_dense and sem_seg_sparse are both for the semantic segmentation task. The difference between them is that the data shape is different. 8 | As for sem_seg_sparse, data shape is N x feature_size and there is a batch variable indicating the batch of each node. 9 | But for sem_seg_dense, data shape is Batch_size x feature_size x nodes_num x 1. 10 | 11 | In gcn_lib, there are two folders: dense and sparse. They are used for different data shapes above. 12 | 13 | 14 | ### Note 15 | We suggest to use sem_seg_dense. It is more efficient. 16 | 17 | ### Train 18 | We keep using 2 Tesla V100 GPUs for distributed training. Run: 19 | ``` 20 | CUDA_VISIBLE_DEVICES=0,1 python examples/sem_seg_sparse/train.py --multi_gpus --phase train --train_path /data/deepgcn/S3DIS 21 | ``` 22 | Note on `--train_path`: Make sure you have the folder. Just need to set `--train_path path/to/data`, dataset will be downloaded automatically. 23 | 24 | If you want to train model with other gcn layers (for example mrgcn), run 25 | ``` 26 | python train.py --conv mr --multi_gpus --phase train --train_path /data/deepgcn/S3DIS 27 | ``` 28 | Other parameters for changing the architecture are: 29 | ``` 30 | --block graph backbone block type {res, dense} 31 | --conv graph conv layer {edge, mr, sage, gin, gcn, gat} 32 | --n_filters number of channels of deep features, default is 64 33 | --n_blocks number of basic blocks, default is 28 34 | ``` 35 | 36 | ### Evaluation 37 | Qucik test on area 5, run: 38 | 39 | ``` 40 | python test.py --pretrained_model checkpoints/densedeepgcn-res-edge-ckpt_50.pth --batch_size 1 --test_path /data/deepgcn/S3DIS 41 | ``` 42 | 43 | #### Visualization 44 | Coming soon!! 45 | 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Robust Optimization as Data Augmentation for Large-scale Graphs 2 | 3 | This is the official repo for the paper [Robust Optimization as Data Augmentation for Large-scale Graphs](https://arxiv.org/abs/2010.09891), accepted at CVPR2022. 4 | 5 | **TL;DR:** FLAG augments node features to generalize GNNs on both node and graph classification tasks. 6 | 7 | ### Highlights 8 | 9 | - **Simple**, adding just a dozen lines of code 10 | - **Genera**l, applicable to any GNN baseline 11 | - **Versatile**, working on both node and graph classification tasks 12 | - **Scalable**, minimum memory overhead, working on the original infrastructure 13 | 14 | ## Experiments 15 | 16 | To reproduce experimental results for **DeeperGCN**, visit [here](https://github.com/devnkong/FLAG/tree/main/deep_gcns_torch/examples/ogb). 17 | 18 | Other baselines including **GCN**, **GraphSAGE**, **GAT**, **GIN**, **MLP**, etc. are available [here](https://github.com/devnkong/FLAG/tree/main/ogb). 19 | 20 | To view the empirical performance of FLAG, please visit the Open Graph Benchmark [Node](https://ogb.stanford.edu/docs/leader_nodeprop/) and [Graph](https://ogb.stanford.edu/docs/leader_graphprop/) classification leaderboards. 21 | 22 | ### Requirements 23 | 24 | - ogb>=1.2.3 25 | - torch-geometric>=1.6.1 26 | - torch>=1.5.0 27 | 28 | ## Citing FLAG 29 | 30 | If you find FLAG useful, please cite our paper. 31 | 32 | ``` 33 | @misc{https://doi.org/10.48550/arxiv.2010.09891, 34 | doi = {10.48550/ARXIV.2010.09891}, 35 | url = {https://arxiv.org/abs/2010.09891}, 36 | author = {Kong, Kezhi and Li, Guohao and Ding, Mucong and Wu, Zuxuan and Zhu, Chen and Ghanem, Bernard and Taylor, Gavin and Goldstein, Tom}, 37 | keywords = {Machine Learning (cs.LG), Machine Learning (stat.ML), FOS: Computer and information sciences, FOS: Computer and information sciences}, 38 | title = {Robust Optimization as Data Augmentation for Large-scale Graphs}, 39 | publisher = {arXiv}, 40 | year = {2020}, 41 | copyright = {arXiv.org perpetual, non-exclusive license} 42 | } 43 | 44 | ``` 45 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/attacks.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn.functional as F 3 | import pdb 4 | 5 | def flag(model_forward, perturb_shape, y, args, optimizer, device, criterion) : 6 | model, forward = model_forward 7 | model.train() 8 | optimizer.zero_grad() 9 | 10 | perturb = torch.FloatTensor(*perturb_shape).uniform_(-args.step_size, args.step_size).to(device) 11 | perturb.requires_grad_() 12 | out = forward(perturb) 13 | loss = criterion(out, y) 14 | loss /= args.m 15 | 16 | for _ in range(args.m-1): 17 | loss.backward() 18 | perturb_data = perturb.detach() + args.step_size * torch.sign(perturb.grad.detach()) 19 | perturb.data = perturb_data.data 20 | perturb.grad[:] = 0 21 | 22 | out = forward(perturb) 23 | loss = criterion(out, y) 24 | loss /= args.m 25 | 26 | loss.backward() 27 | optimizer.step() 28 | 29 | return loss, out 30 | 31 | def flag_biased(model_forward, perturb_shape, y, args, optimizer, device, criterion, training_idx) : 32 | unlabel_idx = list(set(range(perturb_shape[0])) - set(training_idx)) 33 | 34 | model, forward = model_forward 35 | model.train() 36 | optimizer.zero_grad() 37 | 38 | perturb = torch.FloatTensor(*perturb_shape).uniform_(-args.step_size, args.step_size).to(device) 39 | perturb.data[unlabel_idx] *= args.amp 40 | perturb.requires_grad_() 41 | out = forward(perturb) 42 | loss = criterion(out, y) 43 | loss /= args.m 44 | 45 | for _ in range(args.m-1): 46 | loss.backward() 47 | 48 | perturb_data_training = perturb[training_idx].detach() + args.step_size * torch.sign(perturb.grad[training_idx].detach()) 49 | perturb.data[training_idx] = perturb_data_training.data 50 | 51 | perturb_data_unlabel = perturb[unlabel_idx].detach() + args.amp*args.step_size * torch.sign(perturb.grad[unlabel_idx].detach()) 52 | perturb.data[unlabel_idx] = perturb_data_unlabel.data 53 | 54 | perturb.grad[:] = 0 55 | out = forward(perturb) 56 | loss = criterion(out, y) 57 | loss /= args.m 58 | 59 | loss.backward() 60 | optimizer.step() 61 | 62 | return loss, out 63 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/sem_seg_sparse/test.py: -------------------------------------------------------------------------------- 1 | import __init__ 2 | from tqdm import tqdm 3 | import numpy as np 4 | import torch 5 | import torch_geometric.datasets as GeoData 6 | from torch_geometric.data import DataLoader 7 | import torch_geometric.transforms as T 8 | from config import OptInit 9 | from architecture import SparseDeepGCN 10 | from utils.ckpt_util import load_pretrained_models 11 | import logging 12 | 13 | 14 | def main(): 15 | opt = OptInit().get_args() 16 | 17 | logging.info('===> Creating dataloader...') 18 | test_dataset = GeoData.S3DIS(opt.data_dir, 5, train=False, pre_transform=T.NormalizeScale()) 19 | test_loader = DataLoader(test_dataset, batch_size=opt.batch_size, shuffle=False, num_workers=0) 20 | opt.n_classes = test_loader.dataset.num_classes 21 | if opt.no_clutter: 22 | opt.n_classes -= 1 23 | 24 | logging.info('===> Loading the network ...') 25 | model = SparseDeepGCN(opt).to(opt.device) 26 | model, opt.best_value, opt.epoch = load_pretrained_models(model, opt.pretrained_model, opt.phase) 27 | 28 | logging.info('===> Start Evaluation ...') 29 | test(model, test_loader, opt) 30 | 31 | 32 | def test(model, loader, opt): 33 | Is = np.empty((len(loader), opt.n_classes)) 34 | Us = np.empty((len(loader), opt.n_classes)) 35 | 36 | model.eval() 37 | with torch.no_grad(): 38 | for i, data in enumerate(tqdm(loader)): 39 | data = data.to(opt.device) 40 | out = model(data) 41 | pred = out.max(dim=1)[1] 42 | 43 | pred_np = pred.cpu().numpy() 44 | target_np = data.y.cpu().numpy() 45 | 46 | for cl in range(opt.n_classes): 47 | I = np.sum(np.logical_and(pred_np == cl, target_np == cl)) 48 | U = np.sum(np.logical_or(pred_np == cl, target_np == cl)) 49 | Is[i, cl] = I 50 | Us[i, cl] = U 51 | 52 | ious = np.divide(np.sum(Is, 0), np.sum(Us, 0)) 53 | ious[np.isnan(ious)] = 1 54 | for cl in range(opt.n_classes): 55 | logging.info("===> mIOU for class {}: {}".format(cl, ious[cl])) 56 | logging.info("===> mIOU is {}".format(np.mean(ious))) 57 | 58 | 59 | if __name__ == '__main__': 60 | main() 61 | 62 | 63 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ppi/README.md: -------------------------------------------------------------------------------- 1 | ## [Graph Learning on Biological Networks](https://arxiv.org/pdf/1910.06849.pdf) 2 | 3 |

4 | 5 |

6 | 7 | ### Train 8 | We train each model on one tesla V100. 9 | 10 | For training the default ResMRConv-14 with 64 filters, run 11 | ``` 12 | python -u examples/ppi/main.py --phase train --data_dir /data/deepgcn/ppi 13 | ``` 14 | If you want to train model with other gcn layers (for example EdgeConv, 28 layers, 256 channels in the first layer, with dense connection), run 15 | ``` 16 | python -u examples/ppi/main.py --phase train --conv edge --data_dir /data/deepgcn/ppi --block dense --n_filters 256 --n_blocks 28 17 | ``` 18 | 19 | Just need to set `--data_dir` into your data folder, dataset will be downloaded automatically. 20 | Other parameters for changing the architecture are: 21 | ``` 22 | --block graph backbone block type {res, plain, dense} 23 | --conv graph conv layer {edge, mr, sage, gin, gcn, gat} 24 | --n_filters number of channels of deep features, default is 64 25 | --n_blocks number of basic blocks, default is 28 26 | ``` 27 | ### Test 28 | #### Pretrained Models 29 | Our pretrained models can be found from [Goolge Cloud](https://drive.google.com/drive/folders/1LoT1B9FDgylUffHY8K43FFfred-luZaz?usp=sharing). 30 | 31 | The Naming format of our pretrained model: `task-connection-conv_type-n_blocks-n_filters_phase_best.pth`, eg. `ppi-res-mr-28-256_val_best.pth`, which means PPI node classification task, with residual connection, convolution is MRGCN, 28 layers, 256 channels, the best pretrained model found in validation dataset. 32 | 33 | Use parameter `--pretrained_model` to set the specific pretrained model you want. 34 | ``` 35 | python -u examples/ppi/main.py --phase test --pretrained_model checkpoints/ppi-res-mr-28-256_val_best.pth --data_dir /data/deepgcn/ppi --n_filters 256 --n_blocks 28 --conv mr --block res 36 | ``` 37 | 38 | ``` 39 | python -u examples/ppi/main.py --phase test --pretrained_model checkpoints/ppi-dense-mr-14-256_val_best.pth --data_dir /data/deepgcn/ppi --n_filters 256 --n_blocks 14 --conv mr --block dense 40 | ``` 41 | Please also specify the number of blocks and filters according to the name of pretrained models. 42 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/part_sem_seg/visualize.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 5 | sys.path.append(ROOT_DIR) 6 | import os.path as osp 7 | from utils.pc_viz import visualize_part_seg 8 | import argparse 9 | 10 | 11 | category_names = ['Bag', 'Bed', 'Bottle', 'Bowl', 'Chair', 'Clock', 'Dishwasher', 'Display', 'Door', 'Earphone', # 0-9 12 | 'Faucet', 'Hat', 'Keyboard', 'Knife', 'Lamp', 'Laptop', 'Microwave', 'Mug', 'Refrigerator', 'Scissors', # 10-19 13 | 'StorageFurniture', 'Table', 'TrashCan', 'Vase'] # 20-23 14 | 15 | parser = argparse.ArgumentParser(description='Qualitative comparision of ResGCN ' 16 | 'against PlainGCN on PartNet segmentation') 17 | 18 | # dir_path set to the location of the result folder. 19 | # result folder should have such structure: 20 | # result 21 | # ├── plain # result folder for PlainGCN 22 | # ├── Bed # the obj director of category Bed 23 | # ├── Bed_0_pred.obj 24 | # ├── res # result folder for ResGCN 25 | # ├── Bed # the obj director of category Bed 26 | # ├── Bed_0_pred.obj 27 | 28 | parser.add_argument('--category', type=int, default=4) 29 | parser.add_argument('--obj_no', default=0, type=int, help='NO. of which obj in a given category to visualize') 30 | parser.add_argument('--dir_path', default='../result', type=str, help='path to the result') 31 | parser.add_argument('--folders', default='plain,res', type=str, 32 | help='use "," to separate different folders, eg. "res,plain"') 33 | args = parser.parse_args() 34 | 35 | category = category_names[args.category] 36 | obj_no = args.obj_no 37 | folders = list(map(lambda x: x.strip(), args.folders.split(','))) 38 | 39 | folder_paths = list(map(lambda x: osp.join(args.dir_path, x, category), folders)) 40 | 41 | file_name_pred = '_'.join([category, str(obj_no), 'pred.obj']) 42 | file_name_gt = '_'.join([category, str(obj_no), 'gt.obj']) 43 | 44 | # show Ground Truth, PlainGCN, ResGCN 45 | visualize_part_seg(file_name_pred, 46 | file_name_gt, 47 | folder_paths, 48 | limit=-1, 49 | text=['Ground Truth', 'PlainGCN-28', 'ResGCN-28'], 50 | interactive=True, 51 | orientation='horizontal') 52 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbn_arxiv/test.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch_geometric.utils import to_undirected, add_self_loops 3 | from ogb.nodeproppred import PygNodePropPredDataset 4 | from ogb.nodeproppred import Evaluator 5 | from args import ArgsInit 6 | from model import DeeperGCN 7 | 8 | 9 | @torch.no_grad() 10 | def test(model, x, edge_index, y_true, split_idx, evaluator): 11 | model.eval() 12 | out = model(x, edge_index) 13 | 14 | y_pred = out.argmax(dim=-1, keepdim=True) 15 | 16 | train_acc = evaluator.eval({ 17 | 'y_true': y_true[split_idx['train']], 18 | 'y_pred': y_pred[split_idx['train']], 19 | })['acc'] 20 | valid_acc = evaluator.eval({ 21 | 'y_true': y_true[split_idx['valid']], 22 | 'y_pred': y_pred[split_idx['valid']], 23 | })['acc'] 24 | test_acc = evaluator.eval({ 25 | 'y_true': y_true[split_idx['test']], 26 | 'y_pred': y_pred[split_idx['test']], 27 | })['acc'] 28 | 29 | return train_acc, valid_acc, test_acc 30 | 31 | 32 | def main(): 33 | 34 | args = ArgsInit().args 35 | 36 | if args.use_gpu: 37 | device = torch.device("cuda:" + str(args.device)) if torch.cuda.is_available() else torch.device("cpu") 38 | else: 39 | device = torch.device('cpu') 40 | 41 | dataset = PygNodePropPredDataset(name=args.dataset) 42 | data = dataset[0] 43 | split_idx = dataset.get_idx_split() 44 | 45 | evaluator = Evaluator(args.dataset) 46 | 47 | x = data.x.to(device) 48 | y_true = data.y.to(device) 49 | 50 | edge_index = data.edge_index.to(device) 51 | edge_index = to_undirected(edge_index, data.num_nodes) 52 | 53 | if args.self_loop: 54 | edge_index = add_self_loops(edge_index, num_nodes=data.num_nodes)[0] 55 | 56 | args.in_channels = data.x.size(-1) 57 | args.num_tasks = dataset.num_classes 58 | 59 | print(args) 60 | 61 | model = DeeperGCN(args) 62 | 63 | model.load_state_dict(torch.load(args.model_load_path)['model_state_dict']) 64 | model.to(device) 65 | 66 | result = test(model, x, edge_index, y_true, split_idx, evaluator) 67 | train_accuracy, valid_accuracy, test_accuracy = result 68 | 69 | print({'Train': train_accuracy, 70 | 'Validation': valid_accuracy, 71 | 'Test': test_accuracy}) 72 | 73 | model.print_params(final=True) 74 | 75 | 76 | if __name__ == "__main__": 77 | main() 78 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/sem_seg_dense/test.py: -------------------------------------------------------------------------------- 1 | import __init__ 2 | from tqdm import tqdm 3 | import numpy as np 4 | import torch 5 | import torch_geometric.datasets as GeoData 6 | from torch_geometric.data import DenseDataLoader 7 | import torch_geometric.transforms as T 8 | from config import OptInit 9 | from architecture import DenseDeepGCN 10 | from utils.ckpt_util import load_pretrained_models 11 | import logging 12 | 13 | 14 | def main(): 15 | opt = OptInit().get_args() 16 | 17 | logging.info('===> Creating dataloader...') 18 | test_dataset = GeoData.S3DIS(opt.data_dir, opt.area, train=False, pre_transform=T.NormalizeScale()) 19 | test_loader = DenseDataLoader(test_dataset, batch_size=opt.batch_size, shuffle=False, num_workers=0) 20 | opt.n_classes = test_loader.dataset.num_classes 21 | if opt.no_clutter: 22 | opt.n_classes -= 1 23 | 24 | logging.info('===> Loading the network ...') 25 | model = DenseDeepGCN(opt).to(opt.device) 26 | model, opt.best_value, opt.epoch = load_pretrained_models(model, opt.pretrained_model, opt.phase) 27 | 28 | logging.info('===> Start Evaluation ...') 29 | test(model, test_loader, opt) 30 | 31 | 32 | def test(model, loader, opt): 33 | Is = np.empty((len(loader), opt.n_classes)) 34 | Us = np.empty((len(loader), opt.n_classes)) 35 | 36 | model.eval() 37 | with torch.no_grad(): 38 | for i, data in enumerate(tqdm(loader)): 39 | data = data.to(opt.device) 40 | inputs = torch.cat((data.pos.transpose(2, 1).unsqueeze(3), data.x.transpose(2, 1).unsqueeze(3)), 1) 41 | gt = data.y 42 | 43 | out = model(inputs) 44 | pred = out.max(dim=1)[1] 45 | 46 | pred_np = pred.cpu().numpy() 47 | target_np = gt.cpu().numpy() 48 | 49 | for cl in range(opt.n_classes): 50 | cur_gt_mask = (target_np == cl) 51 | cur_pred_mask = (pred_np == cl) 52 | I = np.sum(np.logical_and(cur_pred_mask, cur_gt_mask), dtype=np.float32) 53 | U = np.sum(np.logical_or(cur_pred_mask, cur_gt_mask), dtype=np.float32) 54 | Is[i, cl] = I 55 | Us[i, cl] = U 56 | 57 | ious = np.divide(np.sum(Is, 0), np.sum(Us, 0)) 58 | ious[np.isnan(ious)] = 1 59 | for cl in range(opt.n_classes): 60 | logging.info("===> mIOU for class {}: {}".format(cl, ious[cl])) 61 | logging.info("===> mIOU is {}".format(np.mean(ious))) 62 | 63 | 64 | if __name__ == '__main__': 65 | main() 66 | 67 | 68 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ppi/architecture.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch.nn import Linear as Lin, Sequential as Seq 3 | from gcn_lib.sparse import MultiSeq, MLP, GraphConv, ResGraphBlock, DenseGraphBlock 4 | 5 | 6 | class DeepGCN(torch.nn.Module): 7 | """ 8 | static graph 9 | 10 | """ 11 | def __init__(self, opt): 12 | super(DeepGCN, self).__init__() 13 | channels = opt.n_filters 14 | act = opt.act 15 | norm = opt.norm 16 | bias = opt.bias 17 | conv = opt.conv 18 | heads = opt.n_heads 19 | c_growth = 0 20 | self.n_blocks = opt.n_blocks 21 | self.head = GraphConv(opt.in_channels, channels, conv, act, norm, bias, heads) 22 | 23 | res_scale = 1 if opt.block.lower() == 'res' else 0 24 | if opt.block.lower() == 'dense': 25 | c_growth = channels 26 | self.backbone = MultiSeq(*[DenseGraphBlock(channels+i*c_growth, c_growth, conv, act, norm, bias, heads) 27 | for i in range(self.n_blocks-1)]) 28 | else: 29 | self.backbone = MultiSeq(*[ResGraphBlock(channels, conv, act, norm, bias, heads, res_scale) 30 | for _ in range(self.n_blocks-1)]) 31 | fusion_dims = int(channels * self.n_blocks + c_growth * ((1 + self.n_blocks - 1) * (self.n_blocks - 1) / 2)) 32 | self.fusion_block = MLP([fusion_dims, 1024], act, None, bias) 33 | self.prediction = Seq(*[MLP([1+fusion_dims, 512], act, norm, bias), torch.nn.Dropout(p=opt.dropout), 34 | MLP([512, 256], act, norm, bias), torch.nn.Dropout(p=opt.dropout), 35 | MLP([256, opt.n_classes], None, None, bias)]) 36 | self.model_init() 37 | 38 | def model_init(self): 39 | for m in self.modules(): 40 | if isinstance(m, Lin): 41 | torch.nn.init.kaiming_normal_(m.weight) 42 | m.weight.requires_grad = True 43 | if m.bias is not None: 44 | m.bias.data.zero_() 45 | m.bias.requires_grad = True 46 | 47 | def forward(self, data): 48 | x, edge_index, batch = data.x, data.edge_index, data.batch 49 | feats = [self.head(x, edge_index)] 50 | for i in range(self.n_blocks-1): 51 | feats.append(self.backbone[i](feats[-1], edge_index)[0]) 52 | feats = torch.cat(feats, 1) 53 | fusion, _ = torch.max(self.fusion_block(feats), 1, keepdim=True) 54 | out = self.prediction(torch.cat((feats, fusion), 1)) 55 | return out 56 | 57 | 58 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/sem_seg_dense/README.md: -------------------------------------------------------------------------------- 1 | ## [Semantic segmentation of indoor scenes](https://arxiv.org/pdf/1904.03751.pdf) 2 | 3 |

4 | 5 |

6 | 7 | 8 | Sem_seg_dense and sem_seg_sparse are both for the semantic segmentation task. The difference between them is that the data shape is different. 9 | As for sem_seg_sparse, data shape is N x feature_size and there is a batch variable indicating the batch of each node. 10 | But for sem_seg_dense, data shape is Batch_size x feature_size x nodes_num x 1. 11 | In gcn_lib, there are two folders: dense and sparse. They are used for different data shapes above. 12 | 13 | 14 | ### Train 15 | We keep using 2 Tesla V100 GPUs for distributed training. 16 | ``` 17 | cd examples/sem_seg/dense 18 | ``` 19 | 20 | Run: 21 | ``` 22 | CUDA_VISIBLE_DEVICES=0,1 python train.py --multi_gpus --phase train --data_dir /data/deepgcn/S3DIS --batch_size 16 23 | ``` 24 | Note on `--data_dir`: Make sure you have the folder. Just need to set `--data_dir path/to/data`, dataset will be downloaded automatically. 25 | 26 | If you want to train model with other gcn layers (for example mrgcn), run 27 | ``` 28 | python train.py --conv mr --multi_gpus --phase train --data_dir /data/deepgcn/S3DIS 29 | ``` 30 | Other parameters for changing the architecture are: 31 | ``` 32 | --block graph backbone block type {res, plain, dense} 33 | --conv graph conv layer {edge, mr} 34 | --n_filters number of channels of deep features, default is 64 35 | --n_blocks number of basic blocks, default is 28 36 | ``` 37 | 38 | A shallow version of DeepGCN (ResGCN-7) could be trained by the command below: 39 | ``` 40 | CUDA_VISIBLE_DEVICES=0 python train.py --multi_gpus --phase train --data_dir /data/deepgcn/S3DIS --batch_size 16 --n_blocks 7 41 | ``` 42 | 43 | 44 | ### Evaluation 45 | Qucik test on area 5, run: 46 | 47 | ``` 48 | python test.py --pretrained_model checkpoints/sem_seg_dense-res-edge-28-64-ckpt_best_model.pth --batch_size 32 --test_path /data/deepgcn/S3DIS 49 | ``` 50 | 51 | #### Pretrained Models 52 | Our pretrained model is available here [google driver](https://drive.google.com/open?id=1iAJbHqiNwc4nJlP67sp1xLkl5EtC4PU_) 53 | 54 | Note: Please use our Tensorflow code if you want to reproduce the same result in the paper. 55 | The performance of pytorch code is slightly worse than tensorflow. mIOU is 52.11% compared to 52.49% in the tensorflow version. 56 | ``` 57 | python test.py --pretrained_model checkpoints/sem_seg_dense-res-edge-28-64-ckpt_best_model.pth --batch_size 32 --test_path /data/deepgcn/S3DIS 58 | ``` 59 | Lower the batch size if out of memory. The batch size will not influence the test results. 60 | 61 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/modelnet_cls/data.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import glob 4 | import h5py 5 | import numpy as np 6 | from torch.utils.data import Dataset 7 | 8 | 9 | def download(data_dir): 10 | if not os.path.exists(data_dir): 11 | os.makedirs(data_dir) 12 | if not os.path.exists(os.path.join(data_dir, 'modelnet40_ply_hdf5_2048')): 13 | www = 'https://shapenet.cs.stanford.edu/media/modelnet40_ply_hdf5_2048.zip' 14 | zipfile = os.path.basename(www) 15 | os.system('wget %s; unzip %s' % (www, zipfile)) 16 | os.system('mv %s %s' % (zipfile[:-4], data_dir)) 17 | os.system('rm %s' % (zipfile)) 18 | 19 | 20 | def load_data(data_dir, partition): 21 | download(data_dir) 22 | all_data = [] 23 | all_label = [] 24 | for h5_name in glob.glob(os.path.join(data_dir, 'modelnet40_ply_hdf5_2048', 'ply_data_%s*.h5'%partition)): 25 | with h5py.File(h5_name, 'r') as f: 26 | data = f['data'][:].astype('float32') 27 | label = f['label'][:].astype('int64') 28 | all_data.append(data) 29 | all_label.append(label) 30 | all_data = np.concatenate(all_data, axis=0) 31 | all_label = np.concatenate(all_label, axis=0) 32 | return all_data, all_label 33 | 34 | 35 | def translate_pointcloud(pointcloud): 36 | """ 37 | for scaling and shifting the point cloud 38 | :param pointcloud: 39 | :return: 40 | """ 41 | scale = np.random.uniform(low=2. / 3., high=3. / 2., size=[3]) 42 | shift = np.random.uniform(low=-0.2, high=0.2, size=[3]) 43 | translated_pointcloud = np.add(np.multiply(pointcloud, scale), shift).astype('float32') 44 | return translated_pointcloud 45 | 46 | 47 | class ModelNet40(Dataset): 48 | """ 49 | This is the data loader for ModelNet 40 50 | ModelNet40 contains 12,311 meshed CAD models from 40 categories. 51 | 52 | num_points: 1024 by default 53 | data_dir 54 | paritition: train or test 55 | """ 56 | def __init__(self, num_points=1024, data_dir="/data/deepgcn/modelnet40", partition='train'): 57 | self.data, self.label = load_data(data_dir, partition) 58 | self.num_points = num_points 59 | self.partition = partition 60 | 61 | def __getitem__(self, item): 62 | pointcloud = self.data[item][:self.num_points] 63 | label = self.label[item] 64 | if self.partition == 'train': 65 | pointcloud = translate_pointcloud(pointcloud) 66 | np.random.shuffle(pointcloud) 67 | return pointcloud, label 68 | 69 | def __len__(self): 70 | return self.data.shape[0] 71 | 72 | def num_classes(self): 73 | return np.max(self.label) + 1 74 | 75 | 76 | if __name__ == '__main__': 77 | train = ModelNet40(1024) 78 | test = ModelNet40(1024, 'test') 79 | for data, label in train: 80 | print(data.shape) 81 | print(label.shape) 82 | -------------------------------------------------------------------------------- /ogb/graphproppred/mol/gnn.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch_geometric.nn import MessagePassing 3 | from torch_geometric.nn import global_add_pool, global_mean_pool, global_max_pool, GlobalAttention, Set2Set 4 | import torch.nn.functional as F 5 | from torch_geometric.nn.inits import uniform 6 | 7 | from conv import GNN_node, GNN_node_Virtualnode 8 | 9 | from torch_scatter import scatter_mean 10 | 11 | class GNN(torch.nn.Module): 12 | 13 | def __init__(self, num_tasks, num_layer = 5, emb_dim = 300, 14 | gnn_type = 'gin', virtual_node = True, residual = False, drop_ratio = 0.5, JK = "last", graph_pooling = "mean"): 15 | ''' 16 | num_tasks (int): number of labels to be predicted 17 | virtual_node (bool): whether to add virtual node or not 18 | ''' 19 | 20 | super(GNN, self).__init__() 21 | 22 | self.num_layer = num_layer 23 | self.drop_ratio = drop_ratio 24 | self.JK = JK 25 | self.emb_dim = emb_dim 26 | self.num_tasks = num_tasks 27 | self.graph_pooling = graph_pooling 28 | 29 | if self.num_layer < 2: 30 | raise ValueError("Number of GNN layers must be greater than 1.") 31 | 32 | ### GNN to generate node embeddings 33 | if virtual_node: 34 | self.gnn_node = GNN_node_Virtualnode(num_layer, emb_dim, JK = JK, drop_ratio = drop_ratio, residual = residual, gnn_type = gnn_type) 35 | else: 36 | self.gnn_node = GNN_node(num_layer, emb_dim, JK = JK, drop_ratio = drop_ratio, residual = residual, gnn_type = gnn_type) 37 | 38 | 39 | ### Pooling function to generate whole-graph embeddings 40 | if self.graph_pooling == "sum": 41 | self.pool = global_add_pool 42 | elif self.graph_pooling == "mean": 43 | self.pool = global_mean_pool 44 | elif self.graph_pooling == "max": 45 | self.pool = global_max_pool 46 | elif self.graph_pooling == "attention": 47 | self.pool = GlobalAttention(gate_nn = torch.nn.Sequential(torch.nn.Linear(emb_dim, 2*emb_dim), torch.nn.BatchNorm1d(2*emb_dim), torch.nn.ReLU(), torch.nn.Linear(2*emb_dim, 1))) 48 | elif self.graph_pooling == "set2set": 49 | self.pool = Set2Set(emb_dim, processing_steps = 2) 50 | else: 51 | raise ValueError("Invalid graph pooling type.") 52 | 53 | if graph_pooling == "set2set": 54 | self.graph_pred_linear = torch.nn.Linear(2*self.emb_dim, self.num_tasks) 55 | else: 56 | self.graph_pred_linear = torch.nn.Linear(self.emb_dim, self.num_tasks) 57 | 58 | def forward(self, batched_data, perturb=None): 59 | h_node = self.gnn_node(batched_data, perturb) 60 | 61 | h_graph = self.pool(h_node, batched_data.batch) 62 | 63 | return self.graph_pred_linear(h_graph) 64 | 65 | 66 | if __name__ == '__main__': 67 | GNN(num_tasks = 10) -------------------------------------------------------------------------------- /ogb/graphproppred/ppa/gnn.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch_geometric.nn import MessagePassing 3 | from torch_geometric.nn import global_add_pool, global_mean_pool, global_max_pool, GlobalAttention, Set2Set 4 | import torch.nn.functional as F 5 | from torch_geometric.nn.inits import uniform 6 | 7 | from conv import GNN_node, GNN_node_Virtualnode 8 | 9 | from torch_scatter import scatter_mean 10 | 11 | class GNN(torch.nn.Module): 12 | 13 | def __init__(self, num_class, num_layer = 5, emb_dim = 300, 14 | gnn_type = 'gin', virtual_node = True, residual = False, drop_ratio = 0.5, JK = "last", graph_pooling = "mean"): 15 | ''' 16 | num_tasks (int): number of labels to be predicted 17 | virtual_node (bool): whether to add virtual node or not 18 | ''' 19 | 20 | super(GNN, self).__init__() 21 | 22 | self.num_layer = num_layer 23 | self.drop_ratio = drop_ratio 24 | self.JK = JK 25 | self.emb_dim = emb_dim 26 | self.num_class = num_class 27 | self.graph_pooling = graph_pooling 28 | 29 | if self.num_layer < 2: 30 | raise ValueError("Number of GNN layers must be greater than 1.") 31 | 32 | ### GNN to generate node embeddings 33 | if virtual_node: 34 | self.gnn_node = GNN_node_Virtualnode(num_layer, emb_dim, JK = JK, drop_ratio = drop_ratio, residual = residual, gnn_type = gnn_type) 35 | else: 36 | self.gnn_node = GNN_node(num_layer, emb_dim, JK = JK, drop_ratio = drop_ratio, residual = residual, gnn_type = gnn_type) 37 | 38 | 39 | ### Pooling function to generate whole-graph embeddings 40 | if self.graph_pooling == "sum": 41 | self.pool = global_add_pool 42 | elif self.graph_pooling == "mean": 43 | self.pool = global_mean_pool 44 | elif self.graph_pooling == "max": 45 | self.pool = global_max_pool 46 | elif self.graph_pooling == "attention": 47 | self.pool = GlobalAttention(gate_nn = torch.nn.Sequential(torch.nn.Linear(emb_dim, 2*emb_dim), torch.nn.BatchNorm1d(2*emb_dim), torch.nn.ReLU(), torch.nn.Linear(2*emb_dim, 1))) 48 | elif self.graph_pooling == "set2set": 49 | self.pool = Set2Set(emb_dim, processing_steps = 2) 50 | else: 51 | raise ValueError("Invalid graph pooling type.") 52 | 53 | if graph_pooling == "set2set": 54 | self.graph_pred_linear = torch.nn.Linear(2*self.emb_dim, self.num_class) 55 | else: 56 | self.graph_pred_linear = torch.nn.Linear(self.emb_dim, self.num_class) 57 | 58 | def forward(self, batched_data, perturb=None): 59 | h_node = self.gnn_node(batched_data, perturb) 60 | 61 | h_graph = self.pool(h_node, batched_data.batch) 62 | 63 | return self.graph_pred_linear(h_graph) 64 | 65 | 66 | if __name__ == '__main__': 67 | GNN(num_class = 10) -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbg_mol/test.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch_geometric.data import DataLoader 3 | from model import DeeperGCN 4 | from tqdm import tqdm 5 | from args import ArgsInit 6 | from ogb.graphproppred import PygGraphPropPredDataset, Evaluator 7 | 8 | 9 | @torch.no_grad() 10 | def eval(model, device, loader, evaluator): 11 | model.eval() 12 | y_true = [] 13 | y_pred = [] 14 | 15 | for step, batch in enumerate(tqdm(loader, desc="Iteration")): 16 | batch = batch.to(device) 17 | 18 | if batch.x.shape[0] == 1: 19 | pass 20 | else: 21 | pred = model(batch) 22 | y_true.append(batch.y.view(pred.shape).detach().cpu()) 23 | y_pred.append(pred.detach().cpu()) 24 | 25 | y_true = torch.cat(y_true, dim=0).numpy() 26 | y_pred = torch.cat(y_pred, dim=0).numpy() 27 | 28 | input_dict = {"y_true": y_true, 29 | "y_pred": y_pred} 30 | 31 | return evaluator.eval(input_dict) 32 | 33 | 34 | def main(): 35 | 36 | args = ArgsInit().args 37 | 38 | if args.use_gpu: 39 | device = torch.device("cuda:" + str(args.device)) if torch.cuda.is_available() else torch.device("cpu") 40 | else: 41 | device = torch.device('cpu') 42 | 43 | dataset = PygGraphPropPredDataset(name=args.dataset) 44 | args.num_tasks = dataset.num_tasks 45 | print(args) 46 | 47 | if args.feature == 'full': 48 | pass 49 | elif args.feature == 'simple': 50 | print('using simple feature') 51 | # only retain the top two node/edge features 52 | dataset.data.x = dataset.data.x[:, :2] 53 | dataset.data.edge_attr = dataset.data.edge_attr[:, :2] 54 | 55 | 56 | split_idx = dataset.get_idx_split() 57 | 58 | evaluator = Evaluator(args.dataset) 59 | 60 | train_loader = DataLoader(dataset[split_idx["train"]], batch_size=args.batch_size, shuffle=False, 61 | num_workers=args.num_workers) 62 | valid_loader = DataLoader(dataset[split_idx["valid"]], batch_size=args.batch_size, shuffle=False, 63 | num_workers=args.num_workers) 64 | test_loader = DataLoader(dataset[split_idx["test"]], batch_size=args.batch_size, shuffle=False, 65 | num_workers=args.num_workers) 66 | 67 | model = DeeperGCN(args) 68 | 69 | model.load_state_dict(torch.load(args.model_load_path)['model_state_dict']) 70 | model.to(device) 71 | 72 | train_result = eval(model, device, train_loader, evaluator)[dataset.eval_metric] 73 | valid_result = eval(model, device, valid_loader, evaluator)[dataset.eval_metric] 74 | test_result = eval(model, device, test_loader, evaluator)[dataset.eval_metric] 75 | 76 | print({'Train': train_result, 77 | 'Validation': valid_result, 78 | 'Test': test_result}) 79 | 80 | model.print_params(final=True) 81 | 82 | 83 | if __name__ == "__main__": 84 | main() 85 | -------------------------------------------------------------------------------- /deep_gcns_torch/deepgcn.yml: -------------------------------------------------------------------------------- 1 | name: deepgcn 2 | channels: 3 | - pytorch 4 | - defaults 5 | dependencies: 6 | - _libgcc_mutex=0.1=main 7 | - _tflow_select=2.3.0=mkl 8 | - absl-py=0.8.0=py37_0 9 | - astor=0.8.0=py37_0 10 | - blas=1.0=mkl 11 | - c-ares=1.15.0=h7b6447c_1001 12 | - ca-certificates=2019.8.28=0 13 | - certifi=2019.9.11=py37_0 14 | - cffi=1.12.3=py37h2e261b9_0 15 | - cudatoolkit=10.0.130=0 16 | - freetype=2.9.1=h8a8886c_1 17 | - gast=0.3.2=py_0 18 | - google-pasta=0.1.7=py_0 19 | - grpcio=1.16.1=py37hf8bcb03_1 20 | - h5py=2.9.0=py37h7918eee_0 21 | - hdf5=1.10.4=hb1b8bf9_0 22 | - intel-openmp=2019.4=243 23 | - jpeg=9b=h024ee3a_2 24 | - keras-applications=1.0.8=py_0 25 | - keras-preprocessing=1.1.0=py_1 26 | - libedit=3.1.20181209=hc058e9b_0 27 | - libffi=3.2.1=hd88cf55_4 28 | - libgcc-ng=9.1.0=hdf63c60_0 29 | - libgfortran-ng=7.3.0=hdf63c60_0 30 | - libpng=1.6.37=hbc83047_0 31 | - libprotobuf=3.9.2=hd408876_0 32 | - libstdcxx-ng=9.1.0=hdf63c60_0 33 | - libtiff=4.0.10=h2733197_2 34 | - markdown=3.1.1=py37_0 35 | - mkl=2019.4=243 36 | - mkl-service=2.3.0=py37he904b0f_0 37 | - mkl_fft=1.0.14=py37ha843d7b_0 38 | - mkl_random=1.1.0=py37hd6b4f25_0 39 | - ncurses=6.1=he6710b0_1 40 | - ninja=1.9.0=py37hfd86e86_0 41 | - numpy=1.17.2=py37haad9e8e_0 42 | - numpy-base=1.17.2=py37hde5b4d6_0 43 | - olefile=0.46=py37_0 44 | - openssl=1.1.1d=h7b6447c_3 45 | - pillow=6.2.0=py37h34e0f95_0 46 | - pip=19.2.3=py37_0 47 | - protobuf=3.9.2=py37he6710b0_0 48 | - pycparser=2.19=py37_0 49 | - python=3.7.4=h265db76_1 50 | - pytorch=1.2.0=py3.7_cuda10.0.130_cudnn7.6.2_0 51 | - readline=7.0=h7b6447c_5 52 | - scipy=1.3.1=py37h7c811a0_0 53 | - setuptools=41.4.0=py37_0 54 | - six=1.12.0=py37_0 55 | - sqlite=3.30.0=h7b6447c_0 56 | - tensorboard=1.14.0=py37hf484d3e_0 57 | - tensorflow=1.14.0=mkl_py37h45c423b_0 58 | - tensorflow-base=1.14.0=mkl_py37h7ce6ba3_0 59 | - tensorflow-estimator=1.14.0=py_0 60 | - termcolor=1.1.0=py37_1 61 | - tk=8.6.8=hbc83047_0 62 | - torchvision=0.4.0=py37_cu100 63 | - werkzeug=0.16.0=py_0 64 | - wheel=0.33.6=py37_0 65 | - wrapt=1.11.2=py37h7b6447c_0 66 | - xz=5.2.4=h14c3975_4 67 | - zlib=1.2.11=h7b6447c_3 68 | - zstd=1.3.7=h0b5b093_0 69 | - pip: 70 | - chardet==3.0.4 71 | - decorator==4.4.0 72 | - googledrivedownloader==0.4 73 | - idna==2.8 74 | - isodate==0.6.0 75 | - joblib==0.14.0 76 | - networkx==2.4 77 | - pandas==0.25.1 78 | - plyfile==0.7.1 79 | - pyparsing==2.4.2 80 | - python-dateutil==2.8.0 81 | - pytz==2019.3 82 | - rdflib==4.2.2 83 | - requests==2.22.0 84 | - scikit-learn==0.21.3 85 | - tensorflow-graphics==1.0.0 86 | - torch-cluster==1.4.5 87 | - torch-geometric==1.3.2 88 | - torch-scatter==1.3.2 89 | - torch-sparse==0.4.3 90 | - torch-spline-conv==1.1.1 91 | - tqdm==4.36.1 92 | - urllib3==1.25.6 93 | prefix: /home/qiang/anaconda3/envs/deepgcn 94 | 95 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbg_ppa/test.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch_geometric.data import DataLoader 3 | from model import DeeperGCN 4 | from tqdm import tqdm 5 | from args import ArgsInit 6 | from utils.data_util import add_zeros, extract_node_feature 7 | from ogb.graphproppred import PygGraphPropPredDataset, Evaluator 8 | from functools import partial 9 | 10 | 11 | @torch.no_grad() 12 | def eval(model, device, loader, evaluator): 13 | model.eval() 14 | y_true = [] 15 | y_pred = [] 16 | 17 | for step, batch in enumerate(tqdm(loader, desc="Iteration")): 18 | batch = batch.to(device) 19 | 20 | if batch.x.shape[0] == 1: 21 | pass 22 | else: 23 | pred = model(batch) 24 | y_true.append(batch.y.view(-1, 1).detach().cpu()) 25 | y_pred.append(torch.argmax(pred.detach(), dim=1).view(-1, 1).cpu()) 26 | 27 | y_true = torch.cat(y_true, dim=0).numpy() 28 | y_pred = torch.cat(y_pred, dim=0).numpy() 29 | 30 | input_dict = {"y_true": y_true, "y_pred": y_pred} 31 | 32 | return evaluator.eval(input_dict)['acc'] 33 | 34 | 35 | def main(): 36 | 37 | args = ArgsInit().args 38 | 39 | if args.use_gpu: 40 | device = torch.device("cuda:" + str(args.device)) if torch.cuda.is_available() else torch.device("cpu") 41 | else: 42 | device = torch.device('cpu') 43 | 44 | if args.not_extract_node_feature: 45 | dataset = PygGraphPropPredDataset(name=args.dataset, 46 | transform=add_zeros) 47 | else: 48 | extract_node_feature_func = partial(extract_node_feature, reduce=args.aggr) 49 | dataset = PygGraphPropPredDataset(name=args.dataset, 50 | transform=extract_node_feature_func) 51 | 52 | args.num_tasks = dataset.num_classes 53 | evaluator = Evaluator(args.dataset) 54 | 55 | split_idx = dataset.get_idx_split() 56 | 57 | train_loader = DataLoader(dataset[split_idx["train"]], batch_size=args.batch_size, shuffle=False, 58 | num_workers=args.num_workers) 59 | valid_loader = DataLoader(dataset[split_idx["valid"]], batch_size=args.batch_size, shuffle=False, 60 | num_workers=args.num_workers) 61 | test_loader = DataLoader(dataset[split_idx["test"]], batch_size=args.batch_size, shuffle=False, 62 | num_workers=args.num_workers) 63 | 64 | print(args) 65 | 66 | model = DeeperGCN(args) 67 | model.load_state_dict(torch.load(args.model_load_path)['model_state_dict']) 68 | model.to(device) 69 | 70 | train_accuracy = eval(model, device, train_loader, evaluator) 71 | valid_accuracy = eval(model, device, valid_loader, evaluator) 72 | test_accuracy = eval(model, device, test_loader, evaluator) 73 | 74 | print({'Train': train_accuracy, 75 | 'Validation': valid_accuracy, 76 | 'Test': test_accuracy}) 77 | model.print_params(final=True) 78 | 79 | 80 | if __name__ == "__main__": 81 | main() 82 | -------------------------------------------------------------------------------- /deep_gcns_torch/gcn_lib/sparse/torch_message.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn.functional as F 3 | from torch_geometric.nn import MessagePassing 4 | from torch_scatter import scatter, scatter_softmax 5 | 6 | 7 | class GenMessagePassing(MessagePassing): 8 | def __init__(self, aggr='softmax', 9 | t=1.0, learn_t=False, 10 | p=1.0, learn_p=False): 11 | 12 | if aggr == 'softmax' or aggr == 'softmax_sg': 13 | 14 | super(GenMessagePassing, self).__init__(aggr=None) 15 | self.aggr = aggr 16 | 17 | if learn_t and aggr == 'softmax': 18 | if t < 1.0: 19 | c = torch.nn.Parameter(torch.Tensor([1/t]), requires_grad=True) 20 | self.t = 1 / c 21 | else: 22 | self.t = torch.nn.Parameter(torch.Tensor([t]), requires_grad=True) 23 | else: 24 | self.t = t 25 | 26 | elif aggr == 'power': 27 | 28 | super(GenMessagePassing, self).__init__(aggr=None) 29 | self.aggr = aggr 30 | 31 | if learn_p: 32 | self.p = torch.nn.Parameter(torch.Tensor([p]), requires_grad=True) 33 | else: 34 | self.p = p 35 | else: 36 | super(GenMessagePassing, self).__init__(aggr=aggr) 37 | 38 | def aggregate(self, inputs, index, ptr=None, dim_size=None): 39 | 40 | if self.aggr in ['add', 'mean', 'max', None]: 41 | return super(GenMessagePassing, self).aggregate(inputs, index, ptr, dim_size) 42 | 43 | elif self.aggr == 'softmax': 44 | out = scatter_softmax(inputs*self.t, index, dim=self.node_dim) 45 | out = scatter(inputs*out, index, dim=self.node_dim, 46 | dim_size=dim_size, reduce='sum') 47 | return out 48 | 49 | elif self.aggr == 'softmax_sg': 50 | with torch.no_grad(): 51 | out = scatter_softmax(inputs*self.t, index, dim=self.node_dim) 52 | out = scatter(inputs*out, index, dim=self.node_dim, 53 | dim_size=dim_size, reduce='sum') 54 | return out 55 | 56 | elif self.aggr == 'power': 57 | min_value, max_value = 1e-7, 1e1 58 | torch.clamp_(inputs, min_value, max_value) 59 | out = scatter(torch.pow(inputs, self.p), index, dim=self.node_dim, 60 | dim_size=dim_size, reduce='mean') 61 | torch.clamp_(out, min_value, max_value) 62 | return torch.pow(out, 1/self.p) 63 | 64 | else: 65 | raise NotImplementedError('To be implemented') 66 | 67 | 68 | class MsgNorm(torch.nn.Module): 69 | def __init__(self, learn_msg_scale=False): 70 | super(MsgNorm, self).__init__() 71 | 72 | self.msg_scale = torch.nn.Parameter(torch.Tensor([1.0]), 73 | requires_grad=learn_msg_scale) 74 | 75 | def forward(self, x, msg, p=2): 76 | msg = F.normalize(msg, p=p, dim=1) 77 | x_norm = x.norm(p=p, dim=1, keepdim=True) 78 | msg = msg * x_norm * self.msg_scale 79 | return msg 80 | -------------------------------------------------------------------------------- /deep_gcns_torch/gcn_lib/dense/torch_nn.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch import nn 3 | from torch.nn import Sequential as Seq, Linear as Lin, Conv2d 4 | 5 | 6 | ############################## 7 | # Basic layers 8 | ############################## 9 | def act_layer(act, inplace=False, neg_slope=0.2, n_prelu=1): 10 | # activation layer 11 | 12 | act = act.lower() 13 | if act == 'relu': 14 | layer = nn.ReLU(inplace) 15 | elif act == 'leakyrelu': 16 | layer = nn.LeakyReLU(neg_slope, inplace) 17 | elif act == 'prelu': 18 | layer = nn.PReLU(num_parameters=n_prelu, init=neg_slope) 19 | else: 20 | raise NotImplementedError('activation layer [%s] is not found' % act) 21 | return layer 22 | 23 | 24 | def norm_layer(norm, nc): 25 | # normalization layer 2d 26 | norm = norm.lower() 27 | if norm == 'batch': 28 | layer = nn.BatchNorm2d(nc, affine=True) 29 | elif norm == 'instance': 30 | layer = nn.InstanceNorm2d(nc, affine=False) 31 | else: 32 | raise NotImplementedError('normalization layer [%s] is not found' % norm) 33 | return layer 34 | 35 | 36 | class MLP(Seq): 37 | def __init__(self, channels, act='relu', norm=None, bias=True): 38 | m = [] 39 | for i in range(1, len(channels)): 40 | m.append(Lin(channels[i - 1], channels[i], bias)) 41 | if act: 42 | m.append(act_layer(act)) 43 | if norm: 44 | m.append(norm_layer(norm, channels[-1])) 45 | super(MLP, self).__init__(*m) 46 | 47 | 48 | class BasicConv(Seq): 49 | def __init__(self, channels, act='relu', norm=None, bias=True, drop=0.): 50 | m = [] 51 | for i in range(1, len(channels)): 52 | m.append(Conv2d(channels[i - 1], channels[i], 1, bias=bias)) 53 | if act: 54 | m.append(act_layer(act)) 55 | if norm: 56 | m.append(norm_layer(norm, channels[-1])) 57 | if drop > 0: 58 | m.append(nn.Dropout2d(drop)) 59 | 60 | super(BasicConv, self).__init__(*m) 61 | 62 | self.reset_parameters() 63 | 64 | def reset_parameters(self): 65 | for m in self.modules(): 66 | if isinstance(m, nn.Conv2d): 67 | nn.init.kaiming_normal_(m.weight) 68 | if m.bias is not None: 69 | nn.init.zeros_(m.bias) 70 | elif isinstance(m, nn.BatchNorm2d) or isinstance(m, nn.InstanceNorm2d): 71 | m.weight.data.fill_(1) 72 | m.bias.data.zero_() 73 | 74 | 75 | def batched_index_select(inputs, index): 76 | """ 77 | 78 | :param inputs: torch.Size([batch_size, num_dims, num_vertices, 1]) 79 | :param index: torch.Size([batch_size, num_vertices, k]) 80 | :return: torch.Size([batch_size, num_dims, num_vertices, k]) 81 | """ 82 | 83 | batch_size, num_dims, num_vertices, _ = inputs.shape 84 | k = index.shape[2] 85 | idx = torch.arange(0, batch_size) * num_vertices 86 | idx = idx.view(batch_size, -1) 87 | 88 | inputs = inputs.transpose(2, 1).contiguous().view(-1, num_dims) 89 | index = index.view(batch_size, -1) + idx.type(index.dtype).to(inputs.device) 90 | index = index.view(-1) 91 | 92 | return torch.index_select(inputs, 0, index).view(batch_size, -1, num_dims).transpose(2, 1).view(batch_size, num_dims, -1, k) 93 | 94 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/sem_seg_sparse/architecture.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch.nn import Linear as Lin 3 | import torch_geometric as tg 4 | from gcn_lib.sparse import MultiSeq, MLP, GraphConv, PlainDynBlock, ResDynBlock, DenseDynBlock, DilatedKnnGraph 5 | 6 | 7 | class SparseDeepGCN(torch.nn.Module): 8 | def __init__(self, opt): 9 | super(SparseDeepGCN, self).__init__() 10 | channels = opt.n_filters 11 | k = opt.k 12 | act = opt.act 13 | norm = opt.norm 14 | bias = opt.bias 15 | epsilon = opt.epsilon 16 | stochastic = opt.stochastic 17 | conv = opt.conv 18 | c_growth = channels 19 | 20 | self.n_blocks = opt.n_blocks 21 | 22 | self.knn = DilatedKnnGraph(k, 1, stochastic, epsilon) 23 | self.head = GraphConv(opt.in_channels, channels, conv, act, norm, bias) 24 | 25 | if opt.block.lower() == 'res': 26 | self.backbone = MultiSeq(*[ResDynBlock(channels, k, 1+i, conv, act, norm, bias, stochastic=stochastic, epsilon=epsilon) 27 | for i in range(self.n_blocks-1)]) 28 | fusion_dims = int(channels + c_growth * (self.n_blocks - 1)) 29 | elif opt.block.lower() == 'dense': 30 | self.backbone = MultiSeq(*[DenseDynBlock(channels+c_growth*i, c_growth, k, 1+i, 31 | conv, act, norm, bias, stochastic=stochastic, epsilon=epsilon) 32 | for i in range(self.n_blocks-1)]) 33 | fusion_dims = int( 34 | (channels + channels + c_growth * (self.n_blocks - 1)) * self.n_blocks // 2) 35 | else: 36 | # Use PlainGCN without skip connection and dilated convolution. 37 | stochastic = False 38 | self.backbone = MultiSeq( 39 | *[PlainDynBlock(channels, k, 1, conv, act, norm, bias, stochastic=stochastic, epsilon=epsilon) 40 | for i in range(self.n_blocks - 1)]) 41 | fusion_dims = int(channels + c_growth * (self.n_blocks - 1)) 42 | 43 | self.fusion_block = MLP([fusion_dims, 1024], act, norm, bias) 44 | self.prediction = MultiSeq(*[MLP([fusion_dims+1024, 512], act, norm, bias), 45 | MLP([512, 256], act, norm, bias, drop=opt.dropout), 46 | MLP([256, opt.n_classes], None, None, bias)]) 47 | self.model_init() 48 | 49 | def model_init(self): 50 | for m in self.modules(): 51 | if isinstance(m, Lin): 52 | torch.nn.init.kaiming_normal_(m.weight) 53 | m.weight.requires_grad = True 54 | if m.bias is not None: 55 | m.bias.data.zero_() 56 | m.bias.requires_grad = True 57 | 58 | def forward(self, data): 59 | corr, color, batch = data.pos, data.x, data.batch 60 | x = torch.cat((corr, color), dim=1) 61 | feats = [self.head(x, self.knn(x[:, 0:3], batch))] 62 | for i in range(self.n_blocks-1): 63 | feats.append(self.backbone[i](feats[-1], batch)[0]) 64 | feats = torch.cat(feats, dim=1) 65 | 66 | fusion = tg.utils.scatter_('max', self.fusion_block(feats), batch) 67 | fusion = torch.repeat_interleave(fusion, repeats=feats.shape[0]//fusion.shape[0], dim=0) 68 | return self.prediction(torch.cat((fusion, feats), dim=1)) 69 | -------------------------------------------------------------------------------- /ogb/graphproppred/code/gnn.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch_geometric.nn import MessagePassing 3 | from torch_geometric.nn import global_add_pool, global_mean_pool, global_max_pool, GlobalAttention, Set2Set 4 | import torch.nn.functional as F 5 | from torch_geometric.nn.inits import uniform 6 | 7 | from conv import GNN_node, GNN_node_Virtualnode 8 | 9 | from torch_scatter import scatter_mean 10 | 11 | class GNN(torch.nn.Module): 12 | 13 | def __init__(self, num_vocab, max_seq_len, node_encoder, num_layer = 5, emb_dim = 300, 14 | gnn_type = 'gin', virtual_node = True, residual = False, drop_ratio = 0.5, JK = "last", graph_pooling = "mean"): 15 | ''' 16 | num_tasks (int): number of labels to be predicted 17 | virtual_node (bool): whether to add virtual node or not 18 | ''' 19 | 20 | super(GNN, self).__init__() 21 | 22 | self.num_layer = num_layer 23 | self.drop_ratio = drop_ratio 24 | self.JK = JK 25 | self.emb_dim = emb_dim 26 | self.num_vocab = num_vocab 27 | self.max_seq_len = max_seq_len 28 | self.graph_pooling = graph_pooling 29 | 30 | if self.num_layer < 2: 31 | raise ValueError("Number of GNN layers must be greater than 1.") 32 | 33 | ### GNN to generate node embeddings 34 | if virtual_node: 35 | self.gnn_node = GNN_node_Virtualnode(num_layer, emb_dim, node_encoder, JK = JK, drop_ratio = drop_ratio, residual = residual, gnn_type = gnn_type) 36 | else: 37 | self.gnn_node = GNN_node(num_layer, emb_dim, node_encoder, JK = JK, drop_ratio = drop_ratio, residual = residual, gnn_type = gnn_type) 38 | 39 | 40 | ### Pooling function to generate whole-graph embeddings 41 | if self.graph_pooling == "sum": 42 | self.pool = global_add_pool 43 | elif self.graph_pooling == "mean": 44 | self.pool = global_mean_pool 45 | elif self.graph_pooling == "max": 46 | self.pool = global_max_pool 47 | elif self.graph_pooling == "attention": 48 | self.pool = GlobalAttention(gate_nn = torch.nn.Sequential(torch.nn.Linear(emb_dim, 2*emb_dim), torch.nn.BatchNorm1d(2*emb_dim), torch.nn.ReLU(), torch.nn.Linear(2*emb_dim, 1))) 49 | elif self.graph_pooling == "set2set": 50 | self.pool = Set2Set(emb_dim, processing_steps = 2) 51 | else: 52 | raise ValueError("Invalid graph pooling type.") 53 | 54 | self.graph_pred_linear_list = torch.nn.ModuleList() 55 | 56 | if graph_pooling == "set2set": 57 | for i in range(max_seq_len): 58 | self.graph_pred_linear_list.append(torch.nn.Linear(2*emb_dim, self.num_vocab)) 59 | 60 | else: 61 | for i in range(max_seq_len): 62 | self.graph_pred_linear_list.append(torch.nn.Linear(emb_dim, self.num_vocab)) 63 | 64 | 65 | def forward(self, batched_data, perturb=None): 66 | ''' 67 | Return: 68 | A list of predictions. 69 | i-th element represents prediction at i-th position of the sequence. 70 | ''' 71 | 72 | h_node = self.gnn_node(batched_data, perturb) 73 | 74 | h_graph = self.pool(h_node, batched_data.batch) 75 | 76 | pred_list = [] 77 | # for i in range(self.max_seq_len): 78 | # pred_list.append(self.graph_pred_mlp_list[i](h_graph)) 79 | 80 | for i in range(self.max_seq_len): 81 | pred_list.append(self.graph_pred_linear_list[i](h_graph)) 82 | 83 | return pred_list 84 | 85 | if __name__ == '__main__': 86 | pass -------------------------------------------------------------------------------- /deep_gcns_torch/examples/part_sem_seg/architecture.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from gcn_lib.dense import BasicConv, GraphConv2d, ResDynBlock2d, DenseDynBlock2d, DenseDilatedKnnGraph, PlainDynBlock2d 3 | from torch.nn import Sequential as Seq 4 | import torch.nn.functional as F 5 | 6 | 7 | class DeepGCN(torch.nn.Module): 8 | def __init__(self, opt): 9 | super(DeepGCN, self).__init__() 10 | channels = opt.n_filters 11 | k = opt.k 12 | act = opt.act 13 | norm = opt.norm 14 | bias = opt.bias 15 | knn = 'matrix' # implement knn using matrix multiplication 16 | epsilon = opt.epsilon 17 | stochastic = opt.stochastic 18 | conv = opt.conv 19 | c_growth = channels 20 | emb_dims = 1024 21 | 22 | self.n_blocks = opt.n_blocks 23 | 24 | self.knn = DenseDilatedKnnGraph(k, 1, stochastic, epsilon) 25 | self.head = GraphConv2d(opt.in_channels, channels, conv, act, norm, bias=False) 26 | 27 | if opt.block.lower() == 'res': 28 | if opt.use_dilation: 29 | self.backbone = Seq(*[ResDynBlock2d(channels, k, i + 1, conv, act, norm, 30 | bias, stochastic, epsilon, knn) 31 | for i in range(self.n_blocks - 1)]) 32 | else: 33 | self.backbone = Seq(*[ResDynBlock2d(channels, k, 1, conv, act, norm, 34 | bias, stochastic, epsilon, knn) 35 | for _ in range(self.n_blocks - 1)]) 36 | fusion_dims = int(channels + c_growth * (self.n_blocks - 1)) 37 | elif opt.block.lower() == 'plain': 38 | # Plain GCN. No dilation, no stochastic 39 | stochastic = False 40 | self.backbone = Seq(*[PlainDynBlock2d(channels, k, 1, conv, act, norm, 41 | bias, stochastic, epsilon, knn) 42 | for i in range(self.n_blocks - 1)]) 43 | 44 | fusion_dims = int(channels+c_growth*(self.n_blocks-1)) 45 | else: 46 | raise NotImplementedError('{} is not supported in this experiment'.format(opt.block)) 47 | 48 | self.fusion_block = BasicConv([fusion_dims, emb_dims], 'leakyrelu', norm, bias=False) 49 | self.prediction = Seq(*[BasicConv([emb_dims * 3, 512], 'leakyrelu', norm, drop=opt.dropout), 50 | BasicConv([512, 256], 'leakyrelu', norm, drop=opt.dropout), 51 | BasicConv([256, opt.n_classes], None, None)]) 52 | 53 | self.model_init() 54 | 55 | def model_init(self): 56 | for m in self.modules(): 57 | if isinstance(m, torch.nn.Conv2d): 58 | torch.nn.init.kaiming_normal_(m.weight) 59 | m.weight.requires_grad = True 60 | if m.bias is not None: 61 | m.bias.data.zero_() 62 | m.bias.requires_grad = True 63 | 64 | def forward(self, inputs): 65 | feats = [self.head(inputs, self.knn(inputs[:, 0:3]))] 66 | for i in range(self.n_blocks-1): 67 | feats.append(self.backbone[i](feats[-1])) 68 | feats = torch.cat(feats, 1) 69 | fusion = self.fusion_block(feats) 70 | 71 | x1 = F.adaptive_max_pool2d(fusion, 1) 72 | x2 = F.adaptive_avg_pool2d(fusion, 1) 73 | feat_global_pool = torch.cat((x1, x2), dim=1) 74 | feat_global_pool = torch.repeat_interleave(feat_global_pool, repeats=fusion.shape[2], dim=2) 75 | cat_pooled = torch.cat((feat_global_pool, fusion), dim=1) 76 | out = self.prediction(cat_pooled).squeeze(-1) 77 | return F.log_softmax(out, dim=1) 78 | 79 | -------------------------------------------------------------------------------- /ogb/attacks.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn.functional as F 3 | 4 | import pdb 5 | 6 | def flag_biased(model_forward, perturb_shape, y, args, optimizer, device, criterion, training_idx) : 7 | unlabel_idx = list(set(range(perturb_shape[0])) - set(training_idx)) 8 | 9 | model, forward = model_forward 10 | model.train() 11 | optimizer.zero_grad() 12 | 13 | perturb = torch.FloatTensor(*perturb_shape).uniform_(-args.step_size, args.step_size).to(device) 14 | perturb.data[unlabel_idx] *= args.amp 15 | perturb.requires_grad_() 16 | out = forward(perturb) 17 | loss = criterion(out, y) 18 | loss /= args.m 19 | 20 | for _ in range(args.m-1): 21 | loss.backward() 22 | 23 | perturb_data_training = perturb[training_idx].detach() + args.step_size * torch.sign(perturb.grad[training_idx].detach()) 24 | perturb.data[training_idx] = perturb_data_training.data 25 | 26 | perturb_data_unlabel = perturb[unlabel_idx].detach() + args.amp*args.step_size * torch.sign(perturb.grad[unlabel_idx].detach()) 27 | perturb.data[unlabel_idx] = perturb_data_unlabel.data 28 | 29 | perturb.grad[:] = 0 30 | out = forward(perturb) 31 | loss = criterion(out, y) 32 | loss /= args.m 33 | 34 | loss.backward() 35 | optimizer.step() 36 | 37 | return loss, out 38 | 39 | def flag(model_forward, perturb_shape, y, args, optimizer, device, criterion) : 40 | model, forward = model_forward 41 | model.train() 42 | optimizer.zero_grad() 43 | 44 | perturb = torch.FloatTensor(*perturb_shape).uniform_(-args.step_size, args.step_size).to(device) 45 | perturb.requires_grad_() 46 | out = forward(perturb) 47 | loss = criterion(out, y) 48 | loss /= args.m 49 | 50 | for _ in range(args.m-1): 51 | loss.backward() 52 | perturb_data = perturb.detach() + args.step_size * torch.sign(perturb.grad.detach()) 53 | perturb.data = perturb_data.data 54 | perturb.grad[:] = 0 55 | 56 | out = forward(perturb) 57 | loss = criterion(out, y) 58 | loss /= args.m 59 | 60 | loss.backward() 61 | optimizer.step() 62 | 63 | return loss, out 64 | 65 | 66 | def flag_products(model, clean, y, adjs, args, optimizer, device, criterion, train_idx=None) : 67 | model.train() 68 | if train_idx is not None: 69 | model_forward = lambda x: model(x, adjs)[train_idx] 70 | else: 71 | model_forward = lambda x: model(x, adjs) 72 | optimizer.zero_grad() 73 | 74 | perturb_t = torch.FloatTensor(*clean[:args.batch_size].shape).uniform_(-args.step_size, args.step_size).to(device) 75 | perturb_un = torch.FloatTensor(*clean[args.batch_size:].shape).uniform_(-args.amp*args.step_size, args.amp*args.step_size).to(device) 76 | perturb_t.requires_grad_() 77 | perturb_un.requires_grad_() 78 | 79 | 80 | perturb = torch.cat((perturb_t, perturb_un), dim=0) 81 | out = model_forward(clean + perturb) 82 | loss = criterion(out, y) 83 | loss /= args.m 84 | 85 | 86 | for _ in range(args.m-1): 87 | loss.backward() 88 | 89 | perturb_data_t = perturb_t.detach() + args.step_size * torch.sign(perturb_t.grad.detach()) 90 | perturb_t.data = perturb_data_t.data 91 | perturb_t.grad[:] = 0 92 | 93 | perturb_data_un = perturb_un.detach() + args.amp*args.step_size * torch.sign(perturb_un.grad.detach()) 94 | perturb_un.data = perturb_data_un.data 95 | perturb_un.grad[:] = 0 96 | 97 | perturb = torch.cat((perturb_t, perturb_un), dim=0) 98 | out = model_forward(clean + perturb) 99 | loss = criterion(out, y) 100 | loss /= args.m 101 | 102 | loss.backward() 103 | optimizer.step() 104 | 105 | return loss, out 106 | -------------------------------------------------------------------------------- /deep_gcns_torch/gcn_lib/dense/torch_edge.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch import nn 3 | from torch_cluster import knn_graph 4 | 5 | 6 | class DenseDilated(nn.Module): 7 | """ 8 | Find dilated neighbor from neighbor list 9 | 10 | edge_index: (2, batch_size, num_points, k) 11 | """ 12 | def __init__(self, k=9, dilation=1, stochastic=False, epsilon=0.0): 13 | super(DenseDilated, self).__init__() 14 | self.dilation = dilation 15 | self.stochastic = stochastic 16 | self.epsilon = epsilon 17 | self.k = k 18 | 19 | def forward(self, edge_index): 20 | if self.stochastic: 21 | if torch.rand(1) < self.epsilon and self.training: 22 | num = self.k * self.dilation 23 | randnum = torch.randperm(num)[:self.k] 24 | edge_index = edge_index[:, :, :, randnum] 25 | else: 26 | edge_index = edge_index[:, :, :, ::self.dilation] 27 | else: 28 | edge_index = edge_index[:, :, :, ::self.dilation] 29 | return edge_index 30 | 31 | 32 | def pairwise_distance(x): 33 | """ 34 | Compute pairwise distance of a point cloud. 35 | Args: 36 | x: tensor (batch_size, num_points, num_dims) 37 | Returns: 38 | pairwise distance: (batch_size, num_points, num_points) 39 | """ 40 | x_inner = -2*torch.matmul(x, x.transpose(2, 1)) 41 | x_square = torch.sum(torch.mul(x, x), dim=-1, keepdim=True) 42 | return x_square + x_inner + x_square.transpose(2, 1) 43 | 44 | 45 | def dense_knn_matrix(x, k=16): 46 | """Get KNN based on the pairwise distance. 47 | Args: 48 | x: (batch_size, num_dims, num_points, 1) 49 | k: int 50 | Returns: 51 | nearest neighbors: (batch_size, num_points ,k) (batch_size, num_points, k) 52 | """ 53 | with torch.no_grad(): 54 | x = x.transpose(2, 1).squeeze(-1) 55 | batch_size, n_points, n_dims = x.shape 56 | _, nn_idx = torch.topk(-pairwise_distance(x.detach()), k=k) 57 | center_idx = torch.arange(0, n_points, device=x.device).repeat(batch_size, k, 1).transpose(2, 1) 58 | return torch.stack((nn_idx, center_idx), dim=0) 59 | 60 | 61 | class DenseDilatedKnnGraph(nn.Module): 62 | """ 63 | Find the neighbors' indices based on dilated knn 64 | """ 65 | def __init__(self, k=9, dilation=1, stochastic=False, epsilon=0.0): 66 | super(DenseDilatedKnnGraph, self).__init__() 67 | self.dilation = dilation 68 | self.stochastic = stochastic 69 | self.epsilon = epsilon 70 | self.k = k 71 | self._dilated = DenseDilated(k, dilation, stochastic, epsilon) 72 | self.knn = dense_knn_matrix 73 | 74 | def forward(self, x): 75 | edge_index = self.knn(x, self.k * self.dilation) 76 | return self._dilated(edge_index) 77 | 78 | 79 | class DilatedKnnGraph(nn.Module): 80 | """ 81 | Find the neighbors' indices based on dilated knn 82 | """ 83 | def __init__(self, k=9, dilation=1, stochastic=False, epsilon=0.0): 84 | super(DilatedKnnGraph, self).__init__() 85 | self.dilation = dilation 86 | self.stochastic = stochastic 87 | self.epsilon = epsilon 88 | self.k = k 89 | self._dilated = DenseDilated(k, dilation, stochastic, epsilon) 90 | self.knn = knn_graph 91 | 92 | def forward(self, x): 93 | x = x.squeeze(-1) 94 | B, C, N = x.shape 95 | edge_index = [] 96 | for i in range(B): 97 | edgeindex = self.knn(x[i].contiguous().transpose(1, 0).contiguous(), self.k * self.dilation) 98 | edgeindex = edgeindex.view(2, N, self.k * self.dilation) 99 | edge_index.append(edgeindex) 100 | edge_index = torch.stack(edge_index, dim=1) 101 | return self._dilated(edge_index) 102 | -------------------------------------------------------------------------------- /deep_gcns_torch/README.md: -------------------------------------------------------------------------------- 1 | # DeepGCNs: Can GCNs Go as Deep as CNNs? 2 | In this work, we present new ways to successfully train very deep GCNs. We borrow concepts from CNNs, mainly residual/dense connections and dilated convolutions, and adapt them to GCN architectures. Through extensive experiments, we show the positive effect of these deep GCN frameworks. 3 | 4 | [[Project]](https://www.deepgcns.org/) [[Paper]](https://arxiv.org/abs/1904.03751) [[Slides]](https://docs.google.com/presentation/d/1L82wWymMnHyYJk3xUKvteEWD5fX0jVRbCbI65Cxxku0/edit?usp=sharing) [[Tensorflow Code]](https://github.com/lightaime/deep_gcns) [[Pytorch Code]](https://github.com/lightaime/deep_gcns_torch) 5 | 6 |

7 | 8 |

9 | 10 | ## Overview 11 | We do extensive experiments to show how different components (#Layers, #Filters, #Nearest Neighbors, Dilation, etc.) effect `DeepGCNs`. We also provide ablation studies on different type of Deep GCNs (MRGCN, EdgeConv, GraphSage and GIN). 12 | 13 |

14 | 15 |

16 | 17 | ## Requirements 18 | * [Pytorch>=1.4.0](https://pytorch.org) 19 | * [pytorch_geometric>=1.3.0](https://pytorch-geometric.readthedocs.io/en/latest/) 20 | * [tensorflow graphics](https://github.com/tensorflow/graphics/blob/master/tensorflow_graphics/g3doc/install.md) only used for tensorboard visualization 21 | 22 | Install enviroment by runing: 23 | ``` 24 | source deepgcn_env_install.sh 25 | ``` 26 | 27 | ## Code Architecture 28 | . 29 | ├── misc # Misc images 30 | ├── utils # Common useful modules 31 | ├── gcn_lib # gcn library 32 | │ ├── dense # gcn library for dense data (B x C x N x 1) 33 | │ └── sparse # gcn library for sparse data (N x C) 34 | ├── examples 35 | │ ├── modelnet_cls # code for point clouds classification on ModelNet40 36 | │ ├── sem_seg_dense # code for point clouds semantic segmentation on S3DIS (data type: dense) 37 | │ ├── sem_seg_sparse # code for point clouds semantic segmentation on S3DIS (data type: sparse) 38 | │ ├── part_sem_seg # code for part segmentation on PartNet 39 | │ ├── ppi # code for node classification on PPI dataset 40 | │ └── ogb # code for node/graph property prediction on OGB datasets 41 | └── ... 42 | 43 | ## How to train, test and evaluate our models 44 | Please look the details in `Readme.md` of each task inside `examples` folder. 45 | All the information of code, data, and pretrained models can be found there. 46 | ## Citation 47 | Please cite our paper if you find anything helpful, 48 | 49 | ``` 50 | @InProceedings{li2019deepgcns, 51 | title={DeepGCNs: Can GCNs Go as Deep as CNNs?}, 52 | author={Guohao Li and Matthias Müller and Ali Thabet and Bernard Ghanem}, 53 | booktitle={The IEEE International Conference on Computer Vision (ICCV)}, 54 | year={2019} 55 | } 56 | ``` 57 | 58 | ``` 59 | @misc{li2019deepgcns_journal, 60 | title={DeepGCNs: Making GCNs Go as Deep as CNNs}, 61 | author={Guohao Li and Matthias Müller and Guocheng Qian and Itzel C. Delgadillo and Abdulellah Abualshour and Ali Thabet and Bernard Ghanem}, 62 | year={2019}, 63 | eprint={1910.06849}, 64 | archivePrefix={arXiv}, 65 | primaryClass={cs.CV} 66 | } 67 | ``` 68 | 69 | ``` 70 | @misc{li2020deepergcn, 71 | title={DeeperGCN: All You Need to Train Deeper GCNs}, 72 | author={Guohao Li and Chenxin Xiong and Ali Thabet and Bernard Ghanem}, 73 | year={2020}, 74 | eprint={2006.07739}, 75 | archivePrefix={arXiv}, 76 | primaryClass={cs.LG} 77 | } 78 | ``` 79 | 80 | ## License 81 | MIT License 82 | 83 | ## Contact 84 | For more information please contact [Guohao Li](https://ghli.org), [Matthias Muller](https://matthias.pw/), [Guocheng Qian](https://www.gcqian.com/). 85 | -------------------------------------------------------------------------------- /deep_gcns_torch/gcn_lib/sparse/torch_nn.py: -------------------------------------------------------------------------------- 1 | from torch import nn 2 | from torch.nn import Sequential as Seq, Linear as Lin 3 | from utils.data_util import get_atom_feature_dims, get_bond_feature_dims 4 | 5 | 6 | ############################## 7 | # Basic layers 8 | ############################## 9 | def act_layer(act_type, inplace=False, neg_slope=0.2, n_prelu=1): 10 | # activation layer 11 | 12 | act = act_type.lower() 13 | if act == 'relu': 14 | layer = nn.ReLU(inplace) 15 | elif act == 'leakyrelu': 16 | layer = nn.LeakyReLU(neg_slope, inplace) 17 | elif act == 'prelu': 18 | layer = nn.PReLU(num_parameters=n_prelu, init=neg_slope) 19 | else: 20 | raise NotImplementedError('activation layer [%s] is not found' % act) 21 | return layer 22 | 23 | 24 | def norm_layer(norm_type, nc): 25 | # normalization layer 1d 26 | norm = norm_type.lower() 27 | if norm == 'batch': 28 | layer = nn.BatchNorm1d(nc, affine=True) 29 | elif norm == 'layer': 30 | layer = nn.LayerNorm(nc, elementwise_affine=True) 31 | elif norm == 'instance': 32 | layer = nn.InstanceNorm1d(nc, affine=False) 33 | else: 34 | raise NotImplementedError('normalization layer [%s] is not found' % norm) 35 | return layer 36 | 37 | 38 | class MultiSeq(Seq): 39 | def __init__(self, *args): 40 | super(MultiSeq, self).__init__(*args) 41 | 42 | def forward(self, *inputs): 43 | for module in self._modules.values(): 44 | if type(inputs) == tuple: 45 | inputs = module(*inputs) 46 | else: 47 | inputs = module(inputs) 48 | return inputs 49 | 50 | 51 | class MLP(Seq): 52 | def __init__(self, channels, act='relu', 53 | norm=None, bias=True, 54 | drop=0., last_lin=False): 55 | m = [] 56 | 57 | for i in range(1, len(channels)): 58 | 59 | m.append(Lin(channels[i - 1], channels[i], bias)) 60 | 61 | if (i == len(channels) - 1) and last_lin: 62 | pass 63 | else: 64 | if norm: 65 | m.append(norm_layer(norm, channels[i])) 66 | if act: 67 | m.append(act_layer(act)) 68 | if drop > 0: 69 | m.append(nn.Dropout2d(drop)) 70 | 71 | self.m = m 72 | super(MLP, self).__init__(*self.m) 73 | 74 | 75 | class AtomEncoder(nn.Module): 76 | 77 | def __init__(self, emb_dim): 78 | super(AtomEncoder, self).__init__() 79 | 80 | self.atom_embedding_list = nn.ModuleList() 81 | full_atom_feature_dims = get_atom_feature_dims() 82 | 83 | for i, dim in enumerate(full_atom_feature_dims): 84 | emb = nn.Embedding(dim, emb_dim) 85 | nn.init.xavier_uniform_(emb.weight.data) 86 | self.atom_embedding_list.append(emb) 87 | 88 | def forward(self, x): 89 | x_embedding = 0 90 | for i in range(x.shape[1]): 91 | x_embedding += self.atom_embedding_list[i](x[:, i]) 92 | 93 | return x_embedding 94 | 95 | 96 | class BondEncoder(nn.Module): 97 | 98 | def __init__(self, emb_dim): 99 | super(BondEncoder, self).__init__() 100 | 101 | self.bond_embedding_list = nn.ModuleList() 102 | full_bond_feature_dims = get_bond_feature_dims() 103 | 104 | for i, dim in enumerate(full_bond_feature_dims): 105 | emb = nn.Embedding(dim, emb_dim) 106 | nn.init.xavier_uniform_(emb.weight.data) 107 | self.bond_embedding_list.append(emb) 108 | 109 | def forward(self, edge_attr): 110 | bond_embedding = 0 111 | for i in range(edge_attr.shape[1]): 112 | bond_embedding += self.bond_embedding_list[i](edge_attr[:, i]) 113 | 114 | return bond_embedding 115 | 116 | 117 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/part_sem_seg/README.md: -------------------------------------------------------------------------------- 1 | ## [Part Segmentation on PartNet](https://arxiv.org/pdf/1910.06849.pdf) 2 | 3 |

4 | 5 |

6 | 7 | ### Preparing the Dataset 8 | Make sure you request access to download the PartNet v0 dataset [here](https://cs.stanford.edu/~kaichun/partnet/). It's an official website of Partnet. 9 | Once the data is downloaded, extract the `sem_seg_h5` data and put them inside a new folder called 'raw'. 10 | For example, our data folder structure is like this: `/data/deepgcn/partnet/raw/sem_seg_h5/category-level`. `category` is the name of a category, eg. Bed. `level` is 1, 2, or 3. When we train and test, we set `--data_dir /data/deepgcn/partnet`. 11 | 12 | ### Train 13 | We train each model on one tesla V100. 14 | 15 | For training the default ResEdgeConv-28 with 64 filters on the Bed category, run: 16 | ``` 17 | python main.py --phase train --category 1 --data_dir /data/deepgcn/partnet 18 | ``` 19 | Note that, We only focus on fine-grained level of part segmentation in the experiment. 20 | For all the categories, we use the same training parameters as default (see `config.py` for details). 21 | 22 | If you want to train a model with other gcn layers (for example mrgcn), run 23 | ``` 24 | python main.py --phase train --category 1 --conv mr --data_dir /data/deepgcn/partnet 25 | ``` 26 | Other important parameters are: 27 | ``` 28 | --block graph backbone block type {res, plain, dense} 29 | --conv graph conv layer {edge, mr, sage, gin, gcn, gat} 30 | --n_filters number of channels of deep features, default is 64 31 | --n_blocks number of basic blocks, default is 28 32 | --category NO. of category. default is 1 (Bed) 33 | ``` 34 | The category list is: 35 | ``` 36 | category_names = ['Bag', 'Bed', 'Bottle', 'Bowl', 'Chair', 'Clock', 'Dishwasher', 'Display', 'Door', 'Earphone', # 0-9 37 | 'Faucet', 'Hat', 'Keyboard', 'Knife', 'Lamp', 'Laptop', 'Microwave', 'Mug', 'Refrigerator', 'Scissors', # 10-19 38 | 'StorageFurniture', 'Table', 'TrashCan', 'Vase'] 39 | ``` 40 | ### Test 41 | We test and report results on the testing dataset using the checkpoints which perform the best in the validation dataset. 42 | Our pretrained models can be found from [Google Cloud](https://drive.google.com/drive/folders/15Y7Ao4VBysHBHxyQwYvb2SU1iFi9ZZRK?usp=sharing). 43 | 44 | The Naming format of our pretrained model is: `task-category-segmentationLevel-conv-n_blocks-n_filters-otherParameters-val_best_model_best.pth`, eg. `PartnetSemanticSeg-Bed-L3-res-edge-n28-C64-k9-drop0.5-lr0.005_B6-val_best_model.pth`. 45 | val_best means the checkpoint is the best one on the validation dataset. 46 | 47 | Use the parameter `--pretrained_model` to set a specific pretrained model to load. For example, 48 | ``` 49 | python -u main.py --phase test --category 1 --pretrained_model checkpoints/PartnetSemanticSeg-Bed-L3-res-edge-n28-C64-k9-drop0.5-lr0.005_B6-val_best_model.pth --data_dir /data/deepgcn/partnet --test_batch_size 8 50 | ``` 51 | Please also specify the number of blocks and filters. 52 | Note: 53 | - the path of `--pretrained_model` is a relative path to `main.py`, so don't add `examples/part_sem_seg` in `--pretrained_model`. Or you can feed an absolute path of `--pretrained_model`. 54 | - if you do not have V100, you can set the `test_batch_size` to 1. It does not influence the test accuracy. 55 | 56 | #### Visualization 57 | 1. step1 58 | Use the script `eval.py` to generate `.obj` files to be visualized: 59 | ``` 60 | python -u eval.py --phase test --category 1 --pretrained_model checkpoints/PartnetSemanticSeg-Bed-L3-res-edge-n28-C64-k9-drop0.5-lr0.005_B6-val_best_model.pth --data_dir /data/deepgcn/partnet 61 | ``` 62 | 2. step2 63 | To visualize the output of a trained model please use `visualize.py`. 64 | Define the category's name and model number in the script and run below: 65 | ``` 66 | python -u visualize.py 67 | ``` 68 | -------------------------------------------------------------------------------- /deep_gcns_torch/gcn_lib/sparse/torch_edge.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch import nn 3 | from torch_cluster import knn_graph 4 | 5 | 6 | class Dilated(nn.Module): 7 | """ 8 | Find dilated neighbor from neighbor list 9 | """ 10 | def __init__(self, k=9, dilation=1, stochastic=False, epsilon=0.0): 11 | super(Dilated, self).__init__() 12 | self.dilation = dilation 13 | self.stochastic = stochastic 14 | self.epsilon = epsilon 15 | self.k = k 16 | 17 | def forward(self, edge_index, batch=None): 18 | if self.stochastic: 19 | if torch.rand(1) < self.epsilon and self.training: 20 | num = self.k * self.dilation 21 | randnum = torch.randperm(num)[:self.k] 22 | edge_index = edge_index.view(2, -1, num) 23 | edge_index = edge_index[:, :, randnum] 24 | return edge_index.view(2, -1) 25 | else: 26 | edge_index = edge_index[:, ::self.dilation] 27 | else: 28 | edge_index = edge_index[:, ::self.dilation] 29 | return edge_index 30 | 31 | 32 | class DilatedKnnGraph(nn.Module): 33 | """ 34 | Find the neighbors' indices based on dilated knn 35 | """ 36 | def __init__(self, k=9, dilation=1, stochastic=False, epsilon=0.0, knn='matrix'): 37 | super(DilatedKnnGraph, self).__init__() 38 | self.dilation = dilation 39 | self.stochastic = stochastic 40 | self.epsilon = epsilon 41 | self.k = k 42 | self._dilated = Dilated(k, dilation, stochastic, epsilon) 43 | if knn == 'matrix': 44 | self.knn = knn_graph_matrix 45 | else: 46 | self.knn = knn_graph 47 | 48 | def forward(self, x, batch): 49 | edge_index = self.knn(x, self.k * self.dilation, batch) 50 | return self._dilated(edge_index, batch) 51 | 52 | 53 | def pairwise_distance(x): 54 | """ 55 | Compute pairwise distance of a point cloud. 56 | Args: 57 | x: tensor (batch_size, num_points, num_dims) 58 | Returns: 59 | pairwise distance: (batch_size, num_points, num_points) 60 | """ 61 | x_inner = -2*torch.matmul(x, x.transpose(2, 1)) 62 | x_square = torch.sum(torch.mul(x, x), dim=-1, keepdim=True) 63 | return x_square + x_inner + x_square.transpose(2, 1) 64 | 65 | 66 | def knn_matrix(x, k=16, batch=None): 67 | """Get KNN based on the pairwise distance. 68 | Args: 69 | pairwise distance: (num_points, num_points) 70 | k: int 71 | Returns: 72 | nearest neighbors: (num_points*k ,1) (num_points, k) 73 | """ 74 | with torch.no_grad(): 75 | if batch is None: 76 | batch_size = 1 77 | else: 78 | batch_size = batch[-1] + 1 79 | x = x.view(batch_size, -1, x.shape[-1]) 80 | 81 | neg_adj = -pairwise_distance(x.detach()) 82 | _, nn_idx = torch.topk(neg_adj, k=k) 83 | del neg_adj 84 | 85 | n_points = x.shape[1] 86 | start_idx = torch.arange(0, n_points*batch_size, n_points).long().view(batch_size, 1, 1) 87 | if x.is_cuda: 88 | start_idx = start_idx.cuda() 89 | nn_idx += start_idx 90 | del start_idx 91 | 92 | if x.is_cuda: 93 | torch.cuda.empty_cache() 94 | 95 | nn_idx = nn_idx.view(1, -1) 96 | center_idx = torch.arange(0, n_points*batch_size).repeat(k, 1).transpose(1, 0).contiguous().view(1, -1) 97 | if x.is_cuda: 98 | center_idx = center_idx.cuda() 99 | return nn_idx, center_idx 100 | 101 | 102 | def knn_graph_matrix(x, k=16, batch=None): 103 | """Construct edge feature for each point 104 | Args: 105 | x: (num_points, num_dims) 106 | batch: (num_points, ) 107 | k: int 108 | Returns: 109 | edge_index: (2, num_points*k) 110 | """ 111 | nn_idx, center_idx = knn_matrix(x, k, batch) 112 | return torch.cat((nn_idx, center_idx), dim=0) 113 | 114 | -------------------------------------------------------------------------------- /deep_gcns_torch/utils/tf_logger.py: -------------------------------------------------------------------------------- 1 | # Code referenced from https://gist.github.com/gyglim/1f8dfb1b5c82627ae3efcfbbadb9f514 2 | try: 3 | import tensorflow as tf 4 | import tensorboard.plugins.mesh.summary as meshsummary 5 | except ImportError: 6 | print('tensorflow is not installed.') 7 | import numpy as np 8 | import scipy.misc 9 | 10 | 11 | try: 12 | from StringIO import StringIO # Python 2.7 13 | except ImportError: 14 | from io import BytesIO # Python 3.x 15 | 16 | 17 | class TfLogger(object): 18 | 19 | def __init__(self, log_dir): 20 | """Create a summary writer logging to log_dir.""" 21 | self.writer = tf.compat.v1.summary.FileWriter(log_dir) 22 | 23 | # Camera and scene configuration. 24 | self.config_dict = { 25 | 'camera': {'cls': 'PerspectiveCamera', 'fov': 75}, 26 | 'lights': [ 27 | { 28 | 'cls': 'AmbientLight', 29 | 'color': '#ffffff', 30 | 'intensity': 0.75, 31 | }, { 32 | 'cls': 'DirectionalLight', 33 | 'color': '#ffffff', 34 | 'intensity': 0.75, 35 | 'position': [0, -1, 2], 36 | }], 37 | 'material': { 38 | 'cls': 'MeshStandardMaterial', 39 | 'metalness': 0 40 | } 41 | } 42 | 43 | def scalar_summary(self, tag, value, step): 44 | """Log a scalar variable.""" 45 | summary = tf.compat.v1.Summary(value=[tf.compat.v1.Summary.Value(tag=tag, simple_value=value)]) 46 | self.writer.add_summary(summary, step) 47 | 48 | def image_summary(self, tag, images, step): 49 | """Log a list of images.""" 50 | img_summaries = [] 51 | for i, img in enumerate(images): 52 | # Write the image to a string 53 | s = BytesIO() 54 | scipy.misc.toimage(img).save(s, format="png") 55 | 56 | # Create an Image object 57 | img_sum = tf.compat.v1.Summary.Image(encoded_image_string=s.getvalue(), 58 | height=img.shape[0], width=img.shape[1]) 59 | # Create a Summary value 60 | img_summaries.append(tf.compat.v1.Summary.Value(tag='%s/%d' % (tag, i), image=img_sum)) 61 | 62 | # Create and write Summary 63 | summary = tf.Summary(value=img_summaries) 64 | self.writer.add_summary(summary, step) 65 | 66 | def mesh_summary(self, tag, vertices, faces=None, colors=None, step=0): 67 | 68 | """Log a list of mesh images.""" 69 | if colors is None: 70 | colors = tf.constant(np.zeros_like(vertices)) 71 | vertices = tf.constant(vertices) 72 | if faces is not None: 73 | faces = tf.constant(faces) 74 | meshes_summares=[] 75 | for i in range(vertices.shape[0]): 76 | meshes_summares.append(meshsummary.op( 77 | tag, vertices=vertices, faces=faces, colors=colors, config_dict=self.config_dict)) 78 | 79 | sess = tf.Session() 80 | summaries = sess.run(meshes_summares) 81 | for summary in summaries: 82 | self.writer.add_summary(summary, step) 83 | 84 | def histo_summary(self, tag, values, step, bins=1000): 85 | """Log a histogram of the tensor of values.""" 86 | 87 | # Create a histogram using numpy 88 | counts, bin_edges = np.histogram(values, bins=bins) 89 | 90 | # Fill the fields of the histogram proto 91 | hist = tf.HistogramProto() 92 | hist.min = float(np.min(values)) 93 | hist.max = float(np.max(values)) 94 | hist.num = int(np.prod(values.shape)) 95 | hist.sum = float(np.sum(values)) 96 | hist.sum_squares = float(np.sum(values**2)) 97 | 98 | # Drop the start of the first bin 99 | bin_edges = bin_edges[1:] 100 | 101 | # Add bin edges and counts 102 | for edge in bin_edges: 103 | hist.bucket_limit.append(edge) 104 | for c in counts: 105 | hist.bucket.append(c) 106 | 107 | # Create and write Summary 108 | summary = tf.Summary(value=[tf.Summary.Value(tag=tag, histo=hist)]) 109 | self.writer.add_summary(summary, step) 110 | self.writer.flush() 111 | 112 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbn_arxiv/main.py: -------------------------------------------------------------------------------- 1 | import __init__ 2 | from ogb.nodeproppred import Evaluator 3 | import torch 4 | import torch.nn.functional as F 5 | from torch_geometric.utils import to_undirected, add_self_loops 6 | from args import ArgsInit 7 | from ogb.nodeproppred import PygNodePropPredDataset 8 | from model import DeeperGCN 9 | from utils.ckpt_util import save_ckpt 10 | import logging 11 | import time 12 | 13 | 14 | import pdb 15 | import sys 16 | sys.path.insert(0,'..') 17 | from attacks import * 18 | 19 | 20 | @torch.no_grad() 21 | def test(model, x, edge_index, y_true, split_idx, evaluator): 22 | model.eval() 23 | out = model(x, edge_index) 24 | 25 | y_pred = out.argmax(dim=-1, keepdim=True) 26 | 27 | train_acc = evaluator.eval({ 28 | 'y_true': y_true[split_idx['train']], 29 | 'y_pred': y_pred[split_idx['train']], 30 | })['acc'] 31 | valid_acc = evaluator.eval({ 32 | 'y_true': y_true[split_idx['valid']], 33 | 'y_pred': y_pred[split_idx['valid']], 34 | })['acc'] 35 | test_acc = evaluator.eval({ 36 | 'y_true': y_true[split_idx['test']], 37 | 'y_pred': y_pred[split_idx['test']], 38 | })['acc'] 39 | 40 | return train_acc, valid_acc, test_acc 41 | 42 | 43 | def train(model, x, edge_index, y_true, train_idx, optimizer): 44 | model.train() 45 | 46 | optimizer.zero_grad() 47 | 48 | pred = model(x, edge_index)[train_idx] 49 | 50 | loss = F.nll_loss(pred, y_true.squeeze(1)[train_idx]) 51 | loss.backward() 52 | optimizer.step() 53 | 54 | return loss.item() 55 | 56 | def train_flag(model, x, edge_index, y_true, train_idx, optimizer, device, args): 57 | 58 | forward = lambda perturb : model(x+perturb, edge_index)[train_idx] 59 | model_forward = (model, forward) 60 | target = y_true.squeeze(1)[train_idx] 61 | 62 | loss, out = flag(model_forward, x.shape, target, args, optimizer, device, F.nll_loss) 63 | 64 | return loss.item() 65 | 66 | 67 | def main(): 68 | 69 | args = ArgsInit().save_exp() 70 | 71 | if args.use_gpu: 72 | device = torch.device("cuda:" + str(args.device)) if torch.cuda.is_available() else torch.device("cpu") 73 | else: 74 | device = torch.device('cpu') 75 | 76 | dataset = PygNodePropPredDataset(name=args.dataset) 77 | data = dataset[0] 78 | split_idx = dataset.get_idx_split() 79 | 80 | evaluator = Evaluator(args.dataset) 81 | 82 | x = data.x.to(device) 83 | y_true = data.y.to(device) 84 | train_idx = split_idx['train'].to(device) 85 | 86 | edge_index = data.edge_index.to(device) 87 | edge_index = to_undirected(edge_index, data.num_nodes) 88 | 89 | if args.self_loop: 90 | edge_index = add_self_loops(edge_index, num_nodes=data.num_nodes)[0] 91 | 92 | sub_dir = 'SL_{}'.format(args.self_loop) 93 | 94 | args.in_channels = data.x.size(-1) 95 | args.num_tasks = dataset.num_classes 96 | 97 | logging.info('%s' % args) 98 | 99 | model = DeeperGCN(args).to(device) 100 | 101 | logging.info(model) 102 | 103 | optimizer = torch.optim.Adam(model.parameters(), lr=args.lr) 104 | 105 | results = {'highest_valid': 0, 106 | 'final_train': 0, 107 | 'final_test': 0, 108 | 'highest_train': 0} 109 | 110 | start_time = time.time() 111 | 112 | for epoch in range(1, args.epochs + 1): 113 | 114 | # epoch_loss = train(model, x, edge_index, y_true, train_idx, optimizer) 115 | epoch_loss = train_flag(model, x, edge_index, y_true, train_idx, optimizer, device, args) 116 | 117 | logging.info('Epoch {}, training loss {:.4f}'.format(epoch, epoch_loss)) 118 | model.print_params(epoch=epoch) 119 | 120 | result = test(model, x, edge_index, y_true, split_idx, evaluator) 121 | logging.info(result) 122 | train_accuracy, valid_accuracy, test_accuracy = result 123 | 124 | if train_accuracy > results['highest_train']: 125 | results['highest_train'] = train_accuracy 126 | 127 | if valid_accuracy > results['highest_valid']: 128 | results['highest_valid'] = valid_accuracy 129 | results['final_train'] = train_accuracy 130 | results['final_test'] = test_accuracy 131 | 132 | save_ckpt(model, optimizer, 133 | round(epoch_loss, 4), epoch, 134 | args.model_save_path, 135 | sub_dir, name_post='valid_best') 136 | 137 | logging.info("%s" % results) 138 | 139 | end_time = time.time() 140 | total_time = end_time - start_time 141 | logging.info('Total time: {}'.format(time.strftime('%H:%M:%S', time.gmtime(total_time)))) 142 | 143 | 144 | if __name__ == "__main__": 145 | main() 146 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbn_proteins/test.py: -------------------------------------------------------------------------------- 1 | import __init__ 2 | import torch 3 | from dataset import OGBNDataset 4 | from utils.data_util import intersection, process_indexes 5 | import numpy as np 6 | from ogb.nodeproppred import Evaluator 7 | from model import DeeperGCN 8 | from args import ArgsInit 9 | 10 | 11 | @torch.no_grad() 12 | def multi_evaluate(valid_data_list, dataset, model, evaluator, device): 13 | model.eval() 14 | target = dataset.y.detach().numpy() 15 | 16 | train_pre_ordered_list = [] 17 | valid_pre_ordered_list = [] 18 | test_pre_ordered_list = [] 19 | 20 | test_idx = dataset.test_idx.tolist() 21 | train_idx = dataset.train_idx.tolist() 22 | valid_idx = dataset.valid_idx.tolist() 23 | 24 | for valid_data_item in valid_data_list: 25 | sg_nodes, sg_edges, sg_edges_index, _ = valid_data_item 26 | idx_clusters = np.arange(len(sg_nodes)) 27 | 28 | test_predict = [] 29 | test_target_idx = [] 30 | 31 | train_predict = [] 32 | valid_predict = [] 33 | 34 | train_target_idx = [] 35 | valid_target_idx = [] 36 | 37 | for idx in idx_clusters: 38 | x = dataset.x[sg_nodes[idx]].float().to(device) 39 | sg_nodes_idx = torch.LongTensor(sg_nodes[idx]).to(device) 40 | 41 | mapper = {node: idx for idx, node in enumerate(sg_nodes[idx])} 42 | sg_edges_attr = dataset.edge_attr[sg_edges_index[idx]].to(device) 43 | 44 | inter_tr_idx = intersection(sg_nodes[idx], train_idx) 45 | inter_v_idx = intersection(sg_nodes[idx], valid_idx) 46 | 47 | train_target_idx += inter_tr_idx 48 | valid_target_idx += inter_v_idx 49 | 50 | tr_idx = [mapper[tr_idx] for tr_idx in inter_tr_idx] 51 | v_idx = [mapper[v_idx] for v_idx in inter_v_idx] 52 | 53 | pred = model(x, sg_nodes_idx, sg_edges[idx].to(device), sg_edges_attr).cpu().detach() 54 | 55 | train_predict.append(pred[tr_idx]) 56 | valid_predict.append(pred[v_idx]) 57 | 58 | inter_te_idx = intersection(sg_nodes[idx], test_idx) 59 | test_target_idx += inter_te_idx 60 | 61 | te_idx = [mapper[te_idx] for te_idx in inter_te_idx] 62 | test_predict.append(pred[te_idx]) 63 | 64 | train_pre = torch.cat(train_predict, 0).numpy() 65 | valid_pre = torch.cat(valid_predict, 0).numpy() 66 | test_pre = torch.cat(test_predict, 0).numpy() 67 | 68 | train_pre_ordered = train_pre[process_indexes(train_target_idx)] 69 | valid_pre_ordered = valid_pre[process_indexes(valid_target_idx)] 70 | test_pre_ordered = test_pre[process_indexes(test_target_idx)] 71 | 72 | train_pre_ordered_list.append(train_pre_ordered) 73 | valid_pre_ordered_list.append(valid_pre_ordered) 74 | test_pre_ordered_list.append(test_pre_ordered) 75 | 76 | train_pre_final = torch.mean(torch.Tensor(train_pre_ordered_list), dim=0) 77 | valid_pre_final = torch.mean(torch.Tensor(valid_pre_ordered_list), dim=0) 78 | test_pre_final = torch.mean(torch.Tensor(test_pre_ordered_list), dim=0) 79 | 80 | eval_result = {} 81 | 82 | input_dict = {"y_true": target[train_idx], "y_pred": train_pre_final} 83 | eval_result["train"] = evaluator.eval(input_dict) 84 | 85 | input_dict = {"y_true": target[valid_idx], "y_pred": valid_pre_final} 86 | eval_result["valid"] = evaluator.eval(input_dict) 87 | 88 | input_dict = {"y_true": target[test_idx], "y_pred": test_pre_final} 89 | eval_result["test"] = evaluator.eval(input_dict) 90 | 91 | return eval_result 92 | 93 | 94 | def main(): 95 | args = ArgsInit().args 96 | 97 | if args.use_gpu: 98 | device = torch.device("cuda:" + str(args.device)) if torch.cuda.is_available() else torch.device("cpu") 99 | else: 100 | device = torch.device("cpu") 101 | 102 | dataset = OGBNDataset(dataset_name=args.dataset) 103 | args.num_tasks = dataset.num_tasks 104 | args.nf_path = dataset.extract_node_features(args.aggr) 105 | 106 | evaluator = Evaluator(args.dataset) 107 | 108 | valid_data_list = [] 109 | 110 | for i in range(args.num_evals): 111 | parts = dataset.random_partition_graph(dataset.total_no_of_nodes, 112 | cluster_number=args.valid_cluster_number) 113 | valid_data = dataset.generate_sub_graphs(parts, 114 | cluster_number=args.valid_cluster_number) 115 | valid_data_list.append(valid_data) 116 | 117 | model = DeeperGCN(args) 118 | 119 | model.load_state_dict(torch.load(args.model_load_path)['model_state_dict']) 120 | model.to(device) 121 | result = multi_evaluate(valid_data_list, dataset, model, evaluator, device) 122 | print(result) 123 | model.print_params(final=True) 124 | 125 | 126 | if __name__ == "__main__": 127 | main() 128 | -------------------------------------------------------------------------------- /deep_gcns_torch/gcn_lib/dense/torch_vertex.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch import nn 3 | from .torch_nn import BasicConv, batched_index_select 4 | from .torch_edge import DenseDilatedKnnGraph, DilatedKnnGraph 5 | import torch.nn.functional as F 6 | 7 | 8 | class MRConv2d(nn.Module): 9 | """ 10 | Max-Relative Graph Convolution (Paper: https://arxiv.org/abs/1904.03751) for dense data type 11 | """ 12 | def __init__(self, in_channels, out_channels, act='relu', norm=None, bias=True): 13 | super(MRConv2d, self).__init__() 14 | self.nn = BasicConv([in_channels*2, out_channels], act, norm, bias) 15 | 16 | def forward(self, x, edge_index): 17 | x_i = batched_index_select(x, edge_index[1]) 18 | x_j = batched_index_select(x, edge_index[0]) 19 | x_j, _ = torch.max(x_j - x_i, -1, keepdim=True) 20 | return self.nn(torch.cat([x, x_j], dim=1)) 21 | 22 | 23 | class EdgeConv2d(nn.Module): 24 | """ 25 | Edge convolution layer (with activation, batch normalization) for dense data type 26 | """ 27 | def __init__(self, in_channels, out_channels, act='relu', norm=None, bias=True): 28 | super(EdgeConv2d, self).__init__() 29 | self.nn = BasicConv([in_channels * 2, out_channels], act, norm, bias) 30 | 31 | def forward(self, x, edge_index): 32 | x_i = batched_index_select(x, edge_index[1]) 33 | x_j = batched_index_select(x, edge_index[0]) 34 | max_value, _ = torch.max(self.nn(torch.cat([x_i, x_j - x_i], dim=1)), -1, keepdim=True) 35 | return max_value 36 | 37 | 38 | class GraphConv2d(nn.Module): 39 | """ 40 | Static graph convolution layer 41 | """ 42 | def __init__(self, in_channels, out_channels, conv='edge', act='relu', norm=None, bias=True): 43 | super(GraphConv2d, self).__init__() 44 | if conv == 'edge': 45 | self.gconv = EdgeConv2d(in_channels, out_channels, act, norm, bias) 46 | elif conv == 'mr': 47 | self.gconv = MRConv2d(in_channels, out_channels, act, norm, bias) 48 | else: 49 | raise NotImplementedError('conv:{} is not supported'.format(conv)) 50 | 51 | def forward(self, x, edge_index): 52 | return self.gconv(x, edge_index) 53 | 54 | 55 | class DynConv2d(GraphConv2d): 56 | """ 57 | Dynamic graph convolution layer 58 | """ 59 | def __init__(self, in_channels, out_channels, kernel_size=9, dilation=1, conv='edge', act='relu', 60 | norm=None, bias=True, stochastic=False, epsilon=0.0, knn='matrix'): 61 | super(DynConv2d, self).__init__(in_channels, out_channels, conv, act, norm, bias) 62 | self.k = kernel_size 63 | self.d = dilation 64 | if knn == 'matrix': 65 | self.dilated_knn_graph = DenseDilatedKnnGraph(kernel_size, dilation, stochastic, epsilon) 66 | else: 67 | self.dilated_knn_graph = DilatedKnnGraph(kernel_size, dilation, stochastic, epsilon) 68 | 69 | def forward(self, x): 70 | edge_index = self.dilated_knn_graph(x) 71 | return super(DynConv2d, self).forward(x, edge_index) 72 | 73 | 74 | class PlainDynBlock2d(nn.Module): 75 | """ 76 | Plain Dynamic graph convolution block 77 | """ 78 | def __init__(self, in_channels, kernel_size=9, dilation=1, conv='edge', act='relu', norm=None, 79 | bias=True, stochastic=False, epsilon=0.0, knn='matrix'): 80 | super(PlainDynBlock2d, self).__init__() 81 | self.body = DynConv2d(in_channels, in_channels, kernel_size, dilation, conv, 82 | act, norm, bias, stochastic, epsilon, knn) 83 | 84 | def forward(self, x): 85 | return self.body(x) 86 | 87 | 88 | class ResDynBlock2d(nn.Module): 89 | """ 90 | Residual Dynamic graph convolution block 91 | """ 92 | def __init__(self, in_channels, kernel_size=9, dilation=1, conv='edge', act='relu', norm=None, 93 | bias=True, stochastic=False, epsilon=0.0, knn='matrix', res_scale=1): 94 | super(ResDynBlock2d, self).__init__() 95 | self.body = DynConv2d(in_channels, in_channels, kernel_size, dilation, conv, 96 | act, norm, bias, stochastic, epsilon, knn) 97 | self.res_scale = res_scale 98 | 99 | def forward(self, x): 100 | return self.body(x) + x*self.res_scale 101 | 102 | 103 | class DenseDynBlock2d(nn.Module): 104 | """ 105 | Dense Dynamic graph convolution block 106 | """ 107 | def __init__(self, in_channels, out_channels=64, kernel_size=9, dilation=1, conv='edge', 108 | act='relu', norm=None,bias=True, stochastic=False, epsilon=0.0, knn='matrix'): 109 | super(DenseDynBlock2d, self).__init__() 110 | self.body = DynConv2d(in_channels, out_channels, kernel_size, dilation, conv, 111 | act, norm, bias, stochastic, epsilon, knn) 112 | 113 | def forward(self, x): 114 | dense = self.body(x) 115 | return torch.cat((x, dense), 1) 116 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/sem_seg_dense/architecture.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from gcn_lib.dense import BasicConv, GraphConv2d, PlainDynBlock2d, ResDynBlock2d, DenseDynBlock2d, DenseDilatedKnnGraph 3 | from torch.nn import Sequential as Seq 4 | 5 | 6 | class DenseDeepGCN(torch.nn.Module): 7 | def __init__(self, opt): 8 | super(DenseDeepGCN, self).__init__() 9 | channels = opt.n_filters 10 | k = opt.k 11 | act = opt.act 12 | norm = opt.norm 13 | bias = opt.bias 14 | epsilon = opt.epsilon 15 | stochastic = opt.stochastic 16 | conv = opt.conv 17 | c_growth = channels 18 | self.n_blocks = opt.n_blocks 19 | 20 | self.knn = DenseDilatedKnnGraph(k, 1, stochastic, epsilon) 21 | self.head = GraphConv2d(opt.in_channels, channels, conv, act, norm, bias) 22 | 23 | if opt.block.lower() == 'res': 24 | self.backbone = Seq(*[ResDynBlock2d(channels, k, 1+i, conv, act, norm, bias, stochastic, epsilon) 25 | for i in range(self.n_blocks-1)]) 26 | fusion_dims = int(channels + c_growth * (self.n_blocks - 1)) 27 | elif opt.block.lower() == 'dense': 28 | self.backbone = Seq(*[DenseDynBlock2d(channels+c_growth*i, c_growth, k, 1+i, conv, act, 29 | norm, bias, stochastic, epsilon) 30 | for i in range(self.n_blocks-1)]) 31 | fusion_dims = int( 32 | (channels + channels + c_growth * (self.n_blocks - 1)) * self.n_blocks // 2) 33 | else: 34 | stochastic = False 35 | 36 | self.backbone = Seq(*[PlainDynBlock2d(channels, k, 1, conv, act, norm, 37 | bias, stochastic, epsilon) 38 | for i in range(self.n_blocks - 1)]) 39 | fusion_dims = int(channels + c_growth * (self.n_blocks - 1)) 40 | 41 | self.fusion_block = BasicConv([fusion_dims, 1024], act, norm, bias) 42 | self.prediction = Seq(*[BasicConv([fusion_dims+1024, 512], act, norm, bias), 43 | BasicConv([512, 256], act, norm, bias), 44 | torch.nn.Dropout(p=opt.dropout), 45 | BasicConv([256, opt.n_classes], None, None, bias)]) 46 | 47 | def forward(self, inputs): 48 | feats = [self.head(inputs, self.knn(inputs[:, 0:3]))] 49 | for i in range(self.n_blocks-1): 50 | feats.append(self.backbone[i](feats[-1])) 51 | feats = torch.cat(feats, dim=1) 52 | 53 | fusion = torch.max_pool2d(self.fusion_block(feats), kernel_size=[feats.shape[2], feats.shape[3]]) 54 | fusion = torch.repeat_interleave(fusion, repeats=feats.shape[2], dim=2) 55 | return self.prediction(torch.cat((fusion, feats), dim=1)).squeeze(-1) 56 | 57 | 58 | if __name__ == "__main__": 59 | import random, numpy as np, argparse 60 | seed = 0 61 | torch.manual_seed(seed) 62 | torch.cuda.manual_seed(seed) 63 | torch.cuda.manual_seed_all(seed) 64 | torch.backends.cudnn.deterministic = True 65 | torch.backends.cudnn.benchmark = False 66 | 67 | batch_size = 2 68 | N = 1024 69 | device = 'cuda' 70 | 71 | parser = argparse.ArgumentParser(description='PyTorch implementation of Deep GCN For semantic segmentation') 72 | parser.add_argument('--in_channels', default=9, type=int, help='input channels (default:9)') 73 | parser.add_argument('--n_classes', default=13, type=int, help='num of segmentation classes (default:13)') 74 | parser.add_argument('--k', default=20, type=int, help='neighbor num (default:16)') 75 | parser.add_argument('--block', default='res', type=str, help='graph backbone block type {plain, res, dense}') 76 | parser.add_argument('--conv', default='edge', type=str, help='graph conv layer {edge, mr}') 77 | parser.add_argument('--act', default='relu', type=str, help='activation layer {relu, prelu, leakyrelu}') 78 | parser.add_argument('--norm', default='batch', type=str, help='{batch, instance} normalization') 79 | parser.add_argument('--bias', default=True, type=bool, help='bias of conv layer True or False') 80 | parser.add_argument('--n_filters', default=64, type=int, help='number of channels of deep features') 81 | parser.add_argument('--n_blocks', default=7, type=int, help='number of basic blocks') 82 | parser.add_argument('--dropout', default=0.5, type=float, help='ratio of dropout') 83 | parser.add_argument('--epsilon', default=0.2, type=float, help='stochastic epsilon for gcn') 84 | parser.add_argument('--stochastic', default=False, type=bool, help='stochastic for gcn, True or False') 85 | args = parser.parse_args() 86 | 87 | pos = torch.rand((batch_size, N, 3), dtype=torch.float).to(device) 88 | x = torch.rand((batch_size, N, 6), dtype=torch.float).to(device) 89 | 90 | inputs = torch.cat((pos, x), 2).transpose(1, 2).unsqueeze(-1) 91 | 92 | # net = DGCNNSegDense().to(device) 93 | net = DenseDeepGCN(args).to(device) 94 | print(net) 95 | out = net(inputs) 96 | print(out.shape) 97 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbn_arxiv/args.py: -------------------------------------------------------------------------------- 1 | import __init__ 2 | import argparse 3 | import uuid 4 | import logging 5 | import time 6 | import os 7 | import sys 8 | from utils.logger import create_exp_dir 9 | import glob 10 | 11 | 12 | class ArgsInit(object): 13 | def __init__(self): 14 | parser = argparse.ArgumentParser(description='DeeperGCN') 15 | # dataset 16 | parser.add_argument('--dataset', type=str, default='ogbn-arxiv', 17 | help='dataset name (default: ogbn-arxiv)') 18 | parser.add_argument('--self_loop', action='store_true') 19 | # training & eval settings 20 | parser.add_argument('--use_gpu', action='store_true') 21 | parser.add_argument('--device', type=int, default=0, 22 | help='which gpu to use if any (default: 0)') 23 | parser.add_argument('--epochs', type=int, default=500, 24 | help='number of epochs to train (default: 500)') 25 | parser.add_argument('--lr', type=float, default=0.01, 26 | help='learning rate set for optimizer.') 27 | parser.add_argument('--dropout', type=float, default=0.5) 28 | # model 29 | parser.add_argument('--num_layers', type=int, default=3, 30 | help='the number of layers of the networks') 31 | parser.add_argument('--mlp_layers', type=int, default=1, 32 | help='the number of layers of mlp in conv') 33 | parser.add_argument('--in_channels', type=int, default=128, 34 | help='the dimension of initial embeddings of nodes') 35 | parser.add_argument('--hidden_channels', type=int, default=128, 36 | help='the dimension of embeddings of nodes') 37 | parser.add_argument('--block', default='res+', type=str, 38 | help='graph backbone block type {res+, res, dense, plain}') 39 | parser.add_argument('--conv', type=str, default='gen', 40 | help='the type of GCNs') 41 | parser.add_argument('--gcn_aggr', type=str, default='max', 42 | help='the aggregator of GENConv [mean, max, add, softmax, softmax_sg, power]') 43 | parser.add_argument('--norm', type=str, default='batch', 44 | help='the type of normalization layer') 45 | parser.add_argument('--num_tasks', type=int, default=1, 46 | help='the number of prediction tasks') 47 | # learnable parameters 48 | parser.add_argument('--t', type=float, default=1.0, 49 | help='the temperature of SoftMax') 50 | parser.add_argument('--p', type=float, default=1.0, 51 | help='the power of PowerMean') 52 | parser.add_argument('--learn_t', action='store_true') 53 | parser.add_argument('--learn_p', action='store_true') 54 | # message norm 55 | parser.add_argument('--msg_norm', action='store_true') 56 | parser.add_argument('--learn_msg_scale', action='store_true') 57 | # save model 58 | parser.add_argument('--model_save_path', type=str, default='model_ckpt', 59 | help='the directory used to save models') 60 | parser.add_argument('--save', type=str, default='EXP', help='experiment name') 61 | # load pre-trained model 62 | parser.add_argument('--model_load_path', type=str, default='ogbn_arxiv_pretrained_model.pth', 63 | help='the path of pre-trained model') 64 | 65 | #newly added attack hyper-parameters 66 | parser.add_argument('--step-size', type=float, default=8e-3) 67 | parser.add_argument('-m', type=int, default=3) 68 | 69 | self.args = parser.parse_args() 70 | 71 | def save_exp(self): 72 | 73 | self.args.save = '{}-B_{}-C_{}-L_{}-F_{}-DP_{}' \ 74 | '-GA_{}-T_{}-LT_{}-P_{}-LP_{}' \ 75 | '-MN_{}-LS_{}'.format(self.args.save, self.args.block, self.args.conv, 76 | self.args.num_layers, self.args.hidden_channels, 77 | self.args.dropout, self.args.gcn_aggr, 78 | self.args.t, self.args.learn_t, self.args.p, self.args.learn_p, 79 | self.args.msg_norm, self.args.learn_msg_scale) 80 | 81 | self.args.save = 'log/{}-{}-{}'.format(self.args.save, time.strftime("%Y%m%d-%H%M%S"), str(uuid.uuid4())) 82 | self.args.model_save_path = os.path.join(self.args.save, self.args.model_save_path) 83 | create_exp_dir(self.args.save, scripts_to_save=glob.glob('*.py')) 84 | log_format = '%(asctime)s %(message)s' 85 | logging.basicConfig(stream=sys.stdout, 86 | level=logging.INFO, 87 | format=log_format, 88 | datefmt='%m/%d %I:%M:%S %p') 89 | fh = logging.FileHandler(os.path.join(self.args.save, 'log.txt')) 90 | fh.setFormatter(logging.Formatter(log_format)) 91 | logging.getLogger().addHandler(fh) 92 | 93 | return self.args 94 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/sem_seg_sparse/train.py: -------------------------------------------------------------------------------- 1 | import __init__ 2 | import torch 3 | import torch_geometric.datasets as GeoData 4 | from torch_geometric.data import DataLoader, DataListLoader 5 | import torch_geometric.transforms as T 6 | from torch_geometric.nn.data_parallel import DataParallel 7 | from config import OptInit 8 | from architecture import SparseDeepGCN 9 | from utils.ckpt_util import load_pretrained_models, load_pretrained_optimizer, save_checkpoint 10 | from utils.metrics import AverageMeter 11 | from utils import optim 12 | import logging 13 | 14 | 15 | def main(): 16 | opt = OptInit().get_args() 17 | logging.info('===> Creating dataloader ...') 18 | train_dataset = GeoData.S3DIS(opt.data_dir, test_area=5, train=True, pre_transform=T.NormalizeScale()) 19 | if opt.multi_gpus: 20 | train_loader = DataListLoader(train_dataset, batch_size=opt.batch_size, shuffle=True, num_workers=4) 21 | else: 22 | train_loader = DataLoader(train_dataset, batch_size=opt.batch_size, shuffle=True, num_workers=4) 23 | opt.n_classes = train_loader.dataset.num_classes 24 | 25 | logging.info('===> Loading the network ...') 26 | model = SparseDeepGCN(opt).to(opt.device) 27 | if opt.multi_gpus: 28 | model = DataParallel(SparseDeepGCN(opt)).to(opt.device) 29 | logging.info('===> loading pre-trained ...') 30 | model, opt.best_value, opt.epoch = load_pretrained_models(model, opt.pretrained_model, opt.phase) 31 | logging.info(model) 32 | 33 | logging.info('===> Init the optimizer ...') 34 | criterion = torch.nn.CrossEntropyLoss().to(opt.device) 35 | optimizer = torch.optim.Adam(model.parameters(), lr=opt.lr) 36 | 37 | scheduler = torch.optim.lr_scheduler.StepLR(optimizer, opt.lr_adjust_freq, opt.lr_decay_rate) 38 | optimizer, scheduler, opt.lr = load_pretrained_optimizer(opt.pretrained_model, optimizer, scheduler, opt.lr) 39 | 40 | logging.info('===> Init Metric ...') 41 | opt.losses = AverageMeter() 42 | # opt.test_metric = miou 43 | # opt.test_values = AverageMeter() 44 | opt.test_value = 0. 45 | 46 | logging.info('===> start training ...') 47 | for _ in range(opt.total_epochs): 48 | opt.epoch += 1 49 | train(model, train_loader, optimizer, scheduler, criterion, opt) 50 | # test_value = test(model, test_loader, test_metric, opt) 51 | scheduler.step() 52 | logging.info('Saving the final model.Finish!') 53 | 54 | 55 | def train(model, train_loader, optimizer, scheduler, criterion, opt): 56 | model.train() 57 | for i, data in enumerate(train_loader): 58 | opt.iter += 1 59 | if not opt.multi_gpus: 60 | data = data.to(opt.device) 61 | gt = data.y 62 | else: 63 | gt = torch.cat([data_batch.y for data_batch in data], 0).to(opt.device) 64 | 65 | # ------------------ zero, output, loss 66 | optimizer.zero_grad() 67 | out = model(data) 68 | loss = criterion(out, gt) 69 | 70 | # ------------------ optimization 71 | loss.backward() 72 | optimizer.step() 73 | 74 | opt.losses.update(loss.item()) 75 | # ------------------ show information 76 | if opt.iter % opt.print_freq == 0: 77 | logging.info('Epoch:{}\t Iter: {}\t [{}/{}]\t Loss: {Losses.avg: .4f}'.format( 78 | opt.epoch, opt.iter, i + 1, len(train_loader), Losses=opt.losses)) 79 | opt.losses.reset() 80 | 81 | # ------------------ tensor board log 82 | info = { 83 | 'loss': loss, 84 | 'test_value': opt.test_value, 85 | 'lr': scheduler.get_lr()[0] 86 | } 87 | for tag, value in info.items(): 88 | opt.logger.scalar_summary(tag, value, opt.iter) 89 | 90 | # ------------------ save checkpoints 91 | # min or max. based on the metrics 92 | is_best = (opt.test_value < opt.best_value) 93 | opt.best_value = min(opt.test_value, opt.best_value) 94 | 95 | model_cpu = {k: v.cpu() for k, v in model.state_dict().items()} 96 | # optim_cpu = {k: v.cpu() for k, v in optimizer.state_dict().items()} 97 | save_checkpoint({ 98 | 'epoch': opt.epoch, 99 | 'state_dict': model_cpu, 100 | 'optimizer_state_dict': optimizer.state_dict(), 101 | 'scheduler_state_dict': scheduler.state_dict(), 102 | 'best_value': opt.best_value, 103 | }, is_best, opt.save_path, opt.post) 104 | 105 | 106 | def test(model, test_loader, test_metric, opt): 107 | opt.test_values.reset() 108 | model.eval() 109 | with torch.no_grad(): 110 | for i, data in enumerate(test_loader): 111 | if not opt.multi_gpus: 112 | data = data.to(opt.device) 113 | gt = data.y 114 | else: 115 | gt = torch.cat([data_batch.y for data_batch in data], 0).to(opt.device) 116 | 117 | out = opt.model(data) 118 | test_value = test_metric(out.max(dim=1)[1], gt, opt.n_classes) 119 | opt.test_values.update(test_value, opt.batch_size) 120 | logging.info('Epoch: [{0}]\t Iter: [{1}]\t''TEST loss: {test_values.avg: .4f})\t'.format( 121 | opt.epoch, opt.iter, test_values=opt.test_values)) 122 | 123 | opt.test_value = opt.test_values.avg 124 | 125 | 126 | if __name__ == '__main__': 127 | main() 128 | 129 | 130 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbn_products/args.py: -------------------------------------------------------------------------------- 1 | import __init__ 2 | import argparse 3 | import uuid 4 | import logging 5 | import time 6 | import os 7 | import sys 8 | from utils.logger import create_exp_dir 9 | import glob 10 | 11 | 12 | class ArgsInit(object): 13 | def __init__(self): 14 | parser = argparse.ArgumentParser(description='DeeperGCN') 15 | # dataset 16 | parser.add_argument('--dataset', type=str, default='ogbn-products', 17 | help='dataset name (default: ogbn-products)') 18 | parser.add_argument('--cluster_number', type=int, default=10, 19 | help='the number of sub-graphs for training') 20 | parser.add_argument('--self_loop', action='store_true') 21 | # training & eval settings 22 | parser.add_argument('--use_gpu', action='store_true') 23 | parser.add_argument('--device', type=int, default=0, 24 | help='which gpu to use if any (default: 0)') 25 | parser.add_argument('--epochs', type=int, default=500, 26 | help='number of epochs to train (default: 500)') 27 | parser.add_argument('--lr', type=float, default=0.01, 28 | help='learning rate set for optimizer.') 29 | parser.add_argument('--dropout', type=float, default=0.5) 30 | # model 31 | parser.add_argument('--num_layers', type=int, default=3, 32 | help='the number of layers of the networks') 33 | parser.add_argument('--mlp_layers', type=int, default=1, 34 | help='the number of layers of mlp in conv') 35 | parser.add_argument('--in_channels', type=int, default=128, 36 | help='the dimension of initial embeddings of nodes') 37 | parser.add_argument('--hidden_channels', type=int, default=128, 38 | help='the dimension of embeddings of nodes') 39 | parser.add_argument('--block', default='res+', type=str, 40 | help='graph backbone block type {res+, res, dense, plain}') 41 | parser.add_argument('--conv', type=str, default='gen', 42 | help='the type of GCNs') 43 | parser.add_argument('--gcn_aggr', type=str, default='max', 44 | help='the aggregator of GENConv [mean, max, add, softmax, softmax_sg, power]') 45 | parser.add_argument('--norm', type=str, default='batch', 46 | help='the type of normalization layer') 47 | parser.add_argument('--num_tasks', type=int, default=1, 48 | help='the number of prediction tasks') 49 | # learnable parameters 50 | parser.add_argument('--t', type=float, default=1.0, 51 | help='the temperature of SoftMax') 52 | parser.add_argument('--p', type=float, default=1.0, 53 | help='the power of PowerMean') 54 | parser.add_argument('--learn_t', action='store_true') 55 | parser.add_argument('--learn_p', action='store_true') 56 | # message norm 57 | parser.add_argument('--msg_norm', action='store_true') 58 | parser.add_argument('--learn_msg_scale', action='store_true') 59 | # save model 60 | parser.add_argument('--model_save_path', type=str, default='model_ckpt', 61 | help='the directory used to save models') 62 | parser.add_argument('--save', type=str, default='EXP', help='experiment name') 63 | # load pre-trained model 64 | parser.add_argument('--model_load_path', type=str, default='ogbn_products_pretrained_model.pth', 65 | help='the path of pre-trained model') 66 | 67 | #newly added attack hyper-parameters 68 | parser.add_argument('--step-size', type=float, default=5e-3) 69 | parser.add_argument('-m', type=int, default=3) 70 | parser.add_argument('--amp', type=int, default=2) 71 | 72 | 73 | self.args = parser.parse_args() 74 | 75 | def save_exp(self): 76 | 77 | self.args.save = '{}-B_{}-C_{}-L_{}-F_{}-DP_{}' \ 78 | '-GA_{}-T_{}-LT_{}-P_{}-LP_{}' \ 79 | '-MN_{}-LS_{}'.format(self.args.save, self.args.block, self.args.conv, 80 | self.args.num_layers, self.args.hidden_channels, 81 | self.args.dropout, self.args.gcn_aggr, 82 | self.args.t, self.args.learn_t, self.args.p, self.args.learn_p, 83 | self.args.msg_norm, self.args.learn_msg_scale) 84 | 85 | self.args.save = 'log/{}-{}-{}'.format(self.args.save, time.strftime("%Y%m%d-%H%M%S"), str(uuid.uuid4())) 86 | self.args.model_save_path = os.path.join(self.args.save, self.args.model_save_path) 87 | create_exp_dir(self.args.save, scripts_to_save=glob.glob('*.py')) 88 | log_format = '%(asctime)s %(message)s' 89 | logging.basicConfig(stream=sys.stdout, 90 | level=logging.INFO, 91 | format=log_format, 92 | datefmt='%m/%d %I:%M:%S %p') 93 | fh = logging.FileHandler(os.path.join(self.args.save, 'log.txt')) 94 | fh.setFormatter(logging.Formatter(log_format)) 95 | logging.getLogger().addHandler(fh) 96 | 97 | return self.args 98 | -------------------------------------------------------------------------------- /deep_gcns_torch/utils/ckpt_util.py: -------------------------------------------------------------------------------- 1 | import os 2 | import torch 3 | import shutil 4 | from collections import OrderedDict 5 | import logging 6 | import numpy as np 7 | 8 | 9 | def save_ckpt(model, optimizer, loss, epoch, save_path, name_pre, name_post='best'): 10 | model_cpu = {k: v.cpu() for k, v in model.state_dict().items()} 11 | state = { 12 | 'epoch': epoch, 13 | 'model_state_dict': model_cpu, 14 | 'optimizer_state_dict': optimizer.state_dict(), 15 | 'loss': loss 16 | } 17 | 18 | if not os.path.exists(save_path): 19 | os.mkdir(save_path) 20 | print("Directory ", save_path, " is created.") 21 | 22 | filename = '{}/{}_{}.pth'.format(save_path, name_pre, name_post) 23 | torch.save(state, filename) 24 | print('model has been saved as {}'.format(filename)) 25 | 26 | 27 | def load_pretrained_models(model, pretrained_model, phase, ismax=True): # ismax means max best 28 | if ismax: 29 | best_value = -np.inf 30 | else: 31 | best_value = np.inf 32 | epoch = -1 33 | 34 | if pretrained_model: 35 | if os.path.isfile(pretrained_model): 36 | logging.info("===> Loading checkpoint '{}'".format(pretrained_model)) 37 | checkpoint = torch.load(pretrained_model) 38 | try: 39 | best_value = checkpoint['best_value'] 40 | if best_value == -np.inf or best_value == np.inf: 41 | show_best_value = False 42 | else: 43 | show_best_value = True 44 | except: 45 | best_value = best_value 46 | show_best_value = False 47 | 48 | model_dict = model.state_dict() 49 | ckpt_model_state_dict = checkpoint['state_dict'] 50 | 51 | # rename ckpt (avoid name is not same because of multi-gpus) 52 | is_model_multi_gpus = True if list(model_dict)[0][0][0] == 'm' else False 53 | is_ckpt_multi_gpus = True if list(ckpt_model_state_dict)[0][0] == 'm' else False 54 | 55 | if not (is_model_multi_gpus == is_ckpt_multi_gpus): 56 | temp_dict = OrderedDict() 57 | for k, v in ckpt_model_state_dict.items(): 58 | if is_ckpt_multi_gpus: 59 | name = k[7:] # remove 'module.' 60 | else: 61 | name = 'module.'+k # add 'module' 62 | temp_dict[name] = v 63 | # load params 64 | ckpt_model_state_dict = temp_dict 65 | 66 | model_dict.update(ckpt_model_state_dict) 67 | model.load_state_dict(ckpt_model_state_dict) 68 | 69 | if show_best_value: 70 | logging.info("The pretrained_model is at checkpoint {}. \t " 71 | "Best value: {}".format(checkpoint['epoch'], best_value)) 72 | else: 73 | logging.info("The pretrained_model is at checkpoint {}.".format(checkpoint['epoch'])) 74 | 75 | if phase == 'train': 76 | epoch = checkpoint['epoch'] 77 | else: 78 | epoch = -1 79 | else: 80 | raise ImportError("===> No checkpoint found at '{}'".format(pretrained_model)) 81 | else: 82 | logging.info('===> No pre-trained model') 83 | return model, best_value, epoch 84 | 85 | 86 | def load_pretrained_optimizer(pretrained_model, optimizer, scheduler, lr, use_ckpt_lr=True): 87 | if pretrained_model: 88 | if os.path.isfile(pretrained_model): 89 | checkpoint = torch.load(pretrained_model) 90 | if 'optimizer_state_dict' in checkpoint.keys(): 91 | optimizer.load_state_dict(checkpoint['optimizer_state_dict']) 92 | for state in optimizer.state.values(): 93 | for k, v in state.items(): 94 | if torch.is_tensor(v): 95 | state[k] = v.cuda() 96 | if 'scheduler_state_dict' in checkpoint.keys(): 97 | scheduler.load_state_dict(checkpoint['scheduler_state_dict']) 98 | if use_ckpt_lr: 99 | try: 100 | lr = scheduler.get_lr()[0] 101 | except: 102 | lr = lr 103 | 104 | return optimizer, scheduler, lr 105 | 106 | 107 | def save_checkpoint(state, is_best, save_path, postname): 108 | filename = '{}/{}_ckpt_{}.pth'.format(save_path, postname, int(state['epoch'])) 109 | torch.save(state, filename) 110 | if is_best: 111 | shutil.copyfile(filename, '{}/{}_model_best.pth'.format(save_path, postname)) 112 | 113 | 114 | def change_ckpt_dict(model, optimizer, scheduler, opt): 115 | 116 | for _ in range(opt.epoch): 117 | scheduler.step() 118 | is_best = (opt.test_value < opt.best_value) 119 | opt.best_value = min(opt.test_value, opt.best_value) 120 | 121 | model_cpu = {k: v.cpu() for k, v in model.state_dict().items()} 122 | # optim_cpu = {k: v.cpu() for k, v in optimizer.state_dict().items()} 123 | save_checkpoint({ 124 | 'epoch': opt.epoch, 125 | 'state_dict': model_cpu, 126 | 'optimizer_state_dict': optimizer.state_dict(), 127 | 'scheduler_state_dict': scheduler.state_dict(), 128 | 'best_value': opt.best_value, 129 | }, is_best, opt.save_path, opt.post) 130 | 131 | -------------------------------------------------------------------------------- /ogb/nodeproppred/arxiv/mlp.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | import torch 4 | import torch.nn.functional as F 5 | 6 | from ogb.nodeproppred import PygNodePropPredDataset, Evaluator 7 | 8 | import numpy as np 9 | import sys 10 | sys.path.insert(0,'../..') 11 | from attacks import * 12 | 13 | 14 | class MLP(torch.nn.Module): 15 | def __init__(self, in_channels, hidden_channels, out_channels, num_layers, 16 | dropout): 17 | super(MLP, self).__init__() 18 | 19 | self.lins = torch.nn.ModuleList() 20 | self.lins.append(torch.nn.Linear(in_channels, hidden_channels)) 21 | self.bns = torch.nn.ModuleList() 22 | self.bns.append(torch.nn.BatchNorm1d(hidden_channels)) 23 | for _ in range(num_layers - 2): 24 | self.lins.append(torch.nn.Linear(hidden_channels, hidden_channels)) 25 | self.bns.append(torch.nn.BatchNorm1d(hidden_channels)) 26 | self.lins.append(torch.nn.Linear(hidden_channels, out_channels)) 27 | 28 | self.dropout = dropout 29 | 30 | def reset_parameters(self): 31 | for lin in self.lins: 32 | lin.reset_parameters() 33 | for bn in self.bns: 34 | bn.reset_parameters() 35 | 36 | def forward(self, x): 37 | for i, lin in enumerate(self.lins[:-1]): 38 | x = lin(x) 39 | x = self.bns[i](x) 40 | x = F.relu(x) 41 | x = F.dropout(x, p=self.dropout, training=self.training) 42 | x = self.lins[-1](x) 43 | return torch.log_softmax(x, dim=-1) 44 | 45 | 46 | def train(model, x, y_true, train_idx, optimizer): 47 | model.train() 48 | 49 | optimizer.zero_grad() 50 | out = model(x[train_idx]) 51 | loss = F.nll_loss(out, y_true.squeeze(1)[train_idx]) 52 | loss.backward() 53 | optimizer.step() 54 | 55 | return loss.item() 56 | 57 | def train_flag(model, x, y_true, train_idx, optimizer, args, device) : 58 | 59 | forward = lambda perturb : model(x[train_idx] + perturb) 60 | model_forward = (model, forward) 61 | y = y_true.squeeze(1)[train_idx] 62 | loss, _ = flag(model_forward, x[train_idx].shape, y, args, optimizer, device, F.nll_loss) 63 | 64 | return loss.item() 65 | 66 | 67 | @torch.no_grad() 68 | def test(model, x, y_true, split_idx, evaluator): 69 | model.eval() 70 | 71 | out = model(x) 72 | y_pred = out.argmax(dim=-1, keepdim=True) 73 | 74 | train_acc = evaluator.eval({ 75 | 'y_true': y_true[split_idx['train']], 76 | 'y_pred': y_pred[split_idx['train']], 77 | })['acc'] 78 | valid_acc = evaluator.eval({ 79 | 'y_true': y_true[split_idx['valid']], 80 | 'y_pred': y_pred[split_idx['valid']], 81 | })['acc'] 82 | test_acc = evaluator.eval({ 83 | 'y_true': y_true[split_idx['test']], 84 | 'y_pred': y_pred[split_idx['test']], 85 | })['acc'] 86 | 87 | return train_acc, valid_acc, test_acc 88 | 89 | 90 | def main(): 91 | parser = argparse.ArgumentParser(description='OGBN-Arxiv (MLP)') 92 | parser.add_argument('--device', type=int, default=0) 93 | parser.add_argument('--log_steps', type=int, default=1) 94 | parser.add_argument('--use_node_embedding', action='store_true') 95 | parser.add_argument('--num_layers', type=int, default=3) 96 | parser.add_argument('--hidden_channels', type=int, default=256) 97 | parser.add_argument('--dropout', type=float, default=0.5) 98 | parser.add_argument('--lr', type=float, default=0.01) 99 | parser.add_argument('--epochs', type=int, default=500) 100 | parser.add_argument('--runs', type=int, default=10) 101 | 102 | parser.add_argument('--step-size', type=float, default=2e-3) 103 | parser.add_argument('-m', type=int, default=3) 104 | parser.add_argument('--attack', type=str, default='flag') 105 | 106 | args = parser.parse_args() 107 | print(args) 108 | 109 | device = f'cuda:{args.device}' if torch.cuda.is_available() else 'cpu' 110 | device = torch.device(device) 111 | 112 | dataset = PygNodePropPredDataset(name='ogbn-arxiv') 113 | split_idx = dataset.get_idx_split() 114 | data = dataset[0] 115 | 116 | x = data.x 117 | if args.use_node_embedding: 118 | embedding = torch.load('embedding.pt', map_location='cpu') 119 | x = torch.cat([x, embedding], dim=-1) 120 | x = x.to(device) 121 | 122 | y_true = data.y.to(device) 123 | train_idx = split_idx['train'].to(device) 124 | 125 | model = MLP(x.size(-1), args.hidden_channels, dataset.num_classes, 126 | args.num_layers, args.dropout).to(device) 127 | 128 | evaluator = Evaluator(name='ogbn-arxiv') 129 | 130 | vals, tests = [], [] 131 | for run in range(args.runs): 132 | best_val, final_test = 0, 0 133 | 134 | model.reset_parameters() 135 | optimizer = torch.optim.Adam(model.parameters(), lr=args.lr) 136 | 137 | for epoch in range(1, args.epochs + 1): 138 | loss = train_flag(model, x, y_true, train_idx, optimizer, args, device) 139 | result = test(model, x, y_true, split_idx, evaluator) 140 | train, val, tst = result 141 | if val > best_val: 142 | best_val = val 143 | final_test = tst 144 | 145 | print(f'Run{run} val:{best_val}, test:{final_test}') 146 | vals.append(best_val) 147 | tests.append(final_test) 148 | 149 | print('') 150 | print(f"Average val accuracy: {np.mean(vals)} ± {np.std(vals)}") 151 | print(f"Average test accuracy: {np.mean(tests)} ± {np.std(tests)}") 152 | 153 | 154 | if __name__ == "__main__": 155 | main() 156 | -------------------------------------------------------------------------------- /ogb/nodeproppred/products/mlp.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | import torch 4 | import torch.nn.functional as F 5 | 6 | from ogb.nodeproppred import PygNodePropPredDataset, Evaluator 7 | 8 | import numpy as np 9 | 10 | import time 11 | import sys 12 | sys.path.insert(0,'../..') 13 | from attacks import * 14 | 15 | class MLP(torch.nn.Module): 16 | def __init__(self, in_channels, hidden_channels, out_channels, num_layers, 17 | dropout): 18 | super(MLP, self).__init__() 19 | 20 | self.lins = torch.nn.ModuleList() 21 | self.lins.append(torch.nn.Linear(in_channels, hidden_channels)) 22 | for _ in range(num_layers - 2): 23 | self.lins.append(torch.nn.Linear(hidden_channels, hidden_channels)) 24 | self.lins.append(torch.nn.Linear(hidden_channels, out_channels)) 25 | 26 | self.dropout = dropout 27 | 28 | def reset_parameters(self): 29 | for lin in self.lins: 30 | lin.reset_parameters() 31 | 32 | def forward(self, x): 33 | for lin in self.lins[:-1]: 34 | x = lin(x) 35 | x = F.relu(x) 36 | x = F.dropout(x, p=self.dropout, training=self.training) 37 | x = self.lins[-1](x) 38 | return torch.log_softmax(x, dim=-1) 39 | 40 | 41 | def train(model, x, y_true, train_idx, optimizer): 42 | model.train() 43 | 44 | optimizer.zero_grad() 45 | out = model(x[train_idx]) 46 | loss = F.nll_loss(out, y_true.squeeze(1)[train_idx]) 47 | loss.backward() 48 | optimizer.step() 49 | 50 | return loss.item() 51 | 52 | 53 | def train_flag(model, x, y_true, train_idx, optimizer, args, device) : 54 | 55 | forward = lambda perturb : model(x[train_idx] + perturb) 56 | model_forward = (model, forward) 57 | y = y_true.squeeze(1)[train_idx] 58 | loss, _ = flag(model_forward, x[train_idx].shape, y, args, optimizer, device, F.nll_loss) 59 | 60 | return loss.item() 61 | 62 | @torch.no_grad() 63 | def test(model, x, y_true, split_idx, evaluator): 64 | model.eval() 65 | 66 | out = model(x) 67 | y_pred = out.argmax(dim=-1, keepdim=True) 68 | 69 | train_acc = evaluator.eval({ 70 | 'y_true': y_true[split_idx['train']], 71 | 'y_pred': y_pred[split_idx['train']], 72 | })['acc'] 73 | valid_acc = evaluator.eval({ 74 | 'y_true': y_true[split_idx['valid']], 75 | 'y_pred': y_pred[split_idx['valid']], 76 | })['acc'] 77 | test_acc = evaluator.eval({ 78 | 'y_true': y_true[split_idx['test']], 79 | 'y_pred': y_pred[split_idx['test']], 80 | })['acc'] 81 | 82 | return train_acc, valid_acc, test_acc 83 | 84 | 85 | def main(): 86 | parser = argparse.ArgumentParser(description='OGBN-Products (MLP)') 87 | parser.add_argument('--device', type=int, default=0) 88 | parser.add_argument('--use_node_embedding', action='store_true') 89 | parser.add_argument('--num_layers', type=int, default=3) 90 | parser.add_argument('--hidden_channels', type=int, default=256) 91 | parser.add_argument('--dropout', type=float, default=0.0) 92 | parser.add_argument('--lr', type=float, default=0.01) 93 | parser.add_argument('--epochs', type=int, default=300) 94 | parser.add_argument('--runs', type=int, default=10) 95 | 96 | 97 | parser.add_argument('--step-size', type=float, default=2e-2) 98 | parser.add_argument('-m', type=int, default=3) 99 | parser.add_argument('--test-freq', type=int, default=1) 100 | parser.add_argument('--start-seed', type=int, default=0) 101 | parser.add_argument('--attack', type=str, default='flag') 102 | parser.add_argument('--amp', type=float, default=2) 103 | parser.add_argument('--id', type=str, default='test') 104 | 105 | 106 | args = parser.parse_args() 107 | 108 | device = f'cuda:{args.device}' if torch.cuda.is_available() else 'cpu' 109 | device = torch.device(device) 110 | 111 | dataset = PygNodePropPredDataset(name='ogbn-products') 112 | split_idx = dataset.get_idx_split() 113 | data = dataset[0] 114 | 115 | x = data.x 116 | if args.use_node_embedding: 117 | embedding = torch.load('embedding.pt', map_location='cpu') 118 | x = torch.cat([x, embedding], dim=-1) 119 | x = x.to(device) 120 | 121 | y_true = data.y.to(device) 122 | train_idx = split_idx['train'].to(device) 123 | 124 | model = MLP(x.size(-1), args.hidden_channels, dataset.num_classes, args.num_layers, 125 | args.dropout).to(device) 126 | 127 | evaluator = Evaluator(name='ogbn-products') 128 | 129 | vals, tests = [], [] 130 | for run in range(args.runs): 131 | best_val, final_test = 0, 0 132 | 133 | seed = run + args.start_seed 134 | model.reset_parameters() 135 | optimizer = torch.optim.Adam(model.parameters(), lr=args.lr) 136 | 137 | start = time.time() 138 | 139 | for epoch in range(1, args.epochs + 1): 140 | # loss = train(model, x, y_true, train_idx, optimizer) 141 | loss = train_flag(model, x, y_true, train_idx, optimizer, args, device) 142 | if epoch > 0 and epoch % args.test_freq == 0 or epoch == args.epochs: 143 | result = test(model, x, y_true, split_idx, evaluator) 144 | train, val, tst = result 145 | if val > best_val: 146 | best_val = val 147 | final_test = tst 148 | 149 | print(f'Run{run} val:{best_val}, test:{final_test}') 150 | vals.append(best_val) 151 | tests.append(final_test) 152 | 153 | print('') 154 | print(f"Average val accuracy: {np.mean(vals)} ± {np.std(vals)}") 155 | print(f"Average test accuracy: {np.mean(tests)} ± {np.std(tests)}") 156 | 157 | 158 | 159 | if __name__ == "__main__": 160 | main() 161 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbg_mol/args.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import uuid 3 | import logging 4 | import time 5 | import os 6 | import sys 7 | from utils.logger import create_exp_dir 8 | import glob 9 | 10 | 11 | class ArgsInit(object): 12 | def __init__(self): 13 | parser = argparse.ArgumentParser(description='DeeperGCN') 14 | # dataset 15 | parser.add_argument('--dataset', type=str, default="ogbg-molhiv", 16 | help='dataset name (default: ogbg-molhiv)') 17 | parser.add_argument('--num_workers', type=int, default=0, 18 | help='number of workers (default: 0)') 19 | parser.add_argument('--batch_size', type=int, default=32, 20 | help='input batch size for training (default: 32)') 21 | parser.add_argument('--feature', type=str, default='full', 22 | help='two options: full or simple') 23 | parser.add_argument('--add_virtual_node', action='store_true') 24 | # training & eval settings 25 | parser.add_argument('--use_gpu', action='store_true') 26 | parser.add_argument('--device', type=int, default=0, 27 | help='which gpu to use if any (default: 0)') 28 | parser.add_argument('--epochs', type=int, default=300, 29 | help='number of epochs to train (default: 300)') 30 | parser.add_argument('--lr', type=float, default=0.01, 31 | help='learning rate set for optimizer.') 32 | parser.add_argument('--dropout', type=float, default=0.5) 33 | # model 34 | parser.add_argument('--num_layers', type=int, default=3, 35 | help='the number of layers of the networks') 36 | parser.add_argument('--mlp_layers', type=int, default=1, 37 | help='the number of layers of mlp in conv') 38 | parser.add_argument('--hidden_channels', type=int, default=256, 39 | help='the dimension of embeddings of nodes and edges') 40 | parser.add_argument('--block', default='res+', type=str, 41 | help='graph backbone block type {res+, res, dense, plain}') 42 | parser.add_argument('--conv', type=str, default='gen', 43 | help='the type of GCNs') 44 | parser.add_argument('--gcn_aggr', type=str, default='max', 45 | help='the aggregator of GENConv [mean, max, add, softmax, softmax_sg, power]') 46 | parser.add_argument('--norm', type=str, default='batch', 47 | help='the type of normalization layer') 48 | parser.add_argument('--num_tasks', type=int, default=1, 49 | help='the number of prediction tasks') 50 | # learnable parameters 51 | parser.add_argument('--t', type=float, default=1.0, 52 | help='the temperature of SoftMax') 53 | parser.add_argument('--p', type=float, default=1.0, 54 | help='the power of PowerMean') 55 | parser.add_argument('--learn_t', action='store_true') 56 | parser.add_argument('--learn_p', action='store_true') 57 | # message norm 58 | parser.add_argument('--msg_norm', action='store_true') 59 | parser.add_argument('--learn_msg_scale', action='store_true') 60 | # encode edge in conv 61 | parser.add_argument('--conv_encode_edge', action='store_true') 62 | # graph pooling type 63 | parser.add_argument('--graph_pooling', type=str, default='mean', 64 | help='graph pooling method') 65 | # save model 66 | parser.add_argument('--model_save_path', type=str, default='model_ckpt', 67 | help='the directory used to save models') 68 | parser.add_argument('--save', type=str, default='EXP', help='experiment name') 69 | # load pre-trained model 70 | parser.add_argument('--model_load_path', type=str, default='ogbg_molhiv_pretrained_model.pth', 71 | help='the path of pre-trained model') 72 | 73 | #newly added attack hyper-parameters 74 | parser.add_argument('--step-size', type=float, default=1e-2) 75 | parser.add_argument('-m', type=int, default=3) 76 | 77 | self.args = parser.parse_args() 78 | 79 | def save_exp(self): 80 | 81 | self.args.save = '{}-B_{}-C_{}-L_{}-F_{}-DP_{}' \ 82 | '-GA_{}-T_{}-LT_{}-P_{}-LP_{}' \ 83 | '-MN_{}-LS_{}'.format(self.args.save, self.args.block, self.args.conv, 84 | self.args.num_layers, self.args.hidden_channels, 85 | self.args.dropout, self.args.gcn_aggr, 86 | self.args.t, self.args.learn_t, self.args.p, self.args.learn_p, 87 | self.args.msg_norm, self.args.learn_msg_scale) 88 | 89 | self.args.save = 'log/{}-{}-{}'.format(self.args.save, time.strftime("%Y%m%d-%H%M%S"), str(uuid.uuid4())) 90 | self.args.model_save_path = os.path.join(self.args.save, self.args.model_save_path) 91 | create_exp_dir(self.args.save, scripts_to_save=glob.glob('*.py')) 92 | log_format = '%(asctime)s %(message)s' 93 | logging.basicConfig(stream=sys.stdout, 94 | level=logging.INFO, 95 | format=log_format, 96 | datefmt='%m/%d %I:%M:%S %p') 97 | fh = logging.FileHandler(os.path.join(self.args.save, 'log.txt')) 98 | fh.setFormatter(logging.Formatter(log_format)) 99 | logging.getLogger().addHandler(fh) 100 | 101 | return self.args 102 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbg_ppa/args.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import uuid 3 | import logging 4 | import time 5 | import os 6 | import sys 7 | from utils.logger import create_exp_dir 8 | import glob 9 | 10 | 11 | class ArgsInit(object): 12 | def __init__(self): 13 | parser = argparse.ArgumentParser(description='DeeperGCN') 14 | # dataset 15 | parser.add_argument('--dataset', type=str, default="ogbg-ppa", 16 | help='dataset name (default: ogbg-ppa)') 17 | parser.add_argument('--num_workers', type=int, default=0, 18 | help='number of workers (default: 0)') 19 | parser.add_argument('--batch_size', type=int, default=32, 20 | help='input batch size for training (default: 32)') 21 | # extract node features 22 | parser.add_argument('--aggr', type=str, default='add', 23 | help='the aggregation operator to obtain nodes\' initial features [mean, max, add]') 24 | parser.add_argument('--not_extract_node_feature', action='store_true') 25 | # training & eval settings 26 | parser.add_argument('--use_gpu', action='store_true') 27 | parser.add_argument('--device', type=int, default=0, 28 | help='which gpu to use if any (default: 0)') 29 | parser.add_argument('--epochs', type=int, default=200, 30 | help='number of epochs to train (default: 100)') 31 | parser.add_argument('--lr', type=float, default=0.01, 32 | help='learning rate set for optimizer.') 33 | parser.add_argument('--dropout', type=float, default=0.5) 34 | # model 35 | parser.add_argument('--num_layers', type=int, default=3, 36 | help='the number of layers of the networks') 37 | parser.add_argument('--mlp_layers', type=int, default=2, 38 | help='the number of layers of mlp in conv') 39 | parser.add_argument('--hidden_channels', type=int, default=128, 40 | help='the dimension of embeddings of nodes and edges') 41 | parser.add_argument('--block', default='res+', type=str, 42 | help='graph backbone block type {res+, res, dense, plain}') 43 | parser.add_argument('--conv', type=str, default='gen', 44 | help='the type of GCNs') 45 | parser.add_argument('--gcn_aggr', type=str, default='max', 46 | help='the aggregator of GENConv [mean, max, add, softmax, softmax_sg, power]') 47 | parser.add_argument('--norm', type=str, default='layer', 48 | help='the type of normalization layer') 49 | parser.add_argument('--num_tasks', type=int, default=1, 50 | help='the number of prediction tasks') 51 | # learnable parameters 52 | parser.add_argument('--t', type=float, default=1.0, 53 | help='the temperature of SoftMax') 54 | parser.add_argument('--p', type=float, default=1.0, 55 | help='the power of PowerMean') 56 | parser.add_argument('--learn_t', action='store_true') 57 | parser.add_argument('--learn_p', action='store_true') 58 | # message norm 59 | parser.add_argument('--msg_norm', action='store_true') 60 | parser.add_argument('--learn_msg_scale', action='store_true') 61 | # encode edge in conv 62 | parser.add_argument('--conv_encode_edge', action='store_true') 63 | # graph pooling type 64 | parser.add_argument('--graph_pooling', type=str, default='mean', 65 | help='graph pooling method') 66 | # save model 67 | parser.add_argument('--model_save_path', type=str, default='model_ckpt', 68 | help='the directory used to save models') 69 | parser.add_argument('--save', type=str, default='EXP', help='experiment name') 70 | # load pre-trained model 71 | parser.add_argument('--model_load_path', type=str, default='ogbg_ppa_pretrained_model.pth', 72 | help='the path of pre-trained model') 73 | # others, eval steps 74 | parser.add_argument('--eval_steps', type=int, default=5) 75 | parser.add_argument('--num_layers_threshold', type=int, default=14) 76 | 77 | #newly added attack hyper-parameters 78 | parser.add_argument('--step-size', type=float, default=8e-3) 79 | parser.add_argument('-m', type=int, default=3) 80 | 81 | self.args = parser.parse_args() 82 | 83 | def save_exp(self): 84 | 85 | self.args.save = '{}-B_{}-C_{}-L_{}-F_{}-DP_{}' \ 86 | '-A_{}-GA_{}-T_{}-LT_{}-P_{}-LP_{}' \ 87 | '-MN_{}-LS_{}'.format(self.args.save, self.args.block, self.args.conv, 88 | self.args.num_layers, self.args.hidden_channels, self.args.dropout, 89 | self.args.aggr, self.args.gcn_aggr, 90 | self.args.t, self.args.learn_t, self.args.p, self.args.learn_p, 91 | self.args.msg_norm, self.args.learn_msg_scale) 92 | 93 | self.args.save = 'log/{}-{}-{}'.format(self.args.save, time.strftime("%Y%m%d-%H%M%S"), str(uuid.uuid4())) 94 | self.args.model_save_path = os.path.join(self.args.save, self.args.model_save_path) 95 | create_exp_dir(self.args.save, scripts_to_save=glob.glob('*.py')) 96 | log_format = '%(asctime)s %(message)s' 97 | logging.basicConfig(stream=sys.stdout, 98 | level=logging.INFO, 99 | format=log_format, 100 | datefmt='%m/%d %I:%M:%S %p') 101 | fh = logging.FileHandler(os.path.join(self.args.save, 'log.txt')) 102 | fh.setFormatter(logging.Formatter(log_format)) 103 | logging.getLogger().addHandler(fh) 104 | 105 | return self.args 106 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/sem_seg_dense/train.py: -------------------------------------------------------------------------------- 1 | import __init__ 2 | import numpy as np 3 | import torch 4 | import torch_geometric.datasets as GeoData 5 | from torch_geometric.data import DenseDataLoader 6 | import torch_geometric.transforms as T 7 | from torch.nn import DataParallel 8 | from config import OptInit 9 | from architecture import DenseDeepGCN 10 | from utils.ckpt_util import load_pretrained_models, load_pretrained_optimizer, save_checkpoint 11 | from utils.metrics import AverageMeter 12 | import logging 13 | from tqdm import tqdm 14 | 15 | 16 | def main(): 17 | opt = OptInit().get_args() 18 | logging.info('===> Creating dataloader ...') 19 | train_dataset = GeoData.S3DIS(opt.data_dir, opt.area, True, pre_transform=T.NormalizeScale()) 20 | train_loader = DenseDataLoader(train_dataset, batch_size=opt.batch_size, shuffle=True, num_workers=4) 21 | test_dataset = GeoData.S3DIS(opt.data_dir, opt.area, train=False, pre_transform=T.NormalizeScale()) 22 | test_loader = DenseDataLoader(test_dataset, batch_size=opt.batch_size, shuffle=False, num_workers=0) 23 | opt.n_classes = train_loader.dataset.num_classes 24 | 25 | logging.info('===> Loading the network ...') 26 | model = DenseDeepGCN(opt).to(opt.device) 27 | if opt.multi_gpus: 28 | model = DataParallel(DenseDeepGCN(opt)).to(opt.device) 29 | logging.info('===> loading pre-trained ...') 30 | model, opt.best_value, opt.epoch = load_pretrained_models(model, opt.pretrained_model, opt.phase) 31 | logging.info(model) 32 | 33 | logging.info('===> Init the optimizer ...') 34 | criterion = torch.nn.CrossEntropyLoss().to(opt.device) 35 | optimizer = torch.optim.Adam(model.parameters(), lr=opt.lr) 36 | 37 | scheduler = torch.optim.lr_scheduler.StepLR(optimizer, opt.lr_adjust_freq, opt.lr_decay_rate) 38 | optimizer, scheduler, opt.lr = load_pretrained_optimizer(opt.pretrained_model, optimizer, scheduler, opt.lr) 39 | 40 | logging.info('===> Init Metric ...') 41 | opt.losses = AverageMeter() 42 | opt.test_value = 0. 43 | 44 | logging.info('===> start training ...') 45 | for _ in range(opt.epoch, opt.total_epochs): 46 | opt.epoch += 1 47 | logging.info('Epoch:{}'.format(opt.epoch)) 48 | train(model, train_loader, optimizer, scheduler, criterion, opt) 49 | if opt.epoch % opt.eval_freq == 0 and opt.eval_freq != -1: 50 | test(model, test_loader, opt) 51 | scheduler.step() 52 | logging.info('Saving the final model.Finish!') 53 | 54 | 55 | def train(model, train_loader, optimizer, scheduler, criterion, opt): 56 | opt.losses.reset() 57 | model.train() 58 | with tqdm(train_loader) as tqdm_loader: 59 | for i, data in enumerate(tqdm_loader): 60 | opt.iter += 1 61 | 62 | # tqdm progress bar 63 | desc = 'Epoch:{} Iter:{} [{}/{}] Loss:{Losses.avg: .4f}'\ 64 | .format(opt.epoch, opt.iter, i + 1, len(train_loader), Losses=opt.losses) 65 | tqdm_loader.set_description(desc) 66 | 67 | if not opt.multi_gpus: 68 | data = data.to(opt.device) 69 | inputs = torch.cat((data.pos.transpose(2, 1).unsqueeze(3), data.x.transpose(2, 1).unsqueeze(3)), 1) 70 | gt = data.y.to(opt.device) 71 | # ------------------ zero, output, loss 72 | optimizer.zero_grad() 73 | out = model(inputs) 74 | loss = criterion(out, gt) 75 | 76 | # ------------------ optimization 77 | loss.backward() 78 | optimizer.step() 79 | 80 | opt.losses.update(loss.item()) 81 | 82 | # ------------------ tensor board log 83 | info = { 84 | 'loss': loss, 85 | 'test_value': opt.test_value, 86 | 'lr': scheduler.get_lr()[0] 87 | } 88 | for tag, value in info.items(): 89 | opt.logger.scalar_summary(tag, value, opt.iter) 90 | 91 | # ------------------ save checkpoints 92 | # min or max. based on the metrics 93 | is_best = (opt.test_value < opt.best_value) 94 | opt.best_value = max(opt.test_value, opt.best_value) 95 | 96 | model_cpu = {k: v.cpu() for k, v in model.state_dict().items()} 97 | save_checkpoint({ 98 | 'epoch': opt.epoch, 99 | 'state_dict': model_cpu, 100 | 'optimizer_state_dict': optimizer.state_dict(), 101 | 'scheduler_state_dict': scheduler.state_dict(), 102 | 'best_value': opt.best_value, 103 | }, is_best, opt.ckpt_dir, opt.exp_name) 104 | 105 | 106 | def test(model, loader, opt): 107 | Is = np.empty((len(loader), opt.n_classes)) 108 | Us = np.empty((len(loader), opt.n_classes)) 109 | 110 | model.eval() 111 | with torch.no_grad(): 112 | for i, data in enumerate(tqdm(loader)): 113 | if not opt.multi_gpus: 114 | data = data.to(opt.device) 115 | inputs = torch.cat((data.pos.transpose(2, 1).unsqueeze(3), data.x.transpose(2, 1).unsqueeze(3)), 1) 116 | gt = data.y 117 | 118 | out = model(inputs) 119 | pred = out.max(dim=1)[1] 120 | 121 | pred_np = pred.cpu().numpy() 122 | target_np = gt.cpu().numpy() 123 | 124 | for cl in range(opt.n_classes): 125 | cur_gt_mask = (target_np == cl) 126 | cur_pred_mask = (pred_np == cl) 127 | I = np.sum(np.logical_and(cur_pred_mask, cur_gt_mask), dtype=np.float32) 128 | U = np.sum(np.logical_or(cur_pred_mask, cur_gt_mask), dtype=np.float32) 129 | Is[i, cl] = I 130 | Us[i, cl] = U 131 | 132 | ious = np.divide(np.sum(Is, 0), np.sum(Us, 0)) 133 | ious[np.isnan(ious)] = 1 134 | iou = np.mean(ious) 135 | if opt.phase=='test': 136 | for cl in range(opt.n_classes): 137 | logging.info("===> mIOU for class {}: {}".format(cl, ious[cl])) 138 | 139 | opt.test_value = iou 140 | logging.info('TEST Epoch: [{}]\t mIoU: {:.4f}\t'.format(opt.epoch, opt.test_value)) 141 | 142 | 143 | if __name__ == '__main__': 144 | main() 145 | 146 | 147 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbn_proteins/args.py: -------------------------------------------------------------------------------- 1 | import __init__ 2 | import argparse 3 | import uuid 4 | import logging 5 | import time 6 | import os 7 | import sys 8 | from utils.logger import create_exp_dir 9 | import glob 10 | 11 | 12 | class ArgsInit(object): 13 | def __init__(self): 14 | parser = argparse.ArgumentParser(description='DeeperGCN') 15 | # dataset 16 | parser.add_argument('--dataset', type=str, default='ogbn-proteins', 17 | help='dataset name (default: ogbn-proteins)') 18 | parser.add_argument('--cluster_number', type=int, default=10, 19 | help='the number of sub-graphs for training') 20 | parser.add_argument('--valid_cluster_number', type=int, default=5, 21 | help='the number of sub-graphs for evaluation') 22 | parser.add_argument('--aggr', type=str, default='add', 23 | help='the aggregation operator to obtain nodes\' initial features [mean, max, add]') 24 | parser.add_argument('--nf_path', type=str, default='init_node_features_add.pt', 25 | help='the file path of extracted node features saved.') 26 | # training & eval settings 27 | parser.add_argument('--use_gpu', action='store_true') 28 | parser.add_argument('--device', type=int, default=0, 29 | help='which gpu to use if any (default: 0)') 30 | parser.add_argument('--epochs', type=int, default=1000, 31 | help='number of epochs to train (default: 100)') 32 | parser.add_argument('--num_evals', type=int, default=1, 33 | help='The number of evaluation times') 34 | parser.add_argument('--lr', type=float, default=0.01, 35 | help='learning rate set for optimizer.') 36 | parser.add_argument('--dropout', type=float, default=0.0) 37 | # model 38 | parser.add_argument('--num_layers', type=int, default=3, 39 | help='the number of layers of the networks') 40 | parser.add_argument('--mlp_layers', type=int, default=2, 41 | help='the number of layers of mlp in conv') 42 | parser.add_argument('--hidden_channels', type=int, default=64, 43 | help='the dimension of embeddings of nodes and edges') 44 | parser.add_argument('--block', default='plain', type=str, 45 | help='graph backbone block type {res+, res, dense, plain}') 46 | parser.add_argument('--conv', type=str, default='gen', 47 | help='the type of GCNs') 48 | parser.add_argument('--gcn_aggr', type=str, default='max', 49 | help='the aggregator of GENConv [mean, max, add, softmax, softmax_sg, power]') 50 | parser.add_argument('--norm', type=str, default='layer', 51 | help='the type of normalization layer') 52 | parser.add_argument('--num_tasks', type=int, default=1, 53 | help='the number of prediction tasks') 54 | # learnable parameters 55 | parser.add_argument('--t', type=float, default=1.0, 56 | help='the temperature of SoftMax') 57 | parser.add_argument('--p', type=float, default=1.0, 58 | help='the power of PowerMean') 59 | parser.add_argument('--learn_t', action='store_true') 60 | parser.add_argument('--learn_p', action='store_true') 61 | # message norm 62 | parser.add_argument('--msg_norm', action='store_true') 63 | parser.add_argument('--learn_msg_scale', action='store_true') 64 | # encode edge in conv 65 | parser.add_argument('--conv_encode_edge', action='store_true') 66 | # if use one-hot-encoding node feature 67 | parser.add_argument('--use_one_hot_encoding', action='store_true') 68 | # save model 69 | parser.add_argument('--model_save_path', type=str, default='model_ckpt', 70 | help='the directory used to save models') 71 | parser.add_argument('--save', type=str, default='EXP', help='experiment name') 72 | # load pre-trained model 73 | parser.add_argument('--model_load_path', type=str, default='ogbn_proteins_pretrained_model.pth', 74 | help='the path of pre-trained model') 75 | 76 | #newly added attack hyper-parameters 77 | parser.add_argument('--step-size', type=float, default=8e-3) 78 | parser.add_argument('-m', type=int, default=3) 79 | 80 | self.args = parser.parse_args() 81 | 82 | def save_exp(self): 83 | #newly added operations 84 | 85 | self.args.save = '{}-B_{}-C_{}-L_{}-F_{}-DP_{}' \ 86 | '-A_{}-GA_{}-T_{}-LT_{}-P_{}-LP_{}' \ 87 | '-MN_{}-LS_{}'.format(self.args.save, self.args.block, self.args.conv, 88 | self.args.num_layers, self.args.hidden_channels, self.args.dropout, 89 | self.args.aggr, self.args.gcn_aggr, 90 | self.args.t, self.args.learn_t, self.args.p, self.args.learn_p, 91 | self.args.msg_norm, self.args.learn_msg_scale, ) 92 | 93 | self.args.save = 'log/{}-{}-{}'.format(self.args.save, time.strftime("%Y%m%d-%H%M%S"), str(uuid.uuid4())) 94 | self.args.model_save_path = os.path.join(self.args.save, self.args.model_save_path) 95 | create_exp_dir(self.args.save, scripts_to_save=glob.glob('*.py')) 96 | log_format = '%(asctime)s %(message)s' 97 | logging.basicConfig(stream=sys.stdout, 98 | level=logging.INFO, 99 | format=log_format, 100 | datefmt='%m/%d %I:%M:%S %p') 101 | fh = logging.FileHandler(os.path.join(self.args.save, 'log.txt')) 102 | fh.setFormatter(logging.Formatter(log_format)) 103 | logging.getLogger().addHandler(fh) 104 | 105 | return self.args 106 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/modelnet_cls/architecture.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import __init__ 4 | import torch 5 | import torch.nn as nn 6 | import torch.nn.functional as F 7 | from torch.nn import Sequential as Seq 8 | from gcn_lib.dense import BasicConv, GraphConv2d, ResDynBlock2d, DenseDynBlock2d, DilatedKnnGraph, PlainDynBlock2d 9 | 10 | 11 | class DeepGCN(torch.nn.Module): 12 | def __init__(self, opt): 13 | super(DeepGCN, self).__init__() 14 | channels = opt.n_filters 15 | k = opt.k 16 | act = opt.act 17 | norm = opt.norm 18 | bias = opt.bias 19 | knn = 'matrix' # implement knn using matrix multiplication 20 | epsilon = opt.epsilon 21 | stochastic = opt.stochastic 22 | conv = opt.conv 23 | c_growth = channels 24 | emb_dims = opt.emb_dims 25 | self.n_blocks = opt.n_blocks 26 | 27 | self.knn = DilatedKnnGraph(k, 1, stochastic, epsilon) 28 | self.head = GraphConv2d(opt.in_channels, channels, conv, act, norm, bias=False) 29 | 30 | if opt.block.lower() == 'dense': 31 | self.backbone = Seq(*[DenseDynBlock2d(channels+c_growth*i, c_growth, k, 1+i, conv, act, 32 | norm, bias, stochastic, epsilon, knn) 33 | for i in range(self.n_blocks-1)]) 34 | fusion_dims = int( 35 | (channels + channels + c_growth * (self.n_blocks-1)) * self.n_blocks // 2) 36 | 37 | elif opt.block.lower() == 'res': 38 | if opt.use_dilation: 39 | self.backbone = Seq(*[ResDynBlock2d(channels, k, i + 1, conv, act, norm, 40 | bias, stochastic, epsilon, knn) 41 | for i in range(self.n_blocks - 1)]) 42 | else: 43 | self.backbone = Seq(*[ResDynBlock2d(channels, k, 1, conv, act, norm, 44 | bias, stochastic, epsilon, knn) 45 | for _ in range(self.n_blocks - 1)]) 46 | fusion_dims = int(channels + c_growth * (self.n_blocks - 1)) 47 | else: 48 | # Plain GCN. No dilation, no stochastic 49 | stochastic = False 50 | 51 | self.backbone = Seq(*[PlainDynBlock2d(channels, k, 1, conv, act, norm, 52 | bias, stochastic, epsilon, knn) 53 | for i in range(self.n_blocks - 1)]) 54 | 55 | fusion_dims = int(channels+c_growth*(self.n_blocks-1)) 56 | 57 | # fusion_dims = int((channels + channels + c_growth*self.num_backbone_layers)*(self.num_backbone_layers+1)//2) 58 | self.fusion_block = BasicConv([fusion_dims, emb_dims], 'leakyrelu', norm, bias=False) 59 | self.prediction = Seq(*[BasicConv([emb_dims * 2, 512], 'leakyrelu', norm, drop=opt.dropout), 60 | BasicConv([512, 256], 'leakyrelu', norm, drop=opt.dropout), 61 | BasicConv([256, opt.n_classes], None, None)]) 62 | self.model_init() 63 | 64 | def model_init(self): 65 | for m in self.modules(): 66 | if isinstance(m, torch.nn.Conv2d): 67 | torch.nn.init.kaiming_normal_(m.weight) 68 | m.weight.requires_grad = True 69 | if m.bias is not None: 70 | m.bias.data.zero_() 71 | m.bias.requires_grad = True 72 | 73 | def forward(self, inputs): 74 | feats = [self.head(inputs, self.knn(inputs[:, 0:3]))] 75 | for i in range(self.n_blocks-1): 76 | feats.append(self.backbone[i](feats[-1])) 77 | 78 | feats = torch.cat(feats, dim=1) 79 | fusion = self.fusion_block(feats) 80 | x1 = F.adaptive_max_pool2d(fusion, 1) 81 | x2 = F.adaptive_avg_pool2d(fusion, 1) 82 | return self.prediction(torch.cat((x1, x2), dim=1)).squeeze(-1).squeeze(-1) 83 | 84 | 85 | if __name__ == '__main__': 86 | import argparse 87 | parser = argparse.ArgumentParser(description='Point Cloud Segmentation') 88 | # ----------------- Model related 89 | parser.add_argument('--k', default=9, type=int, help='neighbor num (default:9)') 90 | parser.add_argument('--block', default='res', type=str, help='graph backbone block type {res, plain, dense}') 91 | parser.add_argument('--conv', default='edge', type=str, help='graph conv layer {edge, mr}') 92 | parser.add_argument('--act', default='relu', type=str, help='activation layer {relu, prelu, leakyrelu}') 93 | parser.add_argument('--norm', default='batch', type=str, 94 | help='batch or instance normalization {batch, instance}') 95 | parser.add_argument('--bias', default=True, type=bool, help='bias of conv layer True or False') 96 | parser.add_argument('--n_blocks', type=int, default=14, help='number of basic blocks in the backbone') 97 | parser.add_argument('--n_filters', default=64, type=int, help='number of channels of deep features') 98 | parser.add_argument('--in_channels', type=int, default=3, help='Dimension of input ') 99 | parser.add_argument('--n_classes', type=int, default=40, help='Dimension of out_channels ') 100 | parser.add_argument('--emb_dims', type=int, default=1024, metavar='N', help='Dimension of embeddings') 101 | parser.add_argument('--dropout', type=float, default=0.5, help='dropout rate') 102 | # dilated knn 103 | parser.add_argument('--use_dilation', default=True, type=bool, help='use dilated knn or not') 104 | parser.add_argument('--epsilon', default=0.2, type=float, help='stochastic epsilon for gcn') 105 | parser.add_argument('--stochastic', default=True, type=bool, help='stochastic for gcn, True or False') 106 | 107 | args = parser.parse_args() 108 | args.device = torch.device('cuda') 109 | 110 | feats = torch.rand((2, 3, 1024, 1), dtype=torch.float).to(args.device) 111 | num_neighbors = 20 112 | 113 | 114 | print('Input size {}'.format(feats.size())) 115 | net = DeepGCN(args).to(args.device) 116 | out = net(feats) 117 | 118 | print('Output size {}'.format(out.size())) 119 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbn_arxiv/model.py: -------------------------------------------------------------------------------- 1 | import __init__ 2 | import torch 3 | from gcn_lib.sparse.torch_vertex import GENConv 4 | from gcn_lib.sparse.torch_nn import norm_layer 5 | import torch.nn.functional as F 6 | from torch.utils.checkpoint import checkpoint 7 | import logging 8 | 9 | 10 | class DeeperGCN(torch.nn.Module): 11 | def __init__(self, args): 12 | super(DeeperGCN, self).__init__() 13 | 14 | self.num_layers = args.num_layers 15 | self.dropout = args.dropout 16 | self.block = args.block 17 | 18 | self.checkpoint_grad = False 19 | 20 | in_channels = args.in_channels 21 | hidden_channels = args.hidden_channels 22 | num_tasks = args.num_tasks 23 | conv = args.conv 24 | aggr = args.gcn_aggr 25 | 26 | t = args.t 27 | self.learn_t = args.learn_t 28 | p = args.p 29 | self.learn_p = args.learn_p 30 | self.msg_norm = args.msg_norm 31 | learn_msg_scale = args.learn_msg_scale 32 | 33 | norm = args.norm 34 | mlp_layers = args.mlp_layers 35 | 36 | if aggr in ['softmax_sg', 'softmax', 'power'] and self.num_layers > 7: 37 | self.checkpoint_grad = True 38 | self.ckp_k = self.num_layers // 2 39 | 40 | print('The number of layers {}'.format(self.num_layers), 41 | 'Aggregation method {}'.format(aggr), 42 | 'block: {}'.format(self.block)) 43 | 44 | if self.block == 'res+': 45 | print('LN/BN->ReLU->GraphConv->Res') 46 | elif self.block == 'res': 47 | print('GraphConv->LN/BN->ReLU->Res') 48 | elif self.block == 'dense': 49 | raise NotImplementedError('To be implemented') 50 | elif self.block == "plain": 51 | print('GraphConv->LN/BN->ReLU') 52 | else: 53 | raise Exception('Unknown block Type') 54 | 55 | self.gcns = torch.nn.ModuleList() 56 | self.norms = torch.nn.ModuleList() 57 | 58 | self.node_features_encoder = torch.nn.Linear(in_channels, hidden_channels) 59 | self.node_pred_linear = torch.nn.Linear(hidden_channels, num_tasks) 60 | 61 | for layer in range(self.num_layers): 62 | 63 | if conv == 'gen': 64 | gcn = GENConv(hidden_channels, hidden_channels, 65 | aggr=aggr, 66 | t=t, learn_t=self.learn_t, 67 | p=p, learn_p=self.learn_p, 68 | msg_norm=self.msg_norm, learn_msg_scale=learn_msg_scale, 69 | norm=norm, mlp_layers=mlp_layers) 70 | else: 71 | raise Exception('Unknown Conv Type') 72 | 73 | self.gcns.append(gcn) 74 | self.norms.append(norm_layer(norm, hidden_channels)) 75 | 76 | def forward(self, x, edge_index): 77 | 78 | h = self.node_features_encoder(x) 79 | 80 | if self.block == 'res+': 81 | 82 | h = self.gcns[0](h, edge_index) 83 | 84 | if self.checkpoint_grad: 85 | 86 | for layer in range(1, self.num_layers): 87 | h1 = self.norms[layer - 1](h) 88 | h2 = F.relu(h1) 89 | h2 = F.dropout(h2, p=self.dropout, training=self.training) 90 | 91 | if layer % self.ckp_k != 0: 92 | res = checkpoint(self.gcns[layer], h2, edge_index) 93 | h = res + h 94 | else: 95 | h = self.gcns[layer](h2, edge_index) + h 96 | 97 | else: 98 | for layer in range(1, self.num_layers): 99 | h1 = self.norms[layer - 1](h) 100 | h2 = F.relu(h1) 101 | h2 = F.dropout(h2, p=self.dropout, training=self.training) 102 | h = self.gcns[layer](h2, edge_index) + h 103 | 104 | h = F.relu(self.norms[self.num_layers - 1](h)) 105 | h = F.dropout(h, p=self.dropout, training=self.training) 106 | 107 | elif self.block == 'res': 108 | 109 | h = F.relu(self.norms[0](self.gcns[0](h, edge_index))) 110 | h = F.dropout(h, p=self.dropout, training=self.training) 111 | 112 | for layer in range(1, self.num_layers): 113 | h1 = self.gcns[layer](h, edge_index) 114 | h2 = self.norms[layer](h1) 115 | h = F.relu(h2) + h 116 | h = F.dropout(h, p=self.dropout, training=self.training) 117 | 118 | elif self.block == 'dense': 119 | raise NotImplementedError('To be implemented') 120 | 121 | elif self.block == 'plain': 122 | 123 | h = F.relu(self.norms[0](self.gcns[0](h, edge_index))) 124 | h = F.dropout(h, p=self.dropout, training=self.training) 125 | 126 | for layer in range(1, self.num_layers): 127 | h1 = self.gcns[layer](h, edge_index) 128 | h2 = self.norms[layer](h1) 129 | h = F.relu(h2) 130 | h = F.dropout(h, p=self.dropout, training=self.training) 131 | else: 132 | raise Exception('Unknown block Type') 133 | 134 | h = self.node_pred_linear(h) 135 | 136 | return torch.log_softmax(h, dim=-1) 137 | 138 | def print_params(self, epoch=None, final=False): 139 | 140 | if self.learn_t: 141 | ts = [] 142 | for gcn in self.gcns: 143 | ts.append(gcn.t.item()) 144 | if final: 145 | print('Final t {}'.format(ts)) 146 | else: 147 | logging.info('Epoch {}, t {}'.format(epoch, ts)) 148 | if self.learn_p: 149 | ps = [] 150 | for gcn in self.gcns: 151 | ps.append(gcn.p.item()) 152 | if final: 153 | print('Final p {}'.format(ps)) 154 | else: 155 | logging.info('Epoch {}, p {}'.format(epoch, ps)) 156 | if self.msg_norm: 157 | ss = [] 158 | for gcn in self.gcns: 159 | ss.append(gcn.msg_norm.msg_scale.item()) 160 | if final: 161 | print('Final s {}'.format(ss)) 162 | else: 163 | logging.info('Epoch {}, s {}'.format(epoch, ss)) 164 | 165 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbn_products/model.py: -------------------------------------------------------------------------------- 1 | import __init__ 2 | import torch 3 | from gcn_lib.sparse.torch_vertex import GENConv 4 | from gcn_lib.sparse.torch_nn import norm_layer 5 | import torch.nn.functional as F 6 | from torch.utils.checkpoint import checkpoint 7 | import logging 8 | 9 | 10 | class DeeperGCN(torch.nn.Module): 11 | def __init__(self, args): 12 | super(DeeperGCN, self).__init__() 13 | 14 | self.num_layers = args.num_layers 15 | self.dropout = args.dropout 16 | self.block = args.block 17 | 18 | self.checkpoint_grad = False 19 | 20 | in_channels = args.in_channels 21 | hidden_channels = args.hidden_channels 22 | num_tasks = args.num_tasks 23 | conv = args.conv 24 | aggr = args.gcn_aggr 25 | 26 | t = args.t 27 | self.learn_t = args.learn_t 28 | p = args.p 29 | self.learn_p = args.learn_p 30 | self.msg_norm = args.msg_norm 31 | learn_msg_scale = args.learn_msg_scale 32 | 33 | norm = args.norm 34 | mlp_layers = args.mlp_layers 35 | 36 | if aggr in ['softmax_sg', 'softmax', 'power'] and self.num_layers > 3: 37 | self.checkpoint_grad = True 38 | self.ckp_k = self.num_layers // 2 39 | 40 | print('The number of layers {}'.format(self.num_layers), 41 | 'Aggregation method {}'.format(aggr), 42 | 'block: {}'.format(self.block)) 43 | 44 | if self.block == 'res+': 45 | print('LN/BN->ReLU->GraphConv->Res') 46 | elif self.block == 'res': 47 | print('GraphConv->LN/BN->ReLU->Res') 48 | elif self.block == 'dense': 49 | raise NotImplementedError('To be implemented') 50 | elif self.block == "plain": 51 | print('GraphConv->LN/BN->ReLU') 52 | else: 53 | raise Exception('Unknown block Type') 54 | 55 | self.gcns = torch.nn.ModuleList() 56 | self.norms = torch.nn.ModuleList() 57 | 58 | self.node_features_encoder = torch.nn.Linear(in_channels, hidden_channels) 59 | self.node_pred_linear = torch.nn.Linear(hidden_channels, num_tasks) 60 | 61 | for layer in range(self.num_layers): 62 | 63 | if conv == 'gen': 64 | gcn = GENConv(hidden_channels, hidden_channels, 65 | aggr=aggr, 66 | t=t, learn_t=self.learn_t, 67 | p=p, learn_p=self.learn_p, 68 | msg_norm=self.msg_norm, learn_msg_scale=learn_msg_scale, 69 | norm=norm, mlp_layers=mlp_layers) 70 | else: 71 | raise Exception('Unknown Conv Type') 72 | 73 | self.gcns.append(gcn) 74 | self.norms.append(norm_layer(norm, hidden_channels)) 75 | 76 | def forward(self, x, edge_index): 77 | 78 | h = self.node_features_encoder(x) 79 | 80 | if self.block == 'res+': 81 | 82 | h = self.gcns[0](h, edge_index) 83 | 84 | if self.checkpoint_grad: 85 | 86 | for layer in range(1, self.num_layers): 87 | h1 = self.norms[layer - 1](h) 88 | h2 = F.relu(h1) 89 | h2 = F.dropout(h2, p=self.dropout, training=self.training) 90 | 91 | if layer % self.ckp_k != 0: 92 | res = checkpoint(self.gcns[layer], h2, edge_index) 93 | h = res + h 94 | else: 95 | h = self.gcns[layer](h2, edge_index) + h 96 | 97 | else: 98 | for layer in range(1, self.num_layers): 99 | h1 = self.norms[layer - 1](h) 100 | h2 = F.relu(h1) 101 | h2 = F.dropout(h2, p=self.dropout, training=self.training) 102 | h = self.gcns[layer](h2, edge_index) + h 103 | 104 | h = F.relu(self.norms[self.num_layers - 1](h)) 105 | h = F.dropout(h, p=self.dropout, training=self.training) 106 | 107 | elif self.block == 'res': 108 | 109 | h = F.relu(self.norms[0](self.gcns[0](h, edge_index))) 110 | h = F.dropout(h, p=self.dropout, training=self.training) 111 | 112 | for layer in range(1, self.num_layers): 113 | h1 = self.gcns[layer](h, edge_index) 114 | h2 = self.norms[layer](h1) 115 | h = F.relu(h2) + h 116 | h = F.dropout(h, p=self.dropout, training=self.training) 117 | 118 | elif self.block == 'dense': 119 | raise NotImplementedError('To be implemented') 120 | 121 | elif self.block == 'plain': 122 | 123 | h = F.relu(self.norms[0](self.gcns[0](h, edge_index))) 124 | h = F.dropout(h, p=self.dropout, training=self.training) 125 | 126 | for layer in range(1, self.num_layers): 127 | h1 = self.gcns[layer](h, edge_index) 128 | h2 = self.norms[layer](h1) 129 | h = F.relu(h2) 130 | h = F.dropout(h, p=self.dropout, training=self.training) 131 | else: 132 | raise Exception('Unknown block Type') 133 | 134 | h = self.node_pred_linear(h) 135 | 136 | return torch.log_softmax(h, dim=-1) 137 | 138 | def print_params(self, epoch=None, final=False): 139 | 140 | if self.learn_t: 141 | ts = [] 142 | for gcn in self.gcns: 143 | ts.append(gcn.t.item()) 144 | if final: 145 | print('Final t {}'.format(ts)) 146 | else: 147 | logging.info('Epoch {}, t {}'.format(epoch, ts)) 148 | if self.learn_p: 149 | ps = [] 150 | for gcn in self.gcns: 151 | ps.append(gcn.p.item()) 152 | if final: 153 | print('Final p {}'.format(ps)) 154 | else: 155 | logging.info('Epoch {}, p {}'.format(epoch, ps)) 156 | if self.msg_norm: 157 | ss = [] 158 | for gcn in self.gcns: 159 | ss.append(gcn.msg_norm.msg_scale.item()) 160 | if final: 161 | print('Final s {}'.format(ss)) 162 | else: 163 | logging.info('Epoch {}, s {}'.format(epoch, ss)) 164 | 165 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/part_sem_seg/eval.py: -------------------------------------------------------------------------------- 1 | import os 2 | import __init__ 3 | import numpy as np 4 | from tqdm import tqdm 5 | import os.path as osp 6 | import torch 7 | from torch_geometric.data import DenseDataLoader 8 | from config import OptInit 9 | from architecture import DeepGCN 10 | from utils.ckpt_util import load_pretrained_models 11 | from utils.data_util import PartNet 12 | import logging 13 | 14 | g_class2color = { 15 | '0': [255, 0, 0], 16 | '1': [255, 255, 0], 17 | '2': [0, 255, 0], 18 | '3': [0, 255, 255], 19 | '4': [0, 0, 255], 20 | '5': [255, 0, 255], 21 | 22 | '6': [255, 102, 102], 23 | '7': [255, 255, 102], 24 | '8': [102, 255, 102], 25 | '9': [102, 255, 255], 26 | '10': [102, 102, 255], 27 | '11': [255, 102, 255], 28 | 29 | '12': [255, 153, 153], 30 | '13': [255, 255, 153], 31 | '14': [153, 255, 153], 32 | '15': [153, 255, 255], 33 | '16': [153, 153, 255], 34 | '17': [255, 153, 255], 35 | 36 | '18': [153, 0, 0], 37 | '19': [153, 153, 0], 38 | '20': [0, 153, 0], 39 | '21': [0, 153, 153], 40 | '22': [0, 0, 153], 41 | '23': [153, 0, 153], 42 | 43 | 44 | '24': [102, 0, 0], 45 | '25': [102, 102, 0], 46 | '26': [0, 102, 0], 47 | '27': [0, 102, 102], 48 | '28': [0, 0, 102], 49 | '29': [102, 0, 102], 50 | 51 | '30': [255, 178, 102], 52 | '31': [178, 255, 102], 53 | '32': [102, 255, 178], 54 | '33': [102, 178, 255], 55 | '34': [178, 102, 255], 56 | '35': [255, 102, 178], 57 | 58 | '36': [153, 76, 0], 59 | '37': [76, 153, 0], 60 | '38': [0, 153, 76], 61 | '39': [0, 76, 153], 62 | '40': [76, 0, 153], 63 | '41': [153, 0, 76], 64 | 65 | '42': [255, 204, 153], 66 | '43': [204, 255, 153], 67 | '44': [153, 255, 204], 68 | '45': [153, 204, 255], 69 | '46': [204, 153, 255], 70 | '47': [255, 153, 204], 71 | 72 | '48': [255, 153, 51], 73 | '49': [153, 255, 51], 74 | '50': [51, 255, 153], 75 | '51': [51, 153, 255], 76 | '52': [153, 51, 255], 77 | '53': [255, 51, 153]} 78 | 79 | 80 | def test(model, loader, opt): 81 | save_path = opt.res_dir 82 | 83 | part_intersect = np.zeros(opt.n_classes, dtype=np.float32) 84 | part_union = np.zeros(opt.n_classes, dtype=np.float32) 85 | 86 | model.eval() 87 | shape_iou_tot = 0. 88 | shape_iou_cnt = 0. 89 | with torch.no_grad(): 90 | for i, data in enumerate(tqdm(loader)): 91 | # open files for output 92 | fout = open(osp.join(save_path, opt.category + '_' + str(i) + '_pred.obj'), 'w') 93 | fout_gt = open(osp.join(save_path, opt.category + '_' + str(i) + '_gt.obj'), 'w') 94 | 95 | # load data 96 | data = data.to(opt.device) 97 | inputs = data.pos.transpose(2, 1).unsqueeze(3) 98 | gt = data.y 99 | out = model(inputs.detach()) 100 | pred = out.max(dim=1)[1] 101 | 102 | pos = data.pos.transpose(2, 1).squeeze(0).cpu().numpy() 103 | pred_np = pred.cpu().squeeze(0).numpy() 104 | target_np = gt.cpu().squeeze(0).numpy() 105 | 106 | for i in range(len(pred_np)): 107 | cls_pred = str(pred_np[i]) 108 | cls_gt = str(target_np[i]) 109 | color_pred = g_class2color[cls_pred] 110 | color_gt = g_class2color[cls_gt] 111 | 112 | fout.write('v %f %f %f %d %d %d\n' % (pos[0, i], pos[1, i], pos[2, i], color_pred[0], color_pred[1], color_pred[2])) 113 | fout_gt.write('v %f %f %f %d %d %d\n' % (pos[0, i], pos[1, i], pos[2, i], color_gt[0], color_gt[1], color_gt[2])) 114 | 115 | cur_shape_iou_tot = 0.0 116 | cur_shape_iou_cnt = 0 117 | 118 | for cl in range(opt.n_classes): 119 | cur_gt_mask = (target_np == cl) 120 | cur_pred_mask = (pred_np == cl) 121 | 122 | I = np.sum(np.logical_and(cur_pred_mask, cur_gt_mask), dtype=np.float32) 123 | U = np.sum(np.logical_or(cur_pred_mask, cur_gt_mask), dtype=np.float32) 124 | 125 | if U > 0: 126 | part_intersect[cl] += I 127 | part_union[cl] += U 128 | 129 | cur_shape_iou_tot += I/U 130 | cur_shape_iou_cnt += 1. 131 | 132 | if cur_shape_iou_cnt > 0: 133 | cur_shape_miou = cur_shape_iou_tot / cur_shape_iou_cnt 134 | shape_iou_tot += cur_shape_miou 135 | shape_iou_cnt += 1. 136 | 137 | shape_mIoU = shape_iou_tot / shape_iou_cnt 138 | part_iou = np.divide(part_intersect[1:], part_union[1:]) 139 | mean_part_iou = np.mean(part_iou) 140 | logging.info("===> Finish Testing! Category {}-{}, Part mIOU is {:.4f} \n\n\n ".format( 141 | opt.category_no, opt.category, mean_part_iou)) 142 | 143 | 144 | if __name__ == '__main__': 145 | opt = OptInit()._get_args() 146 | logging.info('===> Creating dataloader ...') 147 | test_dataset = PartNet(opt.data_dir, 'sem_seg_h5', opt.category, opt.level, 'test') 148 | test_loader = DenseDataLoader(test_dataset, batch_size=1, shuffle=True, num_workers=1) 149 | opt.n_classes = test_loader.dataset.num_classes 150 | 151 | logging.info('===> Loading the network ...') 152 | model = DeepGCN(opt).to(opt.device) 153 | logging.info('===> loading pre-trained ...') 154 | model, opt.best_value, opt.epoch = load_pretrained_models(model, opt.pretrained_model, opt.phase) 155 | 156 | test(model, test_loader, opt) 157 | 158 | 159 | -------------------------------------------------------------------------------- /deep_gcns_torch/examples/ogb/ogbg_ppa/model.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import __init__ 3 | from gcn_lib.sparse.torch_vertex import GENConv 4 | from gcn_lib.sparse.torch_nn import norm_layer 5 | import torch.nn.functional as F 6 | from torch_geometric.nn import global_add_pool, global_mean_pool, global_max_pool 7 | import logging 8 | 9 | 10 | class DeeperGCN(torch.nn.Module): 11 | def __init__(self, args): 12 | super(DeeperGCN, self).__init__() 13 | 14 | self.num_layers = args.num_layers 15 | self.dropout = args.dropout 16 | self.block = args.block 17 | 18 | hidden_channels = args.hidden_channels 19 | num_tasks = args.num_tasks 20 | conv = args.conv 21 | aggr = args.gcn_aggr 22 | t = args.t 23 | self.learn_t = args.learn_t 24 | p = args.p 25 | self.learn_p = args.learn_p 26 | self.msg_norm = args.msg_norm 27 | learn_msg_scale = args.learn_msg_scale 28 | 29 | conv_encode_edge = args.conv_encode_edge 30 | norm = args.norm 31 | mlp_layers = args.mlp_layers 32 | 33 | graph_pooling = args.graph_pooling 34 | 35 | print('The number of layers {}'.format(self.num_layers), 36 | 'Aggr aggregation method {}'.format(aggr), 37 | 'block: {}'.format(self.block)) 38 | if self.block == 'res+': 39 | print('LN/BN->ReLU->GraphConv->Res') 40 | elif self.block == 'res': 41 | print('GraphConv->LN/BN->ReLU->Res') 42 | elif self.block == 'dense': 43 | raise NotImplementedError('To be implemented') 44 | elif self.block == "plain": 45 | print('GraphConv->LN/BN->ReLU') 46 | else: 47 | raise Exception('Unknown block Type') 48 | 49 | self.gcns = torch.nn.ModuleList() 50 | self.norms = torch.nn.ModuleList() 51 | 52 | for layer in range(self.num_layers): 53 | 54 | if conv == 'gen': 55 | gcn = GENConv(hidden_channels, hidden_channels, 56 | aggr=aggr, 57 | t=t, learn_t=self.learn_t, 58 | p=p, learn_p=self.learn_p, 59 | msg_norm=self.msg_norm, learn_msg_scale=learn_msg_scale, 60 | encode_edge=conv_encode_edge, edge_feat_dim=hidden_channels, 61 | norm=norm, mlp_layers=mlp_layers) 62 | else: 63 | raise Exception('Unknown Conv Type') 64 | self.gcns.append(gcn) 65 | self.norms.append(norm_layer(norm, hidden_channels)) 66 | 67 | self.node_features_encoder = torch.nn.Linear(7, hidden_channels) 68 | 69 | self.edge_encoder = torch.nn.Linear(7, hidden_channels) 70 | 71 | if graph_pooling == "sum": 72 | self.pool = global_add_pool 73 | elif graph_pooling == "mean": 74 | self.pool = global_mean_pool 75 | elif graph_pooling == "max": 76 | self.pool = global_max_pool 77 | else: 78 | raise Exception('Unknown Pool Type') 79 | 80 | self.graph_pred_linear = torch.nn.Linear(hidden_channels, num_tasks) 81 | 82 | def forward(self, input_batch, perturb=None): 83 | 84 | x = input_batch.x 85 | edge_index = input_batch.edge_index 86 | edge_attr = input_batch.edge_attr 87 | batch = input_batch.batch 88 | 89 | h = self.node_features_encoder(x) if perturb is None else self.node_features_encoder(x) + perturb 90 | 91 | edge_emb = self.edge_encoder(edge_attr) 92 | 93 | if self.block == 'res+': 94 | 95 | h = self.gcns[0](h, edge_index, edge_emb) 96 | 97 | for layer in range(1, self.num_layers): 98 | h1 = self.norms[layer - 1](h) 99 | h2 = F.relu(h1) 100 | h2 = F.dropout(h2, p=self.dropout, training=self.training) 101 | h = self.gcns[layer](h2, edge_index, edge_emb) + h 102 | 103 | h = self.norms[self.num_layers - 1](h) 104 | h = F.dropout(h, p=self.dropout, training=self.training) 105 | 106 | elif self.block == 'res': 107 | 108 | h = F.relu(self.norms[0](self.gcns[0](h, edge_index, edge_emb))) 109 | h = F.dropout(h, p=self.dropout, training=self.training) 110 | 111 | for layer in range(1, self.num_layers): 112 | h1 = self.gcns[layer](h, edge_index, edge_emb) 113 | h2 = self.norms[layer](h1) 114 | h = F.relu(h2) + h 115 | h = F.dropout(h, p=self.dropout, training=self.training) 116 | 117 | elif self.block == 'dense': 118 | raise NotImplementedError('To be implemented') 119 | 120 | elif self.block == 'plain': 121 | 122 | h = F.relu(self.norms[0](self.gcns[0](h, edge_index, edge_emb))) 123 | h = F.dropout(h, p=self.dropout, training=self.training) 124 | 125 | for layer in range(1, self.num_layers): 126 | h1 = self.gcns[layer](h, edge_index, edge_emb) 127 | h2 = self.norms[layer](h1) 128 | if layer != (self.num_layers - 1): 129 | h = F.relu(h2) 130 | else: 131 | h = h2 132 | h = F.dropout(h, p=self.dropout, training=self.training) 133 | else: 134 | raise Exception('Unknown block Type') 135 | 136 | h_graph = self.pool(h, batch) 137 | 138 | return self.graph_pred_linear(h_graph) 139 | 140 | def print_params(self, epoch=None, final=False): 141 | if self.learn_t: 142 | ts = [] 143 | for gcn in self.gcns: 144 | ts.append(gcn.t.item()) 145 | if final: 146 | print('Final t {}'.format(ts)) 147 | else: 148 | logging.info('Epoch {}, t {}'.format(epoch, ts)) 149 | if self.learn_p: 150 | ps = [] 151 | for gcn in self.gcns: 152 | ps.append(gcn.p.item()) 153 | if final: 154 | print('Final p {}'.format(ps)) 155 | else: 156 | logging.info('Epoch {}, p {}'.format(epoch, ps)) 157 | if self.msg_norm: 158 | ss = [] 159 | for gcn in self.gcns: 160 | ss.append(gcn.msg_norm.msg_scale.item()) 161 | if final: 162 | print('Final s {}'.format(ss)) 163 | else: 164 | logging.info('Epoch {}, s {}'.format(epoch, ss)) 165 | --------------------------------------------------------------------------------