Package: ensemble-gnn
Ensemble learning with graph neural networks for disease module discoveryInstallationThe GNN-Subnet package is requiredhttps://github.com/pievos101/GNN-SubNetTo install GNNSubNet run:pipinstalltorch==1.11.0pipinstalltorchvision==0.12.0pipinstalltorch-geometric==2.0.4pipinstalltorch-scatter==2.0.9pipinstalltorch-sparse==0.6.13# to install GNNSubNet from source (cloned GitHub repo) run:pipinstallGNN-SubNet/Preferred versions are: torch==1.11.0, torchvision==0.12.0, torch-geometric==2.0.4, torch-scatter==2.0.9, and torch-sparse==0.6.13.To install Ensemble-GNN from source code (cloned GitHub repo) run:pipinstallEnsemble-GNN/UsageExample 1: Synthetic data - Barabasi Networks# import GNNSubNetfromGNNSubNetimportGNNSubNetasgnn# import ensemble_gnnimportensemble_gnnasegnn# Synthetic data set  ------------------------- #loc="/home/bastian/GitHub/GNN-SubNet/GNNSubNet/datasets/synthetic"ppi=f'{loc}/NETWORK_synthetic.txt'feats=[f'{loc}/FEATURES_synthetic.txt']targ=f'{loc}/TARGET_synthetic.txt'# Read in the synthetic data and build a gnnsubnet objectg=gnn.GNNSubNet(loc,ppi,feats,targ,normalize=False)# Get some general information about the data dimensiong.summary()# train-test split: 80-20g_train,g_test=egnn.split(g,0.8)# initialization: infer subnetworks and build ensemblee1=egnn.ensemble(g_train,niter=1)# niter=10 is recommended# length of ensemblelen(e1.ensemble)# train an gnn model on each subnetwork of the ensemblee1.train()# accuracy for each modulee1.train_accuracy# the nodes of each ensemble member can be accessed viae1.modules_gene_names# The first subnetwork used within the ensemble can be obtained frome1.ensemble[0].dataset[0].edge_index# grow the ensemble (greedy step)# e1.grow(10)# check the accuracye1.train_accuracy# train again with a different train-validation split# e1.train()# check the accuracye1.train_accuracy# predict via Majority Votepredicted_class=e1.predict(g_test)# the overall predictions based on the whole ensemble using majority votepredicted_class#e1.predictions_test_mv# lets check the performance of the ensemble classifierfromsklearn.metricsimportconfusion_matrixfromsklearn.metricsimportaccuracy_scoreacc=accuracy_score(g_test.true_class,predicted_class)confusion_matrix(g_test.true_class,predicted_class)fromsklearn.metrics.clusterimportnormalized_mutual_info_scorenormalized_mutual_info_score(g_test.true_class,predicted_class)print("\n-----------")print("Accuracy of ensemble classifier:",acc)Example 2: Protein-Protein Interaction (PPI) network data# import GNNSubNetfromGNNSubNetimportGNNSubNetasgnn# import ensemble_gnnimportensemble_gnnasegnn# location of the filesloc="/home/bastian/GitHub/GNN-SubNet/TCGA"# PPI networkppi=f'{loc}/KIDNEY_RANDOM_PPI.txt'# single-omic featuresfeats=[f'{loc}/KIDNEY_RANDOM_mRNA_FEATURES.txt']# multi-omic features#feats = [f'{loc}/KIDNEY_RANDOM_mRNA_FEATURES.txt', f'{loc}/KIDNEY_RANDOM_Methy_FEATURES.txt']# outcome classtarg=f'{loc}/KIDNEY_RANDOM_TARGET.txt'# Load the multi-omics datag=gnn.GNNSubNet(loc,ppi,feats,targ)# Get some general information about the data dimensiong.summary()# train-test split: 80-20g_train,g_test=egnn.split(g,0.8)# initialization: infer subnetworks and build ensemblee1=egnn.ensemble(g_train,niter=1)# niter=10 is recommended# length of ensemblelen(e1.ensemble)# train an gnn model on each subnetwork of the ensemblee1.train()# accuracy for each modulee1.train_accuracy# the nodes of each ensemble member can be accessed viae1.modules_gene_names# The first subnetwork used within the ensemble can be obtained frome1.ensemble[0].dataset[0].edge_index# grow the ensemble (greedy step)# e1.grow(10) [optional]# check the accuracye1.train_accuracy# train again with a different train-validation split# e1.train()# check the accuracye1.train_accuracy# predict via Majority Votepredicted_class=e1.predict(g_test)# the overall predictions based on the whole ensemble using majority votepredicted_class# lets check the performance of the ensemble classifierprint("\n-----------")fromsklearn.metricsimportaccuracy_scoreacc=accuracy_score(g_test.true_class,predicted_class)print("Accuracy of ensemble classifier:",acc)fromsklearn.metricsimportbalanced_accuracy_scoreacc_bal=balanced_accuracy_score(g_test.true_class,predicted_class)print("Balanced accuracy of ensemble classifier:",acc_bal)fromsklearn.metrics.clusterimportnormalized_mutual_info_scorenmi=normalized_mutual_info_score(g_test.true_class,predicted_class)print("NMI of ensemble classifier:",nmi)# The results can be compared with non-ensemble-based classification# train the GNN classifierg_train.train()# predictpredicted_class=g_train.predict(g_test)# lets check the performance of the non-ensemble classifierfromsklearn.metricsimportaccuracy_scoreacc=accuracy_score(g_test.true_class,predicted_class)print("Accuracy of ensemble classifier:",acc)fromsklearn.metricsimportbalanced_accuracy_scoreacc_bal=balanced_accuracy_score(g_test.true_class,predicted_class)print("Balanced accuracy of ensemble classifier:",acc_bal)fromsklearn.metrics.clusterimportnormalized_mutual_info_scorenmi=normalized_mutual_info_score(g_test.true_class,predicted_class)print("NMI of ensemble classifier:",nmi)Federated Ensemble Learning with Ensemble-GNNMethod 1The models are collected from the clients and predictions are aggregated via Majority Vote.Example 3# import GNNSubNetfromGNNSubNetimportGNNSubNetasgnn# import ensemble_gnnimportensemble_gnnasegnn# location of the filesloc="/home/bastian/GitHub/GNN-SubNet/TCGA"# PPI networkppi=f'{loc}/KIDNEY_RANDOM_PPI.txt'# single-omic featuresfeats=[f'{loc}/KIDNEY_RANDOM_mRNA_FEATURES.txt']# multi-omic features#feats = [f'{loc}/KIDNEY_RANDOM_mRNA_FEATURES.txt', f'{loc}/KIDNEY_RANDOM_Methy_FEATURES.txt']# outcome classtarg=f'{loc}/KIDNEY_RANDOM_TARGET.txt'# Load the multi-omics datag=gnn.GNNSubNet(loc,ppi,feats,targ)# train-test split: 80-20g_train,g_test=egnn.split(g,0.8)# 50 - 50 split of the training datapartie1,partie2=egnn.split(g_train,0.5)# create local ensemble classier of client 1p1=egnn.ensemble(partie1,niter=1)# train local ensemble classier of client 1p1.train()#p1.grow(10) # greedy step# create local ensemble classier of client 2p2=egnn.ensemble(partie2,niter=1)# train local ensemble classier of client 2p2.train()#p2.grow(10) # greedy step# Lets check the client-specific performancesfromsklearn.metricsimportbalanced_accuracy_scorefromsklearn.metricsimportaccuracy_score## client 1p1_predicted_class=p1.predict(g_test)acc1=accuracy_score(g_test.true_class,p1_predicted_class)acc1_bal=balanced_accuracy_score(g_test.true_class,p1_predicted_class)## client 2p2_predicted_class=p2.predict(g_test)acc2=accuracy_score(g_test.true_class,p2_predicted_class)acc2_bal=balanced_accuracy_score(g_test.true_class,p2_predicted_class)print("\n-----------")print("Balanced accuracy of client 1 ensemble classifier:",acc1_bal)print("Accuracy of client 1 ensemble classifier:",acc1)print("\n-----------")print("Balanced accuracy of client 2 ensemble classifier:",acc2_bal)print("Accuracy of client 2 ensemble classifier:",acc2)fromsklearn.metrics.clusterimportnormalized_mutual_info_scorenmi1=normalized_mutual_info_score(g_test.true_class,p1_predicted_class)nmi2=normalized_mutual_info_score(g_test.true_class,p2_predicted_class)print("\n-----------")print("NMI of client 1 ensemble classifier:",nmi1)print("\n-----------")print("NMI of client 2 ensemble classifier:",nmi2)# aggregate the models from each client# without sharing any dataglobal_model=egnn.aggregate([p1,p2])# Make predictions using the global model via Majority Votepredicted_class=global_model.predict(g_test)# Lets check the performance of the federated ensemble classifierfromsklearn.metricsimportbalanced_accuracy_scorefromsklearn.metricsimportaccuracy_scoreacc=accuracy_score(g_test.true_class,predicted_class)acc_bal=balanced_accuracy_score(g_test.true_class,predicted_class)print("\n-----------")print("Balanced accuracy of global ensemble classifier:",acc_bal)print("Accuracy of global ensemble classifier:",acc)fromsklearn.metrics.clusterimportnormalized_mutual_info_scorenmi=normalized_mutual_info_score(g_test.true_class,predicted_class)print("\n-----------")print("NMI of global ensemble classifier:",nmi)Example 4: Example code for multiple parties#!/usr/bin/env python3fromGNNSubNetimportGNNSubNetasgnnimportensemble_gnnasegnnimportcopy# location of the filesloc="/home/bastian/GitHub/GNN-SubNet/TCGA"# PPI networkppi=f'{loc}/KIDNEY_RANDOM_PPI.txt'# single-omic features#feats = [f'{loc}/KIDNEY_RANDOM_Methy_FEATURES.txt']# multi-omic featuresfeats=[f'{loc}/KIDNEY_RANDOM_mRNA_FEATURES.txt',f'{loc}/KIDNEY_RANDOM_Methy_FEATURES.txt']# outcome classtarg=f'{loc}/KIDNEY_RANDOM_TARGET.txt'# Load the multi-omics datag=gnn.GNNSubNet(loc,ppi,feats,targ)# Number of partiesparties:int=4# train-test split: 80:20g_train,g_test=egnn.split(g,0.8)# Split data equaliy with split_n and train single modelslearned_ensembles:list=[]forpartyinegnn.split_n(g,parties):pn=egnn.ensemble(party,niter=1)pn.train()#pn.grow(10)learned_ensembles.append(pn)# Aggregate all trained ensemble modelsglobal_model=egnn.aggregate(learned_ensembles)# lets check the performance of the federated ensemble classifierfromsklearn.metricsimportbalanced_accuracy_scorefromsklearn.metricsimportaccuracy_scorefromsklearn.metricsimportmatthews_corrcoefscores=[("Acc",accuracy_score),("BalAcc",balanced_accuracy_score),("MCC",matthews_corrcoef)]predicted_class=global_model.predict(g_test)print("# Global model performance")print(", ".join(["%s:%.3f"%(c[0],c[1](g_test.true_class,predicted_class))forcinscores]))forpartyinrange(0,len(learned_ensembles)):test=copy.deepcopy(g_test)pn_predicted_class=learned_ensembles[party].predict(test)print("# Party%dmodel performance"%(party+1))print(", ".join(["%s:%.3f"%(c[0],c[1](test.true_class,pn_predicted_class))forcinscores]))Method 2 (in progress ...)No models and data are shared, only the topologies of the relevant subnetworks. Coming soon ...# import GNNSubNetfromGNNSubNetimportGNNSubNetasgnn# import ensemble_gnnimportensemble_gnnasegnn# location of the filesloc="/home/bastian/GitHub/GNN-SubNet/TCGA"# PPI networkppi=f'{loc}/KIDNEY_RANDOM_PPI.txt'# single-omic featuresfeats=[f'{loc}/KIDNEY_RANDOM_mRNA_FEATURES.txt']# multi-omic features#feats = [f'{loc}/KIDNEY_RANDOM_mRNA_FEATURES.txt', f'{loc}/KIDNEY_RANDOM_Methy_FEATURES.txt']# outcome classtarg=f'{loc}/KIDNEY_RANDOM_TARGET.txt'# Load the multi-omics datag=gnn.GNNSubNet(loc,ppi,feats,targ)partie1,partie2=egnn.split(g)# train local ensemble classierp1=egnn.ensemble(partie1,niter=1)p1.train()#p1.grow(10)p2=egnn.ensemble(partie2,niter=1)p2.train()#p2.grow(10)# Partie1 suggests a subnetsubnet=p1.propose()# Partie2 now checks whether this subnet is useful# if yes, it will locally be included# p2.check(subnet)MiscellaneousLogo made by Adobe Express Logo Maker:https://www.adobe.com/express/create/logoCitationhttps://academic.oup.com/bioinformatics/article/38/Supplement_2/ii120/6702000Bibtex@article{pfeifer2022gnn,
  title={{GNN-SubNet}: Disease subnetwork detection with explainable graph neural networks},
  author={Pfeifer, Bastian and Saranti, Anna and Holzinger, Andreas},
  journal={Bioinformatics},
  volume={38},
  number={Supplement\_2},
  pages={ii120--ii126},
  year={2022},
  publisher={Oxford University Press}
}