Graph Neural Networks
Learn how GNNs read graph-structured data — nodes, edges, and features — by passing messages between neighbours, and build one message-passing step by hand in plain Python before meeting GCN, GraphSAGE, and GAT in PyTorch Geometric.
Learn Graph Neural Networks in our free AI & Machine Learning course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a…
Part of the free AI & Machine Learning course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
🌍 Real-World Analogy: Learning From Your Friends
Want to guess someone's politics, music taste, or whether they will repay a loan? You learn a surprising amount just from who they spend time with . "You are the average of the five people you hang out with" is folk wisdom — and it is almost exactly how a GNN thinks.
Picture a friendship network. Each person holds a few facts about themselves (their features ). In one "round of gossip", everyone updates their own view by averaging in what their direct friends think . Do it again and your friends-of-friends start to influence you too. After a few rounds, your updated profile quietly encodes your whole local neighbourhood — not just you. That round of gossip is message passing , and stacking rounds is exactly what stacking GNN layers does.
1 Graph Data — Nodes, Edges, Features, Adjacency
A graph is data made of nodes (the things — people, atoms, products) joined by edges (the relationships — friendships, bonds, "bought together"). Unlike a table, where every row is independent, a graph's whole point is that the connections carry meaning.
- Node features — a vector of numbers per node (a user's age and activity; an atom's element and charge).
- Edges — which nodes are connected, sometimes with their own features (a bond type, a transaction amount).
- Adjacency — the bookkeeping of who-connects-to-whom. You can store it as an N×N matrix (1 = edge, 0 = no edge) or, more simply, as a dictionary mapping each node to its list of neighbours.
🗂️ The simplest way to store a graph in code
An adjacency dict — every example below uses this:
2 Message Passing — The One Idea Behind Every GNN
Almost every GNN is a stack of message-passing layers, and each layer does the same three steps for every node:
- Gather the feature vectors of the node's neighbours (the "messages").
- Aggregate them into one vector — usually a mean, sometimes a sum or a max.
- Transform that aggregate with a learnable weight matrix and a non-linearity (like ReLU).
After one layer, a node's new embedding reflects its 1-hop neighbourhood. Stack a second layer and it reaches 2 hops (friends of friends); k layers reach k hops. That is the entire trick — aggregate, then transform, then repeat.
The worked example below does step 1 and step 2 in plain Python: each node's new feature is simply the average of its neighbours' features . No NumPy, no PyTorch — just a dictionary and a loop. Read every comment, then run it.
Run the same averaging rule several times and watch a single signal spread across the graph — and start to flatten out. That flattening is a clue to a real GNN pitfall you will meet in Common Errors.
3 GCN, GraphSAGE, and GAT — The Three You Must Know
All three are message-passing layers. They differ in how they aggregate neighbours :
Takes a degree-normalised average of all neighbours. The simplest layer and a strong baseline — it is exactly the averaging you coded above, with a normalisation that stops high-degree nodes from dominating.
Samples a fixed number of neighbours instead of using all of them. That makes it scale to graphs with billions of edges and lets it handle nodes it never saw in training ( inductive learning).
Learns an attention weight for each neighbour, so important neighbours count more. Not every friend should count equally — GAT figures out who matters, the same idea that powers Transformers.
In practice you rarely hand-code these. PyTorch Geometric (PyG) ships GCNConv , SAGEConv , and GATConv as drop-in layers. The next example is read-only PyG showing a single GCNConv doing aggregate-then-transform for you.
4 Three Task Levels — Node, Edge, and Graph
Once message passing has given every node a rich embedding, you can predict at three different levels:
Level
Question
Example
How
Node
What is this node?
Flag a fraudulent account
Classify each node embedding
Edge
Is there a link?
Recommend a friend; predict a bond
Score pairs of node embeddings
Graph
What is the whole graph?
Is this molecule toxic?
Pool all nodes into one vector (a "readout")
The read-only example below uses a GATConv (attention) layer and then produces all three: per-node logits, per-edge scores, and one graph-level vector from mean pooling .
🚀 Where GNNs Win in the Real World
GNNs shine wherever relationships matter as much as the items themselves :
- Social networks. Detect communities, predict who will connect, and flag fake or fraudulent accounts by the company they keep.
- Molecules & drug discovery. Atoms are nodes and bonds are edges, so a molecule is a graph. GNNs predict toxicity, solubility, and binding affinity, and propose new candidate compounds.
- Recommenders. Users and items form a giant interaction graph. Pinterest's PinSage and many e-commerce systems use GNNs to power "you might also like".
- Maps & traffic. Road networks are graphs; Google Maps uses GNNs to improve arrival-time estimates.
- Knowledge graphs & fraud rings. Complete missing facts, and spot money-laundering patterns that single-account models miss entirely.
Now you try. Fill in the blanks marked ___ . Gathering a node's neighbours and averaging them is message passing — get this and you have the heart of every GNN.
Your turn again. Real GNN layers include each node in its own neighbourhood — a self-loop — so a node never forgets its own features. Add it here.
5 Common Errors (And How to Fix Them)
These four mistakes trip up almost everyone building their first GNN:
Stack too many message-passing layers and every node keeps averaging in more of the graph until all embeddings collapse to nearly the same vector — the model can no longer tell nodes apart and accuracy crashes.
✅ Fix: keep it shallow (2-4 layers); add residual / jumping-knowledge connections only if you truly need depth.
If you aggregate only over neighbours, a node throws away its own features in every layer and loses its identity.
✅ Fix: add a self-loop — include the node in its own neighbourhood. PyG's GCNConv does this automatically.
Full-batch GCN on a graph with millions of nodes builds an enormous adjacency operation and runs out of memory.
✅ Fix: sample neighbourhoods in mini-batches (GraphSAGE-style) with a neighbour loader.
Mean aggregation throws away how many neighbours a node has — yet for tasks like counting substructures in molecules, degree is the signal. Mean can't distinguish "1 neighbour" from "100 identical neighbours".
✅ Fix: match the aggregator to the task — sum (GIN) preserves count and is most expressive; mean and max suit others.
📋 Quick Reference
Concept
What it is
Use when
Adjacency
Who connects to whom (dict or matrix)
Describing any graph's structure
Message passing
Gather, aggregate, transform neighbours
The core of every GNN layer
GCN
Degree-normalised mean of neighbours
Strong, simple baseline
GraphSAGE
Sample neighbours; inductive
Huge / changing graphs
GAT
Attention-weighted neighbours
Some neighbours matter more
Self-loop
Node aggregates itself too
Always — keep a node's own features
Readout / pooling
Pool nodes into one graph vector
Graph-level tasks (e.g. molecules)
❓ Frequently Asked Questions
🎯 Mini-Challenge: Two Rounds of Message Passing
Time to fly with less scaffolding. The starter gives you a line graph and a comment outline — write the step() function yourself and apply it twice. Remember the self-loop: average each node together with its neighbours.
Lesson 46 complete — you can now reason about graphs the way a GNN does!
You can describe a graph with nodes, edges, features, and an adjacency dict; you have hand-coded a message-passing step (average of neighbours) and watched information spread; you can explain how GCN, GraphSAGE, and GAT aggregate differently; you know the node-, edge-, and graph-level tasks; and you can spot the classic traps — over-smoothing, missing self-loops, scalability, and the wrong aggregator.
🚀 Up next: AutoML & NAS — let algorithms search for the best model and architecture for you.
Practice quiz
What kind of data does a graph neural network operate on?
- Fixed grids like images only
- Sequences of text only
- Nodes joined by edges, where nodes and edges can carry features
- Single independent rows in a table
Answer: Nodes joined by edges, where nodes and edges can carry features. A GNN works on graph-structured data: nodes (entities) joined by edges (relationships), each carrying features.
What are the three steps of message passing in a GNN layer?
- Gather neighbours' features, aggregate them, then transform
- Encode, attend, decode
- Split, shuffle, merge
- Normalise, dropout, activate
Answer: Gather neighbours' features, aggregate them, then transform. Each layer gathers neighbour messages, aggregates them (mean/sum/max), then transforms with a learnable weight and non-linearity.
After one message-passing layer, a node's embedding reflects information from how far away?
- The whole graph
- Only itself
- Exactly 5 hops
- Its 1-hop neighbours
Answer: Its 1-hop neighbours. After one layer a node knows its 1-hop neighbourhood; stacking k layers reaches k hops.
How does a GCN aggregate neighbours?
- By sampling a fixed number of neighbours
- A degree-normalised average of all neighbours
- With a learned attention weight per neighbour
- By taking the maximum only
Answer: A degree-normalised average of all neighbours. GCN takes a degree-normalised average of all neighbours — a simple, strong baseline.
What makes GraphSAGE different from GCN?
- It samples a fixed number of neighbours, so it scales and is inductive
- It uses attention weights
- It ignores node features
- It only works on small graphs
Answer: It samples a fixed number of neighbours, so it scales and is inductive. GraphSAGE samples a fixed number of neighbours, letting it scale to huge graphs and generalise to unseen nodes (inductive).
What does a GAT layer add over a GCN?
- Self-loops only
- A larger learning rate
- A learned attention weight for each neighbour
- Random neighbour dropout
Answer: A learned attention weight for each neighbour. GAT learns an attention weight per neighbour, so important neighbours count more than unimportant ones.
Which is an example of a node-level task?
- Deciding whether a whole molecule is toxic
- Flagging a fraudulent account
- Predicting whether two users will connect
- Pooling all nodes into one vector
Answer: Flagging a fraudulent account. Node-level tasks label individual nodes — for example flagging a fraudulent account in a transaction graph.
How do you get a single prediction for a whole graph (graph-level task)?
- Take the first node's embedding
- Use only the edges
- Ignore the node features
- Pool (read out) all node embeddings into one vector
Answer: Pool (read out) all node embeddings into one vector. Graph-level tasks pool all node embeddings into one vector (a readout), e.g. via global mean pooling.
What is over-smoothing in a GNN?
- Using too few features
- Stacking too many layers until all node embeddings collapse toward the same value
- Adding too many self-loops
- Sampling too few neighbours
Answer: Stacking too many layers until all node embeddings collapse toward the same value. Over-smoothing happens when too many layers make every node embedding converge, so nodes become indistinguishable.
Why add a self-loop in message passing?
- To remove the node's own features
- To make the graph directed
- So a node keeps its own features instead of losing its identity each layer
- To speed up training
Answer: So a node keeps its own features instead of losing its identity each layer. A self-loop includes the node in its own neighbourhood so its original features are preserved in the aggregate.
Continue this course
- Previous: Recommender Systems
- Next: AutoML & NAS