4. Fundamentals of graph neural networks
In industrial fault localization, the data rarely arrives as a flat table. Sensors sit on specific machines, machines belong to production cells, cells feed downstream processes, and alarms propagate along physical or logical connections. A graph neural network (GNN) is the natural model class for this structure because it learns by exchanging information along edges. This chapter builds the three operations that define nearly every GNN: message passing, aggregation, and readout. Understanding them at the tensor level is what lets you later debug why a fault score is high on the wrong asset.
The graph as a computational object
A graph is written as G = (V, E), where V is the node set and E the edge set. Each node v carries a feature vector xv ∈ ℝd, and each edge (u, v) may carry an edge feature euv. In a plant, a node could be a pump, a motor, a PLC tag, or a vibration sensor; an edge could be a piping connection, a shared power bus, or a control dependency. The adjacency matrix A ∈ ℝN×N encodes connectivity, and the degree matrix D counts neighbors. These two matrices are the backbone of every operation below.
A single GNN layer updates node representations by combining a node's own state with information from its neighbors. Stacking L layers gives each node a receptive field of L hops, which is exactly what you want when a fault on a compressor should influence the representation of a downstream separator two connections away.
Message passing: computing what a neighbor says
Message passing is the local computation step. For each edge (u, v), the model computes a message from the source node u to the target node v:
m_uv = φ( h_u, h_v, e_uv )
Here hu and hv are current node embeddings, euv is the optional edge feature, and φ is a learnable function — typically a small MLP or a linear transform. In the simplest formulation, φ is just a linear projection of the source embedding:
m_uv = W · h_u
The choice of φ matters in industrial settings. If edges carry physical meaning such as pipe diameter or signal type, include euv so the model can weight a high-bandwidth connection differently from a weak one. A common practical form is a concatenation followed by an MLP:
m_uv = MLP( [ h_u || h_v || e_uv ] )
where || denotes concatenation. This is the message-passing stage of the GraphSAGE and GAT families, and it is where edge directionality is enforced: messages flow from u to v, not the reverse, unless you explicitly add the transposed edges.
Aggregation: combining neighbor messages
After messages are computed, each node collects all incoming messages and reduces them to a single vector. This reduction must be permutation-invariant — the result cannot depend on the order in which neighbors are listed. The standard choices are:
- Sum:
a_v = Σ_{u∈N(v)} m_uv— preserves magnitude, sensitive to node degree. - Mean:
a_v = (1/|N(v)|) Σ m_uv— normalizes for degree, stable across varying fan-in. - Max:
a_v = max_{u∈N(v)} m_uv— captures the strongest signal, useful for alarm propagation.
For fault localization, mean aggregation is a safe default because equipment nodes have very different neighbor counts. Max aggregation is attractive when a single upstream anomaly should dominate, for example a sudden pressure spike. The aggregated vector is then combined with the node's own state through an update function:
h_v' = σ( W_self · h_v + W_neigh · a_v + b )
This is the update step of a GraphSAGE-style layer. The self-loop term W_self · h_v ensures a node does not forget its own sensor reading while absorbing neighbor context. Without it, deep stacks tend to over-smooth and all nodes converge to similar embeddings — a failure mode that destroys localization resolution.
Readout: from node embeddings to a graph-level decision
Fault localization often needs a per-node score, but many tasks require a graph-level output: is this batch faulty, is this subsystem degraded? Readout (also called pooling) maps the set of node embeddings to a single graph embedding:
h_G = READOUT( { h_v : v ∈ V } )
Common readout functions mirror aggregation: sum, mean, or max over all nodes. A more expressive option is attention-based readout, where each node receives a learned weight:
α_v = softmax( MLP( h_v ) )
h_G = Σ_v α_v · h_v
Attention readout is valuable when only a few nodes carry diagnostic evidence — for instance, one overheating bearing among fifty healthy components. The learned weights tell you which nodes the model considered important, giving a built-in explanation channel.
How the three operations compose
The figure below shows the full data flow for a two-layer GNN applied to a small industrial graph. Node features enter, messages are computed per edge, aggregated per node, and updated; after the final layer, readout produces a graph-level embedding that feeds a classifier or a per-node fault head.
A worked example with concrete tensors
Consider a three-node subgraph: a pump P1, a motor P2, and a valve P3. Suppose each node has a 2-dimensional feature vector representing normalized vibration and temperature:
h_P1 = [0.8, 0.3]
h_P2 = [0.2, 0.9]
h_P3 = [0.5, 0.5]
Edges exist P1→P2 and P1→P3, meaning the pump feeds both the motor and the valve. Using a linear message function m_uv = W · h_u with W = [[1, 0], [0, 1]] (identity, for clarity), the messages are simply the source embeddings. For node P2, the only incoming message is from P1, so mean aggregation gives a_P2 = [0.8, 0.3]. With W_self = W_neigh = I and a ReLU activation, the updated embedding is:
h_P2' = ReLU( [0.2, 0.9] + [0.8, 0.3] ) = [1.0, 1.2]
Node P2 now carries information about the pump's vibration, which is exactly the propagation behavior that lets a downstream fault be detected before the motor's own sensors saturate. After two layers, P2 would also receive information from P3 if a reverse path existed, extending the receptive field.
Practical considerations for fault localization
Three design decisions dominate real deployments. First, edge direction: physical flow is often directed, but fault influence can propagate both ways. Adding reverse edges doubles the message volume but captures bidirectional coupling. Second, normalization: dividing aggregated messages by sqrt(deg(u) · deg(v)), as in GCN, prevents high-degree hub nodes from dominating. Third, readout granularity: if you need to localize a fault to a specific asset, attach a per-node classification head after the last layer rather than relying on graph-level readout alone.
Verification checklist
| Check | Expected observation | Failure symptom |
|---|---|---|
| Message tensor shape | [E, d] where E = edge count | Shape mismatch on concatenation |
| Aggregation invariance | Shuffling neighbor order leaves output unchanged | Output changes with edge ordering |
| Self-loop presence | Node embedding retains own features | All nodes converge to same vector |
| Receptive field | L layers reach L hops | Distant fault not detected |
| Readout output | Single vector per graph | Per-node tensor passed to graph classifier |
With these three operations understood at the tensor level, you can read any GNN architecture — GCN, GraphSAGE, GAT, GIN — as a variation on the same message-passing skeleton. The next step is to connect this skeleton to the fault-localization objective, where node labels, class imbalance, and graph construction from sensor topology become the dominant engineering concerns.