class SGC(torch.nn.Module): """ Simplifying Graph Convolutional Networks" <https://arxiv.org/abs/1902.07153> """ def __init__(self): super(SGC, self).__init__() self.name = 'SGC' self.conv1 = SGConv(75, 128, K=2, cached=False) self.gather_layer = nn.Linear(128, 1) def reset_parameters(self): self.conv1.reset_parameters() self.gather_layer.reset_parameters() def forward(self, data): x, edge_index, batch = data.x, data.edge_index, data.batch x1 = self.conv1(x, edge_index) y_molecules = global_add_pool(x1, batch) z_molecules = self.gather_layer(y_molecules) return z_molecules def __call__(self, data, std, mean): target = torch.unsqueeze(data.y, 1) out = self.forward(data) loss = F.mse_loss(out, target) z = out.to('cpu').data.numpy() t = target.to('cpu').data.numpy() z, t = std * z + mean, std * t + mean return loss, z, t
class ModelSGC(torch.nn.Module): def __init__(self, num_layers, hidden, activation, data): super(ModelSGC, self).__init__() self.linear_1 = Linear(data.num_features, hidden) self.conv = SGConv(hidden, hidden, K=num_layers) self.linear_2 = Linear(hidden, data.num_class) if activation == "relu": self.activation = relu elif activation == "leaky_relu": self.activation = leaky_relu def reset_parameters(self): self.linear_1.reset_parameters() self.conv.reset_parameters() self.linear_2.reset_parameters() def forward(self, data): x, edge_index, edge_weight = data.x, data.edge_index, data.edge_weight x = self.linear_1(x) x = self.activation(x) x = dropout(x, p=0.5, training=self.training) x = self.conv(x, edge_index, edge_weight=edge_weight) x = dropout(x, p=0.5, training=self.training) x = self.linear_2(x) return log_softmax(x, dim=-1)
class SGC(nn.Module): def __init__(self, dataset, K): super(SGC, self).__init__() self.gc1 = SGConv(dataset.num_features, dataset.num_classes, K=K, cached=True) def reset_parameters(self): self.gc1.reset_parameters() def forward(self, data): x, edge_index = data.x, data.edge_index x = self.gc1(x, edge_index) return F.log_softmax(x, dim=1)
class Net(torch.nn.Module): def __init__(self, dataset): super(Net, self).__init__() self.conv1 = SGConv( dataset.num_features, dataset.num_classes, K=args.K, cached=True) def reset_parameters(self): self.conv1.reset_parameters() def forward(self, data): x, edge_index = data.x, data.edge_index x = self.conv1(x, edge_index) return F.log_softmax(x, dim=1)
class SGC(nn.Module): def __init__(self, in_channels, out_channels, hops): """ takes 'hops' power of the normalized adjacency""" super(SGC, self).__init__() self.conv = SGConv(in_channels, out_channels, hops, cached=True) def reset_parameters(self): self.conv.reset_parameters() def forward(self, data): edge_index = data.graph['edge_index'] x = data.graph['node_feat'] x = self.conv(x, edge_index) return x
class SGC_Net(torch.nn.Module): def __init__(self, features_num, num_class, K, cached): super(SGC_Net, self).__init__() self.conv1 = SGConv(features_num, num_class, K, cached) def reset_parameters(self): self.conv1.reset_parameters() def forward(self, data): x, edge_index = data.x, data.edge_index x = self.conv1(x, edge_index) return F.log_softmax(x, dim=-1) def __repr__(self): return self.__class__.__name__
class GFNN(nn.Module): def __init__(self, dataset, nhid, K): super(GFNN, self).__init__() self.gc1 = SGConv(dataset.num_features, nhid, K=K, cached=True) self.fc1 = nn.Linear(nhid, dataset.num_classes) def reset_parameters(self): self.gc1.reset_parameters() self.fc1.reset_parameters() def forward(self, data): x, edge_index = data.x, data.edge_index x = self.gc1(x, edge_index) x = F.relu(x) x = self.fc1(x) return F.log_softmax(x, dim=1)
class SGCN(torch.nn.Module): def __init__(self, num_layers=2, hidden=16, features_num=16, num_class=2, hidden_droprate=0.5, edge_droprate=0.0): super(SGCN, self).__init__() self.conv1 = SGConv(features_num, hidden) self.convs = torch.nn.ModuleList() for i in range(num_layers - 1): self.convs.append(SGConv(hidden, hidden)) self.lin2 = Linear(hidden, num_class) self.first_lin = Linear(features_num, hidden) self.hidden_droprate = hidden_droprate self.edge_droprate = edge_droprate def reset_parameters(self): self.first_lin.reset_parameters() self.conv1.reset_parameters() for conv in self.convs: conv.reset_parameters() self.lin2.reset_parameters() def forward(self, data): if self.edge_droprate != 0.0: x = data.x edge_index, edge_weight = dropout_adj(data.edge_index, data.edge_weight, self.edge_droprate) else: x, edge_index, edge_weight = data.x, data.edge_index, data.edge_weight x = F.relu(self.first_lin(x)) x = F.dropout(x, p=self.dropout_rate, training=self.training) for conv in self.convs: x = F.relu(conv(x, edge_index, edge_weight=edge_weight)) x = F.dropout(x, p=self.dropout_rate, training=self.training) x = self.lin2(x) # return F.log_softmax(x, dim=-1) # due to focal loss: return the logits, put the log_softmax operation into the GNNAlgo return x def __repr__(self): return self.__class__.__name__
class SGC(torch.nn.Module): """ SGC based on pytorch geometric. Simplifying Graph Convolutional Networks. Parameters ---------- nfeat : int size of input feature dimension nclass : int size of output dimension K: int number of propagation in SGC cached : bool whether to set the cache flag in SGConv lr : float learning rate for SGC weight_decay : float weight decay coefficient (l2 normalization) for GCN. When `with_relu` is True, `weight_decay` will be set to 0. with_bias: bool whether to include bias term in SGC weights. device: str 'cpu' or 'cuda'. Examples -------- We can first load dataset and then train SGC. >>> from deeprobust.graph.data import Dataset >>> from deeprobust.graph.defense import SGC >>> data = Dataset(root='/tmp/', name='cora') >>> adj, features, labels = data.adj, data.features, data.labels >>> idx_train, idx_val, idx_test = data.idx_train, data.idx_val, data.idx_test >>> sgc = SGC(nfeat=features.shape[1], K=3, lr=0.1, nclass=labels.max().item() + 1, device='cuda') >>> sgc = sgc.to('cuda') >>> pyg_data = Dpr2Pyg(data) # convert deeprobust dataset to pyg dataset >>> sgc.fit(pyg_data, train_iters=200, patience=200, verbose=True) # train with earlystopping """ def __init__(self, nfeat, nclass, K=3, cached=True, lr=0.01, weight_decay=5e-4, with_bias=True, device=None): super(SGC, self).__init__() assert device is not None, "Please specify 'device'!" self.device = device self.nfeat = nfeat self.hidden_sizes = [K] self.nclass = nclass self.conv1 = SGConv(nfeat, nclass, bias=with_bias, K=K, cached=cached) self.weight_decay = weight_decay self.lr = lr self.output = None self.best_model = None self.best_output = None def forward(self, data): x, edge_index = data.x, data.edge_index x = self.conv1(x, edge_index) return F.log_softmax(x, dim=1) def initialize(self): """Initialize parameters of SGC. """ self.conv1.reset_parameters() def fit(self, pyg_data, train_iters=200, initialize=True, verbose=False, patience=500, **kwargs): """Train the SGC model, when idx_val is not None, pick the best model according to the validation loss. Parameters ---------- pyg_data : pytorch geometric dataset object train_iters : int number of training epochs initialize : bool whether to initialize parameters before training verbose : bool whether to show verbose logs patience : int patience for early stopping, only valid when `idx_val` is given """ # self.device = self.conv1.weight.device if initialize: self.initialize() self.data = pyg_data[0].to(self.device) if patience > 1000: self.train_without_val(train_iters, verbose) else: # By default, it is trained with early stopping on validation self.train_with_early_stopping(train_iters, patience, verbose) def train_without_val(self, train_iters, verbose): """No early stopping """ if verbose: print('=== training GAT model ===') optimizer = optim.Adam(self.parameters(), lr=self.lr, weight_decay=self.weight_decay) labels = self.data.y train_mask, val_mask = self.data.train_mask, self.data.val_mask for i in range(train_iters): self.train() optimizer.zero_grad() output = self.forward(self.data) loss_train = F.nll_loss(output[train_mask], labels[train_mask]) loss_train.backward() optimizer.step() if verbose and i % 10 == 0: print('Epoch {}, training loss: {}'.format( i, loss_train.item())) self.eval() self.output = self.forward(self.data) def train_with_early_stopping(self, train_iters, patience, verbose): """early stopping based on the validation loss """ if verbose: print('=== training SGC model ===') optimizer = optim.Adam(self.parameters(), lr=self.lr, weight_decay=self.weight_decay) labels = self.data.y train_mask, val_mask = self.data.train_mask, self.data.val_mask early_stopping = patience best_loss_val = 100 for i in range(train_iters): self.train() optimizer.zero_grad() output = self.forward(self.data) loss_train = F.nll_loss(output[train_mask], labels[train_mask]) loss_train.backward() optimizer.step() if verbose and i % 10 == 0: print('Epoch {}, training loss: {}'.format( i, loss_train.item())) self.eval() output = self.forward(self.data) loss_val = F.nll_loss(output[val_mask], labels[val_mask]) if best_loss_val > loss_val: best_loss_val = loss_val self.output = output weights = deepcopy(self.state_dict()) patience = early_stopping else: patience -= 1 if i > early_stopping and patience <= 0: break if verbose: print('=== early stopping at {0}, loss_val = {1} ==='.format( i, best_loss_val)) self.load_state_dict(weights) def test(self, dropout): """Evaluate SGC performance on test set. Parameters ---------- idx_test : node testing indices """ self.eval() self.dropout = dropout test_mask = self.data.test_mask labels = self.data.y output = self.forward(self.data) # output = self.output loss_test = F.nll_loss(output[test_mask], labels[test_mask]) acc_test = utils.accuracy(output[test_mask], labels[test_mask]) print("Test set results:", "loss= {:.4f}".format(loss_test.item()), "accuracy= {:.4f}".format(acc_test.item())) return acc_test.item() def predict(self, pyg_data=None, dropout=0.0): """ Returns ------- torch.FloatTensor output (log probabilities) of SGC """ self.eval() self.dropout = dropout if pyg_data == None: data = self.data else: data = pyg_data[0].to(self.device) self.data = data return self.forward(data)