skip to content

Neighbourhood Aggregation

Every graph network shares one loop: gather messages from neighbours, aggregate them order-free, then update the node state. Interviewers probe why k rounds mean a k-hop receptive field.

on this pageshow

questions

3

What are the three steps of one message-passing round in a graph neural network?

level: juniorimportance: must knowfreq 76%

answer

  1. three stages, always the same order
  2. a neighbour's state plus the edge
  3. pooling must ignore neighbour order
  4. the node keeps its own state too
  5. the combining function is learned

basics

~20 s

One round has three steps: each neighbour builds a message from its state and the connecting edge; the node pools those messages with an order-free aggregator; a learned update then combines the pooled vector with the node's own previous state.

solid answer

~50 s

A round is message, aggregate, update. For a target node, every neighbour produces a message — a learned function of that neighbour's current representation, optionally the target's representation and the edge's own features. All of those messages are then pooled by an aggregator that must ignore the order they arrive in and must accept a variable number of them: sum, mean or max. Finally a learned update, typically a small feed-forward layer over the pooled vector concatenated with (or added to) the node's own previous representation, produces the node's new state. Every node is updated in parallel from the *previous* round's states, so the result does not depend on which node you process first. On a weather-station network the message might be a neighbouring station's readings together with edge features for distance and elevation difference; the update then folds the pooled neighbourhood signal into the station's own state.

code

python · 14 lines
python
adj = {0: [1, 2], 1: [0, 3], 2: [0], 3: [1]}
h = {0: [1.0, 0.0], 1: [0.0, 1.0], 2: [1.0, 1.0], 3: [0.0, 0.0]}

def update(self_vec, pooled):            # stands in for the learned step
    return [0.5 * s + 0.5 * p for s, p in zip(self_vec, pooled)]

for round_no in (1, 2):                  # k rounds reach k hops
    new_h = {}
    for v, nbrs in adj.items():
        msgs = [h[u] for u in nbrs]      # message = the neighbour's state
        pooled = [sum(m[i] for m in msgs) for i in range(2)]   # order-free sum
        new_h[v] = update(h[v], pooled)
    h = new_h                            # every node updated from round k-1 states
    print(round_no, {v: [round(x, 3) for x in vec] for v, vec in h.items()})

go deeper

for a junior

Be ready to name the three stages in order and say what each consumes: message from the neighbour and the edge, order-free pooling, learned update that also takes the node's own state. Saying it cleanly in three sentences is the whole bar here.

for a middle

Explain why the aggregator has to accept a variable number of inputs and ignore their order, and why updates are computed synchronously from the previous round's states. Expect to be asked where edge features enter.

for a senior

Show that you have made these choices on a real graph: what you put in the message, whether the update concatenates or adds the self state, and how parameter sharing across rounds behaved on a small training set. Be able to say what breaks if the self state is dropped.

for a principal

Own the framing that the shared message and update functions are what make the model inductive — parameters describe how to talk to a neighbour, not how to handle a specific node — and be able to argue what that buys you when the production graph grows or rewires between training runs.

## The problem a round has to solve A graph gives each node a feature vector and an unordered, variable-sized set of neighbours. A dense layer cannot consume that: it needs a fixed-length input in a fixed order. Message passing is the standard answer — a single scheme, repeated, that turns an arbitrary neighbourhood into a fixed-length vector. One **round** (one layer) has exactly three stages, applied at every node simultaneously. ### 1. Message For each edge `(u -> v)`, a learned message function produces a vector: `m_uv = MESSAGE(h_u, h_v, e_uv)` where `h_u` is the neighbour's current representation, `h_v` the target's, and `e_uv` the edge's own features. The simplest useful form is `m_uv = W * h_u` — just a linear map of the neighbour's state. Edge features matter whenever the *relationship* carries information: on a network of weather stations, a message can be the neighbour's temperature and pressure readings concatenated with the distance and the elevation difference along that link, so a reading from a station 5 km away at the same altitude is not treated like one from 200 km away and 1,500 m higher. If edges carry no features, the message is a function of the neighbour's state alone. ### 2. Aggregate The messages arriving at `v` form a **multiset** — a bag that may contain repeats and has no meaningful order. The aggregator collapses it into one fixed-length vector: `a_v = AGGREGATE({ m_uv : u in N(v) })` Two requirements are non-negotiable. It must accept any number of inputs, because degree varies from node to node. And it must be **order-free**: relabelling the neighbours must not change the result, since a graph has no canonical ordering of a node's neighbours. Elementwise sum, mean and max all satisfy both; a concatenation or a recurrent pass over the neighbours does not. The choice among sum, mean and max is not cosmetic — it decides which neighbourhoods the layer can tell apart — but the mechanics of a round are the same whichever you pick. ### 3. Update The pooled vector is combined with the node's own previous state by a learned function: `h_v_new = UPDATE(h_v_old, a_v)` usually a small feed-forward layer over the two concatenated, or over their sum, with a nonlinearity. Keeping `h_v_old` in the update is what stops a node from being replaced by a blur of its surroundings and what lets the network weigh its own evidence against its context. Skipping it is a common beginner error. ## Timing: rounds are synchronous All nodes compute their new state from the states at the *end of the previous round*. If instead you updated nodes one at a time and let later nodes read already-updated neighbours, the output would depend on the traversal order — exactly the property the aggregator was chosen to avoid. ## Stacking rounds After one round, `h_v` depends on `v` and its immediate neighbours. After two, it depends on everything within two hops, because the neighbours' round-1 states already carried their own neighbours' information. In general, `k` rounds give a node a receptive field of `k` hops. Rounds may each have their own message and update parameters (the usual choice) or share one set across rounds, which cuts parameter count and lets you vary depth at inference time. ## What is not part of a round A **readout** — pooling all node states into one graph-level vector for graph classification — happens once at the end, not per round; do not confuse it with neighbourhood aggregation. And node-level predictions are made from the final `h_v` by an ordinary output layer, not by the aggregator itself. ## Why it generalises Because the message and update functions are shared across all nodes and all edges, the same trained model applies to a graph with a different number of nodes, different degrees, or nodes never seen during training — the parameters describe how to talk to a neighbour, not how to handle node #17.

  • Are all nodes updated from the same previous-round states, or can one node's new state feed another's within the same round?
    Updates are synchronous: every node reads the states as they stood at the end of the previous round. Letting an already-updated node feed its neighbour inside the same round would make the output depend on the order you happened to visit nodes in, which destroys the permutation-independence the whole scheme is built on and makes results irreproducible across runs.
  • Where do edge features enter a round, and what changes if edges carry no features?
    They enter in the message function, which takes the neighbour's state, optionally the target's, and the edge vector. With no edge features the message reduces to a learned function of the neighbour's state alone. Edge features are how you encode relationship type, weight, distance or direction, so dropping them throws away real signal on graphs where the link means something.
  • Do all rounds share the same message and update parameters?
    Either is valid. Distinct parameters per round is the common choice and gives each hop its own transformation. Tying one parameter set across all rounds is the recurrent variant: it cuts the parameter count sharply, regularises a small-data problem, and lets you run a different number of rounds at inference than you trained with, at the cost of forcing every hop to be processed identically.

Each weather station writes a short note about what it is seeing, every station reads the whole pile of notes from the stations it is wired to without caring who wrote first, and then revises its own forecast using both the pile and what it already believed.

saying these in an interview costs you the question

  • Says neighbours are processed in a fixed order like a sequence
  • Thinks the node's own state is discarded after aggregating
  • Calls the update a fixed formula with no learned parameters
  • Believes one round already sees the whole graph
  • Confuses neighbourhood aggregation with a graph-level readout

context

open as a page

Why does sum aggregation distinguish neighbourhoods that mean and max aggregation cannot?

level: middleimportance: must knowfreq 60%

basics

~20 s

A neighbourhood is a multiset, and only sum keeps both which features appear and how many times. Mean keeps proportions but loses counts; max keeps only extremes. Sum is the more distinguishing aggregator, which is why GIN uses it.

open as a page

How do you choose the number of message-passing rounds in a graph neural network?

level: seniorimportance: should knowfreq 46%

basics

~20 s

k rounds give a node a receptive field of exactly k hops, so pick k to match the distance at which the label's evidence lives, then confirm it with a small validation sweep. Most production graph models land at two or three.

open as a page