Skip to content

Commit 21e415f

Browse files
authored
Merge branch 'master' into 486-add-model-package
2 parents 60aa6ab + fc2535f commit 21e415f

File tree

15 files changed

+780
-10
lines changed

15 files changed

+780
-10
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ ci:
99

1010
repos:
1111
- repo: https://github.com/pre-commit/pre-commit-hooks
12-
rev: v4.0.1
12+
rev: v4.1.0
1313
hooks:
1414
- id: end-of-file-fixer
1515
- id: trailing-whitespace

3d_segmentation/brats_segmentation_3d.ipynb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -885,7 +885,7 @@
885885
],
886886
"metadata": {
887887
"kernelspec": {
888-
"display_name": "Python 3 (ipykernel)",
888+
"display_name": "Python 3",
889889
"language": "python",
890890
"name": "python3"
891891
},
@@ -899,7 +899,7 @@
899899
"name": "python",
900900
"nbconvert_exporter": "python",
901901
"pygments_lexer": "ipython3",
902-
"version": "3.8.12"
902+
"version": "3.7.10"
903903
}
904904
},
905905
"nbformat": 4,
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Federated learning with [OpenFL](https://github.com/intel/openfl)
2+
3+
## Introduction
4+
5+
This repository contains an end-to-end Federated training example based on [MONAI](https://github.com/Project-MONAI/MONAI) 2d mednist registration [tutorial](../../../2d_registration/registration_mednist.ipynb). In this federated learning experiment we will run two collabolators with separated samples from MedNIST dataset.
6+
7+
8+
This example requires Python 3.6 - 3.8
9+
10+
This folder contains:
11+
* `director` folder for running director server.
12+
* `envoy` folder for running collaborators.
13+
* `workspace` folder with jupyter-notebook for running an FL experiment.
14+
* `requirements.txt` with dependencies.
15+
16+
## Installation
17+
First of all, clone the repository and go to [openfl_mednist_2d_registration](./) directory:
18+
19+
```sh
20+
git clone https://github.com/Project-MONAI/tutorials.git
21+
cd tutorials/federated_learning/openfl/openfl_mednist_2d_registration/
22+
```
23+
Then follow the steps to set up federation and run your experiment.
24+
### Virtual Environment
25+
26+
We will use several `Python3.8` virtual environments with `openfl`, `monai` and other packages.
27+
28+
## Set up your federation
29+
You can set up your federation in one of the following modes: with or without TLS connections. For simplicity, here are the commands for running a federation without TLS and locally, so the connection bettween envoys is over the local network). Information on how to establish a secure TLS connection in federation can be found in the related [section](https://openfl.readthedocs.io/en/latest/running_the_federation.html#optional-step-create-pki-certificates-using-step-ca) of the documentation.
30+
31+
32+
### 1\. Start director:
33+
- Create director virtual environment and activate it:
34+
```sh
35+
python3.8 -m venv director_env
36+
source director_env/bin/activate
37+
```
38+
39+
- Install required packages:
40+
```sh
41+
pip install -r requirements.txt
42+
```
43+
44+
- Run director:
45+
```sh
46+
cd director
47+
fx director start --disable-tls --director-config-path director_config.yaml
48+
```
49+
After that, the director will be started on `localhost:50051`, this behavior is specified in [director_config.yaml](./director/director_config.yaml)
50+
51+
### 2\. Start first envoy:
52+
- Open a new terminal.
53+
54+
- Before starting the first envoy, we should create a copy of the [envoy](./envoy) folder. Just copy this directory to another location.
55+
56+
```sh
57+
cp -r envoy envoy_two
58+
```
59+
This one will be needed to start the second envoy.
60+
61+
- Create envoy virtual environment and activate it:
62+
```sh
63+
python3.8 -m venv envoy_one_env
64+
source envoy_one_env/bin/activate
65+
```
66+
67+
- Install required packages:
68+
```sh
69+
pip install -r requirements.txt
70+
cd envoy
71+
pip install -r sd_requirements.txt
72+
```
73+
74+
75+
- Run the next command from [envoy](./envoy) directory to start envoy:
76+
```sh
77+
fx envoy start --shard-name env_one --disable-tls --envoy-config-path envoy_config_one.yaml --director-host localhost --director-port 50051
78+
```
79+
80+
The envoy `env_one` will be connected to the director via given host and port. Also, you can choose the GPU to be used during the experiment, you can specify it in [envoy_config_one.yaml](./envoy/envoy_config_one.yaml). By default we will use `CUDA:0` on the `env_one`.
81+
82+
### 3\. Start second envoy:
83+
84+
- Open a new terminal.
85+
86+
- Create envoy virtual environment and activate it:
87+
```sh
88+
python3.8 -m venv envoy_two_env
89+
source envoy_two_env/bin/activate
90+
```
91+
92+
- Install required packages:
93+
```sh
94+
pip install -r requirements.txt
95+
cd envoy_two
96+
pip install -r sd_requirements.txt
97+
```
98+
99+
- Run the next command from `envoy_two` directory to start envoy:
100+
```sh
101+
fx envoy start --shard-name env_two --disable-tls --envoy-config-path envoy_config_two.yaml --director-host localhost --director-port 50051
102+
```
103+
104+
The envoy `env_two` will be connected to the director via given host and port. Also, you can choose the GPU to be used during the experiment, you can specify it in [envoy_config_two.yaml](./envoy/envoy_config_two.yaml). By default we will use `CUDA:1` on the `env_one`.
105+
106+
---
107+
**_NOTE:_** If your want to run this example in distributed mode, you have to change some variables:
108+
109+
- On director node:
110+
111+
- Change `listen_host` director variable to [FQDN](https://en.wikipedia.org/wiki/Fully_qualified_domain_name) name of machine (see [director_config.yaml](./director/director_config.yaml) file).
112+
- Optional: change `listen_port` director variable to any free port (see [director_config.yaml](./director/director_config.yaml) file).
113+
114+
- On envoy nodes:
115+
116+
- Change `--director-host` variable in the command for envoy running on the current director FQDN
117+
- Change the `--director-port` variable in the command for envoy running on the current director port
118+
---
119+
120+
121+
## Running FL 2d mednist registration experiment
122+
Now we are ready to start federated experiment
123+
1. Open a new terminal.
124+
125+
2. Create new virtual environment and activate it:
126+
```sh
127+
python3.8 -m venv workspace_env
128+
source workspace_env/bin/activate
129+
```
130+
131+
3. Install required packages:
132+
```sh
133+
pip install -r requirements.txt
134+
```
135+
136+
4. Run `Monai_MedNIST.ipynb` jupyter notebook:
137+
```sh
138+
cd workspace
139+
jupyter notebook Monai_MedNIST.ipynb
140+
```
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
settings:
2+
listen_host: localhost
3+
listen_port: 50051
4+
sample_shape: ['64', '64', '1']
5+
target_shape: ['64', '64', '1']
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
params:
2+
cuda_devices: [0, ]
3+
4+
optional_plugin_components:
5+
cuda_device_monitor:
6+
template: openfl.plugins.processing_units_monitor.pynvml_monitor.PynvmlCUDADeviceMonitor
7+
settings: []
8+
9+
shard_descriptor:
10+
template: mednist_dataset_shard_descriptor.MedNistShardDescriptor
11+
params:
12+
data_folder: './'
13+
rank_worldsize: 1,2
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
params:
2+
cuda_devices: [1, ]
3+
4+
optional_plugin_components:
5+
cuda_device_monitor:
6+
template: openfl.plugins.processing_units_monitor.pynvml_monitor.PynvmlCUDADeviceMonitor
7+
settings: []
8+
9+
shard_descriptor:
10+
template: mednist_dataset_shard_descriptor.MedNistShardDescriptor
11+
params:
12+
data_folder: './'
13+
rank_worldsize: 2,2
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
"""Mednist Shard Descriptor."""
2+
3+
import logging
4+
from pathlib import Path
5+
from typing import Dict
6+
from typing import List
7+
8+
from monai.apps import MedNISTDataset
9+
10+
from openfl.interface.interactive_api.shard_descriptor import ShardDataset
11+
from openfl.interface.interactive_api.shard_descriptor import ShardDescriptor
12+
13+
logger = logging.getLogger(__name__)
14+
15+
16+
class MedNistShardDataset(ShardDataset):
17+
"""Mednist shard dataset class."""
18+
19+
def __init__(
20+
self,
21+
data_items: List[Dict[str, str]],
22+
rank: int = 1,
23+
worldsize: int = 1,
24+
) -> None:
25+
"""Initialize Mednist Dataset."""
26+
self.rank = rank
27+
self.worldsize = worldsize
28+
self.data_items = data_items[self.rank - 1::self.worldsize] # sharding
29+
30+
def __len__(self) -> int:
31+
"""Return the len of the shard dataset."""
32+
return len(self.data_items)
33+
34+
def __getitem__(self, index: int) -> Dict[str, str]:
35+
"""Return an item by the index."""
36+
return self.data_items[index]
37+
38+
39+
class MedNistShardDescriptor(ShardDescriptor):
40+
"""Shard descriptor class."""
41+
42+
def __init__(
43+
self,
44+
data_folder: str = './',
45+
rank_worldsize: str = '1,1',
46+
**kwargs
47+
) -> None:
48+
"""Initialize MedNistShardDescriptor."""
49+
self.data_folder = data_folder
50+
self.rank, self.worldsize = map(int, rank_worldsize.split(','))
51+
self.download_data()
52+
53+
def download_data(self) -> None:
54+
"""Download prepared shard dataset."""
55+
dataset_train = MedNISTDataset(root_dir=self.data_folder,
56+
section='training',
57+
download=True, transform=None)
58+
dataset_val = MedNISTDataset(root_dir=self.data_folder,
59+
section='validation',
60+
download=True, transform=None)
61+
self.training_datadict = [
62+
{
63+
'fixed_hand': Path(item['image']).absolute(),
64+
'moving_hand': Path(item['image']).absolute()
65+
} for item in dataset_train.data if item['label'] == 4
66+
] # label 4 is for xray hands
67+
self.validation_datadict = [
68+
{
69+
'fixed_hand': Path(item['image']).absolute(),
70+
'moving_hand': Path(item['image']).absolute()
71+
} for item in dataset_val.data if item['label'] == 4
72+
] # label 4 is for xray hands
73+
74+
def get_dataset(self, dataset_type: str) -> MedNistShardDataset:
75+
"""Return a shard dataset by type."""
76+
if dataset_type == 'train':
77+
return MedNistShardDataset(
78+
data_items=self.training_datadict,
79+
rank=self.rank,
80+
worldsize=self.worldsize
81+
)
82+
elif dataset_type == 'validation':
83+
return MedNistShardDataset(
84+
data_items=self.validation_datadict,
85+
rank=self.rank,
86+
worldsize=self.worldsize
87+
)
88+
else:
89+
raise Exception(
90+
"dataset_type should be one of ['train', 'validation']")
91+
92+
@property
93+
def sample_shape(self) -> List[str]:
94+
"""Return the sample shape info."""
95+
return ['64', '64', '1']
96+
97+
@property
98+
def target_shape(self) -> List[str]:
99+
"""Return the target shape info."""
100+
return ['64', '64', '1']
101+
102+
@property
103+
def dataset_description(self) -> str:
104+
"""Return the shard dataset description."""
105+
return (f'Mednist dataset, shard number {self.rank}'
106+
f' out of {self.worldsize}')
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
gdown
2+
pynvml
3+
monai==0.8.0
4+
scikit-image==0.19.0
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
openfl==1.2.1

0 commit comments

Comments
 (0)