@charles/paris-vertical-fl

5
0
flwr new @charles/paris-vertical-fl

Vertical logistic regression

Three parties hold different columns for the same passengers. The coordinator holds the survival labels. Together they train one logistic regression model. This is a teaching alternative to Flower's neural-network VFL example, using NumPy and Flower Datasets without Torch, a neural head or optimizer serialization.

ProcessLocal columns
SuperNode with partition-id=0Passenger class, sex, model intercept
SuperNode with partition-id=1Age, number of siblings/spouses aboard
SuperNode with partition-id=2Number of parents/children aboard, fare
ServerAppSurvival label

Every process also knows the same public row IDs. Unlike horizontal FL, a partition-id selects columns, not a different group of passengers. The actual SuperNode ID identifies the machine; its magnitude or discovery order does not determine its role.

One training step

For party k, let X_k be its feature matrix and w_k its local weights.

  1. Each party sends z_k = X_k @ w_k, one local logit per training passenger
  2. The coordinator adds the contributions: z = z_0 + z_1 + z_2
  3. It computes predictions p = sigmoid(z) and the binary cross-entropy loss
  4. It sends r = (p - y) / n to each party
  5. Each party updates w_k = w_k - learning_rate * X_k.T @ r
  6. The coordinator waits for all update acknowledgements before the next step

These are exactly the updates of centralized, full-batch logistic regression on the concatenated feature matrix. There is no averaging of local models. The coordinator never needs the feature columns or local weights to train. Prediction also needs all three parties because no party has the complete model.

Run from the teaching-kit root

Use the kit's prepared Python environment (uv sync --frozen if needed). The first run downloads the small public Titanic dataset.

For an isolated 20-step Ray simulation that saves logs and stops its services:

uv run --frozen --no-sync python scripts/rehearse_vfl.py \
  --simulation --output-dir outputs/vfl-simulation

Choose a new output directory on each rehearsal. To use the normal CLI with the local simulation connection instead:

uv run --frozen --no-sync flwr federation simulation-config '' local \
  --num-supernodes 3 --init-args-num-cpus 3 \
  --client-resources-num-cpus 1 --client-resources-num-gpus 0
uv run --frozen --no-sync flwr run apps/vertical-fl local --stream

The empty first argument selects the local SuperLink's default federation. This changes that local federation's simulation size to three.

For a three-step rehearsal with three real local SuperNode processes:

uv run --frozen --no-sync python scripts/rehearse_vfl.py \
  --output-dir outputs/vfl-deployment

The same app can run on your hosted federation when it has exactly three online nodes, configured with one each of partition-id=0, 1 and 2:

uv run --frozen --no-sync flwr run apps/vertical-fl supergrid \
  --federation @charles/flta \
  --run-config 'num-server-rounds=3' --stream

Each deployed node needs this app's dependencies (or runtime dependency installation) and access to the public dataset. The example loads its own Titanic feature slice, not the MNIST files from the distribution exercise.

What to look for

There are 709 training rows and 178 held-out test rows. Median imputation and standardization are fitted on training rows only. IDs are checked before combining predictions, and missing parties cause the run to fail.

With 20 updates at learning rate 0.5, local verification gives training loss 0.4534, test loss 0.4604 and test accuracy 78.7%. Step 0 has zero weights, loss 0.6931, and predicts probability 0.5 for everyone. Our >= 0.5 threshold initially predicts every passenger survived, so its initial test accuracy is only 36.0%. Accuracy need not improve at every step.

Read server_app.py first, then the two handlers in client_app.py. data.py handles the aligned column split. protocol.py contains transport checks and the small mathematical helpers.

What this does and does not protect

The messages contain row IDs, local logits and derivatives, rather than raw feature columns. This protocol does not protect label privacy: for binary labels, the sign of p - y reveals y when 0 < p < 1. Local logits may also leak feature information. There is no encryption of intermediate computations, secure aggregation, differential privacy or private entity matching here.

The dataset is public and downloaded by each process before column selection. This emulates ownership; it does not isolate real organizations' datasets. Production VFL needs aligned entities, an explicit threat model and appropriate privacy mechanisms. Use this example to understand and question the protocol.

See the instructor run sheet for the classroom walkthrough and optional exercises.