ML System Design · ML Systems Lab

Graph ML for Fraud: Why Tabular Models Miss What GNNs Catch

A fraudster reusing one device across 40 accounts looks clean in any tabular model. A GNN sees the device as an edge — and a node two hops from 38 confirmed-fraud accounts is no longer clean. Here is how graph structure becomes the primary fraud signal.

Fraud detection is one of the few production ML problems where graph structure is not a nice-to-have — it is the primary signal. This post covers why, and what building a GNN-based fraud system actually involves.

Why graphs?

A fraudster opening accounts for a synthetic identity ring reuses resources: the same device, the same IP subnet, the same phone number, the same email domain pattern. Each individual account may look completely clean — legitimate transaction amounts, normal velocity, plausible name. But the device connects 40 accounts, 38 of which are confirmed fraud.

A tabular model sees one row per user. It can engineer "number of accounts sharing this device" as a feature, but this requires an explicit join, a daily batch computation, and a schema update every time a new connection type is discovered. A GNN is structural by design — the edges are the model.

The graph structure for fraud

Nodes: accounts, devices, IP addresses, phone numbers, email addresses, bank accounts (for payment fraud), merchants.

Edges: "account A used device D" (shared resource edge), "account A sent money to account B" (transaction edge), "account A and B have the same phone number" (identity edge).

This is a heterogeneous graph: different node types and different edge types. GraphSAGE, GAT (Graph Attention Network), and HGT (Heterogeneous Graph Transformer) all handle this. The simplest production approach: convert to a homogeneous graph by treating all node types as "entities" and all edge types as weighted connections.

Message passing for fraud

In a GNN, each node updates its representation by aggregating from its neighbours. After k layers, node v's representation encodes its k-hop neighbourhood.

Layer 0: h_v = [account_age, balance, velocity_7d, country_code, ...] Layer 1: h_v = σ(W · CONCAT(h_v, mean({h_u : u ∈ N(v)}))) Layer 2: h_v = σ(W · CONCAT(h_v, mean({h_u : u ∈ N(v)})))

After 2 layers, a clean account whose device is shared with one confirmed fraud account has absorbed that fraud account's representation into its own vector. The fraud label diffuses through edges. A clean account two hops from a fraud ring gets a risk score 3-4× higher than an isolated clean account, even without any individual suspicious features.

Label propagation as a baseline

Before building a GNN, always try label propagation. Starting from confirmed fraud labels, propagate a fraud score outward through edges with exponential decay: score(v) = Σ_u (α × score(u)) for neighbours u, where α < 1. This is cheap, interpretable, and surprisingly effective. At Alibaba, label propagation on their transaction graph outperformed their tabular XGBoost model before they added GNN features. Use LP as your baseline, then measure GNN uplift on top of it.

Temporal graphs

Fraud rings are dynamic. An account may share a device with clean accounts at first, then the device gets compromised. Edges should decay: a connection made 90 days ago carries less risk signal than one made yesterday.

Implementation: timestamp each edge, use exponential decay: edge_weight = exp(-λ × days_since_edge). CTDNE (Continuous-Time Dynamic Network Embeddings) and TGN (Temporal Graph Networks) handle this explicitly — they maintain node memory that updates as new edges arrive. Simpler production approach: rebuild the graph weekly with a 30-day lookback window and retrain.

Inductive vs transductive

Transductive GNNs (early GCNs): learn an embedding for each specific node in the training graph. Cannot generalise to new nodes at inference. Useless for fraud — new accounts appear continuously.

Inductive GNNs (GraphSAGE, GAT): learn a function that computes a node's embedding from its features and neighbourhood, not from a fixed embedding table. Can generalise to new nodes as long as the node has features and some edges. GraphSAGE is the standard choice for production fraud systems for this reason.

Feature engineering for the graph

Node features on user accounts: account age, verified status, KYC score, transaction velocity (7d, 30d), device count, average transaction amount, refund rate. Edge features on transaction edges: amount, time of day, merchant category, direction (send/receive). Edge features on shared-resource edges: resource type (device vs IP vs phone), resource age, how many total accounts share this resource.

Production architecture

Two-stage: GNN pre-computes node embeddings in batch (daily or hourly) → stored in Redis → served at transaction time alongside real-time tabular features → fed into a final classifier (XGBoost or logistic regression). The GNN handles the slow, structural signal; the tabular model handles fast, transaction-level signals.

Real-time GNN inference is possible but expensive — GraphSAGE with 2 layers and 10 neighbours per hop requires 100 feature lookups per prediction. Most teams precompute embeddings in batch and refresh every 1–4 hours.

The over-smoothing trap

With more than 3–4 message-passing layers, all node representations converge. The fraud ring members and the clean accounts look identical to the GNN. Use 2 layers for most fraud graphs. Add skip connections to preserve original features. Monitor embedding diversity (pairwise cosine similarity across nodes) as a training health metric.

Try on Colab: build a small fraud detection GNN using PyTorch Geometric. Create a synthetic dataset: 1000 accounts, 50 marked as fraud, connected in 5 rings of 10 accounts via a shared device node. Train a 2-layer GraphSAGE with BCELoss. Compare AUC against a tabular logistic regression using only node features (no edges). The GNN should achieve AUC > 0.95; the tabular model will be near-random for ring members who have no individual suspicious features.

Continue interactively
Read this post inside ML Systems Lab — with Simplify toggle, interview Q&As, inline glossary, and the MLE Path forward pointer.
Open in MSL →