Configure clients

En plus des paramètres du modèle, Flower peut envoyer des valeurs de configuration aux clients. Les valeurs de configuration peuvent être utilisées à diverses fins. Elles constituent, par exemple, un moyen populaire de contrôler les hyperparamètres côté client à partir du serveur.

Valeurs de configuration

Les valeurs de configuration sont représentées sous forme de dictionnaire avec des clés str` et des valeurs de type bool, bytes, double (float de précision 64 bits), int, ou str (ou des types équivalents dans d’autres langages). Voici un exemple de dictionnaire de configuration en Python :

config_dict = {
    "dropout": True,  # str key, bool value
    "learning_rate": 0.01,  # str key, float value
    "batch_size": 32,  # str key, int value
    "optimizer": "sgd",  # str key, str value
}

Flower sérialise ces dictionnaires de configuration (ou config dict en abrégé) dans leur représentation ProtoBuf, les transporte vers le client à l’aide de gRPC, puis les désérialise à nouveau en dictionnaires Python.

Note

Actuellement, il n’est pas possible d’envoyer directement des types de collections (par exemple, Set, List, Map) en tant que valeurs dans les dictionnaires de configuration. Il existe plusieurs solutions pour envoyer des collections en tant que valeurs en les convertissant en l’un des types de valeurs pris en charge (et en les reconvertissant du côté client).

On peut, par exemple, convertir une liste de nombres à virgule flottante en une chaîne JSON, puis envoyer la chaîne JSON à l’aide du dictionnaire de configuration, et enfin reconvertir la chaîne JSON en une liste de nombres à virgule flottante sur le client.

Configuration par le biais de stratégies intégrées

The easiest way to send configuration values to clients is to use a built-in strategy like FedAvg. Built-in strategies support so-called configuration functions. A configuration function is a function that the built-in strategy calls to get the configuration dictionary for the current round. It then forwards the configuration dictionary to all the clients selected during that round.

Commençons par un exemple simple. Imaginons que nous voulions envoyer (a) la taille du lot que le client doit utiliser, (b) le cycle global actuel de l’apprentissage fédéré et (c) le nombre d’époques à former du côté client. Notre fonction de configuration pourrait ressembler à ceci :

def fit_config(server_round: int):
    """Return training configuration dict for each round."""
    config = {
        "batch_size": 32,
        "current_round": server_round,
        "local_epochs": 2,
    }
    return config

To make the built-in strategies use this function, we can pass it to FedAvg during initialization using the parameter on_fit_config_fn:

strategy = FedAvg(
    ...,  # Other FedAvg parameters
    on_fit_config_fn=fit_config,  # The fit_config function we defined earlier
)

Côté client, nous recevons le dictionnaire de configuration dans fit :

class FlowerClient(flwr.client.NumPyClient):
    def fit(parameters, config):
        print(config["batch_size"])  # Prints `32`
        print(config["current_round"])  # Prints `1`/`2`/`...`
        print(config["local_epochs"])  # Prints `2`
        # ... (rest of `fit` method)

Il existe également une fonction on_evaluate_config_fn pour configurer l’évaluation, qui fonctionne de la même manière. Ce sont des fonctions séparées car on peut vouloir envoyer différentes valeurs de configuration à evaluate (par exemple, pour utiliser une taille de lot différente).

Les stratégies intégrées appellent cette fonction à chaque tour (c’est-à-dire à chaque fois que Strategy.configure_fit ou Strategy.configure_evaluate s’exécute). Appeler on_evaluate_config_fn à chaque tour nous permet de varier/changer le dict de config au cours de tours consécutifs. Si nous voulions mettre en place un calendrier d’hyperparamètres, par exemple, pour augmenter le nombre d’époques locales au cours des derniers tours, nous pourrions faire ce qui suit :

def fit_config(server_round: int):
    """Return training configuration dict for each round."""
    config = {
        "batch_size": 32,
        "current_round": server_round,
        "local_epochs": 1 if server_round < 2 else 2,
    }
    return config

The FedAvg strategy will call this function every round.

Configuration des clients individuels

Dans certains cas, il est nécessaire d’envoyer des valeurs de configuration différentes à des clients différents.

This can be achieved by customizing an existing strategy or by implementing a custom strategy from scratch. Here’s a nonsensical example that customizes FedAvg by adding a custom "hello": "world" configuration key/value pair to the config dict of a single client (only the first client in the list, the other clients in this round to not receive this « special » config value):

class CustomClientConfigStrategy(fl.server.strategy.FedAvg):
    def configure_fit(
        self, server_round: int, parameters: Parameters, client_manager: ClientManager
    ) -> List[Tuple[ClientProxy, FitIns]]:
        client_instructions = super().configure_fit(
            server_round, parameters, client_manager
        )

        # Add special "hello": "world" config key/value pair,
        # but only to the first client in the list
        _, fit_ins = client_instructions[0]  # First (ClientProxy, FitIns) pair
        fit_ins.config["hello"] = "world"  # Change config for this client only

        return client_instructions


# Create strategy and run server
strategy = CustomClientConfigStrategy(
    # ... (same arguments as plain FedAvg here)
)
fl.server.start_server(strategy=strategy)