diff --git a/README.rst b/README.rst index 30957ccc5..bc9ec58d0 100644 --- a/README.rst +++ b/README.rst @@ -17,13 +17,13 @@ development on your laptop to real-world production setups in geographically dis Core Features ============= -- **Scalable and resilient.** FEDn is highly scalable and resilient via a tiered - architecture where multiple aggregation servers (combiners) form a network to divide up the work to coordinate clients and aggregate models. +- **Scalable and resilient.** FEDn is scalable and resilient via a tiered + architecture where multiple aggregation servers (combiners) divide up the work to coordinate clients and aggregate models. Benchmarks show high performance both for thousands of clients in a cross-device setting and for large model updates in a cross-silo setting. FEDn has the ability to recover from failure in all critical components. -- **Security**. A key feature is that +- **Security**. FEDn is built using secure industry standard communication protocols (gRPC). A key feature is that clients do not have to expose any ingress ports. - **Track events and training progress in real-time**. FEDn tracks events for clients and aggregation servers, logging to MongoDB. This @@ -39,86 +39,13 @@ Core Features ML model type or framework. Support for Keras and PyTorch is available out-of-the-box. + Getting started =============== -Prerequisites -------------- - -- `Python 3.8, 3.9 or 3.10 `__ -- `Docker `__ -- `Docker Compose `__ - -Quick start ------------ - -Clone this repository, locate into it and start a pseudo-distributed FEDn network using docker-compose: - -.. code-block:: - - docker-compose up - -This starts up the needed backend services MongoDB and Minio, the API Server and one Combiner. You can verify deployment using these urls: - -- API Server: localhost:8092 -- Minio: localhost:9000 -- Mongo Express: localhost:8081 - -Next, we will prepare the client. A key concept in FEDn is the compute package - -a code bundle that contains entrypoints for training and (optionally) validating a model update on the client. -The following steps uses the compute package defined in the example project 'examples/mnist-pytorch'. - -Locate into 'examples/mnist-pytorch' and familiarize yourself with the project structure. The entrypoints -are defined in 'client/entrypoint'. The dependencies needed in the client environment are specified in -'requirements.txt'. For convenience, we have provided utility scripts to set up a virtual environment. - -Start by initializing a virtual enviroment with all of the required dependencies for this project. - -.. code-block:: - - bin/init_venv.sh - -Next create the compute package and a seed model: - -.. code-block:: - - bin/build.sh - -Uploade the generated files 'package.tgz' and 'seed.npz' using the API: +The best way to get started is to take the quickstart tutorial: -The next step is to configure and attach clients. For this we download data and make data partitions: - -Download the data: - -.. code-block:: - - bin/get_data - - -Split the data in 2 partitions: - -.. code-block:: - - bin/split_data - -Data partitions will be generated in the folder 'data/clients'. - -Now navigate to http://localhost:8090/network and download the client config file. Place it in the example working directory. - -To connect a client that uses the data partition 'data/clients/1/mnist.pt': - -.. code-block:: - - docker run \ - -v $PWD/client.yaml:/app/client.yaml \ - -v $PWD/data/clients/1:/var/data \ - -e ENTRYPOINT_OPTS=--data_path=/var/data/mnist.pt \ - --network=fedn_default \ - ghcr.io/scaleoutsystems/fedn/fedn:master-mnist-pytorch run client -in client.yaml --name client1 - -You are now ready to start training the model at http://localhost:8090/control. - -To scale up the experiment, refer to the README at 'examples/mnist-pytorch' (or the corresponding Keras version), where we explain how to use docker-compose to automate deployment of several clients. +- `Quickstart PyTorch `__ Documentation ============= @@ -128,6 +55,13 @@ You will find more details about the architecture, compute package and how to de - `Paper `__ +FEDn Studio +=============== +Scaleout develops a Django Application, FEDn Studio, that provides a UI, authentication/authorization, client identity management, project-based multitenancy for manging multiple projects, and integration with your MLOps pipelines. +There are also additional tooling and charts for deployments on Kubernetes including integration with several projects from the cloud native landscape. See `FEDn Framework `__ +for more information. + + Making contributions ==================== diff --git a/docs/apiclient.rst b/docs/apiclient.rst new file mode 100644 index 000000000..10576710a --- /dev/null +++ b/docs/apiclient.rst @@ -0,0 +1,21 @@ +APIClient +=============== + +FEDn comes with an *APIClient* for interacting with the FEDn network. The APIClient is a Python3 library that can be used to interact with the FEDn network programmatically. + + +The APIClient is available as a Python package on PyPI, and can be installed using pip: + +.. code-block:: bash + + $ pip install fedn + + +To initialize the APIClient, you need to provide the hostname and port of the FEDn API server. The default port is 8092. The following code snippet shows how to initialize the APIClient: + +.. code-block:: python + + from fedn import APIClient + client = APIClient("localhost", 8092) + +For more information on how to use the APIClient, see the :py:mod:`fedn.network.api.client`, and the example `Notebooks `_. diff --git a/docs/conf.py b/docs/conf.py index bd2032b0e..686e82b57 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -12,7 +12,7 @@ author = 'Scaleout Systems AB' # The full version, including alpha/beta/rc tags -release = '0.6.0' +release = '0.8.0' # Add any Sphinx extension module names here, as strings extensions = [ diff --git a/docs/deployment.rst b/docs/deployment.rst deleted file mode 100644 index 974d98842..000000000 --- a/docs/deployment.rst +++ /dev/null @@ -1,95 +0,0 @@ -Distributed Deployment -====================== - -This guide serves as reference deployment for setting up a FEDn network consisting of: - - One host/VM for the object storage and database services (MinIO, MongoDB) - - One host/VM for the controller / reducer - - One host/VM for the combiner - -.. note:: - In this guide we will deploy using the provived docker-compose templates. Please note that additional configurations would be needed for a production-grade network. - -Prerequisites -------------- - -Hosts / VMs -........... - -We assume that you have root access to 3 Ubuntu 20.04 LTS or 22.04 LTS Server hosts / VMs. We recommend at least 4 CPUs and 8GB RAM for the base services and the reducer, and 4 CPUs and 16BG RAM for the combiner host. Each host needs the following: - -- `Python >=3.8, <3.11 `_ -- `Docker `_ -- `Docker Compose `_ - - -Networking -.......... -You will need to configure security groups / ingress settings for each host matching the port settings in the docker-compose templates. -The reducer and clients need to be able to resolve the hostname for the combiners. In this example -we show how this can be achieved if no external DNS resolution is available, by setting "extra host" in the Docker containers for the Reducer and client. Note that there are many other possible ways to achieve this, depending on your setup. - -1. Deploy storage and database services (MinIO, MongoDB and MongoExpress) -------------------------------------------------------------------------- - -First, deploy MinIO and Mongo services on one of the hosts. Edit the `docker-compose.yaml` file to change the default passwords and ports. - -.. code-block:: bash - - sudo docker-compose up -d minio mongo mongo-express - -Remember to open ports on the host so that the API endpoints (the exported port in the 'ports' property for each of the services) can be reached. - -.. warning:: - The deployment of MinIO and MongoDB above is insecure. For a production network, please ensure production deployments of the base services. - -2. Deploy the reducer ---------------------- - -Copy the file "config/settings-reducer.yaml.template" to "config/settings-reducer.yaml", then - -a. Edit 'settings-reducer.yaml' to provide the connection settings for MongoDB and Minio from Step 1. -b. Copy 'config/extra-hosts-reducer.yaml.template' to 'config/extra-hosts-reducer.yaml' and edit it, adding a host:IP mapping for each combiner you plan to deploy. - -Then start the reducer: - -.. code-block:: bash - - sudo docker-compose \ - -f docker-compose.yaml \ - -f config/reducer-settings.override.yaml \ - -f config/extra-hosts-reducer.yaml \ - up -d reducer - -.. note:: - the use of 'extra-hosts-reducer.yaml' is a way to add the host:IP mapping to /etc/hosts in the Docker container in docker-compose. It can be skipped if you handle DNS resolution in some other way. - -3. Deploy combiners -------------------- - -Copy 'config/settings-combiner.yaml.template' to 'config/settings-combiner.yaml' and edit it to provide a name for the combiner (used as a unique identifier for the combiner in the FEDn network), a hostname (which is used by reducer and clients to connect to the combiner RPC server), -and the port (default is 12080, make sure to allow access to this port in your security group/firewall settings). -Also, provide the IP and port for the reducer under the 'controller' tag. Then deploy the combiner: - -.. code-block:: bash - - sudo docker-compose \ - -f docker-compose.yaml \ - -f config/combiner-settings.override.yaml \ - up -d combiner - -Optional: Repeat this step for any number of additional combiner nodes. Make sure to provide an unique name for each combiner, -and update extra_hosts for the reducer (you need to restart the reducer to do so). - -.. warning:: - Note that it is not possible to use the IP address as 'host'. gRPC does not support certificates based on IP addresses. - -4. Attach clients to the FEDn network -------------------------------------- - -You can now choose an example, upload a compute package and an initial model, and attach clients. - -- `Examples `__ - -.. note:: - The clients will also need to be able to resolve each combiner node usign the 'host' argument in the combiner settings file. - There is a template in 'config/extra-hosts-client.yaml.template' that can be modified for this purpose. diff --git a/docs/index.rst b/docs/index.rst index 060b12945..0a16df1f7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,10 +3,9 @@ :caption: Table of Contents introduction - quickstart architecture - deployment - interfaces + quickstart + apiclient aggregators helpers tutorial diff --git a/docs/interfaces.rst b/docs/interfaces.rst deleted file mode 100644 index 270f0110b..000000000 --- a/docs/interfaces.rst +++ /dev/null @@ -1,37 +0,0 @@ -User interfaces -=============== - -FEDn comes with an *APIClient* and a *Dashboard* for interacting with the FEDn network. The APIClient is a Python3 library that can be used to interact with the FEDn network programmatically. -The Dashboard is a web-based user interface that can be used to interact with the FEDn network through a web browser. - -APIClient --------------- -The APIClient is a Python3 library that can be used to interact with the FEDn network programmatically. The APIClient is available as a Python package on PyPI, and can be installed using pip: - -.. code-block:: bash - - $ pip install fedn - - -To initialize the APIClient, you need to provide the hostname and port of the FEDn API server. The default port is 8092. The following code snippet shows how to initialize the APIClient: - -.. code-block:: python - - from fedn import APIClient - client = APIClient("localhost", 8092) - -For more information on how to use the APIClient, see the :py:mod:`fedn.network.api.client`. - -Dashboard --------------- -The Dashboard is a web-based user interface that can be used to interact with the FEDn network through a web browser. The Dashboard is available as a Docker image, and can be run using the following command: - -.. code:: bash - - $ docker-compose up -d dashboard - -OBS! If you have followed any of the examples, the dashboard will already be running! -The Dashboard is now available at http://localhost:8090. If no compute package has been configured, the Dashboard will ask you to upload a compute package. -A compute package is a zip file containing the ML code that will be executed on the clients. -For more information on how to create a compute package, see the :ref:`tutorial-label`. After uploading a compute package, you will also need to upload an initial model. This initial model is -usually the initial weights for the model that will be trained. You can then navigate to the Control Panel to start a training session. diff --git a/docs/introduction.rst b/docs/introduction.rst index 6897690ba..b5895af8c 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -1,23 +1,22 @@ Introduction to Federated Learning ================================== -Federated Learning stands at the forefront of modern machine learning techniques, offering a novel approach to address challenges related to data privacy, security, +Federated Learning offers a novel approach to address challenges related to data privacy, security, and decentralized data distribution. In contrast to traditional machine learning setups where data is collected and stored centrally, -Federated Learning allows for collaborative model training while keeping data localized. This innovative paradigm proves to be particularly advantageous in +Federated Learning allows for collaborative model training while keeping data local with the data owner or device. This is particularly advantageous in scenarios where data cannot be easily shared due to privacy regulations, network limitations, or ownership concerns. At its core, Federated Learning orchestrates model training across distributed devices or servers, referred to as clients or participants. These participants could be diverse endpoints such as mobile devices, IoT gadgets, or remote servers. Rather than transmitting raw data to a central location, -each participant computes gradients locally based on its data. These gradients are then communicated to a central server, often called the aggregator or orchestrator. -The central server aggregates and combines the gradients from multiple participants to update a global model. +each participant computes gradients locally based on its data. These gradients are then communicated to a server, often called the aggregator. +The server aggregates and combines the gradients from multiple participants to update a global model. This iterative process allows the global model to improve without the need to share the raw data. FEDn: the SDK for scalable federated learning --------------------------------------------- -FEDn serves as a System Development Kit (SDK) tailored for scalable federated learning. +FEDn serves as a System Development Kit (SDK) enabling scalable federated learning. It is used to implement the core server side logic (including model aggregation) and the client side integrations. -It implements functionality to deploy and scale the server side in geographically distributed setups. Developers and ML engineers can use FEDn to build custom federated learning systems and bespoke deployments. @@ -28,10 +27,10 @@ adapting to varying project needs and geographical considerations. Scalable and Resilient ...................... -FEDn exhibits scalability and resilience, thanks to its multi-tiered architecture. Multiple aggregation servers, known as combiners, -form a network to divide the workload, coordinating clients, and aggregating models. +FEDn exhibits scalability and resilience, thanks to its tiered architecture. Multiple aggregation servers, in FEDn called combiners, +form a network to divide the workload of coordinating clients and aggregating models. This architecture allows for high performance in various settings, from thousands of clients in a cross-device environment to -large model updates in a cross-silo scenario. Crucially, FEDn has built-in recovery capabilities for all critical components, enhancing system reliability. +large model updates in a cross-silo scenario. Importantly, FEDn has built-in recovery capabilities for all critical components, enhancing system reliability. ML-Framework Agnostic ..................... @@ -42,20 +41,18 @@ This flexibility allows for out-of-the-box support for popular frameworks like K Security ......... -A key security feature of FEDn is its client protection capabilities, negating the need for clients to expose any ingress ports, +A key security feature of FEDn is its client protection capabilities - clients do not need to expose any ingress ports, thus reducing potential security vulnerabilities. Event Tracking and Training progress .................................... -To ensure transparency and control over the learning process, -FEDn logs events in the federation and does real-time tracking of training progress. A flexible API lets the user define validation strategies locally on clients. +To ensure transparency and control over the training process, as well as to provide means to troubleshoot distributed deployments, +FEDn logs events and does real-time tracking of training progress. A flexible API lets the user define validation strategies locally on clients. Data is logged as JSON to MongoDB, enabling users to create custom dashboards and visualizations easily. -User Interfaces +REST-API and Python API Client ............... -FEDn offers a Flask-based Dashboard that allows users to monitor client model validations in real time. It also facilitates tracking client training time distributions -and key performance metrics for clients and combiners, providing a comprehensive view of the system’s operation and performance. - -FEDn also comes with an REST-API for integration with external dashboards and visualization tools, or integration with other systems. \ No newline at end of file +FEDn comes with an REST-API, a CLI and a Python API Client for programmatic interaction with a FEDn network. This allows for flexible automation of experiments, for integration with +other systems, and for easy integration with external dashboards and visualization tools. \ No newline at end of file diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 2b89ff165..ca60fd149 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -1,5 +1,17 @@ -Quick Start -=========== +Quickstart Tutorial PyTorch (MNIST) +=================================== + +This classic example of hand-written text recognition is well suited as a lightweight test when developing on FEDn in pseudo-distributed mode. +A normal high-end laptop or a workstation should be able to sustain a few clients. +The example automates the partitioning of data and deployment of a variable number of clients on a single host. +We here assume working experience with containers, Docker and docker-compose. + +Prerequisites +------------- + +- `Python 3.8, 3.9 or 3.10 `__ +- `Docker `__ +- `Docker Compose `__ Clone this repository, locate into it and start a pseudo-distributed FEDn network using docker-compose: @@ -8,14 +20,18 @@ Clone this repository, locate into it and start a pseudo-distributed FEDn networ docker-compose up +This starts up the needed backend services MongoDB and Minio, the API Server and one Combiner. +You can verify the deployment using these urls: -This will start up all neccecary components for a FEDn network, execept for the clients. +- API Server: http://localhost:8092/get_controller_status +- Minio: http://localhost:9000 +- Mongo Express: http://localhost:8081 .. warning:: The FEDn network is configured to use a local Minio and MongoDB instances for storage. This is not suitable for production, but is fine for testing. .. note:: - You have the option to programmatically interact with the FEDn network using the Python APIClient, or you can use the Dashboard. In these Note sections we will use the APIClient. + To programmatically interact with the FEDn network use the APIClient. Install the FEDn via pip: .. code-block:: bash @@ -25,10 +41,12 @@ This will start up all neccecary components for a FEDn network, execept for the $ cd fedn $ pip install . -Navigate to http://localhost:8090. You should see the FEDn Dashboard, asking you to upload a compute package. The compute package is a tarball of a project. -The project in turn implements the entrypoints used by clients to compute model updates and to validate a model. +Next, we will prepare the client. A key concept in FEDn is the compute package - +a code bundle that contains entrypoints for training and (optionally) validating a model update on the client. -Locate into 'examples/mnist-pytorch'. +Locate into ``examples/mnist-pytorch`` and familiarize yourself with the project structure. The entrypoints +are defined in 'client/entrypoint'. The dependencies needed in the client environment are specified in +``requirements.txt``. For convenience, we have provided utility scripts to set up a virtual environment. Start by initializing a virtual enviroment with all of the required dependencies for this project. @@ -42,17 +60,15 @@ Now create the compute package and an initial model: bin/build.sh -Upload the generated files 'package.tgz' and 'seed.npz' in the FEDn Dashboard. -.. note:: - Instead of uploading in the dashboard do: +Upload the compute package and seed model to FEDn: - .. code:: python +.. code:: python - >>> from fedn import APIClient - >>> client = APIClient(host="localhost", port=8092) - >>> client.set_package("package.tgz", helper="pytorchhelper") - >>> client.set_initial_model("seed.npz") + >>> from fedn import APIClient + >>> client = APIClient(host="localhost", port=8092) + >>> client.set_package("package.tgz", helper="numpyhelper") + >>> client.set_initial_model("seed.npz") The next step is to configure and attach clients. For this we need to download data and make data partitions: @@ -71,19 +87,26 @@ Split the data in 2 parts for the clients: Data partitions will be generated in the folder 'data/clients'. -Now navigate to http://localhost:8090/network and download the client config file. Place it in the example working directory. -.. note:: - In the python enviroment you installed FEDn: +FEDn relies on a configuration file for the client to connect to the server. Create a file called 'client.yaml' with the follwing content: + +.. code-block:: + + network_id: fedn-network + discover_host: api-server + discover_port: 8092 - .. code:: python +(optional) Use the APIClient to fetch the client configuration and save it to a file: - >>> import yaml - >>> config = client.get_client_config(checksum=True) - >>> with open("client.yaml", "w") as f: - >>> f.write(yaml.dump(config)) +.. code:: python -To connect a client that uses the data partition 'data/clients/1/mnist.pt': + >>> import yaml + >>> config = client.get_client_config(checksum=True) + >>> with open("client.yaml", "w") as f: + >>> f.write(yaml.dump(config)) + +Make sure to move the file ``client.yaml`` to the root of the examples/mnist-pytorch folder. +To connect a client that uses the data partition ``data/clients/1/mnist.pt`` and the config file ``client.yaml`` to the network, run the following docker command: .. code-block:: @@ -92,27 +115,58 @@ To connect a client that uses the data partition 'data/clients/1/mnist.pt': -v $PWD/data/clients/1:/var/data \ -e ENTRYPOINT_OPTS=--data_path=/var/data/mnist.pt \ --network=fedn_default \ - ghcr.io/scaleoutsystems/fedn/fedn:develop-mnist-pytorch run client -in client.yaml --name client1 + ghcr.io/scaleoutsystems/fedn/fedn:0.8.0-mnist-pytorch run client -in client.yaml --name client1 -.. note:: - If you are using the APIClient you must also start the training client via "docker run" command as above. +Observe the API Server logs and combiner logs, you should see the client connecting. +You are now ready to start training the model. In the python enviroment you installed FEDn: -You are now ready to start training the model at http://localhost:8090/control. +.. code:: python -.. note:: - In the python enviroment you installed FEDn you can start training via: + >>> ... + >>> client.start_session(session_id="test-session", rounds=3) + # Wait for training to complete, when controller is idle: + >>> client.get_controller_status() + # Show model trail: + >>> client.get_model_trail() + # Show model performance: + >>> client.list_validations() - .. code:: python +Please see :py:mod:`fedn.network.api` for more details on the APIClient. - >>> ... - >>> client.start_session(session_id="test-session", rounds=3) - # Wait for training to complete, when controller is idle: - >>> client.get_controller_status() - # Show model trail: - >>> client.get_model_trail() - # Show model performance: - >>> client.list_validations() +There is also a Jupyter `Notebook `_ version of this tutorial including examples of how to fetch and visualize model validations. + +Automate and scale up experimentation with several clients +---------------------------------------------------------- +Now that you have an understanding of the main components of FEDn, you can use the provided docker-compose templates to automate deployment of FEDn and clients. +To start the network and attach 4 clients. Standing in ``examples/mnist-pytorch``, run the following docker compose command: + +.. code-block:: + + docker-compose -f ../../docker-compose.yaml -f docker-compose.override.yaml up --scale client=4 + + +Access logs and validation data from MongoDB +-------------------------------------------- +You can access and download event logs and validation data via the API, and you can also as a developer obtain +the MongoDB backend data using pymongo or via the MongoExpress interface: + +- http://localhost:8081/db/fedn-network/ + +The credentials are as set in docker-compose.yaml in the root of the repository. + +Access model updates +-------------------- + +You can obtain model updates from the 'fedn-models' bucket in Minio: + +- http://localhost:9000 + + +Clean up +-------- +You can clean up by running + +.. code-block:: - Please see :py:mod:`fedn.network.api` for more details on the APIClient. + docker-compose down -To scale up the experiment, refer to the README at 'examples/mnist-pytorch' (or the corresponding Keras version), where we explain how to use docker-compose to automate deployment of several clients. diff --git a/docs/tutorial.rst b/docs/tutorial.rst index b37053904..72bb0f504 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -15,7 +15,7 @@ The compute package :width: 100% :align: center -The *compute package* is a tar.gz bundle of the code to be executed by each data-provider/client. +The *compute package* is a .tgz bundle of the code to be executed by each data-provider/client. This package is uploaded to the *Controller* upon initialization of the FEDN Network (along with the initial model). When a client connects to the network, it downloads and unpacks the package locally and are then ready to participate in training and/or validation. @@ -39,7 +39,7 @@ In the examples we have roughly the following file and folder structure: | └── docker-compose.yml/Dockerfile | -The "client" folder is the *compute package* which will become a tar.gz bundle of the code to be executed by +The "client" folder is the *compute package* which will become a .tgz bundle of the code to be executed by each data-provider/client. The entry points, mentioned above, are defined in the *fedn.yaml*: .. code-block:: yaml @@ -77,7 +77,7 @@ A *entrypoint.py* example can look like this: from fedn.utils.helpers.helpers import get_helper, save_metadata, save_metrics - HELPER_MODULE = 'pytorchhelper' + HELPER_MODULE = 'numpyhelper' NUM_CLASSES = 10 def _compile_model(): @@ -286,9 +286,9 @@ A *entrypoint.py* example can look like this: -The format of the input and output files (model updates) are dependent on the ML framework used. A helper instance :py:mod:`fedn.utils.plugins.pytorchhelper` is used to handle the serialization and deserialization of the model updates. +The format of the input and output files (model updates) are using numpy ndarrays. A helper instance :py:mod:`fedn.utils.helpers.plugins.numpyhelper` is used to handle the serialization and deserialization of the model updates. The first function (_compile_model) is used to define the model architecture and creates an initial model (which is then used by _init_seed). The second function (_load_data) is used to read the data (train and test) from disk. -The third function (_save_model) is used to save the model to disk using the pytorch helper module :py:mod:`fedn.utils.plugins.pytorchhelper`. The fourth function (_load_model) is used to load the model from disk, again +The third function (_save_model) is used to save the model to disk using the numpy helper module :py:mod:`fedn.utils.helpers.plugins.numpyhelper`. The fourth function (_load_model) is used to load the model from disk, again using the pytorch helper module. The fifth function (_init_seed) is used to initialize the seed model. The sixth function (_train) is used to train the model, observe the two first arguments which will be set by the FEDn client. The seventh function (_validate) is used to validate the model, again observe the two first arguments which will be set by the FEDn client. @@ -302,18 +302,18 @@ For validations it is a requirement that the output is saved in a valid json for In the code example we use the helper function :py:meth:`fedn.utils.helpers.helpers.save_metrics` to save the validation scores as a json file. -The Dahboard in the FEDn UI will plot any scalar metric in this json file, but you can include any type in the file assuming that it is valid json. These values can then be obtained (by an athorized user) from the MongoDB database or using the :py:mod:`fedn.network.api.client`. +These values can then be obtained (by an athorized user) from the MongoDB database or using the :py:meth:`fedn.network.api.client.APIClient.list_validations`. Packaging for distribution -------------------------- -For the compute package we need to compress the *client* folder as .tar.gz file. E.g. using: +For the compute package we need to compress the *client* folder as .tgz file. E.g. using: .. code-block:: bash tar -czvf package.tgz client -This file can then be uploaded to the FEDn network using the FEDn UI or the :py:mod:`fedn.network.api.client`. +This file can then be uploaded to the FEDn network using the :py:meth:`fedn.network.api.client.APIClient.set_package`. More on local data access @@ -335,7 +335,7 @@ We recommend you to test your code before running the client. For example, you c python entrypoint.py validate ../model_update.npz ../validation.json --data_path ../data/mnist.npz -Once everything works as expected you can start the federated network, upload the tar.gz compute package and the initial model. +Once everything works as expected you can start the federated network, upload the .tgz compute package and the initial model (use :py:meth:`fedn.network.api.client.APIClient.set_initial_model` for uploading an initial model). Finally connect a client to the network: .. code-block:: bash @@ -345,7 +345,7 @@ Finally connect a client to the network: -v $PWD/data/clients/1:/var/data \ -e ENTRYPOINT_OPTS=--data_path=/var/data/mnist.pt \ --network=fedn_default \ - ghcr.io/scaleoutsystems/fedn/fedn:master-mnist-pytorch run client -in client.yaml --name client1 + ghcr.io/scaleoutsystems/fedn/fedn:0.8.0-mnist-pytorch run client -in client.yaml --name client1 -The container image "ghcr.io/scaleoutsystems/fedn/fedn:develop-mnist-pytorch" is a pre-built image with the FEDn client and the PyTorch framework installed. +The container image "ghcr.io/scaleoutsystems/fedn/fedn:0.8.0-mnist-pytorch" is a pre-built image with the FEDn client and the PyTorch framework installed. diff --git a/examples/README.md b/examples/README.md index 52adf95e2..27f3dce63 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,6 +1,8 @@ ## Examples The examples distributed here in this folder are maintained by Scaleout. +We recommend all new users to start with the [Quickstart Tutorial (PyTorch)](https://github.com/scaleoutsystems/fedn/tree/master/examples/mnist-pytorch). + ### External examples Below we maintain a list of examples provided both by the Scaleout core team and users. They may or may not be tested with the latest release of FEDn, please refer to the README of each specific project/example for detail. If you have a project that you want to include in this list, talk to a core developer in [Discord](https://discord.gg/CCRgjpMsVA). diff --git a/examples/mnist-keras/README.md b/examples/mnist-keras/README.md index b4b5c0672..795b88f10 100644 --- a/examples/mnist-keras/README.md +++ b/examples/mnist-keras/README.md @@ -1,12 +1,7 @@ # MNIST (TensorFlow/Keras version) -This classic example of hand-written text recognition is well suited both as a lightweight test when developing on FEDn in pseudo-distributed mode. A normal high-end laptop or a workstation should be able to sustain a few clients. The example automates the partitioning of data and deployment of a variable number of clients on a single host. We here assume working experience with containers, Docker and docker-compose. -## Table of Contents -- [MNIST Example (Keras version)](#mnist-example-keras-version) - - [Table of Contents](#table-of-contents) - - [Prerequisites](#prerequisites) - - [Running the example (pseudo-distributed)](#running-the-example-pseudo-distributed) - - [Clean up](#clean-up) +This is a mimimalistic TF/Keras version of the Quickstart Tutorial (PyTorch). For more detailed explaination including a Jupyter Notebook with +examples of API usage for starting and interacting with federated experiments, refer to that tutorial. ## Prerequisites - [Python 3.8, 3.9 or 3.10](https://www.python.org/downloads) @@ -45,43 +40,18 @@ bin/build.sh > The files location will be `package/package.tgz` and `seed.npz`. ### Deploy FEDn -Now we are ready to deploy FEDn with `docker-compose`. -``` -docker-compose -f ../../docker-compose.yaml up -d minio mongo mongo-express reducer combiner -``` - -### Initialize the federated model -Now navigate to http://localhost:8090 to see the reducer UI. You will be asked to upload the compute package and the seed model that you created in the previous step. - -### Attach clients -To attach clients to the network, use the docker-compose.override.yaml template to start 2 clients: +Now we are ready to deploy FEDn and two clients with `docker-compose`. ``` -docker-compose -f ../../docker-compose.yaml -f docker-compose.override.yaml up client +docker-compose -f ../../docker-compose.yaml -f docker-compose.override.yaml up ``` + > **Note**: run with `--scale client=N` to start *N* clients. ### Run federated training -Finally, you can start the experiment from the "control" tab of the UI. +Refer to the notebook to create your own drivers for seeding the federation and running experiments. + + https://github.com/scaleoutsystems/fedn/blob/master/examples/mnist-pytorch/API_Example.ipynb ## Clean up You can clean up by running `docker-compose down`. - -## Connecting to a distributed deployment -To start and remotely connect a client with the required dependencies for this example, start by downloading the `client.yaml` file. You can either navigate the reducer UI or run the following command. - -```bash -curl -k https://:/config/download > client.yaml -``` -> **Note** make sure to replace `` and `` with appropriate values. - -Now you are ready to start the client via Docker by running the following command. - -```bash -docker run -d \ - -v $PWD/client.yaml:/app/client.yaml \ - -v $PWD/data:/var/data \ - -e ENTRYPOINT_OPTS=--data_path=/var/data/mnist.npz \ - ghcr.io/scaleoutsystems/fedn/fedn:develop-mnist-keras run client -in client.yaml -``` -> **Note** If reducer and combiner host names, as specfied in the configuration files, are not resolvable in the client host network you need to use the docker option `--add-hosts` to make them resolvable. Please refer to the Docker documentation for more detail. diff --git a/examples/mnist-pytorch/.gitignore b/examples/mnist-pytorch/.gitignore index 84f374386..a9f01054b 100644 --- a/examples/mnist-pytorch/.gitignore +++ b/examples/mnist-pytorch/.gitignore @@ -2,6 +2,5 @@ data *.npz *.tgz *.tar.gz -*.ipynb .mnist-pytorch client.yaml \ No newline at end of file diff --git a/examples/mnist-pytorch/API_Example.ipynb b/examples/mnist-pytorch/API_Example.ipynb index 1c8c46fae..3ac7b615b 100644 --- a/examples/mnist-pytorch/API_Example.ipynb +++ b/examples/mnist-pytorch/API_Example.ipynb @@ -14,7 +14,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "743dfe47", "metadata": {}, "outputs": [], @@ -38,7 +38,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "1061722d", "metadata": {}, "outputs": [], @@ -58,7 +58,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "5107f6f9", "metadata": {}, "outputs": [], @@ -78,7 +78,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "f0380d35", "metadata": {}, "outputs": [], @@ -98,7 +98,7 @@ }, { "cell_type": "markdown", - "id": "81184448", + "id": "8cc709c2", "metadata": {}, "source": [ "We wait for the session to finish: " @@ -106,8 +106,8 @@ }, { "cell_type": "code", - "execution_count": 14, - "id": "e1143474", + "execution_count": null, + "id": "897451fa", "metadata": {}, "outputs": [], "source": [ @@ -117,7 +117,7 @@ }, { "cell_type": "markdown", - "id": "de35a9df", + "id": "16874cec", "metadata": {}, "source": [ "Next, we retrive all model validations from all clients, extract the training accuracy metric, and compute its mean value accross all clients" @@ -125,8 +125,8 @@ }, { "cell_type": "code", - "execution_count": 15, - "id": "b5db0739", + "execution_count": null, + "id": "4e8044b7", "metadata": {}, "outputs": [], "source": [ @@ -153,31 +153,10 @@ }, { "cell_type": "code", - "execution_count": 16, - "id": "60082e1a", + "execution_count": null, + "id": "42425c43", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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CAhAXl4eXF1dIZFIzC3Zqmk0GgQFBSEvLw9ubm5il2O1+D62DL6PLYPvY8vg+9gyWvN9FAQB5eXlCAgIuOF2ZoeRCRMmoLCwEAsXLoRKpUJkZCR27NhhmtSam5sLqfSvqSiXLl1Cv379TF8vXboUS5cuxfDhw7F79+4mnVMqlaJjx47mlmpT3Nzc+JetBfB9bBl8H1sG38eWwfexZbTW+3ijEZGrzA4jADBnzpxGL8v8PWAEBwff9FoRERERtV8WeTcNERERtR8MIxZOoVBg0aJF7F57i/g+tgy+jy2D72PL4PvYMizhfZQIvIZCREREIuLICBEREYmKYYSIiIhExTBCREREomIYISIiIlExjFiohIQE9O/fH66urvD19cX48eORkZEhdllW74033oBEIsFzzz0ndilW5+LFi3jsscfg5eUFR0dH9O7dG7///rvYZVkVvV6PBQsWICQkBI6OjggLC8Prr7/OXkw3sXfvXowbNw4BAQGQSCT45ptv6r0uCAIWLlwIf39/ODo6IjY2FmfPnhWnWAt2o/expqYG8+bNQ+/eveHs7IyAgABMnToVly5dapPaGEYs1J49ezB79mwcOnQIO3fuRE1NDe68805UVlaKXZrVOnz4MN5//3306dNH7FKszuXLlzF48GDY29vjxx9/xKlTp/DOO++gQ4cOYpdmVd58802sWbMGK1euRHp6Ot5880289dZbWLFihdilWbTKykr07dsXq1atavD1t956C++99x4SExPx22+/wdnZGWPGjEF1dXUbV2rZbvQ+VlVVIS0tDQsWLEBaWhq++uorZGRk4L777mub4gSyCgUFBQIAYc+ePWKXYpXKy8uF8PBwYefOncLw4cOFuXPnil2SVZk3b54wZMgQscuwemPHjhWmT59e77kHH3xQmDx5skgVWR8Awtdff2362mAwCH5+fsLbb79teq60tFRQKBTCp59+KkKF1uHv72NDUlNTBQBCTk5Oq9fDkRErUVZWBgDw9PQUuRLrNHv2bIwdOxaxsbFil2KVtm3bhujoaDz88MPw9fVFv379sG7dOrHLsjqDBg1CcnIyzpw5AwA4fvw49u/fj7vvvlvkyqxXVlYWVCpVvb/b7u7uGDhwIFJSUkSszPqVlZVBIpHAw8Oj1c/VrLVpqG0ZDAY899xzGDx4MHr16iV2OVZny5YtSEtLw+HDh8UuxWplZmZizZo1iI+Px3//+18cPnwY//znPyGXyzFt2jSxy7MaL774IjQaDbp37w6ZTAa9Xo/Fixdj8uTJYpdmtVQqFQCYFmu9SqlUml4j81VXV2PevHmYNGlSmyxCyDBiBWbPno0TJ05g//79YpdidfLy8jB37lzs3LkTDg4OYpdjtQwGA6Kjo7FkyRIAQL9+/XDixAkkJiYyjJjhs88+w6ZNm7B582b07NkTx44dw3PPPYeAgAC+j2Qxampq8Mgjj0AQBKxZs6ZNzsnLNBZuzpw5+P7777Fr1y507NhR7HKszpEjR1BQUIDbbrsNdnZ2sLOzw549e/Dee+/Bzs4Oer1e7BKtgr+/PyIiIuo916NHD+Tm5opUkXX697//jRdffBETJ05E7969MWXKFDz//PNISEgQuzSr5efnBwBQq9X1nler1abXqOmuBpGcnBzs3LmzTUZFAIYRiyUIAubMmYOvv/4av/76K0JCQsQuySqNHj0af/75J44dO2Z6REdHY/LkyTh27BhkMpnYJVqFwYMHX3dr+ZkzZ9C5c2eRKrJOVVVVkErr/9qVyWQwGAwiVWT9QkJC4Ofnh+TkZNNzGo0Gv/32G2JiYkSszPpcDSJnz57FL7/8Ai8vrzY7Ny/TWKjZs2dj8+bN+Pbbb+Hq6mq69unu7g5HR0eRq7Merq6u182zcXZ2hpeXF+ffmOH555/HoEGDsGTJEjzyyCNITU3F2rVrsXbtWrFLsyrjxo3D4sWL0alTJ/Ts2RNHjx7FsmXLMH36dLFLs2gVFRU4d+6c6eusrCwcO3YMnp6e6NSpE5577jn873//Q3h4OEJCQrBgwQIEBARg/Pjx4hVtgW70Pvr7++Ohhx5CWloavv/+e+j1etPnjqenJ+RyeesW1+r361CzAGjw8eGHH4pdmtXjrb3N89133wm9evUSFAqF0L17d2Ht2rVil2R1NBqNMHfuXKFTp06Cg4ODEBoaKrz00kuCVqsVuzSLtmvXrgZ/H06bNk0QBOPtvQsWLBCUSqWgUCiE0aNHCxkZGeIWbYFu9D5mZWU1+rmza9euVq9NIghs/UdERETi4ZwRIiIiEhXDCBEREYmKYYSIiIhExTBCREREomIYISIiIlExjBAREZGoGEaIiIhIVAwjREREJCqGESIiIhIVwwgRERGJimGEiIiIRMUwQkRERKL6fwv9MA9PiTuxAAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "x = range(1,len(mean_acc_fedavg)+1)\n", "plt.plot(x, mean_acc_fedavg)\n", @@ -194,7 +173,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "id": "4f70d7d9", "metadata": {}, "outputs": [], @@ -212,8 +191,8 @@ }, { "cell_type": "code", - "execution_count": 18, - "id": "31dba6d7", + "execution_count": null, + "id": "ce8a89a3", "metadata": {}, "outputs": [], "source": [ @@ -223,7 +202,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "900eb0a7", "metadata": {}, "outputs": [], @@ -258,31 +237,10 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "id": "d064aaf9", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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F1+Ml5cXXl5efPvtt8ycORMvLy8cDofZJdYqkZGRxMbGltvWtm1bkpOTTapIHnvsMcaOHcutt95Khw4duP3223n44YeZOnWq2aXVehEREQCkpaWV256Wllb2mjspjFRjhmHwwAMPsHDhQr7++muaNm1qdkm1Wt++ffn9999Zv3592dK1a1eGDRvG+vXrsdlsZpdYq/Tq1euUru7btm2jSZMmJlUk+fn5WK3lP3ZsNhtOp9OkiuS4pk2bEhERQWJiYtm27Oxsfv75Z3r27On29nWbphobNWoU8+fPZ9GiRQQFBZXd1wsJCcHPz8/k6mqfoKCgU57XCQgIoF69enqOxwQPP/wwl156Kc888wy33HILq1ev5rXXXuO1114zu7Raa9CgQTz99NM0btyYdu3asW7dOqZPn87f/vY3s0urFXJzc9mxY0fZ17t372b9+vWEhobSuHFjRo8ezZQpU2jZsiVNmzZlwoQJREVFlfW4cStDqi3gtMsbb7xhdmlyTO/evY2HHnrI7DJqrc8++8xo3769YbfbjTZt2hivvfaa2SXVatnZ2cZDDz1kNG7c2PD19TWaNWtmjB8/3igqKjK7tFphxYoVp/3MGDFihGEYhuF0Oo0JEyYY4eHhht1uN/r27WskJSVVSW0aZ0RERERMpWdGRERExFQKIyIiImIqhRERERExlcKIiIiImEphREREREylMCIiIiKmUhgRERERUymMiIiIiKkURkRERMRUCiMiIiJiKoURERERMZXCiIiIiJjq/wHq35Vjnbzm4wAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "x = range(1,len(mean_acc_fedavg)+1)\n", "plt.plot(x, mean_acc_fedavg, x, mean_acc_fedopt)\n", diff --git a/examples/mnist-pytorch/README.md b/examples/mnist-pytorch/README.md deleted file mode 100644 index a7bbcc0b3..000000000 --- a/examples/mnist-pytorch/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# MNIST (PyTorch version) -This classic example of hand-written text recognition is well suited as a lightweight test when developing on FEDn in pseudo-distributed mode. A normal high-end laptop or a workstation should be able to sustain a few clients. The example automates the partitioning of data and deployment of a variable number of clients on a single host. We here assume working experience with containers, Docker and docker-compose. - - -## Table of Contents -- [MNIST Example (PyTorch version)](#mnist-example-pytorch-version) - - [Table of Contents](#table-of-contents) - - [Prerequisites](#prerequisites) - - [Running the example (pseudo-distributed)](#running-the-example-pseudo-distributed) - - [Clean up](#clean-up) - -## Prerequisites -- [Python 3.8, 3.9 or 3.10](https://www.python.org/downloads) -- [Docker](https://docs.docker.com/get-docker) -- [Docker Compose](https://docs.docker.com/compose/install) - -## Running the example (pseudo-distributed, single host) - -Clone FEDn and locate into this directory. -```sh -git clone https://github.com/scaleoutsystems/fedn.git -cd fedn/examples/mnist-pytorch -``` - -### Preparing the environment, the local data, the compute package and seed model -Start by initializing a virtual enviroment with all of the required dependencies. -``` -bin/init_venv.sh -``` - -Then, to get the data you can run the following script. -``` -bin/get_data -``` - -The next command splits the data in 2 parts for the clients. -``` -bin/split_data -``` -> **Note**: run with `--n_splits=N` to split in *N* parts. - -Create the compute package and a seed model that you will be asked to upload in the next step. -``` -bin/build.sh -``` -> The files location will be `package/package.tgz` and `seed.npz`. - -### Deploy FEDn -Now we are ready to deploy FEDn with `docker-compose`. -``` -docker-compose -f ../../docker-compose.yaml up -d minio mongo mongo-express reducer combiner -``` - -### Initialize the federated model -Now navigate to http://localhost:8090 to see the reducer UI. You will be asked to upload the compute package and the seed model that you created in the previous step. Make sure to choose the "PyTorch" helper. - -### Attach clients -To attach clients to the network, use the docker-compose.override.yaml template to start 2 clients: - -``` -docker-compose -f ../../docker-compose.yaml -f docker-compose.override.yaml up client -``` -> **Note**: run with `--scale client=N` to start *N* clients. - -### Run federated training -Finally, you can start the experiment from the "control" tab of the UI. - -## Clean up -You can clean up by running `docker-compose down`. diff --git a/examples/mnist-pytorch/README.rst b/examples/mnist-pytorch/README.rst new file mode 100644 index 000000000..7522cb182 --- /dev/null +++ b/examples/mnist-pytorch/README.rst @@ -0,0 +1,149 @@ +Quickstart Tutorial PyTorch (MNIST) +------------- + +This classic example of hand-written text recognition is well suited as a lightweight test when developing on FEDn in pseudo-distributed mode. +A normal high-end laptop or a workstation should be able to sustain a few clients. +The example automates the partitioning of data and deployment of a variable number of clients on a single host. +We here assume working experience with containers, Docker and docker-compose. + +Prerequisites +------------- + +- `Python 3.8, 3.9 or 3.10 `__ +- `Docker `__ +- `Docker Compose `__ + +Quick start +----------- + +Clone this repository, locate into this directory: + +.. code-block:: + + git clone https://github.com/scaleoutsystems/fedn.git + cd fedn/examples/mnist-keras + +Start a pseudo-distributed FEDn network using docker-compose: + +.. code-block:: + + docker-compose -f ../../docker-compose.yaml up + +This starts up the needed backend services MongoDB and Minio, the API Server and one Combiner. +You can verify the deployment using these urls: + +- API Server: http://localhost:8092/get_controller_status +- Minio: http://localhost:9000 +- Mongo Express: http://localhost:8081 + +Next, we will prepare the client. A key concept in FEDn is the compute package - +a code bundle that contains entrypoints for training and (optionally) validating a model update on the client. + +Locate into 'examples/mnist-pytorch' and familiarize yourself with the project structure. The entrypoints +are defined in 'client/entrypoint'. The dependencies needed in the client environment are specified in +'requirements.txt'. For convenience, we have provided utility scripts to set up a virtual environment. + +Start by initializing a virtual enviroment with all of the required dependencies for this project. + +.. code-block:: + + bin/init_venv.sh + +Next create the compute package and a seed model: + +.. code-block:: + + bin/build.sh + +You should now have a file 'package.tgz' and 'seed.npz' in the project folder. + +Next we prepare the local dataset. For this we download MNIST data and make data partitions: + +Download the data: + +.. code-block:: + + bin/get_data + + +Split the data in 10 partitions: + +.. code-block:: + + bin/split_data --n_splits=10 + +Data partitions will be generated in the folder 'data/clients'. + +FEDn relies on a configuration file for the client to connect to the server. Create a file called 'client.yaml' with the follwing content: + +.. code-block:: + + network_id: fedn-network + discover_host: api-server + discover_port: 8092 + +Make sure to move the file ``client.yaml`` to the root of the examples/mnist-pytorch folder. +To connect a client that uses the data partition ``data/clients/1/mnist.pt`` and the config file ``client.yaml`` to the network, run the following docker command: + +.. code-block:: + + docker run \ + -v $PWD/client.yaml:/app/client.yaml \ + -v $PWD/data/clients/1:/var/data \ + -e ENTRYPOINT_OPTS=--data_path=/var/data/mnist.pt \ + --network=fedn_default \ + ghcr.io/scaleoutsystems/fedn/fedn:master-mnist-pytorch run client -in client.yaml --name client1 + +Observe the API Server logs and combiner logs, you should see the client connecting and entering into a state asking for a compute package. + +In a separate terminal, start a second client using the data partition 'data/clients/2/mnist.pt': + +.. code-block:: + + docker run \ + -v $PWD/client.yaml:/app/client.yaml \ + -v $PWD/data/clients/2:/var/data \ + -e ENTRYPOINT_OPTS=--data_path=/var/data/mnist.pt \ + --network=fedn_default \ + ghcr.io/scaleoutsystems/fedn/fedn:master-mnist-pytorch run client -in client.yaml --name client2 + +You are now ready to use the API to initialize the system with the compute package and seed model, and to start federated training. + +- Follow the example in the `Jupyter Notebook `__ + + +Automate experimentation with several clients: +----------- + +Now that you have an understanding of the main components of FEDn, you can use the provided docker-compose templates to automate deployment of FEDn and clients. +To start the network and attach 4 clients: + +.. code-block:: + + docker-compose -f ../../docker-compose.yaml -f docker-compose.override.yaml up --scale client=4 + + +Access logs and validation data from MongoDB +----------- +You can access and download event logs and validation data via the API, and you can also as a developer obtain +the MongoDB backend data using pymongo or via the MongoExpress interface: + +- http://localhost:8081/db/fedn-network/ + +The credentials are as set in docker-compose.yaml in the root of the repository. + +Access model updates +----------- + +You can obtain model updates from the 'fedn-models' bucket in Minio: + +- http://localhost:9000 + + +Clean up +----------- +You can clean up by running + +.. code-block:: + + docker-compose down