运行模拟#

模拟联邦学习工作负载可用于多种案例:您可能希望在大量客户端上运行您的工作负载,但无需采购、配置和管理大量物理设备;您可能希望在您可以访问的计算系统上尽可能快地运行您的 FL 工作负载,而无需经过复杂的设置过程;您可能希望在不同数据和系统异构性、客户端可用性、隐私预算等不同水平的场景中验证您的算法。这些都是模拟 FL 工作负载的一些案例。Flower 可以通过其 "虚拟客户端引擎"(VirtualClientEngine)<contributor-explanation-architecture.html#virtual-client-engine>_或 VCE 来匹配这些情况。

:code:`VirtualClientEngine`用来规划,启动和管理`虚拟`客户端。这些客户端跟`非虚拟`客户端是一样的(即为您通过`flwr.client.start_client <ref-api-flwr.html#start-client>`_启动的客户端),因为它们可以通过创建一个继承自 `flwr.client.NumPyClient <ref-api-flwr.html#flwr.client.NumPyClient>`_ 的类进行配置,因此其行为方式相同。另外,由 VirtualClientEngine 管理的客户端有:

  • 资源感知:这意味着每个客户端都会分配到系统中的一部分计算和内存。作为用户,您可以在模拟开始时对其进行控制,从而控制 Flower FL 模拟的并行程度。每个客户端的资源越少,在同一硬件上并发运行的客户端就越多。

  • 自管理:这意味着用户无需手动启动客户端,而是由 VirtualClientEngine 负责。

  • 即时性:这意味着客户端只有在 FL 进程中需要它时才会被实体化(例如执行 fit() )。之后该对象将被销毁,释放分配给它的资源,并允许其他客户端以这种方式参与。

VirtualClientEngine`使用`Ray <https://www.ray.io/>`_来实现`虚拟`客户端,这是一个用于可扩展 Python 工作负载的开源框架。特别地,Flower :code:`VirtualClientEngine 使用 Actors 来生成 virtual 客户端并运行它们的工作负载。

启动 Flower 模拟#

运行 Flower 模拟器仍然需要定义客户端类、策略以及下载和加载(可能还需要分割)数据集的实用程序。在完成这些工作后,就可以使用 "start_simulation <ref-api-flwr.html#flwr.simulation.start_simulation>`_" 来启动模拟了,一个最简单的示例如下:

import flwr as fl
from flwr.server.strategy import FedAvg

def client_fn(cid: str):
    # Return a standard Flower client
    return MyFlowerClient().to_client()

# Launch the simulation
hist = fl.simulation.start_simulation(
    client_fn=client_fn, # A function to run a _virtual_ client when required
    num_clients=50, # Total number of clients available
    config=fl.server.ServerConfig(num_rounds=3), # Specify number of FL rounds
    strategy=FedAvg() # A Flower strategy
)

虚拟客户端引擎资源#

默认情况下,VCE 可以访问所有系统资源(即所有 CPU、所有 GPU 等),因为这也是启动 Ray 时的默认行为。不过,在某些设置中,您可能希望限制有多少系统资源用于模拟。您可以通过 ray_init_args 输入到 start_simulation 的参数来做到这一点,VCE 会在内部将该参数传递给 Ray 的 ray.init 命令。有关您可以配置的设置的完整列表,请查看 ray.init 文档。如果希望 VCE 使用系统中所有的 CPU 和 GPU,请不要设置 ray_init_args

import flwr as fl

# Launch the simulation by limiting resources visible to Flower's VCE
hist = fl.simulation.start_simulation(
    ...
    # Out of all CPUs and GPUs available in your system,
    # only 8xCPUs and 1xGPUs would be used for simulation.
    ray_init_args = {'num_cpus': 8, 'num_gpus': 1}
)

分配客户端资源#

默认情况下,VirtualClientEngine 会为每个虚拟客户端分配一个 CPU 内核(不分配其他任何内核)。这意味着,如果系统有 10 个内核,那么可以同时运行这么多虚拟客户端。

通常情况下,您可能希望根据 FL 工作负载的复杂性(即计算和内存占用)来调整分配给客户端的资源。您可以在启动模拟时将参数 client_resources 设置为 start_simulation 。Ray 内部使用两个键来调度和生成工作负载(在我们的例子中是 Flower 客户端):

  • num_cpus 表示客户端将获得的 CPU 内核数量。

  • num_gpus 表示分配给客户端的 GPU 内存的**比例**。

让我们来看几个例子:

import flwr as fl

# each client gets 1xCPU (this is the default if no resources are specified)
my_client_resources = {'num_cpus': 1, 'num_gpus': 0.0}
# each client gets 2xCPUs and half a GPU. (with a single GPU, 2 clients run concurrently)
my_client_resources = {'num_cpus': 2, 'num_gpus': 0.5}
# 10 client can run concurrently on a single GPU, but only if you have 20 CPU threads.
my_client_resources = {'num_cpus': 2, 'num_gpus': 0.1}

# Launch the simulation
hist = fl.simulation.start_simulation(
    ...
    client_resources = my_client_resources # A Python dict specifying CPU/GPU resources
)

虽然 client_resources 可用来控制 FL 模拟的并发程度,但这并不能阻止您在同一轮模拟中运行几十、几百甚至上千个客户端,并拥有数量级更多的 "休眠"(即不参与一轮模拟)客户端。比方说,您希望每轮有 100 个客户端,但您的系统只能同时容纳 8 个客户端。VirtualClientEngine 将安排运行 100 个工作(每个工作模拟策略采样的一个客户端),然后以资源感知的方式分批执行。

要了解资源如何用于调度 FL 客户端以及如何定义自定义资源的所有复杂细节,请查看 Ray 文档

模拟示例#

在 Tensorflow/Keras 和 PyTorch 中进行 Flower 模拟的几个可随时运行的完整示例已在 Flower 库 中提供。您也可以在 Google Colab 上运行它们:

多节点 Flower 模拟#

Flower 的 VirtualClientEngine 允许您在多个计算节点上运行 FL 模拟。在开始多节点模拟之前,请确保:

  1. 所有节点都有相同的 Python 环境。

  2. 在所有节点上都有一份代码副本(例如整个软件包)。

  3. 在所有节点中都有一份数据集副本(更多相关信息请参阅 模拟注意事项

  4. :code:`ray_init_args={"address"="auto"}`传递给 `start_simulation <ref-api-flwr.html#flwr.simulation.start_simulation>`_ ,这样 :code:`VirtualClientEngine`就会连接到正在运行的 Ray 实例。

  5. 在头部节点上启动 Ray:在终端上输入 raystart--head。该命令将打印几行输出,其中一行说明如何将其他节点连接到头部节点。

  6. 将其他节点附加到头部节点:复制启动头部后显示的命令,并在新节点的终端上执行:例如 ray start --address='192.168.1.132:6379'

完成上述所有操作后,您就可以在头部节点上运行代码了,就像在单个节点上运行模拟一样。

模拟结束后,如果要拆除集群,只需在每个节点(包括头部节点)的终端运行 ray stop 命令即可。

了解多节点模拟#

在此,我们列举了运行多节点 FL 模拟时的一些有趣功能:

使用 ray status 查看连接到头部节点的所有节点,以及 VirtualClientEngine 可用的总资源。

将新节点附加到头部节点时,头部节点将可见其所有资源(即所有 CPU 和 GPU)。这意味着 VirtualClientEngine 可以调度尽可能多的 "虚拟 "客户端来运行该节点。在某些设置中,您可能希望将某些资源排除在模拟之外。为此,您可以在任何 ray start 命令(包括启动头部时)中添加 --num-cpus=<NUM_CPUS_FROM_NODE>`和/或 `--num-gpus=<NUM_GPUS_FROM_NODE>

模拟的注意事项#

备注

我们正在积极开展这些方面的工作,以便使 FL 工作负载与 Flower 模拟的运行变得轻而易举。

当前的 VCE 允许您在模拟模式下运行联邦学习工作负载,无论您是在个人笔记本电脑上建立简单的场景原型,还是要在多个高性能 GPU 节点上训练复杂的 FL情景。虽然我们为 VCE 增加了更多的功能,但以下几点强调了在使用 Flower 设计 FL 时需要注意的一些事项。我们还强调了我们的实现中目前存在的一些局限性。

GPU 资源#

VCE 会为指定 client_resourcesnum_gpus 关键字的客户端分配 GPU 内存份额。也就是说,Ray(VCE 内部使用)是默认的:

  • 不知道 GPU 上可用的总 VRAM。这意味着,如果您设置 num_gpus=0.5,而系统中有两个不同(如 32GB 和 8GB)VRAM 的 GPU,它们都将同时运行 2 个客户端。

  • 不知道 GPU 上正在运行其他无关(即不是由 VCE 创建)的工作负载。从中可以得到以下两点启示:

    • 您的 Flower 服务器可能需要 GPU 来评估聚合后的 "全局模型"(例如在使用 "评估方法"<how-to-implement-strategies.html#the-evaluate-method>`_时)

    • 如果您想在同一台机器上运行多个独立的 Flower 模拟,则需要在启动实验时使用 CUDA_VISIBLE_DEVICES="<GPU_IDs>" 屏蔽 GPU。

此外,传递给 client_resources 的 GPU 资源限制并不是 "强制 "的(即可以超出),这可能导致客户端使用的 VRAM 超过启动模拟时指定的比例。

使用 GPU 的 TensorFlow#

在 TensorFlow <https://www.tensorflow.org/guide/gpu>`_ 中使用 GPU 时,几乎所有进程可见的 GPU 内存都将被映射。TensorFlow 这样做是出于优化目的。然而,在 FL 模拟等设置中,我们希望将 GPU 分割成多个 "虚拟 "客户端,这并不是一个理想的机制。幸运的是,我们可以通过 `启用内存增长 <https://www.tensorflow.org/guide/gpu#limiting_gpu_memory_growth>`_来禁用这一默认行为。

这需要在主进程(也就是服务器运行的地方)和 VCE 创建的每个角色中完成。通过 actor_kwargs,我们可以传递保留关键字`"on_actor_init_fn"`,以指定在角色初始化时执行的函数。在本例中,为了使 TF 工作负载的 GPU 增长,它看起来如下:

import flwr as fl
from flwr.simulation.ray_transport.utils import enable_tf_gpu_growth

# Enable GPU growth in the main thread (the one used by the
# server to quite likely run global evaluation using GPU)
enable_tf_gpu_growth()

# Start Flower simulation
hist = fl.simulation.start_simulation(
    ...
    actor_kwargs={
        "on_actor_init_fn": enable_tf_gpu_growth # <-- To be executed upon actor init.
    },
)

这正是 "Tensorflow/Keras 模拟 <https://github.com/adap/flower/tree/main/examples/simulation-tensorflow>`_"示例中使用的机制。

多节点设置#

  • VCE 目前不提供控制特定 "虚拟 "客户端在哪个节点上执行的方法。换句话说,如果不止一个节点拥有客户端运行所需的资源,那么这些节点中的任何一个都可能被调度到客户端工作负载上。在 FL 进程的稍后阶段(即在另一轮中),同一客户端可以由不同的节点执行。根据客户访问数据集的方式,这可能需要在所有节点上复制所有数据集分区,或采用数据集服务机制(如使用 nfs 或数据库)来避免数据重复。

  • 根据定义,虚拟客户端是 "无状态 "的,因为它们具有即时性。客户机状态可以作为 Flower 客户机类的一部分来实现,但用户需要确保将其保存到持久存储(如数据库、磁盘)中,而且无论客户机在哪个节点上运行,都能在以后检索到。这也与上述观点有关,因为在某种程度上,客户端的数据集可以被视为一种 "状态"。