diff --git a/.github/workflows/fets-challenge.yml b/.github/workflows/fets-challenge.yml
index 983bdad9de..9ff94dcb85 100644
--- a/.github/workflows/fets-challenge.yml
+++ b/.github/workflows/fets-challenge.yml
@@ -24,27 +24,27 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
+ pip install torch==1.13.1+cpu torchvision==0.14.1+cpu torchaudio==0.13.1 --extra-index-url https://download.pytorch.org/whl/cpu
pip install .
- name: Setup FeTS Challenge Prerequisites
uses: actions/checkout@master
with:
- repository: CBICA/GaNDLF
- ref: 0.0.14
+ repository: MLCommons/GaNDLF
+ ref: master
fetch-depth: 1
path: fets_challenge
- name: FeTS Challenge Task Runner Test
run: |
cd fets_challenge
pwd
- pip install torch==1.8.2+cpu torchvision==0.9.2+cpu torchaudio===0.8.2 -f https://download.pytorch.org/whl/lts/1.8/torch_lts.html
- pip install onnx==1.12.0
pip install -e .
+ pip uninstall onnx -y
# Download data and Split CSVs into training and validation
python -c "from testing.test_full import test_generic_download_data, test_generic_constructTrainingCSV; test_generic_download_data(); test_generic_constructTrainingCSV()"
- head -n 1 testing/data/train_3d_rad_segmentation.csv > /home/runner/work/openfl/openfl/seg_test_val.csv
- tail -n +9 testing/data/train_3d_rad_segmentation.csv >> /home/runner/work/openfl/openfl/seg_test_val.csv
- head -n 8 testing/data/train_3d_rad_segmentation.csv > /home/runner/work/openfl/openfl/seg_test_train.csv
+ head -n 1 testing/data/train_3d_rad_segmentation.csv > /home/runner/work/openfl/openfl/valid.csv
+ tail -n +9 testing/data/train_3d_rad_segmentation.csv >> /home/runner/work/openfl/openfl/valid.csv
+ head -n 8 testing/data/train_3d_rad_segmentation.csv > /home/runner/work/openfl/openfl/train.csv
cd /home/runner/work/openfl/openfl
ls
- python -m tests.github.test_fets_challenge --template fets_challenge_seg_test --fed_workspace aggregator --col1 col1 --col2 col2 --rounds-to-train 1
+ python -m tests.github.test_gandlf --template gandlf_seg_test --fed_workspace aggregator --col1 one --col2 two --rounds-to-train 1
diff --git a/docs/manual.rst b/docs/manual.rst
index 4f73cddb6c..cdab47482a 100644
--- a/docs/manual.rst
+++ b/docs/manual.rst
@@ -34,6 +34,7 @@ Explore new and experimental features:
overview
install
running_the_federation
+ running_the_federation_with_gandlf
source/utilities/utilities
advanced_topics
source/workflow/running_the_federation.tutorial
diff --git a/docs/running_the_federation_with_gandlf.rst b/docs/running_the_federation_with_gandlf.rst
new file mode 100644
index 0000000000..4229d2303a
--- /dev/null
+++ b/docs/running_the_federation_with_gandlf.rst
@@ -0,0 +1,495 @@
+.. # Copyright (C) 2020-2023 Intel Corporation
+.. # SPDX-License-Identifier: Apache-2.0
+
+.. _running_the_federation_with_gandlf:
+
+****************************************************
+Run the Federation with a model defined using GaNDLF
+****************************************************
+
+This guide will show you how to take an existing model using the [Generally Nuanced Deep Learning Framework (GaNDLF)](https://github.com/mlcommons/GaNDLF) experiment to a federated environment.
+
+
+`Aggregator-Based Workflow`_
+ Define an experiment and distribute it manually. All participants can verify model code and [FL plan](https://openfl.readthedocs.io/en/latest/running_the_federation.html#federated-learning-plan-fl-plan-settings) prior to executing the code/model. The federation is terminated when the experiment is finished, and appropriate statistics are generated.
+
+
+.. _running_the_federation_aggregator_based:
+
+Aggregator-Based Workflow
+=========================
+
+An overview of this workflow is shown below.
+
+.. figure:: /images/openfl_flow.png
+
+.. centered:: Overview of the Aggregator-Based Workflow
+
+- `Bare Metal Approach`_
+- `Docker Approach`_
+
+
+This workflow uses short-lived components in a federation, which is terminated when the experiment is finished. The components are as follows:
+
+- The *Collaborator* uses a local dataset to train a global model and sends the model updates to the *Aggregator*, which aggregates them to create the new global model.
+- The *Aggregator* is framework-agnostic, while the *Collaborator* can use any deep learning frameworks, such as `TensorFlow `_\* \ or `PyTorch `_\*\. `GaNDLF ` provides a straightforward way to define complete model training pipelines for healthcare data, and is directly compatible with OpenFL.
+
+This guide will demonstrate how to take an existing [GaNDLF model configuration](https://mlcommons.github.io/GaNDLF/getting_started/) (e.g., for segmentation), embed this within the Federated Learning plan (FL plan) along with the Python\*\ code that defines the model and the data loader. The FL plan is a `YAML `_ file that defines the collaborators, aggregator, connections, models, data, and any other parameters that describe the training.
+
+
+.. _plan_settings:
+
+
+
+
+Configurable Settings
+^^^^^^^^^^^^^^^^^^^^^
+
+- :class:`Aggregator `
+ `openfl.component.Aggregator `_
+
+- :class:`Collaborator `
+ `openfl.component.Collaborator `_
+
+- :class:`Data Loader `
+ `openfl.federated.data.loader.DataLoader `_
+
+- :class:`Task Runner `
+ `openfl.federated.task.runner.TaskRunner `_
+
+- :class:`Assigner `
+ `openfl.component.Assigner `_
+
+
+Tasks
+^^^^^
+
+Each task subsection contains the following:
+
+- ``function``: The function name to call.
+ The function must be the one defined in :class:`TaskRunner ` class.
+- ``kwargs``: kwargs passed to the ``function``.
+
+.. note::
+ See an `example `_ of the :class:`TaskRunner ` class for details.
+
+
+.. _running_the_federation_manual:
+
+
+.. _simulate_a_federation:
+
+
+Simulate a federation
+-------------------
+
+.. note::
+
+ Ensure you have installed the |productName| package on every node (aggregator and collaborators) in the federation.
+
+ See :ref:`install_package` for details.
+
+
+You can use the `"Hello Federation" bash script `_ to quickly create a federation (an aggregator node and two collaborator nodes) to test the project pipeline.
+
+.. literalinclude:: ../tests/github/test_hello_federation.sh
+ :language: bash
+
+However, continue with the following procedure for details in creating a federation with an aggregator-based workflow.
+
+ `STEP 1: Create a Workspace on the Aggregator`_
+
+ - Creates a federated learning workspace on one of the nodes.
+
+
+ `STEP 2: Configure the Federation`_
+
+ - Ensures each node in the federation has a valid public key infrastructure (PKI) certificate.
+ - Distributes the workspace from the aggregator node to the other collaborator nodes.
+
+
+ `STEP 3: Start the Federation`_
+
+
+.. _creating_workspaces:
+
+
+STEP 1: Install GaNDLF prerequisites and Create a Workspace
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+1. Start a Python 3.8 (>=3.7, <3.11) virtual environment and confirm |productName| is available.
+
+ .. code-block:: python
+
+ fx
+
+
+2. [Install GaNDLF from sources](https://mlcommons.github.io/GaNDLF/setup/#install-from-sources) (if not already).
+
+3. Create GaNDLF's Data CSVs. The example below is for 3D Segmentation using the unit test data:
+ .. code-block:: console
+
+ python -c "from testing.test_full import test_generic_download_data, test_generic_constructTrainingCSV; test_generic_download_data(); test_generic_constructTrainingCSV()"
+ # Creates training CSV
+ head -n 8 testing/data/train_3d_rad_segmentation.csv > train.csv
+ head -n 1 testing/data/train_3d_rad_segmentation.csv > val.csv
+ # Creates validation CSV
+ tail -n +9 testing/data/train_3d_rad_segmentation.csv >> val.csv
+
+ .. note::
+
+ This step creates sample data CSVs for this tutorial. In a real federation, you should bring your own Data CSV files from GaNDLF that reflect the data present on your system
+
+
+4. Use the :code:`gandlf_seg_test` template
+
+ Set the environment variables to use the :code:`gandlf_seg_test` as the template and :code:`${HOME}/my_federation` as the path to the workspace directory.
+
+ .. code-block:: console
+
+ export WORKSPACE_TEMPLATE=gandlf_seg_test
+ export WORKSPACE_PATH=${HOME}/my_federation
+
+4. Create a workspace directory for the new federation project.
+
+ .. code-block:: console
+
+ fx workspace create --prefix ${WORKSPACE_PATH} --template ${WORKSPACE_TEMPLATE}
+
+
+5. Change to the workspace directory.
+
+ .. code-block:: console
+
+ cd ${WORKSPACE_PATH}
+
+6. Copy the GaNDLF Data CSVs into the default path for model initialization
+
+ .. code-block:: console
+ # 'one' is the default name of the first collaborator in `plan/data.yaml`.
+ mkdir -p data/one
+ cp ~/GaNDLF/train.csv data/one
+ cp ~/GaNDLF/val.csv data/one
+
+6. Create the FL plan and initialialize the model weights.
+
+
+ This step will initialize the federated learning plan and initialize the random model weights that will be used by all collaborators at the start of the expeirment.
+
+ .. code-block:: console
+
+ fx plan initialize
+
+ Alternatively, to use your own GaNDLF configuration file, you can import it into the plan with the following command:
+
+ .. code-block:: console
+
+ fx plan initialize --gandlf_config ${PATH_TO_GANDLF_CONFIG}.yaml
+
+
+ The FL plan is described by the **plan.yaml** file located in the **plan** directory of the workspace. OpenFL aims to make it as easy as possible to take an existing GaNDLF experiment and make it run across a federation.
+
+ Each YAML top-level section contains the following subsections:
+
+ - ``template``: The name of the class including top-level packages names. An instance of this class is created when the plan gets initialized.
+ - ``settings``: The arguments that are passed to the class constructor.
+ - ``defaults``: The file that contains default settings for this subsection.
+ Any setting from defaults file can be overridden in the **plan.yaml** file.
+
+ The following is an example of the GaNDLF Segmentation Test **plan.yaml**. Notice the **task_runner/settings/gandlf_config** block where the GaNDLF configuration file is embedded:
+
+ .. literalinclude:: ../openfl-workspace/gandlf_seg_test/plan/plan.yaml
+ :language: yaml
+
+
+ This command initializes the FL plan and auto populates the `fully qualified domain name (FQDN) `_ of the aggregator node. This FQDN is embedded within the FL plan so the collaborator nodes know the address of the externally accessible aggregator server to connect to.
+
+ If you have connection issues with the auto populated FQDN in the FL plan, you can do **one of the following**:
+
+ - OPTION 1: override the auto populated FQDN value with the :code:`-a` flag.
+
+ .. code-block:: console
+
+ fx plan initialize -a aggregator-hostname.internal-domain.com
+
+ - OPTION 2: override the apparent FQDN of the system by setting an FQDN environment variable.
+
+ .. code-block:: console
+
+ export FQDN=x.x.x.x
+
+ and initializing the FL plan
+
+ .. code-block:: console
+
+ fx plan initialize
+
+
+.. note::
+
+ Each workspace may have multiple FL plans and multiple collaborator lists associated with it. Therefore, :code:`fx plan initialize` has the following optional parameters.
+
+ +-------------------------+---------------------------------------------------------+
+ | Optional Parameters | Description |
+ +=========================+=========================================================+
+ | -p, --plan_config PATH | Federated Learning plan [default = plan/plan.yaml] |
+ +-------------------------+---------------------------------------------------------+
+ | -c, --cols_config PATH | Authorized collaborator list [default = plan/cols.yaml] |
+ +-------------------------+---------------------------------------------------------+
+ | -d, --data_config PATH | The data set/shard configuration file |
+ +-------------------------+---------------------------------------------------------+
+
+
+
+.. _configure_the_federation:
+
+
+STEP 2: Configure the Federation
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The objectives in this step:
+
+ - Ensure each node in the federation has a valid public key infrastructure (PKI) certificate. See :doc:`/source/utilities/pki` for details on available workflows.
+ - Distribute the workspace from the aggregator node to the other collaborator nodes.
+
+
+.. _install_certs_agg:
+
+**On the Aggregator Node:**
+
+Setting Up the Certificate Authority
+
+1. Change to the path of your workspace:
+
+ .. code-block:: console
+
+ cd WORKSPACE_PATH
+
+2. Set up the aggregator node as the `certificate authority `_ for the federation.
+
+ All certificates will be signed by the aggregator node. Follow the instructions and enter the information as prompted. The command will create a simple database file to keep track of all issued certificates.
+
+ .. code-block:: console
+
+ fx workspace certify
+
+3. Run the aggregator certificate creation command, replacing :code:`AFQDN` with the actual `fully qualified domain name (FQDN) `_ for the aggregator node.
+
+ .. code-block:: console
+
+ fx aggregator generate-cert-request --fqdn AFQDN
+
+ .. note::
+
+ On Linux\*\, you can discover the FQDN with this command:
+
+ .. code-block:: console
+
+ hostname --all-fqdns | awk '{print $1}'
+
+ .. note::
+
+ You can override the apparent FQDN of the system by setting an FQDN environment variable before creating the certificate.
+
+ .. code-block:: console
+
+ export FQDN=x.x.x.x
+ fx aggregator generate-cert-request
+
+ If you omit the :code:`--fdqn` parameter, then :code:`fx` will automatically use the FQDN of the current node assuming the node has been correctly set with a static address.
+
+ .. code-block:: console
+
+ fx aggregator generate-cert-request
+
+4. Run the aggregator certificate signing command, replacing :code:`AFQDN` with the actual `fully qualified domain name (FQDN) `_ for the aggregator node.
+
+ .. code-block:: console
+
+ fx aggregator certify
+
+
+5. This node now has a signed security certificate as the aggregator for this new federation. You should have the following files.
+
+ +---------------------------+--------------------------------------------------+
+ | File Type | Filename |
+ +===========================+==================================================+
+ | Certificate chain | WORKSPACE.PATH/cert/cert_chain.crt |
+ +---------------------------+--------------------------------------------------+
+ | Aggregator certificate | WORKSPACE.PATH/cert/server/agg_{AFQDN}.crt |
+ +---------------------------+--------------------------------------------------+
+ | Aggregator key | WORKSPACE.PATH/cert/server/agg_{AFQDN}.key |
+ +---------------------------+--------------------------------------------------+
+
+ where **AFQDN** is the fully-qualified domain name of the aggregator node.
+
+.. _workspace_export:
+
+Exporting the Workspace
+
+
+1. Export the workspace so that it can be imported to the collaborator nodes.
+
+ .. code-block:: console
+
+ fx workspace export
+
+ The :code:`export` command will archive the current workspace (with a :code:`zip` file extension) and create a **requirements.txt** of the current Python\*\ packages in the virtual environment.
+
+2. The next step is to transfer this workspace archive to each collaborator node.
+
+
+.. _install_certs_colab:
+
+**On the Collaborator Node**:
+
+Importing the Workspace
+
+1. Copy the :ref:`workspace archive ` from the aggregator node to the collaborator nodes.
+
+2. [Install GaNDLF from sources](https://mlcommons.github.io/GaNDLF/setup/#install-from-sources) (if not already).
+
+3. Import the workspace archive.
+
+ .. code-block:: console
+
+ fx workspace import --archive WORKSPACE.zip
+
+ where **WORKSPACE.zip** is the name of the workspace archive. This will unzip the workspace to the current directory and install the required Python packages within the current virtual environment.
+
+4. For each test machine you want to run as collaborator nodes, create a collaborator certificate request to be signed by the certificate authority.
+
+ Replace :code:`COL_LABEL` with the label you assigned to the collaborator. This label does not have to be the FQDN; it can be any unique alphanumeric label.
+
+ .. code-block:: console
+
+ fx collaborator generate-cert-request -n {COL_LABEL} -d data/{COL_LABEL}
+
+
+The creation script will specify the path to the data. In this case, the GaNDLF Data Loader will look for **train.csv** and **valid.csv** at the path that's provided. Before running the experiment, you will need to configure both train.csv and valid.csv **manually for each collaborator** so that each collaborator has the correct datasets. For example, if the collaborator's name is `one`, collaborator one would load `data/one/train.csv` and `data/one/valid.csv` at experiment runtime, and collaborator two would load `data/two/train.csv` and `data/two/valid.csv`.
+
+ This command will also create the following files:
+
+ +-----------------------------+--------------------------------------------------------+
+ | File Type | Filename |
+ +=============================+========================================================+
+ | Collaborator CSR | WORKSPACE.PATH/cert/client/col_{COL_LABEL}.csr |
+ +-----------------------------+--------------------------------------------------------+
+ | Collaborator key | WORKSPACE.PATH/cert/client/col_{COL_LABEL}.key |
+ +-----------------------------+--------------------------------------------------------+
+ | Collaborator CSR Package | WORKSPACE.PATH/col_{COL_LABEL}_to_agg_cert_request.zip |
+ +-----------------------------+--------------------------------------------------------+
+
+5. Copy/scp the WORKSPACE.PATH/col_{COL_LABEL}_to_agg_cert_request.zip file to the aggregator node (or local workspace if using the same system)
+
+ .. code-block:: console
+
+ scp WORKSPACE.PATH/col_{COL_LABEL}_to_agg_cert_request.zip AGGREGATOR_NODE:WORKSPACE_PATH/
+
+
+6. On the aggregator node (i.e., the certificate authority in this example), sign the Collaborator CSR Package from the collaborator nodes.
+
+ .. code-block:: console
+
+ fx collaborator certify --request-pkg /PATH/TO/col_{COL_LABEL}_to_agg_cert_request.zip
+
+ where :code:`/PATH/TO/col_{COL_LABEL}_to_agg_cert_request.zip` is the path to the Collaborator CSR Package containing the :code:`.csr` file from the collaborator node. The certificate authority will sign this certificate for use in the federation.
+
+ The command packages the signed collaborator certificate, along with the **cert_chain.crt** file needed to verify certificate signatures, for transport back to the collaborator node:
+
+ +---------------------------------+------------------------------------------------------------+
+ | File Type | Filename |
+ +=================================+============================================================+
+ | Certificate and Chain Package | WORKSPACE.PATH/agg_to_col_{COL_LABEL}_signed_cert.zip |
+ +---------------------------------+------------------------------------------------------------+
+
+7. Copy/scp the WORKSPACE.PATH/agg_to_col_{COL_LABEL}_signed_cert.zip file to the collaborator node (or local workspace if using the same system)
+
+ .. code-block:: console
+
+ scp WORKSPACE.PATH/agg_to_col_{COL_LABEL}_signed_cert.zip COLLABORATOR_NODE:WORKSPACE_PATH/
+
+
+8. On the collaborator node, import the signed certificate and certificate chain into your workspace.
+
+ .. code-block:: console
+
+ fx collaborator certify --import /PATH/TO/agg_to_col_{COL_LABEL}_signed_cert.zip
+
+
+
+.. _running_the_federation.start_nodes:
+
+
+STEP 3: Start the Federation
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**On the Aggregator Node:**
+
+1. Start the Aggregator.
+
+ .. code-block:: console
+
+ fx aggregator start
+
+ Now, the Aggregator is running and waiting for Collaborators to connect.
+
+.. _running_collaborators:
+
+**On the Collaborator Nodes:**
+
+1. Open a new terminal, change the directory to the workspace, and activate the virtual environment.
+
+2. Run the Collaborator.
+
+ .. code-block:: console
+
+ fx collaborator start -n {COLLABORATOR_LABEL}
+
+ where :code:`COLLABORATOR_LABEL` is the label for this Collaborator.
+
+ .. note::
+
+ Each workspace may have multiple FL plans and multiple collaborator lists associated with it.
+ Therefore, :code:`fx collaborator start` has the following optional parameters.
+
+ +-------------------------+---------------------------------------------------------+
+ | Optional Parameters | Description |
+ +=========================+=========================================================+
+ | -p, --plan_config PATH | Federated Learning plan [default = plan/plan.yaml] |
+ +-------------------------+---------------------------------------------------------+
+ | -d, --data_config PATH | The data set/shard configuration file |
+ +-------------------------+---------------------------------------------------------+
+
+3. Repeat the earlier steps for each collaborator node in the federation.
+
+ When all of the Collaborators connect, the Aggregator starts training. You will see log messages describing the progress of the federated training.
+
+ When the last round of training is completed, the Aggregator stores the final weights in the protobuf file that was specified in the YAML file, which in this example is located at **save/${WORKSPACE_TEMPLATE}_latest.pbuf**.
+
+
+Post Experiment
+^^^^^^^^^^^^^^^
+
+Experiment owners may access the final model in its native format. Once the model has been converted to its native format, inference can be done using `GaNDLF's inference API `.
+Among other training artifacts, the aggregator creates the last and best aggregated (highest validation score) model snapshots. One may convert a snapshot to the native format and save the model to disk by calling the following command from the workspace:
+
+.. code-block:: console
+
+ fx model save -i model_protobuf_path.pth -o save_model_path
+
+In order for this command to succeed, the **TaskRunner** used in the experiment must implement a :code:`save_native()` method.
+
+Another way to access the trained model is by calling the API command directly from a Python script:
+
+.. code-block:: python
+
+ from openfl import get_model
+ model = get_model(plan_config, cols_config, data_config, model_protobuf_path)
+
+In fact, the :code:`get_model()` method returns a **TaskRunner** object loaded with the chosen model snapshot. Users may utilize the linked model as a regular Python object.
+
+Running Inference with GaNDLF
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Now that you have generated the final federated model in pytorch format, you can use the model by following the `GaNDLF inference instructions `
diff --git a/openfl-workspace/fets_challenge_seg_test/plan/cols.yaml b/openfl-workspace/gandlf_seg_test/plan/cols.yaml
similarity index 100%
rename from openfl-workspace/fets_challenge_seg_test/plan/cols.yaml
rename to openfl-workspace/gandlf_seg_test/plan/cols.yaml
diff --git a/openfl-workspace/fets_challenge_seg_test/plan/data.yaml b/openfl-workspace/gandlf_seg_test/plan/data.yaml
similarity index 76%
rename from openfl-workspace/fets_challenge_seg_test/plan/data.yaml
rename to openfl-workspace/gandlf_seg_test/plan/data.yaml
index 8d07ca8b3b..db759f6df3 100644
--- a/openfl-workspace/fets_challenge_seg_test/plan/data.yaml
+++ b/openfl-workspace/gandlf_seg_test/plan/data.yaml
@@ -1,7 +1,6 @@
-# Copyright (C) 2020 Intel Corporation
+# Copyright (C) 2023 Intel Corporation
# Licensed subject to the terms of the separately executed evaluation license agreement between Intel Corporation and you.
# collaborator_name,data_directory_path
-one,1
-
+one,data/one
diff --git a/openfl-workspace/fets_challenge_seg_test/plan/defaults b/openfl-workspace/gandlf_seg_test/plan/defaults
similarity index 100%
rename from openfl-workspace/fets_challenge_seg_test/plan/defaults
rename to openfl-workspace/gandlf_seg_test/plan/defaults
diff --git a/openfl-workspace/fets_challenge_seg_test/plan/plan.yaml b/openfl-workspace/gandlf_seg_test/plan/plan.yaml
similarity index 100%
rename from openfl-workspace/fets_challenge_seg_test/plan/plan.yaml
rename to openfl-workspace/gandlf_seg_test/plan/plan.yaml
diff --git a/openfl-workspace/gandlf_seg_test/requirements.txt b/openfl-workspace/gandlf_seg_test/requirements.txt
new file mode 100644
index 0000000000..c9c735efba
--- /dev/null
+++ b/openfl-workspace/gandlf_seg_test/requirements.txt
@@ -0,0 +1 @@
+onnx==1.12
diff --git a/openfl/federated/data/loader_gandlf.py b/openfl/federated/data/loader_gandlf.py
index 911211e041..b29533e307 100644
--- a/openfl/federated/data/loader_gandlf.py
+++ b/openfl/federated/data/loader_gandlf.py
@@ -9,6 +9,8 @@ class GaNDLFDataLoaderWrapper(DataLoader):
"""Data Loader for the Generally Nuanced Deep Learning Framework (GaNDLF)."""
def __init__(self, data_path, feature_shape):
+ self.train_csv = data_path + '/train.csv'
+ self.val_csv = data_path + '/valid.csv'
self.train_dataloader = None
self.val_dataloader = None
self.feature_shape = feature_shape
diff --git a/openfl/federated/plan/plan.py b/openfl/federated/plan/plan.py
index 8c5e672d25..813f5f285f 100644
--- a/openfl/federated/plan/plan.py
+++ b/openfl/federated/plan/plan.py
@@ -65,18 +65,22 @@ def ignore_aliases(self, data):
@staticmethod
def parse(plan_config_path: Path, cols_config_path: Path = None,
- data_config_path: Path = None, resolve=True):
+ data_config_path: Path = None, gandlf_config_path=None,
+ resolve=True):
"""
Parse the Federated Learning plan.
Args:
- plan_config_path (string): The filepath to the federated learning
- plan
- cols_config_path (string): The filepath to the federation
- collaborator list [optional]
- data_config_path (string): The filepath to the federation
- collaborator data configuration
- [optional]
+ plan_config_path (string): The filepath to the federated learning
+ plan
+ cols_config_path (string): The filepath to the federation
+ collaborator list [optional]
+ data_config_path (string): The filepath to the federation
+ collaborator data configuration
+ [optional]
+ override_config_path (string): The filepath to a yaml file
+ that overrides the configuration
+ [optional]
Returns:
A federated learning plan object
"""
@@ -120,6 +124,15 @@ def parse(plan_config_path: Path, cols_config_path: Path = None,
plan.config[section] = defaults
+ if gandlf_config_path is not None:
+ Plan.logger.info(
+ f'Importing GaNDLF Config into plan '
+ f'from file [red]{gandlf_config_path}[/].',
+ extra={'markup': True})
+
+ gandlf_config = Plan.load(Path(gandlf_config_path))
+ plan.config['task_runner']['settings']['gandlf_config'] = gandlf_config
+
plan.authorized_cols = Plan.load(cols_config_path).get(
'collaborators', []
)
diff --git a/openfl/federated/task/runner_gandlf.py b/openfl/federated/task/runner_gandlf.py
index ca4685eb2e..a4fa794753 100644
--- a/openfl/federated/task/runner_gandlf.py
+++ b/openfl/federated/task/runner_gandlf.py
@@ -26,8 +26,6 @@ class GaNDLFTaskRunner(TaskRunner):
def __init__(
self,
- train_csv: str = None,
- val_csv: str = None,
gandlf_config: Union[str, dict] = None,
device: str = None,
**kwargs
@@ -40,6 +38,10 @@ def __init__(
super().__init__(**kwargs)
# allow pass-through of a gandlf config as a file or a dict
+
+ train_csv = self.data_loader.train_csv
+ val_csv = self.data_loader.val_csv
+
if isinstance(gandlf_config, str) and os.path.exists(gandlf_config):
gandlf_config = yaml.safe_load(open(gandlf_config, "r"))
diff --git a/openfl/interface/plan.py b/openfl/interface/plan.py
index fd2fee0b32..12ec879bea 100644
--- a/openfl/interface/plan.py
+++ b/openfl/interface/plan.py
@@ -38,8 +38,10 @@ def plan(context):
help='The FQDN of the federation agregator')
@option('-f', '--feature_shape', required=False,
help='The input shape to the model')
+@option('-g', '--gandlf_config', required=False,
+ help='GaNDLF Configuration File Path')
def initialize(context, plan_config, cols_config, data_config,
- aggregator_address, feature_shape):
+ aggregator_address, feature_shape, gandlf_config):
"""
Initialize Data Science plan.
@@ -64,7 +66,8 @@ def initialize(context, plan_config, cols_config, data_config,
plan = Plan.parse(plan_config_path=plan_config,
cols_config_path=cols_config,
- data_config_path=data_config)
+ data_config_path=data_config,
+ gandlf_config_path=gandlf_config)
init_state_path = plan.config['aggregator']['settings']['init_state_path']
diff --git a/tests/github/test_fets_challenge.py b/tests/github/test_gandlf.py
similarity index 76%
rename from tests/github/test_fets_challenge.py
rename to tests/github/test_gandlf.py
index 02d84311a8..117bcef3d6 100644
--- a/tests/github/test_fets_challenge.py
+++ b/tests/github/test_gandlf.py
@@ -23,11 +23,11 @@ def exec(command, directory):
parser = argparse.ArgumentParser()
parser.add_argument('--template', default='keras_cnn_mnist')
parser.add_argument('--fed_workspace', default='fed_work12345alpha81671')
- parser.add_argument('--col1', default='one123dragons')
- parser.add_argument('--col2', default='beta34unicorns')
+ parser.add_argument('--col1', default='one')
+ parser.add_argument('--col2', default='two')
parser.add_argument('--rounds-to-train')
- parser.add_argument('--col1-data-path', default='1')
- parser.add_argument('--col2-data-path', default='2')
+ parser.add_argument('--col1-data-path', default='data/one')
+ parser.add_argument('--col2-data-path', default='data/two')
parser.add_argument('--ujjwal', action='store_true')
origin_dir = Path().resolve()
@@ -42,11 +42,12 @@ def exec(command, directory):
shutil.rmtree(fed_workspace, ignore_errors=True)
check_call(['fx', 'workspace', 'create', '--prefix', fed_workspace, '--template', template])
os.chdir(fed_workspace)
+ Path(Path.cwd().resolve() / 'data' / col1).mkdir(exist_ok=True)
with os.scandir(origin_dir) as iterator:
for entry in iterator:
print(entry)
if re.match(r'.*\.csv$', entry.name):
- shutil.copy(entry.path, Path.cwd().resolve())
+ shutil.copy(entry.path, Path.cwd().resolve() / 'data' / col1)
# Initialize FL plan
check_call(['fx', 'plan', 'initialize', '-a', fqdn])
plan_path = Path('plan/plan.yaml')
@@ -68,15 +69,21 @@ def exec(command, directory):
check_call(['fx', 'workspace', 'export'])
certify_aggregator(fqdn)
- if not Path('seg_test_train.csv').exists():
+ if not Path('train.csv').exists():
with os.scandir('..') as iterator:
for entry in iterator:
- if re.match(r'^seg_test.*\.csv$', entry.name):
+ if re.match('train.csv', entry.name):
shutil.copy(entry.path, '.')
+ if re.match('valid.csv', entry.name):
+ shutil.copy(entry.path, '.')
+
workspace_root = Path().resolve()
create_collaborator(col1, workspace_root, col1_data_path, archive_name, fed_workspace)
create_collaborator(col2, workspace_root, col2_data_path, archive_name, fed_workspace)
+ Path(workspace_root / col1 / fed_workspace / 'data' / col1).mkdir(exist_ok=True)
+ Path(workspace_root / col2 / fed_workspace / 'data' / col2).mkdir(exist_ok=True)
+
if args.ujjwal:
with os.scandir('/media/ujjwal/SSD4TB/sbutil/DatasetForTraining_Horizontal/') as iterator:
for entry in iterator:
@@ -87,9 +94,12 @@ def exec(command, directory):
else:
with os.scandir(workspace_root) as iterator:
for entry in iterator:
- if re.match(r'^seg_test.*\.csv$', entry.name):
- shutil.copy(entry.path, workspace_root / col1 / fed_workspace)
- shutil.copy(entry.path, workspace_root / col2 / fed_workspace)
+ if re.match('train.csv', entry.name):
+ shutil.copy(entry.path, workspace_root / col1 / fed_workspace / 'data' / col1)
+ shutil.copy(entry.path, workspace_root / col2 / fed_workspace / 'data' / col2)
+ if re.match('valid.csv', entry.name):
+ shutil.copy(entry.path, workspace_root / col1 / fed_workspace / 'data' / col1)
+ shutil.copy(entry.path, workspace_root / col2 / fed_workspace / 'data' / col2)
with ProcessPoolExecutor(max_workers=3) as executor:
executor.submit(exec, ['fx', 'aggregator', 'start'], workspace_root)