diff --git a/examples/GCNII/cora/model.py b/examples/GCNII/cora/model.py new file mode 100644 index 00000000..37ea75dc --- /dev/null +++ b/examples/GCNII/cora/model.py @@ -0,0 +1,104 @@ +import torch.nn as nn +import torch +import math +import numpy as np +import torch.nn.functional as F +from torch.nn.parameter import Parameter + +class GraphConvolution(nn.Module): + + def __init__(self, in_features, out_features, residual=False, variant=False): + super(GraphConvolution, self).__init__() + self.variant = variant + if self.variant: + self.in_features = 2*in_features + else: + self.in_features = in_features + + self.out_features = out_features + self.residual = residual + self.weight = Parameter(torch.FloatTensor(self.in_features,self.out_features)) + self.reset_parameters() + + def reset_parameters(self): + stdv = 1. / math.sqrt(self.out_features) + self.weight.data.uniform_(-stdv, stdv) + + def forward(self, input, adj , h0 , lamda, alpha, l): + theta = math.log(lamda/l+1) + hi = torch.spmm(adj, input) + if self.variant: + support = torch.cat([hi,h0],1) + r = (1-alpha)*hi+alpha*h0 + else: + support = (1-alpha)*hi+alpha*h0 + r = support + output = theta*torch.mm(support, self.weight)+(1-theta)*r + if self.residual: + output = output+input + return output + +class GCNII(nn.Module): + def __init__(self, nfeat, nlayers,nhidden, nclass, dropout, lamda, alpha, variant): + super(GCNII, self).__init__() + self.convs = nn.ModuleList() + for _ in range(nlayers): + self.convs.append(GraphConvolution(nhidden, nhidden,variant=variant)) + self.fcs = nn.ModuleList() + self.fcs.append(nn.Linear(nfeat, nhidden)) + self.fcs.append(nn.Linear(nhidden, nclass)) + self.params1 = list(self.convs.parameters()) + self.params2 = list(self.fcs.parameters()) + self.act_fn = nn.ReLU() + self.dropout = dropout + self.alpha = alpha + self.lamda = lamda + + def forward(self, x, adj): + _layers = [] + x = F.dropout(x, self.dropout, training=self.training) + layer_inner = self.act_fn(self.fcs[0](x)) + _layers.append(layer_inner) + for i,con in enumerate(self.convs): + layer_inner = F.dropout(layer_inner, self.dropout, training=self.training) + layer_inner = self.act_fn(con(layer_inner,adj,_layers[0],self.lamda,self.alpha,i+1)) + layer_inner = F.dropout(layer_inner, self.dropout, training=self.training) + layer_inner = self.fcs[-1](layer_inner) + return F.log_softmax(layer_inner, dim=1) + +class GCNIIppi(nn.Module): + def __init__(self, nfeat, nlayers,nhidden, nclass, dropout, lamda, alpha,variant): + super(GCNIIppi, self).__init__() + self.convs = nn.ModuleList() + for _ in range(nlayers): + self.convs.append(GraphConvolution(nhidden, nhidden,variant=variant,residual=True)) + self.fcs = nn.ModuleList() + self.fcs.append(nn.Linear(nfeat, nhidden)) + self.fcs.append(nn.Linear(nhidden, nclass)) + self.act_fn = nn.ReLU() + self.sig = nn.Sigmoid() + self.dropout = dropout + self.alpha = alpha + self.lamda = lamda + + def forward(self, x, adj): + _layers = [] + x = F.dropout(x, self.dropout, training=self.training) + layer_inner = self.act_fn(self.fcs[0](x)) + _layers.append(layer_inner) + for i,con in enumerate(self.convs): + layer_inner = F.dropout(layer_inner, self.dropout, training=self.training) + layer_inner = self.act_fn(con(layer_inner,adj,_layers[0],self.lamda,self.alpha,i+1)) + layer_inner = F.dropout(layer_inner, self.dropout, training=self.training) + layer_inner = self.sig(self.fcs[-1](layer_inner)) + return layer_inner + + +if __name__ == '__main__': + pass + + + + + + diff --git a/examples/GCNII/cora/pretrained/3b102c7c28a64837a26ee61de518a92e.pt b/examples/GCNII/cora/pretrained/3b102c7c28a64837a26ee61de518a92e.pt new file mode 100644 index 00000000..2ac78ad4 Binary files /dev/null and b/examples/GCNII/cora/pretrained/3b102c7c28a64837a26ee61de518a92e.pt differ diff --git a/examples/GCNII/cora/pretrained/b0968187e71c4c089b2eee3dc297c1aa.pt b/examples/GCNII/cora/pretrained/b0968187e71c4c089b2eee3dc297c1aa.pt new file mode 100644 index 00000000..95f95d46 Binary files /dev/null and b/examples/GCNII/cora/pretrained/b0968187e71c4c089b2eee3dc297c1aa.pt differ diff --git a/examples/GCNII/cora/pretrained/cbc3746630c246b38d1c9e1b61329393.pt b/examples/GCNII/cora/pretrained/cbc3746630c246b38d1c9e1b61329393.pt new file mode 100644 index 00000000..94a0e066 Binary files /dev/null and b/examples/GCNII/cora/pretrained/cbc3746630c246b38d1c9e1b61329393.pt differ diff --git a/examples/GCNII/cora/pretrained/df8b8d75d74a461fa79143e9388c6afb.pt b/examples/GCNII/cora/pretrained/df8b8d75d74a461fa79143e9388c6afb.pt new file mode 100644 index 00000000..7374efb7 Binary files /dev/null and b/examples/GCNII/cora/pretrained/df8b8d75d74a461fa79143e9388c6afb.pt differ diff --git a/examples/GCNII/cora/pretrained/f51a09b1f5e249e49ec24b963928d2ac.pt b/examples/GCNII/cora/pretrained/f51a09b1f5e249e49ec24b963928d2ac.pt new file mode 100644 index 00000000..0b26e6d6 Binary files /dev/null and b/examples/GCNII/cora/pretrained/f51a09b1f5e249e49ec24b963928d2ac.pt differ diff --git a/examples/GCNII/cora/train.py b/examples/GCNII/cora/train.py new file mode 100644 index 00000000..ca694d15 --- /dev/null +++ b/examples/GCNII/cora/train.py @@ -0,0 +1,141 @@ +#GCNII +#paper:Simple and Deep Graph Convolutional Networks +#Arxiv:https://arxiv.org/abs/2007.02133 +#acc:78 +#Runtime:117.3718s(single 6G GPU) +#Usage:python train.py + +from __future__ import division +from __future__ import print_function +import time +import random +import argparse +import numpy as np +import torch +import torch.nn.functional as F +import torch.optim as optim +from utils import * +from model import * +import sys +sys.path.append("../../../rllm/dataloader") +from load_data import load_data +import uuid + +# Training settings +parser = argparse.ArgumentParser() +parser.add_argument('--seed', type=int, default=42, help='Random seed.') +parser.add_argument('--epochs', type=int, default=1500, help='Number of epochs to train.') +parser.add_argument('--lr', type=float, default=0.01, help='learning rate.') +parser.add_argument('--wd1', type=float, default=0.01, help='weight decay (L2 loss on parameters).') +parser.add_argument('--wd2', type=float, default=5e-4, help='weight decay (L2 loss on parameters).') +parser.add_argument('--layer', type=int, default=64, help='Number of layers.') +parser.add_argument('--hidden', type=int, default=64, help='hidden dimensions.') +parser.add_argument('--dropout', type=float, default=0.6, help='Dropout rate (1 - keep probability).') +parser.add_argument('--patience', type=int, default=100, help='Patience') +parser.add_argument('--data', default='cora', help='dateset') +parser.add_argument('--dev', type=int, default=0, help='device id') +parser.add_argument('--alpha', type=float, default=0.05, help='alpha_l') +parser.add_argument('--lamda', type=float, default=0.5, help='lamda.') +parser.add_argument('--variant', action='store_true', default=False, help='GCN* model.') +parser.add_argument('--test', action='store_true', default=False, help='evaluation on test set.') +args = parser.parse_args() +random.seed(args.seed) +np.random.seed(args.seed) +torch.manual_seed(args.seed) +torch.cuda.manual_seed(args.seed) + +# Load data +cudaid = "cuda:"+str(args.dev) +device = torch.device(cudaid) +#adj, features, labels,idx_train,idx_val,idx_test = load_citation(args.data) +data, adj, features, labels, idx_train, idx_val, idx_test = load_data('cora', device=device) +labels = labels.argmax(dim=-1) +features = features.to(device) +adj = adj.to(device) +checkpt_file = 'pretrained/'+uuid.uuid4().hex+'.pt' +print(cudaid,checkpt_file) + +model = GCNII(nfeat=features.shape[1], + nlayers=args.layer, + nhidden=args.hidden, + nclass=int(labels.max()) + 1, + dropout=args.dropout, + lamda = args.lamda, + alpha=args.alpha, + variant=args.variant).to(device) + +optimizer = optim.Adam([ + {'params':model.params1,'weight_decay':args.wd1}, + {'params':model.params2,'weight_decay':args.wd2}, + ],lr=args.lr) + +def train(): + model.train() + optimizer.zero_grad() + output = model(features,adj) + print(output[idx_train].size()) + print(labels[idx_train].size()) + acc_train = accuracy(output[idx_train], labels[idx_train].to(device)) + loss_train = F.nll_loss(output[idx_train], labels[idx_train].to(device)) + loss_train.backward() + optimizer.step() + return loss_train.item(),acc_train.item() + + +def validate(): + model.eval() + with torch.no_grad(): + output = model(features,adj) + loss_val = F.nll_loss(output[idx_val], labels[idx_val].to(device)) + acc_val = accuracy(output[idx_val], labels[idx_val].to(device)) + return loss_val.item(),acc_val.item() + +def test(): + model.load_state_dict(torch.load(checkpt_file)) + model.eval() + with torch.no_grad(): + output = model(features, adj) + loss_test = F.nll_loss(output[idx_test], labels[idx_test].to(device)) + acc_test = accuracy(output[idx_test], labels[idx_test].to(device)) + return loss_test.item(),acc_test.item() + +t_total = time.time() +bad_counter = 0 +best = 999999999 +best_epoch = 0 +acc = 0 +for epoch in range(args.epochs): + loss_tra,acc_tra = train() + loss_val,acc_val = validate() + if(epoch+1)%1 == 0: + print('Epoch:{:04d}'.format(epoch+1), + 'train', + 'loss:{:.3f}'.format(loss_tra), + 'acc:{:.2f}'.format(acc_tra*100), + '| val', + 'loss:{:.3f}'.format(loss_val), + 'acc:{:.2f}'.format(acc_val*100)) + if loss_val < best: + best = loss_val + best_epoch = epoch + acc = acc_val + torch.save(model.state_dict(), checkpt_file) + bad_counter = 0 + else: + bad_counter += 1 + + if bad_counter == args.patience: + break + +if args.test: + acc = test()[1] + +print("Train cost: {:.4f}s".format(time.time() - t_total)) +print('Load {}th epoch'.format(best_epoch)) +print("Test" if args.test else "Val","acc.:{:.1f}".format(acc*100)) + + + + + + diff --git a/examples/GCNII/cora/utils.py b/examples/GCNII/cora/utils.py new file mode 100644 index 00000000..e069be84 --- /dev/null +++ b/examples/GCNII/cora/utils.py @@ -0,0 +1,395 @@ +import numpy as np +import scipy.sparse as sp +import torch +import torch.nn.functional as F +import sys +sys.path.append("../../../rllm") +import pickle as pkl +import networkx as nx +import json +from networkx.readwrite import json_graph +import pdb +sys.setrecursionlimit(99999) + +def accuracy(output, labels): + preds = output.max(1)[1].type_as(labels) + correct = preds.eq(labels).double() + correct = correct.sum() + return correct / len(labels) + +def normalize(mx): + """Row-normalize sparse matrix""" + rowsum = np.array(mx.sum(1)) + rowsum = (rowsum==0)*1+rowsum + r_inv = np.power(rowsum, -1).flatten() + r_inv[np.isinf(r_inv)] = 0. + r_mat_inv = sp.diags(r_inv) + mx = r_mat_inv.dot(mx) + return mx + +def sys_normalized_adjacency(adj): + adj = sp.coo_matrix(adj) + adj = adj + sp.eye(adj.shape[0]) + row_sum = np.array(adj.sum(1)) + row_sum=(row_sum==0)*1+row_sum + d_inv_sqrt = np.power(row_sum, -0.5).flatten() + d_inv_sqrt[np.isinf(d_inv_sqrt)] = 0. + d_mat_inv_sqrt = sp.diags(d_inv_sqrt) + return d_mat_inv_sqrt.dot(adj).dot(d_mat_inv_sqrt).tocoo() + +def sparse_mx_to_torch_sparse_tensor(sparse_mx): + """Convert a scipy sparse matrix to a torch sparse tensor.""" + sparse_mx = sparse_mx.tocoo().astype(np.float32) + indices = torch.from_numpy( + np.vstack((sparse_mx.row, sparse_mx.col)).astype(np.int64)) + values = torch.from_numpy(sparse_mx.data) + shape = torch.Size(sparse_mx.shape) + return torch.sparse.FloatTensor(indices, values, shape) + +def parse_index_file(filename): + """Parse index file.""" + index = [] + for line in open(filename): + index.append(int(line.strip())) + return index + +# adapted from tkipf/gcn +def load_citation(dataset_str="cora"): + """ + Load Citation Networks Datasets. + """ + names = ['x', 'y', 'tx', 'ty', 'allx', 'ally', 'graph'] + objects = [] + for i in range(len(names)): + with open("D:/rllm/rllm/datasets/cora/ind.{}.{}".format(dataset_str.lower(), names[i]), 'rb') as f: + if sys.version_info > (3, 0): + objects.append(pkl.load(f, encoding='latin1')) + else: + objects.append(pkl.load(f)) + + x, y, tx, ty, allx, ally, graph = tuple(objects) + test_idx_reorder = parse_index_file("D:/rllm/rllm/datasets/cora/ind.{}.test.index".format(dataset_str)) + test_idx_range = np.sort(test_idx_reorder) + + if dataset_str == 'citeseer': + # Fix citeseer dataset (there are some isolated nodes in the graph) + # Find isolated nodes, add them as zero-vecs into the right position + test_idx_range_full = range(min(test_idx_reorder), max(test_idx_reorder)+1) + tx_extended = sp.lil_matrix((len(test_idx_range_full), x.shape[1])) + tx_extended[test_idx_range-min(test_idx_range), :] = tx + tx = tx_extended + ty_extended = np.zeros((len(test_idx_range_full), y.shape[1])) + ty_extended[test_idx_range-min(test_idx_range), :] = ty + ty = ty_extended + + features = sp.vstack((allx, tx)).tolil() + features[test_idx_reorder, :] = features[test_idx_range, :] + adj = nx.adjacency_matrix(nx.from_dict_of_lists(graph)) + adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj) + labels = np.vstack((ally, ty)) + labels[test_idx_reorder, :] = labels[test_idx_range, :] + + idx_test = test_idx_range.tolist() + idx_train = range(len(y)) + idx_val = range(len(y), len(y)+500) + + features = normalize(features) + # porting to pytorch + features = torch.FloatTensor(np.array(features.todense())).float() + labels = torch.LongTensor(labels) + labels = torch.max(labels, dim=1)[1] + # adj = sparse_mx_to_torch_sparse_tensor(adj).float() + idx_train = torch.LongTensor(idx_train) + idx_val = torch.LongTensor(idx_val) + idx_test = torch.LongTensor(idx_test) + adj = sys_normalized_adjacency(adj) + adj = sparse_mx_to_torch_sparse_tensor(adj) + return adj, features, labels, idx_train, idx_val, idx_test + + +# adapted from PetarV/GAT +def run_dfs(adj, msk, u, ind, nb_nodes): + if msk[u] == -1: + msk[u] = ind + #for v in range(nb_nodes): + for v in adj[u,:].nonzero()[1]: + #if adj[u,v]== 1: + run_dfs(adj, msk, v, ind, nb_nodes) + +def dfs_split(adj): + # Assume adj is of shape [nb_nodes, nb_nodes] + nb_nodes = adj.shape[0] + ret = np.full(nb_nodes, -1, dtype=np.int32) + + graph_id = 0 + + for i in range(nb_nodes): + if ret[i] == -1: + run_dfs(adj, ret, i, graph_id, nb_nodes) + graph_id += 1 + + return ret + +def test(adj, mapping): + nb_nodes = adj.shape[0] + for i in range(nb_nodes): + #for j in range(nb_nodes): + for j in adj[i, :].nonzero()[1]: + if mapping[i] != mapping[j]: + # if adj[i,j] == 1: + return False + return True + +def find_split(adj, mapping, ds_label): + nb_nodes = adj.shape[0] + dict_splits={} + for i in range(nb_nodes): + #for j in range(nb_nodes): + for j in adj[i, :].nonzero()[1]: + if mapping[i]==0 or mapping[j]==0: + dict_splits[0]=None + elif mapping[i] == mapping[j]: + if ds_label[i]['val'] == ds_label[j]['val'] and ds_label[i]['test'] == ds_label[j]['test']: + + if mapping[i] not in dict_splits.keys(): + if ds_label[i]['val']: + dict_splits[mapping[i]] = 'val' + + elif ds_label[i]['test']: + dict_splits[mapping[i]]='test' + + else: + dict_splits[mapping[i]] = 'train' + + else: + if ds_label[i]['test']: + ind_label='test' + elif ds_label[i]['val']: + ind_label='val' + else: + ind_label='train' + if dict_splits[mapping[i]]!= ind_label: + print ('inconsistent labels within a graph exiting!!!') + return None + else: + print ('label of both nodes different, exiting!!') + return None + return dict_splits + +def load_ppi(): + + print ('Loading G...') + with open('ppi/ppi-G.json') as jsonfile: + g_data = json.load(jsonfile) + # print (len(g_data)) + G = json_graph.node_link_graph(g_data) + + #Extracting adjacency matrix + adj=nx.adjacency_matrix(G) + + prev_key='' + for key, value in g_data.items(): + if prev_key!=key: + # print (key) + prev_key=key + + # print ('Loading id_map...') + with open('ppi/ppi-id_map.json') as jsonfile: + id_map = json.load(jsonfile) + # print (len(id_map)) + + id_map = {int(k):int(v) for k,v in id_map.items()} + for key, value in id_map.items(): + id_map[key]=[value] + # print (len(id_map)) + + print ('Loading features...') + features_=np.load('ppi/ppi-feats.npy') + # print (features_.shape) + + #standarizing features + from sklearn.preprocessing import StandardScaler + + train_ids = np.array([id_map[n] for n in G.nodes() if not G.node[n]['val'] and not G.node[n]['test']]) + train_feats = features_[train_ids[:,0]] + scaler = StandardScaler() + scaler.fit(train_feats) + features_ = scaler.transform(features_) + + features = sp.csr_matrix(features_).tolil() + + + print ('Loading class_map...') + class_map = {} + with open('ppi/ppi-class_map.json') as jsonfile: + class_map = json.load(jsonfile) + # print (len(class_map)) + + #pdb.set_trace() + #Split graph into sub-graphs + # print ('Splitting graph...') + splits=dfs_split(adj) + + #Rearrange sub-graph index and append sub-graphs with 1 or 2 nodes to bigger sub-graphs + # print ('Re-arranging sub-graph IDs...') + list_splits=splits.tolist() + group_inc=1 + + for i in range(np.max(list_splits)+1): + if list_splits.count(i)>=3: + splits[np.array(list_splits) == i] =group_inc + group_inc+=1 + else: + #splits[np.array(list_splits) == i] = 0 + ind_nodes=np.argwhere(np.array(list_splits) == i) + ind_nodes=ind_nodes[:,0].tolist() + split=None + + for ind_node in ind_nodes: + if g_data['nodes'][ind_node]['val']: + if split is None or split=='val': + splits[np.array(list_splits) == i] = 21 + split='val' + else: + raise ValueError('new node is VAL but previously was {}'.format(split)) + elif g_data['nodes'][ind_node]['test']: + if split is None or split=='test': + splits[np.array(list_splits) == i] = 23 + split='test' + else: + raise ValueError('new node is TEST but previously was {}'.format(split)) + else: + if split is None or split == 'train': + splits[np.array(list_splits) == i] = 1 + split='train' + else: + pdb.set_trace() + raise ValueError('new node is TRAIN but previously was {}'.format(split)) + + #counting number of nodes per sub-graph + list_splits=splits.tolist() + nodes_per_graph=[] + for i in range(1,np.max(list_splits) + 1): + nodes_per_graph.append(list_splits.count(i)) + + #Splitting adj matrix into sub-graphs + subgraph_nodes=np.max(nodes_per_graph) + adj_sub=np.empty((len(nodes_per_graph), subgraph_nodes, subgraph_nodes)) + feat_sub = np.empty((len(nodes_per_graph), subgraph_nodes, features.shape[1])) + labels_sub = np.empty((len(nodes_per_graph), subgraph_nodes, 121)) + + for i in range(1, np.max(list_splits) + 1): + #Creating same size sub-graphs + indexes = np.where(splits == i)[0] + subgraph_=adj[indexes,:][:,indexes] + + if subgraph_.shape[0] adj) - adj.multiply(adj.T > adj) + tmp = sys_normalized_adjacency(adj) + train_adj_list.append(sparse_mx_to_torch_sparse_tensor(tmp)) + for i in range(val_adj.shape[0]): + adj = sp.coo_matrix(val_adj[i]) + adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj) + tmp = sys_normalized_adjacency(adj) + val_adj_list.append(sparse_mx_to_torch_sparse_tensor(tmp)) + adj = sp.coo_matrix(test_adj[i]) + adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj) + tmp = sys_normalized_adjacency(adj) + test_adj_list.append(sparse_mx_to_torch_sparse_tensor(tmp)) + + train_feat = torch.FloatTensor(train_feat) + val_feat = torch.FloatTensor(val_feat) + test_feat = torch.FloatTensor(test_feat) + + train_labels = torch.FloatTensor(train_labels) + val_labels = torch.FloatTensor(val_labels) + test_labels = torch.FloatTensor(test_labels) + + tr_msk = torch.LongTensor(tr_msk) + vl_msk = torch.LongTensor(vl_msk) + ts_msk = torch.LongTensor(ts_msk) + + return train_adj_list,val_adj_list,test_adj_list,train_feat,val_feat,test_feat,train_labels,val_labels, test_labels, train_nodes, val_nodes, test_nodes + diff --git a/examples/GCNII/model.py b/examples/GCNII/model.py new file mode 100644 index 00000000..37ea75dc --- /dev/null +++ b/examples/GCNII/model.py @@ -0,0 +1,104 @@ +import torch.nn as nn +import torch +import math +import numpy as np +import torch.nn.functional as F +from torch.nn.parameter import Parameter + +class GraphConvolution(nn.Module): + + def __init__(self, in_features, out_features, residual=False, variant=False): + super(GraphConvolution, self).__init__() + self.variant = variant + if self.variant: + self.in_features = 2*in_features + else: + self.in_features = in_features + + self.out_features = out_features + self.residual = residual + self.weight = Parameter(torch.FloatTensor(self.in_features,self.out_features)) + self.reset_parameters() + + def reset_parameters(self): + stdv = 1. / math.sqrt(self.out_features) + self.weight.data.uniform_(-stdv, stdv) + + def forward(self, input, adj , h0 , lamda, alpha, l): + theta = math.log(lamda/l+1) + hi = torch.spmm(adj, input) + if self.variant: + support = torch.cat([hi,h0],1) + r = (1-alpha)*hi+alpha*h0 + else: + support = (1-alpha)*hi+alpha*h0 + r = support + output = theta*torch.mm(support, self.weight)+(1-theta)*r + if self.residual: + output = output+input + return output + +class GCNII(nn.Module): + def __init__(self, nfeat, nlayers,nhidden, nclass, dropout, lamda, alpha, variant): + super(GCNII, self).__init__() + self.convs = nn.ModuleList() + for _ in range(nlayers): + self.convs.append(GraphConvolution(nhidden, nhidden,variant=variant)) + self.fcs = nn.ModuleList() + self.fcs.append(nn.Linear(nfeat, nhidden)) + self.fcs.append(nn.Linear(nhidden, nclass)) + self.params1 = list(self.convs.parameters()) + self.params2 = list(self.fcs.parameters()) + self.act_fn = nn.ReLU() + self.dropout = dropout + self.alpha = alpha + self.lamda = lamda + + def forward(self, x, adj): + _layers = [] + x = F.dropout(x, self.dropout, training=self.training) + layer_inner = self.act_fn(self.fcs[0](x)) + _layers.append(layer_inner) + for i,con in enumerate(self.convs): + layer_inner = F.dropout(layer_inner, self.dropout, training=self.training) + layer_inner = self.act_fn(con(layer_inner,adj,_layers[0],self.lamda,self.alpha,i+1)) + layer_inner = F.dropout(layer_inner, self.dropout, training=self.training) + layer_inner = self.fcs[-1](layer_inner) + return F.log_softmax(layer_inner, dim=1) + +class GCNIIppi(nn.Module): + def __init__(self, nfeat, nlayers,nhidden, nclass, dropout, lamda, alpha,variant): + super(GCNIIppi, self).__init__() + self.convs = nn.ModuleList() + for _ in range(nlayers): + self.convs.append(GraphConvolution(nhidden, nhidden,variant=variant,residual=True)) + self.fcs = nn.ModuleList() + self.fcs.append(nn.Linear(nfeat, nhidden)) + self.fcs.append(nn.Linear(nhidden, nclass)) + self.act_fn = nn.ReLU() + self.sig = nn.Sigmoid() + self.dropout = dropout + self.alpha = alpha + self.lamda = lamda + + def forward(self, x, adj): + _layers = [] + x = F.dropout(x, self.dropout, training=self.training) + layer_inner = self.act_fn(self.fcs[0](x)) + _layers.append(layer_inner) + for i,con in enumerate(self.convs): + layer_inner = F.dropout(layer_inner, self.dropout, training=self.training) + layer_inner = self.act_fn(con(layer_inner,adj,_layers[0],self.lamda,self.alpha,i+1)) + layer_inner = F.dropout(layer_inner, self.dropout, training=self.training) + layer_inner = self.sig(self.fcs[-1](layer_inner)) + return layer_inner + + +if __name__ == '__main__': + pass + + + + + + diff --git a/examples/GCNII/rel-movielens1m/classification/model.py b/examples/GCNII/rel-movielens1m/classification/model.py new file mode 100644 index 00000000..0dbe65d3 --- /dev/null +++ b/examples/GCNII/rel-movielens1m/classification/model.py @@ -0,0 +1,121 @@ +import torch.nn as nn +import torch +import math +import numpy as np +import torch.nn.functional as F +from torch.nn.parameter import Parameter + +class GraphConvolution(nn.Module): + + def __init__(self, in_features, out_features, residual=False, variant=False): + super(GraphConvolution, self).__init__() + self.variant = variant + if self.variant: + self.in_features = 2*in_features + else: + self.in_features = in_features + + self.out_features = out_features + self.residual = residual + self.weight = Parameter(torch.FloatTensor(self.in_features,self.out_features)) + self.reset_parameters() + + def reset_parameters(self): + #stdv = 1. / (math.sqrt(self.out_features)+1e-8) + stdv = 0 + self.weight.data.uniform_(-stdv, stdv) + + def forward(self, input, adj , h0 , lamda, alpha, l): + theta = math.log(lamda/l+1) + hi = torch.spmm(adj, input) + if self.variant: + support = torch.cat([hi,h0],1) + r = (1-alpha)*hi+alpha*h0 + else: + support = (1-alpha)*hi+alpha*h0 + r = support + output = theta*torch.mm(support, self.weight)+(1-theta)*r + if self.residual: + output = output+input + return output +class BatchNormLayer(nn.Module): + def __init__(self, num_features): + super(BatchNormLayer, self).__init__() + #eps在分母上加一个数防止出现除0 + self.bn = nn.BatchNorm1d(num_features,eps=1e-8).to('cuda') + + def forward(self, x): + return self.bn(x) +class GCNII(nn.Module): + def __init__(self, nfeat, nlayers,nhidden, nclass, dropout, lamda, alpha, variant,train=True): + super(GCNII, self).__init__() + self.convs = nn.ModuleList() + for _ in range(nlayers): + self.convs.append(GraphConvolution(nhidden, nhidden,variant=variant)) + self.fcs = nn.ModuleList() + self.fcs.append(nn.Linear(nfeat, nhidden)) + self.fcs.append(nn.Linear(nhidden, nclass)) + self.params1 = list(self.convs.parameters()) + self.params2 = list(self.fcs.parameters()) + self.act_fn = nn.ReLU() + #self.act_fn = nn.Sigmoid() + self.dropout = dropout + self.alpha = alpha + self.lamda = lamda + self.trainning = train + + def forward(self, x, adj): + #print("1",x) + _layers = [] + x = F.dropout(x, self.dropout, training=self.training) + #print("2",x) + layer_inner = self.act_fn(self.fcs[0](x)) + #print("3",layer_inner) + _layers.append(layer_inner) + for i,con in enumerate(self.convs): + layer_inner = F.dropout(layer_inner, self.dropout, training=self.training) + layer_inner = self.act_fn(con(layer_inner,adj,_layers[0],self.lamda,self.alpha,i+1)) + bn_layer = BatchNormLayer(num_features=layer_inner.size(1)) + layer_inner = bn_layer(layer_inner) + #print(layer_inner) + layer_inner = F.dropout(layer_inner, self.dropout, training=self.training) + layer_inner = self.fcs[-1](layer_inner) + #print(layer_inner) + return F.log_softmax(layer_inner, dim=1) + +class GCNIIppi(nn.Module): + def __init__(self, nfeat, nlayers,nhidden, nclass, dropout, lamda, alpha,variant): + super(GCNIIppi, self).__init__() + self.convs = nn.ModuleList() + for _ in range(nlayers): + self.convs.append(GraphConvolution(nhidden, nhidden,variant=variant,residual=True)) + self.fcs = nn.ModuleList() + self.fcs.append(nn.Linear(nfeat, nhidden)) + self.fcs.append(nn.Linear(nhidden, nclass)) + self.act_fn = nn.ReLU() + self.sig = nn.Sigmoid() + self.dropout = dropout + self.alpha = alpha + self.lamda = lamda + + def forward(self, x, adj): + _layers = [] + x = F.dropout(x, self.dropout, training=self.training) + layer_inner = self.act_fn(self.fcs[0](x)) + _layers.append(layer_inner) + for i,con in enumerate(self.convs): + layer_inner = F.dropout(layer_inner, self.dropout, training=self.training) + layer_inner = self.act_fn(con(layer_inner,adj,_layers[0],self.lamda,self.alpha,i+1)) + layer_inner = F.dropout(layer_inner, self.dropout, training=self.training) + layer_inner = self.sig(self.fcs[-1](layer_inner)) + return layer_inner + + +if __name__ == '__main__': + pass + + + + + + diff --git a/examples/GCNII/rel-movielens1m/classification/train.py b/examples/GCNII/rel-movielens1m/classification/train.py new file mode 100644 index 00000000..c22373c0 --- /dev/null +++ b/examples/GCNII/rel-movielens1m/classification/train.py @@ -0,0 +1,166 @@ +#GCNII +#paper:Simple and Deep Graph Convolutional Networks +#Arxiv:https://arxiv.org/abs/2007.02133 +#loss:0.2783 +#Runtime:198.0222s(single 6G GPU) +#Usage:python train.py + +from __future__ import division +from __future__ import print_function +import time +import random +import argparse +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +from utils import * +from model import * +import sys +sys.path.append("../../../../rllm/dataloader") +from load_data import load_data +# sys.path.append("D:/rllm/examples/gcn/rel-movielens1m/classification") +# from models import * + +import uuid +from sklearn.metrics import f1_score + +t_total = time.time() +# Training settings +parser = argparse.ArgumentParser() +parser.add_argument('--seed', type=int, default=30, help='Random seed.') +parser.add_argument('--no-cuda', action='store_true', default=False,help='Disables CUDA training.') +parser.add_argument('--epochs', type=int, default=1500, help='Number of epochs to train.') +parser.add_argument('--lr', type=float, default=1e-3, help='learning rate.') +parser.add_argument('--wd1', type=float, default=0.01, help='weight decay (L2 loss on parameters).') +parser.add_argument('--wd2', type=float, default=5e-4, help='weight decay (L2 loss on parameters).') +parser.add_argument('--layer', type=int, default=64, help='Number of layers.') +parser.add_argument('--hidden', type=int, default=64, help='hidden dimensions.') +parser.add_argument('--dropout', type=float, default=0.6 , help='Dropout rate (1 - keep probability).') +parser.add_argument('--patience', type=int, default=100, help='Patience') +parser.add_argument('--data', default='cora', help='dateset') +parser.add_argument('--dev', type=int, default=0, help='device id') +parser.add_argument('--alpha', type=float, default=0.05, help='alpha_l') +parser.add_argument('--lamda', type=float, default=0.5, help='lamda.') +parser.add_argument('--variant', action='store_true', default=False, help='GCN* model.') +parser.add_argument('--test', action='store_true', default=False, help='evaluation on test set.') +#args = parser.parse_args() +#random.seed(args.seed) +#np.random.seed(args.seed) +#torch.manual_seed(args.seed) +#torch.cuda.manual_seed(args.seed) +args = parser.parse_args() +args.cuda = not args.no_cuda and torch.cuda.is_available() +device = 'cuda' if args.cuda else 'cpu' + +np.random.seed(args.seed) +torch.manual_seed(args.seed) +if args.cuda: + torch.cuda.manual_seed(args.seed) + +# Load data +cudaid = "cuda:"+str(args.dev) +device = torch.device(cudaid) + +#adj, features, labels,idx_train,idx_val,idx_test = load_citation(args.data) +data, adj, features, labels, idx_train, idx_val, idx_test = load_data('movielens-classification', device=device) +# print(data.x["movie"]) +#labels = labels.argmax(dim=-1) +labels_train = labels.cpu()[idx_train.cpu()] +labels_val = labels.cpu()[idx_val.cpu()] +labels_test = labels.cpu()[idx_test.cpu()] +# torch.savetxt("features.txt",features) +#注释了59 61 +#features = features.to(device) +# print(features) +#adj = adj.to(device) +checkpt_file = 'pretrained/'+uuid.uuid4().hex+'.pt' +# print(cudaid,checkpt_file) +# print(labels.shape[1]) +model = GCNII(nfeat=features.shape[1], + nlayers=args.layer, + nhidden=args.hidden, + # nclass=int(labels.max()) + 1, + nclass=labels.shape[1], + dropout=args.dropout, + lamda = args.lamda, + alpha=args.alpha, + variant=args.variant, + train = True).to(device) + +optimizer = optim.Adam([ + {'params':model.params1,'weight_decay':args.wd1}, + {'params':model.params2,'weight_decay':args.wd2}, + ],lr=args.lr) + + + +loss_func = nn.BCEWithLogitsLoss() + + +def train(epoch): + t = time.time() + model.train() + optimizer.zero_grad() + output = model(features,adj) + #print("output",output) + pred = np.where(output.cpu() > -1.0, 1, 0) + loss_train = loss_func(output[idx_train], labels[idx_train]) + f1_micro_train = f1_score(labels_train, pred[idx_train.cpu()], average="micro") + f1_macro_train = f1_score(labels_train, pred[idx_train.cpu()], average="macro") + loss_train.backward() + optimizer.step() + + loss_val = loss_func(output[idx_val], labels[idx_val]) + f1_micro_val = f1_score(labels_val, pred[idx_val.cpu()], average="micro") + f1_macro_val = f1_score(labels_val, pred[idx_val.cpu()], average="macro") + print('Epoch: {:04d}'.format(epoch+1), + 'loss_train: {:.4f}'.format(loss_train.item()), + 'f1_train: {:.4f} {:.4f}'.format(f1_micro_train, f1_macro_train), + 'loss_val: {:.4f}'.format(loss_val.item()), + 'f1_val: {:.4f} {:.4f}'.format(f1_micro_val, f1_macro_val), + 'time: {:.4f}s'.format(time.time() - t)) + + +def validate(): + model.eval() + with torch.no_grad(): + output = model(features,adj) + loss_val = F.nll_loss(output[idx_val], labels[idx_val].to(device)) + acc_val = accuracy(output[idx_val], labels[idx_val].to(device)) + return loss_val.item(),acc_val.item() + +#def test(): + #model.load_state_dict(torch.load(checkpt_file)) + #model.eval() + #with torch.no_grad(): + #output = model(features, adj) + #loss_test = F.nll_loss(output[idx_test], labels[idx_test].to(device)) + #acc_test = accuracy(output[idx_test], labels[idx_test].to(device)) + #return loss_test.item(),acc_test.item() + +def test(): + model.eval() + output = model(features, adj) + pred = np.where(output.cpu() > -1.0, 1, 0) + loss_test = loss_func(output[idx_test], labels[idx_test]) + f1_micro_test = f1_score(labels_test, pred[idx_test.cpu()], average="micro") + f1_macro_test = f1_score(labels_test, pred[idx_test.cpu()], average="macro") + print("Test set results:", + "loss= {:.4f}".format(loss_test.item()), + "f1_test= {:.4f} {:.4f}".format(f1_micro_test, f1_macro_test)) + +# Train model +for epoch in range(args.epochs): + train(epoch) +print("Optimization Finished!") +print("Total time elapsed: {:.4f}s".format(time.time() - t_total)) + +# Testing +test() + + + + + diff --git a/examples/GCNII/rel-movielens1m/classification/utils.py b/examples/GCNII/rel-movielens1m/classification/utils.py new file mode 100644 index 00000000..e069be84 --- /dev/null +++ b/examples/GCNII/rel-movielens1m/classification/utils.py @@ -0,0 +1,395 @@ +import numpy as np +import scipy.sparse as sp +import torch +import torch.nn.functional as F +import sys +sys.path.append("../../../rllm") +import pickle as pkl +import networkx as nx +import json +from networkx.readwrite import json_graph +import pdb +sys.setrecursionlimit(99999) + +def accuracy(output, labels): + preds = output.max(1)[1].type_as(labels) + correct = preds.eq(labels).double() + correct = correct.sum() + return correct / len(labels) + +def normalize(mx): + """Row-normalize sparse matrix""" + rowsum = np.array(mx.sum(1)) + rowsum = (rowsum==0)*1+rowsum + r_inv = np.power(rowsum, -1).flatten() + r_inv[np.isinf(r_inv)] = 0. + r_mat_inv = sp.diags(r_inv) + mx = r_mat_inv.dot(mx) + return mx + +def sys_normalized_adjacency(adj): + adj = sp.coo_matrix(adj) + adj = adj + sp.eye(adj.shape[0]) + row_sum = np.array(adj.sum(1)) + row_sum=(row_sum==0)*1+row_sum + d_inv_sqrt = np.power(row_sum, -0.5).flatten() + d_inv_sqrt[np.isinf(d_inv_sqrt)] = 0. + d_mat_inv_sqrt = sp.diags(d_inv_sqrt) + return d_mat_inv_sqrt.dot(adj).dot(d_mat_inv_sqrt).tocoo() + +def sparse_mx_to_torch_sparse_tensor(sparse_mx): + """Convert a scipy sparse matrix to a torch sparse tensor.""" + sparse_mx = sparse_mx.tocoo().astype(np.float32) + indices = torch.from_numpy( + np.vstack((sparse_mx.row, sparse_mx.col)).astype(np.int64)) + values = torch.from_numpy(sparse_mx.data) + shape = torch.Size(sparse_mx.shape) + return torch.sparse.FloatTensor(indices, values, shape) + +def parse_index_file(filename): + """Parse index file.""" + index = [] + for line in open(filename): + index.append(int(line.strip())) + return index + +# adapted from tkipf/gcn +def load_citation(dataset_str="cora"): + """ + Load Citation Networks Datasets. + """ + names = ['x', 'y', 'tx', 'ty', 'allx', 'ally', 'graph'] + objects = [] + for i in range(len(names)): + with open("D:/rllm/rllm/datasets/cora/ind.{}.{}".format(dataset_str.lower(), names[i]), 'rb') as f: + if sys.version_info > (3, 0): + objects.append(pkl.load(f, encoding='latin1')) + else: + objects.append(pkl.load(f)) + + x, y, tx, ty, allx, ally, graph = tuple(objects) + test_idx_reorder = parse_index_file("D:/rllm/rllm/datasets/cora/ind.{}.test.index".format(dataset_str)) + test_idx_range = np.sort(test_idx_reorder) + + if dataset_str == 'citeseer': + # Fix citeseer dataset (there are some isolated nodes in the graph) + # Find isolated nodes, add them as zero-vecs into the right position + test_idx_range_full = range(min(test_idx_reorder), max(test_idx_reorder)+1) + tx_extended = sp.lil_matrix((len(test_idx_range_full), x.shape[1])) + tx_extended[test_idx_range-min(test_idx_range), :] = tx + tx = tx_extended + ty_extended = np.zeros((len(test_idx_range_full), y.shape[1])) + ty_extended[test_idx_range-min(test_idx_range), :] = ty + ty = ty_extended + + features = sp.vstack((allx, tx)).tolil() + features[test_idx_reorder, :] = features[test_idx_range, :] + adj = nx.adjacency_matrix(nx.from_dict_of_lists(graph)) + adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj) + labels = np.vstack((ally, ty)) + labels[test_idx_reorder, :] = labels[test_idx_range, :] + + idx_test = test_idx_range.tolist() + idx_train = range(len(y)) + idx_val = range(len(y), len(y)+500) + + features = normalize(features) + # porting to pytorch + features = torch.FloatTensor(np.array(features.todense())).float() + labels = torch.LongTensor(labels) + labels = torch.max(labels, dim=1)[1] + # adj = sparse_mx_to_torch_sparse_tensor(adj).float() + idx_train = torch.LongTensor(idx_train) + idx_val = torch.LongTensor(idx_val) + idx_test = torch.LongTensor(idx_test) + adj = sys_normalized_adjacency(adj) + adj = sparse_mx_to_torch_sparse_tensor(adj) + return adj, features, labels, idx_train, idx_val, idx_test + + +# adapted from PetarV/GAT +def run_dfs(adj, msk, u, ind, nb_nodes): + if msk[u] == -1: + msk[u] = ind + #for v in range(nb_nodes): + for v in adj[u,:].nonzero()[1]: + #if adj[u,v]== 1: + run_dfs(adj, msk, v, ind, nb_nodes) + +def dfs_split(adj): + # Assume adj is of shape [nb_nodes, nb_nodes] + nb_nodes = adj.shape[0] + ret = np.full(nb_nodes, -1, dtype=np.int32) + + graph_id = 0 + + for i in range(nb_nodes): + if ret[i] == -1: + run_dfs(adj, ret, i, graph_id, nb_nodes) + graph_id += 1 + + return ret + +def test(adj, mapping): + nb_nodes = adj.shape[0] + for i in range(nb_nodes): + #for j in range(nb_nodes): + for j in adj[i, :].nonzero()[1]: + if mapping[i] != mapping[j]: + # if adj[i,j] == 1: + return False + return True + +def find_split(adj, mapping, ds_label): + nb_nodes = adj.shape[0] + dict_splits={} + for i in range(nb_nodes): + #for j in range(nb_nodes): + for j in adj[i, :].nonzero()[1]: + if mapping[i]==0 or mapping[j]==0: + dict_splits[0]=None + elif mapping[i] == mapping[j]: + if ds_label[i]['val'] == ds_label[j]['val'] and ds_label[i]['test'] == ds_label[j]['test']: + + if mapping[i] not in dict_splits.keys(): + if ds_label[i]['val']: + dict_splits[mapping[i]] = 'val' + + elif ds_label[i]['test']: + dict_splits[mapping[i]]='test' + + else: + dict_splits[mapping[i]] = 'train' + + else: + if ds_label[i]['test']: + ind_label='test' + elif ds_label[i]['val']: + ind_label='val' + else: + ind_label='train' + if dict_splits[mapping[i]]!= ind_label: + print ('inconsistent labels within a graph exiting!!!') + return None + else: + print ('label of both nodes different, exiting!!') + return None + return dict_splits + +def load_ppi(): + + print ('Loading G...') + with open('ppi/ppi-G.json') as jsonfile: + g_data = json.load(jsonfile) + # print (len(g_data)) + G = json_graph.node_link_graph(g_data) + + #Extracting adjacency matrix + adj=nx.adjacency_matrix(G) + + prev_key='' + for key, value in g_data.items(): + if prev_key!=key: + # print (key) + prev_key=key + + # print ('Loading id_map...') + with open('ppi/ppi-id_map.json') as jsonfile: + id_map = json.load(jsonfile) + # print (len(id_map)) + + id_map = {int(k):int(v) for k,v in id_map.items()} + for key, value in id_map.items(): + id_map[key]=[value] + # print (len(id_map)) + + print ('Loading features...') + features_=np.load('ppi/ppi-feats.npy') + # print (features_.shape) + + #standarizing features + from sklearn.preprocessing import StandardScaler + + train_ids = np.array([id_map[n] for n in G.nodes() if not G.node[n]['val'] and not G.node[n]['test']]) + train_feats = features_[train_ids[:,0]] + scaler = StandardScaler() + scaler.fit(train_feats) + features_ = scaler.transform(features_) + + features = sp.csr_matrix(features_).tolil() + + + print ('Loading class_map...') + class_map = {} + with open('ppi/ppi-class_map.json') as jsonfile: + class_map = json.load(jsonfile) + # print (len(class_map)) + + #pdb.set_trace() + #Split graph into sub-graphs + # print ('Splitting graph...') + splits=dfs_split(adj) + + #Rearrange sub-graph index and append sub-graphs with 1 or 2 nodes to bigger sub-graphs + # print ('Re-arranging sub-graph IDs...') + list_splits=splits.tolist() + group_inc=1 + + for i in range(np.max(list_splits)+1): + if list_splits.count(i)>=3: + splits[np.array(list_splits) == i] =group_inc + group_inc+=1 + else: + #splits[np.array(list_splits) == i] = 0 + ind_nodes=np.argwhere(np.array(list_splits) == i) + ind_nodes=ind_nodes[:,0].tolist() + split=None + + for ind_node in ind_nodes: + if g_data['nodes'][ind_node]['val']: + if split is None or split=='val': + splits[np.array(list_splits) == i] = 21 + split='val' + else: + raise ValueError('new node is VAL but previously was {}'.format(split)) + elif g_data['nodes'][ind_node]['test']: + if split is None or split=='test': + splits[np.array(list_splits) == i] = 23 + split='test' + else: + raise ValueError('new node is TEST but previously was {}'.format(split)) + else: + if split is None or split == 'train': + splits[np.array(list_splits) == i] = 1 + split='train' + else: + pdb.set_trace() + raise ValueError('new node is TRAIN but previously was {}'.format(split)) + + #counting number of nodes per sub-graph + list_splits=splits.tolist() + nodes_per_graph=[] + for i in range(1,np.max(list_splits) + 1): + nodes_per_graph.append(list_splits.count(i)) + + #Splitting adj matrix into sub-graphs + subgraph_nodes=np.max(nodes_per_graph) + adj_sub=np.empty((len(nodes_per_graph), subgraph_nodes, subgraph_nodes)) + feat_sub = np.empty((len(nodes_per_graph), subgraph_nodes, features.shape[1])) + labels_sub = np.empty((len(nodes_per_graph), subgraph_nodes, 121)) + + for i in range(1, np.max(list_splits) + 1): + #Creating same size sub-graphs + indexes = np.where(splits == i)[0] + subgraph_=adj[indexes,:][:,indexes] + + if subgraph_.shape[0] adj) - adj.multiply(adj.T > adj) + tmp = sys_normalized_adjacency(adj) + train_adj_list.append(sparse_mx_to_torch_sparse_tensor(tmp)) + for i in range(val_adj.shape[0]): + adj = sp.coo_matrix(val_adj[i]) + adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj) + tmp = sys_normalized_adjacency(adj) + val_adj_list.append(sparse_mx_to_torch_sparse_tensor(tmp)) + adj = sp.coo_matrix(test_adj[i]) + adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj) + tmp = sys_normalized_adjacency(adj) + test_adj_list.append(sparse_mx_to_torch_sparse_tensor(tmp)) + + train_feat = torch.FloatTensor(train_feat) + val_feat = torch.FloatTensor(val_feat) + test_feat = torch.FloatTensor(test_feat) + + train_labels = torch.FloatTensor(train_labels) + val_labels = torch.FloatTensor(val_labels) + test_labels = torch.FloatTensor(test_labels) + + tr_msk = torch.LongTensor(tr_msk) + vl_msk = torch.LongTensor(vl_msk) + ts_msk = torch.LongTensor(ts_msk) + + return train_adj_list,val_adj_list,test_adj_list,train_feat,val_feat,test_feat,train_labels,val_labels, test_labels, train_nodes, val_nodes, test_nodes + diff --git a/examples/GCNII/rel-movielens1m/regression/model.py b/examples/GCNII/rel-movielens1m/regression/model.py new file mode 100644 index 00000000..ac249ebe --- /dev/null +++ b/examples/GCNII/rel-movielens1m/regression/model.py @@ -0,0 +1,149 @@ +import torch.nn as nn +import torch +import math +import numpy as np +import torch.nn.functional as F +from torch.nn.parameter import Parameter + +class GraphConvolution(nn.Module): + + def __init__(self, in_features, out_features, residual=False, variant=False): + super(GraphConvolution, self).__init__() + self.variant = variant + if self.variant: + self.in_features = 2*in_features + else: + self.in_features = in_features + + self.out_features = out_features + self.residual = residual + self.weight = Parameter(torch.FloatTensor(self.in_features,self.out_features)) + self.reset_parameters() + + def reset_parameters(self): + #stdv = 1. / (math.sqrt(self.out_features)+1e-8) + stdv = 0 + self.weight.data.uniform_(-stdv, stdv) + + def forward(self, input, adj , h0 , lamda, alpha, l): + theta = math.log(lamda/l+1) + hi = torch.spmm(adj, input) + if self.variant: + support = torch.cat([hi,h0],1) + r = (1-alpha)*hi+alpha*h0 + else: + support = (1-alpha)*hi+alpha*h0 + r = support + output = theta*torch.mm(support, self.weight)+(1-theta)*r + if self.residual: + output = output+input + return output +class BatchNormLayer(nn.Module): + def __init__(self, num_features): + super(BatchNormLayer, self).__init__() + #eps在分母上加一个数防止出现除0 + self.bn = nn.BatchNorm1d(num_features,eps=1e-8).to('cuda') + + def forward(self, x): + return self.bn(x) +class GCNII(nn.Module): + def __init__(self, nfeat, nlayers,nhidden, nclass, dropout, lamda, alpha, variant,train=True): + super(GCNII, self).__init__() + self.convs = nn.ModuleList() + for _ in range(nlayers): + self.convs.append(GraphConvolution(nhidden, nhidden,variant=variant)) + self.fcs = nn.ModuleList() + self.fcs.append(nn.Linear(nfeat, nhidden)) + self.fcs.append(nn.Linear(nhidden, nclass)) + self.params1 = list(self.convs.parameters()) + self.params2 = list(self.fcs.parameters()) + self.act_fn = nn.ReLU() + #self.act_fn = nn.Sigmoid() + self.dropout = dropout + self.alpha = alpha + self.lamda = lamda + self.trainning = train + + def forward(self, x, adj): + #print("1",x) + _layers = [] + x = F.dropout(x, self.dropout, training=self.training) + #print("2",x) + layer_inner = self.act_fn(self.fcs[0](x)) + #print("3",layer_inner) + _layers.append(layer_inner) + for i,con in enumerate(self.convs): + layer_inner = F.dropout(layer_inner, self.dropout, training=self.training) + layer_inner = self.act_fn(con(layer_inner,adj,_layers[0],self.lamda,self.alpha,i+1)) + bn_layer = BatchNormLayer(num_features=layer_inner.size(1)) + layer_inner = bn_layer(layer_inner) + #print(layer_inner) + layer_inner = F.dropout(layer_inner, self.dropout, training=self.training) + layer_inner = self.fcs[-1](layer_inner) + #print(layer_inner) + return F.log_softmax(layer_inner, dim=1) + +class Decoder(nn.Module): + def __init__(self, nhid): + super(Decoder, self).__init__() + self.lin1 = nn.Linear(2 * nhid, nhid) + self.lin2 = nn.Linear(nhid, 1) + + def forward(self, z, adj): + row, col = adj.indices()[0], adj.indices()[1] + # print(z[col[:2]]) + # print(z[4000:4002]) + # z = (z[row] * z[col]).sum(dim=1) + + z = torch.cat([z[row], z[col]], dim=-1) + z = self.lin1(z).relu() + z = self.lin2(z) + return z.view(-1) + + +class Model(nn.Module): + def __init__(self, nfeat, nlayers,nhidden, nclass, dropout, lamda, alpha, variant,train=True): + super(Model, self).__init__() + self.encoder = GCNII(self, nfeat, nlayers,nhidden, nclass, dropout, lamda, alpha, variant) + self.decoder = Decoder(nhidden) + + def forward(self, x_all, adj, adj_drop): + z_all = self.encoder(x_all, adj_drop) + return self.decoder(z_all, adj) + +class GCNIIppi(nn.Module): + def __init__(self, nfeat, nlayers,nhidden, nclass, dropout, lamda, alpha,variant): + super(GCNIIppi, self).__init__() + self.convs = nn.ModuleList() + for _ in range(nlayers): + self.convs.append(GraphConvolution(nhidden, nhidden,variant=variant,residual=True)) + self.fcs = nn.ModuleList() + self.fcs.append(nn.Linear(nfeat, nhidden)) + self.fcs.append(nn.Linear(nhidden, nclass)) + self.act_fn = nn.ReLU() + self.sig = nn.Sigmoid() + self.dropout = dropout + self.alpha = alpha + self.lamda = lamda + + def forward(self, x, adj): + _layers = [] + x = F.dropout(x, self.dropout, training=self.training) + layer_inner = self.act_fn(self.fcs[0](x)) + _layers.append(layer_inner) + for i,con in enumerate(self.convs): + layer_inner = F.dropout(layer_inner, self.dropout, training=self.training) + layer_inner = self.act_fn(con(layer_inner,adj,_layers[0],self.lamda,self.alpha,i+1)) + layer_inner = F.dropout(layer_inner, self.dropout, training=self.training) + layer_inner = self.sig(self.fcs[-1](layer_inner)) + return layer_inner + + +if __name__ == '__main__': + pass + + + + + + diff --git a/examples/GCNII/rel-movielens1m/regression/train.py b/examples/GCNII/rel-movielens1m/regression/train.py new file mode 100644 index 00000000..878ffaa0 --- /dev/null +++ b/examples/GCNII/rel-movielens1m/regression/train.py @@ -0,0 +1,138 @@ +#GCNII +#paper:Simple and Deep Graph Convolutional Networks +#Arxiv:https://arxiv.org/abs/2007.02133 +#loss:0.2783 +#Runtime:161.5396s(single 6G GPU) +#Usage:python train.py + +from __future__ import division +from __future__ import print_function +import time +import random +import argparse +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +from utils import * +from model import * +import sys +sys.path.append("../../../../rllm/dataloader") +from load_data import load_data +# sys.path.append("D:/rllm/examples/gcn/rel-movielens1m/classification") +# from models import * + +import uuid +from sklearn.metrics import f1_score + +t_total = time.time() +# Training settings +parser = argparse.ArgumentParser() +parser.add_argument('--seed', type=int, default=30, help='Random seed.') +parser.add_argument('--no-cuda', action='store_true', default=False,help='Disables CUDA training.') +parser.add_argument('--epochs', type=int, default=1500, help='Number of epochs to train.') +parser.add_argument('--lr', type=float, default=1e-3, help='learning rate.') +parser.add_argument('--wd1', type=float, default=0.01, help='weight decay (L2 loss on parameters).') +parser.add_argument('--wd2', type=float, default=5e-4, help='weight decay (L2 loss on parameters).') +parser.add_argument('--layer', type=int, default=64, help='Number of layers.') +parser.add_argument('--hidden', type=int, default=64, help='hidden dimensions.') +parser.add_argument('--dropout', type=float, default=0.6 , help='Dropout rate (1 - keep probability).') +parser.add_argument('--patience', type=int, default=100, help='Patience') +parser.add_argument('--data', default='cora', help='dateset') +parser.add_argument('--dev', type=int, default=0, help='device id') +parser.add_argument('--alpha', type=float, default=0.05, help='alpha_l') +parser.add_argument('--lamda', type=float, default=0.5, help='lamda.') +parser.add_argument('--variant', action='store_true', default=False, help='GCN* model.') +parser.add_argument('--test', action='store_true', default=False, help='evaluation on test set.') +#args = parser.parse_args() +#random.seed(args.seed) +#np.random.seed(args.seed) +#torch.manual_seed(args.seed) +#torch.cuda.manual_seed(args.seed) +args = parser.parse_args() +args.cuda = not args.no_cuda and torch.cuda.is_available() +device = 'cuda' if args.cuda else 'cpu' + +np.random.seed(args.seed) +torch.manual_seed(args.seed) +if args.cuda: + torch.cuda.manual_seed(args.seed) + +# Load data +cudaid = "cuda:"+str(args.dev) +device = torch.device(cudaid) + +#adj, features, labels,idx_train,idx_val,idx_test = load_citation(args.data) +data, adj, features, labels, idx_train, idx_val, idx_test = load_data('movielens-classification', device=device) +from random import random +adj_dense = adj.to_dense() +adj_drop = torch.zeros_like(adj_dense).to(device) +adj_drop = adj_drop.to_sparse().coalesce() + +# print(data.x["movie"]) +#labels = labels.argmax(dim=-1) +labels_train = labels.cpu()[idx_train.cpu()] +labels_val = labels.cpu()[idx_val.cpu()] +labels_test = labels.cpu()[idx_test.cpu()] +# torch.savetxt("features.txt",features) +#注释了59 61 +#features = features.to(device) +# print(features) +#adj = adj.to(device) +checkpt_file = 'pretrained/'+uuid.uuid4().hex+'.pt' +# print(cudaid,checkpt_file) +# print(labels.shape[1]) +model = GCNII(nfeat=features.shape[1], + nlayers=args.layer, + nhidden=args.hidden, + # nclass=int(labels.max()) + 1, + nclass=labels.shape[1], + dropout=args.dropout, + lamda = args.lamda, + alpha=args.alpha, + variant=args.variant, + train = True).to(device) + +optimizer = optim.Adam([ + {'params':model.params1,'weight_decay':args.wd1}, + {'params':model.params2,'weight_decay':args.wd2}, + ],lr=args.lr) + + +loss_func = nn.BCEWithLogitsLoss() + +def train(epoch): + t = time.time() + model.train() + optimizer.zero_grad() + output = model(features, adj) + # print(output[[0, 1, 2, 3, 4, 5]]) + loss_train = loss_func(output[idx_train], labels[idx_train]) + loss_train.backward() + optimizer.step() + + loss_val = loss_func(output[idx_val], labels[idx_val]) + print('Epoch: {:04d}'.format(epoch+1), + 'loss_train: {:.4f}'.format(loss_train.item()), + 'loss_val: {:.4f}'.format(loss_val.item()), + 'time: {:.4f}s'.format(time.time() - t)) + + +def test(): + model.eval() + output = model(features, adj) + loss_test = loss_func(output[idx_test], labels[idx_test]) + print("Test set results:", + "loss= {:.4f}".format(loss_test.item())) + + +# Train model +t_total = time.time() +for epoch in range(args.epochs): + train(epoch) +print("Optimization Finished!") +print("Total time elapsed: {:.4f}s".format(time.time() - t_total)) + +# Testing +test() diff --git a/examples/GCNII/rel-movielens1m/regression/utils.py b/examples/GCNII/rel-movielens1m/regression/utils.py new file mode 100644 index 00000000..e069be84 --- /dev/null +++ b/examples/GCNII/rel-movielens1m/regression/utils.py @@ -0,0 +1,395 @@ +import numpy as np +import scipy.sparse as sp +import torch +import torch.nn.functional as F +import sys +sys.path.append("../../../rllm") +import pickle as pkl +import networkx as nx +import json +from networkx.readwrite import json_graph +import pdb +sys.setrecursionlimit(99999) + +def accuracy(output, labels): + preds = output.max(1)[1].type_as(labels) + correct = preds.eq(labels).double() + correct = correct.sum() + return correct / len(labels) + +def normalize(mx): + """Row-normalize sparse matrix""" + rowsum = np.array(mx.sum(1)) + rowsum = (rowsum==0)*1+rowsum + r_inv = np.power(rowsum, -1).flatten() + r_inv[np.isinf(r_inv)] = 0. + r_mat_inv = sp.diags(r_inv) + mx = r_mat_inv.dot(mx) + return mx + +def sys_normalized_adjacency(adj): + adj = sp.coo_matrix(adj) + adj = adj + sp.eye(adj.shape[0]) + row_sum = np.array(adj.sum(1)) + row_sum=(row_sum==0)*1+row_sum + d_inv_sqrt = np.power(row_sum, -0.5).flatten() + d_inv_sqrt[np.isinf(d_inv_sqrt)] = 0. + d_mat_inv_sqrt = sp.diags(d_inv_sqrt) + return d_mat_inv_sqrt.dot(adj).dot(d_mat_inv_sqrt).tocoo() + +def sparse_mx_to_torch_sparse_tensor(sparse_mx): + """Convert a scipy sparse matrix to a torch sparse tensor.""" + sparse_mx = sparse_mx.tocoo().astype(np.float32) + indices = torch.from_numpy( + np.vstack((sparse_mx.row, sparse_mx.col)).astype(np.int64)) + values = torch.from_numpy(sparse_mx.data) + shape = torch.Size(sparse_mx.shape) + return torch.sparse.FloatTensor(indices, values, shape) + +def parse_index_file(filename): + """Parse index file.""" + index = [] + for line in open(filename): + index.append(int(line.strip())) + return index + +# adapted from tkipf/gcn +def load_citation(dataset_str="cora"): + """ + Load Citation Networks Datasets. + """ + names = ['x', 'y', 'tx', 'ty', 'allx', 'ally', 'graph'] + objects = [] + for i in range(len(names)): + with open("D:/rllm/rllm/datasets/cora/ind.{}.{}".format(dataset_str.lower(), names[i]), 'rb') as f: + if sys.version_info > (3, 0): + objects.append(pkl.load(f, encoding='latin1')) + else: + objects.append(pkl.load(f)) + + x, y, tx, ty, allx, ally, graph = tuple(objects) + test_idx_reorder = parse_index_file("D:/rllm/rllm/datasets/cora/ind.{}.test.index".format(dataset_str)) + test_idx_range = np.sort(test_idx_reorder) + + if dataset_str == 'citeseer': + # Fix citeseer dataset (there are some isolated nodes in the graph) + # Find isolated nodes, add them as zero-vecs into the right position + test_idx_range_full = range(min(test_idx_reorder), max(test_idx_reorder)+1) + tx_extended = sp.lil_matrix((len(test_idx_range_full), x.shape[1])) + tx_extended[test_idx_range-min(test_idx_range), :] = tx + tx = tx_extended + ty_extended = np.zeros((len(test_idx_range_full), y.shape[1])) + ty_extended[test_idx_range-min(test_idx_range), :] = ty + ty = ty_extended + + features = sp.vstack((allx, tx)).tolil() + features[test_idx_reorder, :] = features[test_idx_range, :] + adj = nx.adjacency_matrix(nx.from_dict_of_lists(graph)) + adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj) + labels = np.vstack((ally, ty)) + labels[test_idx_reorder, :] = labels[test_idx_range, :] + + idx_test = test_idx_range.tolist() + idx_train = range(len(y)) + idx_val = range(len(y), len(y)+500) + + features = normalize(features) + # porting to pytorch + features = torch.FloatTensor(np.array(features.todense())).float() + labels = torch.LongTensor(labels) + labels = torch.max(labels, dim=1)[1] + # adj = sparse_mx_to_torch_sparse_tensor(adj).float() + idx_train = torch.LongTensor(idx_train) + idx_val = torch.LongTensor(idx_val) + idx_test = torch.LongTensor(idx_test) + adj = sys_normalized_adjacency(adj) + adj = sparse_mx_to_torch_sparse_tensor(adj) + return adj, features, labels, idx_train, idx_val, idx_test + + +# adapted from PetarV/GAT +def run_dfs(adj, msk, u, ind, nb_nodes): + if msk[u] == -1: + msk[u] = ind + #for v in range(nb_nodes): + for v in adj[u,:].nonzero()[1]: + #if adj[u,v]== 1: + run_dfs(adj, msk, v, ind, nb_nodes) + +def dfs_split(adj): + # Assume adj is of shape [nb_nodes, nb_nodes] + nb_nodes = adj.shape[0] + ret = np.full(nb_nodes, -1, dtype=np.int32) + + graph_id = 0 + + for i in range(nb_nodes): + if ret[i] == -1: + run_dfs(adj, ret, i, graph_id, nb_nodes) + graph_id += 1 + + return ret + +def test(adj, mapping): + nb_nodes = adj.shape[0] + for i in range(nb_nodes): + #for j in range(nb_nodes): + for j in adj[i, :].nonzero()[1]: + if mapping[i] != mapping[j]: + # if adj[i,j] == 1: + return False + return True + +def find_split(adj, mapping, ds_label): + nb_nodes = adj.shape[0] + dict_splits={} + for i in range(nb_nodes): + #for j in range(nb_nodes): + for j in adj[i, :].nonzero()[1]: + if mapping[i]==0 or mapping[j]==0: + dict_splits[0]=None + elif mapping[i] == mapping[j]: + if ds_label[i]['val'] == ds_label[j]['val'] and ds_label[i]['test'] == ds_label[j]['test']: + + if mapping[i] not in dict_splits.keys(): + if ds_label[i]['val']: + dict_splits[mapping[i]] = 'val' + + elif ds_label[i]['test']: + dict_splits[mapping[i]]='test' + + else: + dict_splits[mapping[i]] = 'train' + + else: + if ds_label[i]['test']: + ind_label='test' + elif ds_label[i]['val']: + ind_label='val' + else: + ind_label='train' + if dict_splits[mapping[i]]!= ind_label: + print ('inconsistent labels within a graph exiting!!!') + return None + else: + print ('label of both nodes different, exiting!!') + return None + return dict_splits + +def load_ppi(): + + print ('Loading G...') + with open('ppi/ppi-G.json') as jsonfile: + g_data = json.load(jsonfile) + # print (len(g_data)) + G = json_graph.node_link_graph(g_data) + + #Extracting adjacency matrix + adj=nx.adjacency_matrix(G) + + prev_key='' + for key, value in g_data.items(): + if prev_key!=key: + # print (key) + prev_key=key + + # print ('Loading id_map...') + with open('ppi/ppi-id_map.json') as jsonfile: + id_map = json.load(jsonfile) + # print (len(id_map)) + + id_map = {int(k):int(v) for k,v in id_map.items()} + for key, value in id_map.items(): + id_map[key]=[value] + # print (len(id_map)) + + print ('Loading features...') + features_=np.load('ppi/ppi-feats.npy') + # print (features_.shape) + + #standarizing features + from sklearn.preprocessing import StandardScaler + + train_ids = np.array([id_map[n] for n in G.nodes() if not G.node[n]['val'] and not G.node[n]['test']]) + train_feats = features_[train_ids[:,0]] + scaler = StandardScaler() + scaler.fit(train_feats) + features_ = scaler.transform(features_) + + features = sp.csr_matrix(features_).tolil() + + + print ('Loading class_map...') + class_map = {} + with open('ppi/ppi-class_map.json') as jsonfile: + class_map = json.load(jsonfile) + # print (len(class_map)) + + #pdb.set_trace() + #Split graph into sub-graphs + # print ('Splitting graph...') + splits=dfs_split(adj) + + #Rearrange sub-graph index and append sub-graphs with 1 or 2 nodes to bigger sub-graphs + # print ('Re-arranging sub-graph IDs...') + list_splits=splits.tolist() + group_inc=1 + + for i in range(np.max(list_splits)+1): + if list_splits.count(i)>=3: + splits[np.array(list_splits) == i] =group_inc + group_inc+=1 + else: + #splits[np.array(list_splits) == i] = 0 + ind_nodes=np.argwhere(np.array(list_splits) == i) + ind_nodes=ind_nodes[:,0].tolist() + split=None + + for ind_node in ind_nodes: + if g_data['nodes'][ind_node]['val']: + if split is None or split=='val': + splits[np.array(list_splits) == i] = 21 + split='val' + else: + raise ValueError('new node is VAL but previously was {}'.format(split)) + elif g_data['nodes'][ind_node]['test']: + if split is None or split=='test': + splits[np.array(list_splits) == i] = 23 + split='test' + else: + raise ValueError('new node is TEST but previously was {}'.format(split)) + else: + if split is None or split == 'train': + splits[np.array(list_splits) == i] = 1 + split='train' + else: + pdb.set_trace() + raise ValueError('new node is TRAIN but previously was {}'.format(split)) + + #counting number of nodes per sub-graph + list_splits=splits.tolist() + nodes_per_graph=[] + for i in range(1,np.max(list_splits) + 1): + nodes_per_graph.append(list_splits.count(i)) + + #Splitting adj matrix into sub-graphs + subgraph_nodes=np.max(nodes_per_graph) + adj_sub=np.empty((len(nodes_per_graph), subgraph_nodes, subgraph_nodes)) + feat_sub = np.empty((len(nodes_per_graph), subgraph_nodes, features.shape[1])) + labels_sub = np.empty((len(nodes_per_graph), subgraph_nodes, 121)) + + for i in range(1, np.max(list_splits) + 1): + #Creating same size sub-graphs + indexes = np.where(splits == i)[0] + subgraph_=adj[indexes,:][:,indexes] + + if subgraph_.shape[0] adj) - adj.multiply(adj.T > adj) + tmp = sys_normalized_adjacency(adj) + train_adj_list.append(sparse_mx_to_torch_sparse_tensor(tmp)) + for i in range(val_adj.shape[0]): + adj = sp.coo_matrix(val_adj[i]) + adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj) + tmp = sys_normalized_adjacency(adj) + val_adj_list.append(sparse_mx_to_torch_sparse_tensor(tmp)) + adj = sp.coo_matrix(test_adj[i]) + adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj) + tmp = sys_normalized_adjacency(adj) + test_adj_list.append(sparse_mx_to_torch_sparse_tensor(tmp)) + + train_feat = torch.FloatTensor(train_feat) + val_feat = torch.FloatTensor(val_feat) + test_feat = torch.FloatTensor(test_feat) + + train_labels = torch.FloatTensor(train_labels) + val_labels = torch.FloatTensor(val_labels) + test_labels = torch.FloatTensor(test_labels) + + tr_msk = torch.LongTensor(tr_msk) + vl_msk = torch.LongTensor(vl_msk) + ts_msk = torch.LongTensor(ts_msk) + + return train_adj_list,val_adj_list,test_adj_list,train_feat,val_feat,test_feat,train_labels,val_labels, test_labels, train_nodes, val_nodes, test_nodes + diff --git a/examples/GCNII/utils.py b/examples/GCNII/utils.py new file mode 100644 index 00000000..e069be84 --- /dev/null +++ b/examples/GCNII/utils.py @@ -0,0 +1,395 @@ +import numpy as np +import scipy.sparse as sp +import torch +import torch.nn.functional as F +import sys +sys.path.append("../../../rllm") +import pickle as pkl +import networkx as nx +import json +from networkx.readwrite import json_graph +import pdb +sys.setrecursionlimit(99999) + +def accuracy(output, labels): + preds = output.max(1)[1].type_as(labels) + correct = preds.eq(labels).double() + correct = correct.sum() + return correct / len(labels) + +def normalize(mx): + """Row-normalize sparse matrix""" + rowsum = np.array(mx.sum(1)) + rowsum = (rowsum==0)*1+rowsum + r_inv = np.power(rowsum, -1).flatten() + r_inv[np.isinf(r_inv)] = 0. + r_mat_inv = sp.diags(r_inv) + mx = r_mat_inv.dot(mx) + return mx + +def sys_normalized_adjacency(adj): + adj = sp.coo_matrix(adj) + adj = adj + sp.eye(adj.shape[0]) + row_sum = np.array(adj.sum(1)) + row_sum=(row_sum==0)*1+row_sum + d_inv_sqrt = np.power(row_sum, -0.5).flatten() + d_inv_sqrt[np.isinf(d_inv_sqrt)] = 0. + d_mat_inv_sqrt = sp.diags(d_inv_sqrt) + return d_mat_inv_sqrt.dot(adj).dot(d_mat_inv_sqrt).tocoo() + +def sparse_mx_to_torch_sparse_tensor(sparse_mx): + """Convert a scipy sparse matrix to a torch sparse tensor.""" + sparse_mx = sparse_mx.tocoo().astype(np.float32) + indices = torch.from_numpy( + np.vstack((sparse_mx.row, sparse_mx.col)).astype(np.int64)) + values = torch.from_numpy(sparse_mx.data) + shape = torch.Size(sparse_mx.shape) + return torch.sparse.FloatTensor(indices, values, shape) + +def parse_index_file(filename): + """Parse index file.""" + index = [] + for line in open(filename): + index.append(int(line.strip())) + return index + +# adapted from tkipf/gcn +def load_citation(dataset_str="cora"): + """ + Load Citation Networks Datasets. + """ + names = ['x', 'y', 'tx', 'ty', 'allx', 'ally', 'graph'] + objects = [] + for i in range(len(names)): + with open("D:/rllm/rllm/datasets/cora/ind.{}.{}".format(dataset_str.lower(), names[i]), 'rb') as f: + if sys.version_info > (3, 0): + objects.append(pkl.load(f, encoding='latin1')) + else: + objects.append(pkl.load(f)) + + x, y, tx, ty, allx, ally, graph = tuple(objects) + test_idx_reorder = parse_index_file("D:/rllm/rllm/datasets/cora/ind.{}.test.index".format(dataset_str)) + test_idx_range = np.sort(test_idx_reorder) + + if dataset_str == 'citeseer': + # Fix citeseer dataset (there are some isolated nodes in the graph) + # Find isolated nodes, add them as zero-vecs into the right position + test_idx_range_full = range(min(test_idx_reorder), max(test_idx_reorder)+1) + tx_extended = sp.lil_matrix((len(test_idx_range_full), x.shape[1])) + tx_extended[test_idx_range-min(test_idx_range), :] = tx + tx = tx_extended + ty_extended = np.zeros((len(test_idx_range_full), y.shape[1])) + ty_extended[test_idx_range-min(test_idx_range), :] = ty + ty = ty_extended + + features = sp.vstack((allx, tx)).tolil() + features[test_idx_reorder, :] = features[test_idx_range, :] + adj = nx.adjacency_matrix(nx.from_dict_of_lists(graph)) + adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj) + labels = np.vstack((ally, ty)) + labels[test_idx_reorder, :] = labels[test_idx_range, :] + + idx_test = test_idx_range.tolist() + idx_train = range(len(y)) + idx_val = range(len(y), len(y)+500) + + features = normalize(features) + # porting to pytorch + features = torch.FloatTensor(np.array(features.todense())).float() + labels = torch.LongTensor(labels) + labels = torch.max(labels, dim=1)[1] + # adj = sparse_mx_to_torch_sparse_tensor(adj).float() + idx_train = torch.LongTensor(idx_train) + idx_val = torch.LongTensor(idx_val) + idx_test = torch.LongTensor(idx_test) + adj = sys_normalized_adjacency(adj) + adj = sparse_mx_to_torch_sparse_tensor(adj) + return adj, features, labels, idx_train, idx_val, idx_test + + +# adapted from PetarV/GAT +def run_dfs(adj, msk, u, ind, nb_nodes): + if msk[u] == -1: + msk[u] = ind + #for v in range(nb_nodes): + for v in adj[u,:].nonzero()[1]: + #if adj[u,v]== 1: + run_dfs(adj, msk, v, ind, nb_nodes) + +def dfs_split(adj): + # Assume adj is of shape [nb_nodes, nb_nodes] + nb_nodes = adj.shape[0] + ret = np.full(nb_nodes, -1, dtype=np.int32) + + graph_id = 0 + + for i in range(nb_nodes): + if ret[i] == -1: + run_dfs(adj, ret, i, graph_id, nb_nodes) + graph_id += 1 + + return ret + +def test(adj, mapping): + nb_nodes = adj.shape[0] + for i in range(nb_nodes): + #for j in range(nb_nodes): + for j in adj[i, :].nonzero()[1]: + if mapping[i] != mapping[j]: + # if adj[i,j] == 1: + return False + return True + +def find_split(adj, mapping, ds_label): + nb_nodes = adj.shape[0] + dict_splits={} + for i in range(nb_nodes): + #for j in range(nb_nodes): + for j in adj[i, :].nonzero()[1]: + if mapping[i]==0 or mapping[j]==0: + dict_splits[0]=None + elif mapping[i] == mapping[j]: + if ds_label[i]['val'] == ds_label[j]['val'] and ds_label[i]['test'] == ds_label[j]['test']: + + if mapping[i] not in dict_splits.keys(): + if ds_label[i]['val']: + dict_splits[mapping[i]] = 'val' + + elif ds_label[i]['test']: + dict_splits[mapping[i]]='test' + + else: + dict_splits[mapping[i]] = 'train' + + else: + if ds_label[i]['test']: + ind_label='test' + elif ds_label[i]['val']: + ind_label='val' + else: + ind_label='train' + if dict_splits[mapping[i]]!= ind_label: + print ('inconsistent labels within a graph exiting!!!') + return None + else: + print ('label of both nodes different, exiting!!') + return None + return dict_splits + +def load_ppi(): + + print ('Loading G...') + with open('ppi/ppi-G.json') as jsonfile: + g_data = json.load(jsonfile) + # print (len(g_data)) + G = json_graph.node_link_graph(g_data) + + #Extracting adjacency matrix + adj=nx.adjacency_matrix(G) + + prev_key='' + for key, value in g_data.items(): + if prev_key!=key: + # print (key) + prev_key=key + + # print ('Loading id_map...') + with open('ppi/ppi-id_map.json') as jsonfile: + id_map = json.load(jsonfile) + # print (len(id_map)) + + id_map = {int(k):int(v) for k,v in id_map.items()} + for key, value in id_map.items(): + id_map[key]=[value] + # print (len(id_map)) + + print ('Loading features...') + features_=np.load('ppi/ppi-feats.npy') + # print (features_.shape) + + #standarizing features + from sklearn.preprocessing import StandardScaler + + train_ids = np.array([id_map[n] for n in G.nodes() if not G.node[n]['val'] and not G.node[n]['test']]) + train_feats = features_[train_ids[:,0]] + scaler = StandardScaler() + scaler.fit(train_feats) + features_ = scaler.transform(features_) + + features = sp.csr_matrix(features_).tolil() + + + print ('Loading class_map...') + class_map = {} + with open('ppi/ppi-class_map.json') as jsonfile: + class_map = json.load(jsonfile) + # print (len(class_map)) + + #pdb.set_trace() + #Split graph into sub-graphs + # print ('Splitting graph...') + splits=dfs_split(adj) + + #Rearrange sub-graph index and append sub-graphs with 1 or 2 nodes to bigger sub-graphs + # print ('Re-arranging sub-graph IDs...') + list_splits=splits.tolist() + group_inc=1 + + for i in range(np.max(list_splits)+1): + if list_splits.count(i)>=3: + splits[np.array(list_splits) == i] =group_inc + group_inc+=1 + else: + #splits[np.array(list_splits) == i] = 0 + ind_nodes=np.argwhere(np.array(list_splits) == i) + ind_nodes=ind_nodes[:,0].tolist() + split=None + + for ind_node in ind_nodes: + if g_data['nodes'][ind_node]['val']: + if split is None or split=='val': + splits[np.array(list_splits) == i] = 21 + split='val' + else: + raise ValueError('new node is VAL but previously was {}'.format(split)) + elif g_data['nodes'][ind_node]['test']: + if split is None or split=='test': + splits[np.array(list_splits) == i] = 23 + split='test' + else: + raise ValueError('new node is TEST but previously was {}'.format(split)) + else: + if split is None or split == 'train': + splits[np.array(list_splits) == i] = 1 + split='train' + else: + pdb.set_trace() + raise ValueError('new node is TRAIN but previously was {}'.format(split)) + + #counting number of nodes per sub-graph + list_splits=splits.tolist() + nodes_per_graph=[] + for i in range(1,np.max(list_splits) + 1): + nodes_per_graph.append(list_splits.count(i)) + + #Splitting adj matrix into sub-graphs + subgraph_nodes=np.max(nodes_per_graph) + adj_sub=np.empty((len(nodes_per_graph), subgraph_nodes, subgraph_nodes)) + feat_sub = np.empty((len(nodes_per_graph), subgraph_nodes, features.shape[1])) + labels_sub = np.empty((len(nodes_per_graph), subgraph_nodes, 121)) + + for i in range(1, np.max(list_splits) + 1): + #Creating same size sub-graphs + indexes = np.where(splits == i)[0] + subgraph_=adj[indexes,:][:,indexes] + + if subgraph_.shape[0] adj) - adj.multiply(adj.T > adj) + tmp = sys_normalized_adjacency(adj) + train_adj_list.append(sparse_mx_to_torch_sparse_tensor(tmp)) + for i in range(val_adj.shape[0]): + adj = sp.coo_matrix(val_adj[i]) + adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj) + tmp = sys_normalized_adjacency(adj) + val_adj_list.append(sparse_mx_to_torch_sparse_tensor(tmp)) + adj = sp.coo_matrix(test_adj[i]) + adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj) + tmp = sys_normalized_adjacency(adj) + test_adj_list.append(sparse_mx_to_torch_sparse_tensor(tmp)) + + train_feat = torch.FloatTensor(train_feat) + val_feat = torch.FloatTensor(val_feat) + test_feat = torch.FloatTensor(test_feat) + + train_labels = torch.FloatTensor(train_labels) + val_labels = torch.FloatTensor(val_labels) + test_labels = torch.FloatTensor(test_labels) + + tr_msk = torch.LongTensor(tr_msk) + vl_msk = torch.LongTensor(vl_msk) + ts_msk = torch.LongTensor(ts_msk) + + return train_adj_list,val_adj_list,test_adj_list,train_feat,val_feat,test_feat,train_labels,val_labels, test_labels, train_nodes, val_nodes, test_nodes +