FL Client over Secure RPCΒΆ

In this notebook, we will present how to launch a gRPC client as an FL client with an authenticator. To pair with the server notebook, we consider only one client.

[1]:
num_clients = 1

Load client configurationsΒΆ

We load the configuration for the client from examples/resources/configs/mnist/client_1.yaml

[2]:
from omegaconf import OmegaConf

client_config_file = "../../examples/resources/configs/mnist/client_1.yaml"
client_config = OmegaConf.load(client_config_file)
print(OmegaConf.to_yaml(client_config))
client_id: Client1
train_configs:
  device: cpu
  logging_output_dirname: ./output
  logging_output_filename: result
data_configs:
  dataset_path: ./resources/dataset/mnist_dataset.py
  dataset_name: get_mnist
  dataset_kwargs:
    num_clients: 2
    client_id: 0
    partition_strategy: class_noniid
    visualization: true
    output_dirname: ./output
    output_filename: visualization.pdf
comm_configs:
  grpc_configs:
    server_uri: localhost:50051
    max_message_size: 1048576
    use_ssl: false

πŸ’‘ We need to change the relative path in data_configs.dataset_path to point to the right file relative to this notebook.

πŸ’‘ We also need to change data_configs.dataset_kwargs.num_clients to 1 to make sure we only partition the MNIST dataset to one client split. We change data_configs.dataset_kwargs.visualization to False as well.

[3]:
client_config.data_configs.dataset_path = (
    "../../examples/resources/dataset/mnist_dataset.py"
)
client_config.data_configs.dataset_kwargs.num_clients = num_clients
client_config.data_configs.dataset_kwargs.visualization = False

Create secure SSL channel and authenticatorΒΆ

The client requires a root certificate to verify the server certificate. In this example, we provide that root certificate, assuming that the server uses self-signed certificate and key provided by gRPC official documentation.

πŸ’‘ Please check this tutorial for more details on how to generate SSL certificates for securing the gRPC connections in practice.

To use the provided root certificate, user just to need to set the following. If the user would like to use his own root certificate, just change this to the file path to the local root certificate.

[4]:
client_config.comm_configs.grpc_configs.use_ssl = True
client_config.comm_configs.grpc_configs.root_certificate = (
    "../../src/appfl/comm/grpc/credentials/root.crt"
)

We also need to set configurations to use the naive authenticator and provide the auth_token agreed with the server for authentication.

[5]:
client_config.comm_configs.grpc_configs.use_authenticator = True
client_config.comm_configs.grpc_configs.authenticator = "NaiveAuthenticator"
client_config.comm_configs.grpc_configs.authenticator_args = {
    "auth_token": "A_SECRET_DEMO_TOKEN"
}

Create the client agent and communicatorΒΆ

Now we are ready to create the client agent using the client_agent defined and modified above, as well as a GRPCClientCommunicator to send request to the server.

⚠️ Please make sure that you have started the server from the other notebook!

[6]:
from appfl.agent import ClientAgent
from appfl.comm.grpc import GRPCClientCommunicator

client_agent = ClientAgent(client_agent_config=client_config)
client_communicator = GRPCClientCommunicator(
    client_id=client_agent.get_id(),
    **client_config.comm_configs.grpc_configs,
)
appfl: βœ…[2025-01-08 09:56:31,188 Client1]: Logging to ./output/result_Client1_2025-01-08-09-56-31.txt

Start the FL experimentΒΆ

Client start the FL experiment by doing the following things:

  • Obtain general client-side configurations from the server and load them

  • Obtain the initial global model from the server

  • [Optional] Send the number of local data to the server

  • Iteratively train the model and update the global model until receiving a DONE status flag from the server.

πŸ’‘ The server is also logging several information regarding the recipe of client requests.

[7]:
# Obtain general client-side configurations from the server and load them
client_config = client_communicator.get_configuration()
client_agent.load_config(client_config)

# Obtain the initial global model from the server
init_global_model = client_communicator.get_global_model(init_model=True)
client_agent.load_parameters(init_global_model)

# Send the number of local data to the server
sample_size = client_agent.get_sample_size()
client_communicator.invoke_custom_action(
    action="set_sample_size", sample_size=sample_size
)

while True:
    client_agent.train()
    local_model = client_agent.get_parameters()
    if isinstance(local_model, tuple):
        local_model, meta_data_local = local_model[0], local_model[1]
    else:
        meta_data_local = {}
    new_global_model, metadata = client_communicator.update_global_model(
        local_model, **meta_data_local
    )
    if metadata["status"] == "DONE":
        break
    client_agent.load_parameters(new_global_model)
client_communicator.invoke_custom_action(action="close_connection")
appfl: βœ…[2025-01-08 09:56:38,152 Client1]:      Round   Pre Val?       Time Train Loss Train Accuracy   Val Loss Val Accuracy
appfl: βœ…[2025-01-08 09:56:39,176 Client1]:          0          Y                                          2.3006      15.9300
appfl: βœ…[2025-01-08 09:56:42,203 Client1]:          0          N     3.0259     0.0621        82.6719     0.1759      94.5900
appfl: βœ…[2025-01-08 09:56:43,288 Client1]:          1          Y                                          0.1759      94.5900
appfl: βœ…[2025-01-08 09:56:46,287 Client1]:          1          N     2.9982     0.0181        94.7969     0.1036      96.7900
appfl: βœ…[2025-01-08 09:56:47,328 Client1]:          2          Y                                          0.1036      96.7900
appfl: βœ…[2025-01-08 09:56:50,102 Client1]:          2          N     2.7733     0.0125        96.4062     0.0782      97.5500
appfl: βœ…[2025-01-08 09:56:51,153 Client1]:          3          Y                                          0.0782      97.5500
appfl: βœ…[2025-01-08 09:56:54,004 Client1]:          3          N     2.8507     0.0100        97.1719     0.0651      97.9100
appfl: βœ…[2025-01-08 09:56:55,064 Client1]:          4          Y                                          0.0651      97.9100
appfl: βœ…[2025-01-08 09:56:58,036 Client1]:          4          N     2.9708     0.0081        97.6406     0.0603      98.0500
appfl: βœ…[2025-01-08 09:56:59,089 Client1]:          5          Y                                          0.0603      98.0500
appfl: βœ…[2025-01-08 09:57:01,960 Client1]:          5          N     2.8703     0.0072        97.9531     0.0503      98.4800
appfl: βœ…[2025-01-08 09:57:03,026 Client1]:          6          Y                                          0.0503      98.4800
appfl: βœ…[2025-01-08 09:57:05,943 Client1]:          6          N     2.9161     0.0065        98.0000     0.0406      98.7400
appfl: βœ…[2025-01-08 09:57:07,040 Client1]:          7          Y                                          0.0406      98.7400
appfl: βœ…[2025-01-08 09:57:09,872 Client1]:          7          N     2.8316     0.0052        98.3906     0.0523      98.3900
appfl: βœ…[2025-01-08 09:57:10,958 Client1]:          8          Y                                          0.0523      98.3900
appfl: βœ…[2025-01-08 09:57:13,716 Client1]:          8          N     2.7568     0.0065        98.0781     0.0383      98.7300
appfl: βœ…[2025-01-08 09:57:14,771 Client1]:          9          Y                                          0.0383      98.7300
appfl: βœ…[2025-01-08 09:57:17,594 Client1]:          9          N     2.8216     0.0049        98.6562     0.0402      98.7100
[7]:
{}