Berlin Tech Meetup: The Future of Relational Foundation Models, Systems, and Real-World Applications

Register now:
PyG/Use Case11 min read

Loyalty Optimization: GNN on Customer-Program Graphs

US companies spend $75B annually on loyalty programs, yet 60% of reward spend is deadweight (given to customers who would have purchased anyway). Here is how to build a GNN that identifies truly incremental rewards.

PyTorch Geometric

TL;DR

  • 1Loyalty optimization is a causal graph problem. GNNs distinguish incremental behavior (caused by the reward) from deadweight spend (would have happened anyway) using customer-reward-purchase graph context.
  • 2HeteroConv on the customer-reward-tier graph models engagement patterns, referral networks, and reward redemption behavior for incrementality estimation.
  • 3On RelBench benchmarks, GNNs achieve 75.83 AUROC vs 62.44 for flat-table LightGBM. Network effects and engagement patterns drive the improvement.
  • 4The GNN predicts incrementality per reward-customer pair. An optimization layer allocates budget to maximize total incremental revenue.
  • 5KumoRFM predicts reward incrementality with one PQL query (76.71 AUROC zero-shot), identifying which rewards drive real behavior change.

The business problem

US companies spend $75 billion annually on loyalty programs. Bond research shows that 60% of this spend is deadweight loss: rewards given to customers who would have made the purchase regardless. The remaining 40% drives genuinely incremental behavior. Identifying which rewards, for which customers, at what time, actually change behavior is the key to loyalty program ROI.

Traditional loyalty analytics use simple metrics: reward earn rate, redemption rate, and member spend lift. These aggregate metrics hide the individual-level incrementality that determines true program ROI. A customer who earns 5x points but would have bought the same items anyway generates zero incremental value.

Why flat ML fails

  • No counterfactual reasoning: Flat models predict “will this customer purchase?” but not “will this customer purchase because of the reward?” The graph provides the control group: similar customers without the reward offer.
  • No referral network: Loyalty programs with referral bonuses create network effects. One customer's loyalty generates referred customers, multiplying the reward's value. Flat models miss this amplification.
  • No tier dynamics: Customers near a tier threshold exhibit different reward sensitivity than those mid-tier. The tier graph captures this positional context.
  • No reward interaction: Multiple simultaneous rewards (points bonus + free shipping + exclusive access) interact. The reward graph captures which combinations drive behavior vs creating diminishing returns.

The relational schema

schema.txt
Node types:
  Customer  (id, tier, points_balance, tenure, ltv)
  Reward    (id, type, value, expiry, conditions)
  Tier      (id, name, threshold, benefits)
  Product   (id, category, price, reward_eligible)
  Promotion (id, type, discount, target_segment)

Edge types:
  Customer  --[earned]-->     Reward    (timestamp, source)
  Customer  --[redeemed]-->   Reward    (timestamp, for_product)
  Customer  --[in_tier]-->    Tier
  Customer  --[purchased]-->  Product   (amount, timestamp)
  Customer  --[referred]-->   Customer  (signup_date)
  Promotion --[targeted]-->   Customer  (channel, timestamp)

The loyalty graph captures earn/redeem behavior, tier dynamics, referral networks, and promotion targeting. All the signals needed for incrementality estimation.

PyG architecture: HeteroConv for incrementality estimation

loyalty_model.py
import torch
import torch.nn.functional as F
from torch_geometric.nn import SAGEConv, HeteroConv, Linear

class LoyaltyGNN(torch.nn.Module):
    def __init__(self, hidden_dim=128):
        super().__init__()
        self.customer_lin = Linear(-1, hidden_dim)
        self.reward_lin = Linear(-1, hidden_dim)
        self.tier_lin = Linear(-1, hidden_dim)
        self.product_lin = Linear(-1, hidden_dim)

        self.conv1 = HeteroConv({
            ('customer', 'earned', 'reward'): SAGEConv(
                hidden_dim, hidden_dim),
            ('customer', 'redeemed', 'reward'): SAGEConv(
                hidden_dim, hidden_dim),
            ('customer', 'in_tier', 'tier'): SAGEConv(
                hidden_dim, hidden_dim),
            ('customer', 'purchased', 'product'): SAGEConv(
                hidden_dim, hidden_dim),
            ('customer', 'referred', 'customer'): SAGEConv(
                hidden_dim, hidden_dim),
        }, aggr='sum')

        self.conv2 = HeteroConv({
            ('customer', 'earned', 'reward'): SAGEConv(
                hidden_dim, hidden_dim),
            ('customer', 'in_tier', 'tier'): SAGEConv(
                hidden_dim, hidden_dim),
            ('customer', 'purchased', 'product'): SAGEConv(
                hidden_dim, hidden_dim),
            ('customer', 'referred', 'customer'): SAGEConv(
                hidden_dim, hidden_dim),
        }, aggr='sum')

        # Two heads: purchase prediction with and without reward
        self.with_reward = Linear(hidden_dim, 1)
        self.without_reward = Linear(hidden_dim, 1)

    def forward(self, x_dict, edge_index_dict):
        x_dict['customer'] = self.customer_lin(
            x_dict['customer'])
        x_dict['reward'] = self.reward_lin(x_dict['reward'])
        x_dict['tier'] = self.tier_lin(x_dict['tier'])
        x_dict['product'] = self.product_lin(x_dict['product'])

        x_dict = {k: F.relu(v) for k, v in
                  self.conv1(x_dict, edge_index_dict).items()}
        x_dict = self.conv2(x_dict, edge_index_dict)

        h = x_dict['customer']
        p_with = torch.sigmoid(self.with_reward(h).squeeze(-1))
        p_without = torch.sigmoid(
            self.without_reward(h).squeeze(-1))

        # Incrementality = P(purchase|reward) - P(purchase|no reward)
        return p_with, p_without, p_with - p_without

Dual-head architecture estimates purchase probability with and without reward. The difference is the incremental lift, identifying which rewards truly change behavior.

Expected performance

  • Aggregate metrics (heuristic): ~50 AUROC
  • LightGBM (flat-table): 62.44 AUROC
  • GNN (HeteroConv): 75.83 AUROC
  • KumoRFM (zero-shot): 76.71 AUROC

Or use KumoRFM in one line

KumoRFM PQL
PREDICT incremental_purchase FOR customer
USING customer, reward, tier, purchase, promotion

One PQL query. KumoRFM identifies which rewards drive incremental behavior vs deadweight spend.

Frequently asked questions

Why use GNNs for loyalty program optimization?

Loyalty programs create a rich graph: customers earn and redeem rewards, participate in tiers, respond to promotions, and influence other customers through referrals. GNNs model these interconnections to predict which rewards drive incremental behavior vs which are given to customers who would have purchased anyway (deadweight loss).

What graph structure represents a loyalty program?

Customers, rewards, tiers, promotions, and products form the graph. Edges connect customers to earned/redeemed rewards, customers to their tier, promotions to targeted customers, and customers to referred customers. The graph captures engagement depth and network effects of referral programs.

How do GNNs identify which rewards drive incremental behavior?

By comparing customers who received a reward to similar customers who did not (via the graph neighborhood), the GNN estimates the incremental lift of each reward type. Customers with many purchasing neighbors would have bought anyway; rewards to customers on the edge of purchasing are more incremental.

Can GNNs optimize reward allocation in real-time?

The GNN predicts incrementality per reward-customer pair. An optimization layer then allocates a limited reward budget to maximize total incremental revenue. Pre-computed customer embeddings enable real-time scoring of reward candidates at the point of interaction.

How does KumoRFM optimize loyalty programs?

KumoRFM takes your loyalty database (customers, transactions, rewards, tiers, promotions) and predicts incremental purchase probability with one PQL query. It identifies which customers will respond to rewards and which would purchase without them.

Learn more about graph ML

PyTorch Geometric is the open-source foundation for graph neural networks. Explore more layers, concepts, and production patterns.