From c25d144e4c8d0911d9b1615729ce7e0c5d4f69c9 Mon Sep 17 00:00:00 2001 From: Shailesh Tanwar <135304487+tanwarsh@users.noreply.github.com> Date: Sat, 30 Nov 2024 16:05:32 +0530 Subject: [PATCH 1/2] Fix for 'importlib' has no attribute 'util' (#1180) * fix for AttributeError: module 'importlib' has no attribute 'util' Signed-off-by: yes * formatting fix Signed-off-by: yes --------- Signed-off-by: yes --- openfl/federated/__init__.py | 8 ++++---- openfl/federated/data/__init__.py | 10 +++++----- openfl/federated/task/__init__.py | 10 +++++----- openfl/native/native.py | 4 ++-- openfl/pipelines/__init__.py | 4 ++-- openfl/utilities/optimizers/keras/__init__.py | 4 ++-- openfl/utilities/optimizers/torch/__init__.py | 4 ++-- 7 files changed, 22 insertions(+), 22 deletions(-) diff --git a/openfl/federated/__init__.py b/openfl/federated/__init__.py index ea24e0ddfa..369ca3b54f 100644 --- a/openfl/federated/__init__.py +++ b/openfl/federated/__init__.py @@ -4,23 +4,23 @@ """openfl.federated package.""" -import importlib +from importlib import util from openfl.federated.data import DataLoader # NOQA from openfl.federated.plan import Plan # NOQA from openfl.federated.task import TaskRunner # NOQA -if importlib.util.find_spec("tensorflow") is not None: +if util.find_spec("tensorflow") is not None: from openfl.federated.data import FederatedDataSet # NOQA from openfl.federated.data import KerasDataLoader, TensorFlowDataLoader from openfl.federated.task import FederatedModel # NOQA from openfl.federated.task import KerasTaskRunner, TensorFlowTaskRunner -if importlib.util.find_spec("torch") is not None: +if util.find_spec("torch") is not None: from openfl.federated.data import FederatedDataSet # NOQA from openfl.federated.data import PyTorchDataLoader from openfl.federated.task import FederatedModel # NOQA from openfl.federated.task import PyTorchTaskRunner -if importlib.util.find_spec("xgboost") is not None: +if util.find_spec("xgboost") is not None: from openfl.federated.data import FederatedDataSet # NOQA from openfl.federated.data import XGBoostDataLoader from openfl.federated.task import FederatedModel # NOQA diff --git a/openfl/federated/data/__init__.py b/openfl/federated/data/__init__.py index 91bb604b62..67cfcc094e 100644 --- a/openfl/federated/data/__init__.py +++ b/openfl/federated/data/__init__.py @@ -4,26 +4,26 @@ """Data package.""" -import importlib +from importlib import util from warnings import catch_warnings, simplefilter with catch_warnings(): simplefilter(action="ignore", category=FutureWarning) - if importlib.util.find_spec("tensorflow") is not None: + if util.find_spec("tensorflow") is not None: # ignore deprecation warnings in command-line interface import tensorflow # NOQA from openfl.federated.data.loader import DataLoader # NOQA -if importlib.util.find_spec("tensorflow") is not None: +if util.find_spec("tensorflow") is not None: from openfl.federated.data.federated_data import FederatedDataSet # NOQA from openfl.federated.data.loader_keras import KerasDataLoader # NOQA from openfl.federated.data.loader_tf import TensorFlowDataLoader # NOQA -if importlib.util.find_spec("torch") is not None: +if util.find_spec("torch") is not None: from openfl.federated.data.federated_data import FederatedDataSet # NOQA from openfl.federated.data.loader_pt import PyTorchDataLoader # NOQA -if importlib.util.find_spec("xgboost") is not None: +if util.find_spec("xgboost") is not None: from openfl.federated.data.federated_data import FederatedDataSet # NOQA from openfl.federated.data.loader_xgb import XGBoostDataLoader # NOQA diff --git a/openfl/federated/task/__init__.py b/openfl/federated/task/__init__.py index 8b29264128..bdb6313e61 100644 --- a/openfl/federated/task/__init__.py +++ b/openfl/federated/task/__init__.py @@ -4,24 +4,24 @@ """Task package.""" -import importlib +from importlib import util from warnings import catch_warnings, simplefilter with catch_warnings(): simplefilter(action="ignore", category=FutureWarning) - if importlib.util.find_spec("tensorflow") is not None: + if util.find_spec("tensorflow") is not None: # ignore deprecation warnings in command-line interface import tensorflow # NOQA from openfl.federated.task.runner import TaskRunner # NOQA -if importlib.util.find_spec("tensorflow") is not None: +if util.find_spec("tensorflow") is not None: from openfl.federated.task.fl_model import FederatedModel # NOQA from openfl.federated.task.runner_keras import KerasTaskRunner # NOQA from openfl.federated.task.runner_tf import TensorFlowTaskRunner # NOQA -if importlib.util.find_spec("torch") is not None: +if util.find_spec("torch") is not None: from openfl.federated.task.fl_model import FederatedModel # NOQA from openfl.federated.task.runner_pt import PyTorchTaskRunner # NOQA -if importlib.util.find_spec("xgboost") is not None: +if util.find_spec("xgboost") is not None: from openfl.federated.task.fl_model import FederatedModel # NOQA from openfl.federated.task.runner_xgb import XGBoostTaskRunner # NOQA diff --git a/openfl/native/native.py b/openfl/native/native.py index 88b8c5f426..c266c73ac8 100644 --- a/openfl/native/native.py +++ b/openfl/native/native.py @@ -7,11 +7,11 @@ This file defines openfl entrypoints to be used directly through python (not CLI) """ -import importlib import json import logging import os from copy import copy +from importlib import util from logging import basicConfig, getLogger from pathlib import Path from sys import path @@ -164,7 +164,7 @@ def setup_logging(level="INFO", log_file=None): """ # Setup logging - if importlib.util.find_spec("tensorflow") is not None: + if util.find_spec("tensorflow") is not None: import tensorflow as tf # pylint: disable=import-outside-toplevel tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.ERROR) diff --git a/openfl/pipelines/__init__.py b/openfl/pipelines/__init__.py index f8e7b3e549..1e549fd3c4 100644 --- a/openfl/pipelines/__init__.py +++ b/openfl/pipelines/__init__.py @@ -1,6 +1,6 @@ # Copyright 2022 VMware, Inc. # SPDX-License-Identifier: Apache-2.0 -import importlib +from importlib import util from openfl.pipelines.kc_pipeline import KCPipeline from openfl.pipelines.no_compression_pipeline import NoCompressionPipeline @@ -9,5 +9,5 @@ from openfl.pipelines.stc_pipeline import STCPipeline from openfl.pipelines.tensor_codec import TensorCodec -if importlib.util.find_spec("torch") is not None: +if util.find_spec("torch") is not None: from openfl.pipelines.eden_pipeline import EdenPipeline # NOQA diff --git a/openfl/utilities/optimizers/keras/__init__.py b/openfl/utilities/optimizers/keras/__init__.py index 39f450df05..2ae100ebcc 100644 --- a/openfl/utilities/optimizers/keras/__init__.py +++ b/openfl/utilities/optimizers/keras/__init__.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 -import importlib +from importlib import util -if importlib.util.find_spec("tensorflow") is not None: +if util.find_spec("tensorflow") is not None: from openfl.utilities.optimizers.keras.fedprox import FedProxOptimizer # NOQA diff --git a/openfl/utilities/optimizers/torch/__init__.py b/openfl/utilities/optimizers/torch/__init__.py index a6cd0c95f6..77e989cdd4 100644 --- a/openfl/utilities/optimizers/torch/__init__.py +++ b/openfl/utilities/optimizers/torch/__init__.py @@ -3,8 +3,8 @@ """PyTorch optimizers package.""" -import importlib +from importlib import util -if importlib.util.find_spec("torch") is not None: +if util.find_spec("torch") is not None: from openfl.utilities.optimizers.torch.fedprox import FedProxAdam # NOQA from openfl.utilities.optimizers.torch.fedprox import FedProxOptimizer # NOQA From f875b780b39cbfab91817c8d973762c856623b61 Mon Sep 17 00:00:00 2001 From: refai06 <149057514+refai06@users.noreply.github.com> Date: Mon, 2 Dec 2024 19:20:05 +0530 Subject: [PATCH 2/2] Workflow Interface: Remove duplicate tutorials (#1181) * streamline tutorial Signed-off-by: refai06 * Resolve broken link Signed-off-by: refai06 * broken link update Signed-off-by: refai06 --------- Signed-off-by: refai06 --- ...kspace_Creation_from_JupyterNotebook.ipynb | 4 +- .../experimental/workflow/101_MNIST.ipynb | 4 +- .../workflow/102_Aggregator_Validation.ipynb | 8 +- ...c_Institutional_Incremental_Learning.ipynb | 6 +- .../workflow/104_Keras_MNIST_with_CPU.ipynb | 2 +- .../workflow/104_Keras_MNIST_with_GPU.ipynb | 4 +- .../experimental/workflow/104_MNIST_XPU.ipynb | 4 +- .../201_Exclusive_GPUs_with_Ray.ipynb | 6 +- .../workflow/301_MNIST_Watermarking.ipynb | 6 +- .../401_FedProx_with_Synthetic_nonIID.ipynb | 6 +- ...gregator_Validation_Ray_Watermarking.ipynb | 910 ------------------ .../402_FedProx_with_Synthetic_nonIID.ipynb | 822 ---------------- ...gregator_Validation_Ray_Watermarking.ipynb | 14 +- ...rox_PyTorch_MNIST_Workflow_Tutorial.ipynb} | 0 .../Workflow_Interface_NeuralChat.ipynb | 6 +- .../TwoPartyWorkspaceCreation.ipynb | 2 +- ...low_Interface_102_Vision_Transformer.ipynb | 4 +- 17 files changed, 38 insertions(+), 1770 deletions(-) delete mode 100644 openfl-tutorials/experimental/workflow/401_MNIST_Aggregator_Validation_Ray_Watermarking.ipynb delete mode 100644 openfl-tutorials/experimental/workflow/402_FedProx_with_Synthetic_nonIID.ipynb rename openfl-tutorials/experimental/workflow/{401_Federated_FedProx_PyTorch_MNIST_Workflow_Tutorial.ipynb => 403_Federated_FedProx_PyTorch_MNIST_Workflow_Tutorial.ipynb} (100%) diff --git a/openfl-tutorials/experimental/workflow/1001_Workspace_Creation_from_JupyterNotebook.ipynb b/openfl-tutorials/experimental/workflow/1001_Workspace_Creation_from_JupyterNotebook.ipynb index c8b4347966..525abfc312 100644 --- a/openfl-tutorials/experimental/workflow/1001_Workspace_Creation_from_JupyterNotebook.ipynb +++ b/openfl-tutorials/experimental/workflow/1001_Workspace_Creation_from_JupyterNotebook.ipynb @@ -26,7 +26,7 @@ "4. User can utilize the experimental `fx` commands to deploy and run the federation seamlessly\n", "\n", "\n", - "The methodology is described using an existing [OpenFL Watermarking Tutorial](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/Workflow_Interface_301_MNIST_Watermarking.ipynb). Let's get started !\n", + "The methodology is described using an existing [OpenFL Watermarking Tutorial](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/workflow/301_MNIST_Watermarking.ipynb). Let's get started !\n", "\n" ] }, @@ -747,7 +747,7 @@ "\n", "Users can directly specify a collaborator's private attributes via `collaborator.private_attributes` which is a dictionary where key is name of the attribute and value is the object that is made accessible to collaborator.\n", "\n", - "For more detailed information on specifying these private attributes, please refer to the first quick start [notebook](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/Workflow_Interface_101_MNIST.ipynb)" + "For more detailed information on specifying these private attributes, please refer to the first quick start [notebook](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/workflow/101_MNIST.ipynb)" ] }, { diff --git a/openfl-tutorials/experimental/workflow/101_MNIST.ipynb b/openfl-tutorials/experimental/workflow/101_MNIST.ipynb index 194ed3b034..5757c31199 100644 --- a/openfl-tutorials/experimental/workflow/101_MNIST.ipynb +++ b/openfl-tutorials/experimental/workflow/101_MNIST.ipynb @@ -7,7 +7,7 @@ "metadata": {}, "source": [ "# Workflow Interface 101: Quickstart\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/101_MNIST.ipynb)" + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/workflow/101_MNIST.ipynb)" ] }, { @@ -663,7 +663,7 @@ "metadata": {}, "source": [ "# Congratulations!\n", - "Now that you've completed your first workflow interface quickstart notebook, see some of the more advanced things you can do in our [other tutorials](broken_link), including:\n", + "Now that you've completed your first workflow interface quickstart notebook, see some of the more advanced things you can do in our [other tutorials](https://github.com/securefederatedai/openfl/tree/develop/openfl-tutorials/experimental/workflow), including:\n", "\n", "- Using the LocalRuntime Ray Backend for dedicated GPU access\n", "- Vertical Federated Learning\n", diff --git a/openfl-tutorials/experimental/workflow/102_Aggregator_Validation.ipynb b/openfl-tutorials/experimental/workflow/102_Aggregator_Validation.ipynb index 9d0fb930ce..c08afc649e 100644 --- a/openfl-tutorials/experimental/workflow/102_Aggregator_Validation.ipynb +++ b/openfl-tutorials/experimental/workflow/102_Aggregator_Validation.ipynb @@ -7,7 +7,7 @@ "metadata": {}, "source": [ "# Workflow Interface 102 - Held out aggregator validation\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/Workflow_Interface_102_Aggregator_Validation.ipynb)" + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/workflow/102_Aggregator_Validation.ipynb)" ] }, { @@ -16,7 +16,7 @@ "id": "bd059520", "metadata": {}, "source": [ - "In this tutorial, we build on the ideas from the [first](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/101_MNIST.ipynb) quick start notebook, and demonstrate how to perform validation on the aggregator after training." + "In this tutorial, we build on the ideas from the [first](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/workflow/101_MNIST.ipynb) quick start notebook, and demonstrate how to perform validation on the aggregator after training." ] }, { @@ -186,7 +186,7 @@ "scrolled": true }, "source": [ - "Now we come to the updated flow definition. Here we use the same tasks as the [quickstart](https://github.com/psfoley/openfl/blob/experimental-workflow-interface/openfl-tutorials/experimental/Workflow_Interface_MNIST.ipynb), but give the aggregator a `test_loader` as a private attribute. The aggregator will do a forward pass on each of the aggregator's models using it's validation data, and weight the highest accuracy model higher than others. " + "Now we come to the updated flow definition. Here we use the same tasks as the [quickstart](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/workflow/101_MNIST.ipynb), but give the aggregator a `test_loader` as a private attribute. The aggregator will do a forward pass on each of the aggregator's models using it's validation data, and weight the highest accuracy model higher than others. " ] }, { @@ -385,7 +385,7 @@ "metadata": {}, "source": [ "# Congratulations!\n", - "Now that you've completed your this notebook, see some of the more advanced things you can do in our [other tutorials](broken_link), including:\n", + "Now that you've completed your this notebook, see some of the more advanced things you can do in our [other tutorials](https://github.com/securefederatedai/openfl/tree/develop/openfl-tutorials/experimental/workflow), including:\n", "\n", "- Using the LocalRuntime Ray Backend for dedicated GPU access\n", "- Vertical Federated Learning\n", diff --git a/openfl-tutorials/experimental/workflow/103_Cyclic_Institutional_Incremental_Learning.ipynb b/openfl-tutorials/experimental/workflow/103_Cyclic_Institutional_Incremental_Learning.ipynb index 79e7456d6a..2e1535b0ab 100644 --- a/openfl-tutorials/experimental/workflow/103_Cyclic_Institutional_Incremental_Learning.ipynb +++ b/openfl-tutorials/experimental/workflow/103_Cyclic_Institutional_Incremental_Learning.ipynb @@ -7,7 +7,7 @@ "metadata": {}, "source": [ "# Workflow Interface 103 - Cyclic Institutional Incremental Learning\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/Workflow_Interface_103_Cyclic_Institutional_Incremental_Learning.ipynb)" + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/workflow/103_Cyclic_Institutional_Incremental_Learning.ipynb)" ] }, { @@ -195,7 +195,7 @@ "scrolled": true }, "source": [ - "Now we come to the updated flow definition. Here we use the same tasks as the [quickstart](https://github.com/psfoley/openfl/blob/experimental-workflow-interface/openfl-tutorials/experimental/Workflow_Interface_MNIST.ipynb), but give the aggregator a `test_loader` as a private attribute. The aggregator will do a forward pass on each of the aggregator's models using it's validation data, and weight the highest accuracy model higher than others. " + "Now we come to the updated flow definition. Here we use the same tasks as the [quickstart](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/workflow/101_MNIST.ipynb), but give the aggregator a `test_loader` as a private attribute. The aggregator will do a forward pass on each of the aggregator's models using it's validation data, and weight the highest accuracy model higher than others. " ] }, { @@ -658,7 +658,7 @@ "metadata": {}, "source": [ "# Congratulations!\n", - "Now that you've completed your this notebook, see some of the more advanced things you can do in our [other tutorials](broken_link), including:\n", + "Now that you've completed your this notebook, see some of the more advanced things you can do in our [other tutorials](https://github.com/securefederatedai/openfl/tree/develop/openfl-tutorials/experimental/workflow), including:\n", "\n", "- Using the LocalRuntime Ray Backend for dedicated GPU access\n", "- Vertical Federated Learning\n", diff --git a/openfl-tutorials/experimental/workflow/104_Keras_MNIST_with_CPU.ipynb b/openfl-tutorials/experimental/workflow/104_Keras_MNIST_with_CPU.ipynb index 65d948fee6..650c4c759b 100644 --- a/openfl-tutorials/experimental/workflow/104_Keras_MNIST_with_CPU.ipynb +++ b/openfl-tutorials/experimental/workflow/104_Keras_MNIST_with_CPU.ipynb @@ -6,7 +6,7 @@ "metadata": {}, "source": [ "# # Workflow Interface 104: Working with Keras on CPU\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/snehal-das/openfl/blob/develop/openfl-tutorials/experimental/104_Keras_MNIST_with_CPU.ipynb)\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/workflow/104_Keras_MNIST_with_CPU.ipynb)\n", "\n", "## Training a CNN on CPU using the Workflow Interface and MNIST data.\n", "\n", diff --git a/openfl-tutorials/experimental/workflow/104_Keras_MNIST_with_GPU.ipynb b/openfl-tutorials/experimental/workflow/104_Keras_MNIST_with_GPU.ipynb index 03862d94c5..cb7dfb8a86 100644 --- a/openfl-tutorials/experimental/workflow/104_Keras_MNIST_with_GPU.ipynb +++ b/openfl-tutorials/experimental/workflow/104_Keras_MNIST_with_GPU.ipynb @@ -5,7 +5,7 @@ "metadata": {}, "source": [ "# Workflow Interface 104: Working with Keras\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/104_Keras_MNIST_with_GPU.ipynb)" + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/workflow/104_Keras_MNIST_with_GPU.ipynb)" ] }, { @@ -323,7 +323,7 @@ "metadata": {}, "source": [ "# Congratulations!\n", - "Now that you've completed your this notebook, see some of the more advanced things you can do in our [other tutorials](broken_link), including:\n", + "Now that you've completed your this notebook, see some of the more advanced things you can do in our [other tutorials](https://github.com/securefederatedai/openfl/tree/develop/openfl-tutorials/experimental/workflow), including:\n", "\n", "- Using the LocalRuntime Ray Backend for dedicated GPU access\n", "- Vertical Federated Learning\n", diff --git a/openfl-tutorials/experimental/workflow/104_MNIST_XPU.ipynb b/openfl-tutorials/experimental/workflow/104_MNIST_XPU.ipynb index e4e196d387..4a14e610b6 100644 --- a/openfl-tutorials/experimental/workflow/104_MNIST_XPU.ipynb +++ b/openfl-tutorials/experimental/workflow/104_MNIST_XPU.ipynb @@ -6,7 +6,7 @@ "metadata": {}, "source": [ "# Workflow Interface 104: MNIST XPU\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/104_MNIST_XPU.ipynb)" + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/workflow/104_MNIST_XPU.ipynb)" ] }, { @@ -701,7 +701,7 @@ "metadata": {}, "source": [ "# Congratulations!\n", - "Now that you've completed your first workflow interface quickstart notebook, see some of the more advanced things you can do in our [other tutorials](broken_link), including:\n", + "Now that you've completed your first workflow interface quickstart notebook, see some of the more advanced things you can do in our [other tutorials](https://github.com/securefederatedai/openfl/tree/develop/openfl-tutorials/experimental/workflow), including:\n", "\n", "- Using the LocalRuntime Ray Backend for dedicated GPU access\n", "- Vertical Federated Learning\n", diff --git a/openfl-tutorials/experimental/workflow/201_Exclusive_GPUs_with_Ray.ipynb b/openfl-tutorials/experimental/workflow/201_Exclusive_GPUs_with_Ray.ipynb index 2183a0eb05..f3c597b72a 100644 --- a/openfl-tutorials/experimental/workflow/201_Exclusive_GPUs_with_Ray.ipynb +++ b/openfl-tutorials/experimental/workflow/201_Exclusive_GPUs_with_Ray.ipynb @@ -7,7 +7,7 @@ "metadata": {}, "source": [ "# Workflow Interface 201: Using Ray to request exclusive GPUs\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/201_Exclusive_GPUs_with_Ray.ipynb)" + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/workflow/201_Exclusive_GPUs_with_Ray.ipynb)" ] }, { @@ -288,7 +288,7 @@ "id": "49c4afa8", "metadata": {}, "source": [ - "In this step we define entities necessary to run the flow and create a function which returns dataset as private attributes of collaborator. As described in [quickstart](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/Workflow_Interface_101_MNIST.ipynb) we define entities necessary for the flow.\n", + "In this step we define entities necessary to run the flow and create a function which returns dataset as private attributes of collaborator. As described in [quickstart](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/workflow/101_MNIST.ipynb) we define entities necessary for the flow.\n", "\n", "To request GPU(s) with ray-backend, we specify `num_gpus=0.5` as the argument while instantiating Collaborator, this will reserve 0.5 GPU for each of the 2 collaborators and therefore require a dedicated GPU for the experiment. Tune this based on your use case, for example `num_gpus=0.5` for an experiment with 4 collaborators will require 2 dedicated GPUs. **NOTE:** Collaborator cannot span over multiple GPUs, for example `num_gpus=0.4` with 5 collaborators will require 3 dedicated GPUs. In this case collaborator 1 and 2 use GPU#1, collaborator 3 and 4 use GPU#2, and collaborator 5 uses GPU#3." ] @@ -614,7 +614,7 @@ "metadata": {}, "source": [ "# Congratulations!\n", - "Now that you've completed your **workflow interface 201 tutorial**, see some of the more advanced things you can do in our [other tutorials](broken_link), including:\n", + "Now that you've completed your **workflow interface 201 tutorial**, see some of the more advanced things you can do in our [other tutorials](https://github.com/securefederatedai/openfl/tree/develop/openfl-tutorials/experimental/workflow), including:\n", "\n", "- Vertical Federated Learning\n", "- Model Watermarking\n", diff --git a/openfl-tutorials/experimental/workflow/301_MNIST_Watermarking.ipynb b/openfl-tutorials/experimental/workflow/301_MNIST_Watermarking.ipynb index 235155458b..ebc964868c 100644 --- a/openfl-tutorials/experimental/workflow/301_MNIST_Watermarking.ipynb +++ b/openfl-tutorials/experimental/workflow/301_MNIST_Watermarking.ipynb @@ -8,7 +8,7 @@ "source": [ "# Workflow Interface 301: Watermarking\n", "\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/301_MNIST_Watermarking.ipynb)" + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/workflow/301_MNIST_Watermarking.ipynb)" ] }, { @@ -432,7 +432,7 @@ "id": "c917b085", "metadata": {}, "source": [ - "Let us now define the Workflow for Watermark embedding. Here we use the same tasks as the [quickstart](https://github.com/psfoley/openfl/blob/experimental-workflow-interface/openfl-tutorials/experimental/Workflow_Interface_MNIST.ipynb), and define following additional steps for Watermarking\n", + "Let us now define the Workflow for Watermark embedding. Here we use the same tasks as the [quickstart](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/workflow/101_MNIST.ipynb), and define following additional steps for Watermarking\n", "- PRE-TRAIN (watermark_retrain): At the start (once), initial model is trained on Watermark dataset for a specified number of epochs \n", "- RE-TRAIN (watermark_pretrain): Every training round, Aggregated model is retrained on Watermark dataset until a desired acc threshold is reached or max number of retrain rounds are expired\n", "\n", @@ -684,7 +684,7 @@ "source": [ "In the `FederatedFlow_MNIST_Watermarking` definition above, you will notice that certain attributes of the flow were not initialized, namely the `watermark_data_loader` for Aggregator and `train_loader`, `test_loader` for the Collaborators. \n", "\n", - "- Collaborator attributes are created in the same manner as described in [quickstart](https://github.com/psfoley/openfl/blob/experimental-workflow-interface/openfl-tutorials/experimental/Workflow_Interface_101_MNIST.ipynb)\n", + "- Collaborator attributes are created in the same manner as described in [quickstart](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/workflow/101_MNIST.ipynb)\n", "\n", "- `watermark_data_loader` is created as a **private attribute** of the Aggregator which is set by `callable_to_initialize_aggregator_private_attributes` callable function. It is exposed only via the runtime. This property enables the Watermark dataset to be hidden from the collaborators as Aggregator private attributes are filtered before the state is transferred to Collaborators (in the same manner as Collaborator private attributes are hidden from Aggregator)\n", "\n", diff --git a/openfl-tutorials/experimental/workflow/401_FedProx_with_Synthetic_nonIID.ipynb b/openfl-tutorials/experimental/workflow/401_FedProx_with_Synthetic_nonIID.ipynb index f933718fac..ec5e495662 100644 --- a/openfl-tutorials/experimental/workflow/401_FedProx_with_Synthetic_nonIID.ipynb +++ b/openfl-tutorials/experimental/workflow/401_FedProx_with_Synthetic_nonIID.ipynb @@ -6,7 +6,7 @@ "metadata": {}, "source": [ "# Workflow Interface 401: Synthetic non-IID Dataset with FedProx Optimizer\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/Workflow_Interface_401_FedProx_with_Synthetic_nonIID.ipynb)" + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/workflow/401_FedProx_with_Synthetic_nonIID.ipynb)" ] }, { @@ -342,7 +342,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Let us now define the Workflow for our experiment. Here we use the methodology as provided in [quickstart](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/Workflow_Interface_101_MNIST.ipynb), and define the workflow consisting of following steps:\n", + "Let us now define the Workflow for our experiment. Here we use the methodology as provided in [quickstart](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/workflow/101_MNIST.ipynb), and define the workflow consisting of following steps:\n", "-\t`start`: Start of the flow \n", "-\t`compute_loss_and_accuracy`: Compute Train Loss and Test Accuracy on aggregated model. Performed *foreach collaborator* in Federation\n", "-\t`gather_results_and_take_weighted_average`: Collect train loss, and test accuracy metrics for each collaborator and take weighted average to compute the *Aggregated* Train Loss and Test Accuracy. Performed on Aggregator\n", @@ -574,7 +574,7 @@ "source": [ "# Setup Federation\n", "\n", - "In this step we define entities necessary to run the flow and create a function which returns dataset as private attributes of collaborator. As described in [quickstart](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/Workflow_Interface_101_MNIST.ipynb) we define entities necessary for the flow." + "In this step we define entities necessary to run the flow and create a function which returns dataset as private attributes of collaborator. As described in [quickstart](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/workflow/101_MNIST.ipynb) we define entities necessary for the flow." ] }, { diff --git a/openfl-tutorials/experimental/workflow/401_MNIST_Aggregator_Validation_Ray_Watermarking.ipynb b/openfl-tutorials/experimental/workflow/401_MNIST_Aggregator_Validation_Ray_Watermarking.ipynb deleted file mode 100644 index fc99e22214..0000000000 --- a/openfl-tutorials/experimental/workflow/401_MNIST_Aggregator_Validation_Ray_Watermarking.ipynb +++ /dev/null @@ -1,910 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "id": "dc13070c", - "metadata": {}, - "source": [ - "# Workflow Interface 401: Aggregator validation with a watermark dataset using Ray\n", - "\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/401_MNIST_Aggregator_Validation_Ray_Watermarking.ipynb)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "8f28c451", - "metadata": {}, - "source": [ - "This tutorial is a merge of some of the previous notebooks.\n", - "\n", - "The purpose of this OpenFL Workflow Interface tutorial is to showcase the following:\n", - "- Performing validation on the aggregator (see the [102](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/102_Aggregator_Validation.ipynb) notebook)\n", - "- Training with watermarking of DL Model in Federated Learning (see the [301](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/301_MNIST_Watermarking.ipynb) notebook)\n", - "- Utilizing multiple GPUs for concurrent model training using the Ray Backend in LocalRuntime (see the [201](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/201_Exclusive_GPUs_with_Ray.ipynb) notebook).\n", - "\n", - "Watermarking enables the Model owner to assert ownership rights and detect stolen model instances." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "a4394089", - "metadata": {}, - "source": [ - "# Getting Started" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "857f9995", - "metadata": {}, - "source": [ - "First we start by installing the necessary dependencies for the workflow interface:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f7475cba", - "metadata": {}, - "outputs": [], - "source": [ - "!pip install git+https://github.com/securefederatedai/openfl.git\n", - "!pip install -r workflow_interface_requirements.txt\n", - "!pip install matplotlib\n", - "!pip install torchvision\n", - "!pip install git+https://github.com/pyviz-topics/imagen.git@master\n", - "!pip install holoviews==1.15.4\n", - "\n", - "# Uncomment this if running in Google Colab\n", - "#!pip install -r https://raw.githubusercontent.com/intel/openfl/develop/openfl-tutorials/experimental/workflow/workflow_interface_requirements.txt\n", - "#import os\n", - "#os.environ[\"USERNAME\"] = \"colab\"" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "7bd566df", - "metadata": {}, - "source": [ - "We begin with the quintessential example of a PyTorch CNN model trained on the MNIST dataset. Let's start by defining our data loaders, model, optimizer, and helper functions like we would for any other deep learning experiment." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9bd8ac2d", - "metadata": {}, - "outputs": [], - "source": [ - "import torch.nn as nn\n", - "import torch.nn.functional as F\n", - "import torch.optim as optim\n", - "import torch\n", - "import torchvision\n", - "import numpy as np\n", - "import random\n", - "import pathlib\n", - "import os\n", - "import matplotlib\n", - "import matplotlib.pyplot as plt\n", - "import PIL.Image as Image\n", - "import imagen as ig\n", - "import numbergen as ng\n", - "import os\n", - "\n", - "random_seed = 1\n", - "torch.backends.cudnn.enabled = False\n", - "torch.manual_seed(random_seed)\n", - "\n", - "# MNIST Train and Test datasets\n", - "mnist_train = torchvision.datasets.MNIST(\n", - " \"./files/\",\n", - " train=True,\n", - " download=True,\n", - " transform=torchvision.transforms.Compose(\n", - " [\n", - " torchvision.transforms.ToTensor(),\n", - " torchvision.transforms.Normalize((0.1307,), (0.3081,)),\n", - " ]\n", - " ),\n", - ")\n", - "\n", - "mnist_test = torchvision.datasets.MNIST(\n", - " \"./files/\",\n", - " train=False,\n", - " download=True,\n", - " transform=torchvision.transforms.Compose(\n", - " [\n", - " torchvision.transforms.ToTensor(),\n", - " torchvision.transforms.Normalize((0.1307,), (0.3081,)),\n", - " ]\n", - " ),\n", - ")\n", - "\n", - "\n", - "class Net(nn.Module):\n", - " def __init__(self, dropout=0.0):\n", - " super(Net, self).__init__()\n", - " self.dropout = dropout\n", - " self.block = nn.Sequential(\n", - " nn.Conv2d(1, 32, 2),\n", - " nn.MaxPool2d(2),\n", - " nn.ReLU(),\n", - " nn.Conv2d(32, 64, 2),\n", - " nn.MaxPool2d(2),\n", - " nn.ReLU(),\n", - " nn.Conv2d(64, 128, 2),\n", - " nn.ReLU(),\n", - " )\n", - " self.fc1 = nn.Linear(128 * 5**2, 200)\n", - " self.fc2 = nn.Linear(200, 10)\n", - " self.relu = nn.ReLU()\n", - " self.dropout = nn.Dropout(p=dropout)\n", - "\n", - " def forward(self, x):\n", - " x = self.dropout(x)\n", - " out = self.block(x)\n", - " out = out.view(-1, 128 * 5**2)\n", - " out = self.dropout(out)\n", - " out = self.relu(self.fc1(out))\n", - " out = self.dropout(out)\n", - " out = self.fc2(out)\n", - " return F.log_softmax(out, 1)\n", - "\n", - "\n", - "def inference(network, test_loader):\n", - " if torch.cuda.is_available():\n", - " network = network.to('cuda:0')\n", - " network.eval()\n", - " correct = 0\n", - " with torch.no_grad():\n", - " for data, target in test_loader:\n", - " if torch.cuda.is_available():\n", - " data = data.to('cuda:0')\n", - " target = target.to('cuda:0')\n", - " output = network(data)\n", - " pred = output.data.max(1, keepdim=True)[1]\n", - " correct += pred.eq(target.data.view_as(pred)).sum()\n", - " accuracy = float(correct / len(test_loader.dataset))\n", - " return accuracy\n", - "\n", - "\n", - "def train_model(model, optimizer, data_loader, entity, round_number, log=False):\n", - " if torch.cuda.is_available():\n", - " model = model.to('cuda:0')\n", - " \n", - " # Helper function to train the model\n", - " train_loss = 0\n", - " model.train() \n", - " \n", - " for batch_idx, (X, y) in enumerate(data_loader):\n", - " if torch.cuda.is_available():\n", - " X = X.to(\"cuda:0\")\n", - " y = y.to(\"cuda:0\")\n", - " optimizer.zero_grad()\n", - " \n", - " output = model(X)\n", - " loss = F.nll_loss(output, y)\n", - " loss.backward()\n", - "\n", - " optimizer.step()\n", - "\n", - " train_loss += loss.item() * len(X)\n", - " if batch_idx % log_interval == 0 and log:\n", - " print(\n", - " \"{:<20} Train Epoch: {:<3} [{:<3}/{:<4} ({:<.0f}%)] Loss: {:<.6f}\".format(\n", - " entity,\n", - " round_number,\n", - " batch_idx * len(X),\n", - " len(data_loader.dataset),\n", - " 100.0 * batch_idx / len(data_loader),\n", - " loss.item(),\n", - " )\n", - " )\n", - " train_loss /= len(data_loader.dataset)\n", - " return train_loss" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "f0c55175", - "metadata": {}, - "source": [ - "The Watermark dataset consists of mislabelled (input, output) data pairs and is designed such that the model learns to exhibit an unusual prediction behavior on data points from this dataset. The unusual behavior can then be used to demonstrate model ownership and identify illegitimate model copies.\n", - "\n", - "Let us prepare and inspect the sample Watermark dataset. It consists of 100 images = 10 classes (1 for each digit) x 10 images (per class). Watermark images were generated by superimposing a unique pattern (per class) on a noisy background (10 images/class). (Reference - WAFFLE: Watermarking in Federated Learning https://arxiv.org/abs/2008.07298)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bcad2624", - "metadata": {}, - "outputs": [], - "source": [ - "watermark_dir = \"./files/watermark-dataset/MWAFFLE/\"\n", - "\n", - "\n", - "def generate_watermark(\n", - " x_size=28, y_size=28, num_class=10, num_samples_per_class=10, img_dir=watermark_dir\n", - "):\n", - " \"\"\"\n", - " Generate Watermark by superimposing a pattern on noisy background.\n", - "\n", - " Parameters\n", - " ----------\n", - " x_size: x dimension of the image\n", - " y_size: y dimension of the image\n", - " num_class: number of classes in the original dataset\n", - " num_samples_per_class: number of samples to be generated per class\n", - " img_dir: directory for saving watermark dataset\n", - "\n", - " Reference\n", - " ---------\n", - " WAFFLE: Watermarking in Federated Learning (https://arxiv.org/abs/2008.07298)\n", - "\n", - " \"\"\"\n", - " x_pattern = int(x_size * 2 / 3.0 - 1)\n", - " y_pattern = int(y_size * 2 / 3.0 - 1)\n", - "\n", - " np.random.seed(0)\n", - " for cls in range(num_class):\n", - " patterns = []\n", - " random_seed = 10 + cls\n", - " patterns.append(\n", - " ig.Line(\n", - " xdensity=x_pattern,\n", - " ydensity=y_pattern,\n", - " thickness=0.001,\n", - " orientation=np.pi * ng.UniformRandom(seed=random_seed),\n", - " x=ng.UniformRandom(seed=random_seed) - 0.5,\n", - " y=ng.UniformRandom(seed=random_seed) - 0.5,\n", - " scale=0.8,\n", - " )\n", - " )\n", - " patterns.append(\n", - " ig.Arc(\n", - " xdensity=x_pattern,\n", - " ydensity=y_pattern,\n", - " thickness=0.001,\n", - " orientation=np.pi * ng.UniformRandom(seed=random_seed),\n", - " x=ng.UniformRandom(seed=random_seed) - 0.5,\n", - " y=ng.UniformRandom(seed=random_seed) - 0.5,\n", - " size=0.33,\n", - " )\n", - " )\n", - "\n", - " pat = np.zeros((x_pattern, y_pattern))\n", - " for i in range(6):\n", - " j = np.random.randint(len(patterns))\n", - " pat += patterns[j]()\n", - " res = pat > 0.5\n", - " pat = res.astype(int)\n", - "\n", - " x_offset = np.random.randint(x_size - x_pattern + 1)\n", - " y_offset = np.random.randint(y_size - y_pattern + 1)\n", - "\n", - " for i in range(num_samples_per_class):\n", - " base = np.random.rand(x_size, y_size)\n", - " # base = np.zeros((x_input, y_input))\n", - " base[\n", - " x_offset : x_offset + pat.shape[0],\n", - " y_offset : y_offset + pat.shape[1],\n", - " ] += pat\n", - " d = np.ones((x_size, x_size))\n", - " img = np.minimum(base, d)\n", - " if not os.path.exists(img_dir + str(cls) + \"/\"):\n", - " os.makedirs(img_dir + str(cls) + \"/\")\n", - " plt.imsave(\n", - " img_dir + str(cls) + \"/wm_\" + str(i + 1) + \".png\",\n", - " img,\n", - " cmap=matplotlib.cm.gray,\n", - " )\n", - "\n", - "\n", - "# If the Watermark dataset does not exist, generate and save the Watermark images\n", - "watermark_path = pathlib.Path(watermark_dir)\n", - "if watermark_path.exists() and watermark_path.is_dir():\n", - " print(\n", - " f\"Watermark dataset already exists at: {watermark_path}. Proceeding to next step ... \"\n", - " )\n", - " pass\n", - "else:\n", - " print(f\"Generating Watermark dataset... \")\n", - " generate_watermark()\n", - "\n", - "\n", - "class WatermarkDataset(torch.utils.data.Dataset):\n", - " def __init__(self, images_dir, label_dir=None, transforms=None):\n", - " self.images_dir = os.path.abspath(images_dir)\n", - " self.image_paths = [\n", - " os.path.join(self.images_dir, d) for d in os.listdir(self.images_dir)\n", - " ]\n", - " self.label_paths = label_dir\n", - " self.transform = transforms\n", - " temp = []\n", - "\n", - " # Recursively counting total number of images in the directory\n", - " for image_path in self.image_paths:\n", - " for path in os.walk(image_path):\n", - " if len(path) <= 1:\n", - " continue\n", - " path = path[2]\n", - " for im_n in [image_path + \"/\" + p for p in path]:\n", - " temp.append(im_n)\n", - " self.image_paths = temp\n", - "\n", - " if len(self.image_paths) == 0:\n", - " raise Exception(f\"No file(s) found under {images_dir}\")\n", - "\n", - " def __len__(self):\n", - " return len(self.image_paths)\n", - "\n", - " def __getitem__(self, idx):\n", - " image_filepath = self.image_paths[idx]\n", - " image = Image.open(image_filepath)\n", - " image = image.convert(\"RGB\")\n", - " image = self.transform(image)\n", - " label = int(image_filepath.split(\"/\")[-2])\n", - "\n", - " return image, label\n", - "\n", - "\n", - "def get_watermark_transforms():\n", - " return torchvision.transforms.Compose(\n", - " [\n", - " torchvision.transforms.Grayscale(),\n", - " torchvision.transforms.Resize(28),\n", - " torchvision.transforms.ToTensor(),\n", - " torchvision.transforms.Normalize(mean=(0.5,), std=(0.5,)), # Normalize\n", - " ]\n", - " )\n", - "\n", - "\n", - "watermark_data = WatermarkDataset(\n", - " images_dir=watermark_dir,\n", - " transforms=get_watermark_transforms(),\n", - ")\n", - "\n", - "# Set display_watermark to True to display the Watermark dataset\n", - "display_watermark = True\n", - "if display_watermark:\n", - " # Inspect and plot the Watermark Images\n", - " wm_images = np.empty((100, 28, 28))\n", - " wm_labels = np.empty([100, 1], dtype=int)\n", - "\n", - " for i in range(len(watermark_data)):\n", - " img, label = watermark_data[i]\n", - " wm_labels[label * 10 + i % 10] = label\n", - " wm_images[label * 10 + i % 10, :, :] = img.numpy()\n", - "\n", - " fig = plt.figure(figsize=(120, 120))\n", - " for i in range(100):\n", - " plt.subplot(10, 10, i + 1)\n", - " plt.imshow(wm_images[i], interpolation=\"none\")\n", - " plt.title(\"Label: {}\".format(wm_labels[i]), fontsize=80)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "d82d34fd", - "metadata": {}, - "source": [ - "Next we import the `FLSpec`, `LocalRuntime`, placement decorators (`aggregator/collaborator`), and `InspectFlow`.\n", - "\n", - "- `FLSpec` – Defines the flow specification. User defined flows are subclasses of this.\n", - "- `Runtime` – Defines where the flow runs, infrastructure for task transitions (how information gets sent). The `LocalRuntime` runs the flow on a single node.\n", - "- `aggregator/collaborator` - placement decorators that define where the task will be assigned.\n", - "- `InspectFlow` – Utility to visualize the User-defined workflow as a Graph (only currently compatible in flows without loops)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "89cf4866", - "metadata": {}, - "outputs": [], - "source": [ - "from copy import deepcopy\n", - "\n", - "from openfl.experimental.workflow.interface import FLSpec, Aggregator, Collaborator\n", - "from openfl.experimental.workflow.runtime import LocalRuntime\n", - "from openfl.experimental.workflow.placement import aggregator, collaborator\n", - "from openfl.experimental.workflow.utilities.ui import InspectFlow\n", - "\n", - "\n", - "def FedAvg(models, weights=None): \n", - " models = [model.to('cpu') for model in models]\n", - " new_model = models[0]\n", - " state_dicts = [model.state_dict() for model in models]\n", - " state_dict = new_model.state_dict()\n", - " for key in models[1].state_dict():\n", - " state_dict[key] = torch.from_numpy(np.average([state[key].numpy() for state in state_dicts],\n", - " axis=0, \n", - " weights=weights))\n", - " new_model.load_state_dict(state_dict)\n", - " return new_model" - ] - }, - { - "attachments": { - "image.png": { - "image/png": "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" - } - }, - "cell_type": "markdown", - "id": "c917b085", - "metadata": {}, - "source": [ - "Let us now define the Workflow for Watermark embedding. Here we use the same tasks as the [quickstart](https://github.com/psfoley/openfl/blob/experimental-workflow-interface/openfl-tutorials/experimental/MNIST.ipynb), and define following additional steps for Watermarking:\n", - "- PRE-TRAIN (watermark_retrain): At the start (once), initial model is trained on Watermark dataset for a specified number of epochs.\n", - "- RE-TRAIN (watermark_pretrain): Every training round, Aggregated model is retrained on Watermark dataset until a desired acc threshold is reached or max number of retrain rounds are expired.\n", - "\n", - "Notice that both the PRE-TRAIN and RE-TRAIN tasks are defined as Aggregator processing tasks.\n", - "\n", - "![image.png](attachment:image.png)\\\n", - "\n", - "
Workflow for Watermarking" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "52c4a752", - "metadata": {}, - "outputs": [], - "source": [ - "class AggregatorValCollaboratorGPUWatermarking(FLSpec):\n", - " \"\"\"\n", - " This Flow demonstrates Watermarking on a Deep Learning Model in Federated Learning\n", - " Ref: WAFFLE: Watermarking in Federated Learning (https://arxiv.org/abs/2008.07298)\n", - " \"\"\"\n", - "\n", - " def __init__(\n", - " self,\n", - " model=None,\n", - " optimizer=None,\n", - " watermark_pretrain_optimizer=None,\n", - " watermark_retrain_optimizer=None,\n", - " round_number=0,\n", - " **kwargs,\n", - " ):\n", - " super().__init__(**kwargs)\n", - "\n", - " if model is not None:\n", - " self.model = model\n", - " self.optimizer = optimizer\n", - " self.watermark_pretrain_optimizer = watermark_pretrain_optimizer\n", - " self.watermark_retrain_optimizer = watermark_retrain_optimizer\n", - " else:\n", - " self.model = Net()\n", - " self.optimizer = optim.SGD(\n", - " self.model.parameters(), lr=learning_rate, momentum=momentum\n", - " )\n", - " self.watermark_pretrain_optimizer = optim.SGD(\n", - " self.model.parameters(),\n", - " lr=watermark_pretrain_learning_rate,\n", - " momentum=watermark_pretrain_momentum,\n", - " weight_decay=watermark_pretrain_weight_decay,\n", - " )\n", - " self.watermark_retrain_optimizer = optim.SGD(\n", - " self.model.parameters(), lr=watermark_retrain_learning_rate\n", - " )\n", - " self.round_number = round_number\n", - "\n", - " @aggregator\n", - " def start(self):\n", - " \"\"\"\n", - " This is the start of the Flow.\n", - " \"\"\"\n", - " self.private = 10\n", - " self.current_round = 0\n", - " print(f\": Start of flow ... \")\n", - " self.collaborators = self.runtime.collaborators\n", - "\n", - " # Randomly select a fraction of actual collaborator every round\n", - " fraction = 0.5\n", - " if int(fraction * len(self.collaborators)) < 1:\n", - " raise Exception(\n", - " f\"Cannot run training with {fraction*100}% selected collaborators out of {len(self.collaborators)} Collaborators. Atleast one collaborator is required to run the training\"\n", - " )\n", - " self.subset_collaborators = random.sample(\n", - " self.collaborators, int(fraction * (len(self.collaborators)))\n", - " )\n", - "\n", - " self.next(self.watermark_pretrain)\n", - "\n", - " @aggregator\n", - " def watermark_pretrain(self):\n", - " \"\"\"\n", - " Pre-Train the Model before starting Federated Learning.\n", - " \"\"\"\n", - " if not self.watermark_pretraining_completed:\n", - "\n", - " print(\": Performing Watermark Pre-training\")\n", - "\n", - " for i in range(self.pretrain_epochs):\n", - "\n", - " watermark_pretrain_loss = train_model(\n", - " self.model,\n", - " self.watermark_pretrain_optimizer,\n", - " self.watermark_data_loader,\n", - " \":\",\n", - " i,\n", - " log=False,\n", - " )\n", - " watermark_pretrain_validation_score = inference(\n", - " self.model, self.watermark_data_loader\n", - " )\n", - "\n", - " print(\n", - " \": Watermark Pretraining: Round: {:<3} Loss: {:<.6f} Acc: {:<.6f}\".format(\n", - " i,\n", - " watermark_pretrain_loss,\n", - " watermark_pretrain_validation_score,\n", - " )\n", - " )\n", - "\n", - " self.watermark_pretraining_completed = True\n", - "\n", - " self.next(\n", - " self.aggregated_model_validation,\n", - " foreach=\"subset_collaborators\",\n", - " exclude=[\"watermark_pretrain_optimizer\", \"watermark_retrain_optimizer\"],\n", - " )\n", - "\n", - " @collaborator\n", - " def aggregated_model_validation(self):\n", - " \"\"\"\n", - " Perform Aggregated Model validation on Collaborators.\n", - " \"\"\"\n", - " self.agg_validation_score = inference(self.model, self.test_loader)\n", - " print(\n", - " f\" Aggregated Model validation score = {self.agg_validation_score}\"\n", - " )\n", - "\n", - " self.next(self.train)\n", - "\n", - " @collaborator\n", - " def train(self):\n", - " \"\"\"\n", - " Train model on Local collab dataset.\n", - "\n", - " \"\"\"\n", - " print(\": Performing Model Training on Local dataset ... \")\n", - "\n", - " self.optimizer = optim.SGD(\n", - " self.model.parameters(), lr=learning_rate, momentum=momentum\n", - " )\n", - "\n", - " self.loss = train_model(\n", - " self.model,\n", - " self.optimizer,\n", - " self.train_loader,\n", - " \"\"),\n", - " self.round_number,\n", - " log=True,\n", - " )\n", - "\n", - " self.next(self.local_model_validation)\n", - "\n", - "\n", - " @collaborator\n", - " def local_model_validation(self):\n", - " \"\"\"\n", - " Validate locally trained model.\n", - "\n", - " \"\"\"\n", - " self.local_validation_score = inference(self.model, self.test_loader)\n", - " print(\n", - " f\" Local model validation score = {self.local_validation_score}\"\n", - " )\n", - " self.next(self.join)\n", - "\n", - " @aggregator\n", - " def join(self, inputs):\n", - " \"\"\"\n", - " Model aggregation step.\n", - " \"\"\"\n", - "\n", - " self.average_loss = sum(input.loss for input in inputs) / len(inputs)\n", - " self.aggregated_model_accuracy = sum(\n", - " input.agg_validation_score for input in inputs\n", - " ) / len(inputs)\n", - " self.local_model_accuracy = sum(\n", - " input.local_validation_score for input in inputs\n", - " ) / len(inputs)\n", - "\n", - " print(f\": Joining models from collaborators...\")\n", - "\n", - " print(\n", - " f\" Aggregated model validation score = {self.aggregated_model_accuracy}\"\n", - " )\n", - " print(f\" Average training loss = {self.average_loss}\")\n", - " print(f\" Average local model validation values = {self.local_model_accuracy}\")\n", - " \n", - " highest_accuracy = 0\n", - " highest_accuracy_model_idx = -1\n", - " for idx,col in enumerate(inputs):\n", - " accuracy_for_held_out_agg_data = inference(col.model,self.test_loader)\n", - " if accuracy_for_held_out_agg_data > highest_accuracy:\n", - " highest_accuracy = accuracy_for_held_out_agg_data\n", - " highest_accuracy_model_idx = idx\n", - " \n", - " relative_model_weights = len(inputs)*[1]\n", - " # Give highest accuracy model (on held out aggregator data) 2x the importance\n", - " relative_model_weights[highest_accuracy_model_idx] = 2\n", - " print(f'Aggregator validation score: {highest_accuracy}')\n", - " print(f'Highest accuracy model sent from {inputs[highest_accuracy_model_idx].input}. Receiving 2x weight in updated model')\n", - " self.model = FedAvg([input.model for input in inputs],weights=relative_model_weights)\n", - " self.optimizer = [input.optimizer for input in inputs][0]\n", - " self.current_round += 1\n", - " if self.current_round < self.round_number:\n", - " self.next(self.aggregated_model_validation, foreach='collaborators', exclude=['private'])\n", - " else:\n", - " self.next(self.watermark_retrain)\n", - "\n", - " @aggregator\n", - " def watermark_retrain(self):\n", - " \"\"\"\n", - " Retrain the aggregated model.\n", - "\n", - " \"\"\"\n", - " print(\": Performing Watermark Retraining ... \")\n", - " self.watermark_retrain_optimizer = optim.SGD(\n", - " self.model.parameters(), lr=watermark_retrain_learning_rate\n", - " )\n", - "\n", - " retrain_round = 0\n", - "\n", - " # Perform re-training until (accuracy >= acc_threshold) or (retrain_round > number of retrain_epochs)\n", - " self.watermark_retrain_validation_score = inference(\n", - " self.model, self.watermark_data_loader\n", - " )\n", - " while (\n", - " self.watermark_retrain_validation_score < self.watermark_acc_threshold\n", - " ) and (retrain_round < self.retrain_epochs):\n", - " self.watermark_retrain_train_loss = train_model(\n", - " self.model,\n", - " self.watermark_retrain_optimizer,\n", - " self.watermark_data_loader,\n", - " \"\",\n", - " retrain_round,\n", - " log=False,\n", - " )\n", - " self.watermark_retrain_validation_score = inference(\n", - " self.model, self.watermark_data_loader\n", - " )\n", - "\n", - " print(\n", - " \": Watermark Retraining: Train Epoch: {:<3} Retrain Round: {:<3} Loss: {:<.6f}, Acc: {:<.6f}\".format(\n", - " self.round_number,\n", - " retrain_round,\n", - " self.watermark_retrain_train_loss,\n", - " self.watermark_retrain_validation_score,\n", - " )\n", - " )\n", - "\n", - " retrain_round += 1\n", - "\n", - " self.next(self.end)\n", - "\n", - " @aggregator\n", - " def end(self):\n", - " \"\"\"\n", - " This is the last step in the Flow.\n", - "\n", - " \"\"\"\n", - " print(f\"This is the end of the flow\")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "c6da2c42", - "metadata": {}, - "source": [ - "In the `AggregatorValCollaboratorGPUWatermarking` definition above, you will notice that certain attributes of the flow were not initialized, namely the `watermark_data_loader` for Aggregator and `train_loader`, `test_loader` for the Collaborators. \n", - "\n", - "- Collaborator attributes are created in the same manner as described in [quickstart](https://github.com/psfoley/openfl/blob/experimental-workflow-interface/openfl-tutorials/experimental/101_MNIST.ipynb).\n", - "\n", - "- `watermark_data_loader` is created as a **private attribute** of the Aggregator and it is exposed only via the runtime. This property enables the Watermark dataset to be hidden from the collaborators as Aggregator private attributes are filtered before the state is transferred to Collaborators (in the same manner as Collaborator private attributes are hidden from Aggregator).\n", - "\n", - "Lets define these attributes along with some other parameters (seed, batch-sizes, optimizer parameters) and create the LocalRuntime:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bffcc141", - "metadata": {}, - "outputs": [], - "source": [ - "# Set random seed\n", - "random_seed = 42\n", - "torch.manual_seed(random_seed)\n", - "np.random.seed(random_seed)\n", - "torch.backends.cudnn.enabled = False\n", - "\n", - "# Batch sizes\n", - "batch_size_train = 64\n", - "batch_size_test = 64\n", - "batch_size_watermark = 50\n", - "\n", - "# MNIST parameters\n", - "learning_rate = 5e-2\n", - "momentum = 5e-1\n", - "log_interval = 20\n", - "\n", - "# Watermarking parameters\n", - "watermark_pretrain_learning_rate = 1e-1\n", - "watermark_pretrain_momentum = 5e-1\n", - "watermark_pretrain_weight_decay = 5e-05\n", - "watermark_retrain_learning_rate = 5e-3\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "3d7ce52f", - "metadata": {}, - "source": [ - "## Setup Federation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c5f6e104", - "metadata": {}, - "outputs": [], - "source": [ - "# Setup Aggregator with private attributes\n", - "aggregator = Aggregator(num_gpus=0.0)\n", - "\n", - "# Setup Collaborators with private attributes\n", - "collaborator_names = [\n", - " \"Portland\",\n", - " \"Seattle\",\n", - " \"Chandler\",\n", - " \"Bangalore\",\n", - " \"New Delhi\",\n", - "]\n", - "print(f\"Creating collaborators {collaborator_names}\")\n", - "collaborators = [Collaborator(name=name, num_gpus=0.0) for name in collaborator_names]\n", - "\n", - "aggregator_test = deepcopy(mnist_test)\n", - "aggregator_test.targets = mnist_test.targets[len(collaborators)::len(collaborators)+1]\n", - "aggregator_test.data = mnist_test.data[len(collaborators)::len(collaborators)+1]\n", - "\n", - "aggregator.private_attributes = {\n", - " \"watermark_data_loader\": torch.utils.data.DataLoader(\n", - " watermark_data, batch_size=batch_size_watermark, shuffle=True\n", - " ),\n", - " \"test_loader\": torch.utils.data.DataLoader(aggregator_test,batch_size=batch_size_train, shuffle=True),\n", - " \"pretrain_epochs\": 25,\n", - " \"retrain_epochs\": 25,\n", - " \"watermark_acc_threshold\": 0.98,\n", - " \"watermark_pretraining_completed\": False,\n", - "}\n", - "\n", - "for idx, collaborator in enumerate(collaborators):\n", - " local_train = deepcopy(mnist_train)\n", - " local_test = deepcopy(mnist_test)\n", - " local_train.data = mnist_train.data[idx :: len(collaborators)]\n", - " local_train.targets = mnist_train.targets[idx :: len(collaborators)]\n", - " local_test.data = mnist_test.data[idx :: len(collaborators)]\n", - " local_test.targets = mnist_test.targets[idx :: len(collaborators)]\n", - " collaborator.private_attributes = {\n", - " \"train_loader\": torch.utils.data.DataLoader(\n", - " local_train, batch_size=batch_size_train, shuffle=True\n", - " ),\n", - " \"test_loader\": torch.utils.data.DataLoader(\n", - " local_test, batch_size=batch_size_train, shuffle=True\n", - " ),\n", - " }\n", - "\n", - "local_runtime = LocalRuntime(aggregator=aggregator, collaborators=collaborators)\n", - "print(f\"Local runtime collaborators = {local_runtime.collaborators}\")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "02935ccf", - "metadata": {}, - "source": [ - "Now that we have our flow and runtime defined, let's run the experiment! " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c6d19819", - "metadata": {}, - "outputs": [], - "source": [ - "model = None\n", - "best_model = None\n", - "optimizer = None\n", - "watermark_pretrain_optimizer = None\n", - "watermark_retrain_optimizer = None\n", - "\n", - "top_model_accuracy = 0\n", - "\n", - "flflow = AggregatorValCollaboratorGPUWatermarking(\n", - " model,\n", - " optimizer,\n", - " watermark_pretrain_optimizer,\n", - " watermark_retrain_optimizer,\n", - " 0,\n", - " checkpoint=True,\n", - ")\n", - "flflow.runtime = local_runtime\n", - "\n", - "for i in range(5):\n", - " print(f\"Starting round {i}...\")\n", - " flflow.run()\n", - " flflow.round_number += 1\n", - " aggregated_model_accuracy = flflow.aggregated_model_accuracy\n", - " if aggregated_model_accuracy > top_model_accuracy:\n", - " print(\n", - " f\"\\nAccuracy improved to {aggregated_model_accuracy} for round {i}, Watermark Acc: {flflow.watermark_retrain_validation_score}\\n\"\n", - " )\n", - " top_model_accuracy = aggregated_model_accuracy\n", - " best_model = flflow.model\n", - "\n", - "torch.save(best_model.state_dict(), \"watermarked_mnist_model.pth\")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "bf66c1cd", - "metadata": {}, - "source": [ - "Finally we visualize the User-workflow as a Flowgraph as an html file. This requires checkpointing to be enabled and `InspectFlow` generates the Flowgraph for the Federated Flowobject and associated run-id." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d363a2cf", - "metadata": {}, - "outputs": [], - "source": [ - "# Inspect Flowgraph\n", - "if flflow._checkpoint:\n", - " InspectFlow(flflow, flflow._run_id, show_html=True)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "openfl_org", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/openfl-tutorials/experimental/workflow/402_FedProx_with_Synthetic_nonIID.ipynb b/openfl-tutorials/experimental/workflow/402_FedProx_with_Synthetic_nonIID.ipynb deleted file mode 100644 index f933718fac..0000000000 --- a/openfl-tutorials/experimental/workflow/402_FedProx_with_Synthetic_nonIID.ipynb +++ /dev/null @@ -1,822 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Workflow Interface 401: Synthetic non-IID Dataset with FedProx Optimizer\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/Workflow_Interface_401_FedProx_with_Synthetic_nonIID.ipynb)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this OpenFL workflow interface tutorial, we shall learn how to implement FedProx and compare its performance with FedAvg algorithm using a Synthetic non-IID dataset. Reference: [Federated Optimization in Heterogeneous Networks](https://arxiv.org/pdf/1812.06127.pdf)." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Getting Started" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First we start by installing the necessary dependencies for the workflow interface" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!pip install git+https://github.com/securefederatedai/openfl.git\n", - "!pip install -r workflow_interface_requirements.txt\n", - "!pip install torch\n", - "!pip install torchvision\n", - "!pip install matplotlib\n", - "!pip install seaborn\n", - "\n", - "# Uncomment following lines if running in Google Colab\n", - "# import os\n", - "# os.environ[\"USERNAME\"] = \"colab\"" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next we import necessary libraries, and define Synthetic non-iid dataset as described in [Federated Optimization in Heterogeneous Networks](https://arxiv.org/pdf/1812.06127.pdf)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import torch as pt\n", - "import torch.utils.data as data\n", - "import torch.nn as nn\n", - "import torch.nn.functional as F\n", - "\n", - "import numpy as np\n", - "\n", - "import random\n", - "import collections\n", - "import matplotlib.pyplot as plt\n", - "%matplotlib inline\n", - "\n", - "import warnings\n", - "warnings.filterwarnings(\"ignore\")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "RANDOM_SEED = 10\n", - "batch_size = 10\n", - "\n", - "# Sets seed to reproduce the results\n", - "def set_seed(seed):\n", - " pt.manual_seed(seed)\n", - " pt.cuda.manual_seed_all(seed)\n", - " pt.use_deterministic_algorithms(True)\n", - " pt.backends.cudnn.deterministic = True\n", - " pt.backends.cudnn.benchmark = False\n", - " pt.backends.cudnn.enabled = False\n", - " np.random.seed(seed)\n", - " random.seed(seed)\n", - "\n", - "set_seed(RANDOM_SEED)\n", - "\n", - "\n", - "def one_hot(labels, classes):\n", - " return np.eye(classes)[labels]\n", - "\n", - "\n", - "def softmax(x):\n", - " ex = np.exp(x)\n", - " sum_ex = np.sum(np.exp(x))\n", - " return ex / sum_ex\n", - "\n", - "\n", - "def generate_synthetic(alpha, beta, iid, num_collaborators, num_classes):\n", - " dimension = 60\n", - " NUM_CLASS = num_classes\n", - " NUM_USER = num_collaborators\n", - "\n", - " samples_per_user = np.random.lognormal(4, 2, (NUM_USER)).astype(int) + 50\n", - " num_samples = np.sum(samples_per_user)\n", - "\n", - " X_split = [[] for _ in range(NUM_USER)]\n", - " y_split = [[] for _ in range(NUM_USER)]\n", - "\n", - " #### define some eprior ####\n", - " mean_W = np.random.normal(0, alpha, NUM_USER)\n", - " mean_b = mean_W\n", - " B = np.random.normal(0, beta, NUM_USER)\n", - " mean_x = np.zeros((NUM_USER, dimension))\n", - "\n", - " diagonal = np.zeros(dimension)\n", - " for j in range(dimension):\n", - " diagonal[j] = np.power((j + 1), -1.2)\n", - " cov_x = np.diag(diagonal)\n", - "\n", - " for i in range(NUM_USER):\n", - " if iid == 1:\n", - " mean_x[i] = np.ones(dimension) * B[i] # all zeros\n", - " else:\n", - " mean_x[i] = np.random.normal(B[i], 1, dimension)\n", - "\n", - " if iid == 1:\n", - " W_global = np.random.normal(0, 1, (dimension, NUM_CLASS))\n", - " b_global = np.random.normal(0, 1, NUM_CLASS)\n", - "\n", - " for i in range(NUM_USER):\n", - "\n", - " W = np.random.normal(mean_W[i], 1, (dimension, NUM_CLASS))\n", - " b = np.random.normal(mean_b[i], 1, NUM_CLASS)\n", - "\n", - " if iid == 1:\n", - " W = W_global\n", - " b = b_global\n", - "\n", - " xx = np.random.multivariate_normal(\n", - " mean_x[i], cov_x, samples_per_user[i])\n", - " yy = np.zeros(samples_per_user[i])\n", - "\n", - " for j in range(samples_per_user[i]):\n", - " tmp = np.dot(xx[j], W) + b\n", - " yy[j] = np.argmax(softmax(tmp))\n", - "\n", - " X_split[i] = xx.tolist()\n", - " y_split[i] = yy.tolist()\n", - "\n", - " return X_split, y_split\n", - "\n", - "\n", - "class SyntheticFederatedDataset:\n", - " def __init__(self, num_collaborators, batch_size=1, num_classes=10, **kwargs):\n", - " self.batch_size = batch_size\n", - " X, y = generate_synthetic(0.0, 0.0, 0, num_collaborators, num_classes)\n", - " X = [np.array([np.array(sample).astype(np.float32)\n", - " for sample in col]) for col in X]\n", - " y = [np.array([np.array(one_hot(int(sample), num_classes))\n", - " for sample in col]) for col in y]\n", - " self.X_train_all = np.array([col[:int(0.9 * len(col))] for col in X], dtype=np.ndarray)\n", - " self.X_valid_all = np.array([col[int(0.9 * len(col)):] for col in X], dtype=np.ndarray)\n", - " self.y_train_all = np.array([col[:int(0.9 * len(col))] for col in y], dtype=np.ndarray)\n", - " self.y_valid_all = np.array([col[int(0.9 * len(col)):] for col in y], dtype=np.ndarray)\n", - "\n", - " def split(self, index):\n", - " return {\n", - " \"train_loader\":\n", - " data.DataLoader(\n", - " data.TensorDataset(\n", - " pt.from_numpy(self.X_train_all[index]),\n", - " pt.from_numpy(self.y_train_all[index])\n", - " ), \n", - " batch_size=batch_size, shuffle=True\n", - " ),\n", - " \"test_loader\":\n", - " data.DataLoader(\n", - " data.TensorDataset(\n", - " pt.from_numpy(self.X_valid_all[index]),\n", - " pt.from_numpy(self.y_valid_all[index])\n", - " ), \n", - " batch_size=batch_size, shuffle=True\n", - " )\n", - " }" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that we have defined dataset class. Let define model, optimizer, and some helper functions like we would for any other deep learning experiment." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from openfl.interface.aggregation_functions.weighted_average import weighted_average as wa\n", - "\n", - "\n", - "class Net(nn.Module):\n", - " \"\"\"\n", - " Model to train the dataset\n", - "\n", - " Args:\n", - " None\n", - " \n", - " Returns:\n", - " model: class Net object\n", - " \"\"\"\n", - " def __init__(self):\n", - " # Set RANDOM_STATE to reproduce same model\n", - " pt.set_rng_state(pt.manual_seed(RANDOM_SEED).get_state())\n", - " super(Net, self).__init__()\n", - " self.linear1 = nn.Linear(60, 100)\n", - " self.linear2 = nn.Linear(100, 10)\n", - "\n", - " def forward(self, x):\n", - " x = self.linear1(x)\n", - " x = self.linear2(x)\n", - " return x\n", - "\n", - "\n", - "def cross_entropy(output, target):\n", - " \"\"\"\n", - " cross-entropy metric\n", - "\n", - " Args:\n", - " output: model ouput,\n", - " target: target label\n", - "\n", - " Returns:\n", - " crossentropy_loss: float\n", - " \"\"\"\n", - " return F.cross_entropy(output, pt.max(target, 1)[1])\n", - "\n", - "\n", - "def compute_loss_and_acc(network, dataloader):\n", - " \"\"\"\n", - " Model test method\n", - "\n", - " Args:\n", - " network: class Net object (model)\n", - " dataloader: torch.utils.data.DataLoader\n", - "\n", - " Returns:\n", - " (accuracy,\n", - " loss,\n", - " correct,\n", - " dataloader_size)\n", - " \"\"\"\n", - " network.eval()\n", - " test_loss = 0\n", - " correct = 0\n", - " with pt.no_grad():\n", - " for data, target in dataloader:\n", - " output = network(data)\n", - " test_loss += cross_entropy(output, target).item()\n", - " tar = target.argmax(dim=1, keepdim=True)\n", - " pred = output.argmax(dim=1, keepdim=True)\n", - " correct += pred.eq(tar).sum().cpu().numpy()\n", - " dataloader_size = len(dataloader.dataset)\n", - " test_loss /= dataloader_size\n", - " accuracy = float(correct / dataloader_size)\n", - " return accuracy, test_loss, correct\n", - "\n", - "\n", - "def weighted_average(tensors, weights):\n", - " \"\"\"\n", - " Take weighted average of models / optimizers / loss / accuracy\n", - " Incase of taking weighted average of optimizer do the following steps:\n", - " 1. Call \"_get_optimizer_state\" (openfl.federated.task.runner_pt._get_optimizer_state)\n", - " pass optimizer to it, to take optimizer state dictionary.\n", - " 2. Pass optimizer state dictionaries list to here.\n", - " 3. To set the weighted average optimizer state dictionary back to optimizer,\n", - " call \"_set_optimizer_state\" (openfl.federated.task.runner_pt._set_optimizer_state)\n", - " and pass optimizer, device, and optimizer dictionary received in step 2.\n", - "\n", - " Args:\n", - " tensors: Models state_dict list or optimizers state_dict list or loss list or accuracy list\n", - " weights: Weight for each element in the list\n", - "\n", - " Returns:\n", - " dict: Incase model list / optimizer list OR\n", - " float: Incase of loss list or accuracy list\n", - " \"\"\"\n", - " # Check the type of first element of tensors list\n", - " if type(tensors[0]) in (dict, collections.OrderedDict):\n", - " optimizer = False\n", - " # If __opt_state_needed found then optimizer state dictionary is passed\n", - " if \"__opt_state_needed\" in tensors[0]:\n", - " optimizer = True\n", - " # Remove __opt_state_needed from all state dictionary in list\n", - " [tensor.pop(\"__opt_state_needed\") for tensor in tensors]\n", - " tmp_list = []\n", - " # Take keys in order to rebuild the state dictionary taking keys back up\n", - " input_state_dict_keys = tensors[0].keys()\n", - " for tensor in tensors:\n", - " # Append values of each state dictionary in list\n", - " # If type(value) is Tensor then it needs to be detached\n", - " tmp_list.append(np.array([value.detach() if type(value) is pt.Tensor else value for value in tensor.values()], dtype=object))\n", - " # Take weighted average of list of arrays\n", - " # new_params passed is weighted average of each array in tmp_list\n", - " new_params = wa(tmp_list, weights)\n", - " new_state = {}\n", - " # Take weighted average parameters and building a dictionary\n", - " [new_state.update({k:new_params[i]}) if optimizer else new_state.update({k:pt.from_numpy(new_params[i].numpy())}) \\\n", - " for i, k in enumerate(input_state_dict_keys)]\n", - " return new_state\n", - " else:\n", - " return wa(tensors, weights)" - ] - }, - { - "attachments": { - "federated-flow-diagram.png": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAzMAAAJZCAYAAACds2s+AAAAAXNSR0IArs4c6QAACih0RVh0bXhmaWxlACUzQ214ZmlsZSUyMGhvc3QlM0QlMjJhcHAuZGlhZ3JhbXMubmV0JTIyJTIwbW9kaWZpZWQlM0QlMjIyMDIzLTA1LTA1VDA5JTNBMzklM0E1OS4wNTVaJTIyJTIwYWdlbnQlM0QlMjJNb3ppbGxhJTJGNS4wJTIwKFdpbmRvd3MlMjBOVCUyMDEwLjAlM0IlMjBXaW42NCUzQiUyMHg2NCklMjBBcHBsZVdlYktpdCUyRjUzNy4zNiUyMChLSFRNTCUyQyUyMGxpa2UlMjBHZWNrbyklMjBDaHJvbWUlMkYxMTMuMC4wLjAlMjBTYWZhcmklMkY1MzcuMzYlMjIlMjBldGFnJTNEJTIybjRCVEJwRzN5TXkya3JubmRGVXYlMjIlMjB2ZXJzaW9uJTNEJTIyMjEuMS43JTIyJTIwdHlwZSUzRCUyMmRldmljZSUyMiUzRSUzQ2RpYWdyYW0lMjBuYW1lJTNEJTIyUGFnZS0xJTIyJTIwaWQlM0QlMjJnMDBDN1BBcVZyaXRlTVVsdmxnWiUyMiUzRTdWeGRkNm80RlAwMVByWXJmSWslMkJVc3J0eDlqYXBYWTZmWEpSaWNnVWpZTjRxJTJGZlhUNUNnSkdCRmlvQnQlMkJsSVNra055Mkp0a244UTBKSDI2dXZITSUyQmVRQldkQnRpTUJhTmFUcmhpZ0tLZ0Q0WDVDekRuTVVRUWt6Yk0lMkJ4U0tGZFJ0JTJGNUEwa21xV2N2SFFzdXFJSSUyQlFxN3Z6T25NRVpyTjRNaW44a3pQUXg5MHNURnk2YWZPVFJzbU12b2owMDNtdmppV1B3bHp0OTBLOG0lMkJoWTAlMkJpSjRQb3p0U01DcE9NeGNTMDBFY3NTeklha3U0aDVJZFgwNVVPM2NCNWtWJTJGQ2VyJTJGMjNOMDJ6SU16UDBzRjBYeWZ0JTJCJTJGdUxRZGFvJTJGSDk3ZEo4ZUgyNWFJcWhtZCUyQm11eVE5SnEzMTE1RUxiQTh0NTZRWTlIeTRTbk84JTJCUllWQjhtR0NkdnVZcHhBTklXJTJCdDhaRmlLRUxRU0oxQ0Vaa2t2ellPYndWNFdFU2MzWXo4clZKWHJLOXRiM3pBNzRncmpqQ0xVS0tWNW91ZnV6VkdPRk94ZDNUJTJGRzhadk1FcjZzb20lMkZ6ZFZGbk56bGxvbHNIV3gyRUJld3dVRWViNUtOZFFmYUwxQlpBMzNKelJJUCUyQlROWTNQMnRwUjViSHZQVXhzeTBHemJnN2JwSXklMkYyOU5EdW9hY25Daktnd3BpYVdUQjRCd0slMkIlMkZURnhmTmlmbTZQZzdnZiUyQmt1QzhpVDkxeWUyeDQ3bzZjbkV6Z3JxU0NPVjJTeVk5aWVXUE4zJTJCQngzMFB2Y040RFlQVW1LTGZCS21CWFE5aU44VFR5RGY5V0JwJTJGekdBOERTMG5ublRSNkgzVEJZQVROQSUyQkViWjlUS0JPbnhpZXMzRXVZN2NjeFJoQ3BwU1FKSXAlMkJLSDFLdCUyQktGM0g1NmVCOGF3MCUyQjMzaDlyajlWRFQ5ZWVlcHI5V3dSbU1PdGQ4UTE3dFdBUEFHeGlMeDdBR2dLYWglMkZmbzJyRkhGUzFHaEJwcFdra2Q0VUNtUlJ6TG5FZWZSdWZHb2RpeFNPSXM0aTg2TVJZcGNQeDYxRTI4WVdsZ01reVR5JTJGQW15MGN4MGpWM3UxUTREZ1FOM1pUb0l6WWtQJTJGNFclMkJ2eWJLM2x6NmlNWUY5cUszJTJGaWVvZjZsRXlWZGlicE80WGxHcE5Va2w4TkV5VlZqUkhEOXdVeDUwWUYlMkJqcFRlQ2g1V29iM28yJTJGTXlnbEk0MkQ3cW03JTJGeW1XNWNHSEZMMUNUbWJidzVCcWFRR2I0WFM1b3lOc0FPa1dqejZjTkJTaTdFVWRqRmhhWVBrYlklMkZ5Z3p2cSUyRkhtZ0c2NGNQMVlOcDE0amklMkZoNlZ5bElmQ2RHRkk1Z0dZaVhvQjM3VXlrUUNpTEloJTJCZERkcHVnWEhSbmlPUEJtYVVGRVZHY0dybm1ZdUdNYUt6U1lNJTJCRFFQNHRMJTJCUmJMcWN6SVRZdlVGS21CVkhlVndtajBNRllFYWo1R0tJQTJwQ2dNSVpPVFltMElCV254SGxTUXFtVUVrQmdrSnh6MEdBWE9vUjJ5YU5Fb2ZHbXJQSjByMUslMkIwUWEzUm0lMkZZTSUyRnJQblVHb2xRZmFYOGJ3eGJpN3VSMFlXRG4lMkZiZlMwRyUyQk5vNGZwOWx6NEFVSVZSJTJCeGpaakNmZVFOZSUyRmkyemVMbjJ3OHl4cUtVUyUyQkxITXhSQ3cwJTJGdlJsVnZXTmpxRVBobnEzMDlHdXVqMXQwTzMxT1lNNGd5SUdBZlZTcFpWNDJvSjc2U1JTazVNekxzJTJGck5xTTdpVHluSjFjaXU2bmpHRVV1cE9PNnJPbFZxdzZLb3g0NHk4dXNreXNPT2FQaUVDdFY0UzFXaE9ja0JSNjcwdWRMWlZHaXpTbFJlMG9vWjBHSlJHQ0toWEwyZ2FKYVRrUkJnSnJvaFVGUHUzc2NocW9CYSUyQjdLZFFOZnNxNnJjbURHRWtsTXlvWnlGNnlsUXZmcGNpWnhKcDJZU2ZWaGpsZ2tjNzY4WnlvamMlMkZpMnFSJTJGTG5PMjJxZHFOUVduUjM5SVZ6czllWmp5OE95cmElMkZITklDb21WTGpSS0toUEFhdVdVT096YXU5UXVkJTJCMWRhaFpPaXJNR2VCNUtuNTRVV2VNRDFaS0NsZXU1U1pING1hRlVzdTVYJTJCVWhSOVVoeEVPdFMxaDBwVXJ0S1VqRHhZWm1kM3VTTkQ4dHNvUG5Vbk9CTEp2V1BEMGNEUmMwNXdRYUlsYUlXVFVvblJkcWlTWFhpJTJGTDU3OTFpRkNPY2JTR29wd2RrdFdGdDJWTGg3Uks3WEQ5S054MnRPbU9UaEN6JTJGOXVJWm9YS3JCbmtXNVhyJTJCWnZYc2NHTDFIclRQc2RMdFBuRHA4ck5rMzFxVDhSclpzNmlocG0lMkJnTGtTeVJTSG1sNU1zQkdiOVZEckZhUjhsNFRUVTIzOXFUeXdnNTZ6NlRQY2RGbGFNaW1nZUNSRmxGUkpNTlcyWFRFQmcyNWpwV2JCNFVXT3h2Ym9zZFhBRDR0RmxzZWZyUU1Id1JOcUJRUGFPMGtnVGhlM3ZyRmh3clkyOXYzdEJ0MHBTVWtVJTJCRllmaXNEa2ZnR0Q0WmhxWDgydzVaVTJYSGxhS1djd3olMkZjQXpuWGk1SU1jViUyQjBrJTJCTllYNk1COGZ3WnFMN2xaTTdHRk9GYlFESHlkMkJ2bUh4M2JISWt2RSUyRiUzQyUyRmRpYWdyYW0lM0UlM0MlMkZteGZpbGUlM0UlCf0tAAAgAElEQVR4XuydCZgc1XXvz63qWbSMQBubQGzqNg8BAYSM8RJw4CUE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" - } - }, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let us now define the Workflow for our experiment. Here we use the methodology as provided in [quickstart](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/Workflow_Interface_101_MNIST.ipynb), and define the workflow consisting of following steps:\n", - "-\t`start`: Start of the flow \n", - "-\t`compute_loss_and_accuracy`: Compute Train Loss and Test Accuracy on aggregated model. Performed *foreach collaborator* in Federation\n", - "-\t`gather_results_and_take_weighted_average`: Collect train loss, and test accuracy metrics for each collaborator and take weighted average to compute the *Aggregated* Train Loss and Test Accuracy. Performed on Aggregator\n", - "-\t`select_collaborators`: Randomly select *n_selected_collaborators* from the entire set of collaborators in Federation. Performed on Aggregator\n", - "-\t‘train_selected_collaborators` - Train selected collaborators on its individual datasets for *local_epoch* number of times. Performed on *n_selected_collaborators*\n", - "-\t`join`: Take weighted average of the model. Performed on Aggregator\n", - "-\t`end`: End of one round of flow. Flow can be run for *n_epochs* to obtain the desired results\n", - "\n", - "We also import the FedProxOptimizer from openfl.utilities.optimizer\n", - "\n", - "![federated-flow-diagram.png](attachment:federated-flow-diagram.png)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from openfl.experimental.workflow.interface import FLSpec, Aggregator, Collaborator\n", - "from openfl.experimental.workflow.runtime import LocalRuntime\n", - "from openfl.experimental.workflow.placement import aggregator, collaborator\n", - "from openfl.utilities.optimizers.torch import FedProxOptimizer" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "class FedProxFlow(FLSpec):\n", - "\n", - " def __init__(self, model=None, optimizer=None, n_selected_collaborators=10, n_rounds=10, **kwargs):\n", - " super(FedProxFlow, self).__init__(**kwargs)\n", - " self.round_number = 1\n", - " self.n_selected_collaborators = n_selected_collaborators\n", - " self.n_rounds = n_rounds\n", - " self.loss_and_acc = {\"Train Loss\": [], \"Test Accuracy\": []}\n", - " if model is not None:\n", - " self.model = model\n", - " self.optimizer = optimizer\n", - " else:\n", - " self.model = Net()\n", - " self.optimizer = FedProxOptimizer(\n", - " self.model.parameters(), lr=learning_rate, mu=mu, weight_decay=weight_decay)\n", - "\n", - " @aggregator\n", - " def start(self):\n", - " \"\"\"\n", - " Start of the flow. Call compute_loss_and_accuracy step for each collaborator\n", - " \"\"\"\n", - " print(f'\\nStarting round number {self.round_number} .... \\n')\n", - " self.collaborators = self.runtime.collaborators\n", - " self.next(self.compute_loss_and_accuracy, foreach='collaborators')\n", - "\n", - " @collaborator\n", - " def compute_loss_and_accuracy(self):\n", - " \"\"\"\n", - " Compute training accuracy, training loss, aggregated validation accuracy,\n", - " aggregated validation loss, \n", - " \"\"\"\n", - " # Compute Train Loss and Train Acc\n", - " self.training_accuracy, self.training_loss, _, = compute_loss_and_acc(\n", - " self.model, self.train_loader)\n", - " \n", - " # Compute Test Loss and Test Acc\n", - " self.agg_validation_score, self.agg_validation_loss, test_correct = compute_loss_and_acc(\n", - " self.model, self.test_loader)\n", - "\n", - " self.train_dataset_length = len(self.train_loader.dataset)\n", - " self.test_dataset_length = len(self.test_loader.dataset)\n", - "\n", - " print(\n", - " \" | Train Round: {:<5} : Train Loss {:<.6f}, Test Acc: {:<.6f} [{}/{}]\".format(\n", - " self.input,\n", - " self.round_number,\n", - " self.training_loss,\n", - " self.agg_validation_score,\n", - " test_correct, \n", - " self.test_dataset_length\n", - " )\n", - " )\n", - "\n", - " self.next(self.gather_results_and_take_weighted_average)\n", - "\n", - " @aggregator\n", - " def gather_results_and_take_weighted_average(self, inputs):\n", - " \"\"\"\n", - " Gather results of all collaborators computed in previous \n", - " step.\n", - " Compute train and test weightes, and compute weighted average of \n", - " aggregated training loss, and aggregated test accuracy\n", - " \"\"\"\n", - " # Calculate train_weights and test_weights\n", - " train_datasize, test_datasize = [], []\n", - " for input_ in inputs:\n", - " train_datasize.append(input_.train_dataset_length)\n", - " test_datasize.append(input_.test_dataset_length)\n", - "\n", - " self.train_weights, self.test_weights = [], []\n", - " for input_ in inputs:\n", - " self.train_weights.append(input_.train_dataset_length / sum(train_datasize))\n", - " self.test_weights.append(input_.test_dataset_length / sum(test_datasize))\n", - "\n", - " aggregated_model_accuracy_list, aggregated_model_loss_list = [], []\n", - " for input_ in inputs:\n", - " aggregated_model_loss_list.append(input_.training_loss)\n", - " aggregated_model_accuracy_list.append(input_.agg_validation_score)\n", - "\n", - " # Weighted average of training loss\n", - " self.aggregated_model_training_loss = weighted_average(aggregated_model_loss_list, self.train_weights)\n", - " # Weighted average of aggregated model accuracy\n", - " self.aggregated_model_test_accuracy = weighted_average(aggregated_model_accuracy_list, self.test_weights)\n", - "\n", - " # Store experiment results\n", - " self.loss_and_acc[\"Train Loss\"].append(self.aggregated_model_training_loss)\n", - " self.loss_and_acc[\"Test Accuracy\"].append(self.aggregated_model_test_accuracy)\n", - "\n", - " print(\n", - " \" | Train Round: {:<5} : Agg Train Loss {:<.6f}, Agg Test Acc: {:<.6f}\".format(\n", - " self.round_number,\n", - " self.aggregated_model_training_loss,\n", - " self.aggregated_model_test_accuracy\n", - " )\n", - " )\n", - "\n", - " self.next(self.select_collaborators)\n", - "\n", - " @aggregator\n", - " def select_collaborators(self):\n", - " \"\"\"\n", - " Randomly select n_selected_collaborators collaborator\n", - " \"\"\"\n", - " np.random.seed(self.round_number)\n", - " self.selected_collaborator_indices = np.random.choice(range(len(self.collaborators)), \\\n", - " self.n_selected_collaborators, replace=False)\n", - " self.selected_collaborators = [self.collaborators[idx] for idx in self.selected_collaborator_indices]\n", - "\n", - " self.next(self.train_selected_collaborators, foreach=\"selected_collaborators\")\n", - "\n", - " @collaborator\n", - " def train_selected_collaborators(self):\n", - " \"\"\"\n", - " Train selected collaborators\n", - " \"\"\"\n", - " self.model.train(mode=True)\n", - "\n", - " self.train_dataset_length = len(self.train_loader.dataset)\n", - "\n", - " # Rebuild the optimizer with global model parameters\n", - " self.optimizer = FedProxOptimizer(\n", - " self.model.parameters(), lr=learning_rate, mu=mu, weight_decay=weight_decay)\n", - " # Set global model parameters as old weights to enable computation of proximal term\n", - " self.optimizer.set_old_weights([p.clone().detach() for p in self.model.parameters()])\n", - "\n", - " for epoch in range(local_epoch):\n", - " train_loss = []\n", - " correct = 0\n", - " for data, target in self.train_loader:\n", - " self.optimizer.zero_grad()\n", - " output = self.model(data)\n", - " loss = cross_entropy(output, target)\n", - " loss.backward()\n", - " self.optimizer.step()\n", - " pred = output.argmax(dim=1, keepdim=True)\n", - " tar = target.argmax(dim=1, keepdim=True)\n", - " correct += pred.eq(tar).sum().cpu().numpy()\n", - " train_loss.append(loss.item())\n", - " training_accuracy = float(correct / self.train_dataset_length)\n", - " training_loss = np.mean(train_loss)\n", - " print(\n", - " \" | Train Round: {:<5} | Local Epoch: {:<3}: FedProx Optimization Train Loss {:<.6f}, Train Acc: {:<.6f} [{}/{}]\".format(\n", - " self.input,\n", - " self.round_number,\n", - " epoch,\n", - " training_loss,\n", - " training_accuracy,\n", - " correct, \n", - " len(self.train_loader.dataset)\n", - " )\n", - " )\n", - "\n", - " self.next(self.join)\n", - " \n", - " @aggregator\n", - " def join(self, inputs):\n", - " \"\"\"\n", - " Compute train dataset, and take weighted average of model.\n", - " \"\"\"\n", - " train_datasize = sum([input_.train_dataset_length for input_ in inputs])\n", - "\n", - " train_weights, model_state_dict_list = [], [] \n", - " for input_ in inputs:\n", - " train_weights.append(input_.train_dataset_length / train_datasize)\n", - " model_state_dict_list.append(input_.model.state_dict())\n", - "\n", - " avg_model_dict = weighted_average(model_state_dict_list, train_weights)\n", - " self.model.load_state_dict(avg_model_dict)\n", - "\n", - " self.next(self.internal_loop)\n", - "\n", - " @aggregator\n", - " def internal_loop(self):\n", - " \"\"\"\n", - " Check if training is finished for `self.n_rounds`\n", - " if finished move to end step. Otherwise, go back to start\n", - " step for next round of training.\n", - " \"\"\"\n", - " if self.round_number < self.n_rounds:\n", - " self.round_number += 1\n", - " self.next(self.start)\n", - " else:\n", - " self.next(self.end)\n", - "\n", - " @aggregator\n", - " def end(self):\n", - " \"\"\"\n", - " This is the 'end' step.\n", - " \"\"\"\n", - " self.round_number += 1\n", - " print('This is end of the flow')" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Setup Federation\n", - "\n", - "In this step we define entities necessary to run the flow and create a function which returns dataset as private attributes of collaborator. As described in [quickstart](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/Workflow_Interface_101_MNIST.ipynb) we define entities necessary for the flow." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "num_collaborators = 30\n", - "\n", - "# Setup aggregator\n", - "aggregator = Aggregator()\n", - "\n", - "# Setup collaborators with private attributes\n", - "collaborator_names = [f\"col{i}\" for i in range(num_collaborators)]\n", - "\n", - "synthetic_federated_dataset = SyntheticFederatedDataset(\n", - " batch_size=batch_size, num_classes=10, num_collaborators=len(collaborator_names), seed=RANDOM_SEED)\n", - "\n", - "def callable_to_initialize_collaborator_private_attributes(index):\n", - " return synthetic_federated_dataset.split(index)\n", - "\n", - "collaborators = []\n", - "for idx, collaborator_name in enumerate(collaborator_names):\n", - " collaborators.append(\n", - " Collaborator(\n", - " name=collaborator_name, num_cpus=0.0, num_gpus=0.0,\n", - " private_attributes_callable=callable_to_initialize_collaborator_private_attributes,\n", - " index=idx\n", - " )\n", - " )\n", - "\n", - "local_runtime = LocalRuntime(\n", - " aggregator=aggregator, collaborators=collaborators, backend=\"single_process\")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We define `loss_and_acc` dictionary to store the test results of our experiment." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "loss_and_acc = {\n", - " \"FedProx\": {\n", - " \"Train Loss\": [], \"Test Accuracy\": []\n", - " },\n", - " \"FedAvg\": {\n", - " \"Train Loss\": [], \"Test Accuracy\": []\n", - " }\n", - "}" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Data Distribution\n", - "\n", - "Now that our Federation is setup and actors (Aggregator & Collaborators) are initialized, let us take a moment to analyze the *Synthetic non-IID dataset*. We check how the targets for individual collaborators are distributed across each of the classes by computing and plotting the heat-map distribution." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import seaborn as sns\n", - "from matplotlib.colors import LogNorm" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "targets_for_collaborators = []\n", - "\n", - "for idx, collab in enumerate(collaborators):\n", - " # Train, and Test dataset is divided into 9:1 ratio\n", - " _, train_y = callable_to_initialize_collaborator_private_attributes(idx)[\"train_loader\"].dataset[:]\n", - " _, test_y = callable_to_initialize_collaborator_private_attributes(idx)[\"test_loader\"].dataset[:]\n", - " # Append train, and test into 1 tensor array\n", - " y = pt.cat((train_y, test_y))\n", - " targets = np.argmax(y.numpy(), axis = 1)\n", - " # Count number of samples for each class\n", - " frequency = np.zeros(10, dtype=np.int32)\n", - " for i, item in enumerate(targets):\n", - " frequency[item] += 1\n", - " targets_for_collaborators.append(frequency)\n", - "\n", - "result_arr = np.array(targets_for_collaborators).T.tolist()\n", - "fig, ax = plt.subplots(figsize=(20, 5))\n", - "ax = sns.heatmap(result_arr, annot=True, fmt=\"d\", annot_kws={\"fontsize\": 7}, ax=ax, norm=LogNorm(), cbar=False)\n", - "ax.set_title('Distribution of Classes in Dataset across Collaborators', fontsize=12)\n", - "ax.set_xlabel('Collaborator ID', fontsize=10)\n", - "ax.set_ylabel('Classes (0 - 9)', fontsize=10)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# FedProx\n", - "\n", - "Now that we have flow and runtime defined, let's define our parameters and run the experiment with FedProxOptimizer (mu > 0)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Randomly select `n_selected_collaborators` collaborators\n", - "# Must be less than total collaborators\n", - "n_selected_collaborators = 10\n", - "n_epochs = 100\n", - "learning_rate = 0.01\n", - "weight_decay = 0.001\n", - "local_epoch = 20\n", - "\n", - "# Set `mu` to `1.0` for FedProx\n", - "mu = 1.0\n", - "\n", - "flflow = FedProxFlow(n_selected_collaborators=n_selected_collaborators, n_rounds=n_epochs, checkpoint=False)\n", - "flflow.runtime = local_runtime\n", - "\n", - "flflow.run()\n", - "loss_and_acc[\"FedProx\"][\"Train Loss\"] = flflow.loss_and_acc[\"Train Loss\"][:]\n", - "loss_and_acc[\"FedProx\"][\"Test Accuracy\"] = flflow.loss_and_acc[\"Test Accuracy\"][:]" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# FedAvg\n", - "\n", - "Now that we have obtained FedProx results, let's define the parameters for FedAvg and run experiment. Note that for comparison we only change the parameter mu to 0.0 (i.e. FedProxOptimizer with mu = 0.0)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "mu = 0.0\n", - "\n", - "flflow = FedProxFlow(n_selected_collaborators=n_selected_collaborators, n_rounds=n_epochs, checkpoint=False)\n", - "flflow.runtime = local_runtime\n", - "\n", - "flflow.run()\n", - "loss_and_acc[\"FedAvg\"][\"Train Loss\"] = flflow.loss_and_acc[\"Train Loss\"][:]\n", - "loss_and_acc[\"FedAvg\"][\"Test Accuracy\"] = flflow.loss_and_acc[\"Test Accuracy\"][:]" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Compare Results\n", - "\n", - "Now that we have obtained results for both the optimizers available we conclude the tutorial by comparing the Aggregated Training Loss and Aggregated Test Accuracy. Reference: Appendix C.3.2, Figure 6 of [Federated Optimization in Heterogeneous Networks](https://arxiv.org/pdf/1812.06127.pdf) for Synthetic (0,0) dataset." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(24, 6))\n", - "fig.subplots_adjust(hspace=0.4, top=0.8)\n", - "\n", - "fedprox_loss = loss_and_acc[\"FedProx\"][\"Train Loss\"]\n", - "fedavg_loss = loss_and_acc[\"FedAvg\"][\"Train Loss\"]\n", - "ax1.plot(fedprox_loss,'gv-', label='FedProx (mu=1.0)')\n", - "ax1.plot(fedavg_loss,'rs-', label='FedAvg (mu=0.0)')\n", - "ax1.legend()\n", - "ax1.minorticks_on()\n", - "ax1.grid(which='major',linestyle='-',color='0.5')\n", - "ax1.grid(which='minor',linestyle='--',color='0.25')\n", - "ax1.set_title('Train Loss')\n", - "ax1.set_xlabel('Training Round')\n", - "ax1.set_ylabel('Training Loss')\n", - "\n", - "fedprox_accuracy = loss_and_acc[\"FedProx\"][\"Test Accuracy\"]\n", - "fedavg_accuracy = loss_and_acc[\"FedAvg\"][\"Test Accuracy\"]\n", - "ax2.plot(fedprox_accuracy,'gv-', label='FedProx (mu=1.0)')\n", - "ax2.plot(fedavg_accuracy, 'rs-', label='FedAvg (mu=0.0)')\n", - "ax2.legend()\n", - "ax2.minorticks_on()\n", - "ax2.grid(which='major',linestyle='-',color='0.5')\n", - "ax2.grid(which='minor',linestyle='--',color='0.25')\n", - "ax2.set_title('Test Accuracy')\n", - "ax2.set_xlabel('Training Round')\n", - "ax2.set_ylabel('Test Accuracy')\n", - "\n", - "fig.suptitle('Comparison of FedProx (mu > 0) and FedAvg (mu = 0)', fontsize='18')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "env_fedprox_example", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.19" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "c96b31a6dd4c6365f3cc206f3a3aedb434a4eb5a8aa6c7dc735a6d54c4b635a9" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/openfl-tutorials/experimental/workflow/402_MNIST_Aggregator_Validation_Ray_Watermarking.ipynb b/openfl-tutorials/experimental/workflow/402_MNIST_Aggregator_Validation_Ray_Watermarking.ipynb index 5e1dd0f6c9..5e7d0cbb09 100644 --- a/openfl-tutorials/experimental/workflow/402_MNIST_Aggregator_Validation_Ray_Watermarking.ipynb +++ b/openfl-tutorials/experimental/workflow/402_MNIST_Aggregator_Validation_Ray_Watermarking.ipynb @@ -6,9 +6,9 @@ "id": "dc13070c", "metadata": {}, "source": [ - "# Workflow Interface 401: Aggregator validation with a watermark dataset using Ray\n", + "# Workflow Interface 402: Aggregator validation with a watermark dataset using Ray\n", "\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/401_MNIST_Aggregator_Validation_Ray_Watermarking.ipynb)" + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/workflow/402_MNIST_Aggregator_Validation_Ray_Watermarking.ipynb)" ] }, { @@ -20,9 +20,9 @@ "This tutorial is a merge of some of the previous notebooks.\n", "\n", "The purpose of this OpenFL Workflow Interface tutorial is to showcase the following:\n", - "- Performing validation on the aggregator (see the [102](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/102_Aggregator_Validation.ipynb) notebook)\n", - "- Training with watermarking of DL Model in Federated Learning (see the [301](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/301_MNIST_Watermarking.ipynb) notebook)\n", - "- Utilizing multiple GPUs for concurrent model training using the Ray Backend in LocalRuntime (see the [201](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/201_Exclusive_GPUs_with_Ray.ipynb) notebook).\n", + "- Performing validation on the aggregator (see the [102](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/workflow/102_Aggregator_Validation.ipynb) notebook)\n", + "- Training with watermarking of DL Model in Federated Learning (see the [301](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/workflow/301_MNIST_Watermarking.ipynb) notebook)\n", + "- Utilizing multiple GPUs for concurrent model training using the Ray Backend in LocalRuntime (see the [201](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/workflow/201_Exclusive_GPUs_with_Ray.ipynb) notebook).\n", "\n", "Watermarking enables the Model owner to assert ownership rights and detect stolen model instances." ] @@ -443,7 +443,7 @@ "id": "c917b085", "metadata": {}, "source": [ - "Let us now define the Workflow for Watermark embedding. Here we use the same tasks as the [quickstart](https://github.com/psfoley/openfl/blob/experimental-workflow-interface/openfl-tutorials/experimental/MNIST.ipynb), and define following additional steps for Watermarking:\n", + "Let us now define the Workflow for Watermark embedding. Here we use the same tasks as the [quickstart](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/workflow/101_MNIST.ipynb), and define following additional steps for Watermarking:\n", "- PRE-TRAIN (watermark_retrain): At the start (once), initial model is trained on Watermark dataset for a specified number of epochs.\n", "- RE-TRAIN (watermark_pretrain): Every training round, Aggregated model is retrained on Watermark dataset until a desired acc threshold is reached or max number of retrain rounds are expired.\n", "\n", @@ -713,7 +713,7 @@ "source": [ "In the `AggregatorValCollaboratorGPUWatermarking` definition above, you will notice that certain attributes of the flow were not initialized, namely the `watermark_data_loader` for Aggregator and `train_loader`, `test_loader` for the Collaborators. \n", "\n", - "- Collaborator attributes are created in the same manner as described in [quickstart](https://github.com/psfoley/openfl/blob/experimental-workflow-interface/openfl-tutorials/experimental/101_MNIST.ipynb).\n", + "- Collaborator attributes are created in the same manner as described in [quickstart](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/workflow/101_MNIST.ipynb).\n", "\n", "- `watermark_data_loader` is created as a **private attribute** of the Aggregator and it is exposed only via the runtime. This property enables the Watermark dataset to be hidden from the collaborators as Aggregator private attributes are filtered before the state is transferred to Collaborators (in the same manner as Collaborator private attributes are hidden from Aggregator).\n", "\n", diff --git a/openfl-tutorials/experimental/workflow/401_Federated_FedProx_PyTorch_MNIST_Workflow_Tutorial.ipynb b/openfl-tutorials/experimental/workflow/403_Federated_FedProx_PyTorch_MNIST_Workflow_Tutorial.ipynb similarity index 100% rename from openfl-tutorials/experimental/workflow/401_Federated_FedProx_PyTorch_MNIST_Workflow_Tutorial.ipynb rename to openfl-tutorials/experimental/workflow/403_Federated_FedProx_PyTorch_MNIST_Workflow_Tutorial.ipynb diff --git a/openfl-tutorials/experimental/workflow/LLM/neuralchat/Workflow_Interface_NeuralChat.ipynb b/openfl-tutorials/experimental/workflow/LLM/neuralchat/Workflow_Interface_NeuralChat.ipynb index 749ebbc8bb..860392bf1b 100644 --- a/openfl-tutorials/experimental/workflow/LLM/neuralchat/Workflow_Interface_NeuralChat.ipynb +++ b/openfl-tutorials/experimental/workflow/LLM/neuralchat/Workflow_Interface_NeuralChat.ipynb @@ -7,7 +7,7 @@ "source": [ "# Workflow Interface\n", "## Fine-tuning neural-chat-7b-v3 using Intel(R) Extension for Transformers and OpenFL\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/LLM/neuralchat/Workflow_Interface_NeuralChat.ipynb)" + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/workflow/LLM/neuralchat/Workflow_Interface_NeuralChat.ipynb)" ] }, { @@ -15,7 +15,7 @@ "id": "bd059520", "metadata": {}, "source": [ - "In this tutorial, we build on the ideas from the [first](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/101_MNIST.ipynb) quick start notebook, and demonstrate how to fine-tune a Large Language Model (LLM) in a federated learning workflow. \n", + "In this tutorial, we build on the ideas from the [first](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/workflow/101_MNIST.ipynb) quick start notebook, and demonstrate how to fine-tune a Large Language Model (LLM) in a federated learning workflow. \n", "\n", "We will fine-tune **Intel's [neural-chat-7b](https://huggingface.co/Intel/neural-chat-7b-v1)** model on the [MedQuAD](https://github.com/abachaa/MedQuAD) dataset, an open-source medical question-answer pair dataset collated from 12 NIH websites. To do this, we will leverage the **[Intel(R) Extension for Transformers](https://github.com/intel/intel-extension-for-transformers)**, which extends th [Hugging Face Transformers](https://github.com/huggingface/transformers) library with added features for optimal performance on Intel hardware.." ] @@ -619,7 +619,7 @@ "metadata": {}, "source": [ "# Congratulations!\n", - "Now that you've completed this notebook, check out our [other tutorials](https://github.com/securefederatedai/openfl/tree/886704508b8b3b0638372003d72e0bcf7f2e7114/openfl-tutorials/experimental), including:\n", + "Now that you've completed this notebook, check out our [other tutorials](https://github.com/securefederatedai/openfl/tree/develop/openfl-tutorials/experimental/workflow), including:\n", "\n", "- Using the LocalRuntime Ray Backend for dedicated GPU access\n", "- Vertical Federated Learning\n", diff --git a/openfl-tutorials/experimental/workflow/Vertical_FL/TwoPartyWorkspaceCreation.ipynb b/openfl-tutorials/experimental/workflow/Vertical_FL/TwoPartyWorkspaceCreation.ipynb index 733985d4aa..1395a5095a 100644 --- a/openfl-tutorials/experimental/workflow/Vertical_FL/TwoPartyWorkspaceCreation.ipynb +++ b/openfl-tutorials/experimental/workflow/Vertical_FL/TwoPartyWorkspaceCreation.ipynb @@ -24,7 +24,7 @@ "4. User can utilize the experimental `fx` commands to deploy and run the federation seamlessly\n", "\n", "\n", - "The methodology is described using an existing [OpenFL Two Party VFL Tutorial](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/Vertical_FL/Workflow_Interface_VFL_Two_Party.ipynb). Let's get started !" + "The methodology is described using an existing [OpenFL Two Party VFL Tutorial](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/workflow/Vertical_FL/TwoParty.ipynb). Let's get started !" ] }, { diff --git a/openfl-tutorials/experimental/workflow/Vision_Transformer/Workflow_Interface_102_Vision_Transformer.ipynb b/openfl-tutorials/experimental/workflow/Vision_Transformer/Workflow_Interface_102_Vision_Transformer.ipynb index fcb174e39b..13362771f9 100644 --- a/openfl-tutorials/experimental/workflow/Vision_Transformer/Workflow_Interface_102_Vision_Transformer.ipynb +++ b/openfl-tutorials/experimental/workflow/Vision_Transformer/Workflow_Interface_102_Vision_Transformer.ipynb @@ -7,7 +7,7 @@ "source": [ "# Workflow Interface 102: \n", "# Vision Transformer for Image Classification using MedMNIST\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/Vision_Transformer/102_Vision_Transformer.ipynb)" + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/experimental/workflow/Vision_Transformer/Workflow_Interface_102_Vision_Transformer.ipynb)" ] }, { @@ -24,7 +24,7 @@ "\n", "In contrast to tradition convolutional neural networks which focus on capturing local image features within a spatial window using a sliding filter, the self-attention mechanism enables vision transformers to capture global relationships between image patches. \n", "\n", - "In this tutorial, you will learn how to set up a horizontal federated learning workflow using the OpenFL Experimental Workflow Interface to train a vision transformer to classify images from the MedMNIST dataset. This notebook expands on the use case from the [first](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/101_MNIST.ipynb) quick start notebook. Its objective is to demonstrate how a user can modify the workflow interface for different use cases" + "In this tutorial, you will learn how to set up a horizontal federated learning workflow using the OpenFL Experimental Workflow Interface to train a vision transformer to classify images from the MedMNIST dataset. This notebook expands on the use case from the [first](https://github.com/securefederatedai/openfl/blob/develop/openfl-tutorials/experimental/workflow/101_MNIST.ipynb) quick start notebook. Its objective is to demonstrate how a user can modify the workflow interface for different use cases" ] }, {