시뮬레이션 실행#

Federated 학습 워크로드 시뮬레이션은 다양한 사용 사례에 유용합니다. 대규모 클라이언트 집단에서 워크로드를 실행하되 많은 수의 물리적 장치를 소싱, 구성 및 관리할 필요가 없는 경우, 복잡한 설정 과정을 거치지 않고도 액세스 가능한 컴퓨팅 시스템에서 최대한 빠르게 FL 워크로드를 실행하려는 경우, 다양한 수준의 데이터 및 시스템 이질성, 클라이언트 가용성, 개인정보 예산 등의 다양한 시나리오에서 알고리즘을 검증하려는 경우 등 여러 가지 사용 사례에 유용합니다. 이러한 사례는 FL 워크로드 시뮬레이션이 적합한 사용 사례 중 일부입니다. Flower는 VirtualClientEngine 또는 VCE를 통해 이러한 시나리오를 수용할 수 있습니다.

VirtualClientEngine`은 `virtual 클라이언트를 예약, 실행 및 관리합니다. 이러한 클라이언트는 non-virtual 클라이언트(예: flwr.client.start_client 명령을 통해 실행하는 클라이언트)와 동일하며, `flwr.client.NumPyClient <ref-api-flwr.html#flwr.client.NumPyClient>`_에서 상속하는 클래스 생성으로 구성될 수 있으므로 동일한 방식으로 동작합니다. 그 외에도 :code:`VirtualClientEngine`에 의해 관리되는 클라이언트는 다음과 같습니다:

  • resource-aware: 이는 각 클라이언트가 시스템에서 컴퓨팅 및 메모리의 일부를 할당받는다는 것을 의미합니다. 사용자는 시뮬레이션을 시작할 때 이를 제어할 수 있으며, 이를 통해 Flower FL 시뮬레이션의 병렬 처리 정도를 제어할 수 있습니다. 클라이언트당 리소스가 적을수록 동일한 하드웨어에서 더 많은 클라이언트를 동시에 실행할 수 있습니다.

  • self-managed: 이는 사용자가 클라이언트를 수동으로 실행할 필요가 없으며, 대신 :code:`VirtualClientEngine`의 내부에 위임된다는 의미입니다.

  • ephemeral: 이는 클라이언트가 FL 프로세스에서 필요할 때만 구체화됨을 의미합니다(예: `fit() <ref-api-flwr.html#flwr.client.Client.fit>`_을 수행하기 위해). 객체는 나중에 소멸되어 할당된 리소스를 해제하고 다른 클라이언트가 참여할 수 있도록 허용합니다.

VirtualClientEngine`은 확장 가능한 파이썬 워크로드를 위한 오픈 소스 프레임워크인 `Ray <https://www.ray.io/>`_를 사용하여 `virtual 클라이언트를 구현합니다. 특히 Flower의 VirtualClientEngine`은 `Actors <https://docs.ray.io/en/latest/ray-core/actors.html>`_를 사용하여 `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
)

VirtualClientEngine 리소스#

기본적으로 VCE는 모든 시스템 리소스(예: 모든 CPU, 모든 GPU 등)에 액세스할 수 있으며, 이는 Ray를 시작할 때의 기본 동작이기도 합니다. 그러나 일부 설정에서는 시뮬레이션에 사용되는 시스템 리소스의 수를 제한하고 싶을 수 있습니다. 이 설정은 VCE가 내부적으로 Ray의 ray.init 명령에 전달하는 start_simulation`에 대한 :code:`ray_init_args 입력 인수를 통해 수행할 수 있습니다. 구성할 수 있는 전체 설정 목록은 ray.init 설명서를 확인하세요. VCE가 시스템의 모든 CPU와 GPU를 사용하도록 하려면 :code:`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}
)

클라이언트 리소스 할당#

기본적으로 :code:`VirtualClientEngine`은 각 가상 클라이언트에 단일 CPU 코어를 할당합니다(그 외에는 아무것도 할당하지 않음). 즉, 시스템에 코어가 10개인 경우 그만큼 많은 가상 클라이언트를 동시에 실행할 수 있습니다.

대부분의 경우 FL 워크로드의 복잡성(즉, 컴퓨팅 및 메모리 사용량)에 따라 클라이언트에 할당되는 리소스를 조정하고 싶을 것입니다. 시뮬레이션을 시작할 때 client_resources argument를 `start_simulation <ref-api-flwr.html#flwr.simulation.start_simulation>`_로 설정하여 이를 수행할 수 있습니다. Ray는 내부적으로 두 개의 키를 사용하여 워크로드(이 경우 Flower 클라이언트)를 스케줄링하고 스폰합니다:

  • :code:`num_cpus`는 클라이언트에서 사용할 수 있는 CPU 코어 수를 나타냅니다.

  • :code:`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
)

code:`client_resources`를 사용하여 FL 시뮬레이션의 동시성 정도를 제어할 수 있지만, 동일한 라운드에서 수십, 수백 또는 수천 개의 클라이언트를 실행하고 훨씬 더 많은 ‘휴면’(즉, 라운드에 참여하지 않는) 클라이언트를 보유하는 것을 막을 수는 없습니다. 라운드당 100명의 클라이언트를 받고 싶지만 시스템이 동시에 8명의 클라이언트만 수용할 수 있다고 가정해 봅시다. code:`VirtualClientEngine`은 실행할 100개의 작업(각각 전략에서 샘플링한 클라이언트를 시뮬레이션)을 예약한 다음 리소스 인식 방식으로 8개씩 일괄적으로 실행합니다.

리소스가 FL 클라이언트를 예약하는 데 사용되는 방법과 사용자 지정 리소스를 정의하는 방법에 대한 모든 복잡한 세부 사항을 이해하려면 ‘Ray 문서 <https://docs.ray.io/en/latest/ray-core/scheduling/resources.html>’를 참조하세요.

시뮬레이션 예제#

Tensorflow/Keras와 파이토치에서 바로 실행할 수 있는 몇 가지 Flower 시뮬레이션 예제는 `Flower 레포지토리 <https://github.com/adap/flower>`_에서 제공됩니다. Google Colab에서도 실행할 수 있습니다:

멀티 노드 Flower 시뮬레이션#

Flower의 :code:`VirtualClientEngine`을 사용하면 여러 컴퓨팅 노드에서 FL 시뮬레이션을 실행할 수 있습니다. 멀티 노드 시뮬레이션을 시작하기 전에 다음 사항을 확인하세요:

  1. 모든 노드에서 동일한 Python 환경을 유지합니다.

  2. 모든 노드에 코드 사본(예: 전체 레포지토리)을 보관하세요.

  3. 모든 노드에 데이터 세트의 사본을 보유하세요(자세한 내용은 :ref:`simulation considerations <considerations-for-simulations>`에서 확인하세요)

  4. :code:`ray_init_args={“address”=”auto”}`를 `start_simulation <ref-api-flwr.html#flwr.simulation.start_simulation>`_에 전달하여 :code:`VirtualClientEngine`이 실행 중인 Ray 인스턴스에 연결되도록 합니다.

  5. 헤드 노드에서 Ray 시작: 터미널에서 :code:`ray start –head`를 입력합니다. 이 명령은 몇 줄을 출력하며, 그 중 하나는 다른 노드를 헤드 노드에 연결하는 방법을 나타냅니다.

  6. 헤드 노드에 다른 노드 연결: 헤드를 시작한 후 표시된 명령어을 복사하여 새 노드의 터미널에서 실행합니다: 예: ray start --address='192.168.1.132:6379'

위의 모든 작업이 완료되면 단일 노드에서 시뮬레이션을 실행할 때와 마찬가지로 헤드 노드에서 코드를 실행할 수 있습니다.

시뮬레이션이 완료되면 클러스터를 해체하려면 각 노드(헤드 노드 포함)의 터미널에서 ray stop 명령을 실행하기만 하면 됩니다.

멀티 노드 시뮬레이션에 대해 알아두면 좋은 사항#

여기에서는 멀티 노드 FL 시뮬레이션을 실행할 때 흥미로운 몇 가지 기능을 나열합니다:

사용자는 :code:`ray status`를 통해 헤드 노드에 연결된 모든 노드와 :code:`VirtualClientEngine`에 사용 가능한 총 리소스를 확인할 수 있습니다.

새 노드를 헤드에 연결하면 해당 노드의 모든 리소스(즉, 모든 CPU, 모든 GPU)가 헤드 노드에 표시됩니다. 즉, VirtualClientEngine`은 해당 노드가 실행할 있는 만큼의 `가상 클라이언트를 예약할 수 있습니다. 일부 설정에서는 시뮬레이션에서 특정 리소스를 제외하고 싶을 수 있습니다. 모든 ray start 명령(헤드 시작 시 포함)에 –num-cpus=<NUM_CPUS_FROM_NODE> 및/또는 `–num-gpus=<NUM_GPUS_FROM_NODE>`를 추가하여 이 작업을 수행하면 됩니다

시뮬레이션 시 고려 사항#

참고

Flower 시뮬레이션으로 모든 FL 워크로드를 간편하게 실행할 수 있도록 이러한 측면에서 적극적으로 노력하고 있습니다.

현재 VCE를 사용하면 개인 노트북에서 간단한 시나리오를 프로토타이핑하든, 여러 고성능 GPU 노드에서 복잡한 FL 파이프라인을 훈련하든 상관없이 시뮬레이션 모드에서 Federated 학습 워크로드를 실행할 수 있습니다. VCE에 더 많은 기능을 추가하는 동안, 아래에서는 Flower로 FL 파이프라인을 설계할 때 염두에 두어야 할 몇 가지 사항을 강조합니다. 또한 현재 구현에서 몇 가지 제한 사항을 강조합니다.

GPU 리소스#

VCE는 client_resources`에서 :code:`num_gpus 키를 지정하는 클라이언트에 GPU 메모리 공유를 할당합니다. 즉, (VCE에서 내부적으로 사용하는) Ray가 기본적으로 사용됩니다:

  • GPU에서 사용 가능한 총 VRAM을 인식하지 못합니다. 즉, 시스템에 서로 다른(예: 32GB와 8GB) VRAM 용량을 가진 두 개의 GPU가 있고 :code:`num_gpus=0.5`를 설정하면 둘 다 동시에 2개의 클라이언트를 실행하게 됩니다.

  • 관련 없는(즉, VCE에 의해 생성되지 않은) 다른 워크로드가 GPU에서 실행되고 있는지 알지 못합니다. 여기서 두 가지 시사점을 얻을 수 있습니다:

    • 집계 후 ‘글로벌 모델’을 평가하려면 Flower 서버에 GPU가 필요할 수 있습니다(예: `evaluate method <how-to-implement-strategies.html#the-evaluate-method>`_를 사용할 때)

    • 동일한 머신에서 여러 개의 독립적인 Flower 시뮬레이션을 실행하려면, 실험을 시작할 때 :code:`CUDA_VISIBLE_DEVICES=”<GPU_IDs>”`로 GPU를 마스킹해야 합니다.

또한 :code:`client_resources`에 전달된 GPU 리소스 제한이 ‘강제’되지 않아(즉, 초과할 수 있음) 클라이언트가 시뮬레이션을 시작할 때 지정된 비율보다 더 많은 VRAM을 사용하는 상황이 발생할 수 있습니다.

GPU를 사용한 TensorFlow#

TensorFlow와 함께 GPU를 사용 <https://www.tensorflow.org/guide/gpu>`_하면 프로세스에 보이는 모든 GPU의 거의 전체 GPU 메모리가 매핑됩니다. 이는 최적화 목적으로 TensorFlow에서 수행됩니다. 그러나 GPU를 여러 개의 ‘가상’ 클라이언트로 분할하려는 FL 시뮬레이션과 같은 설정에서는 이는 바람직한 메커니즘이 아닙니다. 다행히도 ‘메모리 증가 활성화’를 통해 이 기본 동작을 비활성화할 수 있습니다.

이 작업은 메인 프로세스(서버가 실행되는 곳)와 VCE에서 생성한 각 액터에서 수행해야 합니다. :code:`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 Simulation <https://github.com/adap/flower/tree/main/examples/simulation-tensorflow>`_ 예제에서 사용된 메커니즘입니다.

멀티 노드 설정#

  • VCE는 현재 특정 ‘가상’ 클라이언트를 어느 노드에서 실행할지 제어하는 방법을 제공하지 않습니다. 즉, 클라이언트가 실행하는 데 필요한 리소스가 하나 이상의 노드에 있는 경우 해당 노드 중 어느 노드에나 클라이언트 워크로드가 예약될 수 있습니다. FL 프로세스 후반부(즉, 다른 라운드에서)에는 동일한 클라이언트가 다른 노드에서 실행될 수 있습니다. 클라이언트가 데이터 세트에 액세스하는 방식에 따라 모든 노드에 모든 데이터 세트 파티션의 복사본을 보유하거나 데이터 중복을 피하기 위해 데이터 세트 제공 메커니즘(예: nfs, 데이터베이스 사용)을 사용해야 할 수 있습니다.

  • 정의상 가상 클라이언트는 임시적 특성으로 인해 ‘상태 없음’입니다. 클라이언트 상태는 Flower 클라이언트 클래스의 일부로 구현할 수 있지만, 사용자는 이를 영구 저장소(예: 데이터베이스, 디스크)에 저장하여 나중에 실행 중인 노드와 관계없이 동일한 클라이언트가 검색할 수 있도록 해야 합니다. 이는 어떤 식으로든 클라이언트의 데이터 세트가 일종의 ‘상태’로 볼 수 있기 때문에 위의 요점과도 관련이 있습니다.