|
| 1 | +from torchmetrics import MetricCollection |
| 2 | +from torchmetrics.classification import MulticlassAUROC, MulticlassAccuracy, MulticlassF1Score |
| 3 | + |
| 4 | +from hypertorch.data import AlgebraDataset, DataLoader, Node2VecEnricher |
| 5 | +from hypertorch.nc import Node2VecGCNNcConfig, Node2VecGCNEncoderConfig, Node2VecGCNNcModule |
| 6 | +from hypertorch.train import MultiModelTrainer |
| 7 | +from hypertorch.types import ModelConfig |
| 8 | +from hypertorch.utils import node_labels_from_node_degrees |
| 9 | + |
| 10 | + |
| 11 | +if __name__ == "__main__": |
| 12 | + verbose = False |
| 13 | + num_workers = 8 |
| 14 | + num_features = 32 |
| 15 | + num_classes = 3 |
| 16 | + metrics = MetricCollection( |
| 17 | + { |
| 18 | + "auc": MulticlassAUROC(num_classes=num_classes), |
| 19 | + "accuracy": MulticlassAccuracy(num_classes=num_classes), |
| 20 | + "f1": MulticlassF1Score(num_classes=num_classes), |
| 21 | + } |
| 22 | + ) |
| 23 | + |
| 24 | + print("Loading and preparing dataset...") |
| 25 | + |
| 26 | + dataset = AlgebraDataset(sampling_strategy="node", task="node-classification") |
| 27 | + dataset.hdata.y = node_labels_from_node_degrees( |
| 28 | + node_incidences=dataset.hdata.hyperedge_index[0], |
| 29 | + num_nodes=dataset.hdata.num_nodes, |
| 30 | + num_classes=num_classes, |
| 31 | + ) |
| 32 | + |
| 33 | + train_dataset, val_dataset, test_dataset = dataset.split( |
| 34 | + ratios=[0.7, 0.1, 0.2], |
| 35 | + node_space_setting="transductive", |
| 36 | + shuffle=True, |
| 37 | + seed=42, |
| 38 | + ) |
| 39 | + |
| 40 | + print("Computing Node2Vec embeddings from the train graph...") |
| 41 | + |
| 42 | + node2vec_enricher = Node2VecEnricher( |
| 43 | + num_features=num_features, |
| 44 | + context_size=10, |
| 45 | + walk_length=20, |
| 46 | + num_walks_per_node=10, |
| 47 | + num_negative_samples=1, |
| 48 | + num_nodes=dataset.hdata.num_nodes, |
| 49 | + num_epochs=10, |
| 50 | + learning_rate=0.01, |
| 51 | + batch_size=128, |
| 52 | + sparse=False, |
| 53 | + verbose=verbose, |
| 54 | + ) |
| 55 | + train_dataset.enrich_node_features( |
| 56 | + enricher=node2vec_enricher, |
| 57 | + enrichment_mode="replace", |
| 58 | + ) |
| 59 | + val_dataset.enrich_node_features_from(train_dataset) |
| 60 | + test_dataset.enrich_node_features_from(train_dataset) |
| 61 | + |
| 62 | + print("Creating dataloaders...") |
| 63 | + |
| 64 | + train_loader = DataLoader( |
| 65 | + train_dataset, |
| 66 | + batch_size=128, |
| 67 | + shuffle=False, |
| 68 | + num_workers=num_workers, |
| 69 | + persistent_workers=True, |
| 70 | + ) |
| 71 | + val_loader = DataLoader( |
| 72 | + val_dataset, |
| 73 | + batch_size=128, |
| 74 | + shuffle=False, |
| 75 | + num_workers=num_workers, |
| 76 | + persistent_workers=True, |
| 77 | + ) |
| 78 | + test_loader = DataLoader( |
| 79 | + test_dataset, |
| 80 | + sample_full_hypergraph=True, |
| 81 | + shuffle=False, |
| 82 | + num_workers=num_workers, |
| 83 | + persistent_workers=True, |
| 84 | + ) |
| 85 | + |
| 86 | + gcn_config: Node2VecGCNNcConfig = { |
| 87 | + "out_channels": num_classes, |
| 88 | + "hidden_channels": num_features, |
| 89 | + "num_layers": 2, |
| 90 | + "drop_rate": 0.1, |
| 91 | + "bias": True, |
| 92 | + "improved": False, |
| 93 | + "add_self_loops": True, |
| 94 | + "normalize": True, |
| 95 | + "cached": False, |
| 96 | + "graph_reduction_strategy": "clique_expansion", |
| 97 | + "num_nodes": dataset.hdata.num_nodes, |
| 98 | + } |
| 99 | + precomputed_config: Node2VecGCNEncoderConfig = { |
| 100 | + "mode": "precomputed", |
| 101 | + "num_features": num_features, |
| 102 | + "node2vec_config": {}, |
| 103 | + "gcn_config": gcn_config, |
| 104 | + } |
| 105 | + |
| 106 | + node2vecgcn_precomputed = Node2VecGCNNcModule( |
| 107 | + classifier_config=precomputed_config, |
| 108 | + lr=0.001, |
| 109 | + weight_decay=0.0, |
| 110 | + metrics=metrics, |
| 111 | + ) |
| 112 | + |
| 113 | + node2vecgcn_joint = Node2VecGCNNcModule( |
| 114 | + classifier_config={ |
| 115 | + "mode": "joint", |
| 116 | + "num_features": num_features, |
| 117 | + "node2vec_config": { |
| 118 | + "context_size": 10, |
| 119 | + "walk_length": 20, |
| 120 | + "num_walks_per_node": 10, |
| 121 | + "p": 1.0, |
| 122 | + "q": 1.0, |
| 123 | + "num_negative_samples": 1, |
| 124 | + "train_hyperedge_index": train_dataset.hdata.hyperedge_index, |
| 125 | + "num_nodes": dataset.hdata.num_nodes, |
| 126 | + "graph_reduction_strategy": "clique_expansion", |
| 127 | + "random_walk_batch_size": 128, |
| 128 | + "node2vec_loss_weight": 0.4, |
| 129 | + }, |
| 130 | + "gcn_config": gcn_config, |
| 131 | + }, |
| 132 | + lr=0.001, |
| 133 | + weight_decay=0.0, |
| 134 | + metrics=metrics, |
| 135 | + ) |
| 136 | + |
| 137 | + configs = [ |
| 138 | + ModelConfig( |
| 139 | + name="node2vecgcn-precomputed", |
| 140 | + version="node-classification", |
| 141 | + model=node2vecgcn_precomputed, |
| 142 | + train_dataloader=train_loader, |
| 143 | + val_dataloader=val_loader, |
| 144 | + test_dataloader=test_loader, |
| 145 | + ), |
| 146 | + ModelConfig( |
| 147 | + name="node2vecgcn-joint", |
| 148 | + version="node-classification", |
| 149 | + model=node2vecgcn_joint, |
| 150 | + train_dataloader=train_loader, |
| 151 | + val_dataloader=val_loader, |
| 152 | + test_dataloader=test_loader, |
| 153 | + ), |
| 154 | + ] |
| 155 | + |
| 156 | + print("Starting training and evaluation...") |
| 157 | + |
| 158 | + with MultiModelTrainer( |
| 159 | + model_configs=configs, |
| 160 | + max_epochs=60, |
| 161 | + accelerator="auto", |
| 162 | + log_every_n_steps=1, |
| 163 | + enable_checkpointing=False, |
| 164 | + devices=1, |
| 165 | + test_devices=1, |
| 166 | + enable_model_summary=True, |
| 167 | + ) as trainer: |
| 168 | + trainer.fit_all(train_dataloader=train_loader, val_dataloader=val_loader, verbose=True) |
| 169 | + trainer.test_all(dataloader=test_loader, verbose=True) |
| 170 | + |
| 171 | + print("Complete!") |
0 commit comments