From e66feaf284ac5d132d4aeb0b1a049fd2d3b0983a Mon Sep 17 00:00:00 2001 From: Beverly Klemme Date: Thu, 27 Apr 2023 14:47:26 -0700 Subject: [PATCH 1/9] created new ineractive_api dir to hold pytorch fedprox mnist example --- .../PyTorch_FedProx_MNIST/README.md | 70 ++ .../director/director_config.yaml | 5 + .../director/start_director.sh | 4 + .../envoy/envoy_config.yaml | 11 + .../envoy/medmnist_shard_descriptor.py | 129 ++++ .../envoy/requirements.txt | 3 + .../envoy/start_envoy.sh | 6 + .../workspace/PyTorch_Kvasir_UNet.ipynb | 610 ++++++++++++++++++ .../PyTorch_FedProx_MNIST/workspace/layers.py | 103 +++ 9 files changed, 941 insertions(+) create mode 100644 openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/README.md create mode 100644 openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/director/director_config.yaml create mode 100755 openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/director/start_director.sh create mode 100644 openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/envoy/envoy_config.yaml create mode 100644 openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/envoy/medmnist_shard_descriptor.py create mode 100644 openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/envoy/requirements.txt create mode 100755 openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/envoy/start_envoy.sh create mode 100644 openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/PyTorch_Kvasir_UNet.ipynb create mode 100644 openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/layers.py diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/README.md b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/README.md new file mode 100644 index 0000000000..2b1b03bd25 --- /dev/null +++ b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/README.md @@ -0,0 +1,70 @@ +# PyTorch_Kvasir_UNet + +## **How to run this tutorial (without TLC and locally as a simulation):** +
+ +### 0. If you haven't done so already, create a virtual environment, install OpenFL, and upgrade pip: + - For help with this step, visit the "Install the Package" section of the [OpenFL installation instructions](https://openfl.readthedocs.io/en/latest/install.html#install-the-package). + +
+ +### 1. Split terminal into 3 (1 terminal for the director, 1 for the envoy, and 1 for the experiment) + +
+ +### 2. Do the following in each terminal: + - Activate the virtual environment from step 0: + + ```sh + source venv/bin/activate + ``` + - If you are in a network environment with a proxy, ensure proxy environment variables are set in each of your terminals. + - Navigate to the tutorial: + + ```sh + cd openfl/openfl-tutorials/interactive_api/PyTorch_Kvasir_UNet + ``` + +
+ +### 3. In the first terminal, run the director: + +```sh +cd director +./start_director.sh +``` + +
+ +### 4. In the second terminal, install requirements and run the envoy: + +```sh +cd envoy +pip install -r sd_requirements.txt +``` + - If you have GPUs: +```sh +./start_envoy.sh env_one envoy_config.yaml +``` + - For no GPUs, use: +```sh +./start_envoy.sh env_one envoy_config_no_gpu.yaml +``` + + +Optional: Run a second envoy in an additional terminal: + - Ensure step 2 is complete for this terminal as well. + - Repeat step 4 instructions above but change "env_one" name to "env_two" (or another name of your choice). + +
+ +### 5. Now that your director and envoy terminals are set up, run the Jupyter Notebook in your experiment terminal: + +```sh +cd workspace +jupyter lab PyTorch_Kvasir_UNet.ipynb +``` +- A Jupyter Server URL will appear in your terminal. In your browser, proceed to that link. Once the webpage loads, click on the PyTorch_Kvasir_UNet.ipynb file. +- To run the experiment, select the icon that looks like two triangles to "Restart Kernel and Run All Cells". +- You will notice activity in your terminals as the experiment runs, and when the experiment is finished the director terminal will display a message that the experiment has finished successfully. + diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/director/director_config.yaml b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/director/director_config.yaml new file mode 100644 index 0000000000..f7d3847170 --- /dev/null +++ b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/director/director_config.yaml @@ -0,0 +1,5 @@ +settings: + listen_host: localhost + listen_port: 50051 + sample_shape: ['28', '28', '3'] + target_shape: ['1','1'] diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/director/start_director.sh b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/director/start_director.sh new file mode 100755 index 0000000000..5806a6cc0a --- /dev/null +++ b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/director/start_director.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -e + +fx director start --disable-tls -c director_config.yaml \ No newline at end of file diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/envoy/envoy_config.yaml b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/envoy/envoy_config.yaml new file mode 100644 index 0000000000..05ee5cec4c --- /dev/null +++ b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/envoy/envoy_config.yaml @@ -0,0 +1,11 @@ +params: + cuda_devices: [] + +optional_plugin_components: {} + +shard_descriptor: + template: medmnist_shard_descriptor.MedMNISTShardDescriptor + params: + rank_worldsize: 1, 1 + datapath: data/. + dataname: bloodmnist diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/envoy/medmnist_shard_descriptor.py b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/envoy/medmnist_shard_descriptor.py new file mode 100644 index 0000000000..d5e639fed4 --- /dev/null +++ b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/envoy/medmnist_shard_descriptor.py @@ -0,0 +1,129 @@ +# Copyright (C) 2020-2021 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +"""MedMNIST Shard Descriptor.""" + +import logging +import os +from typing import Any, List, Tuple +from medmnist.info import INFO, HOMEPAGE + +import numpy as np + +from openfl.interface.interactive_api.shard_descriptor import ShardDataset +from openfl.interface.interactive_api.shard_descriptor import ShardDescriptor + +logger = logging.getLogger(__name__) + + +class MedMNISTShardDataset(ShardDataset): + """MedMNIST Shard dataset class.""" + + def __init__(self, x, y, data_type: str = 'train', rank: int = 1, worldsize: int = 1) -> None: + """Initialize MedMNISTDataset.""" + self.data_type = data_type + self.rank = rank + self.worldsize = worldsize + self.x = x[self.rank - 1::self.worldsize] + self.y = y[self.rank - 1::self.worldsize] + + def __getitem__(self, index: int) -> Tuple[Any, Any]: + """Return an item by the index.""" + return self.x[index], self.y[index] + + def __len__(self) -> int: + """Return the len of the dataset.""" + return len(self.x) + + +class MedMNISTShardDescriptor(ShardDescriptor): + """MedMNIST Shard descriptor class.""" + + def __init__( + self, + rank_worldsize: str = '1, 1', + datapath: str = '', + dataname: str = 'bloodmnist', + **kwargs + ) -> None: + """Initialize MedMNISTShardDescriptor.""" + self.rank, self.worldsize = tuple(int(num) for num in rank_worldsize.split(',')) + + self.datapath = datapath + self.dataset_name = dataname + self.info = INFO[self.dataset_name] + + (x_train, y_train), (x_test, y_test) = self.load_data() + self.data_by_type = { + 'train': (x_train, y_train), + 'val': (x_test, y_test) + } + + def get_shard_dataset_types(self) -> List[str]: + """Get available shard dataset types.""" + return list(self.data_by_type) + + def get_dataset(self, dataset_type='train') -> MedMNISTShardDataset: + """Return a shard dataset by type.""" + if dataset_type not in self.data_by_type: + raise Exception(f'Wrong dataset type: {dataset_type}') + return MedMNISTShardDataset( + *self.data_by_type[dataset_type], + data_type=dataset_type, + rank=self.rank, + worldsize=self.worldsize + ) + + @property + def sample_shape(self) -> List[str]: + """Return the sample shape info.""" + return ['28', '28', '3'] + + @property + def target_shape(self) -> List[str]: + """Return the target shape info.""" + return ['1', '1'] + + @property + def dataset_description(self) -> str: + """Return the dataset description.""" + return (f'MedMNIST dataset, shard number {self.rank}' + f' out of {self.worldsize}') + + @staticmethod + def download_data(datapath: str = 'data/', + dataname: str = 'bloodmnist', + info: dict = {}) -> None: + + logger.info(f"{datapath}\n{dataname}\n{info}") + try: + from torchvision.datasets.utils import download_url + download_url(url=info["url"], + root=datapath, + filename=dataname, + md5=info["MD5"]) + except Exception: + raise RuntimeError('Something went wrong when downloading! ' + + 'Go to the homepage to download manually. ' + + HOMEPAGE) + + def load_data(self) -> Tuple[Tuple[Any, Any], Tuple[Any, Any]]: + """Download prepared dataset.""" + + dataname = self.dataset_name + '.npz' + dataset = os.path.join(self.datapath, dataname) + + if not os.path.isfile(dataset): + logger.info(f"Dataset {dataname} not found at:{self.datapath}.\n\tDownloading...") + MedMNISTShardDescriptor.download_data(self.datapath, dataname, self.info) + logger.info("DONE!") + + data = np.load(dataset) + + x_train = data["train_images"] + x_test = data["test_images"] + + y_train = data["train_labels"] + y_test = data["test_labels"] + logger.info('MedMNIST data was loaded!') + return (x_train, y_train), (x_test, y_test) diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/envoy/requirements.txt b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/envoy/requirements.txt new file mode 100644 index 0000000000..363c0d69f9 --- /dev/null +++ b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/envoy/requirements.txt @@ -0,0 +1,3 @@ +medmnist +setuptools>=65.5.1 # not directly required, pinned by Snyk to avoid a vulnerability +wheel>=0.38.0 # not directly required, pinned by Snyk to avoid a vulnerability diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/envoy/start_envoy.sh b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/envoy/start_envoy.sh new file mode 100755 index 0000000000..cdd84e7fb6 --- /dev/null +++ b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/envoy/start_envoy.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -e +ENVOY_NAME=$1 +ENVOY_CONF=$2 + +fx envoy start -n "$ENVOY_NAME" --disable-tls --envoy-config-path "$ENVOY_CONF" -dh localhost -dp 50051 diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/PyTorch_Kvasir_UNet.ipynb b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/PyTorch_Kvasir_UNet.ipynb new file mode 100644 index 0000000000..ed583e3194 --- /dev/null +++ b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/PyTorch_Kvasir_UNet.ipynb @@ -0,0 +1,610 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "liquid-jacket", + "metadata": {}, + "source": [ + "# Federated Kvasir with Director example" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "alike-sharing", + "metadata": {}, + "outputs": [], + "source": [ + "# Install dependencies if not already installed\n", + "!pip install torchvision==0.8.1" + ] + }, + { + "cell_type": "markdown", + "id": "16986f22", + "metadata": {}, + "source": [ + "# Connect to the Federation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4485ac79", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a federation\n", + "from openfl.interface.interactive_api.federation import Federation\n", + "\n", + "# please use the same identificator that was used in signed certificate\n", + "client_id = 'frontend'\n", + "director_node_fqdn = 'localhost'\n", + "director_port = 50050\n", + "\n", + "# 1) Run with API layer - Director mTLS \n", + "# If the user wants to enable mTLS their must provide CA root chain, and signed key pair to the federation interface\n", + "# cert_chain = 'cert/root_ca.crt'\n", + "# API_certificate = 'cert/frontend.crt'\n", + "# API_private_key = 'cert/frontend.key'\n", + "\n", + "# federation = Federation(\n", + "# client_id=client_id,\n", + "# director_node_fqdn=director_node_fqdn,\n", + "# director_port=director_port,\n", + "# tls=True,\n", + "# cert_chain=cert_chain,\n", + "# api_cert=api_certificate,\n", + "# api_private_key=api_private_key\n", + "# )\n", + "\n", + "# --------------------------------------------------------------------------------------------------------------------\n", + "\n", + "# 2) Run with TLS disabled (trusted environment)\n", + "# Federation can also determine local fqdn automatically\n", + "federation = Federation(\n", + " client_id=client_id,\n", + " director_node_fqdn=director_node_fqdn,\n", + " director_port=director_port,\n", + " tls=False\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e35802d5", + "metadata": {}, + "outputs": [], + "source": [ + "# import time\n", + "# while True:\n", + "# shard_registry = federation.get_shard_registry()\n", + "# print(shard_registry)\n", + "# time.sleep(5)\n", + "shard_registry = federation.get_shard_registry()\n", + "shard_registry" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67ae50de", + "metadata": {}, + "outputs": [], + "source": [ + "federation.target_shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "920216d3", + "metadata": {}, + "outputs": [], + "source": [ + "# First, request a dummy_shard_desc that holds information about the federated dataset \n", + "dummy_shard_desc = federation.get_dummy_shard_descriptor(size=10)\n", + "dummy_shard_dataset = dummy_shard_desc.get_dataset('train')\n", + "sample, target = dummy_shard_dataset[0]\n", + "f\"Sample shape: {sample.shape}, target shape: {target.shape}\"" + ] + }, + { + "cell_type": "markdown", + "id": "obvious-tyler", + "metadata": {}, + "source": [ + "## Creating a FL experiment using Interactive API" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "rubber-address", + "metadata": {}, + "outputs": [], + "source": [ + "from openfl.interface.interactive_api.experiment import TaskInterface, DataInterface, ModelInterface, FLExperiment" + ] + }, + { + "cell_type": "markdown", + "id": "sustainable-public", + "metadata": {}, + "source": [ + "### Register dataset" + ] + }, + { + "cell_type": "markdown", + "id": "unlike-texas", + "metadata": {}, + "source": [ + "We extract User dataset class implementation.\n", + "Is it convinient?\n", + "What if the dataset is not a class?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "64f37dcf", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import PIL\n", + "import numpy as np\n", + "from torch.utils.data import Dataset, DataLoader, SubsetRandomSampler\n", + "from torchvision import transforms as tsf\n", + "\n", + "\n", + "class KvasirShardDataset(Dataset):\n", + " \n", + " def __init__(self, dataset):\n", + " self._dataset = dataset\n", + " \n", + " # Prepare transforms\n", + " self.img_trans = tsf.Compose([\n", + " tsf.ToPILImage(),\n", + " tsf.Resize((332, 332)),\n", + " tsf.ToTensor(),\n", + " tsf.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])])\n", + " self.mask_trans = tsf.Compose([\n", + " tsf.ToPILImage(),\n", + " tsf.Resize((332, 332), interpolation=PIL.Image.NEAREST),\n", + " tsf.ToTensor()])\n", + " \n", + " def __getitem__(self, index):\n", + " img, mask = self._dataset[index]\n", + " img = self.img_trans(img).numpy()\n", + " mask = self.mask_trans(mask).numpy()\n", + " return img, mask\n", + " \n", + " def __len__(self):\n", + " return len(self._dataset)\n", + "\n", + " \n", + "\n", + "# Now you can implement you data loaders using dummy_shard_desc\n", + "class KvasirSD(DataInterface):\n", + "\n", + " def __init__(self, validation_fraction=1/8, **kwargs):\n", + " super().__init__(**kwargs)\n", + " \n", + " self.validation_fraction = validation_fraction\n", + " \n", + " @property\n", + " def shard_descriptor(self):\n", + " return self._shard_descriptor\n", + " \n", + " @shard_descriptor.setter\n", + " def shard_descriptor(self, shard_descriptor):\n", + " \"\"\"\n", + " Describe per-collaborator procedures or sharding.\n", + "\n", + " This method will be called during a collaborator initialization.\n", + " Local shard_descriptor will be set by Envoy.\n", + " \"\"\"\n", + " self._shard_descriptor = shard_descriptor\n", + " self._shard_dataset = KvasirShardDataset(shard_descriptor.get_dataset('train'))\n", + " \n", + " validation_size = max(1, int(len(self._shard_dataset) * self.validation_fraction))\n", + " \n", + " self.train_indeces = np.arange(len(self._shard_dataset) - validation_size)\n", + " self.val_indeces = np.arange(len(self._shard_dataset) - validation_size, len(self._shard_dataset))\n", + " \n", + " def get_train_loader(self, **kwargs):\n", + " \"\"\"\n", + " Output of this method will be provided to tasks with optimizer in contract\n", + " \"\"\"\n", + " train_sampler = SubsetRandomSampler(self.train_indeces)\n", + " return DataLoader(\n", + " self._shard_dataset,\n", + " num_workers=8,\n", + " batch_size=self.kwargs['train_bs'],\n", + " sampler=train_sampler\n", + " )\n", + "\n", + " def get_valid_loader(self, **kwargs):\n", + " \"\"\"\n", + " Output of this method will be provided to tasks without optimizer in contract\n", + " \"\"\"\n", + " val_sampler = SubsetRandomSampler(self.val_indeces)\n", + " return DataLoader(\n", + " self._shard_dataset,\n", + " num_workers=8,\n", + " batch_size=self.kwargs['valid_bs'],\n", + " sampler=val_sampler\n", + " )\n", + "\n", + " def get_train_data_size(self):\n", + " \"\"\"\n", + " Information for aggregation\n", + " \"\"\"\n", + " return len(self.train_indeces)\n", + "\n", + " def get_valid_data_size(self):\n", + " \"\"\"\n", + " Information for aggregation\n", + " \"\"\"\n", + " return len(self.val_indeces)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d8df35f5", + "metadata": {}, + "outputs": [], + "source": [ + "fed_dataset = KvasirSD(train_bs=4, valid_bs=8)\n", + "fed_dataset.shard_descriptor = dummy_shard_desc\n", + "for i, (sample, target) in enumerate(fed_dataset.get_train_loader()):\n", + " print(sample.shape)" + ] + }, + { + "cell_type": "markdown", + "id": "caring-distinction", + "metadata": {}, + "source": [ + "### Describe a model and optimizer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "visible-victor", + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import torch.nn as nn\n", + "import torch.optim as optim" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "foreign-gospel", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "UNet model definition\n", + "\"\"\"\n", + "from layers import soft_dice_coef, soft_dice_loss, DoubleConv, Down, Up\n", + "\n", + "\n", + "class UNet(nn.Module):\n", + " def __init__(self, n_channels=3, n_classes=1):\n", + " super().__init__()\n", + " self.inc = DoubleConv(n_channels, 64)\n", + " self.down1 = Down(64, 128)\n", + " self.down2 = Down(128, 256)\n", + " self.down3 = Down(256, 512)\n", + " self.up1 = Up(512, 256)\n", + " self.up2 = Up(256, 128)\n", + " self.up3 = Up(128, 64)\n", + " self.outc = nn.Conv2d(64, n_classes, 1)\n", + "\n", + " def forward(self, x):\n", + " x1 = self.inc(x)\n", + " x2 = self.down1(x1)\n", + " x3 = self.down2(x2)\n", + " x4 = self.down3(x3)\n", + " x = self.up1(x4, x3)\n", + " x = self.up2(x, x2)\n", + " x = self.up3(x, x1)\n", + " x = self.outc(x)\n", + " x = torch.sigmoid(x)\n", + " return x\n", + " \n", + "model_unet = UNet()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "greater-activation", + "metadata": {}, + "outputs": [], + "source": [ + "optimizer_adam = optim.Adam(model_unet.parameters(), lr=1e-4)" + ] + }, + { + "cell_type": "markdown", + "id": "caroline-passion", + "metadata": {}, + "source": [ + "#### Register model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "handled-teens", + "metadata": {}, + "outputs": [], + "source": [ + "from copy import deepcopy\n", + "\n", + "framework_adapter = 'openfl.plugins.frameworks_adapters.pytorch_adapter.FrameworkAdapterPlugin'\n", + "MI = ModelInterface(model=model_unet, optimizer=optimizer_adam, framework_plugin=framework_adapter)\n", + "\n", + "# Save the initial model state\n", + "initial_model = deepcopy(model_unet)" + ] + }, + { + "cell_type": "markdown", + "id": "portuguese-groove", + "metadata": {}, + "source": [ + "### Define and register FL tasks" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "increasing-builder", + "metadata": {}, + "outputs": [], + "source": [ + "TI = TaskInterface()\n", + "import torch\n", + "\n", + "import tqdm\n", + "from openfl.interface.aggregation_functions import Median\n", + "\n", + "# The Interactive API supports registering functions definied in main module or imported.\n", + "def function_defined_in_notebook(some_parameter):\n", + " print(f'Also I accept a parameter and it is {some_parameter}')\n", + "\n", + "#The Interactive API supports overriding of the aggregation function\n", + "aggregation_function = Median()\n", + "\n", + "# Task interface currently supports only standalone functions.\n", + "@TI.add_kwargs(**{'some_parameter': 42})\n", + "@TI.register_fl_task(model='unet_model', data_loader='train_loader', \\\n", + " device='device', optimizer='optimizer') \n", + "@TI.set_aggregation_function(aggregation_function)\n", + "def train(unet_model, train_loader, optimizer, device, loss_fn=soft_dice_loss, some_parameter=None):\n", + " \n", + " \"\"\" \n", + " The following constructions, that may lead to resource race\n", + " is no longer needed:\n", + " \n", + " if not torch.cuda.is_available():\n", + " device = 'cpu'\n", + " else:\n", + " device = 'cuda'\n", + " \n", + " \"\"\"\n", + "\n", + " print(f'\\n\\n TASK TRAIN GOT DEVICE {device}\\n\\n')\n", + " \n", + " function_defined_in_notebook(some_parameter)\n", + " \n", + " train_loader = tqdm.tqdm(train_loader, desc=\"train\")\n", + " \n", + " unet_model.train()\n", + " unet_model.to(device)\n", + "\n", + " losses = []\n", + "\n", + " for data, target in train_loader:\n", + " data, target = torch.tensor(data).to(device), torch.tensor(\n", + " target).to(device, dtype=torch.float32)\n", + " optimizer.zero_grad()\n", + " output = unet_model(data)\n", + " loss = loss_fn(output=output, target=target)\n", + " loss.backward()\n", + " optimizer.step()\n", + " losses.append(loss.detach().cpu().numpy())\n", + " \n", + " return {'train_loss': np.mean(losses),}\n", + "\n", + "\n", + "@TI.register_fl_task(model='unet_model', data_loader='val_loader', device='device') \n", + "def validate(unet_model, val_loader, device):\n", + " print(f'\\n\\n TASK VALIDATE GOT DEVICE {device}\\n\\n')\n", + " \n", + " unet_model.eval()\n", + " unet_model.to(device)\n", + " \n", + " val_loader = tqdm.tqdm(val_loader, desc=\"validate\")\n", + "\n", + " val_score = 0\n", + " total_samples = 0\n", + "\n", + " with torch.no_grad():\n", + " for data, target in val_loader:\n", + " samples = target.shape[0]\n", + " total_samples += samples\n", + " data, target = torch.tensor(data).to(device), \\\n", + " torch.tensor(target).to(device, dtype=torch.int64)\n", + " output = unet_model(data)\n", + " val = soft_dice_coef(output, target)\n", + " val_score += val.sum().cpu().numpy()\n", + " \n", + " return {'dice_coef': val_score / total_samples,}" + ] + }, + { + "cell_type": "markdown", + "id": "derived-bride", + "metadata": {}, + "source": [ + "## Time to start a federated learning experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mature-renewal", + "metadata": {}, + "outputs": [], + "source": [ + "# create an experimnet in federation\n", + "experiment_name = 'kvasir_test_experiment'\n", + "fl_experiment = FLExperiment(federation=federation, experiment_name=experiment_name)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lightweight-causing", + "metadata": {}, + "outputs": [], + "source": [ + "# If I use autoreload I got a pickling error\n", + "\n", + "# The following command zips the workspace and python requirements to be transfered to collaborator nodes\n", + "fl_experiment.start(model_provider=MI, \n", + " task_keeper=TI,\n", + " data_loader=fed_dataset,\n", + " rounds_to_train=2,\n", + " opt_treatment='CONTINUE_GLOBAL',\n", + " device_assignment_policy='CUDA_PREFERRED')\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f1543a36", + "metadata": {}, + "outputs": [], + "source": [ + "# If user want to stop IPython session, then reconnect and check how experiment is going \n", + "# fl_experiment.restore_experiment_state(MI)\n", + "\n", + "fl_experiment.stream_metrics()" + ] + }, + { + "cell_type": "markdown", + "id": "8c30b301", + "metadata": {}, + "source": [ + "## Now we validate the best model!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "55acff59", + "metadata": {}, + "outputs": [], + "source": [ + "best_model = fl_experiment.get_best_model()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9479fb7f", + "metadata": {}, + "outputs": [], + "source": [ + "# We remove exremove_experiment_datamove_experiment_datamove_experiment_datariment data from director\n", + "fl_experiment.remove_experiment_data()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "75c8aeab", + "metadata": {}, + "outputs": [], + "source": [ + "best_model.inc.conv[0].weight\n", + "# model_unet.inc.conv[0].weight" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2acb7e6", + "metadata": {}, + "outputs": [], + "source": [ + "# Validating initial model\n", + "validate(initial_model, fed_dataset.get_valid_loader(), 'cpu')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c12ca93f", + "metadata": {}, + "outputs": [], + "source": [ + "# Validating trained model\n", + "validate(best_model, fed_dataset.get_valid_loader(), 'cpu')" + ] + }, + { + "cell_type": "markdown", + "id": "1e6734f6", + "metadata": {}, + "source": [ + "## We can tune model further!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3940e75e", + "metadata": {}, + "outputs": [], + "source": [ + "MI = ModelInterface(model=best_model, optimizer=optimizer_adam, framework_plugin=framework_adapter)\n", + "fl_experiment.start(model_provider=MI, task_keeper=TI, data_loader=fed_dataset, rounds_to_train=4, \\\n", + " opt_treatment='CONTINUE_GLOBAL')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1bd786d2", + "metadata": {}, + "outputs": [], + "source": [ + "best_model = fl_experiment.get_best_model()\n", + "# Validating trained model\n", + "validate(best_model, fed_dataset.get_valid_loader(), 'cpu')" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/layers.py b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/layers.py new file mode 100644 index 0000000000..12d913c15e --- /dev/null +++ b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/layers.py @@ -0,0 +1,103 @@ +# Copyright (C) 2021-2022 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +"""Layers for Unet model.""" + +import torch +import torch.nn as nn +import torch.nn.functional as F + + +def soft_dice_loss(output, target): + """Calculate loss.""" + num = target.size(0) + m1 = output.view(num, -1) + m2 = target.view(num, -1) + intersection = m1 * m2 + score = 2.0 * (intersection.sum(1) + 1) / (m1.sum(1) + m2.sum(1) + 1) + score = 1 - score.sum() / num + return score + + +def soft_dice_coef(output, target): + """Calculate soft DICE coefficient.""" + num = target.size(0) + m1 = output.view(num, -1) + m2 = target.view(num, -1) + intersection = m1 * m2 + score = 2.0 * (intersection.sum(1) + 1) / (m1.sum(1) + m2.sum(1) + 1) + return score.sum() + + +class DoubleConv(nn.Module): + """Pytorch double conv class.""" + + def __init__(self, in_ch, out_ch): + """Initialize layer.""" + super(DoubleConv, self).__init__() + self.in_ch = in_ch + self.out_ch = out_ch + self.conv = nn.Sequential( + nn.Conv2d(in_ch, out_ch, 3, padding=1), + nn.BatchNorm2d(out_ch), + nn.ReLU(inplace=True), + nn.Conv2d(out_ch, out_ch, 3, padding=1), + nn.BatchNorm2d(out_ch), + nn.ReLU(inplace=True), + ) + + def forward(self, x): + """Do forward pass.""" + x = self.conv(x) + return x + + +class Down(nn.Module): + """Pytorch nn module subclass.""" + + def __init__(self, in_ch, out_ch): + """Initialize layer.""" + super(Down, self).__init__() + self.mpconv = nn.Sequential( + nn.MaxPool2d(2), + DoubleConv(in_ch, out_ch) + ) + + def forward(self, x): + """Do forward pass.""" + x = self.mpconv(x) + return x + + +class Up(nn.Module): + """Pytorch nn module subclass.""" + + def __init__(self, in_ch, out_ch, bilinear=False): + """Initialize layer.""" + super(Up, self).__init__() + self.in_ch = in_ch + self.out_ch = out_ch + if bilinear: + self.up = nn.Upsample( + scale_factor=2, + mode='bilinear', + align_corners=True + ) + else: + self.up = nn.ConvTranspose2d(in_ch, in_ch // 2, 2, stride=2) + self.conv = DoubleConv(in_ch, out_ch) + + def forward(self, x1, x2): + """Do forward pass.""" + x1 = self.up(x1) + diff_y = x2.size()[2] - x1.size()[2] + diff_x = x2.size()[3] - x1.size()[3] + + x1 = F.pad( + x1, + (diff_x // 2, diff_x - diff_x // 2, diff_y // 2, diff_y - diff_y // 2) + ) + + x = torch.cat([x2, x1], dim=1) + x = self.conv(x) + return x From 6b7b5efa9f26fe2d9275715117d48a7b1261416a Mon Sep 17 00:00:00 2001 From: Beverly Klemme Date: Thu, 27 Apr 2023 14:52:28 -0700 Subject: [PATCH 2/9] corrected files --- .../PyTorch_FedProx_MNIST/README.md | 46 +- .../workspace/PyTorch_Kvasir_UNet.ipynb | 610 ----------------- .../workspace/Pytorch_MedMNIST_2D.ipynb | 614 ++++++++++++++++++ .../PyTorch_FedProx_MNIST/workspace/layers.py | 103 --- 4 files changed, 637 insertions(+), 736 deletions(-) delete mode 100644 openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/PyTorch_Kvasir_UNet.ipynb create mode 100644 openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_MedMNIST_2D.ipynb delete mode 100644 openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/layers.py diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/README.md b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/README.md index 2b1b03bd25..40afb0bfda 100644 --- a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/README.md +++ b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/README.md @@ -1,15 +1,22 @@ -# PyTorch_Kvasir_UNet +# MedMNIST 2D Classification Tutorial -## **How to run this tutorial (without TLC and locally as a simulation):** +![MedMNISTv2_overview](https://raw.githubusercontent.com/MedMNIST/MedMNIST/main/assets/medmnistv2.jpg) + +For more details, please refer to the original paper: +**MedMNIST v2: A Large-Scale Lightweight Benchmark for 2D and 3D Biomedical Image Classification** ([arXiv](https://arxiv.org/abs/2110.14795)), and [PyPI](https://pypi.org/project/medmnist/). + + +## I. About model and experiments + +We use a simple convolutional neural network and settings coming from [the experiments](https://github.com/MedMNIST/experiments) repository.
+## II. How to run this tutorial (without TLC and locally as a simulation): ### 0. If you haven't done so already, create a virtual environment, install OpenFL, and upgrade pip: - For help with this step, visit the "Install the Package" section of the [OpenFL installation instructions](https://openfl.readthedocs.io/en/latest/install.html#install-the-package). -
### 1. Split terminal into 3 (1 terminal for the director, 1 for the envoy, and 1 for the experiment) -
### 2. Do the following in each terminal: @@ -22,9 +29,8 @@ - Navigate to the tutorial: ```sh - cd openfl/openfl-tutorials/interactive_api/PyTorch_Kvasir_UNet + cd openfl/openfl-tutorials/interactive_api/PyTorch_MedMNIST_2D ``` -
### 3. In the first terminal, run the director: @@ -33,38 +39,32 @@ cd director ./start_director.sh ``` -
### 4. In the second terminal, install requirements and run the envoy: ```sh cd envoy -pip install -r sd_requirements.txt -``` - - If you have GPUs: -```sh +pip install -r requirements.txt ./start_envoy.sh env_one envoy_config.yaml -``` - - For no GPUs, use: -```sh -./start_envoy.sh env_one envoy_config_no_gpu.yaml ``` - Optional: Run a second envoy in an additional terminal: - Ensure step 2 is complete for this terminal as well. - - Repeat step 4 instructions above but change "env_one" name to "env_two" (or another name of your choice). - + - Run the second envoy: +```sh +cd envoy +./start_envoy.sh env_two envoy_config.yaml +```
-### 5. Now that your director and envoy terminals are set up, run the Jupyter Notebook in your experiment terminal: +### 5. In the third terminal (or forth terminal, if you chose to do two envoys) run the Jupyter Notebook: ```sh cd workspace -jupyter lab PyTorch_Kvasir_UNet.ipynb +jupyter lab Pytorch_MedMNIST_2D.ipynb ``` -- A Jupyter Server URL will appear in your terminal. In your browser, proceed to that link. Once the webpage loads, click on the PyTorch_Kvasir_UNet.ipynb file. +- A Jupyter Server URL will appear in your terminal. In your browser, proceed to that link. Once the webpage loads, click on the Pytorch_MedMNIST_2D.ipynb file. - To run the experiment, select the icon that looks like two triangles to "Restart Kernel and Run All Cells". -- You will notice activity in your terminals as the experiment runs, and when the experiment is finished the director terminal will display a message that the experiment has finished successfully. - +- You will notice activity in your terminals as the experiments runs, and when the experiment is finished the director terminal will display a message that the experiment was finished successfully. + \ No newline at end of file diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/PyTorch_Kvasir_UNet.ipynb b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/PyTorch_Kvasir_UNet.ipynb deleted file mode 100644 index ed583e3194..0000000000 --- a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/PyTorch_Kvasir_UNet.ipynb +++ /dev/null @@ -1,610 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "liquid-jacket", - "metadata": {}, - "source": [ - "# Federated Kvasir with Director example" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "alike-sharing", - "metadata": {}, - "outputs": [], - "source": [ - "# Install dependencies if not already installed\n", - "!pip install torchvision==0.8.1" - ] - }, - { - "cell_type": "markdown", - "id": "16986f22", - "metadata": {}, - "source": [ - "# Connect to the Federation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4485ac79", - "metadata": {}, - "outputs": [], - "source": [ - "# Create a federation\n", - "from openfl.interface.interactive_api.federation import Federation\n", - "\n", - "# please use the same identificator that was used in signed certificate\n", - "client_id = 'frontend'\n", - "director_node_fqdn = 'localhost'\n", - "director_port = 50050\n", - "\n", - "# 1) Run with API layer - Director mTLS \n", - "# If the user wants to enable mTLS their must provide CA root chain, and signed key pair to the federation interface\n", - "# cert_chain = 'cert/root_ca.crt'\n", - "# API_certificate = 'cert/frontend.crt'\n", - "# API_private_key = 'cert/frontend.key'\n", - "\n", - "# federation = Federation(\n", - "# client_id=client_id,\n", - "# director_node_fqdn=director_node_fqdn,\n", - "# director_port=director_port,\n", - "# tls=True,\n", - "# cert_chain=cert_chain,\n", - "# api_cert=api_certificate,\n", - "# api_private_key=api_private_key\n", - "# )\n", - "\n", - "# --------------------------------------------------------------------------------------------------------------------\n", - "\n", - "# 2) Run with TLS disabled (trusted environment)\n", - "# Federation can also determine local fqdn automatically\n", - "federation = Federation(\n", - " client_id=client_id,\n", - " director_node_fqdn=director_node_fqdn,\n", - " director_port=director_port,\n", - " tls=False\n", - ")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e35802d5", - "metadata": {}, - "outputs": [], - "source": [ - "# import time\n", - "# while True:\n", - "# shard_registry = federation.get_shard_registry()\n", - "# print(shard_registry)\n", - "# time.sleep(5)\n", - "shard_registry = federation.get_shard_registry()\n", - "shard_registry" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "67ae50de", - "metadata": {}, - "outputs": [], - "source": [ - "federation.target_shape" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "920216d3", - "metadata": {}, - "outputs": [], - "source": [ - "# First, request a dummy_shard_desc that holds information about the federated dataset \n", - "dummy_shard_desc = federation.get_dummy_shard_descriptor(size=10)\n", - "dummy_shard_dataset = dummy_shard_desc.get_dataset('train')\n", - "sample, target = dummy_shard_dataset[0]\n", - "f\"Sample shape: {sample.shape}, target shape: {target.shape}\"" - ] - }, - { - "cell_type": "markdown", - "id": "obvious-tyler", - "metadata": {}, - "source": [ - "## Creating a FL experiment using Interactive API" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "rubber-address", - "metadata": {}, - "outputs": [], - "source": [ - "from openfl.interface.interactive_api.experiment import TaskInterface, DataInterface, ModelInterface, FLExperiment" - ] - }, - { - "cell_type": "markdown", - "id": "sustainable-public", - "metadata": {}, - "source": [ - "### Register dataset" - ] - }, - { - "cell_type": "markdown", - "id": "unlike-texas", - "metadata": {}, - "source": [ - "We extract User dataset class implementation.\n", - "Is it convinient?\n", - "What if the dataset is not a class?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "64f37dcf", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import PIL\n", - "import numpy as np\n", - "from torch.utils.data import Dataset, DataLoader, SubsetRandomSampler\n", - "from torchvision import transforms as tsf\n", - "\n", - "\n", - "class KvasirShardDataset(Dataset):\n", - " \n", - " def __init__(self, dataset):\n", - " self._dataset = dataset\n", - " \n", - " # Prepare transforms\n", - " self.img_trans = tsf.Compose([\n", - " tsf.ToPILImage(),\n", - " tsf.Resize((332, 332)),\n", - " tsf.ToTensor(),\n", - " tsf.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])])\n", - " self.mask_trans = tsf.Compose([\n", - " tsf.ToPILImage(),\n", - " tsf.Resize((332, 332), interpolation=PIL.Image.NEAREST),\n", - " tsf.ToTensor()])\n", - " \n", - " def __getitem__(self, index):\n", - " img, mask = self._dataset[index]\n", - " img = self.img_trans(img).numpy()\n", - " mask = self.mask_trans(mask).numpy()\n", - " return img, mask\n", - " \n", - " def __len__(self):\n", - " return len(self._dataset)\n", - "\n", - " \n", - "\n", - "# Now you can implement you data loaders using dummy_shard_desc\n", - "class KvasirSD(DataInterface):\n", - "\n", - " def __init__(self, validation_fraction=1/8, **kwargs):\n", - " super().__init__(**kwargs)\n", - " \n", - " self.validation_fraction = validation_fraction\n", - " \n", - " @property\n", - " def shard_descriptor(self):\n", - " return self._shard_descriptor\n", - " \n", - " @shard_descriptor.setter\n", - " def shard_descriptor(self, shard_descriptor):\n", - " \"\"\"\n", - " Describe per-collaborator procedures or sharding.\n", - "\n", - " This method will be called during a collaborator initialization.\n", - " Local shard_descriptor will be set by Envoy.\n", - " \"\"\"\n", - " self._shard_descriptor = shard_descriptor\n", - " self._shard_dataset = KvasirShardDataset(shard_descriptor.get_dataset('train'))\n", - " \n", - " validation_size = max(1, int(len(self._shard_dataset) * self.validation_fraction))\n", - " \n", - " self.train_indeces = np.arange(len(self._shard_dataset) - validation_size)\n", - " self.val_indeces = np.arange(len(self._shard_dataset) - validation_size, len(self._shard_dataset))\n", - " \n", - " def get_train_loader(self, **kwargs):\n", - " \"\"\"\n", - " Output of this method will be provided to tasks with optimizer in contract\n", - " \"\"\"\n", - " train_sampler = SubsetRandomSampler(self.train_indeces)\n", - " return DataLoader(\n", - " self._shard_dataset,\n", - " num_workers=8,\n", - " batch_size=self.kwargs['train_bs'],\n", - " sampler=train_sampler\n", - " )\n", - "\n", - " def get_valid_loader(self, **kwargs):\n", - " \"\"\"\n", - " Output of this method will be provided to tasks without optimizer in contract\n", - " \"\"\"\n", - " val_sampler = SubsetRandomSampler(self.val_indeces)\n", - " return DataLoader(\n", - " self._shard_dataset,\n", - " num_workers=8,\n", - " batch_size=self.kwargs['valid_bs'],\n", - " sampler=val_sampler\n", - " )\n", - "\n", - " def get_train_data_size(self):\n", - " \"\"\"\n", - " Information for aggregation\n", - " \"\"\"\n", - " return len(self.train_indeces)\n", - "\n", - " def get_valid_data_size(self):\n", - " \"\"\"\n", - " Information for aggregation\n", - " \"\"\"\n", - " return len(self.val_indeces)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d8df35f5", - "metadata": {}, - "outputs": [], - "source": [ - "fed_dataset = KvasirSD(train_bs=4, valid_bs=8)\n", - "fed_dataset.shard_descriptor = dummy_shard_desc\n", - "for i, (sample, target) in enumerate(fed_dataset.get_train_loader()):\n", - " print(sample.shape)" - ] - }, - { - "cell_type": "markdown", - "id": "caring-distinction", - "metadata": {}, - "source": [ - "### Describe a model and optimizer" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "visible-victor", - "metadata": {}, - "outputs": [], - "source": [ - "import torch\n", - "import torch.nn as nn\n", - "import torch.optim as optim" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "foreign-gospel", - "metadata": {}, - "outputs": [], - "source": [ - "\"\"\"\n", - "UNet model definition\n", - "\"\"\"\n", - "from layers import soft_dice_coef, soft_dice_loss, DoubleConv, Down, Up\n", - "\n", - "\n", - "class UNet(nn.Module):\n", - " def __init__(self, n_channels=3, n_classes=1):\n", - " super().__init__()\n", - " self.inc = DoubleConv(n_channels, 64)\n", - " self.down1 = Down(64, 128)\n", - " self.down2 = Down(128, 256)\n", - " self.down3 = Down(256, 512)\n", - " self.up1 = Up(512, 256)\n", - " self.up2 = Up(256, 128)\n", - " self.up3 = Up(128, 64)\n", - " self.outc = nn.Conv2d(64, n_classes, 1)\n", - "\n", - " def forward(self, x):\n", - " x1 = self.inc(x)\n", - " x2 = self.down1(x1)\n", - " x3 = self.down2(x2)\n", - " x4 = self.down3(x3)\n", - " x = self.up1(x4, x3)\n", - " x = self.up2(x, x2)\n", - " x = self.up3(x, x1)\n", - " x = self.outc(x)\n", - " x = torch.sigmoid(x)\n", - " return x\n", - " \n", - "model_unet = UNet()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "greater-activation", - "metadata": {}, - "outputs": [], - "source": [ - "optimizer_adam = optim.Adam(model_unet.parameters(), lr=1e-4)" - ] - }, - { - "cell_type": "markdown", - "id": "caroline-passion", - "metadata": {}, - "source": [ - "#### Register model" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "handled-teens", - "metadata": {}, - "outputs": [], - "source": [ - "from copy import deepcopy\n", - "\n", - "framework_adapter = 'openfl.plugins.frameworks_adapters.pytorch_adapter.FrameworkAdapterPlugin'\n", - "MI = ModelInterface(model=model_unet, optimizer=optimizer_adam, framework_plugin=framework_adapter)\n", - "\n", - "# Save the initial model state\n", - "initial_model = deepcopy(model_unet)" - ] - }, - { - "cell_type": "markdown", - "id": "portuguese-groove", - "metadata": {}, - "source": [ - "### Define and register FL tasks" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "increasing-builder", - "metadata": {}, - "outputs": [], - "source": [ - "TI = TaskInterface()\n", - "import torch\n", - "\n", - "import tqdm\n", - "from openfl.interface.aggregation_functions import Median\n", - "\n", - "# The Interactive API supports registering functions definied in main module or imported.\n", - "def function_defined_in_notebook(some_parameter):\n", - " print(f'Also I accept a parameter and it is {some_parameter}')\n", - "\n", - "#The Interactive API supports overriding of the aggregation function\n", - "aggregation_function = Median()\n", - "\n", - "# Task interface currently supports only standalone functions.\n", - "@TI.add_kwargs(**{'some_parameter': 42})\n", - "@TI.register_fl_task(model='unet_model', data_loader='train_loader', \\\n", - " device='device', optimizer='optimizer') \n", - "@TI.set_aggregation_function(aggregation_function)\n", - "def train(unet_model, train_loader, optimizer, device, loss_fn=soft_dice_loss, some_parameter=None):\n", - " \n", - " \"\"\" \n", - " The following constructions, that may lead to resource race\n", - " is no longer needed:\n", - " \n", - " if not torch.cuda.is_available():\n", - " device = 'cpu'\n", - " else:\n", - " device = 'cuda'\n", - " \n", - " \"\"\"\n", - "\n", - " print(f'\\n\\n TASK TRAIN GOT DEVICE {device}\\n\\n')\n", - " \n", - " function_defined_in_notebook(some_parameter)\n", - " \n", - " train_loader = tqdm.tqdm(train_loader, desc=\"train\")\n", - " \n", - " unet_model.train()\n", - " unet_model.to(device)\n", - "\n", - " losses = []\n", - "\n", - " for data, target in train_loader:\n", - " data, target = torch.tensor(data).to(device), torch.tensor(\n", - " target).to(device, dtype=torch.float32)\n", - " optimizer.zero_grad()\n", - " output = unet_model(data)\n", - " loss = loss_fn(output=output, target=target)\n", - " loss.backward()\n", - " optimizer.step()\n", - " losses.append(loss.detach().cpu().numpy())\n", - " \n", - " return {'train_loss': np.mean(losses),}\n", - "\n", - "\n", - "@TI.register_fl_task(model='unet_model', data_loader='val_loader', device='device') \n", - "def validate(unet_model, val_loader, device):\n", - " print(f'\\n\\n TASK VALIDATE GOT DEVICE {device}\\n\\n')\n", - " \n", - " unet_model.eval()\n", - " unet_model.to(device)\n", - " \n", - " val_loader = tqdm.tqdm(val_loader, desc=\"validate\")\n", - "\n", - " val_score = 0\n", - " total_samples = 0\n", - "\n", - " with torch.no_grad():\n", - " for data, target in val_loader:\n", - " samples = target.shape[0]\n", - " total_samples += samples\n", - " data, target = torch.tensor(data).to(device), \\\n", - " torch.tensor(target).to(device, dtype=torch.int64)\n", - " output = unet_model(data)\n", - " val = soft_dice_coef(output, target)\n", - " val_score += val.sum().cpu().numpy()\n", - " \n", - " return {'dice_coef': val_score / total_samples,}" - ] - }, - { - "cell_type": "markdown", - "id": "derived-bride", - "metadata": {}, - "source": [ - "## Time to start a federated learning experiment" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "mature-renewal", - "metadata": {}, - "outputs": [], - "source": [ - "# create an experimnet in federation\n", - "experiment_name = 'kvasir_test_experiment'\n", - "fl_experiment = FLExperiment(federation=federation, experiment_name=experiment_name)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "lightweight-causing", - "metadata": {}, - "outputs": [], - "source": [ - "# If I use autoreload I got a pickling error\n", - "\n", - "# The following command zips the workspace and python requirements to be transfered to collaborator nodes\n", - "fl_experiment.start(model_provider=MI, \n", - " task_keeper=TI,\n", - " data_loader=fed_dataset,\n", - " rounds_to_train=2,\n", - " opt_treatment='CONTINUE_GLOBAL',\n", - " device_assignment_policy='CUDA_PREFERRED')\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f1543a36", - "metadata": {}, - "outputs": [], - "source": [ - "# If user want to stop IPython session, then reconnect and check how experiment is going \n", - "# fl_experiment.restore_experiment_state(MI)\n", - "\n", - "fl_experiment.stream_metrics()" - ] - }, - { - "cell_type": "markdown", - "id": "8c30b301", - "metadata": {}, - "source": [ - "## Now we validate the best model!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "55acff59", - "metadata": {}, - "outputs": [], - "source": [ - "best_model = fl_experiment.get_best_model()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9479fb7f", - "metadata": {}, - "outputs": [], - "source": [ - "# We remove exremove_experiment_datamove_experiment_datamove_experiment_datariment data from director\n", - "fl_experiment.remove_experiment_data()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "75c8aeab", - "metadata": {}, - "outputs": [], - "source": [ - "best_model.inc.conv[0].weight\n", - "# model_unet.inc.conv[0].weight" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a2acb7e6", - "metadata": {}, - "outputs": [], - "source": [ - "# Validating initial model\n", - "validate(initial_model, fed_dataset.get_valid_loader(), 'cpu')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c12ca93f", - "metadata": {}, - "outputs": [], - "source": [ - "# Validating trained model\n", - "validate(best_model, fed_dataset.get_valid_loader(), 'cpu')" - ] - }, - { - "cell_type": "markdown", - "id": "1e6734f6", - "metadata": {}, - "source": [ - "## We can tune model further!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3940e75e", - "metadata": {}, - "outputs": [], - "source": [ - "MI = ModelInterface(model=best_model, optimizer=optimizer_adam, framework_plugin=framework_adapter)\n", - "fl_experiment.start(model_provider=MI, task_keeper=TI, data_loader=fed_dataset, rounds_to_train=4, \\\n", - " opt_treatment='CONTINUE_GLOBAL')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1bd786d2", - "metadata": {}, - "outputs": [], - "source": [ - "best_model = fl_experiment.get_best_model()\n", - "# Validating trained model\n", - "validate(best_model, fed_dataset.get_valid_loader(), 'cpu')" - ] - } - ], - "metadata": { - "language_info": { - "name": "python" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_MedMNIST_2D.ipynb b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_MedMNIST_2D.ipynb new file mode 100644 index 0000000000..4cdcd36d43 --- /dev/null +++ b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_MedMNIST_2D.ipynb @@ -0,0 +1,614 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "26fdd9ed", + "metadata": {}, + "source": [ + "# Federated MedMNIST2D " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5504ab79", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install medmnist" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d0570122", + "metadata": {}, + "outputs": [], + "source": [ + "# Install dependencies if not already installed\n", + "import tqdm\n", + "import numpy as np\n", + "import torch\n", + "import torch.nn as nn\n", + "import torch.optim as optim\n", + "from torch.utils.data import Dataset, DataLoader\n", + "from torchvision import transforms as T\n", + "import torch.nn.functional as F\n", + "\n", + "import medmnist" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22ba64da", + "metadata": {}, + "outputs": [], + "source": [ + "from medmnist import INFO, Evaluator\n", + "\n", + "## Change dataflag here to reflect the ones defined in the envoy_conifg_xxx.yaml\n", + "dataname = 'bloodmnist'\n" + ] + }, + { + "cell_type": "markdown", + "id": "246f9c98", + "metadata": {}, + "source": [ + "## Connect to the Federation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d657e463", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a federation\n", + "from openfl.interface.interactive_api.federation import Federation\n", + "\n", + "# please use the same identificator that was used in signed certificate\n", + "client_id = 'api'\n", + "director_node_fqdn = 'localhost'\n", + "director_port=50051\n", + "\n", + "# 2) Run with TLS disabled (trusted environment)\n", + "# Federation can also determine local fqdn automatically\n", + "federation = Federation(\n", + " client_id=client_id,\n", + " director_node_fqdn=director_node_fqdn,\n", + " director_port=director_port, \n", + " tls=False\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47dcfab3", + "metadata": {}, + "outputs": [], + "source": [ + "shard_registry = federation.get_shard_registry()\n", + "shard_registry" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2a6c237", + "metadata": {}, + "outputs": [], + "source": [ + "# First, request a dummy_shard_desc that holds information about the federated dataset \n", + "dummy_shard_desc = federation.get_dummy_shard_descriptor(size=10)\n", + "dummy_shard_dataset = dummy_shard_desc.get_dataset('train')\n", + "sample, target = dummy_shard_dataset[0]\n", + "f\"Sample shape: {sample.shape}, target shape: {target.shape}\"" + ] + }, + { + "cell_type": "markdown", + "id": "cc0dbdbd", + "metadata": {}, + "source": [ + "## Describing FL experimen" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fc88700a", + "metadata": {}, + "outputs": [], + "source": [ + "from openfl.interface.interactive_api.experiment import TaskInterface, DataInterface, ModelInterface, FLExperiment" + ] + }, + { + "cell_type": "markdown", + "id": "9b3081a6", + "metadata": {}, + "source": [ + "## Load MedMNIST INFO" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e0377d3a", + "metadata": {}, + "outputs": [], + "source": [ + "num_epochs = 3\n", + "TRAIN_BS, VALID_BS = 64, 128\n", + "\n", + "lr = 0.001\n", + "gamma=0.1\n", + "milestones = [0.5 * num_epochs, 0.75 * num_epochs]\n", + "\n", + "info = INFO[dataname]\n", + "task = info['task']\n", + "n_channels = info['n_channels']\n", + "n_classes = len(info['label'])" + ] + }, + { + "cell_type": "markdown", + "id": "b0979470", + "metadata": {}, + "source": [ + "### Register dataset" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f0dc457e", + "metadata": {}, + "outputs": [], + "source": [ + "## Data transformations\n", + "data_transform = T.Compose([T.ToTensor(), \n", + " T.Normalize(mean=[.5], std=[.5])]\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09ba2f64", + "metadata": {}, + "outputs": [], + "source": [ + "from PIL import Image\n", + "\n", + "class TransformedDataset(Dataset):\n", + " \"\"\"Image Person ReID Dataset.\"\"\"\n", + "\n", + "\n", + " def __init__(self, dataset, transform=None, target_transform=None):\n", + " \"\"\"Initialize Dataset.\"\"\"\n", + " self.dataset = dataset\n", + " self.transform = transform\n", + " self.target_transform = target_transform\n", + "\n", + " def __len__(self):\n", + " \"\"\"Length of dataset.\"\"\"\n", + " return len(self.dataset)\n", + "\n", + " def __getitem__(self, index):\n", + " \n", + " img, label = self.dataset[index]\n", + " \n", + " if self.target_transform:\n", + " label = self.target_transform(label) \n", + " else:\n", + " label = label.astype(int)\n", + " \n", + " if self.transform:\n", + " img = Image.fromarray(img)\n", + " img = self.transform(img)\n", + " else:\n", + " base_transform = T.PILToTensor()\n", + " img = Image.fromarray(img)\n", + " img = base_transform(img) \n", + "\n", + " return img, label\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "db2d563e", + "metadata": {}, + "outputs": [], + "source": [ + "class MedMnistFedDataset(DataInterface):\n", + " def __init__(self, **kwargs):\n", + " self.kwargs = kwargs\n", + " \n", + " @property\n", + " def shard_descriptor(self):\n", + " return self._shard_descriptor\n", + " \n", + " @shard_descriptor.setter\n", + " def shard_descriptor(self, shard_descriptor):\n", + " \"\"\"\n", + " Describe per-collaborator procedures or sharding.\n", + "\n", + " This method will be called during a collaborator initialization.\n", + " Local shard_descriptor will be set by Envoy.\n", + " \"\"\"\n", + " self._shard_descriptor = shard_descriptor\n", + "\n", + " self.train_set = TransformedDataset(\n", + " self._shard_descriptor.get_dataset('train'),\n", + " transform=data_transform\n", + " ) \n", + " \n", + " self.valid_set = TransformedDataset(\n", + " self._shard_descriptor.get_dataset('val'),\n", + " transform=data_transform\n", + " )\n", + " \n", + " def get_train_loader(self, **kwargs):\n", + " \"\"\"\n", + " Output of this method will be provided to tasks with optimizer in contract\n", + " \"\"\"\n", + " return DataLoader(\n", + " self.train_set, num_workers=8, batch_size=self.kwargs['train_bs'], shuffle=True)\n", + "\n", + " def get_valid_loader(self, **kwargs):\n", + " \"\"\"\n", + " Output of this method will be provided to tasks without optimizer in contract\n", + " \"\"\"\n", + " return DataLoader(self.valid_set, num_workers=8, batch_size=self.kwargs['valid_bs'])\n", + "\n", + " def get_train_data_size(self):\n", + " \"\"\"\n", + " Information for aggregation\n", + " \"\"\"\n", + " return len(self.train_set)\n", + "\n", + " def get_valid_data_size(self):\n", + " \"\"\"\n", + " Information for aggregation\n", + " \"\"\"\n", + " return len(self.valid_set)\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "b0dfb459", + "metadata": {}, + "source": [ + "### Create Mnist federated dataset" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4af5c4c2", + "metadata": {}, + "outputs": [], + "source": [ + "fed_dataset = MedMnistFedDataset(train_bs=TRAIN_BS, valid_bs=VALID_BS)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f63908e", + "metadata": {}, + "outputs": [], + "source": [ + "fed_dataset.shard_descriptor = dummy_shard_desc\n", + "for i, (sample, target) in enumerate(fed_dataset.get_train_loader()):\n", + " print(sample.shape, target.shape)" + ] + }, + { + "cell_type": "markdown", + "id": "075d1d6c", + "metadata": {}, + "source": [ + "### Describe the model and optimizer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8477a001", + "metadata": {}, + "outputs": [], + "source": [ + "# define a simple CNN model\n", + "class Net(nn.Module):\n", + " def __init__(self, in_channels, num_classes):\n", + " super(Net, self).__init__()\n", + "\n", + " self.layer1 = nn.Sequential(\n", + " nn.Conv2d(in_channels, 16, kernel_size=3),\n", + " nn.BatchNorm2d(16),\n", + " nn.ReLU())\n", + "\n", + " self.layer2 = nn.Sequential(\n", + " nn.Conv2d(16, 16, kernel_size=3),\n", + " nn.BatchNorm2d(16),\n", + " nn.ReLU(),\n", + " nn.MaxPool2d(kernel_size=2, stride=2))\n", + "\n", + " self.layer3 = nn.Sequential(\n", + " nn.Conv2d(16, 64, kernel_size=3),\n", + " nn.BatchNorm2d(64),\n", + " nn.ReLU())\n", + " \n", + " self.layer4 = nn.Sequential(\n", + " nn.Conv2d(64, 64, kernel_size=3),\n", + " nn.BatchNorm2d(64),\n", + " nn.ReLU())\n", + "\n", + " self.layer5 = nn.Sequential(\n", + " nn.Conv2d(64, 64, kernel_size=3, padding=1),\n", + " nn.BatchNorm2d(64),\n", + " nn.ReLU(),\n", + " nn.MaxPool2d(kernel_size=2, stride=2))\n", + "\n", + " self.fc = nn.Sequential(\n", + " nn.Linear(64 * 4 * 4, 128),\n", + " nn.ReLU(),\n", + " nn.Linear(128, 128),\n", + " nn.ReLU(),\n", + " nn.Linear(128, num_classes))\n", + "\n", + " def forward(self, x):\n", + " x = self.layer1(x)\n", + " x = self.layer2(x)\n", + " x = self.layer3(x)\n", + " x = self.layer4(x)\n", + " x = self.layer5(x)\n", + " x = x.view(x.size(0), -1)\n", + " x = self.fc(x)\n", + " return x\n", + "\n", + "model = Net(in_channels=n_channels, num_classes=n_classes)\n", + " \n", + "# define loss function and optimizer\n", + "if task == \"multi-label, binary-class\":\n", + " criterion = nn.BCEWithLogitsLoss()\n", + "else:\n", + " criterion = nn.CrossEntropyLoss()\n", + " \n", + "optimizer = optim.SGD(model.parameters(), lr=lr, momentum=0.9)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f2154486", + "metadata": {}, + "outputs": [], + "source": [ + "print(model)" + ] + }, + { + "cell_type": "markdown", + "id": "8d1c78ee", + "metadata": {}, + "source": [ + "### Register model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59831bcd", + "metadata": {}, + "outputs": [], + "source": [ + "from copy import deepcopy\n", + "\n", + "framework_adapter = 'openfl.plugins.frameworks_adapters.pytorch_adapter.FrameworkAdapterPlugin'\n", + "MI = ModelInterface(model=model, optimizer=optimizer, framework_plugin=framework_adapter)\n", + "\n", + "# Save the initial model state\n", + "initial_model = deepcopy(model)" + ] + }, + { + "cell_type": "markdown", + "id": "849c165b", + "metadata": {}, + "source": [ + "## Define and register FL tasks" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4ff463bd", + "metadata": {}, + "outputs": [], + "source": [ + "TI = TaskInterface()\n", + "\n", + "train_custom_params={'criterion':criterion,'task':task}\n", + "\n", + "# Task interface currently supports only standalone functions.\n", + "@TI.add_kwargs(**train_custom_params)\n", + "@TI.register_fl_task(model='model', data_loader='train_loader',\n", + " device='device', optimizer='optimizer')\n", + "def train(model, train_loader, device, optimizer, criterion, task):\n", + " total_loss = []\n", + " \n", + " train_loader = tqdm.tqdm(train_loader, desc=\"train\")\n", + " model.train()\n", + " model.to(device)\n", + " \n", + " for inputs, targets in train_loader:\n", + " \n", + " optimizer.zero_grad()\n", + " outputs = model(inputs.to(device))\n", + " \n", + " if task == 'multi-label, binary-class':\n", + " targets = targets.to(torch.float32).to(device)\n", + " loss = criterion(outputs, targets)\n", + " else:\n", + " targets = torch.squeeze(targets, 1).long().to(device)\n", + " loss = criterion(outputs, targets)\n", + " \n", + " total_loss.append(loss.item())\n", + " \n", + " loss.backward()\n", + " optimizer.step()\n", + "\n", + " return {'train_loss': np.mean(total_loss),}\n", + "\n", + "\n", + "\n", + "val_custom_params={'criterion':criterion, \n", + " 'task':task}\n", + "\n", + "@TI.add_kwargs(**val_custom_params)\n", + "@TI.register_fl_task(model='model', data_loader='val_loader', device='device')\n", + "def validate(model, val_loader, device, criterion, task):\n", + "\n", + " val_loader = tqdm.tqdm(val_loader, desc=\"validate\")\n", + " model.eval()\n", + " model.to(device)\n", + "\n", + " val_score = 0\n", + " total_samples = 0\n", + " total_loss = []\n", + " y_score = torch.tensor([]).to(device)\n", + "\n", + " with torch.no_grad():\n", + " for inputs, targets in val_loader:\n", + " outputs = model(inputs.to(device))\n", + " \n", + " if task == 'multi-label, binary-class':\n", + " targets = targets.to(torch.float32).to(device)\n", + " loss = criterion(outputs, targets)\n", + " m = nn.Sigmoid()\n", + " outputs = m(outputs).to(device)\n", + " else:\n", + " targets = torch.squeeze(targets, 1).long().to(device)\n", + " loss = criterion(outputs, targets)\n", + " m = nn.Softmax(dim=1)\n", + " outputs = m(outputs).to(device)\n", + " targets = targets.float().resize_(len(targets), 1)\n", + "\n", + " total_loss.append(loss.item())\n", + " \n", + " total_samples += targets.shape[0]\n", + " pred = outputs.argmax(dim=1)\n", + " val_score += pred.eq(targets).sum().cpu().numpy()\n", + " \n", + " acc = val_score / total_samples \n", + " test_loss = sum(total_loss) / len(total_loss)\n", + "\n", + " return {'acc': acc,\n", + " 'test_loss': test_loss,\n", + " }" + ] + }, + { + "cell_type": "markdown", + "id": "8f0ebf2d", + "metadata": {}, + "source": [ + "## Time to start a federated learning experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d41b7896", + "metadata": {}, + "outputs": [], + "source": [ + "# create an experimnet in federation\n", + "experiment_name = 'medmnist_exp'\n", + "fl_experiment = FLExperiment(federation=federation, experiment_name=experiment_name)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "41b44de9", + "metadata": {}, + "outputs": [], + "source": [ + "# The following command zips the workspace and python requirements to be transfered to collaborator nodes\n", + "fl_experiment.start(model_provider=MI, \n", + " task_keeper=TI,\n", + " data_loader=fed_dataset,\n", + " rounds_to_train=3,\n", + " opt_treatment='RESET',\n", + " device_assignment_policy='CUDA_PREFERRED')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "01fa7cea", + "metadata": {}, + "outputs": [], + "source": [ + "# If user want to stop IPython session, then reconnect and check how experiment is going\n", + "# fl_experiment.restore_experiment_state(model_interface)\n", + "\n", + "fl_experiment.stream_metrics(tensorboard_logs=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "92940763", + "metadata": {}, + "outputs": [], + "source": [ + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1690ea49", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.8.10 64-bit", + "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.10" + }, + "vscode": { + "interpreter": { + "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/layers.py b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/layers.py deleted file mode 100644 index 12d913c15e..0000000000 --- a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/layers.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (C) 2021-2022 Intel Corporation -# SPDX-License-Identifier: Apache-2.0 - -"""Layers for Unet model.""" - -import torch -import torch.nn as nn -import torch.nn.functional as F - - -def soft_dice_loss(output, target): - """Calculate loss.""" - num = target.size(0) - m1 = output.view(num, -1) - m2 = target.view(num, -1) - intersection = m1 * m2 - score = 2.0 * (intersection.sum(1) + 1) / (m1.sum(1) + m2.sum(1) + 1) - score = 1 - score.sum() / num - return score - - -def soft_dice_coef(output, target): - """Calculate soft DICE coefficient.""" - num = target.size(0) - m1 = output.view(num, -1) - m2 = target.view(num, -1) - intersection = m1 * m2 - score = 2.0 * (intersection.sum(1) + 1) / (m1.sum(1) + m2.sum(1) + 1) - return score.sum() - - -class DoubleConv(nn.Module): - """Pytorch double conv class.""" - - def __init__(self, in_ch, out_ch): - """Initialize layer.""" - super(DoubleConv, self).__init__() - self.in_ch = in_ch - self.out_ch = out_ch - self.conv = nn.Sequential( - nn.Conv2d(in_ch, out_ch, 3, padding=1), - nn.BatchNorm2d(out_ch), - nn.ReLU(inplace=True), - nn.Conv2d(out_ch, out_ch, 3, padding=1), - nn.BatchNorm2d(out_ch), - nn.ReLU(inplace=True), - ) - - def forward(self, x): - """Do forward pass.""" - x = self.conv(x) - return x - - -class Down(nn.Module): - """Pytorch nn module subclass.""" - - def __init__(self, in_ch, out_ch): - """Initialize layer.""" - super(Down, self).__init__() - self.mpconv = nn.Sequential( - nn.MaxPool2d(2), - DoubleConv(in_ch, out_ch) - ) - - def forward(self, x): - """Do forward pass.""" - x = self.mpconv(x) - return x - - -class Up(nn.Module): - """Pytorch nn module subclass.""" - - def __init__(self, in_ch, out_ch, bilinear=False): - """Initialize layer.""" - super(Up, self).__init__() - self.in_ch = in_ch - self.out_ch = out_ch - if bilinear: - self.up = nn.Upsample( - scale_factor=2, - mode='bilinear', - align_corners=True - ) - else: - self.up = nn.ConvTranspose2d(in_ch, in_ch // 2, 2, stride=2) - self.conv = DoubleConv(in_ch, out_ch) - - def forward(self, x1, x2): - """Do forward pass.""" - x1 = self.up(x1) - diff_y = x2.size()[2] - x1.size()[2] - diff_x = x2.size()[3] - x1.size()[3] - - x1 = F.pad( - x1, - (diff_x // 2, diff_x - diff_x // 2, diff_y // 2, diff_y - diff_y // 2) - ) - - x = torch.cat([x2, x1], dim=1) - x = self.conv(x) - return x From d897905830f6db2cb26a2142c78815d8492d087b Mon Sep 17 00:00:00 2001 From: Beverly Klemme Date: Thu, 27 Apr 2023 15:04:29 -0700 Subject: [PATCH 3/9] changed to FedProxOptimizer and ran set_old_weights for new FedProx Pytorch example --- .../workspace/Pytorch_MedMNIST_2D.ipynb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_MedMNIST_2D.ipynb b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_MedMNIST_2D.ipynb index 4cdcd36d43..78eabe0e45 100644 --- a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_MedMNIST_2D.ipynb +++ b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_MedMNIST_2D.ipynb @@ -35,7 +35,8 @@ "from torchvision import transforms as T\n", "import torch.nn.functional as F\n", "\n", - "import medmnist" + "import medmnist\n", + "import openfl.utilities.optimizers.torch.fedprox as FP" ] }, { @@ -383,7 +384,7 @@ "else:\n", " criterion = nn.CrossEntropyLoss()\n", " \n", - "optimizer = optim.SGD(model.parameters(), lr=lr, momentum=0.9)" + "optimizer = FP.FedProxOptimizer(params = model.parameters(), lr=lr, momentum=0.9)" ] }, { @@ -453,6 +454,7 @@ " for inputs, targets in train_loader:\n", " \n", " optimizer.zero_grad()\n", + " optimizer.set_old_weights(model.get_weights())\n", " outputs = model(inputs.to(device))\n", " \n", " if task == 'multi-label, binary-class':\n", From 71f73870a22cb00529bdd7feb84e8baf6b75614a Mon Sep 17 00:00:00 2001 From: Beverly Klemme Date: Thu, 27 Apr 2023 15:58:32 -0700 Subject: [PATCH 4/9] renamed FedProx notebook --- .../Pytorch_FedProx_MedMNIST_2D.ipynb | 616 ++++++++++++++++++ 1 file changed, 616 insertions(+) create mode 100644 openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb new file mode 100644 index 0000000000..78eabe0e45 --- /dev/null +++ b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb @@ -0,0 +1,616 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "26fdd9ed", + "metadata": {}, + "source": [ + "# Federated MedMNIST2D " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5504ab79", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install medmnist" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d0570122", + "metadata": {}, + "outputs": [], + "source": [ + "# Install dependencies if not already installed\n", + "import tqdm\n", + "import numpy as np\n", + "import torch\n", + "import torch.nn as nn\n", + "import torch.optim as optim\n", + "from torch.utils.data import Dataset, DataLoader\n", + "from torchvision import transforms as T\n", + "import torch.nn.functional as F\n", + "\n", + "import medmnist\n", + "import openfl.utilities.optimizers.torch.fedprox as FP" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22ba64da", + "metadata": {}, + "outputs": [], + "source": [ + "from medmnist import INFO, Evaluator\n", + "\n", + "## Change dataflag here to reflect the ones defined in the envoy_conifg_xxx.yaml\n", + "dataname = 'bloodmnist'\n" + ] + }, + { + "cell_type": "markdown", + "id": "246f9c98", + "metadata": {}, + "source": [ + "## Connect to the Federation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d657e463", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a federation\n", + "from openfl.interface.interactive_api.federation import Federation\n", + "\n", + "# please use the same identificator that was used in signed certificate\n", + "client_id = 'api'\n", + "director_node_fqdn = 'localhost'\n", + "director_port=50051\n", + "\n", + "# 2) Run with TLS disabled (trusted environment)\n", + "# Federation can also determine local fqdn automatically\n", + "federation = Federation(\n", + " client_id=client_id,\n", + " director_node_fqdn=director_node_fqdn,\n", + " director_port=director_port, \n", + " tls=False\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47dcfab3", + "metadata": {}, + "outputs": [], + "source": [ + "shard_registry = federation.get_shard_registry()\n", + "shard_registry" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2a6c237", + "metadata": {}, + "outputs": [], + "source": [ + "# First, request a dummy_shard_desc that holds information about the federated dataset \n", + "dummy_shard_desc = federation.get_dummy_shard_descriptor(size=10)\n", + "dummy_shard_dataset = dummy_shard_desc.get_dataset('train')\n", + "sample, target = dummy_shard_dataset[0]\n", + "f\"Sample shape: {sample.shape}, target shape: {target.shape}\"" + ] + }, + { + "cell_type": "markdown", + "id": "cc0dbdbd", + "metadata": {}, + "source": [ + "## Describing FL experimen" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fc88700a", + "metadata": {}, + "outputs": [], + "source": [ + "from openfl.interface.interactive_api.experiment import TaskInterface, DataInterface, ModelInterface, FLExperiment" + ] + }, + { + "cell_type": "markdown", + "id": "9b3081a6", + "metadata": {}, + "source": [ + "## Load MedMNIST INFO" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e0377d3a", + "metadata": {}, + "outputs": [], + "source": [ + "num_epochs = 3\n", + "TRAIN_BS, VALID_BS = 64, 128\n", + "\n", + "lr = 0.001\n", + "gamma=0.1\n", + "milestones = [0.5 * num_epochs, 0.75 * num_epochs]\n", + "\n", + "info = INFO[dataname]\n", + "task = info['task']\n", + "n_channels = info['n_channels']\n", + "n_classes = len(info['label'])" + ] + }, + { + "cell_type": "markdown", + "id": "b0979470", + "metadata": {}, + "source": [ + "### Register dataset" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f0dc457e", + "metadata": {}, + "outputs": [], + "source": [ + "## Data transformations\n", + "data_transform = T.Compose([T.ToTensor(), \n", + " T.Normalize(mean=[.5], std=[.5])]\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09ba2f64", + "metadata": {}, + "outputs": [], + "source": [ + "from PIL import Image\n", + "\n", + "class TransformedDataset(Dataset):\n", + " \"\"\"Image Person ReID Dataset.\"\"\"\n", + "\n", + "\n", + " def __init__(self, dataset, transform=None, target_transform=None):\n", + " \"\"\"Initialize Dataset.\"\"\"\n", + " self.dataset = dataset\n", + " self.transform = transform\n", + " self.target_transform = target_transform\n", + "\n", + " def __len__(self):\n", + " \"\"\"Length of dataset.\"\"\"\n", + " return len(self.dataset)\n", + "\n", + " def __getitem__(self, index):\n", + " \n", + " img, label = self.dataset[index]\n", + " \n", + " if self.target_transform:\n", + " label = self.target_transform(label) \n", + " else:\n", + " label = label.astype(int)\n", + " \n", + " if self.transform:\n", + " img = Image.fromarray(img)\n", + " img = self.transform(img)\n", + " else:\n", + " base_transform = T.PILToTensor()\n", + " img = Image.fromarray(img)\n", + " img = base_transform(img) \n", + "\n", + " return img, label\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "db2d563e", + "metadata": {}, + "outputs": [], + "source": [ + "class MedMnistFedDataset(DataInterface):\n", + " def __init__(self, **kwargs):\n", + " self.kwargs = kwargs\n", + " \n", + " @property\n", + " def shard_descriptor(self):\n", + " return self._shard_descriptor\n", + " \n", + " @shard_descriptor.setter\n", + " def shard_descriptor(self, shard_descriptor):\n", + " \"\"\"\n", + " Describe per-collaborator procedures or sharding.\n", + "\n", + " This method will be called during a collaborator initialization.\n", + " Local shard_descriptor will be set by Envoy.\n", + " \"\"\"\n", + " self._shard_descriptor = shard_descriptor\n", + "\n", + " self.train_set = TransformedDataset(\n", + " self._shard_descriptor.get_dataset('train'),\n", + " transform=data_transform\n", + " ) \n", + " \n", + " self.valid_set = TransformedDataset(\n", + " self._shard_descriptor.get_dataset('val'),\n", + " transform=data_transform\n", + " )\n", + " \n", + " def get_train_loader(self, **kwargs):\n", + " \"\"\"\n", + " Output of this method will be provided to tasks with optimizer in contract\n", + " \"\"\"\n", + " return DataLoader(\n", + " self.train_set, num_workers=8, batch_size=self.kwargs['train_bs'], shuffle=True)\n", + "\n", + " def get_valid_loader(self, **kwargs):\n", + " \"\"\"\n", + " Output of this method will be provided to tasks without optimizer in contract\n", + " \"\"\"\n", + " return DataLoader(self.valid_set, num_workers=8, batch_size=self.kwargs['valid_bs'])\n", + "\n", + " def get_train_data_size(self):\n", + " \"\"\"\n", + " Information for aggregation\n", + " \"\"\"\n", + " return len(self.train_set)\n", + "\n", + " def get_valid_data_size(self):\n", + " \"\"\"\n", + " Information for aggregation\n", + " \"\"\"\n", + " return len(self.valid_set)\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "b0dfb459", + "metadata": {}, + "source": [ + "### Create Mnist federated dataset" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4af5c4c2", + "metadata": {}, + "outputs": [], + "source": [ + "fed_dataset = MedMnistFedDataset(train_bs=TRAIN_BS, valid_bs=VALID_BS)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f63908e", + "metadata": {}, + "outputs": [], + "source": [ + "fed_dataset.shard_descriptor = dummy_shard_desc\n", + "for i, (sample, target) in enumerate(fed_dataset.get_train_loader()):\n", + " print(sample.shape, target.shape)" + ] + }, + { + "cell_type": "markdown", + "id": "075d1d6c", + "metadata": {}, + "source": [ + "### Describe the model and optimizer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8477a001", + "metadata": {}, + "outputs": [], + "source": [ + "# define a simple CNN model\n", + "class Net(nn.Module):\n", + " def __init__(self, in_channels, num_classes):\n", + " super(Net, self).__init__()\n", + "\n", + " self.layer1 = nn.Sequential(\n", + " nn.Conv2d(in_channels, 16, kernel_size=3),\n", + " nn.BatchNorm2d(16),\n", + " nn.ReLU())\n", + "\n", + " self.layer2 = nn.Sequential(\n", + " nn.Conv2d(16, 16, kernel_size=3),\n", + " nn.BatchNorm2d(16),\n", + " nn.ReLU(),\n", + " nn.MaxPool2d(kernel_size=2, stride=2))\n", + "\n", + " self.layer3 = nn.Sequential(\n", + " nn.Conv2d(16, 64, kernel_size=3),\n", + " nn.BatchNorm2d(64),\n", + " nn.ReLU())\n", + " \n", + " self.layer4 = nn.Sequential(\n", + " nn.Conv2d(64, 64, kernel_size=3),\n", + " nn.BatchNorm2d(64),\n", + " nn.ReLU())\n", + "\n", + " self.layer5 = nn.Sequential(\n", + " nn.Conv2d(64, 64, kernel_size=3, padding=1),\n", + " nn.BatchNorm2d(64),\n", + " nn.ReLU(),\n", + " nn.MaxPool2d(kernel_size=2, stride=2))\n", + "\n", + " self.fc = nn.Sequential(\n", + " nn.Linear(64 * 4 * 4, 128),\n", + " nn.ReLU(),\n", + " nn.Linear(128, 128),\n", + " nn.ReLU(),\n", + " nn.Linear(128, num_classes))\n", + "\n", + " def forward(self, x):\n", + " x = self.layer1(x)\n", + " x = self.layer2(x)\n", + " x = self.layer3(x)\n", + " x = self.layer4(x)\n", + " x = self.layer5(x)\n", + " x = x.view(x.size(0), -1)\n", + " x = self.fc(x)\n", + " return x\n", + "\n", + "model = Net(in_channels=n_channels, num_classes=n_classes)\n", + " \n", + "# define loss function and optimizer\n", + "if task == \"multi-label, binary-class\":\n", + " criterion = nn.BCEWithLogitsLoss()\n", + "else:\n", + " criterion = nn.CrossEntropyLoss()\n", + " \n", + "optimizer = FP.FedProxOptimizer(params = model.parameters(), lr=lr, momentum=0.9)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f2154486", + "metadata": {}, + "outputs": [], + "source": [ + "print(model)" + ] + }, + { + "cell_type": "markdown", + "id": "8d1c78ee", + "metadata": {}, + "source": [ + "### Register model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59831bcd", + "metadata": {}, + "outputs": [], + "source": [ + "from copy import deepcopy\n", + "\n", + "framework_adapter = 'openfl.plugins.frameworks_adapters.pytorch_adapter.FrameworkAdapterPlugin'\n", + "MI = ModelInterface(model=model, optimizer=optimizer, framework_plugin=framework_adapter)\n", + "\n", + "# Save the initial model state\n", + "initial_model = deepcopy(model)" + ] + }, + { + "cell_type": "markdown", + "id": "849c165b", + "metadata": {}, + "source": [ + "## Define and register FL tasks" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4ff463bd", + "metadata": {}, + "outputs": [], + "source": [ + "TI = TaskInterface()\n", + "\n", + "train_custom_params={'criterion':criterion,'task':task}\n", + "\n", + "# Task interface currently supports only standalone functions.\n", + "@TI.add_kwargs(**train_custom_params)\n", + "@TI.register_fl_task(model='model', data_loader='train_loader',\n", + " device='device', optimizer='optimizer')\n", + "def train(model, train_loader, device, optimizer, criterion, task):\n", + " total_loss = []\n", + " \n", + " train_loader = tqdm.tqdm(train_loader, desc=\"train\")\n", + " model.train()\n", + " model.to(device)\n", + " \n", + " for inputs, targets in train_loader:\n", + " \n", + " optimizer.zero_grad()\n", + " optimizer.set_old_weights(model.get_weights())\n", + " outputs = model(inputs.to(device))\n", + " \n", + " if task == 'multi-label, binary-class':\n", + " targets = targets.to(torch.float32).to(device)\n", + " loss = criterion(outputs, targets)\n", + " else:\n", + " targets = torch.squeeze(targets, 1).long().to(device)\n", + " loss = criterion(outputs, targets)\n", + " \n", + " total_loss.append(loss.item())\n", + " \n", + " loss.backward()\n", + " optimizer.step()\n", + "\n", + " return {'train_loss': np.mean(total_loss),}\n", + "\n", + "\n", + "\n", + "val_custom_params={'criterion':criterion, \n", + " 'task':task}\n", + "\n", + "@TI.add_kwargs(**val_custom_params)\n", + "@TI.register_fl_task(model='model', data_loader='val_loader', device='device')\n", + "def validate(model, val_loader, device, criterion, task):\n", + "\n", + " val_loader = tqdm.tqdm(val_loader, desc=\"validate\")\n", + " model.eval()\n", + " model.to(device)\n", + "\n", + " val_score = 0\n", + " total_samples = 0\n", + " total_loss = []\n", + " y_score = torch.tensor([]).to(device)\n", + "\n", + " with torch.no_grad():\n", + " for inputs, targets in val_loader:\n", + " outputs = model(inputs.to(device))\n", + " \n", + " if task == 'multi-label, binary-class':\n", + " targets = targets.to(torch.float32).to(device)\n", + " loss = criterion(outputs, targets)\n", + " m = nn.Sigmoid()\n", + " outputs = m(outputs).to(device)\n", + " else:\n", + " targets = torch.squeeze(targets, 1).long().to(device)\n", + " loss = criterion(outputs, targets)\n", + " m = nn.Softmax(dim=1)\n", + " outputs = m(outputs).to(device)\n", + " targets = targets.float().resize_(len(targets), 1)\n", + "\n", + " total_loss.append(loss.item())\n", + " \n", + " total_samples += targets.shape[0]\n", + " pred = outputs.argmax(dim=1)\n", + " val_score += pred.eq(targets).sum().cpu().numpy()\n", + " \n", + " acc = val_score / total_samples \n", + " test_loss = sum(total_loss) / len(total_loss)\n", + "\n", + " return {'acc': acc,\n", + " 'test_loss': test_loss,\n", + " }" + ] + }, + { + "cell_type": "markdown", + "id": "8f0ebf2d", + "metadata": {}, + "source": [ + "## Time to start a federated learning experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d41b7896", + "metadata": {}, + "outputs": [], + "source": [ + "# create an experimnet in federation\n", + "experiment_name = 'medmnist_exp'\n", + "fl_experiment = FLExperiment(federation=federation, experiment_name=experiment_name)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "41b44de9", + "metadata": {}, + "outputs": [], + "source": [ + "# The following command zips the workspace and python requirements to be transfered to collaborator nodes\n", + "fl_experiment.start(model_provider=MI, \n", + " task_keeper=TI,\n", + " data_loader=fed_dataset,\n", + " rounds_to_train=3,\n", + " opt_treatment='RESET',\n", + " device_assignment_policy='CUDA_PREFERRED')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "01fa7cea", + "metadata": {}, + "outputs": [], + "source": [ + "# If user want to stop IPython session, then reconnect and check how experiment is going\n", + "# fl_experiment.restore_experiment_state(model_interface)\n", + "\n", + "fl_experiment.stream_metrics(tensorboard_logs=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "92940763", + "metadata": {}, + "outputs": [], + "source": [ + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1690ea49", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.8.10 64-bit", + "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.10" + }, + "vscode": { + "interpreter": { + "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 3bd77d1b4e68319241847718e3e4af03ff84c667 Mon Sep 17 00:00:00 2001 From: Beverly Klemme Date: Thu, 27 Apr 2023 17:56:29 -0700 Subject: [PATCH 5/9] used mode.parrameters() to get pytorch model weights --- .../Pytorch_FedProx_MedMNIST_2D.ipynb | 4 +- .../workspace/Pytorch_MedMNIST_2D.ipynb | 616 ------------------ 2 files changed, 2 insertions(+), 618 deletions(-) delete mode 100644 openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_MedMNIST_2D.ipynb diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb index 78eabe0e45..be1c9fe242 100644 --- a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb +++ b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb @@ -454,7 +454,7 @@ " for inputs, targets in train_loader:\n", " \n", " optimizer.zero_grad()\n", - " optimizer.set_old_weights(model.get_weights())\n", + " optimizer.set_old_weights(model.parameters())\n", " outputs = model(inputs.to(device))\n", " \n", " if task == 'multi-label, binary-class':\n", @@ -589,7 +589,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3.8.10 64-bit", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_MedMNIST_2D.ipynb b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_MedMNIST_2D.ipynb deleted file mode 100644 index 78eabe0e45..0000000000 --- a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_MedMNIST_2D.ipynb +++ /dev/null @@ -1,616 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "26fdd9ed", - "metadata": {}, - "source": [ - "# Federated MedMNIST2D " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5504ab79", - "metadata": {}, - "outputs": [], - "source": [ - "!pip install medmnist" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d0570122", - "metadata": {}, - "outputs": [], - "source": [ - "# Install dependencies if not already installed\n", - "import tqdm\n", - "import numpy as np\n", - "import torch\n", - "import torch.nn as nn\n", - "import torch.optim as optim\n", - "from torch.utils.data import Dataset, DataLoader\n", - "from torchvision import transforms as T\n", - "import torch.nn.functional as F\n", - "\n", - "import medmnist\n", - "import openfl.utilities.optimizers.torch.fedprox as FP" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "22ba64da", - "metadata": {}, - "outputs": [], - "source": [ - "from medmnist import INFO, Evaluator\n", - "\n", - "## Change dataflag here to reflect the ones defined in the envoy_conifg_xxx.yaml\n", - "dataname = 'bloodmnist'\n" - ] - }, - { - "cell_type": "markdown", - "id": "246f9c98", - "metadata": {}, - "source": [ - "## Connect to the Federation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d657e463", - "metadata": {}, - "outputs": [], - "source": [ - "# Create a federation\n", - "from openfl.interface.interactive_api.federation import Federation\n", - "\n", - "# please use the same identificator that was used in signed certificate\n", - "client_id = 'api'\n", - "director_node_fqdn = 'localhost'\n", - "director_port=50051\n", - "\n", - "# 2) Run with TLS disabled (trusted environment)\n", - "# Federation can also determine local fqdn automatically\n", - "federation = Federation(\n", - " client_id=client_id,\n", - " director_node_fqdn=director_node_fqdn,\n", - " director_port=director_port, \n", - " tls=False\n", - ")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "47dcfab3", - "metadata": {}, - "outputs": [], - "source": [ - "shard_registry = federation.get_shard_registry()\n", - "shard_registry" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a2a6c237", - "metadata": {}, - "outputs": [], - "source": [ - "# First, request a dummy_shard_desc that holds information about the federated dataset \n", - "dummy_shard_desc = federation.get_dummy_shard_descriptor(size=10)\n", - "dummy_shard_dataset = dummy_shard_desc.get_dataset('train')\n", - "sample, target = dummy_shard_dataset[0]\n", - "f\"Sample shape: {sample.shape}, target shape: {target.shape}\"" - ] - }, - { - "cell_type": "markdown", - "id": "cc0dbdbd", - "metadata": {}, - "source": [ - "## Describing FL experimen" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fc88700a", - "metadata": {}, - "outputs": [], - "source": [ - "from openfl.interface.interactive_api.experiment import TaskInterface, DataInterface, ModelInterface, FLExperiment" - ] - }, - { - "cell_type": "markdown", - "id": "9b3081a6", - "metadata": {}, - "source": [ - "## Load MedMNIST INFO" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e0377d3a", - "metadata": {}, - "outputs": [], - "source": [ - "num_epochs = 3\n", - "TRAIN_BS, VALID_BS = 64, 128\n", - "\n", - "lr = 0.001\n", - "gamma=0.1\n", - "milestones = [0.5 * num_epochs, 0.75 * num_epochs]\n", - "\n", - "info = INFO[dataname]\n", - "task = info['task']\n", - "n_channels = info['n_channels']\n", - "n_classes = len(info['label'])" - ] - }, - { - "cell_type": "markdown", - "id": "b0979470", - "metadata": {}, - "source": [ - "### Register dataset" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f0dc457e", - "metadata": {}, - "outputs": [], - "source": [ - "## Data transformations\n", - "data_transform = T.Compose([T.ToTensor(), \n", - " T.Normalize(mean=[.5], std=[.5])]\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "09ba2f64", - "metadata": {}, - "outputs": [], - "source": [ - "from PIL import Image\n", - "\n", - "class TransformedDataset(Dataset):\n", - " \"\"\"Image Person ReID Dataset.\"\"\"\n", - "\n", - "\n", - " def __init__(self, dataset, transform=None, target_transform=None):\n", - " \"\"\"Initialize Dataset.\"\"\"\n", - " self.dataset = dataset\n", - " self.transform = transform\n", - " self.target_transform = target_transform\n", - "\n", - " def __len__(self):\n", - " \"\"\"Length of dataset.\"\"\"\n", - " return len(self.dataset)\n", - "\n", - " def __getitem__(self, index):\n", - " \n", - " img, label = self.dataset[index]\n", - " \n", - " if self.target_transform:\n", - " label = self.target_transform(label) \n", - " else:\n", - " label = label.astype(int)\n", - " \n", - " if self.transform:\n", - " img = Image.fromarray(img)\n", - " img = self.transform(img)\n", - " else:\n", - " base_transform = T.PILToTensor()\n", - " img = Image.fromarray(img)\n", - " img = base_transform(img) \n", - "\n", - " return img, label\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "db2d563e", - "metadata": {}, - "outputs": [], - "source": [ - "class MedMnistFedDataset(DataInterface):\n", - " def __init__(self, **kwargs):\n", - " self.kwargs = kwargs\n", - " \n", - " @property\n", - " def shard_descriptor(self):\n", - " return self._shard_descriptor\n", - " \n", - " @shard_descriptor.setter\n", - " def shard_descriptor(self, shard_descriptor):\n", - " \"\"\"\n", - " Describe per-collaborator procedures or sharding.\n", - "\n", - " This method will be called during a collaborator initialization.\n", - " Local shard_descriptor will be set by Envoy.\n", - " \"\"\"\n", - " self._shard_descriptor = shard_descriptor\n", - "\n", - " self.train_set = TransformedDataset(\n", - " self._shard_descriptor.get_dataset('train'),\n", - " transform=data_transform\n", - " ) \n", - " \n", - " self.valid_set = TransformedDataset(\n", - " self._shard_descriptor.get_dataset('val'),\n", - " transform=data_transform\n", - " )\n", - " \n", - " def get_train_loader(self, **kwargs):\n", - " \"\"\"\n", - " Output of this method will be provided to tasks with optimizer in contract\n", - " \"\"\"\n", - " return DataLoader(\n", - " self.train_set, num_workers=8, batch_size=self.kwargs['train_bs'], shuffle=True)\n", - "\n", - " def get_valid_loader(self, **kwargs):\n", - " \"\"\"\n", - " Output of this method will be provided to tasks without optimizer in contract\n", - " \"\"\"\n", - " return DataLoader(self.valid_set, num_workers=8, batch_size=self.kwargs['valid_bs'])\n", - "\n", - " def get_train_data_size(self):\n", - " \"\"\"\n", - " Information for aggregation\n", - " \"\"\"\n", - " return len(self.train_set)\n", - "\n", - " def get_valid_data_size(self):\n", - " \"\"\"\n", - " Information for aggregation\n", - " \"\"\"\n", - " return len(self.valid_set)\n", - " " - ] - }, - { - "cell_type": "markdown", - "id": "b0dfb459", - "metadata": {}, - "source": [ - "### Create Mnist federated dataset" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4af5c4c2", - "metadata": {}, - "outputs": [], - "source": [ - "fed_dataset = MedMnistFedDataset(train_bs=TRAIN_BS, valid_bs=VALID_BS)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7f63908e", - "metadata": {}, - "outputs": [], - "source": [ - "fed_dataset.shard_descriptor = dummy_shard_desc\n", - "for i, (sample, target) in enumerate(fed_dataset.get_train_loader()):\n", - " print(sample.shape, target.shape)" - ] - }, - { - "cell_type": "markdown", - "id": "075d1d6c", - "metadata": {}, - "source": [ - "### Describe the model and optimizer" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8477a001", - "metadata": {}, - "outputs": [], - "source": [ - "# define a simple CNN model\n", - "class Net(nn.Module):\n", - " def __init__(self, in_channels, num_classes):\n", - " super(Net, self).__init__()\n", - "\n", - " self.layer1 = nn.Sequential(\n", - " nn.Conv2d(in_channels, 16, kernel_size=3),\n", - " nn.BatchNorm2d(16),\n", - " nn.ReLU())\n", - "\n", - " self.layer2 = nn.Sequential(\n", - " nn.Conv2d(16, 16, kernel_size=3),\n", - " nn.BatchNorm2d(16),\n", - " nn.ReLU(),\n", - " nn.MaxPool2d(kernel_size=2, stride=2))\n", - "\n", - " self.layer3 = nn.Sequential(\n", - " nn.Conv2d(16, 64, kernel_size=3),\n", - " nn.BatchNorm2d(64),\n", - " nn.ReLU())\n", - " \n", - " self.layer4 = nn.Sequential(\n", - " nn.Conv2d(64, 64, kernel_size=3),\n", - " nn.BatchNorm2d(64),\n", - " nn.ReLU())\n", - "\n", - " self.layer5 = nn.Sequential(\n", - " nn.Conv2d(64, 64, kernel_size=3, padding=1),\n", - " nn.BatchNorm2d(64),\n", - " nn.ReLU(),\n", - " nn.MaxPool2d(kernel_size=2, stride=2))\n", - "\n", - " self.fc = nn.Sequential(\n", - " nn.Linear(64 * 4 * 4, 128),\n", - " nn.ReLU(),\n", - " nn.Linear(128, 128),\n", - " nn.ReLU(),\n", - " nn.Linear(128, num_classes))\n", - "\n", - " def forward(self, x):\n", - " x = self.layer1(x)\n", - " x = self.layer2(x)\n", - " x = self.layer3(x)\n", - " x = self.layer4(x)\n", - " x = self.layer5(x)\n", - " x = x.view(x.size(0), -1)\n", - " x = self.fc(x)\n", - " return x\n", - "\n", - "model = Net(in_channels=n_channels, num_classes=n_classes)\n", - " \n", - "# define loss function and optimizer\n", - "if task == \"multi-label, binary-class\":\n", - " criterion = nn.BCEWithLogitsLoss()\n", - "else:\n", - " criterion = nn.CrossEntropyLoss()\n", - " \n", - "optimizer = FP.FedProxOptimizer(params = model.parameters(), lr=lr, momentum=0.9)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f2154486", - "metadata": {}, - "outputs": [], - "source": [ - "print(model)" - ] - }, - { - "cell_type": "markdown", - "id": "8d1c78ee", - "metadata": {}, - "source": [ - "### Register model" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "59831bcd", - "metadata": {}, - "outputs": [], - "source": [ - "from copy import deepcopy\n", - "\n", - "framework_adapter = 'openfl.plugins.frameworks_adapters.pytorch_adapter.FrameworkAdapterPlugin'\n", - "MI = ModelInterface(model=model, optimizer=optimizer, framework_plugin=framework_adapter)\n", - "\n", - "# Save the initial model state\n", - "initial_model = deepcopy(model)" - ] - }, - { - "cell_type": "markdown", - "id": "849c165b", - "metadata": {}, - "source": [ - "## Define and register FL tasks" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4ff463bd", - "metadata": {}, - "outputs": [], - "source": [ - "TI = TaskInterface()\n", - "\n", - "train_custom_params={'criterion':criterion,'task':task}\n", - "\n", - "# Task interface currently supports only standalone functions.\n", - "@TI.add_kwargs(**train_custom_params)\n", - "@TI.register_fl_task(model='model', data_loader='train_loader',\n", - " device='device', optimizer='optimizer')\n", - "def train(model, train_loader, device, optimizer, criterion, task):\n", - " total_loss = []\n", - " \n", - " train_loader = tqdm.tqdm(train_loader, desc=\"train\")\n", - " model.train()\n", - " model.to(device)\n", - " \n", - " for inputs, targets in train_loader:\n", - " \n", - " optimizer.zero_grad()\n", - " optimizer.set_old_weights(model.get_weights())\n", - " outputs = model(inputs.to(device))\n", - " \n", - " if task == 'multi-label, binary-class':\n", - " targets = targets.to(torch.float32).to(device)\n", - " loss = criterion(outputs, targets)\n", - " else:\n", - " targets = torch.squeeze(targets, 1).long().to(device)\n", - " loss = criterion(outputs, targets)\n", - " \n", - " total_loss.append(loss.item())\n", - " \n", - " loss.backward()\n", - " optimizer.step()\n", - "\n", - " return {'train_loss': np.mean(total_loss),}\n", - "\n", - "\n", - "\n", - "val_custom_params={'criterion':criterion, \n", - " 'task':task}\n", - "\n", - "@TI.add_kwargs(**val_custom_params)\n", - "@TI.register_fl_task(model='model', data_loader='val_loader', device='device')\n", - "def validate(model, val_loader, device, criterion, task):\n", - "\n", - " val_loader = tqdm.tqdm(val_loader, desc=\"validate\")\n", - " model.eval()\n", - " model.to(device)\n", - "\n", - " val_score = 0\n", - " total_samples = 0\n", - " total_loss = []\n", - " y_score = torch.tensor([]).to(device)\n", - "\n", - " with torch.no_grad():\n", - " for inputs, targets in val_loader:\n", - " outputs = model(inputs.to(device))\n", - " \n", - " if task == 'multi-label, binary-class':\n", - " targets = targets.to(torch.float32).to(device)\n", - " loss = criterion(outputs, targets)\n", - " m = nn.Sigmoid()\n", - " outputs = m(outputs).to(device)\n", - " else:\n", - " targets = torch.squeeze(targets, 1).long().to(device)\n", - " loss = criterion(outputs, targets)\n", - " m = nn.Softmax(dim=1)\n", - " outputs = m(outputs).to(device)\n", - " targets = targets.float().resize_(len(targets), 1)\n", - "\n", - " total_loss.append(loss.item())\n", - " \n", - " total_samples += targets.shape[0]\n", - " pred = outputs.argmax(dim=1)\n", - " val_score += pred.eq(targets).sum().cpu().numpy()\n", - " \n", - " acc = val_score / total_samples \n", - " test_loss = sum(total_loss) / len(total_loss)\n", - "\n", - " return {'acc': acc,\n", - " 'test_loss': test_loss,\n", - " }" - ] - }, - { - "cell_type": "markdown", - "id": "8f0ebf2d", - "metadata": {}, - "source": [ - "## Time to start a federated learning experiment" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d41b7896", - "metadata": {}, - "outputs": [], - "source": [ - "# create an experimnet in federation\n", - "experiment_name = 'medmnist_exp'\n", - "fl_experiment = FLExperiment(federation=federation, experiment_name=experiment_name)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "41b44de9", - "metadata": {}, - "outputs": [], - "source": [ - "# The following command zips the workspace and python requirements to be transfered to collaborator nodes\n", - "fl_experiment.start(model_provider=MI, \n", - " task_keeper=TI,\n", - " data_loader=fed_dataset,\n", - " rounds_to_train=3,\n", - " opt_treatment='RESET',\n", - " device_assignment_policy='CUDA_PREFERRED')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "01fa7cea", - "metadata": {}, - "outputs": [], - "source": [ - "# If user want to stop IPython session, then reconnect and check how experiment is going\n", - "# fl_experiment.restore_experiment_state(model_interface)\n", - "\n", - "fl_experiment.stream_metrics(tensorboard_logs=False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "92940763", - "metadata": {}, - "outputs": [], - "source": [ - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1690ea49", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.8.10 64-bit", - "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.10" - }, - "vscode": { - "interpreter": { - "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From 12cf8001236471bc5baec4291df1571adf3965c6 Mon Sep 17 00:00:00 2001 From: Beverly Klemme Date: Thu, 27 Apr 2023 19:15:55 -0700 Subject: [PATCH 6/9] got weights using state_dict --- .../Pytorch_FedProx_MedMNIST_2D.ipynb | 372 ++++++++++++++++-- 1 file changed, 343 insertions(+), 29 deletions(-) diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb index be1c9fe242..5b34c5f59b 100644 --- a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb +++ b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb @@ -10,17 +10,74 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "5504ab79", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: medmnist in /home/bjklemme/openflvenv/lib/python3.8/site-packages (2.2.1)\n", + "Requirement already satisfied: torch in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from medmnist) (2.0.0)\n", + "Requirement already satisfied: fire in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from medmnist) (0.5.0)\n", + "Requirement already satisfied: numpy in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from medmnist) (1.24.2)\n", + "Requirement already satisfied: torchvision in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from medmnist) (0.15.1)\n", + "Requirement already satisfied: tqdm in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from medmnist) (4.65.0)\n", + "Requirement already satisfied: pandas in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from medmnist) (1.5.3)\n", + "Requirement already satisfied: scikit-learn in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from medmnist) (1.2.2)\n", + "Requirement already satisfied: Pillow in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from medmnist) (9.5.0)\n", + "Requirement already satisfied: scikit-image in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from medmnist) (0.20.0)\n", + "Requirement already satisfied: typing-extensions in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (4.5.0)\n", + "Requirement already satisfied: jinja2 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (3.1.2)\n", + "Requirement already satisfied: nvidia-cublas-cu11==11.10.3.66; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (11.10.3.66)\n", + "Requirement already satisfied: filelock in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (3.12.0)\n", + "Requirement already satisfied: nvidia-cusparse-cu11==11.7.4.91; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (11.7.4.91)\n", + "Requirement already satisfied: sympy in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (1.11.1)\n", + "Requirement already satisfied: triton==2.0.0; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (2.0.0)\n", + "Requirement already satisfied: nvidia-nvtx-cu11==11.7.91; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (11.7.91)\n", + "Requirement already satisfied: nvidia-cufft-cu11==10.9.0.58; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (10.9.0.58)\n", + "Requirement already satisfied: nvidia-cuda-runtime-cu11==11.7.99; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (11.7.99)\n", + "Requirement already satisfied: nvidia-curand-cu11==10.2.10.91; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (10.2.10.91)\n", + "Requirement already satisfied: nvidia-cuda-cupti-cu11==11.7.101; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (11.7.101)\n", + "Requirement already satisfied: nvidia-cusolver-cu11==11.4.0.1; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (11.4.0.1)\n", + "Requirement already satisfied: nvidia-nccl-cu11==2.14.3; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (2.14.3)\n", + "Requirement already satisfied: nvidia-cuda-nvrtc-cu11==11.7.99; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (11.7.99)\n", + "Requirement already satisfied: networkx in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (3.1)\n", + "Requirement already satisfied: nvidia-cudnn-cu11==8.5.0.96; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (8.5.0.96)\n", + "Requirement already satisfied: six in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from fire->medmnist) (1.16.0)\n", + "Requirement already satisfied: termcolor in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from fire->medmnist) (2.3.0)\n", + "Requirement already satisfied: requests in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torchvision->medmnist) (2.28.2)\n", + "Requirement already satisfied: python-dateutil>=2.8.1 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from pandas->medmnist) (2.8.2)\n", + "Requirement already satisfied: pytz>=2020.1 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from pandas->medmnist) (2023.2)\n", + "Requirement already satisfied: joblib>=1.1.1 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from scikit-learn->medmnist) (1.2.0)\n", + "Requirement already satisfied: threadpoolctl>=2.0.0 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from scikit-learn->medmnist) (3.1.0)\n", + "Requirement already satisfied: scipy>=1.3.2 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from scikit-learn->medmnist) (1.9.1)\n", + "Requirement already satisfied: packaging>=20.0 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from scikit-image->medmnist) (23.0)\n", + "Requirement already satisfied: lazy_loader>=0.1 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from scikit-image->medmnist) (0.2)\n", + "Requirement already satisfied: tifffile>=2019.7.26 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from scikit-image->medmnist) (2023.4.12)\n", + "Requirement already satisfied: PyWavelets>=1.1.1 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from scikit-image->medmnist) (1.4.1)\n", + "Requirement already satisfied: imageio>=2.4.1 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from scikit-image->medmnist) (2.28.0)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from jinja2->torch->medmnist) (2.1.2)\n", + "Requirement already satisfied: wheel in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from nvidia-cublas-cu11==11.10.3.66; platform_system == \"Linux\" and platform_machine == \"x86_64\"->torch->medmnist) (0.40.0)\n", + "Requirement already satisfied: setuptools in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from nvidia-cublas-cu11==11.10.3.66; platform_system == \"Linux\" and platform_machine == \"x86_64\"->torch->medmnist) (67.7.2)\n", + "Requirement already satisfied: mpmath>=0.19 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from sympy->torch->medmnist) (1.3.0)\n", + "Requirement already satisfied: cmake in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from triton==2.0.0; platform_system == \"Linux\" and platform_machine == \"x86_64\"->torch->medmnist) (3.26.3)\n", + "Requirement already satisfied: lit in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from triton==2.0.0; platform_system == \"Linux\" and platform_machine == \"x86_64\"->torch->medmnist) (16.0.2)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from requests->torchvision->medmnist) (3.1.0)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from requests->torchvision->medmnist) (2022.12.7)\n", + "Requirement already satisfied: idna<4,>=2.5 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from requests->torchvision->medmnist) (3.4)\n", + "Requirement already satisfied: urllib3<1.27,>=1.21.1 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from requests->torchvision->medmnist) (1.26.15)\n" + ] + } + ], "source": [ "!pip install medmnist" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "d0570122", "metadata": {}, "outputs": [], @@ -41,7 +98,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "22ba64da", "metadata": {}, "outputs": [], @@ -62,10 +119,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "d657e463", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "E0427 19:14:27.775750139 26587 http_proxy.cc:92] 'https' scheme not supported in proxy URI\n" + ] + } + ], "source": [ "# Create a federation\n", "from openfl.interface.interactive_api.federation import Federation\n", @@ -87,10 +152,35 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "id": "47dcfab3", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "{'env_one': {'shard_info': node_info {\n", + " name: \"env_one\"\n", + " }\n", + " shard_description: \"MedMNIST dataset, shard number 1 out of 1\"\n", + " sample_shape: \"28\"\n", + " sample_shape: \"28\"\n", + " sample_shape: \"3\"\n", + " target_shape: \"1\"\n", + " target_shape: \"1\",\n", + " 'is_online': True,\n", + " 'is_experiment_running': False,\n", + " 'last_updated': '2023-04-27 19:13:57',\n", + " 'current_time': '2023-04-27 19:14:27',\n", + " 'valid_duration': seconds: 120,\n", + " 'experiment_name': 'ExperimentName Mock'}}" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "shard_registry = federation.get_shard_registry()\n", "shard_registry" @@ -98,10 +188,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "a2a6c237", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "'Sample shape: (28, 28, 3), target shape: (1, 1)'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# First, request a dummy_shard_desc that holds information about the federated dataset \n", "dummy_shard_desc = federation.get_dummy_shard_descriptor(size=10)\n", @@ -120,7 +221,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "fc88700a", "metadata": {}, "outputs": [], @@ -138,7 +239,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "e0377d3a", "metadata": {}, "outputs": [], @@ -166,7 +267,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "id": "f0dc457e", "metadata": {}, "outputs": [], @@ -179,7 +280,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "id": "09ba2f64", "metadata": {}, "outputs": [], @@ -222,7 +323,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "id": "db2d563e", "metadata": {}, "outputs": [], @@ -292,7 +393,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "id": "4af5c4c2", "metadata": {}, "outputs": [], @@ -302,10 +403,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "id": "7f63908e", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "torch.Size([10, 3, 28, 28]) torch.Size([10, 1, 1])\n" + ] + } + ], "source": [ "fed_dataset.shard_descriptor = dummy_shard_desc\n", "for i, (sample, target) in enumerate(fed_dataset.get_train_loader()):\n", @@ -322,7 +431,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "id": "8477a001", "metadata": {}, "outputs": [], @@ -389,10 +498,53 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "id": "f2154486", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Net(\n", + " (layer1): Sequential(\n", + " (0): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1))\n", + " (1): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU()\n", + " )\n", + " (layer2): Sequential(\n", + " (0): Conv2d(16, 16, kernel_size=(3, 3), stride=(1, 1))\n", + " (1): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU()\n", + " (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + " )\n", + " (layer3): Sequential(\n", + " (0): Conv2d(16, 64, kernel_size=(3, 3), stride=(1, 1))\n", + " (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU()\n", + " )\n", + " (layer4): Sequential(\n", + " (0): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1))\n", + " (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU()\n", + " )\n", + " (layer5): Sequential(\n", + " (0): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU()\n", + " (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + " )\n", + " (fc): Sequential(\n", + " (0): Linear(in_features=1024, out_features=128, bias=True)\n", + " (1): ReLU()\n", + " (2): Linear(in_features=128, out_features=128, bias=True)\n", + " (3): ReLU()\n", + " (4): Linear(in_features=128, out_features=8, bias=True)\n", + " )\n", + ")\n" + ] + } + ], "source": [ "print(model)" ] @@ -407,7 +559,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "id": "59831bcd", "metadata": {}, "outputs": [], @@ -431,7 +583,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "id": "4ff463bd", "metadata": {}, "outputs": [], @@ -454,7 +606,7 @@ " for inputs, targets in train_loader:\n", " \n", " optimizer.zero_grad()\n", - " optimizer.set_old_weights(model.parameters())\n", + " optimizer.set_old_weights(model.state_dict())\n", " outputs = model(inputs.to(device))\n", " \n", " if task == 'multi-label, binary-class':\n", @@ -529,7 +681,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "id": "d41b7896", "metadata": {}, "outputs": [], @@ -541,10 +693,162 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "id": "41b44de9", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
[19:14:28] INFO     Building 🡆 Object CloudpickleSerializer from openfl.plugins.interface_serializer.cloudpickle_serializer Module.                  plan.py:171\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2;36m[19:14:28]\u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Building \u001b[31m🡆\u001b[0m Object \u001b[31mCloudpickleSerializer\u001b[0m from \u001b[31mopenfl.plugins.interface_serializer.cloudpickle_serializer\u001b[0m Module. \u001b]8;id=588775;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py\u001b\\\u001b[2mplan.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=543696;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py#171\u001b\\\u001b[2m171\u001b[0m\u001b]8;;\u001b\\\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
           INFO     Building 🡆 Object FrameworkAdapterPlugin from openfl.plugins.frameworks_adapters.pytorch_adapter Module.                         plan.py:171\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Building \u001b[31m🡆\u001b[0m Object \u001b[31mFrameworkAdapterPlugin\u001b[0m from \u001b[31mopenfl.plugins.frameworks_adapters.pytorch_adapter\u001b[0m Module. \u001b]8;id=630801;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py\u001b\\\u001b[2mplan.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=742466;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py#171\u001b\\\u001b[2m171\u001b[0m\u001b]8;;\u001b\\\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/bjklemme/openflvenv/lib/python3.8/site-packages/_distutils_hack/__init__.py:33: UserWarning: Setuptools is replacing distutils.\n", + " warnings.warn(\"Setuptools is replacing distutils.\")\n" + ] + }, + { + "data": { + "text/html": [ + "
           INFO     Starting experiment!                                                                                                       experiment.py:245\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Starting experiment! \u001b]8;id=18397;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/interface/interactive_api/experiment.py\u001b\\\u001b[2mexperiment.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=703756;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/interface/interactive_api/experiment.py#245\u001b\\\u001b[2m245\u001b[0m\u001b]8;;\u001b\\\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
           INFO     FL-Plan hash is 101ce5e38ae55d2d65bd01ed1ff1d23be7f216cc2ba17915b2fa2da83fb8f31b6e0bf0db50b5cd853d6eb24ff2813eda                 plan.py:235\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m FL-Plan hash is \u001b[34m101ce5e38ae55d2d65bd01ed1ff1d23be7f216cc2ba17915b2fa2da83fb8f31b6e0bf0db50b5cd853d6eb24ff2813eda\u001b[0m \u001b]8;id=915780;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py\u001b\\\u001b[2mplan.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=260848;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py#235\u001b\\\u001b[2m235\u001b[0m\u001b]8;;\u001b\\\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
           INFO     FL-Plan hash is 101ce5e38ae55d2d65bd01ed1ff1d23be7f216cc2ba17915b2fa2da83fb8f31b6e0bf0db50b5cd853d6eb24ff2813eda                 plan.py:235\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m FL-Plan hash is \u001b[34m101ce5e38ae55d2d65bd01ed1ff1d23be7f216cc2ba17915b2fa2da83fb8f31b6e0bf0db50b5cd853d6eb24ff2813eda\u001b[0m \u001b]8;id=797366;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py\u001b\\\u001b[2mplan.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=292809;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py#235\u001b\\\u001b[2m235\u001b[0m\u001b]8;;\u001b\\\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
           INFO     Building 🡆 Object CoreTaskRunner from openfl.federated.task.task_runner Module.                                                  plan.py:171\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Building \u001b[31m🡆\u001b[0m Object \u001b[31mCoreTaskRunner\u001b[0m from \u001b[31mopenfl.federated.task.task_runner\u001b[0m Module. \u001b]8;id=534726;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py\u001b\\\u001b[2mplan.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=824701;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py#171\u001b\\\u001b[2m171\u001b[0m\u001b]8;;\u001b\\\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
           INFO     Building 🡆 Object FrameworkAdapterPlugin from openfl.plugins.frameworks_adapters.pytorch_adapter Module.                         plan.py:171\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Building \u001b[31m🡆\u001b[0m Object \u001b[31mFrameworkAdapterPlugin\u001b[0m from \u001b[31mopenfl.plugins.frameworks_adapters.pytorch_adapter\u001b[0m Module. \u001b]8;id=559039;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py\u001b\\\u001b[2mplan.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=365234;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py#171\u001b\\\u001b[2m171\u001b[0m\u001b]8;;\u001b\\\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
           WARNING  tried to remove tensor: __opt_state_needed not present in the tensor dict                                                       utils.py:172\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[31mWARNING \u001b[0m tried to remove tensor: __opt_state_needed not present in the tensor dict \u001b]8;id=872466;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/utilities/utils.py\u001b\\\u001b[2mutils.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=142002;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/utilities/utils.py#172\u001b\\\u001b[2m172\u001b[0m\u001b]8;;\u001b\\\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
           WARNING  tried to remove tensor: __opt_state_needed not present in the tensor dict                                                       utils.py:172\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[31mWARNING \u001b[0m tried to remove tensor: __opt_state_needed not present in the tensor dict \u001b]8;id=290143;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/utilities/utils.py\u001b\\\u001b[2mutils.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=939997;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/utilities/utils.py#172\u001b\\\u001b[2m172\u001b[0m\u001b]8;;\u001b\\\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
           INFO     SetNewExperiment                                                                                                      director_client.py:209\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m SetNewExperiment \u001b]8;id=673240;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/transport/grpc/director_client.py\u001b\\\u001b[2mdirector_client.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=46132;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/transport/grpc/director_client.py#209\u001b\\\u001b[2m209\u001b[0m\u001b]8;;\u001b\\\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
           INFO     Experiment was submitted to the director!                                                                                  experiment.py:259\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Experiment was submitted to the director! \u001b]8;id=978253;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/interface/interactive_api/experiment.py\u001b\\\u001b[2mexperiment.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=636446;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/interface/interactive_api/experiment.py#259\u001b\\\u001b[2m259\u001b[0m\u001b]8;;\u001b\\\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# The following command zips the workspace and python requirements to be transfered to collaborator nodes\n", "fl_experiment.start(model_provider=MI, \n", @@ -557,9 +861,11 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, "id": "01fa7cea", - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ "# If user want to stop IPython session, then reconnect and check how experiment is going\n", @@ -585,6 +891,14 @@ "metadata": {}, "outputs": [], "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10d7d5a2", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { From d8e4ab70f4cef37a0c64edd730453de5ee02d297 Mon Sep 17 00:00:00 2001 From: Beverly Klemme Date: Fri, 28 Apr 2023 06:56:42 -0700 Subject: [PATCH 7/9] changed old wieghts to list (for serialization) and fixed README --- .../PyTorch_FedProx_MNIST/README.md | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/README.md b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/README.md index 40afb0bfda..33bf8543c4 100644 --- a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/README.md +++ b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/README.md @@ -1,4 +1,4 @@ -# MedMNIST 2D Classification Tutorial +# MedMNIST 2D Classification Using FedProx Optimizer Tutorial ![MedMNISTv2_overview](https://raw.githubusercontent.com/MedMNIST/MedMNIST/main/assets/medmnistv2.jpg) @@ -15,21 +15,21 @@ We use a simple convolutional neural network and settings coming from [the exper ### 0. If you haven't done so already, create a virtual environment, install OpenFL, and upgrade pip: - For help with this step, visit the "Install the Package" section of the [OpenFL installation instructions](https://openfl.readthedocs.io/en/latest/install.html#install-the-package).
- + ### 1. Split terminal into 3 (1 terminal for the director, 1 for the envoy, and 1 for the experiment) -
+
### 2. Do the following in each terminal: - Activate the virtual environment from step 0: - + ```sh source venv/bin/activate ``` - If you are in a network environment with a proxy, ensure proxy environment variables are set in each of your terminals. - Navigate to the tutorial: - + ```sh - cd openfl/openfl-tutorials/interactive_api/PyTorch_MedMNIST_2D + cd openfl/openfl-tutorials/interactive_api/PyTorch_FedProx_MedMNIST_2D ```
@@ -62,9 +62,8 @@ cd envoy ```sh cd workspace -jupyter lab Pytorch_MedMNIST_2D.ipynb +jupyter lab Pytorch_FedProx_MedMNIST_2D.ipynb ``` -- A Jupyter Server URL will appear in your terminal. In your browser, proceed to that link. Once the webpage loads, click on the Pytorch_MedMNIST_2D.ipynb file. -- To run the experiment, select the icon that looks like two triangles to "Restart Kernel and Run All Cells". -- You will notice activity in your terminals as the experiments runs, and when the experiment is finished the director terminal will display a message that the experiment was finished successfully. - \ No newline at end of file +- A Jupyter Server URL will appear in your terminal. In your browser, proceed to that link. Once the webpage loads, click on the Pytorch_FedProx_MedMNIST_2D.ipynb file. +- To run the experiment, select the icon that looks like two triangles to "Restart Kernel and Run All Cells". +- You will notice activity in your terminals as the experiments runs, and when the experiment is finished the director terminal will display a message that the experiment was finished successfully. From f87409ce55ae1c4db48b2fd7cc7d0171ebc742f6 Mon Sep 17 00:00:00 2001 From: Beverly Klemme Date: Fri, 28 Apr 2023 07:02:12 -0700 Subject: [PATCH 8/9] input wieghts before zero_grad --- .../Pytorch_FedProx_MedMNIST_2D.ipynb | 367 ++---------------- 1 file changed, 33 insertions(+), 334 deletions(-) diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb index 5b34c5f59b..a4aadf8e1d 100644 --- a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb +++ b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb @@ -10,74 +10,17 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "5504ab79", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Requirement already satisfied: medmnist in /home/bjklemme/openflvenv/lib/python3.8/site-packages (2.2.1)\n", - "Requirement already satisfied: torch in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from medmnist) (2.0.0)\n", - "Requirement already satisfied: fire in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from medmnist) (0.5.0)\n", - "Requirement already satisfied: numpy in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from medmnist) (1.24.2)\n", - "Requirement already satisfied: torchvision in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from medmnist) (0.15.1)\n", - "Requirement already satisfied: tqdm in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from medmnist) (4.65.0)\n", - "Requirement already satisfied: pandas in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from medmnist) (1.5.3)\n", - "Requirement already satisfied: scikit-learn in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from medmnist) (1.2.2)\n", - "Requirement already satisfied: Pillow in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from medmnist) (9.5.0)\n", - "Requirement already satisfied: scikit-image in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from medmnist) (0.20.0)\n", - "Requirement already satisfied: typing-extensions in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (4.5.0)\n", - "Requirement already satisfied: jinja2 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (3.1.2)\n", - "Requirement already satisfied: nvidia-cublas-cu11==11.10.3.66; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (11.10.3.66)\n", - "Requirement already satisfied: filelock in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (3.12.0)\n", - "Requirement already satisfied: nvidia-cusparse-cu11==11.7.4.91; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (11.7.4.91)\n", - "Requirement already satisfied: sympy in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (1.11.1)\n", - "Requirement already satisfied: triton==2.0.0; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (2.0.0)\n", - "Requirement already satisfied: nvidia-nvtx-cu11==11.7.91; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (11.7.91)\n", - "Requirement already satisfied: nvidia-cufft-cu11==10.9.0.58; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (10.9.0.58)\n", - "Requirement already satisfied: nvidia-cuda-runtime-cu11==11.7.99; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (11.7.99)\n", - "Requirement already satisfied: nvidia-curand-cu11==10.2.10.91; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (10.2.10.91)\n", - "Requirement already satisfied: nvidia-cuda-cupti-cu11==11.7.101; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (11.7.101)\n", - "Requirement already satisfied: nvidia-cusolver-cu11==11.4.0.1; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (11.4.0.1)\n", - "Requirement already satisfied: nvidia-nccl-cu11==2.14.3; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (2.14.3)\n", - "Requirement already satisfied: nvidia-cuda-nvrtc-cu11==11.7.99; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (11.7.99)\n", - "Requirement already satisfied: networkx in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (3.1)\n", - "Requirement already satisfied: nvidia-cudnn-cu11==8.5.0.96; platform_system == \"Linux\" and platform_machine == \"x86_64\" in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torch->medmnist) (8.5.0.96)\n", - "Requirement already satisfied: six in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from fire->medmnist) (1.16.0)\n", - "Requirement already satisfied: termcolor in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from fire->medmnist) (2.3.0)\n", - "Requirement already satisfied: requests in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from torchvision->medmnist) (2.28.2)\n", - "Requirement already satisfied: python-dateutil>=2.8.1 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from pandas->medmnist) (2.8.2)\n", - "Requirement already satisfied: pytz>=2020.1 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from pandas->medmnist) (2023.2)\n", - "Requirement already satisfied: joblib>=1.1.1 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from scikit-learn->medmnist) (1.2.0)\n", - "Requirement already satisfied: threadpoolctl>=2.0.0 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from scikit-learn->medmnist) (3.1.0)\n", - "Requirement already satisfied: scipy>=1.3.2 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from scikit-learn->medmnist) (1.9.1)\n", - "Requirement already satisfied: packaging>=20.0 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from scikit-image->medmnist) (23.0)\n", - "Requirement already satisfied: lazy_loader>=0.1 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from scikit-image->medmnist) (0.2)\n", - "Requirement already satisfied: tifffile>=2019.7.26 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from scikit-image->medmnist) (2023.4.12)\n", - "Requirement already satisfied: PyWavelets>=1.1.1 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from scikit-image->medmnist) (1.4.1)\n", - "Requirement already satisfied: imageio>=2.4.1 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from scikit-image->medmnist) (2.28.0)\n", - "Requirement already satisfied: MarkupSafe>=2.0 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from jinja2->torch->medmnist) (2.1.2)\n", - "Requirement already satisfied: wheel in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from nvidia-cublas-cu11==11.10.3.66; platform_system == \"Linux\" and platform_machine == \"x86_64\"->torch->medmnist) (0.40.0)\n", - "Requirement already satisfied: setuptools in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from nvidia-cublas-cu11==11.10.3.66; platform_system == \"Linux\" and platform_machine == \"x86_64\"->torch->medmnist) (67.7.2)\n", - "Requirement already satisfied: mpmath>=0.19 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from sympy->torch->medmnist) (1.3.0)\n", - "Requirement already satisfied: cmake in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from triton==2.0.0; platform_system == \"Linux\" and platform_machine == \"x86_64\"->torch->medmnist) (3.26.3)\n", - "Requirement already satisfied: lit in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from triton==2.0.0; platform_system == \"Linux\" and platform_machine == \"x86_64\"->torch->medmnist) (16.0.2)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from requests->torchvision->medmnist) (3.1.0)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from requests->torchvision->medmnist) (2022.12.7)\n", - "Requirement already satisfied: idna<4,>=2.5 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from requests->torchvision->medmnist) (3.4)\n", - "Requirement already satisfied: urllib3<1.27,>=1.21.1 in /home/bjklemme/openflvenv/lib/python3.8/site-packages (from requests->torchvision->medmnist) (1.26.15)\n" - ] - } - ], + "outputs": [], "source": [ "!pip install medmnist" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "d0570122", "metadata": {}, "outputs": [], @@ -98,7 +41,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "22ba64da", "metadata": {}, "outputs": [], @@ -119,18 +62,10 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "d657e463", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "E0427 19:14:27.775750139 26587 http_proxy.cc:92] 'https' scheme not supported in proxy URI\n" - ] - } - ], + "outputs": [], "source": [ "# Create a federation\n", "from openfl.interface.interactive_api.federation import Federation\n", @@ -152,35 +87,10 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "47dcfab3", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'env_one': {'shard_info': node_info {\n", - " name: \"env_one\"\n", - " }\n", - " shard_description: \"MedMNIST dataset, shard number 1 out of 1\"\n", - " sample_shape: \"28\"\n", - " sample_shape: \"28\"\n", - " sample_shape: \"3\"\n", - " target_shape: \"1\"\n", - " target_shape: \"1\",\n", - " 'is_online': True,\n", - " 'is_experiment_running': False,\n", - " 'last_updated': '2023-04-27 19:13:57',\n", - " 'current_time': '2023-04-27 19:14:27',\n", - " 'valid_duration': seconds: 120,\n", - " 'experiment_name': 'ExperimentName Mock'}}" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "shard_registry = federation.get_shard_registry()\n", "shard_registry" @@ -188,21 +98,10 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "a2a6c237", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'Sample shape: (28, 28, 3), target shape: (1, 1)'" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# First, request a dummy_shard_desc that holds information about the federated dataset \n", "dummy_shard_desc = federation.get_dummy_shard_descriptor(size=10)\n", @@ -221,7 +120,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "fc88700a", "metadata": {}, "outputs": [], @@ -239,7 +138,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "e0377d3a", "metadata": {}, "outputs": [], @@ -267,7 +166,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "f0dc457e", "metadata": {}, "outputs": [], @@ -280,7 +179,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "09ba2f64", "metadata": {}, "outputs": [], @@ -323,7 +222,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "db2d563e", "metadata": {}, "outputs": [], @@ -393,7 +292,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "4af5c4c2", "metadata": {}, "outputs": [], @@ -403,18 +302,10 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "7f63908e", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "torch.Size([10, 3, 28, 28]) torch.Size([10, 1, 1])\n" - ] - } - ], + "outputs": [], "source": [ "fed_dataset.shard_descriptor = dummy_shard_desc\n", "for i, (sample, target) in enumerate(fed_dataset.get_train_loader()):\n", @@ -431,7 +322,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "id": "8477a001", "metadata": {}, "outputs": [], @@ -498,53 +389,10 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "id": "f2154486", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Net(\n", - " (layer1): Sequential(\n", - " (0): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1))\n", - " (1): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU()\n", - " )\n", - " (layer2): Sequential(\n", - " (0): Conv2d(16, 16, kernel_size=(3, 3), stride=(1, 1))\n", - " (1): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU()\n", - " (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", - " )\n", - " (layer3): Sequential(\n", - " (0): Conv2d(16, 64, kernel_size=(3, 3), stride=(1, 1))\n", - " (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU()\n", - " )\n", - " (layer4): Sequential(\n", - " (0): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1))\n", - " (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU()\n", - " )\n", - " (layer5): Sequential(\n", - " (0): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU()\n", - " (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", - " )\n", - " (fc): Sequential(\n", - " (0): Linear(in_features=1024, out_features=128, bias=True)\n", - " (1): ReLU()\n", - " (2): Linear(in_features=128, out_features=128, bias=True)\n", - " (3): ReLU()\n", - " (4): Linear(in_features=128, out_features=8, bias=True)\n", - " )\n", - ")\n" - ] - } - ], + "outputs": [], "source": [ "print(model)" ] @@ -559,7 +407,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "id": "59831bcd", "metadata": {}, "outputs": [], @@ -583,7 +431,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "id": "4ff463bd", "metadata": {}, "outputs": [], @@ -604,9 +452,10 @@ " model.to(device)\n", " \n", " for inputs, targets in train_loader:\n", - " \n", + " \n", + " optimizer.set_old_weights(list(model.parameters()))\n", " optimizer.zero_grad()\n", - " optimizer.set_old_weights(model.state_dict())\n", + " \n", " outputs = model(inputs.to(device))\n", " \n", " if task == 'multi-label, binary-class':\n", @@ -681,7 +530,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "id": "d41b7896", "metadata": {}, "outputs": [], @@ -693,162 +542,12 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "id": "41b44de9", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
[19:14:28] INFO     Building 🡆 Object CloudpickleSerializer from openfl.plugins.interface_serializer.cloudpickle_serializer Module.                  plan.py:171\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2;36m[19:14:28]\u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Building \u001b[31m🡆\u001b[0m Object \u001b[31mCloudpickleSerializer\u001b[0m from \u001b[31mopenfl.plugins.interface_serializer.cloudpickle_serializer\u001b[0m Module. \u001b]8;id=588775;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py\u001b\\\u001b[2mplan.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=543696;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py#171\u001b\\\u001b[2m171\u001b[0m\u001b]8;;\u001b\\\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
           INFO     Building 🡆 Object FrameworkAdapterPlugin from openfl.plugins.frameworks_adapters.pytorch_adapter Module.                         plan.py:171\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Building \u001b[31m🡆\u001b[0m Object \u001b[31mFrameworkAdapterPlugin\u001b[0m from \u001b[31mopenfl.plugins.frameworks_adapters.pytorch_adapter\u001b[0m Module. \u001b]8;id=630801;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py\u001b\\\u001b[2mplan.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=742466;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py#171\u001b\\\u001b[2m171\u001b[0m\u001b]8;;\u001b\\\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/bjklemme/openflvenv/lib/python3.8/site-packages/_distutils_hack/__init__.py:33: UserWarning: Setuptools is replacing distutils.\n", - " warnings.warn(\"Setuptools is replacing distutils.\")\n" - ] - }, - { - "data": { - "text/html": [ - "
           INFO     Starting experiment!                                                                                                       experiment.py:245\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Starting experiment! \u001b]8;id=18397;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/interface/interactive_api/experiment.py\u001b\\\u001b[2mexperiment.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=703756;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/interface/interactive_api/experiment.py#245\u001b\\\u001b[2m245\u001b[0m\u001b]8;;\u001b\\\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
           INFO     FL-Plan hash is 101ce5e38ae55d2d65bd01ed1ff1d23be7f216cc2ba17915b2fa2da83fb8f31b6e0bf0db50b5cd853d6eb24ff2813eda                 plan.py:235\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m FL-Plan hash is \u001b[34m101ce5e38ae55d2d65bd01ed1ff1d23be7f216cc2ba17915b2fa2da83fb8f31b6e0bf0db50b5cd853d6eb24ff2813eda\u001b[0m \u001b]8;id=915780;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py\u001b\\\u001b[2mplan.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=260848;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py#235\u001b\\\u001b[2m235\u001b[0m\u001b]8;;\u001b\\\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
           INFO     FL-Plan hash is 101ce5e38ae55d2d65bd01ed1ff1d23be7f216cc2ba17915b2fa2da83fb8f31b6e0bf0db50b5cd853d6eb24ff2813eda                 plan.py:235\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m FL-Plan hash is \u001b[34m101ce5e38ae55d2d65bd01ed1ff1d23be7f216cc2ba17915b2fa2da83fb8f31b6e0bf0db50b5cd853d6eb24ff2813eda\u001b[0m \u001b]8;id=797366;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py\u001b\\\u001b[2mplan.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=292809;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py#235\u001b\\\u001b[2m235\u001b[0m\u001b]8;;\u001b\\\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
           INFO     Building 🡆 Object CoreTaskRunner from openfl.federated.task.task_runner Module.                                                  plan.py:171\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Building \u001b[31m🡆\u001b[0m Object \u001b[31mCoreTaskRunner\u001b[0m from \u001b[31mopenfl.federated.task.task_runner\u001b[0m Module. \u001b]8;id=534726;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py\u001b\\\u001b[2mplan.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=824701;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py#171\u001b\\\u001b[2m171\u001b[0m\u001b]8;;\u001b\\\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
           INFO     Building 🡆 Object FrameworkAdapterPlugin from openfl.plugins.frameworks_adapters.pytorch_adapter Module.                         plan.py:171\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Building \u001b[31m🡆\u001b[0m Object \u001b[31mFrameworkAdapterPlugin\u001b[0m from \u001b[31mopenfl.plugins.frameworks_adapters.pytorch_adapter\u001b[0m Module. \u001b]8;id=559039;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py\u001b\\\u001b[2mplan.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=365234;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/federated/plan/plan.py#171\u001b\\\u001b[2m171\u001b[0m\u001b]8;;\u001b\\\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
           WARNING  tried to remove tensor: __opt_state_needed not present in the tensor dict                                                       utils.py:172\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[31mWARNING \u001b[0m tried to remove tensor: __opt_state_needed not present in the tensor dict \u001b]8;id=872466;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/utilities/utils.py\u001b\\\u001b[2mutils.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=142002;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/utilities/utils.py#172\u001b\\\u001b[2m172\u001b[0m\u001b]8;;\u001b\\\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
           WARNING  tried to remove tensor: __opt_state_needed not present in the tensor dict                                                       utils.py:172\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[31mWARNING \u001b[0m tried to remove tensor: __opt_state_needed not present in the tensor dict \u001b]8;id=290143;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/utilities/utils.py\u001b\\\u001b[2mutils.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=939997;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/utilities/utils.py#172\u001b\\\u001b[2m172\u001b[0m\u001b]8;;\u001b\\\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
           INFO     SetNewExperiment                                                                                                      director_client.py:209\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m SetNewExperiment \u001b]8;id=673240;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/transport/grpc/director_client.py\u001b\\\u001b[2mdirector_client.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=46132;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/transport/grpc/director_client.py#209\u001b\\\u001b[2m209\u001b[0m\u001b]8;;\u001b\\\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
           INFO     Experiment was submitted to the director!                                                                                  experiment.py:259\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Experiment was submitted to the director! \u001b]8;id=978253;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/interface/interactive_api/experiment.py\u001b\\\u001b[2mexperiment.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=636446;file:///home/bjklemme/openflvenv/lib/python3.8/site-packages/openfl/interface/interactive_api/experiment.py#259\u001b\\\u001b[2m259\u001b[0m\u001b]8;;\u001b\\\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "metadata": { + "scrolled": true + }, + "outputs": [], "source": [ "# The following command zips the workspace and python requirements to be transfered to collaborator nodes\n", "fl_experiment.start(model_provider=MI, \n", @@ -861,7 +560,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "id": "01fa7cea", "metadata": { "scrolled": true From d10b2aec56bbb22462cd0ac7d229b8658262b80e Mon Sep 17 00:00:00 2001 From: Beverly Klemme Date: Fri, 28 Apr 2023 13:23:37 -0700 Subject: [PATCH 9/9] [Enhancement: 506] Add an example that uses the FedProx optimizer in the interative_api This duplicates the MedNIST_2D example in the interative api but changes it to use the FedProx optimizer. Fixes: #506 Signed-off-by: Klemme, Beverly Signed-off-by: Baker, Grant Signed-off-by: ELizabeth Simon, Neethu Signed-off-by: Jillela, Emmanuel --- .../workspace/Pytorch_FedProx_MedMNIST_2D.ipynb | 1 - 1 file changed, 1 deletion(-) diff --git a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb index a4aadf8e1d..3bd9dd0d5f 100644 --- a/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb +++ b/openfl-tutorials/interactive_api/PyTorch_FedProx_MNIST/workspace/Pytorch_FedProx_MedMNIST_2D.ipynb @@ -473,7 +473,6 @@ " return {'train_loss': np.mean(total_loss),}\n", "\n", "\n", - "\n", "val_custom_params={'criterion':criterion, \n", " 'task':task}\n", "\n",