Skip to content

ajbarea/phalanx-fl

 
 

Repository files navigation

Phalanx

Phalanx

Federated learning on the latest Flower, with OpenTelemetry-native observability

A research testbed built on the current Flower (flwr) app-model: a federated LoRA fine-tune of a tiny BERT over non-IID data, aggregating only the adapters, with every round and client emitted as OpenTelemetry traces and metrics.

CI Pipeline codecov Python Flower OpenTelemetry uv


What is this?

Phalanx is a federated-learning research testbed that rides the latest Flower release: the flwr Message API, flwr-datasets, HuggingFace Transformers, and PEFT/LoRA. Its distinguishing feature is OpenTelemetry-native observability. The server emits a span and FL metrics for every round; each client emits a span for its local train and evaluate passes. You can watch a federated run in any OTLP backend (Jaeger, Grafana Tempo, an OpenTelemetry Collector), which stock Flower does not provide out of the box.

The default showcase is a federated LoRA fine-tune of Google's tiny BERT on IMDB sentiment, partitioned non-IID with a Dirichlet partitioner. Only the LoRA adapters and the classification head are federated; the frozen backbone stays on each client, so the payload on the wire is small.

$ make trace                      # local simulation, traces printed to the console

[ROUND 1/3]
aggregate_train: Received 2 results and 0 failures
aggregate_evaluate: Received 2 results and 0 failures
  -> Aggregated MetricRecord: {'loss': 0.67, 'accuracy': 0.62}
# plus an `fl.round` OTel span (fl.round, fl.loss, fl.accuracy, fl.clients) per round,
# and an `fl.client.{train,evaluate}` span per participating client.

Quick start

git clone https://github.com/ajbarea/phalanx-fl.git
cd phalanx-fl

make sync        # install deps (CPU torch + HF stack + dev tools)
make smoke       # fast 2-round federated simulation
make trace       # run with OTel traces printed to the console (no collector needed)

make run runs the full simulation, make test runs the suite, make lint runs ruff + ty. Run make with no target for the full list.

Federation setup (flwr 1.31)

flwr 1.31 keeps simulation and federation settings in ~/.flwr/config.toml, not in pyproject.toml. The [tool.flwr.federations.local-simulation] block shipped in pyproject.toml is migrated there automatically on your first flwr run (flwr then comments the local copy out; see flwr#6824). Override the federation size per run without editing any file:

uv run flwr run . local-simulation --federation-config 'options.num-supernodes=10'

Observability

By default telemetry is recorded but not exported (no connection noise). Point it at a collector to export traces and metrics over OTLP:

export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
make run

Or print spans to the terminal with OTEL_TRACES_EXPORTER=console (this is what make trace does).


Architecture

A standard Flower app-model layout:

Module Role
phalanx/task.py Model (HF transformer + PEFT/LoRA), data (flwr-datasets, IID or Dirichlet non-IID), train/eval, adapter-state helpers
phalanx/client_app.py ClientApp: loads broadcast adapters, trains locally, replies with adapters only; wraps each pass in a client span
phalanx/server_app.py ServerApp + ObservableFedAvg: FedAvg over adapters; emits an fl.round span + aggregated loss/accuracy/participation metrics each round
phalanx/telemetry.py OpenTelemetry layer: tracer/meter providers, round/client spans, FL metrics; OTLP / console / in-memory exporters

Stack: Flower (Message API + Simulation Engine) · PyTorch + HuggingFace Transformers + PEFT/LoRA · flwr-datasets · OpenTelemetry · uv + Ruff + ty


Configuration

Run config lives in pyproject.toml under [tool.flwr.app.config], overridable per run with --run-config:

Key Default Meaning
num-server-rounds 3 number of FL rounds
model-name google/bert_uncased_L-2_H-128_A-2 HF model (tiny BERT)
dataset stanfordnlp/imdb HF dataset
num-labels 2 classification labels
partitioner dirichlet dirichlet (non-IID) or iid
dirichlet-alpha 0.5 lower means more label skew
local-epochs 1 local epochs per round
fraction-train / fraction-evaluate 0.1 client sampling fractions
otel-service-name phalanx-fl OTel service.name resource attribute
uv run flwr run . local-simulation --run-config 'num-server-rounds=5 partitioner=iid'

Documentation

Full docs at ajbarea.github.io/phalanx-fl, built with Zensical.


Why "Phalanx"

Greek φάλαγξ: the infantry formation where many soldiers advance as one disciplined line, each shield interlocked with the next. That is the shape of federated learning here. Many clients train in step and their updates aggregate into a single model that moves as one. The observability layer is the vantage point above the formation, where every unit and every round is in view.


  2026 AJ Barea

About

Break and defend federated learning in one JSON config — 9 aggregation strategies, 11 attack types, 22 datasets.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages

  • Python 88.5%
  • Makefile 7.1%
  • HTML 4.4%