配置客户端#

除了模型参数,Flower 还可以向客户端发送配置值。配置值有多种用途。它们是一种从服务器控制客户端超参数的常用方法。

配置值#

配置值以字典的形式表示,字典的键为 str,值的类型为 boolbytes``double``(64 位精度浮点型)、``int``或 ``str`(或不同语言中的等效类型)。下面是一个 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 将这些配置字典(简称 config dict)序列化为 ProtoBuf 表示形式,使用 gRPC 将其传输到客户端,然后再反序列化为 Python 字典。

备注

目前,还不支持在配置字典中直接发送作为值的集合类型(例如,Set`, List, Map`)。有几种变通方法可将集合转换为支持的值类型之一(并在客户端将其转换回),从而将集合作为值发送。

例如,可以将浮点数列表转换为 JSON 字符串,然后使用配置字典发送 JSON 字符串,再在客户端将 JSON 字符串转换回浮点数列表。

通过内置策略进行配置#

向客户端发送配置值的最简单方法是使用内置策略,如 FedAvg。内置策略支持所谓的配置函数。配置函数是内置策略调用的函数,用于获取当前轮的配置字典。然后,它会将配置字典转发给该轮中选择的所有客户端。

让我们从一个简单的例子开始。想象一下,我们想要发送给客户端(a)应该使用的批次大小,(b)当前联邦学习的全局轮次,以及(c)客户端训练的遍历数。我们的配置函数可以是这样的:

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

为了让内置策略使用这个函数,我们可以在初始化时使用参数 on_fit_config_fn 将它传递给 FedAvg

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

在客户端,我们在 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)

还有一个 on_evaluate_config_fn 用于配置评估,其工作方式相同。它们是不同的函数,因为可能需要向 evaluate 发送不同的配置值(例如,使用不同的批量大小)。

内置策略每轮都会调用此函数(即每次运行 Strategy.configure_fitStrategy.configure_evaluate 时)。每轮调用 on_evaluate_config_fn 允许我们在连续几轮中改变配置指令。例如,如果我们想实现一个超参数时间表,以增加后几轮的本地遍历次数,我们可以这样做:

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

代码:`FedAvg`策略*每轮*都会调用该函数。

配置个别客户端#

在某些情况下,有必要向不同的客户端发送不同的配置值。

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)