Configure a ClientApp¶

Flower provides the ability to send configuration values to clients, allowing server-side control over client behavior. This feature enables flexible and dynamic adjustment of client-side hyperparameters, improving collaboration and experimentation.

Sending ConfigRecords to a ClientApp¶

Make use of a ConfigRecord to send configuration values in a Message from your ServerApp to a ClientApp. A ConfigRecord is a special type of Python dictionary that allows communicating basic types such as int, float, string, bool and also bytes if you need to communicate more complex data structures that need to be serialized. Lists of these types are also supported.

Let’s see a few examples:

from flwr.app import ConfigRecord

# A config record can hold basic scalars
config = ConfigRecord({"lr": 0.1, "max-local-steps": 20000, "loss-w": [0.1, 0.2]})

# It can also communicate strings and booleans
config = ConfigRecord({"augment": True, "wandb-project-name": "awesome-flower-app"})

When you use a Flower strategy, the easiest way to get your ConfigRecord communicated as part of the Message that gets sent to the ClientApp is by passing it to the start of your strategy of choice (e.g. FedAvg). Let’s see how this looks in code:

# Create ServerApp
app = ServerApp()


@app.main()
def main(grid: Grid, context: Context) -> None:
    """Main entry point for the ServerApp."""

    # ... Read run config, initialize global model, etc
    # Initialize FedAvg strategy
    strategy = FedAvg()

    # Construct the config to be embedded into the Messages that will
    # be sent to the ClientApps
    config = ConfigRecord({"lr": 0.1, "optim": "adam-w", "augment": True})

    # Start strategy, run FedAvg for `num_rounds`
    result = strategy.start(
        grid=grid,
        initial_arrays=arrays,
        train_config=config,
        num_rounds=10,
    )

Passing the above ConfigRecord to the strategy’s start method ensures that the exact same ConfigRecord is received on the client side. But what if we’d like the configuration to change during the course of the federated learning process or as rounds advance?

Note

Note that Flower strategies insert the current server round number into the ConfigRecord for you under the key server-round. In this way, the ClientApp knows what’s the current round of the federated learning process. Note this is always inserted even if no ConfigRecord is passed to the strategy start method. When that’s the case, the only content of the ConfigRecord that arrives to the ClientApp will be such key with the corresponding round number.

Dynamic modification of ConfigRecord¶

Given a ConfigRecord passed upon starting the execution of a strategy (i.e. passed to the start method), the contents of the ConfigRecord that arrive to the ClientApp won’t change (with the exception of the value under the server-round key).

However, some applications do benefit or even require certain dynamism in the configuration values that one might send over to the ClientApps. For example, the learning rate the local optimizers at the ClientApps make use of. As the federated learning rounds go by, it is often reasonable to reduce the learning rate. This dynamism can be introduced at the strategy by implementing a custom strategy that just overrides the configure_train method. This method is responsible for, among other aspects, to create the Messages that will be sent to the ClientApps. These Messages would typically include an ArrayRecord carrying the parameters of the model to be federated as well as the ConfigRecord containing the configurations that the ClientApp should use. Let’s see how to design a custom strategy that alters the ConfigRecord passed to the start method.

Tip

To learn more about how configure_train and other methods in the strategies check the Strategies Explainer.

Let’s create a new class inheriting from FedAvg and override the configure_train method. We then use this new strategy in our ServerApp.

from typing import Iterable
from flwr.serverapp import Grid
from flwr.serverapp.strategy import FedAvg
from flwr.app import ArrayRecord, ConfigRecord, Message


class CustomFedAdagrad(FedAvg):
    def configure_train(
        self, server_round: int, arrays: ArrayRecord, config: ConfigRecord, grid: Grid
    ) -> Iterable[Message]:
        """Configure the next round of federated training and maybe do LR decay."""
        # Decrease learning rate by a factor of 0.5 every 5 rounds
        # Note: server_round starts at 1, not 0
        if server_round % 5 == 0:
            config["lr"] *= 0.5
            print("LR decreased to:", config["lr"])
        # Pass the updated config and the rest of arguments to the parent class
        return super().configure_train(server_round, arrays, config, grid)

In this how-to guide, we have shown how to define (when calling the start method of the strategy) and modify (by overriding the configure_train method) a ConfigRecord to customize how ClientApps perform training. You can follow equivalent steps to define and customize the ConfigRecord for an evaluation round. To do this, use the evaluate_config argument in the strategy’s start method and then optionally override the configure_evaluate method.