Skip to content

Commit

Permalink
Merge branch 'main' into fds-add-concatenate-divisions
Browse files Browse the repository at this point in the history
  • Loading branch information
adam-narozniak committed Mar 12, 2024
2 parents d651c10 + 1057001 commit bd04e37
Show file tree
Hide file tree
Showing 38 changed files with 897 additions and 874 deletions.
1 change: 0 additions & 1 deletion datasets/flwr_datasets/federated_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@ def __init__(
self._partitioners: Dict[str, Partitioner] = _instantiate_partitioners(
partitioners
)
self._partition_type = None
self._partition_division = self._initialize_partition_division(
partition_division
)
Expand Down
13 changes: 9 additions & 4 deletions examples/app-pytorch/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
trainloader, testloader = load_data()


# Define Flower client
# Define FlowerClient and client_fn
class FlowerClient(NumPyClient):

def fit(self, parameters, config):
Expand All @@ -31,16 +31,21 @@ def evaluate(self, parameters, config):


def client_fn(cid: str):
"""Create and return an instance of Flower `Client`."""
return FlowerClient().to_client()


# Run via `flower-client-app client:app`
app = ClientApp(client_fn=client_fn)
# Flower ClientApp
app = ClientApp(
client_fn=client_fn,
)


# Legacy mode
if __name__ == "__main__":
fl.client.start_client(
from flwr.client import start_client

start_client(
server_address="127.0.0.1:8080",
client=FlowerClient().to_client(),
)
15 changes: 9 additions & 6 deletions examples/app-pytorch/server.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import List, Tuple

import flwr as fl
from flwr.server import ServerApp, ServerConfig
from flwr.server.strategy import FedAvg
from flwr.common import Metrics, ndarrays_to_parameters

from task import Net, get_weights
Expand Down Expand Up @@ -33,7 +34,7 @@ def weighted_average(metrics: List[Tuple[int, Metrics]]) -> Metrics:


# Define strategy
strategy = fl.server.strategy.FedAvg(
strategy = FedAvg(
fraction_fit=1.0, # Select all available clients
fraction_evaluate=0.0, # Disable evaluation
min_available_clients=2,
Expand All @@ -43,19 +44,21 @@ def weighted_average(metrics: List[Tuple[int, Metrics]]) -> Metrics:


# Define config
config = fl.server.ServerConfig(num_rounds=3)
config = ServerConfig(num_rounds=3)


# Run via `flower-server-app server:app`
app = fl.server.ServerApp(
# Flower ServerApp
app = ServerApp(
config=config,
strategy=strategy,
)


# Legacy mode
if __name__ == "__main__":
fl.server.start_server(
from flwr.server import start_server

start_server(
server_address="0.0.0.0:8080",
config=config,
strategy=strategy,
Expand Down
10 changes: 9 additions & 1 deletion examples/app-pytorch/server_workflow.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from typing import List, Tuple

from task import Net, get_weights

import flwr as fl
from flwr.common import Context, Metrics
from flwr.common import Context, Metrics, ndarrays_to_parameters
from flwr.server import Driver, LegacyContext


Expand All @@ -26,12 +28,18 @@ def weighted_average(metrics: List[Tuple[int, Metrics]]) -> Metrics:
}


# Initialize model parameters
ndarrays = get_weights(Net())
parameters = ndarrays_to_parameters(ndarrays)


# Define strategy
strategy = fl.server.strategy.FedAvg(
fraction_fit=1.0, # Select all available clients
fraction_evaluate=0.0, # Disable evaluation
min_available_clients=2,
fit_metrics_aggregation_fn=weighted_average,
initial_parameters=parameters,
)


Expand Down
4 changes: 3 additions & 1 deletion examples/app-pytorch/task.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from collections import OrderedDict
from logging import INFO

import torch
import torch.nn as nn
import torch.nn.functional as F
from flwr.common.logger import log
from torch.utils.data import DataLoader
from torchvision.datasets import CIFAR10
from torchvision.transforms import Compose, Normalize, ToTensor
Expand Down Expand Up @@ -42,7 +44,7 @@ def load_data():

def train(net, trainloader, valloader, epochs, device):
"""Train the model on the training set."""
print("Starting training...")
log(INFO, "Starting training...")
net.to(device) # move model to GPU if available
criterion = torch.nn.CrossEntropyLoss().to(device)
optimizer = torch.optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
Expand Down
93 changes: 93 additions & 0 deletions examples/app-secure-aggregation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Secure aggregation with Flower (the SecAgg+ protocol) 🧪

> 🧪 = This example covers experimental features that might change in future versions of Flower
> Please consult the regular PyTorch code examples ([quickstart](https://github.com/adap/flower/tree/main/examples/quickstart-pytorch), [advanced](https://github.com/adap/flower/tree/main/examples/advanced-pytorch)) to learn how to use Flower with PyTorch.
The following steps describe how to use Secure Aggregation in flower, with `ClientApp` using `secaggplus_mod` and `ServerApp` using `SecAggPlusWorkflow`.

## Preconditions

Let's assume the following project structure:

```bash
$ tree .
.
├── client.py # Client application using `secaggplus_mod`
├── server.py # Server application using `SecAggPlusWorkflow`
├── workflow_with_log.py # Augmented `SecAggPlusWorkflow`
├── run.sh # Quick start script
├── pyproject.toml # Project dependencies (poetry)
└── requirements.txt # Project dependencies (pip)
```

## Installing dependencies

Project dependencies (such as and `flwr`) are defined in `pyproject.toml`. We recommend [Poetry](https://python-poetry.org/docs/) to install those dependencies and manage your virtual environment ([Poetry installation](https://python-poetry.org/docs/#installation)), but feel free to use a different way of installing dependencies and managing virtual environments if you have other preferences.

### Poetry

```shell
poetry install
poetry shell
```

Poetry will install all your dependencies in a newly created virtual environment. To verify that everything works correctly you can run the following command:

```shell
poetry run python3 -c "import flwr"
```

### pip

Write the command below in your terminal to install the dependencies according to the configuration file requirements.txt.

```shell
pip install -r requirements.txt
```

If you don't see any errors you're good to go!

## Run the example with one command (recommended)

```bash
./run.sh
```

## Run the example with the simulation engine

```bash
flower-simulation --server-app server:app --client-app client:app --num-supernodes 5
```

## Alternatively, run the example (in 7 terminal windows)

Start the Flower Superlink in one terminal window:

```bash
flower-superlink --insecure
```

Start 5 Flower `ClientApp` in 5 separate terminal windows:

```bash
flower-client-app client:app --insecure
```

Start the Flower `ServerApp`:

```bash
flower-server-app server:app --insecure --verbose
```

## Amend the example for practical usage

For real-world applications, modify the `workflow` in `server.py` as follows:

```python
workflow = fl.server.workflow.DefaultWorkflow(
fit_workflow=SecAggPlusWorkflow(
num_shares=<number of shares>,
reconstruction_threshold=<reconstruction threshold>,
)
)
```
34 changes: 34 additions & 0 deletions examples/app-secure-aggregation/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import time

from flwr.client import ClientApp, NumPyClient
from flwr.client.mod import secaggplus_mod
import numpy as np


# Define FlowerClient and client_fn
class FlowerClient(NumPyClient):
def fit(self, parameters, config):
# Instead of training and returning model parameters,
# the client directly returns [1.0, 1.0, 1.0] for demonstration purposes.
ret_vec = [np.ones(3)]
# Force a significant delay for testing purposes
if "drop" in config and config["drop"]:
print(f"Client dropped for testing purposes.")
time.sleep(8)
else:
print(f"Client uploading {ret_vec[0]}...")
return ret_vec, 1, {}


def client_fn(cid: str):
"""Create and return an instance of Flower `Client`."""
return FlowerClient().to_client()


# Flower ClientApp
app = ClientApp(
client_fn=client_fn,
mods=[
secaggplus_mod,
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ requires = ["poetry-core>=1.4.0"]
build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "secaggplus-mt"
name = "app-secure-aggregation"
version = "0.1.0"
description = "Secure Aggregation with Driver API"
description = "Flower Secure Aggregation example."
authors = ["The Flower Authors <[email protected]>"]

[tool.poetry.dependencies]
python = ">=3.8,<3.11"
flwr-nightly = { version = "^1.5.0.dev20230629", extras = ["simulation", "rest"] }
python = "^3.8"
# Mandatory dependencies
flwr-nightly = { version = "1.8.0.dev20240309", extras = ["simulation"] }
1 change: 1 addition & 0 deletions examples/app-secure-aggregation/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
flwr-nightly[simulation]==1.8.0.dev20240309
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ sleep 2
# Number of client processes to start
N=5 # Replace with your desired value

echo "Starting $N clients in background..."
echo "Starting $N ClientApps in background..."

# Start N client processes
for i in $(seq 1 $N)
Expand All @@ -22,8 +22,8 @@ do
sleep 0.1
done

echo "Starting driver..."
python driver.py
echo "Starting ServerApp..."
flower-server-app --insecure server:app --verbose

echo "Clearing background processes..."

Expand Down
45 changes: 45 additions & 0 deletions examples/app-secure-aggregation/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from flwr.common import Context
from flwr.server import Driver, LegacyContext, ServerApp, ServerConfig
from flwr.server.strategy import FedAvg
from flwr.server.workflow import DefaultWorkflow, SecAggPlusWorkflow

from workflow_with_log import SecAggPlusWorkflowWithLogs


# Define strategy
strategy = FedAvg(
fraction_fit=1.0, # Select all available clients
fraction_evaluate=0.0, # Disable evaluation
min_available_clients=5,
)


# Flower ServerApp
app = ServerApp()


@app.main()
def main(driver: Driver, context: Context) -> None:
# Construct the LegacyContext
context = LegacyContext(
state=context.state,
config=ServerConfig(num_rounds=3),
strategy=strategy,
)

# Create the workflow
workflow = DefaultWorkflow(
fit_workflow=SecAggPlusWorkflowWithLogs(
num_shares=3,
reconstruction_threshold=2,
timeout=5,
)
# # For real-world applications, use the following code instead
# fit_workflow=SecAggPlusWorkflow(
# num_shares=<number of shares>,
# reconstruction_threshold=<reconstruction threshold>,
# )
)

# Execute
workflow(driver, context)
Loading

0 comments on commit bd04e37

Please sign in to comment.