├── .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 |
--------------------------------------------------------------------------------