Example #1
0
def evaluate_test_set_performance(model_dir):
    """Measures the test set performance of the model under the specified model directory.

    :param model_dir: directory containing the model state dictionaries for each fold and the model
        configuration (including the population graph parameterisation)
    :return: the test set performance for each fold.
    """

    with open(os.path.join(model_dir, 'config.yaml')) as file:
        cfg = yaml.full_load(file)

        graph_name = cfg['graph_name']['value']
        conv_type = cfg['model']['value']

        n_conv_layers = cfg['n_conv_layers']['value']
        layer_sizes = ast.literal_eval(cfg['layer_sizes']['value'])
        dropout_p = cfg['dropout']['value']

        similarity_feature_set = [Phenotype(i) for i in ast.literal_eval(cfg['similarity']['value'])[0]]
        similarity_threshold = ast.literal_eval(cfg['similarity']['value'])[1]

    if graph_name not in GRAPH_NAMES:
        graph_construct.construct_population_graph(similarity_feature_set=similarity_feature_set,
                                                   similarity_threshold=similarity_threshold,
                                                   functional=False,
                                                   structural=True,
                                                   euler=True)

    graph = graph_construct.load_population_graph(graph_root, graph_name)

    folds = brain_gnn_train.get_cv_subject_split(graph, n_folds=5)
    results = {}

    for i, fold in enumerate(folds):
        brain_gnn_train.set_training_masks(graph, *fold)
        graph_transform.graph_feature_transform(graph)

        if ConvTypes(conv_type) == ConvTypes.GCN:
            model = BrainGCN(graph.num_node_features, n_conv_layers, layer_sizes, dropout_p)
        else:
            model = BrainGAT(graph.num_node_features, n_conv_layers, layer_sizes, dropout_p)

        model.load_state_dict(torch.load(os.path.join(model_dir, 'fold-{}_state_dict.pt'.format(i))))
        model = model.to(device)
        model.eval()

        data = graph.to(device)
        model = model(data)

        predicted = model[data.test_mask].cpu()
        actual = graph.y[data.test_mask].cpu()

        r2 = r2_score(actual.detach().numpy(), predicted.detach().numpy())
        r = pearsonr(actual.detach().numpy().flatten(), predicted.detach().numpy().flatten())
        results['fold_{}'.format(i)] = {'r': [x.item() for x in r], 'r2': r2.item()}
        mse = mean_squared_error(actual.detach().numpy(), predicted.detach().numpy())
        results=mse
        break

    return results
Example #2
0
def train_with_cross_validation(conv_type,
                                graph,
                                device,
                                n_folds=10,
                                n_conv_layers=0,
                                layer_sizes=None,
                                epochs=350,
                                lr=0.005,
                                dropout_p=0,
                                weight_decay=1e-5,
                                log=True,
                                early_stopping=True,
                                patience=10,
                                delta=0.005):
    """
    Trains the graph neural network on a population graph.

    :param conv_type: convolution type, ConvType.GCN or ConvType.GAT
    :param graph: the population graph
    :param device: device on which the neural network will be trained. 'cpu' or 'cuda:x'
    :param n_conv_layers: number of convolutional layers in GNN
    :param layer_sizes: array of layer sizes, first n_conv_layers of which convolutional, the rest fully connected
    :param epochs: number of epochs for which to train
    :param lr: learning rate
    :param dropout_p: dropout probability (probability of killing the unit)
    :param weight_decay: weight decay
    :param log: indicates whether to log results to wandb.
    :param early_stopping: indicates whether to use early stopping.
    :param patience: indicates how long to tolerate no improvement in MSE before early stopping.
    :param delta: defines the threshold of how much the model has to improve in `patience` epochs.
    :return: array of results as in train method, repeated for all folds.
    """

    folds = get_cv_subject_split(graph, n_folds=n_folds)
    results = []
    run_name = None

    wandb.init(project="brain-age-gnn",
               config=hyperparameter_defaults,
               reinit=True)
    wandb.save("*.pt")

    for i, fold in enumerate(folds):
        set_training_masks(graph, *fold)
        graph_transform.graph_feature_transform(graph)

        _, fold_result = train(conv_type,
                               graph,
                               device,
                               n_conv_layers=n_conv_layers,
                               layer_sizes=layer_sizes,
                               epochs=epochs,
                               lr=lr,
                               dropout_p=dropout_p,
                               weight_decay=weight_decay,
                               log=log,
                               early_stopping=early_stopping,
                               patience=patience,
                               delta=delta,
                               cv=True,
                               fold=i,
                               run_name=run_name)
        run_name = fold_result[0]
        fold_scores = fold_result[1:]
        results.append(fold_scores)
        if np.mean([
                F.mse_loss(predicted, actual).item()
                for predicted, actual in results
        ],
                   axis=0) > 30:
            break

    # Add cross-validation summaries to the last fold
    cv_mse = [
        F.mse_loss(predicted, actual).item() for predicted, actual in results
    ]
    cv_r2 = [
        r2_score(actual.detach().numpy(),
                 predicted.detach().numpy()) for predicted, actual in results
    ]
    cv_r = [
        pearsonr(actual.detach().numpy().flatten(),
                 predicted.detach().numpy().flatten())
        for predicted, actual in results
    ]
    wandb.run.summary["cv_validation_average_mse"] = np.mean(cv_mse, axis=0)
    wandb.run.summary["cv_validation_average_r2"] = np.mean(cv_r2, axis=0)
    wandb.run.summary["cv_validation_average_r"] = np.mean(cv_r, axis=0)

    return results
Example #3
0
def label_permutation_test(model_dir):
    """Permutation test measuring the performance of the model when the labels are shuffled.

    :param model_dir: directory containing the model state dictionaries for each fold and the model
        configuration (including the population graph parameterisation)
    :return: the test set performance for each permutation.
    """

    with open(os.path.join(model_dir, 'config.yaml')) as file:
        cfg = yaml.full_load(file)

        graph_name = cfg['graph_name']['value']
        conv_type = cfg['model']['value']

        n_conv_layers = cfg['n_conv_layers']['value']
        layer_sizes = ast.literal_eval(cfg['layer_sizes']['value'])
        dropout_p = cfg['dropout']['value']

        similarity_feature_set = [Phenotype(i) for i in ast.literal_eval(cfg['similarity']['value'])[0]]
        similarity_threshold = ast.literal_eval(cfg['similarity']['value'])[1]

    if graph_name not in GRAPH_NAMES:
        graph_construct.construct_population_graph(similarity_feature_set=similarity_feature_set,
                                                   similarity_threshold=similarity_threshold,
                                                   functional=False,
                                                   structural=True,
                                                   euler=True)

    graph = graph_construct.load_population_graph(graph_root, graph_name)

    folds = brain_gnn_train.get_cv_subject_split(graph, n_folds=5)
    fold = folds[0]
    brain_gnn_train.set_training_masks(graph, *fold)
    graph_transform.graph_feature_transform(graph)

    rs = []
    r2s = []
    mses = []

    for i in range(1000):
        graph.to('cpu')
        permute_population_graph_labels(graph, i)

        if ConvTypes(conv_type) == ConvTypes.GCN:
            model = BrainGCN(graph.num_node_features, n_conv_layers, layer_sizes, dropout_p)
        else:
            model = BrainGAT(graph.num_node_features, n_conv_layers, layer_sizes, dropout_p)

        model.load_state_dict(torch.load(os.path.join(model_dir, 'fold-{}_state_dict.pt'.format(0))))
        model = model.to(device)

        data = graph.to(device)
        model.eval()
        model = model(data)

        predicted = model[data.test_mask].cpu()
        actual = graph.y[data.test_mask].cpu()

        r2 = r2_score(actual.detach().numpy(), predicted.detach().numpy())
        r = pearsonr(actual.detach().numpy().flatten(), predicted.detach().numpy().flatten())
        mse = mean_squared_error(actual.detach().numpy(), predicted.detach().numpy())

        rs.append(r[0])
        r2s.append(r2)
        mses.append(mse)
        print(r[0], r2, mse)

    np.save(os.path.join('notebooks', 'permutations_{}_{}'.format(conv_type, 'r')), rs)
    np.save(os.path.join('notebooks', 'permutations_{}_{}'.format(conv_type, 'r2')), r2s)
    np.save(os.path.join('notebooks', 'permutations_{}_{}'.format(conv_type, 'mse')), mses)

    return [rs, r2s]
Example #4
0
def evaluate_noise_performance(model_dir, noise_type='node'):
    """Measures the test set performance of the model under the specified model directory when noise is added.

    :param model_dir: directory containing the model state dictionaries for each fold and the model
        configuration (including the population graph parameterisation)
    :param noise_type: 'node', 'node_feature_permutation' or 'edge'.
    :return: the dictionary of results under five different random seeds and increasing probabilities of added noise.
    """

    with open(os.path.join(model_dir, 'config.yaml')) as file:
        cfg = yaml.full_load(file)

        graph_name = cfg['graph_name']['value']
        conv_type = cfg['model']['value']

        n_conv_layers = cfg['n_conv_layers']['value']
        layer_sizes = ast.literal_eval(cfg['layer_sizes']['value'])
        dropout_p = cfg['dropout']['value']

        lr = cfg['learning_rate']['value']
        weight_decay = cfg['weight_decay']['value']

        similarity_feature_set = [Phenotype(i) for i in ast.literal_eval(cfg['similarity']['value'])[0]]
        similarity_threshold = ast.literal_eval(cfg['similarity']['value'])[1]

    if graph_name not in GRAPH_NAMES:
        graph_construct.construct_population_graph(similarity_feature_set=similarity_feature_set,
                                                   similarity_threshold=similarity_threshold,
                                                   functional=False,
                                                   structural=True,
                                                   euler=True)

    graph = graph_construct.load_population_graph(graph_root, graph_name)

    folds = brain_gnn_train.get_cv_subject_split(graph, n_folds=5)
    fold = folds[0]
    results = {}

    for i in range(1, 5):
        brain_gnn_train.set_training_masks(graph, *fold)
        results_fold = {}

        for p in [0.01, 0.05, 0.1, 0.2, 0.3, 0.5, 0.8, 0.95]:
            graph.to('cpu')
            graph_transform.graph_feature_transform(graph)
            if noise_type == 'node':
                add_population_graph_noise(graph, p, random_state=i)
            if noise_type == 'edge':
                remove_population_graph_edges(graph, p, random_state=i)
            if noise_type == 'node-feature-permutation':
                permute_population_graph_features(graph, p, random_state=i)


            data = graph.to(device)
            epochs = 10000
            model, _ = brain_gnn_train.train(conv_type, graph, device, n_conv_layers, layer_sizes, epochs, lr,
                                             dropout_p, weight_decay, patience=100)
            model.eval()
            model = model(data)

            predicted = model[data.test_mask].cpu()
            actual = data.y[data.test_mask].cpu()
            r2 = r2_score(actual.detach().numpy(), predicted.detach().numpy())
            r = pearsonr(actual.detach().numpy().flatten(), predicted.detach().numpy().flatten())
            results_fold['p={}_metric=r'.format(p)] = [x.item() for x in r][0]
            wandb.run.summary['{}_{}_{}_p={}_metric=r'.format(conv_type, noise_type, i, p)] = [x.item() for x in r][0]
            results_fold['p={}_metric=r2'.format(p)] = r2.item()
            wandb.run.summary['{}_{}_{}_p={}_metric=r2'.format(conv_type, noise_type, i, p)] = r2.item()

            gc.collect()

        results['{}_{}_{}'.format(conv_type, noise_type, i)] = results_fold

    return results
Example #5
0
        structural=structural,
        euler=euler)

if args.model == 'gcn' or args.model == 'gat':
    n_conv_layers = args.n_conv_layers
else:
    n_conv_layers = 0

torch.manual_seed(99)
np.random.seed(0)

population_graph = graph_construct.load_population_graph(
    graph_root, graph_name)
fold = brain_gnn_train.get_stratified_subject_split(population_graph)
brain_gnn_train.set_training_masks(population_graph, *fold)
graph_transform.graph_feature_transform(population_graph)

device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

brain_gnn_train.train_with_cross_validation(args.model,
                                            population_graph,
                                            device,
                                            n_conv_layers=n_conv_layers,
                                            layer_sizes=ast.literal_eval(
                                                args.layer_sizes),
                                            lr=args.learning_rate,
                                            weight_decay=args.weight_decay,
                                            dropout_p=args.dropout,
                                            epochs=args.epochs,
                                            n_folds=5,
                                            patience=100)