Hierarchical Regional Aggregation Topologies
A flat federated topology sends every client’s update to one server, which makes the server a bandwidth bottleneck, a latency bottleneck, and a single point at which every update in the fleet is present at once. Inserting a tier of regional aggregators fixes the first two and changes the third: raw client updates now land at a regional node instead. Whether that is an improvement or a new exposure depends entirely on what the regional tier is allowed to see, which is a cryptographic question rather than a topology one. This page works through both, under model synchronization strategies in Federated Learning Workflows for Geospatial Data.
Parameter Configuration and Calibration
fan_in— clients per regional aggregator. Server ingress falls by roughly this factor. Values between 8 and 64 are typical; beyond that the regional node becomes its own bottleneck and its failure takes out a whole region’s round.regional_rounds— local aggregations per global round. Running several regional rounds before a global sync reduces cross-region traffic further and lets regional models specialise, at the cost of divergence between regions. One to three is the usual range; beyond that the global merge starts to lose the specialisation it was supposed to combine.secagg_scope— where masking is applied. Three options: client-to-regional only (regional node sees the regional aggregate, not individual clients), client-to-global end-to-end (regional node sees nothing useful, and merely forwards), or none. Only the first two are defensible, and the second is strictly stronger.min_clients_per_region— the floor for a regional aggregate to be forwarded. A regional aggregate over three clients is close to one client’s update. Below the floor, either hold the aggregate for the next round or forward the masked updates directly to the global tier.
| Topology | Server ingress | What the middle tier sees | Extra latency |
|---|---|---|---|
| Flat star | model | n/a | none |
| Regional, SecAgg to region | model | the regional aggregate | one hop |
| Regional, SecAgg end-to-end | model | ciphertext only | one hop + key setup |
| Regional, no SecAgg | model | every client update | one hop |
Reference Implementation
from __future__ import annotations
import math
from dataclasses import dataclass, field
from typing import Mapping, Sequence
class RegionTooSmall(RuntimeError):
"""A regional aggregate would expose too few clients to forward."""
@dataclass(frozen=True)
class Topology:
"""A two-tier aggregation plan and the properties that follow from it."""
clients: int
fan_in: int
model_mb: float
regional_rounds: int = 1
min_clients_per_region: int = 8
@property
def regions(self) -> int:
return max(1, math.ceil(self.clients / self.fan_in))
def server_ingress_mb(self) -> float:
"""Traffic reaching the global server per global round."""
return self.regions * self.model_mb
def regional_ingress_mb(self) -> float:
"""Traffic reaching ONE regional aggregator per global round."""
return self.fan_in * self.model_mb * self.regional_rounds
def flat_ingress_mb(self) -> float:
return self.clients * self.model_mb
def saving(self) -> float:
return 1.0 - self.server_ingress_mb() / self.flat_ingress_mb()
@dataclass
class RegionalAggregator:
"""One middle-tier node. Holds masked updates only if SecAgg reaches the region."""
region: str
topology: Topology
_received: list[tuple[str, float]] = field(default_factory=list)
def submit(self, client_id: str, masked_norm: float) -> None:
self._received.append((client_id, masked_norm))
def forward(self) -> tuple[float, int]:
"""Aggregate and forward, refusing to expose an under-populated region.
Refusing is the correct default: a regional aggregate over a handful of
clients is close to an individual update, and forwarding it upward would
make the middle tier a disclosure point rather than a bandwidth optimisation.
"""
if len(self._received) < self.topology.min_clients_per_region:
raise RegionTooSmall(
f"{self.region}: {len(self._received)} clients, floor is "
f"{self.topology.min_clients_per_region} — hold or forward masked"
)
total = sum(v for _, v in self._received)
count = len(self._received)
self._received.clear()
return total / count, count
def divergence_bound(regional_rounds: int, lr: float, grad_norm: float) -> float:
"""Worst-case drift between two regional models before a global merge.
Regional specialisation is the point of the middle tier and its risk: after
`regional_rounds` local steps two regions can be this far apart, and a naive
average of two badly diverged models is worse than either.
"""
return 2.0 * regional_rounds * lr * grad_norm
Validation Checkpoint
def _validate() -> None:
flat = Topology(clients=1024, fan_in=1024, model_mb=12.4)
tiered = Topology(clients=1024, fan_in=8, model_mb=12.4)
# 1. The tier must actually reduce server ingress by roughly the fan-in.
assert tiered.server_ingress_mb() < flat.flat_ingress_mb() / 7
assert 0.85 < tiered.saving() < 0.95, tiered.saving()
# 2. The saving at the server is paid for at the regional node.
assert tiered.regional_ingress_mb() == 8 * 12.4
# 3. More regional rounds increase regional traffic, not server traffic.
chatty = Topology(clients=1024, fan_in=8, model_mb=12.4, regional_rounds=3)
assert chatty.server_ingress_mb() == tiered.server_ingress_mb()
assert chatty.regional_ingress_mb() == 3 * tiered.regional_ingress_mb()
# 4. An under-populated region must refuse to forward.
node = RegionalAggregator("islet", tiered)
for i in range(4):
node.submit(f"c{i}", 1.0)
try:
node.forward()
except RegionTooSmall as exc:
assert "floor is 8" in str(exc)
else:
raise AssertionError("a small region must refuse to forward")
# 5. A populated region forwards a mean and its contributor count.
for i in range(8):
node.submit(f"d{i}", 2.0)
mean, count = node.forward()
assert abs(mean - 2.0) < 1e-9 and count == 12
# 6. Divergence grows linearly in the number of regional rounds.
assert abs(divergence_bound(3, 0.01, 1.0) / divergence_bound(1, 0.01, 1.0) - 3.0) < 1e-9
print("hierarchical topology: all assertions passed")
_validate()
Assertion 4 is the one that turns a topology decision into a privacy decision. Adding a middle tier without a population floor creates a place where a near-individual update sits in the clear, which is precisely the exposure the federated design was built to avoid.
Incident Response and Edge Cases
- One regional aggregator fails. Its whole region misses the round, which is a much larger correlated dropout than the flat topology ever produces. Plan for it: allow clients to fall back to a neighbouring region or directly to the global tier, and make the fallback path part of the regular test suite rather than an emergency measure.
- Regional models diverge and the global merge degrades. Reduce
regional_rounds, or merge with a weighted average that accounts for each region’s sample count rather than a plain mean. The divergence bound above gives an a-priori estimate of when this will start. - A region is consistently under the client floor. It is not a region. Merge it with its neighbour for aggregation, and be explicit that the finest aggregation granularity is coarser than the administrative map suggests.
- The middle tier is operated by a different party. Then the trust model changed and the topology question became a contractual one. End-to-end secure aggregation is the technical answer: the regional node forwards ciphertext it cannot open, and its role reduces to bandwidth.
- Latency rose more than the extra hop explains. Regional rounds are synchronous within the region, so a straggler now blocks a region rather than the fleet — smaller blast radius, but the same failure. Apply the same deadline or staleness policy at the regional tier as at the global one.
Before adopting a middle tier at all, measure whether the flat topology is actually constrained. Server ingress is the usual justification, and it is frequently not the binding constraint: rounds are more often limited by client-side compute, by straggler latency, or by the cohort size the privacy budget allows. A regional tier that solves a bandwidth problem you did not have adds a failure domain, a trust boundary and an extra hop of latency for nothing.
Frequently Asked Questions
Does a regional tier weaken the privacy guarantee?
It does unless secure aggregation covers the client-to-regional hop. Without masking, the regional node holds individual client updates in the clear — which is exactly the exposure the architecture exists to prevent, now moved somewhere less scrutinised. With masking to the region, the node sees only a regional aggregate; with end-to-end masking it sees nothing at all.
How many regional rounds before a global sync?
One to three for most deployments. More regional rounds save cross-region traffic and let regions specialise, but the models drift apart and the global merge starts destroying what it was meant to combine. The divergence grows linearly in the number of local rounds, so the trade is easy to bound in advance.
Should regions be geographic or network-topological?
Network-topological for bandwidth — the point is to keep traffic local — and geographic for the data. Where they conflict, prefer network topology for the aggregation tree and keep geography for stratified selection and reporting; conflating the two makes both harder to reason about.
What happens to the privacy budget in a two-tier design?
It is unchanged: a client still makes one release per round regardless of how many hops the update takes. What changes is where the noise is added — once, at the global aggregate — and that noise must be calibrated to the sensitivity of the whole fleet, not of a single region.
Related
- Model Synchronization Strategies — the parent method and its sync modes.
- Secure Aggregation Protocols — the masking that makes a middle tier safe.
- Implementing FedAvg for Spatial Time Series — the weighted merge used at each tier.
- Geographic Stratified Sampling for Client Cohorts — how clients reach a region in the first place.
Up one level: Model Synchronization Strategies · Section: Federated Learning Workflows for Geospatial Data.