Federated learning for recommendation
Every model in this course assumed a comfortable premise: clicks, watch-times, and queries all land in one warehouse and you train on them. Federated learning challenges that premise directly — it trains a shared model while the raw behavior never leaves the device. A beautiful answer to privacy, a brutal answer to systems design.
Why anyone would accept this much pain
Centralizing raw user behavior is the default because it is easy and it works. Federation gives that up, so it had better be buying something. The trigger is 22 · Privacy turned into a constraint: regulation (GDPR, PIPL) plus eroding user trust make a central honeypot of raw behavior a liability rather than an asset. Federation buys three things back:
- Privacy & compliance. If a user's watch history, location-laced clicks, and dwell times never leave their phone, a whole class of breach and subpoena risk simply does not apply — there is no central store to leak.
- Breaking data silos. Two parties (a video app and a retailer) can jointly improve a model without either handing over raw logs — each trains locally, only model updates are shared.
- Freshness. The device holds the user's very latest interactions. A local update can reflect a click from ten seconds ago, versus a T+1 batch pipeline that is a day stale.
The honest framing: federation trades a large, recurring systems tax (communication, heterogeneity, convergence loss) for a reduction in privacy/legal risk. Whether the trade is positive depends entirely on how high the risk side is — which is why federation is real in mobile keyboards and health, and mostly aspirational in mainstream feed ranking.
FedAvg, derived
Start from ordinary SGD. To take one gradient step on the global model w you need the average gradient over all data:
Client k holds nk examples; n = Σ nk is the total. The key observation: this average is separable. Each client computes its own term ∇Lk on its own data, and the server needs only the data-size-weighted average. No raw example ever moves. That single fact is the whole reason federation is possible.
Naively you would have each client send one gradient per step. But a step is tiny and the network round-trip is enormous — you would spend almost all your time talking, not learning. The fix that defines Federated Averaging (FedAvg): let each client run many local SGD steps before it phones home, and average the resulting weights instead of single gradients. One round:
| # | Step | Who | On the wire |
|---|---|---|---|
| 1 | Select a small fraction C of available clients (e.g. 0.1% of phones idle, charging, on Wi-Fi) | server | — |
| 2 | Broadcast the current global model wt | server → clients | wt (down) |
| 3 | Local training: each client runs E epochs of SGD on its own data → wtk | each client | nothing leaves |
| 4 | Upload the update Δk = wtk − wt (a weight delta, not data) | clients → server | Δk (up) |
| 5 | Aggregate: wt+1 = wt + Σk (nk/n) · Δk | server | — |
Repeat for hundreds of rounds. The sanity check: when E = 1 and clients take a single full-batch step, FedAvg reduces exactly to centralized SGD. The interesting regime is E > 1, where it stops being equivalent and the tradeoffs begin.
The communication–computation tradeoff
The reason FedAvg exists is that bandwidth, not FLOPs, is the scarce resource. A phone has spare cycles when charging overnight; what it lacks is the willingness (or the data plan) to upload a full model every few milliseconds. So the design knob is E, the local epochs per round:
Crank E up and each client does more useful work between syncs, so you need fewer rounds → fewer uploads → less total communication. The win is large: FedAvg with E in the tens cuts communication rounds 10–100× versus naive per-step gradient sharing. But the win is not free.
Two cheaper tricks ride alongside E to shrink bytes-per-round directly:
- Sparsification + quantization. Send only the top-k largest-magnitude entries of Δk (e.g. top 10%) and round them to 1 bit — together this routinely cuts >90% of uplink bytes. You must add error feedback (accumulate the dropped residual locally, fold it into the next round) or the model develops a systematic bias.
- Differentiated cadence. Active users sync daily, dormant users weekly; stragglers don't hold up a round. Helps the tail but complicates convergence guarantees.
The non-IID problem — the quiet model killer
Centralized SGD shuffles all data, so every mini-batch is a roughly unbiased sample of the global distribution. Federation cannot shuffle: each client sees only its owner's data, and no two users are an i.i.d. sample of "everyone." One user watches only cooking videos; another only basketball. Their local gradients point in genuinely different directions. Two flavors:
- Feature-distribution shift — regional preference differences mean clients live in different parts of feature space.
- Label-distribution shift — different demographics have wildly different base click rates.
Averaging skewed local models drifts the result away from the true global optimum (client drift), and convergence slows or plateaus below centralized accuracy. How much? On a well-shuffled (IID) federated setup FedAvg essentially matches centralized training; under realistic non-IID splits the typical penalty is a 3–8% drop in accuracy / AUC, and in pathological cases (e.g. each phone sees only one category) it can stall far worse or fail to converge at all. Recommendation makes it worse because the user–item interaction matrix is extremely sparse: each client touches a sliver of the catalog, amplifying the skew. Standard responses:
| Technique | Idea | What it costs |
|---|---|---|
| FedProx | Add a proximal term μ‖w − wt‖² to the local loss, leashing each client's update to the global model so it can't drift too far. | One hyperparameter μ; slower local progress. |
| Personalized FL (Per-FedAvg / MAML-style) | Don't force one global model — learn a good shared initialization each client fine-tunes locally in a few steps. Accepts that users differ. | Each client keeps its own head; more eval complexity. |
| Layer split | Federate the shared lower layers (embeddings, feature extractors); keep the top ranking layers local and personalized. | Architectural commitment; harder to reason about globally. |
| Dynamic-weighted aggregation | Weight a client by more than its data size — down-weight clients whose distribution (by KL divergence to global) is an outlier. | Server must estimate distributions without seeing data. |
On top of statistical heterogeneity sits system heterogeneity: a flagship phone and a five-year-old budget device differ 10× in compute, so a synchronous round runs at the speed of its slowest participant. Stragglers are handled by over-selecting clients and dropping the laggards, by asynchronous aggregation, or by giving weak devices a smaller distilled model to train.
Horizontal vs vertical — which axis are you partitioning?
Everything above quietly assumed one kind of split: millions of phones, each holding the same feature schema but a different user. That is horizontal federated learning (HFL) — the data is partitioned by sample/user, the feature space is shared. It is the cross-device regime, and FedAvg is its workhorse. The on-device item-table problem below is an HFL setting through and through.
Vertical federated learning (VFL) flips the axis: the same users, but different feature spaces held by different organizations. A video platform knows your watch behavior; a bank knows your spend. Neither has the other's columns. Before you can train you must figure out which rows are the same person without revealing your user lists — encrypted entity alignment via private set intersection (PSI). Then training proceeds over encrypted intermediate representations exchanged between the parties (one side forwards its partial activations, gradients flow back), and only the party holding the labels ever sees the loss signal. This leans hard on the SMPC / homomorphic-encryption machinery from lesson 22 — far heavier crypto than HFL's plain averaging.
The third, sparser case is federated transfer learning: little overlap in either users or features, so you transfer a representation learned by one party to bootstrap another rather than jointly fitting one model. Naming the axis matters in an interview — "federated" alone is ambiguous until you say which dimension is shared.
Federating a multi-task ranker — gradient conflict, amplified
A production ranker is never single-objective — it jointly predicts watch-time, like, comment, share (12 · Multi-task). On a central trainer those heads already fight: the gradient that raises like-rate can lower the gradient for watch-time, because the behaviours trade off (a user who taps "like" and swipes away is the opposite of one who watches to the end). Centralized training averages that conflict over a shuffled, population-representative batch, so the tug-of-war is at least balanced. Federation breaks that balance.
The fix has two halves — balance the objectives locally before upload, then de-conflict at aggregation.
| Where | Technique | Mechanism |
|---|---|---|
| On device, before upload | GradNorm / uncertainty weighting (Kendall) | Re-scale each objective's loss so no single head's gradient dominates the local update — equalize gradient magnitudes (GradNorm) or weight by learned task uncertainty. Stops the "liker" client from uploading a like-only delta. |
| On device, before upload | Gradient projection (PCGrad-style) | If two objective gradients have negative inner product, project the conflicting one onto the normal plane of the main objective (watch-time) so they no longer cancel. Project all heads into a common subspace to kill the oscillation. |
| Server, at aggregation | Per-objective dynamic weights / CAGR | Weight each client by its per-objective contribution, not just data size — up-weight clients whose watch-time gradient is healthy; add a conflict-aware regularizer (CAGR) penalizing aggregated directions that worsen any objective. |
Worked projection. Take two objective gradients on a client — watch-time (the main objective) and like:
Their inner product is glike · gwatch = (−0.8)(1.0) + (0.6)(0.0) = −0.8 < 0 — a genuine conflict: stepping along glike partly undoes the watch-time step. Naive summation gives gwatch + glike = (0.2, 0.6), whose watch-time component has collapsed from 1.0 to 0.2 — the like head has eaten 80% of the watch-time progress. PCGrad projection removes the offending component of glike along gwatch:
Now the combined step is gwatch + glikeproj = (1.0, 0.6): watch-time keeps its full magnitude and the like head still contributes its non-conflicting component. The phone uploads a delta that improves the main objective and respects the secondary one, instead of an angry like-only vector that fights every other client at aggregation.
The recsys killer — where does the item embedding table live?
This is what makes federated recommendation genuinely different from federated keyboards or vision. As lesson 04 · Ranking models hammered, a recommender's parameters are dominated not by its dense network but by its embedding tables — one learned vector per user and per item. Three hundred million items at 64-dim fp32 is 300 × 10⁶ × 64 × 4 ≈ 77 GB. FedAvg's step 2 says "broadcast the global model to each client." You cannot broadcast a 77 GB item table to a phone, and the phone cannot train it.
The standard architecture is a hybrid central-item / federated-user split — exactly the sharding mindset from 19 · Large-scale optimization, but with the shard boundary drawn along a privacy line instead of just a memory line:
- User side (on device, federated). The user embedding and the personal ranking head live and train on the phone. They never leave — exactly the sensitive part you wanted to protect.
- Item side (central server). The big item table lives server-side. The phone receives only the vectors for the handful of items the server actually shows the user — those few hundred rows go down, and only their gradients come back up.
- Partial updates. Because each client touches almost no items, item-embedding updates are extremely sparse. The server aggregates these sparse partial updates across many clients into the central table. (And the very fact that a client uploads a gradient for item j leaks that it saw item j — which is precisely why secure aggregation, next, is not optional here.)
This split is why the clean "data stays put, model travels" slogan gets muddy for recommendation: the model is too big to travel, so you travel only the per-request slice of it, and you accept that the global item knowledge is effectively still centralized.
Secure aggregation and its link to differential privacy
A subtle point: FedAvg already keeps raw data on-device, but the updates can still leak. A single client's Δk, or the set of item gradients it sends, can be inverted to reconstruct training examples (gradient-inversion attacks), and the model can memorize, exposing it to membership-inference. Two complementary defenses:
- Secure aggregation (SecAgg). A cryptographic protocol so the server learns only the sum Σ Δk, never any individual Δk. Clients add pairwise-cancelling masks; the masks vanish in the sum but hide each contribution. The server gets the aggregate it needs and nothing more.
- Differential privacy (DP). Clip each update and add calibrated Gaussian noise so no single user's participation measurably changes the result — a formal, provable bound on what can be inferred (see 22 · Privacy). The cost is accuracy: more noise (smaller ε) means stronger privacy and a worse recommender, so ε is a tunable dial, not a free upgrade.
- By signal frequency. A dense, high-frequency signal (likes — many events per user) is easy to estimate even after heavy noising, so it can absorb a small ε (more noise). A sparse, high-value signal (completion / long watch — few events) is fragile, so give it a larger ε (less noise) or its signal vanishes. Counter-intuitive but it follows from where you can afford to lose precision.
- By user activity. Scale noise inversely to activity: a heavy user's gradient is an average over many interactions, so a fixed noise std barely dents it; a light user's gradient is nearly a single example, so the same noise drowns it. Give active users smaller ε (more noise, they can take it), light users larger ε.
- By feature sensitivity. Spend least on the most sensitive parameters — e.g. a tiny ε ≈ 0.5 on the user-embedding gradient, more on shared dense layers; noise inversely proportional to objective importance so the watch-time head stays sharp.
Robust aggregation — surviving poisoned clients
Gradient-inversion was a privacy threat: an honest server might learn too much. The mirror-image threat is integrity: a malicious or simply faulty client sends crafted or garbage updates — model poisoning, data poisoning, or a targeted backdoor that misfires only on a chosen trigger. Plain FedAvg is defenceless here. It computes a weighted mean, and a mean has breakdown point zero: a single contributor pushing a large enough Δk can drag the global model anywhere it likes.
The fix is to replace the mean with a statistic that resists outliers — a Byzantine-robust aggregator. Three standard ones:
| Aggregator | Idea |
|---|---|
| Krum | Score each update by the sum of distances to its nearest neighbours; select the single update most agreed-with, discarding the rest. An outlier is far from everyone and never wins. |
| Coordinate-wise median | Take the median of each parameter independently. A minority of extreme values can't move a median. |
| Trimmed mean | Drop the top and bottom β-fraction per coordinate, then average the rest — a tunable midpoint between mean and median. |
The catch is the same one the non-IID section warned about, now wearing a security hat: a legitimately different client looks exactly like an attacker. A user who only watches cooking videos sends an update that is a genuine outlier, and a robust aggregator will happily trim it away — so you buy robustness at the cost of throwing out real signal from the tail of the heterogeneity distribution. Tune β too aggressively and you re-introduce the convergence penalty you were fighting in the first place.
How do you even evaluate a federated model?
Every offline metric you know — AUC, NDCG, mean watch-time — assumes a central test set: pool the labels, score the model, compute the number. Federation forbids exactly that. The labels live on devices and never leave, so there is no place to run roc_auc_score(y, ŷ). This is a genuine, non-obvious failure mode: a team can build a flawless federated training pipeline and then discover it has no honest way to measure what it built. The answer mirrors training — don't move the data, aggregate the statistics.
The recurring trick: every metric you want is a function of some aggregate (a count, a sum, a sorted distribution). Compute the local piece on each device, then combine the pieces through SecAgg so the server sees only the global statistic, never a client's raw scores.
| Metric | Local computation (on device) | How it aggregates |
|---|---|---|
| Seed-aligned sampling | Server broadcasts one shared sampling seed; each client hashes its examples and keeps the same deterministic fraction (federated hashing). | Guarantees every client evaluates on a comparable slice without revealing which examples it held — the prerequisite for any of the rows below. |
| AUC (Fed-AUC) | Each client computes quantiles of its predicted scores for positives and negatives separately (a small score histogram, not the raw pairs). | SecAgg sums the per-client histograms into a global positive/negative score distribution; AUC is then read off the reconstructed curve. No individual score ever surfaces. |
| Watch-time (Fed-NDCG, mean) | Discretize watch-time into bins (short / mid / long) and emit an encrypted count per bin; optionally Box-Cox first to tame extreme values. | SecAgg adds the per-bin histograms; the mean and tail come from the summed histogram, not from any user's actual seconds-watched. |
Worked Fed-AUC. Say three clients each report, after SecAgg, a 4-bucket score histogram for positives and negatives. The summed global counts are:
AUC is the probability a random positive outscores a random negative, estimable from these histograms. Bucket-4 positives (50) beat all negatives in buckets 1–3 (60+25+10 = 95) and tie the 5 in bucket 4; bucket-3 positives (30) beat buckets 1–2 negatives (85); and so on. Summing winning pairs over 100 positives × 100 negatives = 10,000 comparisons gives 50·95 + 30·85 + 15·60 = 8,200 clear wins plus 1,225 same-bucket ties at half credit (612.5), so AUC ≈ (8,200 + 612.5) / 10,000 ≈ 0.88 — computed without the server ever seeing one (score, label) pair. Coarser buckets cost accuracy in the estimate; that granularity is itself a privacy knob.
Federated cold start — bootstrapping a client with no history
The non-IID section gave personalized FL (Per-FedAvg) for users who differ; cold start is the harder cousin — a brand-new user or a newly-joined client (a fresh region/platform) with almost no local data to fine-tune on, where the central tricks from 15 · Cold start and 38 · Cold start (advanced) can't run because the cold user's data won't move. Three named federated mechanisms, each attacking it differently:
| Mechanism | Idea | When it fits |
|---|---|---|
| FedMeta (Meta-FedAvg, MAML-style) | The server learns a meta-initialization — not a finished model but a starting point engineered so that a handful of local SGD steps adapt it fast. A new device downloads it and reaches usable personalization from a few likes/dwells. | New users on existing clients; you want one-shot-ish adaptation. |
| FedProto | Each client keeps local class-/interest-prototype vectors (centroids), not raw data. The server aggregates them into a prototype-similarity matrix that guides transfer — a cold client borrows the prototypes of similar clients to seed its representation. | New region/client whose feature space overlaps existing ones; prototypes are cheap and naturally privacy-light. |
| Federated distillation | The server holds a small clean public set, runs the global model to produce soft labels (or DP-GAN synthetic interactions), and distills that signal into cold clients — injecting global knowledge to counter the extreme non-IID of a data-starved client. | Severely sparse clients where even a shared init isn't enough; doubles as a non-IID smoother. |
The privacy tie-in: cold start is exactly when you'd relax the budget — F2's adaptive ε says spend more ε early (accept higher privacy cost to get a usable profile fast), then tighten as the user accumulates real local signal.
Interactive · federated round-cost estimator
The thing to feel is how communication cost behaves as you turn the dials. More local epochs cut rounds; compression cuts bytes-per-round; but a bigger model and more clients push everything back up. And the item table, if you ever tried to federate it, dwarfs all of it — which is the whole argument for the hybrid split above.
An honest verdict — when does federation actually pay?
Federated learning is real, deployed technology (mobile keyboard prediction is the canonical success). For a recommender, be clinical:
| Dimension | Centralized | Federated |
|---|---|---|
| Raw data location | central warehouse | stays on device / in silo |
| What's on the wire | raw logs | model updates only (ideally masked + noised) |
| Privacy / legal risk | high (a honeypot) | low (no central raw store) |
| Convergence / accuracy | best case (IID, shuffled) | worse (non-IID drift, DP noise) |
| Communication cost | one-time ingest | recurring, every round, every device |
| Item embedding table | trivially central | can't be on-device → hybrid split |
| Operational complexity | moderate | high (client selection, stragglers, SecAgg) |
Interview prompts you should be ready for
- "Derive FedAvg and say where it's exact versus approximate." (The full-data gradient is a data-size-weighted sum of per-client gradients, so it's separable — clients compute local terms, server averages weights. With E = 1 full-batch it is exactly centralized SGD; with E > 1 local steps drift and the averaged model is only an approximation of the centralized optimum.)
- "Why average weights every E epochs instead of gradients every step?" (Bandwidth, not compute, is scarce. Per-step gradient sharing spends all its time on network round-trips. Larger E means more local work per sync, fewer rounds, 10–100× less communication — at the cost of client drift that grows with non-IID-ness.)
- "What is the non-IID problem and how do you fight it?" (Clients aren't i.i.d. samples of the population; their local optima diverge, so averaging drifts off the global optimum. Mitigations: FedProx proximal term, personalized FL / per-client heads, layer splits, distribution-aware aggregation weights.)
- "You want to federate a recommender. Where does the 77 GB item embedding table go?" (It cannot live on a phone. Hybrid split: user embedding + personal head federated on-device; item table stays central; the phone gets only the few hundred item rows it's scoring, and uploads sparse partial gradients. This is the recsys-specific crux.)
- "FedAvg keeps raw data on-device — isn't that already private? What can still leak?" (No. Uploaded deltas are invertible via gradient-inversion; the released model can memorize. SecAgg hides individual updates from the server; DP bounds leakage from the released model. They cover different threats; use both.)
- "A client uploads a gradient for item j. What did you just leak, and how do you stop it?" (That the user interacted with item j — sparse item gradients are an interaction-history side channel. SecAgg so the server sees only the summed update, plus DP noise so j's presence isn't individually inferable.)
- "When would you NOT use federation for a feed recommender?" (When accuracy is paramount, the item table is unavoidably central, and you already hold the data lawfully. The recurring comms tax plus non-IID/DP accuracy hit lose to a well-run central pipeline. Federate only the small sensitive on-device piece.)
- "You federate a multi-task ranker — watch-time, like, comment. What breaks, and how do you fix it on-device versus at the server?" (Objective gradients conflict — like vs watch-time have negative inner product — and non-IID amplifies it because each client over-weights one objective. Fix locally before upload: GradNorm/uncertainty weighting to balance heads, PCGrad-style projection so conflicting gradients stop cancelling. Fix at aggregation: per-objective dynamic weights / CAGR so the priority adapts to each client. Walk the projection: g_like·g_watch < 0 → project g_like onto the normal of g_watch so watch-time keeps its full magnitude.)
- "Your federated model trains fine but you can't pool a test set. How do you compute AUC and watch-time?" (Aggregate statistics, not data. Seed-aligned sampling so clients evaluate comparable slices; for AUC, per-client positive/negative score quantile histograms summed via SecAgg into a global curve; for watch-time, encrypted binned histograms. DP-noise each released statistic and track cumulative leakage with Rényi-DP. Watch the A/B trap: device-ID-hash buckets must reconcile with federated-node partitions.)