Recommender Systems2018advanced11 min read

PinSage: Graph Convolutional Neural Networks for Web-Scale Recommender Systems

PinSage: شبكات التفاف بيانية لأنظمة التوصية على نطاق الويب

Ying, R. · He, R. · Chen, K. · Eksombatchai, P. · Hamilton, W. L. · Leskovec, J. — KDD

The problem

Graph Convolutional Networks (GCNs) achieved strong results on academic benchmarks, but scaling them to production graphs with billions of nodes and tens of billions of edges was unsolved. Traditional GCNs require the full graph Laplacian during — impossible when the graph has 3 billion nodes. GraphSAGE improved things by sampling neighborhoods, but still assumed the entire graph fits in GPU memory. Meanwhile, content-only embeddings (visual features, text annotations) missed the rich relational signal: a bed-rail pin looks like a garden fence visually, but its graph neighbors reveal it belongs with bedroom furniture.

The contribution

PinSage: a highly scalable GCN that generates node embeddings by combining graph structure with node features. Key innovations include random-walk-based neighborhood sampling that defines importance-weighted neighborhoods instead of fixed k-hop expansions (46% gain), a producer-consumer architecture that separates CPU-bound sampling from GPU-bound training, curriculum training that feeds progressively harder negative examples (12% gain), and a pipeline for generating embeddings for all 3 billion nodes. Deployed at Pinterest, PinSage achieved 150% improvement in hit-rate over the best baseline.

The impact

PinSage was the first industrial-scale deployment of graph neural networks, proving that GCNs could move from academic benchmarks to production systems serving hundreds of millions of users. It established the blueprint for scalable graph learning: localized convolutions, importance sampling, and curriculum-based training. Its ideas influenced subsequent systems at Uber, Alibaba, and other platforms, and the random-walk neighborhood concept became a standard tool in the graph ML toolkit.

Imagine you just moved to a new city and want restaurant recommendations. You could browse every restaurant's menu (content features), but that's slow and misses context. Instead, you ask your neighbors: "Where do you eat?" Then you weigh their answers — the foodie next door counts more than a stranger across town. You blend their top picks into your own taste profile.

PinSage does exactly this on Pinterest's graph: every pin asks its graph neighbors what they look like, weighs their answers by how closely related they are, and builds a vector summary that captures both its own content and its neighborhood's collective identity.

Pinterest as a giant bipartite graph

Pinterest's world has two kinds of objects: pins (images with metadata) and boards (user-curated collections). When a user saves a pin to a board, an edge is created. The result is a — pins connect to boards, boards connect to pins, but pins never connect directly to other pins.

At the time of the paper, this graph had 3 billion nodes (2 billion pins + 1 billion boards) and 18 billion edges. Two pins sharing a board are implicitly related: a user who pins a pasta recipe and a wooden cutting board to the same "Kitchen Ideas" board tells the system those items belong together — a signal no image classifier could capture.

Open in Lab
Click on a pin to see its board connections and 2-hop pin neighbors. Pins on the same board share an implicit relationship.
The demo wakes as you arrive…

The key insight is that a pin's identity is not just its pixels — it's also defined by the company it keeps. A bed-rail pin might look like a garden fence to a vision model, but its graph neighborhood (bedroom furniture, mattress pins, nightstand pins) reveals its true category. PinSage learns to fuse both signals: the pin's own visual and textual features with the aggregated features of its neighbors.

Why standard GCNs cannot handle billions of nodes

A standard Graph Convolutional Network multiplies node features by the full graph Laplacian at every layer — a matrix with 3 billion rows and columns. This is completely infeasible.

GraphSAGE solved part of the problem by sampling a fixed number of neighbors per node instead of using the full adjacency. But it still assumed the entire graph fits in GPU memory, and it sampled neighbors uniformly — treating every neighbor as equally important regardless of how strongly related they are.

PinSage overcomes both limitations: it uses random walks to define importance-weighted neighborhoods, and a producer-consumer architecture that keeps only the current on the GPU while the CPU prepares the next one.

Innovation 1: Random walks define smarter neighborhoods

Instead of taking all nodes within kk hops (which explodes exponentially), PinSage launches short random walks from the target node and counts how often each neighbor is visited. Neighbors with high visit counts are more "important" — they appear on many paths from the target, signaling a strong structural relationship.

Think of it like footpaths in a park: the most worn paths reveal which destinations matter most. A node visited 50 times across 200 random walks is far more structurally relevant than one visited once.

Formally, the visits approximate the scores with respect to the target node. The top-TT nodes by visit count become the node's neighborhood, and their normalized visit counts serve as importance weights during .

Open in Lab
Click "Walk!" to launch random walks from the center node. Compare the importance-weighted neighborhood (right) with the uniform k-hop neighborhood (left).
The demo wakes as you arrive…

The PinSage convolution: aggregate, transform, combine

At each layer, every node performs three steps to update its representation. Imagine the node as a manager summarizing feedback from a team meeting:

Step 1 — Gather and transform: each neighbor's features are passed through a (the same for all neighbors), producing a transformed view.

Step 2 — Importance-weighted pooling: the transformed neighbor features are averaged using the random-walk importance weights. This is the "weighted vote" — important neighbors speak louder.

Step 3 — Combine with self: the pooled neighborhood vector is concatenated with the node's own current representation, then passed through another dense layer and normalized. The result is the node's new representation, enriched with neighborhood context.

nu=ReLU ⁣(Q⋅γ ⁣({hv:v∈N(u)},α))n_u = \text{ReLU}\!\left(Q \cdot \gamma\!\left(\{h_v : v \in \mathcal{N}(u)\}, \alpha\right)\right)
Step 1–2: neighborhood aggregation with importance weights — γ\gamma is the importance-weighted mean — each neighbor vv's feature hvh_v is multiplied by its random-walk weight αv\alpha_v. QQ projects the result to mm dimensions, and ReLU adds nonlinearity.
zu=W⋅CONCAT(hu,  nu)∥W⋅CONCAT(hu,  nu)∥2z_u = \frac{W \cdot \text{CONCAT}(h_u,\; n_u)}{\|W \cdot \text{CONCAT}(h_u,\; n_u)\|_2}
Step 3: combine self with neighborhood, then normalize — The node's own representation huh_u is concatenated with the pooled neighborhood nun_u, transformed by weight matrix WW, and L2-normalized. Normalization stabilizes training and ensures all embeddings live on the unit sphere.
Open in Lab
Step through the three stages of a PinSage convolution layer for a single node.
The demo wakes as you arrive…
PinSage localized convolution in NumPypython

Simplified to show the idea — not the real implementation.

import numpy as np

def pinsage_convolve(h_self, h_neighbors, alpha_weights, Q, q, W, w):
    """One layer of PinSage convolution for a single node.

    h_self:       (d,)    — the node's own feature vector
    h_neighbors:  (T, d)  — features of the T sampled neighbors
    alpha_weights:(T,)    — importance weights from random walks (sum to 1)
    Q, q:         (m, d) and (m,) — neighborhood transform params
    W, w:         (d, d+m) and (d,) — combine transform params
    """
    # Step 1-2: transform each neighbor, then importance-weighted pool
    n_u = np.relu(Q @ (alpha_weights @ h_neighbors) + q)   # (m,)

    # Step 3: concatenate with self, transform, normalize
    combined = np.concatenate([h_self, n_u])                # (d+m,)
    z_u = W @ combined + w                                  # (d,)
    z_u = z_u / (np.linalg.norm(z_u) + 1e-8)               # unit sphere
    return z_u

# Stack K layers: output of layer k becomes input to layer k+1.
# Each layer has its own Q, W parameters — shared across ALL nodes.

Innovation 2: Importance pooling replaces uniform aggregation

In GraphSAGE, all sampled neighbors contribute equally: mean-pooling or max-pooling treats every neighbor as the same. PinSage replaces this with — a weighted mean where the weight of each neighbor vv is its normalized random-walk visit count αv\alpha_v.

The effect is dramatic: if 200 random walks from a pasta-recipe pin visit a "cooking tools" board 50 times but a "random humor" board only twice, the cooking tools board speaks 25× louder during aggregation. The node's new representation is shaped primarily by its most structurally relevant neighbors, not diluted by distant or incidental connections.

Open in Lab
Compare uniform pooling (left) with importance-weighted pooling (right). Notice how the result shifts toward the most-visited neighbors.
The demo wakes as you arrive…

Training: max-margin loss with curriculum-scheduled hard negatives

PinSage is trained to push the embeddings of related pins close together and unrelated pins apart. Training pairs come from user behavior logs: two pins saved to the same board within one hour form a positive pair (they are related). A randomly sampled pin forms a negative pair (almost certainly unrelated).

The loss function is a max-margin ranking loss: it insists that the between a query pin qq and its positive match ii exceeds the dot product with every negative example nkn_k by at least a margin Δ\Delta.

J(q,i)=∑nkmax⁡ ⁣(0,  zq⋅znk−zq⋅zi+Δ)J(q, i) = \sum_{n_k} \max\!\big(0,\; z_q \cdot z_{n_k} - z_q \cdot z_i + \Delta\big)
Max-margin ranking loss — For each negative sample nkn_k: if its score zq⋅znkz_q \cdot z_{n_k} gets within Δ\Delta of the positive score zq⋅ziz_q \cdot z_i, the loss is positive and the model updates. Otherwise the margin is satisfied and the loss is zero — the model moves on.

The problem with easy negatives. A random pin from the 2-billion pin pool is almost always trivially different from the query — like asking "is a pasta recipe related to a motorcycle?" The model quickly learns to distinguish these easy cases and stops improving.

The solution: hard negatives via Personalized PageRank. PinSage computes random-walk PageRank scores for each query pin and selects hard negatives from the 2000–5000 ranking range — pins that are somewhat related but not true matches. These force the model to learn fine-grained distinctions.

Curriculum training prevents hard negatives from overwhelming an untrained model. At nn, the model is given n−1n-1 hard negatives per example, starting from zero. The model first masters easy distinctions, then progressively faces harder ones — like a student moving from addition to algebra. This curriculum yielded a 12% performance gain.

Open in Lab
Watch how negative examples get harder as training epochs progress. Epoch 1 uses only easy random negatives; later epochs add hard negatives from the PageRank tail.
The demo wakes as you arrive…

Scaling to billions: producer-consumer and MapReduce inference

Training a GCN on billions of nodes requires careful orchestration between CPU and GPU. PinSage uses a producer-consumer pipeline: while the GPU processes the current mini-batch (model forward pass, backward pass, gradient update), the CPU simultaneously samples neighborhoods and fetches features for the next mini-batch. This eliminates GPU idle time — the most expensive resource never waits.

For inference after training, PinSage uses a two-stage MapReduce pipeline to generate embeddings for all 3 billion nodes efficiently. The first MapReduce job projects all node features into the low-dimensional space. The second job joins each node with its neighbors' projected features and computes the aggregation. This avoids redundant computation: each node's features are projected exactly once, even if it appears as a neighbor of thousands of other nodes.

Open in Lab
See how the two-stage MapReduce pipeline avoids redundant computation when generating embeddings for all nodes.
The demo wakes as you arrive…

Results: 150% improvement and production deployment

PinSage was evaluated on two tasks: related pin recommendation (given a query pin, rank the most related pins) and homefeed recommendation (recommend pins a user is likely to save next).

In offline evaluation on related pin recommendation, PinSage achieved a hit-rate of 67% and an MRR of 0.59 — a 150% relative improvement in hit-rate and 60% in MRR over the best baseline (Pixie, a graph-based random-walk method). Content-only approaches (visual features, text annotations, and their combination) scored far below.

Ablation studies confirmed the impact of each innovation: importance pooling with hard negatives (PinSage full) consistently beat max-pooling, mean-pooling, and versions without hard negatives. The distribution showed PinSage vectors were more spread out (kurtosis 0.43 vs. 2.49 for annotation embeddings), meaning the representation space was used more effectively.

In A/B tests at Pinterest, PinSage-powered recommendations led to measurable engagement lifts, and the system was deployed to serve recommendations across the platform.

Why PinSage changed graph machine learning

Before PinSage, graph neural networks were an academic curiosity — impressive on Cora and Citeseer (a few thousand nodes), but unproven at real scale. PinSage proved three things that shaped the field:

First, that graph structure is a powerful that complements content features. The bed-rail vs. garden-fence example became a canonical illustration of why graph context matters.

Second, that clever engineering makes GCNs practical: random-walk neighborhoods, producer-consumer training, and MapReduce inference aren't theoretical contributions — they are systems innovations that turned a theoretical idea into a production system.

Third, that training strategy matters as much as architecture: importance pooling and curriculum training contributed more performance gains (46% + 12%) than any architectural change.

  1. 2017

    GraphSAGE

    Introduced inductive learning on graphs via neighborhood sampling and aggregation. Enabled GCNs to generalize to unseen nodes but required the full graph in GPU memory.

  2. 2018

    PinSage

    Scaled GCNs to 3 billion nodes with random-walk neighborhoods, importance pooling, and curriculum training. First industrial GNN deployment.

  3. 2019

    Alibaba Graph-based Recommendations

    Large-scale graph embedding systems at Alibaba applied similar random-walk and sampling ideas to e-commerce recommendation, extending PinSage's blueprint.

  4. 2019

    Uber Eats Graph Learning

    Uber applied graph neural networks to food delivery recommendations, adapting the producer-consumer training pattern to dynamic restaurant-dish graphs.

  5. 2020

    Graph Transformer Networks

    Combined Transformer attention with graph structure, building on the neighborhood aggregation paradigm PinSage helped establish.

CitationYing, He, Chen, Eksombatchai, Hamilton, Leskovec. Graph Convolutional Neural Networks for Web-Scale Recommender Systems. KDD, 2018.

Terms in this paper