How does this work?
Each round, every client copies the current global model, trains it for a few epochs on its
local slice of a synthetic 2-D, 3-class dataset, and sends the weights back. The server averages
those weights (FedAvg) into a new global model. Accuracy is measured on one shared held-out test set.
Raising imbalance skews each client toward different classes (via a Dirichlet split), so
their models disagree — exactly the hard case federated learning has to cope with.
Even normalization is federated: clients share only count/sum/sum² aggregates of their raw
points, and the server combines those into a global mean/std that everyone standardizes with.
The held-out test set never touches the stats. (Real deployments would protect even these
aggregates with secure aggregation.)