Paper · 2022-01-17 · cs.LG
Towards Unsupervised Deep Graph Structure Learning
SUBLIME learns a graph's adjacency without labels. A learner (full-matrix or metric-based) builds the graph, and a node-level contrastive loss, computed with a GCN encoder, pulls it toward an anchor graph that is slowly updated toward the learned one. Author-reported: 84.2% Cora node classification in structure refinement, and top results on several benchmarks.
Graph structure learningContrastive learningGraph neural networkData augmentationgraph-learningself-supervisedcontrastive-learning
arXivPDFCodeMarkdown16 min read
AI-compiled by claude-haiku-5-5 on 2026-11-03, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
Many graph models assume the connections between items are already known. Often they are not, or the known ones are noisy. This paper asks whether a model can work out useful connections by itself, without any labels.
The method keeps two versions of the connection map. The first is the one the model is learning. The second is a reference copy that starts from the data and is nudged, slowly and over time, toward the learned map. A training signal asks the two versions to agree about each item after both have been randomly perturbed. The learned map therefore improves against a reference that keeps improving too, which the authors argue avoids copying the original noise and avoids over-fitting the starting graph.
The result is a graph that can be used by a normal graph neural network for classification or clustering. The authors report that it competes with methods that do use labels.
TL;DR
SUBLIME is an unsupervised graph structure learning framework. A learner (full-matrix, attentive, MLP or GNN) produces an adjacency matrix, which is sparsified, symmetrised and normalised. A two-view contrastive loss, computed with a 2-layer GCN encoder, matches each node across the learned graph and an anchor graph. The anchor is updated by an exponential moving average toward the learned graph. Author-reported results are competitive with supervised structure-learning baselines on eight benchmarks §5.2.
Problem
Graph neural networks depend on a given graph. Noisy edges hurt them, and they cannot be applied directly to unstructured data §1. Deep graph structure learning (GSL) methods fix this by learning the graph jointly with a GNN, but the existing ones are supervised: they need node labels, which can be biased toward some edge types and limit the tasks they can serve §1.
The paper defines two unsupervised tasks, with no labels used for structure optimisation §3:
- Structure inference: from features X alone, learn an adjacency S in [0, 1]^(n x n) §3, Definition 1.
- Structure refinement: given a noisy adjacency A and features X, learn a refined S §3, Definition 2.
The working hypothesis is that a downstream model fed with S performs better than one fed with the original input §3.
Method
The framework has two modules: a graph structure learning module (learner and post-processor) and a structure bootstrapping contrastive learning module §4, fig. 1.
Graph learner §4.1. The learner produces a sketched matrix S~ from parameters ω. Four variants are defined:
- FGP learner eq. 1: one free parameter per matrix entry, S~ = sigma(Omega) with Omega in R^(n x n). Each edge is assumed independent. It has O(n^2) memory and parameters app. B.
- Metric learners eq. 2: an embedding network h gives node embeddings E,
and a non-parametric similarity phi (cosine or Minkowski) gives S~ = phi(E).
Three embedding networks are defined:
- Attentive eq. 3: each layer multiplies features elementwise by a learned vector w, then applies a nonlinearity. Despite the GAT-like description, no attention coefficients are computed.
- MLP eq. 4: each layer is a dense matrix Omega^(l) followed by a nonlinearity.
- GNN eq. 5: GCN layers on features and the original adjacency A with self-loops (A~ = A + I). The paper says it is only used for structure refinement §4.1.
The learner is chosen per dataset. For graphs under 3000 nodes, FGP. For larger graphs with high-dimensional features, attentive. For large graphs with low-dimensional features, MLP. GNN can be added for refinement app. B.
Post-processor §4.2. Four steps turn S~ into S, applied in this order:
- Sparsification eq. 6: keep each row's top-k entries, set the rest to 0. FGP is not sparsified, to keep gradients flowing. For large graphs, the kNN search is restricted to a batch of nodes §4.2, citing SLAPS.
- Activation sigma_q eq. 7: ReLU for metric learners; ELU for FGP.
- Symmetrisation eq. 7: average the matrix with its transpose.
- Normalisation eq. 8: symmetric degree normalisation, D^(-1/2) S D^(-1/2), with D the degree matrix of the symmetrised matrix.
Two views and augmentation §4.3.1, §4.3.2.
- Learner view: G_l = (S, X). It is initialised as the kNN graph on the features. For FGP the kNN entries start at 1 and the rest at 0. For attentive, every weight starts at 1. For MLP and GNN, the weight matrices start as identity §4.3.1.
- Anchor view: G_a = (A_a, X). For refinement A_a starts as the original A. For inference A_a starts as the identity I. It is not trained by gradient descent but by the bootstrapping rule below §4.3.1.
- Augmentation: feature masking zeroes a random subset of feature dimensions with a shared mask across nodes eq. 9. Edge dropping removes entries of the adjacency with an independent mask eq. 10. The learner and anchor views use different feature-masking rates p_l and p_a, and the same edge-dropping rate eq. 11, §4.3.2.
Node-level contrastive learning §4.3.3. A GCN encoder f_theta with L1 = 2 layers produces H_l and H_a eq. 12. A projector g_phi with L2 = 2 MLP layers produces Z_l and Z_a eq. 13. The loss is a symmetric NT-Xent over cosine similarities with temperature t, matching node i in one view to node i in the other eq. 14:
\mathcal{L} = \frac{1}{2n} \sum_{i=1}^{n} \Big[ \ell(z_{l,i},z_{a,i}) + \ell(z_{a,i},z_{l,i}) \Big], \quad \ell(z_{l,i},z_{a,i}) = \log\frac{e^{\mathrm{sim}(z_{l,i},z_{a,i})/t}}{\sum_{k=1}^{n} e^{\mathrm{sim}(z_{l,i},z_{a,k})/t}} eq. 14
The equation is printed without a leading minus sign. Minimising it as printed would maximise agreement. The released code uses the negative log, and the Pitfalls and Paper vs code sections explain how the code's version also differs in the denominator.
Structure bootstrapping §4.4. With a fixed anchor, the learner can copy the anchor's noise, lose guidance once the anchor is learned, and over-fit the anchor. So the anchor is moved every c iterations toward the learned graph, with decay tau in [0, 1]:
\mathbf{A}_a \leftarrow \tau \mathbf{A}_a + (1 - \tau) \mathbf{S} eq. 15
The paper says tau > 0.99 keeps training stable §4.4. Setting tau = 1 freezes the anchor §5.3.
Training §4.5, alg. 1 in app. D. All parameters (learner, encoder, projector) are updated jointly with Adam. After the update, the anchor is updated every c iterations. The paper notes a complexity of O(n^2 d + m d1 L1 + n d1^2 L1 + n d2^2 L2 + n k) §4.5, app. C. Scalability measures are: metric learners instead of FGP, locality-sensitive kNN, mini-batch contrast, and c >= 10 §4.5.
# SUBLIME training loop [alg. 1] [§4.5]
# Input: features (n, d); adjacency (n, n) or None; k; tau; c; epochs
# Output: learned adjacency (n, n)
def train(features, adjacency, k, tau, c, epochs):
# Anchor starts at the given graph, or at the identity [alg. 1]
if adjacency is not None:
anchor = adjacency
else:
anchor = identity(n) # (n, n)
# Learner, encoder and projector parameters [alg. 1]
learner, encoder, projector = init_parameters(features, k)
for epoch in range(epochs):
# Learned graph: sketch, then post-process [§4.1] [§4.2]
sketch = learner(features) # (n, n)
learned = post_process(sketch, k, learner.kind)
# Learner view: masked features and dropped edges [§4.3.2] [eq. 9-11]
learner_view = augment(learned, features, rate_learner, p_edge)
# Anchor view, with its own feature-mask rate [§4.3.2] [eq. 11]
anchor_view = augment(anchor, features, rate_anchor, p_edge)
# Encode, project, and compare the two views [§4.3.3] [eq. 12-13]
z_learner = projector(encoder(learner_view))
z_anchor = projector(encoder(anchor_view))
loss = contrastive_loss(z_learner, z_anchor, t=0.2) # [eq. 14]
# Joint gradient step over learner, encoder and projector [alg. 1]
gradient_step(loss, learner, encoder, projector)
# Bootstrap: anchor moves toward the learned graph [§4.4] [eq. 15]
if epoch % c == 0:
anchor = tau * anchor + (1 - tau) * learned
return learned# Post-processor [§4.2] [eq. 6-8]
# Input: sketch (n, n); k; kind in {fgp, attentive, mlp, gnn}
def post_process(sketch, k, kind):
# Sparsify to the top-k entries of each row; FGP is skipped [eq. 6]
if kind != "fgp":
sketch = keep_top_k_per_row(sketch, k) # (n, n)
# Activation: ELU for FGP, ReLU for metric learners [eq. 7]
if kind == "fgp":
sketch = elu(sketch)
else:
sketch = relu(sketch)
# Symmetrise by averaging with the transpose [eq. 7]
sketch = (sketch + sketch.T) / 2
# Symmetric degree normalisation [eq. 8]
degree = sketch.sum(axis=1) # (n,)
inv_sqrt = 1 / sqrt(degree)
return inv_sqrt[:, None] * sketch * inv_sqrt[None, :]# Augmentation for one view [§4.3.2] [eq. 9-10]
# Input: adjacency (n, n); features (n, d); rates in [0, 1]
def augment(adjacency, features, feat_rate, edge_rate):
# Feature masking: zero the same random subset of dimensions for all nodes
keep_dims = bernoulli(1 - feat_rate, size=d) # (d,)
masked_features = features * keep_dims # (n, d)
# Edge dropping: each entry removed independently with edge_rate
keep_edges = bernoulli(1 - edge_rate, size=(n, n)) # (n, n)
masked_adjacency = adjacency * keep_edges # (n, n)
return masked_adjacency, masked_featuresHyperparameters
Paper values come from the text and appendix. Script values come from the released run scripts, which are the settings behind the cited Cora and Citeseer runs. Where the paper states no value, the card says so.
| Name | Value | Where | Notes |
|---|---|---|---|
| learner variant | FGP (Cora), attentive (Citeseer), MLP (20news); GNN for refinement | app. B | assigned by node count and feature dimension |
| kNN k (grid) | {5, 10, ..., 40} | app. F | |
| kNN k (chosen) | Cora 30, Citeseer 20, Pubmed 15 | §5.4 | paper's sensitivity study |
| kNN k (Cora SI script) | 30 | cora_si.sh | |
| kNN k (Cora clustering script) | 20 | cora_clu.sh | not stated in paper |
| representation dim d1 (grid) | {16, 32, 64, 128, 256, 512} | app. F | |
| representation dim d1 (scripts) | 256 (Cora, Citeseer), 64 (Pubmed) | cora_si.sh pubmed_si.sh | |
| projection dim d2 | same grid as d1 | app. F | scripts use the same value as d1 |
| encoder layers L1 | 2 | app. F | |
| projector layers L2 | 2 | app. F | |
| embedding network layers L | 2 | app. F | |
| temperature t | 0.2, fixed | app. F model.py | |
| Adam learning rate (grid) | {0.01, 0.001, 0.0001} | app. F | |
| Adam learning rate (Cora SI) | 0.01 | cora_si.sh | |
| weight decay | not stated in paper | cora_si.sh | 0.0 in all released scripts |
| training epochs | not stated in paper | cora_si.sh | 4000 (Cora SI), 2500 (Cora clustering), 1000 (Citeseer), 2000 (Pubmed SI) |
| feature mask p^(x) (grid) | 0.1 to 0.9 | app. F | |
| feature mask p^(x)_a (sensitivity) | 0.6 to 0.8 best on Cora | §5.4 | |
| feature mask p^(x)_l (sensitivity) | robust in [0, 0.7] | §5.4 | |
| feature mask learner / anchor (Cora SI) | 0.5 / 0.7 | cora_si.sh | |
| edge drop p^(a) (grid) | {0, 0.25, 0.5, 0.75} | app. F | |
| edge drop p^(a) (good range) | 0.2 to 0.65 | app. G | Cora, Citeseer, Pubmed |
| edge drop p^(a) (Cora SI) | 0.5 | cora_si.sh | |
| bootstrap decay tau (grid) | {0.99, 0.999, 0.9999, 0.99999, 1} | app. F | |
| bootstrap decay tau (best, Cora) | 0.9999 (Cora 84.2) | tab. 4 | |
| bootstrap decay tau (released) | 1 (Cora SI, Pubmed SI); 0.9999 (Citeseer SI, Cora SR, Cora clustering) | cora_si.sh | tau = 1 disables bootstrapping |
| bootstrap interval c | c >= 10 (scalability) | §4.5 | scripts use 0 (every step) except Pubmed SR at 50 |
| contrastive mini-batch size | not stated in paper | pubmed_si.sh | 2000 on Pubmed |
| eval GCN (most datasets) | 2 layers, 32 hidden | app. F | |
| eval GCN (ogbn-arxiv) | 3 layers, 256 hidden | app. F | not in released code |
| eval seeds (classification) | 0, 1, 2, 3, 4; averaged over 5 rounds | app. F | |
| eval seed (clustering) | 0; K-means 10 runs | app. F | |
| eval GCN dropout (Cora SI vs Cora SR) | dropedge 0.25 vs 0.75 | cora_si.sh cora_sr.sh | paper gives no value |
| hardware | Intel Xeon 4214R, four Quadro RTX 6000 | app. F | |
| software | PyTorch 1.7.1, DGL 0.7.1 | app. F |
Dataset statistics (nodes, edges, classes, features, label rate) are in tab. 7. Dataset splits follow prior work app. E.
Results
Author-reported. Setup: eight benchmarks, five seeds for classification,
GCN evaluators trained on the learned graph, baselines reproduced from their
official code or copied from their papers app. F. Bold and underline markers
from the paper are omitted. The Available data column lists what each method
uses during graph learning: X features, Y labels, A_knn kNN graph, A the
original graph tab. 1, tab. 2.
Node classification, structure inference tab. 1
| Available data | Method | Cora | Citeseer | Pubmed | ogbn-arxiv | Wine | Cancer | Digits | 20news |
|---|---|---|---|---|---|---|---|---|---|
| - | LR | 60.8±0.0 | 62.2±0.0 | 72.4±0.0 | 52.5±0.0 | 92.1±1.3 | 93.3±0.5 | 85.5±1.5 | 42.7±1.7 |
| - | Linear SVM | 58.9±0.0 | 58.3±0.0 | 72.7±0.1 | 51.8±0.0 | 93.9±1.6 | 90.6±4.5 | 87.1±1.8 | 40.3±1.4 |
| - | MLP | 56.1±1.6 | 56.7±1.7 | 71.4±0.0 | 54.7±0.1 | 89.7±1.9 | 92.9±1.2 | 36.3±0.3 | 38.6±1.4 |
| - | GCN (kNN) | 66.5±0.4 | 68.3±1.3 | 70.4±0.4 | 54.1±0.3 | 93.2±3.1 | 83.8±1.4 | 91.3±0.5 | 41.3±0.6 |
| - | GAT (kNN) | 66.2±0.5 | 70.0±0.6 | 69.6±0.5 | OOM | 91.5±2.4 | 95.1±0.8 | 91.4±0.1 | 45.0±1.2 |
| - | SAGE (kNN) | 66.1±0.7 | 68.0±1.6 | 68.7±0.2 | 55.2±0.4 | 87.4±0.8 | 93.7±0.3 | 91.6±0.7 | 45.4±0.4 |
| X, Y | LDS | 71.5±0.8 | 71.5±1.1 | OOM | OOM | 97.3±0.4 | 94.4±1.9 | 92.5±0.7 | 46.4±1.6 |
| X, Y, A_knn | GRCN | 69.6±0.2 | 70.4±0.3 | 70.6±0.1 | OOM | 96.6±0.4 | 95.4±0.6 | 92.8±0.2 | 41.8±0.2 |
| X, Y, A_knn | Pro-GNN | 69.2±1.4 | 69.8±1.7 | OOM | OOM | 95.1±1.5 | 96.5±0.1 | 93.9±1.9 | 45.7±1.4 |
| X, Y, A_knn | GEN | 69.1±0.7 | 70.7±1.1 | 70.7±0.9 | OOM | 96.9±1.0 | 96.8±0.4 | 94.1±0.4 | 47.1±0.3 |
| X, Y | IDGL | 70.9±0.6 | 68.2±0.6 | 70.1±1.3 | 55.0±0.2 | 98.1±1.1 | 95.1±1.0 | 93.2±0.9 | 48.5±0.6 |
| X, Y | SLAPS | 73.4±0.3 | 72.6±0.6 | 74.4±0.6 | 56.6±0.1 | 96.6±0.4 | 96.6±0.2 | 94.4±0.7 | 50.4±0.7 |
| A_knn | GDC | 68.1±1.2 | 68.8±0.8 | 68.4±0.4 | OOM | 96.1±1.0 | 95.9±0.4 | 92.6±0.5 | 46.4±0.9 |
| X | SLAPS-2s | 72.1±0.4 | 69.4±1.4 | 71.1±0.5 | 54.2±0.2 | 96.2±2.1 | 95.9±1.2 | 93.6±0.8 | 47.7±0.7 |
| X | SUBLIME | 73.0±0.6 | 73.1±0.3 | 73.8±0.6 | 55.5±0.1 | 98.2±1.6 | 97.2±0.2 | 94.3±0.4 | 49.2±0.6 |
Node classification, structure refinement tab. 2
| Available data | Method | Cora | Citeseer | Pubmed | ogbn-arxiv |
|---|---|---|---|---|---|
| - | GCN | 81.5 | 70.3 | 79.0 | 71.7±0.3 |
| - | GAT | 83.0±0.7 | 72.5±0.7 | 79.0±0.3 | OOM |
| - | SAGE | 77.4±1.0 | 67.0±1.0 | 76.6±0.8 | 71.5±0.3 |
| X, Y, A | LDS | 83.9±0.6 | 74.8±0.3 | OOM | OOM |
| X, Y, A | GRCN | 84.0±0.2 | 73.0±0.3 | 78.9±0.2 | OOM |
| X, Y, A | Pro-GNN | 82.1±0.4 | 71.3±0.4 | OOM | OOM |
| X, Y, A | GEN | 82.3±0.4 | 73.5±1.5 | 80.9±0.8 | OOM |
| X, Y, A | IDGL | 84.0±0.5 | 73.1±0.7 | 83.0±0.2 | 72.0±0.3 |
| A | GDC | 83.6±0.2 | 73.4±0.3 | 78.7±0.4 | OOM |
| X, A | SUBLIME | 84.2±0.5 | 73.5±0.6 | 81.0±0.6 | 71.8±0.3 |
Node clustering, structure refinement tab. 3
| Method | Cora C-ACC | Cora NMI | Cora F1 | Cora ARI | Citeseer C-ACC | Citeseer NMI | Citeseer F1 | Citeseer ARI |
|---|---|---|---|---|---|---|---|---|
| K-means | 50.0 | 31.7 | 37.6 | 23.9 | 54.4 | 31.2 | 41.3 | 28.5 |
| SC | 39.8 | 29.7 | 33.2 | 17.4 | 30.8 | 9.0 | 25.7 | 8.2 |
| GE | 30.1 | 5.9 | 23.0 | 4.6 | 29.3 | 5.7 | 21.3 | 4.3 |
| DW | 52.9 | 38.4 | 43.5 | 29.1 | 39.0 | 13.1 | 30.5 | 13.7 |
| DNGR | 41.9 | 31.8 | 34.0 | 14.2 | 32.6 | 18.0 | 30.0 | 4.3 |
| M-NMF | 42.3 | 25.6 | 32.0 | 16.1 | 33.6 | 9.9 | 25.5 | 7.0 |
| RMSC | 46.6 | 32.0 | 34.7 | 20.3 | 51.6 | 30.8 | 40.4 | 26.6 |
| TADW | 53.6 | 36.6 | 40.1 | 24.0 | 52.9 | 32.0 | 43.6 | 28.6 |
| VGAE | 59.2 | 40.8 | 45.6 | 34.7 | 39.2 | 16.3 | 27.8 | 10.1 |
| ARGA | 64.0 | 44.9 | 61.9 | 35.2 | 57.3 | 35.0 | 54.6 | 34.1 |
| MGAE | 68.1 | 48.9 | 53.1 | 56.5 | 66.9 | 41.6 | 52.6 | 42.5 |
| AGC | 68.9 | 53.7 | 65.6 | 44.8 | 67.0 | 41.1 | 62.5 | 41.5 |
| DAEGC | 70.4 | 52.8 | 68.2 | 49.6 | 67.2 | 39.7 | 63.6 | 41.0 |
| SUBLIME | 71.3 | 54.2 | 63.5 | 50.3 | 68.5 | 44.1 | 63.2 | 43.9 |
Bootstrap decay ablation, structure refinement tab. 4
| Dataset | tau = 1 | 0.99999 | 0.9999 | 0.999 | 0.99 |
|---|---|---|---|---|---|
| Cora | 82.1 | 83.2 | 84.2 | 82.4 | 70.9 |
| Citeseer | 71.9 | 72.6 | 73.5 | 73.4 | 72.6 |
| Pubmed | 80.1 | 80.3 | 81.0 | 80.8 | 80.5 |
The authors say the bootstrapping step adds about 1.5 points on average over tau = 1 §5.3. Their learning curves for Cora show a drop around 1500 iterations when tau = 1 §5.3, fig. 2.
Other results. Sensitivity to feature-masking rate and kNN size [§5.4, fig. 3]. Robustness to random edge deletion and addition on Cora, against GCN and Pro-GNN §5.5, fig. 4. Heatmaps of learned Cora edges, showing more intra-class edges than the original graph §5.6, fig. 5.
Paper vs code
Code: official repository at commit 93398db994f21bd2b03f15db414e1e03244144e9,
linked in §5.1 of the paper, MIT License LICENSE. Discrepancies
checked against model.py, utils.py, graph_learners.py, layers.py,
main.py, data_loader.py and the run scripts.
- Contrastive denominator. The paper's denominator sums over all k, including k = i eq. 14. The code subtracts the positive term from the column and row sums, so each loss is minus the log of positive over the negatives only model.py. Paper says: positive over all. Code does: positive over negatives. The code also uses the negative log, as standard NT-Xent does, while eq. 14 is printed without the minus.
- Edge dropout is rescaled. Eq. 10 gives A ⊙ M with no rescale. The code
uses
nn.Dropout(p)on the dense adjacency, which scales surviving entries by 1/(1 - p) model.py model.py. The sparse path also rescales layers.py. - Feature masking is a fixed count. Eq. 9 and §4.3.2 describe a Bernoulli mask per dimension. The code draws exactly floor(d · rate) dimensions without replacement and uses them for every node utils.py.
- Top-k keeps k + 1 entries. The metric learners keep k + 1 entries per row, so the node itself is counted as a neighbour graph_learners.py graph_learners.py graph_learners.py. Paper says top-k.
- FGP off-kNN entries are not zero. Parameters are initialised to 0 on kNN entries and to -6 elsewhere, and the activation is ELU + 1. This gives 1 for kNN entries and about exp(-6) ≈ 0.0025 for the rest, not exactly 0 [code: utils.py#L91-L96] graph_learners.py. The code uses ELU + 1, not plain ELU as in §4.2.
- Sparse path differs. In the sparse path (Pubmed), degree normalisation happens inside kNN construction before the ReLU, and the symmetrised pair is added as duplicate directed edges, not averaged graph_learners.py utils.py. This does not match eq. 7 and 8 literally.
- Last-layer activation. The attentive and MLP learners apply the nonlinearity between layers but not after the last one, while eq. 3 and 4 write it for each layer graph_learners.py graph_learners.py.
- MLP learner keeps biases. It sets the weights to identity, but the bias
of
nn.Linearis not reset graph_learners.py. - Encoder dropout. The encoder applies dropout after each hidden layer with rate 0.5 by default. The paper does not mention encoder dropout model.py main.py.
- Bootstrapping is off in released scripts.
taudefaults to 1, which disables the update main.py main.py. The Cora and Pubmed structure inference scripts use tau = 1 cora_si.sh pubmed_si.sh. Citeseer structure inference uses 0.9999 citeseer_si.sh. The paper's tau ablation is a refinement study §5.3. - Bootstrap interval. The default
cis 0, so the anchor updates every step, while §4.5 says c >= 10 main.py main.py. The Pubmed refinement script uses c = 50 pubmed_sr.sh. - Contrast batches are contiguous. Mini-batching splits node indices into consecutive blocks, and the shuffle is commented out main.py. Each block's loss is weighted by its size.
- GNN learner has no self-loops and a fixed anchor. The GNN learner uses the normalised input adjacency without adding I, and it holds the anchor from the start of training, so it does not see bootstrapped updates [code: graph_learners.py#L131-L143] main.py data_loader.py. No released script uses the GNN learner.
- Datasets missing. The code accepts only cora, citeseer and pubmed main.py. There is no loader for ogbn-arxiv, Wine, Cancer, Digits or 20news, so those rows cannot be reproduced from this release.
- Clustering reports the last evaluation. The printed clustering metrics are from the last evaluation step, not the best one main.py main.py.
- Clustering seed. The code runs one trial with seed 0 and 10 K-means restarts, which matches app. F main.py.
- Evaluator is retrained per evaluation step. A fresh GCN is trained on the learned graph at each evaluation epoch, with dropout set per script main.py cora_si.sh cora_sr.sh.
- Hyperparameter grid. The paper's grid goes up to 512 dimensions. The released scripts use 256 for Cora and Citeseer cora_si.sh.
- Checked, no discrepancy. The anchor update uses the learned graph from before the optimiser step, as Algorithm 1 does main.py app. D. The anchor update formula matches eq. 15 main.py.
Pitfalls
- p is a drop rate, not a keep rate. §4.3.2 says each mask element is drawn
from a Bernoulli with probability p, and eq. 9 keeps dimensions where the mask
is 1. The text on sensitivity and the code both treat p as the fraction
removed. The code does
features * (1 - mask)main.py. Implement p as the removal rate. - Rescaling changes the loss scale. With edge dropout rescaled by 1/(1 - p), the surviving edges are heavier than the eq. 10 version. Decide which to implement and report it.
- Denominator choice changes the objective. Excluding the positive from the denominator, as the code does, makes a per-node term negative whenever the positive similarity exceeds the sum over negatives. An implementer following eq. 14 will not match the released numbers.
- Missing sign in eq. 14. Without the minus, the stated objective would push the two views apart. Use the negative log.
- Bootstrapping is the key claim, and the released scripts turn it off for Cora and Pubmed structure inference. The paper's tau ablation is on refinement, not inference. Reproducing the inference numbers therefore means setting tau = 1.
- Anchor update interval. The paper says c >= 10 for scale, but the default is every step. The effect of c on accuracy is not reported.
- Sparse versus dense paths. The Pubmed numbers come from the sparse path, which does not compute the same symmetrisation and normalisation as the dense one. A dense reimplementation will not match Pubmed results.
- Learner choice is per dataset. The paper gives only the node-count and dimension guidance app. B. FGP needs O(n^2) memory and parameters, so it is unsuitable for large graphs app. B app. C. The paper uses FGP for Cora.
- Hardware and software. The released code calls
.cuda()in several places and needs CUDA utils.py utils.py. It pins PyTorch 1.7.1 and DGL 0.7.1 app. F.np.boolin the data loader does not exist in current NumPy releases data_loader.py. - Evaluation details differ per script. The evaluator's dropout and epochs change between scripts, and the paper states only the layer count and width app. F cora_si.sh cora_sr.sh.
- Baselines. Some baselines are copied from their papers and some are rerun with tuned settings, so the comparison mixes sources app. F.
- Scale. The paper's large-graph path uses kNN sparsification, a locality-sensitive kNN and mini-batch contrast §4.5. The ogbn-arxiv configuration is not in the release.
- No results from the code. This card does not report numbers from running the released code. All numbers are the paper's.
Lineage
- Builds on: the graph learner family and kNN initialisation of LDS, SLAPS and IDGL, and the sparsification and locality-sensitive kNN of SLAPS [§4.1, §4.2, app. B]. The NT-Xent loss and the SimCLR-style framework §4.3.3. The bootstrapping idea from earlier self-supervised work §4.4.
- Compared against: GCN, GAT, SAGE, LDS, GRCN, Pro-GNN, GEN, IDGL, SLAPS, GDC, SLAPS-2s for classification §5.1; K-means, SC, GE, DW, DNGR, M-NMF, RMSC, TADW, VGAE, ARGA, MGAE, AGC, DAEGC for clustering §5.1.
- Superseded by: not stated in this wiki.
Source and license
Adapted from “Towards Unsupervised Deep Graph Structure Learning” by Yixin Liu, Yu Zheng, Daokun Zhang, Hongxu Chen, Hao Peng, Shirui Pan (arXiv:2201.06367), used under CC BY 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. Code citations refer to https://github.com/GRAND-Lab/SUBLIME at commit 93398db994f2 (MIT). Any quoted code is shown under that license.