import numpy as np import torch import torch.nn as nn import torch.nn.functional as F import dgl from dgl.data import AsNodePredDataset {{ data_import_code }} {{ model_code }} {% if user_cfg.general_pipeline.early_stop %} class EarlyStopping: def __init__(self, patience: int = -1, checkpoint_path: str = 'checkpoint.pt'): self.patience = patience self.checkpoint_path = checkpoint_path self.counter = 0 self.best_score = None self.early_stop = False def step(self, acc, model): score = acc if self.best_score is None: self.best_score = score self.save_checkpoint(model) elif score < self.best_score: self.counter += 1 print(f'EarlyStopping counter: {self.counter} out of {self.patience}') if self.counter >= self.patience: self.early_stop = True else: self.best_score = score self.save_checkpoint(model) self.counter = 0 return self.early_stop def save_checkpoint(self, model): '''Save model when validation loss decreases.''' torch.save(model.state_dict(), self.checkpoint_path) def load_checkpoint(self, model): model.load_state_dict(torch.load(self.checkpoint_path)) {% endif %} def accuracy(logits, labels): _, indices = torch.max(logits, dim=1) correct = torch.sum(indices == labels) return correct.item() * 1.0 / len(labels) def train(cfg, pipeline_cfg, device, data, model, optimizer, loss_fcn): g = data[0] # Only train on the first graph g = dgl.remove_self_loop(g) g = dgl.add_self_loop(g) g = g.to(device) node_feat = g.ndata.get('feat', None) edge_feat = g.edata.get('feat', None) label = g.ndata['label'] train_mask, val_mask, test_mask = g.ndata['train_mask'].bool(), g.ndata['val_mask'].bool(), g.ndata['test_mask'].bool() {% if user_cfg.general_pipeline.early_stop %} stopper = EarlyStopping(**pipeline_cfg['early_stop']) {% endif %} val_acc = 0. for epoch in range(pipeline_cfg['num_epochs']): model.train() logits = model(g, node_feat, edge_feat) loss = loss_fcn(logits[train_mask], label[train_mask]) optimizer.zero_grad() loss.backward() optimizer.step() train_acc = accuracy(logits[train_mask], label[train_mask]) if epoch != 0 and epoch % pipeline_cfg['eval_period'] == 0: val_acc = accuracy(logits[val_mask], label[val_mask]) {% if user_cfg.general_pipeline.early_stop %} if stopper.step(val_acc, model): break {% endif %} print("Epoch {:05d} | Loss {:.4f} | TrainAcc {:.4f} | ValAcc {:.4f}". format(epoch, loss.item(), train_acc, val_acc)) {% if user_cfg.general_pipeline.early_stop %} stopper.load_checkpoint(model) {% endif %} model.eval() with torch.no_grad(): logits = model(g, node_feat, edge_feat) test_acc = accuracy(logits[test_mask], label[test_mask]) return test_acc def main(): {{ user_cfg_str }} device = cfg['device'] pipeline_cfg = cfg['general_pipeline'] # load data data = AsNodePredDataset({{data_initialize_code}}) # create model model_cfg = cfg["model"] cfg["model"]["data_info"] = { "in_size": model_cfg['embed_size'] if model_cfg['embed_size'] > 0 else data[0].ndata['feat'].shape[1], "out_size": data.num_classes, "num_nodes": data[0].num_nodes() } model = {{ model_class_name }}(**cfg["model"]) model = model.to(device) loss = torch.nn.{{ user_cfg.general_pipeline.loss }}() optimizer = torch.optim.{{ user_cfg.general_pipeline.optimizer.name }}(model.parameters(), **pipeline_cfg["optimizer"]) # train test_acc = train(cfg, pipeline_cfg, device, data, model, optimizer, loss) return test_acc if __name__ == '__main__': all_acc = [] num_runs = {{ user_cfg.general_pipeline.num_runs }} for run in range(num_runs): print(f'Run experiment #{run}') test_acc = main() print("Test Accuracy {:.4f}".format(test_acc)) all_acc.append(test_acc) avg_acc = np.round(np.mean(all_acc), 6) std_acc = np.round(np.std(all_acc), 6) print(f'Accuracy across {num_runs} runs: {avg_acc} ± {std_acc}')