diff --git a/openfl-tutorials/experimental/Workflow_Interface_401_FedProx_with_Synthetic_nonIID.ipynb b/openfl-tutorials/experimental/Workflow_Interface_401_FedProx_with_Synthetic_nonIID.ipynb new file mode 100644 index 0000000000..d6359cf96d --- /dev/null +++ b/openfl-tutorials/experimental/Workflow_Interface_401_FedProx_with_Synthetic_nonIID.ipynb @@ -0,0 +1,814 @@ +{ + "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/intel/openfl.git\n", + "!pip install -r https://raw.githubusercontent.com/intel/openfl/develop/openfl-tutorials/experimental/requirements_workflow_interface.txt\n", + "!pip install matplotlib\n", + "!pip install seaborn\n", + "!pip install torch torchvision\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, collaborators):\n", + " for i, collaborator in enumerate(collaborators):\n", + " collaborator.private_attributes = {\n", + " \"train_loader\":\n", + " data.DataLoader(\n", + " data.TensorDataset(\n", + " pt.from_numpy(self.X_train_all[i]),\n", + " pt.from_numpy(self.y_train_all[i])\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[i]),\n", + " pt.from_numpy(self.y_valid_all[i])\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": { + "image.png": { + "image/png": "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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/intel/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", + "\n", + "![image.png](attachment:image.png)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from openfl.experimental.interface import FLSpec, Aggregator, Collaborator\n", + "from openfl.experimental.runtime import LocalRuntime\n", + "from openfl.experimental.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 assign the synthetic dataset as private atributes of collaborators." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "num_collaborators = 30\n", + "\n", + "# Setup aggregator\n", + "aggregator = Aggregator()\n", + "aggregator.private_attributes = {}\n", + "\n", + "# Setup collaborators with private attributes\n", + "collaborator_names = [f\"col{i}\" for i in range(num_collaborators)]\n", + "\n", + "collaborators = [Collaborator(name=name) for name in collaborator_names]\n", + "\n", + "synthetic_federated_dataset = SyntheticFederatedDataset(\n", + " batch_size=batch_size, num_classes=10, num_collaborators=len(collaborators), seed=RANDOM_SEED)\n", + "synthetic_federated_dataset.split(collaborators)\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 collab in collaborators:\n", + " # Train, and Test dataset is divided into 9:1 ratio\n", + " _, train_y = collab.private_attributes[\"train_loader\"].dataset[:]\n", + " _, test_y = collab.private_attributes[\"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.16" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "c96b31a6dd4c6365f3cc206f3a3aedb434a4eb5a8aa6c7dc735a6d54c4b635a9" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/openfl/experimental/interface/keras/__init__.py b/openfl/experimental/interface/keras/__init__.py new file mode 100644 index 0000000000..1d7d84eb7f --- /dev/null +++ b/openfl/experimental/interface/keras/__init__.py @@ -0,0 +1,7 @@ +# Copyright (C) 2020-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +"""openfl.experimental.interface.keras package.""" + +from .aggregation_functions import WeightedAverage + +__all__ = ["WeightedAverage", ] diff --git a/openfl/experimental/interface/keras/aggregation_functions/__init__.py b/openfl/experimental/interface/keras/aggregation_functions/__init__.py new file mode 100644 index 0000000000..94708487bc --- /dev/null +++ b/openfl/experimental/interface/keras/aggregation_functions/__init__.py @@ -0,0 +1,7 @@ +# Copyright (C) 2020-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +"""openfl.experimenal.interface.keras.aggregation_functions package.""" + +from .weighted_average import WeightedAverage + +__all__ = ["WeightedAverage", ] diff --git a/openfl/experimental/interface/keras/aggregation_functions/weighted_average.py b/openfl/experimental/interface/keras/aggregation_functions/weighted_average.py new file mode 100644 index 0000000000..326e57aece --- /dev/null +++ b/openfl/experimental/interface/keras/aggregation_functions/weighted_average.py @@ -0,0 +1,13 @@ +# Copyright (C) 2020-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +"""openfl.experimental.interface.keras.aggregation_functions.weighted_average package.""" + + +class WeightedAverage: + """Weighted average aggregation for keras or tensorflow.""" + + def __init__(self) -> None: + """ + WeightedAverage class for Keras or Tensorflow library. + """ + raise NotImplementedError("WeightedAverage for keras will be implemented in the future.") diff --git a/openfl/experimental/interface/torch/__init__.py b/openfl/experimental/interface/torch/__init__.py new file mode 100644 index 0000000000..969f47b43a --- /dev/null +++ b/openfl/experimental/interface/torch/__init__.py @@ -0,0 +1,7 @@ +# Copyright (C) 2020-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +"""openfl.experimental.interface.torch package.""" + +from .aggregation_functions import WeightedAverage + +__all__ = ["WeightedAverage", ] diff --git a/openfl/experimental/interface/torch/aggregation_functions/__init__.py b/openfl/experimental/interface/torch/aggregation_functions/__init__.py new file mode 100644 index 0000000000..2afa83b219 --- /dev/null +++ b/openfl/experimental/interface/torch/aggregation_functions/__init__.py @@ -0,0 +1,7 @@ +# Copyright (C) 2020-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +"""openfl.experimenal.interface.torch.aggregation_functions package.""" + +from .weighted_average import WeightedAverage + +__all__ = ["WeightedAverage", ] diff --git a/openfl/experimental/interface/torch/aggregation_functions/weighted_average.py b/openfl/experimental/interface/torch/aggregation_functions/weighted_average.py new file mode 100644 index 0000000000..a91cadfa0d --- /dev/null +++ b/openfl/experimental/interface/torch/aggregation_functions/weighted_average.py @@ -0,0 +1,77 @@ +# Copyright (C) 2020-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +"""openfl.experimental.interface.torch.aggregation_functions.weighted_average package.""" + +import collections +import numpy as np +import torch as pt + + +def weighted_average(tensors, weights): + """Compute weighted average.""" + return np.average(tensors, weights=weights, axis=0) + + +class WeightedAverage: + """Weighted average aggregation.""" + + def __call__(self, objects_list, weights_list) -> np.ndarray: + """ + Compute weighted average of models, optimizers, loss, or accuracy metrics. + For taking weighted average of optimizer do the following steps: + 1. Call "_get_optimizer_state" (openfl.federated.task.runner_pt._get_optimizer_state) + pass optimizer to it, to take optimizer state dictionary. + 2. Pass optimizer state dictionaries list to here. + 3. To set the weighted average optimizer state dictionary back to optimizer, + call "_set_optimizer_state" (openfl.federated.task.runner_pt._set_optimizer_state) + and pass optimizer, device, and optimizer dictionary received in step 2. + + Args: + objects_list: List of objects for which weighted average is to be computed. + - List of Model state dictionaries , or + - List of Metrics (Loss or accuracy), or + - List of optimizer state dictionaries (following steps need to be performed) + 1. Obtain optimizer state dictionary by invoking "_get_optimizer_state" + (openfl.federated.task.runner_pt._get_optimizer_state). + 2. Create a list of optimizer state dictionary obtained in step - 1 + Invoke WeightedAverage on this list. + 3. Invoke "_set_optimizer_state" to set weighted average of optimizer + state back to optimizer (openfl.federated.task.runner_pt._set_optimizer_state). + weights_list: Weight for each element in the list. + + Returns: + dict: For model or optimizer + float: For Loss or Accuracy metrics + """ + # Check the type of first element of tensors list + if type(objects_list[0]) in (dict, collections.OrderedDict): + optimizer = False + # If __opt_state_needed found then optimizer state dictionary is passed + if "__opt_state_needed" in objects_list[0]: + optimizer = True + # Remove __opt_state_needed from all state dictionary in list, and + # check if weightedaverage of optimizer can be taken. + for tensor in objects_list: + error_msg = "Optimizer is stateless, WeightedAverage cannot be taken" + assert tensor.pop("__opt_state_needed") == "true", error_msg + + tmp_list = [] + # # Take keys in order to rebuild the state dictionary taking keys back up + for tensor in objects_list: + # Append values of each state dictionary in list + # If type(value) is Tensor then it needs to be detached + tmp_list.append(np.array([value.detach() if isinstance(value, pt.Tensor) else value + for value in tensor.values()], dtype=object)) + # Take weighted average of list of arrays + # new_params passed is weighted average of each array in tmp_list + new_params = weighted_average(tmp_list, weights_list) + new_state = {} + # Take weighted average parameters and building a dictionary + for i, k in enumerate(objects_list[0].keys()): + if optimizer: + new_state[k] = new_params[i] + else: + new_state[k] = pt.from_numpy(new_params[i].numpy()) + return new_state + else: + return weighted_average(objects_list, weights_list) diff --git a/tests/openfl/experimental/__init__.py b/tests/openfl/experimental/__init__.py new file mode 100644 index 0000000000..5e9b6e2622 --- /dev/null +++ b/tests/openfl/experimental/__init__.py @@ -0,0 +1,3 @@ +# Copyright (C) 2020-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +"""tests.openfl.experimental package.""" diff --git a/tests/openfl/experimental/optimizer.pth b/tests/openfl/experimental/optimizer.pth new file mode 100644 index 0000000000..455ca9bb56 Binary files /dev/null and b/tests/openfl/experimental/optimizer.pth differ diff --git a/tests/openfl/experimental/test_weighted_average.py b/tests/openfl/experimental/test_weighted_average.py new file mode 100644 index 0000000000..a8cf0d5894 --- /dev/null +++ b/tests/openfl/experimental/test_weighted_average.py @@ -0,0 +1,125 @@ +# Copyright (C) 2020-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +"""Test for openfl.experimenal.interface.torch.aggregation_function module.""" + +from openfl.experimental.interface.torch import WeightedAverage +from openfl.federated.task.runner_pt import _get_optimizer_state + +import torch as pt +import numpy as np + +import pickle +from typing import List, Dict, Any +from copy import deepcopy + + +class Net(pt.nn.Module): + """ + Returns a simple model for test case + """ + def __init__(self) -> None: + super(Net, self).__init__() + self.linear1 = pt.nn.Linear(10, 20) + self.linear2 = pt.nn.Linear(20, 1) + + def forward(self, x): + x = self.linear1(x) + x = self.linear2(x) + return x + + +def get_optimizer() -> Any: + """ + Get optimizer + """ + with open("optimizer.pth", "rb") as f: + return pickle.load(f) + + +def take_model_weighted_average(models_state_dicts_list: List[Dict], + weights_list: List[int]) -> Dict: + """ + Take models weighted average manually + """ + tmp_list = [] + for model_state_dict in models_state_dicts_list: + tmp_list.append(np.array([value.detach() for value in model_state_dict.values()], + dtype=object)) + + new_params = np.average(tmp_list, weights=weights_list, axis=0) + + new_state = {} + for i, k in enumerate(models_state_dicts_list[0].keys()): + new_state[k] = pt.from_numpy(new_params[i].numpy()) + return new_state + + +def take_optimizer_weighted_average(optimizer_state_dicts_list: List[Dict], + weights_list: List[int]) -> Dict: + """ + Take models weighted average manually + """ + for optimizer_state_dict in optimizer_state_dicts_list: + assert optimizer_state_dict.pop("__opt_state_needed") == "true" + + tmp_list = [] + for optimizer_state_dict in optimizer_state_dicts_list: + tmp_list.append(np.array(list(optimizer_state_dict.values()), dtype=object)) + + new_params = np.average(tmp_list, weights=weights_list, axis=0) + + new_state = {} + for i, k in enumerate(optimizer_state_dicts_list[0].keys()): + new_state[k] = new_params[i] + return new_state + + +def test_list_weighted_average(): + """ + Test list weighted average + """ + float_element_list = [0.4, 0.21, 0.1, 0.03] + weights_list = [0.1, 0.25, 0.325, 0.325] + + weighted_average = WeightedAverage() + + averaged_loss_using_class = weighted_average(deepcopy(float_element_list), + weights_list) + averaged_loss_manually = np.average(deepcopy(float_element_list), + weights=weights_list, axis=0) + + assert np.all(averaged_loss_using_class) == np.all(averaged_loss_manually) + + +def test_model_weighted_average(): + """ + Test model weighted average + """ + model_state_dicts_list = [Net().state_dict() for _ in range(4)] + weights_list = [0.1, 0.25, 0.325, 0.325] + + weighted_average = WeightedAverage() + + averaged_model_using_class = weighted_average(deepcopy(model_state_dicts_list), + weights_list) + averaged_model_manually = take_model_weighted_average(deepcopy(model_state_dicts_list), + weights_list) + + assert all(averaged_model_using_class) == all(averaged_model_manually) + + +def test_optimizer_weighted_average(): + """ + Test optimizers weighted average + """ + optimizer_state_dicts_list = [_get_optimizer_state(get_optimizer()) for _ in range(4)] + weights_list = [0.1, 0.25, 0.325, 0.325] + + weighted_average = WeightedAverage() + + averaged_optimizer_using_class = weighted_average(deepcopy(optimizer_state_dicts_list), + weights_list) + averaged_optimizer_manually = take_optimizer_weighted_average( + deepcopy(optimizer_state_dicts_list), weights_list) + + assert all(averaged_optimizer_using_class) == all(averaged_optimizer_manually)