diff options
Diffstat (limited to 'experiments/run_combo_20seeds.py')
| -rw-r--r-- | experiments/run_combo_20seeds.py | 72 |
1 files changed, 36 insertions, 36 deletions
diff --git a/experiments/run_combo_20seeds.py b/experiments/run_combo_20seeds.py index 1598964..acc8846 100644 --- a/experiments/run_combo_20seeds.py +++ b/experiments/run_combo_20seeds.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 -"""Task 2ceadaa7: GRAFT + Forward Tricks combo experiments (20 seeds). +"""Task 2ceadaa7: KAFT + Forward Tricks combo experiments (20 seeds). -Combos: GRAFT+ResGCN, GRAFT+DropEdge, GRAFT+PairNorm, GRAFT+JKNet +Combos: KAFT+ResGCN, KAFT+DropEdge, KAFT+PairNorm, KAFT+JKNet Each compared to: BP, forward_trick_only, GRAFT_only, combo """ @@ -12,7 +12,7 @@ import json import os from scipy import stats as scipy_stats from src.data import load_dataset, spmm, build_normalized_adj -from src.trainers import BPTrainer, GraphGrAPETrainer, _FeedbackTrainerBase +from src.trainers import BPTrainer, KAFTTrainer, _FeedbackTrainerBase from run_deep_baselines import ResGCNTrainer, JKNetTrainer from run_dropedge import BPDropEdgeTrainer from run_pairnorm_baseline import BPPairNormTrainer, pairnorm @@ -28,10 +28,10 @@ grape_extra = dict(diffusion_alpha=0.5, diffusion_iters=10, # ═══════════════════════════════════════════════════════════════════════════ -# GRAFT + ResGCN combo (fixed version) +# KAFT + ResGCN combo (fixed version) # ═══════════════════════════════════════════════════════════════════════════ -class GRAFTResGCN(GraphGrAPETrainer): - """GRAFT backward + ResGCN forward (skip connections).""" +class GRAFTResGCN(KAFTTrainer): + """KAFT backward + ResGCN forward (skip connections).""" def forward(self): X = self.data['X'] @@ -57,10 +57,10 @@ class GRAFTResGCN(GraphGrAPETrainer): # ═══════════════════════════════════════════════════════════════════════════ -# GRAFT + DropEdge combo +# KAFT + DropEdge combo # ═══════════════════════════════════════════════════════════════════════════ -class GRAFTDropEdge(GraphGrAPETrainer): - """GRAFT backward + DropEdge forward (random edge dropping).""" +class GRAFTDropEdge(KAFTTrainer): + """KAFT backward + DropEdge forward (random edge dropping).""" def __init__(self, *args, drop_rate=0.5, **kwargs): super().__init__(*args, **kwargs) @@ -81,9 +81,9 @@ class GRAFTDropEdge(GraphGrAPETrainer): ).coalesce() def forward(self): - # DropEdge only in forward pass, GRAFT backward uses original A_hat + # DropEdge only in forward pass, KAFT backward uses original A_hat self.data['A_hat'] = self._drop_edges() - result = super().forward() # uses GraphGrAPETrainer.forward() + result = super().forward() # uses KAFTTrainer.forward() self.data['A_hat'] = self._A_hat_orig return result @@ -93,10 +93,10 @@ class GRAFTDropEdge(GraphGrAPETrainer): # ═══════════════════════════════════════════════════════════════════════════ -# GRAFT + PairNorm combo +# KAFT + PairNorm combo # ═══════════════════════════════════════════════════════════════════════════ -class GRAFTPairNorm(GraphGrAPETrainer): - """GRAFT backward + PairNorm forward (center & scale normalization).""" +class GRAFTPairNorm(KAFTTrainer): + """KAFT backward + PairNorm forward (center & scale normalization).""" def __init__(self, *args, pn_scale=1.0, **kwargs): super().__init__(*args, **kwargs) @@ -138,13 +138,13 @@ class GRAFTPairNorm(GraphGrAPETrainer): # ═══════════════════════════════════════════════════════════════════════════ -# GRAFT + JKNet combo +# KAFT + JKNet combo # ═══════════════════════════════════════════════════════════════════════════ -class GRAFTJKNet(GraphGrAPETrainer): - """GRAFT backward + JKNet forward (jumping knowledge max-pool). +class GRAFTJKNet(KAFTTrainer): + """KAFT backward + JKNet forward (jumping knowledge max-pool). Note: JKNet changes the output architecture. We max-pool hidden layers - and project to num_classes. GRAFT backward operates on hidden layers + and project to num_classes. KAFT backward operates on hidden layers as usual; the JK projection is treated as the output layer. """ @@ -187,7 +187,7 @@ class GRAFTJKNet(GraphGrAPETrainer): def _update_weights(self, inter, E0, deltas): """Override to handle JK projection separately.""" - # Update hidden layers using GRAFT feedback as usual + # Update hidden layers using KAFT feedback as usual X = self.data['X'] Hs = inter['Hs'] H0 = inter['H0'] @@ -265,7 +265,7 @@ def main(): per_seed_data = {} # Reuse existing per-seed data from other experiments - # BP, ResGCN, GRAFT from resgcn_20seeds + # BP, ResGCN, KAFT from resgcn_20seeds try: with open('results/resgcn_20seeds/per_seed_data.json') as f: resgcn_cache = json.load(f) @@ -292,11 +292,11 @@ def main(): 'DropEdge': (BPDropEdgeTrainer, {'drop_rate': 0.5}), 'PairNorm': (BPPairNormTrainer, {'pn_scale': 1.0}), 'JKNet': (JKNetTrainer, {}), - 'GRAFT': (GraphGrAPETrainer, grape_extra), - 'GRAFT+ResGCN': (GRAFTResGCN, grape_extra), - 'GRAFT+DropEdge': (GRAFTDropEdge, {**grape_extra, 'drop_rate': 0.5}), - 'GRAFT+PairNorm': (GRAFTPairNorm, {**grape_extra, 'pn_scale': 1.0}), - 'GRAFT+JKNet': (GRAFTJKNet, grape_extra), + 'KAFT': (KAFTTrainer, grape_extra), + 'KAFT+ResGCN': (GRAFTResGCN, grape_extra), + 'KAFT+DropEdge': (GRAFTDropEdge, {**grape_extra, 'drop_rate': 0.5}), + 'KAFT+PairNorm': (GRAFTPairNorm, {**grape_extra, 'pn_scale': 1.0}), + 'KAFT+JKNet': (GRAFTJKNet, grape_extra), } datasets_cfg = { @@ -330,7 +330,7 @@ def main(): cached = resgcn_cache[f"{ds_name}_BP"].get(sk) elif mname == 'ResGCN' and f"{ds_name}_ResGCN" in resgcn_cache: cached = resgcn_cache[f"{ds_name}_ResGCN"].get(sk) - elif mname == 'GRAFT' and f"{ds_name}_GRAFT" in resgcn_cache: + elif mname == 'KAFT' and f"{ds_name}_GRAFT" in resgcn_cache: cached = resgcn_cache[f"{ds_name}_GRAFT"].get(sk) elif mname == 'DropEdge': de_key = f"{ds_name}_gcn_L6" @@ -369,14 +369,14 @@ def main(): for ds in ['Cora', 'CiteSeer', 'DBLP']: print(f"\n--- {ds} GCN L=6 lr=0.01 ---") - print(f"{'Method':<18} {'Mean±Std':>12} {'vs GRAFT':>18} {'vs FwdTrick':>18}") + print(f"{'Method':<18} {'Mean±Std':>12} {'vs KAFT':>18} {'vs FwdTrick':>18}") print("-" * 70) - # Get GRAFT accs for comparison + # Get KAFT accs for comparison gr_accs = np.array([per_seed_data[f"{ds}_GRAFT"][str(s)] for s in SEEDS]) * 100 for mname in ['BP', 'ResGCN', 'DropEdge', 'PairNorm', 'JKNet', - 'GRAFT', 'GRAFT+ResGCN', 'GRAFT+DropEdge', 'GRAFT+PairNorm', 'GRAFT+JKNet']: + 'KAFT', 'KAFT+ResGCN', 'KAFT+DropEdge', 'KAFT+PairNorm', 'KAFT+JKNet']: key = f"{ds}_{mname}" if key not in per_seed_data or len(per_seed_data[key]) < 20: print(f" {mname:<16} MISSING ({len(per_seed_data.get(key, {}))} seeds)") @@ -387,8 +387,8 @@ def main(): results[key] = {'mean': float(m), 'std': float(s), 'accs': accs.tolist()} - # Paired t-test vs GRAFT - if mname != 'GRAFT': + # Paired t-test vs KAFT + if mname != 'KAFT': t_stat, p_val = scipy_stats.ttest_rel(accs, gr_accs) delta = m - gr_accs.mean() sig = '***' if p_val < 0.001 else ('**' if p_val < 0.01 else ('*' if p_val < 0.05 else 'ns')) @@ -402,8 +402,8 @@ def main(): # Paired t-test vs forward trick only fwd_map = { - 'GRAFT+ResGCN': 'ResGCN', 'GRAFT+DropEdge': 'DropEdge', - 'GRAFT+PairNorm': 'PairNorm', 'GRAFT+JKNet': 'JKNet' + 'KAFT+ResGCN': 'ResGCN', 'KAFT+DropEdge': 'DropEdge', + 'KAFT+PairNorm': 'PairNorm', 'KAFT+JKNet': 'JKNet' } if mname in fwd_map: fwd_key = f"{ds}_{fwd_map[mname]}" @@ -431,8 +431,8 @@ def main(): for ds in ['Cora', 'CiteSeer', 'DBLP']: print(f"\n{ds}:") gr_m = results.get(f"{ds}_GRAFT", {}).get('mean', 0) - for combo, fwd in [('GRAFT+ResGCN', 'ResGCN'), ('GRAFT+DropEdge', 'DropEdge'), - ('GRAFT+PairNorm', 'PairNorm'), ('GRAFT+JKNet', 'JKNet')]: + for combo, fwd in [('KAFT+ResGCN', 'ResGCN'), ('KAFT+DropEdge', 'DropEdge'), + ('KAFT+PairNorm', 'PairNorm'), ('KAFT+JKNet', 'JKNet')]: ck = f"{ds}_{combo}" fk = f"{ds}_{fwd}" if ck in results and fk in results: @@ -442,7 +442,7 @@ def main(): vs_fw = results[ck].get(f'vs_{fwd}', {}) better_than_both = c_m > gr_m and c_m > f_m marker = "✓ ADDITIVE" if better_than_both else "✗ not additive" - print(f" {combo}: {c_m:.1f} | GRAFT={gr_m:.1f} | {fwd}={f_m:.1f} → {marker}") + print(f" {combo}: {c_m:.1f} | KAFT={gr_m:.1f} | {fwd}={f_m:.1f} → {marker}") # Save with open(os.path.join(OUT_DIR, 'results.json'), 'w') as f: |
