MEDfl 2.0.4.dev0__py3-none-any.whl → 2.0.4.dev2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. MEDfl/rw/client.py +98 -29
  2. MEDfl/rw/model.py +46 -74
  3. MEDfl/rw/server.py +71 -18
  4. MEDfl/rw/strategy.py +73 -78
  5. {medfl-2.0.4.dev0.dist-info → MEDfl-2.0.4.dev2.dist-info}/METADATA +2 -14
  6. MEDfl-2.0.4.dev2.dist-info/RECORD +36 -0
  7. {medfl-2.0.4.dev0.dist-info → MEDfl-2.0.4.dev2.dist-info}/WHEEL +1 -1
  8. MEDfl/rw/rwConfig.py +0 -21
  9. MEDfl/rw/verbose_server.py +0 -21
  10. Medfl/LearningManager/__init__.py +0 -13
  11. Medfl/LearningManager/client.py +0 -150
  12. Medfl/LearningManager/dynamicModal.py +0 -287
  13. Medfl/LearningManager/federated_dataset.py +0 -60
  14. Medfl/LearningManager/flpipeline.py +0 -192
  15. Medfl/LearningManager/model.py +0 -223
  16. Medfl/LearningManager/params.yaml +0 -14
  17. Medfl/LearningManager/params_optimiser.py +0 -442
  18. Medfl/LearningManager/plot.py +0 -229
  19. Medfl/LearningManager/server.py +0 -181
  20. Medfl/LearningManager/strategy.py +0 -82
  21. Medfl/LearningManager/utils.py +0 -331
  22. Medfl/NetManager/__init__.py +0 -10
  23. Medfl/NetManager/database_connector.py +0 -43
  24. Medfl/NetManager/dataset.py +0 -92
  25. Medfl/NetManager/flsetup.py +0 -320
  26. Medfl/NetManager/net_helper.py +0 -254
  27. Medfl/NetManager/net_manager_queries.py +0 -142
  28. Medfl/NetManager/network.py +0 -194
  29. Medfl/NetManager/node.py +0 -184
  30. Medfl/__init__.py +0 -3
  31. Medfl/scripts/__init__.py +0 -2
  32. Medfl/scripts/base.py +0 -30
  33. Medfl/scripts/create_db.py +0 -126
  34. medfl-2.0.4.dev0.dist-info/RECORD +0 -62
  35. {medfl-2.0.4.dev0.dist-info/licenses → MEDfl-2.0.4.dev2.dist-info}/LICENSE +0 -0
  36. {medfl-2.0.4.dev0.dist-info → MEDfl-2.0.4.dev2.dist-info}/top_level.txt +0 -0
@@ -1,192 +0,0 @@
1
- import datetime
2
- from typing import List
3
- import json
4
- import pandas as pd
5
-
6
-
7
- # File: create_query.py
8
- from sqlalchemy import text
9
- from torch.utils.data import DataLoader, TensorDataset
10
- import torch
11
-
12
- from MEDfl.LearningManager.server import FlowerServer
13
- from MEDfl.LearningManager.utils import params, test
14
- from MEDfl.NetManager.net_helper import get_flpipeline_from_name
15
- from MEDfl.NetManager.net_manager_queries import (CREATE_FLPIPELINE_QUERY,
16
- DELETE_FLPIPELINE_QUERY , CREATE_TEST_RESULTS_QUERY)
17
- from MEDfl.NetManager.database_connector import DatabaseManager
18
-
19
- def create_query(name, description, creation_date, result):
20
- query = text(
21
- f"INSERT INTO FLpipeline(name, description, creation_date, results) "
22
- f"VALUES ('{name}', '{description}', '{creation_date}', '{result}')"
23
- )
24
- return query
25
-
26
-
27
-
28
- class FLpipeline:
29
- """
30
- FLpipeline class for managing Federated Learning pipelines.
31
-
32
- Attributes:
33
- name (str): The name of the FLpipeline.
34
- description (str): A description of the FLpipeline.
35
- server (FlowerServer): The FlowerServer object associated with the FLpipeline.
36
-
37
- Methods:
38
- __init__(self, name: str, description: str, server: FlowerServer) -> None:
39
- Initialize FLpipeline with the specified name, description, and server.
40
-
41
-
42
- """
43
-
44
- def __init__(
45
- self, name: str, description: str, server: FlowerServer
46
- ) -> None:
47
- self.name = name
48
- self.description = description
49
- self.server = server
50
- self.validate()
51
-
52
- db_manager = DatabaseManager()
53
- db_manager.connect()
54
- self.eng = db_manager.get_connection()
55
-
56
- def validate(self) -> None:
57
- """
58
- Validate the name, description, and server attributes.
59
- Raises:
60
- TypeError: If the name is not a string, the description is not a string,
61
- or the server is not a FlowerServer object.
62
- """
63
- if not isinstance(self.name, str):
64
- raise TypeError("name argument must be a string")
65
-
66
- if not isinstance(self.description, str):
67
- raise TypeError("description argument must be a string")
68
-
69
- # if not isinstance(self.server, FlowerServer):
70
- # raise TypeError("server argument must be a FlowerServer")
71
-
72
- def create(self, result: str) -> None:
73
- """
74
- Create a new FLpipeline entry in the database with the given result.
75
-
76
- Args:
77
- result (str): The result string to store in the database.
78
-
79
- """
80
- creation_date = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
81
- query = CREATE_FLPIPELINE_QUERY.format(
82
- name=self.name,
83
- description=self.description,
84
- creation_date=creation_date,
85
- result=result,
86
- )
87
- self.eng.execute(text(query))
88
- self.id = get_flpipeline_from_name(self.name)
89
- try:
90
- self.server.fed_dataset.update(
91
- FLpipeId=self.id, FedId=self.server.fed_dataset.id
92
- )
93
- except:
94
- pass
95
-
96
- def delete(self) -> None:
97
- """
98
- Delete the FLpipeline entry from the database based on its name.
99
-
100
- Note: This is a placeholder method and needs to be implemented based on your specific database setup.
101
-
102
- """
103
- # Placeholder code for deleting the FLpipeline entry from the database based on the name.
104
- # You need to implement the actual deletion based on your database setup.
105
- self.eng.execute(DELETE_FLPIPELINE_QUERY.format(self.name))
106
-
107
-
108
- def test_by_node(self, node_name: str, test_frac=1) -> dict:
109
- """
110
- Test the FLpipeline by node with the specified test_frac.
111
-
112
- Args:
113
- node_name (str): The name of the node to test.
114
- test_frac (float, optional): The fraction of the test data to use. Default is 1.
115
-
116
- Returns:
117
- dict: A dictionary containing the node name and the classification report.
118
-
119
- """
120
- idx = self.server.fed_dataset.test_nodes.index(node_name)
121
- global_model, test_loader = (
122
- self.server.global_model,
123
- self.server.fed_dataset.testloaders[idx],
124
- )
125
-
126
- # Move model to GPU if available
127
- device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
128
- global_model.model.to(device)
129
-
130
- # Prepare test data
131
- test_data = test_loader.dataset
132
- num_samples = int(test_frac * len(test_data))
133
- test_data = TensorDataset(test_data[:num_samples][0].to(device), test_data[:num_samples][1].to(device))
134
-
135
- # Create DataLoader for test data
136
- test_loader = DataLoader(test_data, batch_size=params["test_batch_size"])
137
-
138
- # Perform testing
139
- classification_report = test(model=global_model.model, test_loader=test_loader, device=device)
140
-
141
- return {
142
- "node_name": node_name,
143
- "classification_report": str(classification_report),
144
- }
145
-
146
-
147
- def auto_test(self, test_frac=1) -> List[dict]:
148
- """
149
- Automatically test the FLpipeline on all nodes with the specified test_frac.
150
-
151
- Args:
152
- test_frac (float, optional): The fraction of the test data to use. Default is 1.
153
-
154
- Returns:
155
- List[dict]: A list of dictionaries containing the node names and the classification reports.
156
-
157
- """
158
- result = [
159
- self.test_by_node(node, test_frac)
160
- for node in self.server.fed_dataset.test_nodes
161
- ]
162
- self.create("\n".join(str(res).replace("'", '"') for res in result))
163
-
164
- # stockage des resultats des tests
165
- for entry in result:
166
- node_name = entry['node_name']
167
- classification_report_str = entry['classification_report']
168
-
169
- # Convert the 'classification_report' string to a dictionary
170
- classification_report_dict = json.loads(classification_report_str.replace("'", "\""))
171
- try:
172
- # Insert record into the 'testResults' table
173
- query = CREATE_TEST_RESULTS_QUERY.format(
174
- pipelineId = self.id,
175
- nodeName = node_name ,
176
- confusion_matrix = json.dumps(classification_report_dict['confusion matrix']),
177
- accuracy =classification_report_dict['Accuracy'] ,
178
- sensivity = classification_report_dict['Sensitivity/Recall'] ,
179
- ppv = classification_report_dict['PPV/Precision'] ,
180
- npv= classification_report_dict['NPV'] ,
181
- f1score= classification_report_dict['F1-score'] ,
182
- fpr= classification_report_dict['False positive rate'] ,
183
- tpr= classification_report_dict['True positive rate']
184
- )
185
- self.eng.execute(text(query))
186
- except Exception as e:
187
- # This block will catch any other exceptions
188
- print(f"An unexpected error occurred: {e}")
189
-
190
-
191
-
192
- return result
@@ -1,223 +0,0 @@
1
- #!/usr/bin/env python3
2
- # froked from https://github.com/pythonlessons/mltu/blob/main/mltu/torch/model.py
3
-
4
- import typing
5
- from collections import OrderedDict
6
- from typing import Dict, List, Optional, Tuple
7
-
8
- import numpy as np
9
- import torch
10
- import torch.nn as nn
11
- from sklearn.metrics import accuracy_score,roc_auc_score
12
-
13
- from .utils import params
14
-
15
-
16
- class Model:
17
- """
18
- Model class for training and testing PyTorch neural networks.
19
-
20
- Attributes:
21
- model (torch.nn.Module): PyTorch neural network.
22
- optimizer (torch.optim.Optimizer): PyTorch optimizer.
23
- criterion (typing.Callable): Loss function.
24
- """
25
-
26
- def __init__(
27
- self,
28
- model: torch.nn.Module,
29
- optimizer: torch.optim.Optimizer,
30
- criterion: typing.Callable,
31
- ) -> None:
32
- """
33
- Initialize Model class with the specified model, optimizer, and criterion.
34
-
35
- Args:
36
- model (torch.nn.Module): PyTorch neural network.
37
- optimizer (torch.optim.Optimizer): PyTorch optimizer.
38
- criterion (typing.Callable): Loss function.
39
- """
40
- self.model = model
41
- self.optimizer = optimizer
42
- self.criterion = criterion
43
- # Get device on which model is running
44
- self.validate()
45
-
46
- def validate(self) -> None:
47
- """
48
- Validate model and optimizer.
49
- """
50
- if not isinstance(self.model, torch.nn.Module):
51
- raise TypeError("model argument must be a torch.nn.Module")
52
-
53
- if not isinstance(self.optimizer, torch.optim.Optimizer):
54
- raise TypeError(
55
- "optimizer argument must be a torch.optim.Optimizer"
56
- )
57
-
58
- def get_parameters(self) -> List[np.ndarray]:
59
- """
60
- Get the parameters of the model as a list of NumPy arrays.
61
-
62
- Returns:
63
- List[np.ndarray]: The parameters of the model as a list of NumPy arrays.
64
- """
65
- return [
66
- val.cpu().numpy() for _, val in self.model.state_dict().items()
67
- ]
68
-
69
- def set_parameters(self, parameters: List[np.ndarray]) -> None:
70
- """
71
- Set the parameters of the model from a list of NumPy arrays.
72
-
73
- Args:
74
- parameters (List[np.ndarray]): The parameters to be set.
75
- """
76
- params_dict = zip(self.model.state_dict().keys(), parameters)
77
- state_dict = OrderedDict({k: torch.Tensor(v) for k, v in params_dict})
78
- self.model.load_state_dict(state_dict, strict=True)
79
-
80
- def train(
81
- self, train_loader, epoch, device, privacy_engine, diff_priv=False
82
- ) -> float:
83
- """
84
- Train the model on the given train_loader for one epoch.
85
-
86
- Args:
87
- train_loader: The data loader for training data.
88
- epoch (int): The current epoch number.
89
- device: The device on which to perform the training.
90
- privacy_engine: The privacy engine used for differential privacy (if enabled).
91
- diff_priv (bool, optional): Whether differential privacy is used. Default is False.
92
-
93
- Returns:
94
- float: The value of epsilon used in differential privacy.
95
- """
96
- self.model.train()
97
- epsilon = 0
98
- losses = []
99
- top1_acc = []
100
-
101
- for i, (X_train, y_train) in enumerate(train_loader):
102
- X_train, y_train = X_train.to(device), y_train.to(device)
103
-
104
- self.optimizer.zero_grad()
105
-
106
- # compute output
107
- y_hat = torch.squeeze(self.model(X_train), 1)
108
- loss = self.criterion(y_hat, y_train)
109
-
110
- preds = np.argmax(y_hat.detach().cpu().numpy(), axis=0)
111
- labels = y_train.detach().cpu().numpy()
112
-
113
- # measure accuracy and record loss
114
- acc = (preds == labels).mean()
115
-
116
- losses.append(loss.item())
117
- top1_acc.append(acc)
118
-
119
- loss.backward()
120
- self.optimizer.step()
121
-
122
- if diff_priv:
123
- epsilon = privacy_engine.get_epsilon(float(params["DELTA"]))
124
-
125
- if (i + 1) % 10 == 0:
126
- if diff_priv:
127
- epsilon = privacy_engine.get_epsilon(float(params["DELTA"]))
128
- print(
129
- f"\tTrain Epoch: {epoch} \t"
130
- f"Loss: {np.mean(losses):.6f} "
131
- f"Acc@1: {np.mean(top1_acc) * 100:.6f} "
132
- f"(ε = {epsilon:.2f}, δ = {params['DELTA']})"
133
- )
134
- else:
135
- print(
136
- f"\tTrain Epoch: {epoch} \t"
137
- f"Loss: {np.mean(losses):.6f} "
138
- f"Acc@1: {np.mean(top1_acc) * 100:.6f}"
139
- )
140
-
141
- return epsilon
142
-
143
- def evaluate(self, val_loader, device=torch.device("cpu")) -> Tuple[float, float]:
144
- """
145
- Evaluate the model on the given validation data.
146
-
147
- Args:
148
- val_loader: The data loader for validation data.
149
- device: The device on which to perform the evaluation. Default is 'cpu'.
150
-
151
- Returns:
152
- Tuple[float, float]: The evaluation loss and accuracy.
153
- """
154
- correct, total, loss, accuracy, auc = 0, 0, 0.0, [], []
155
- self.model.eval()
156
-
157
- with torch.no_grad():
158
- for X_test, y_test in val_loader:
159
- X_test, y_test = X_test.to(device), y_test.to(device) # Move data to device
160
-
161
- y_hat = torch.squeeze(self.model(X_test), 1)
162
-
163
-
164
- criterion = self.criterion.to(y_hat.device)
165
- loss += criterion(y_hat, y_test).item()
166
-
167
-
168
- # Move y_hat to CPU for accuracy computation
169
- y_hat_cpu = y_hat.cpu().detach().numpy()
170
- accuracy.append(accuracy_score(y_test.cpu().numpy(), y_hat_cpu.round()))
171
-
172
- # Move y_test to CPU for AUC computation
173
- y_test_cpu = y_test.cpu().numpy()
174
- y_prob_cpu = y_hat.cpu().detach().numpy()
175
- if (len(np.unique(y_test_cpu)) != 1):
176
- auc.append(roc_auc_score(y_test_cpu, y_prob_cpu))
177
-
178
- total += y_test.size(0)
179
- correct += np.sum(y_hat_cpu.round() == y_test_cpu)
180
-
181
- loss /= len(val_loader.dataset)
182
- return loss, np.mean(accuracy), np.mean(auc)
183
-
184
-
185
- @staticmethod
186
- def save_model(model , model_name:str):
187
- """
188
- Saves a PyTorch model to a file.
189
-
190
- Args:
191
- model (torch.nn.Module): PyTorch model to be saved.
192
- model_name (str): Name of the model file.
193
-
194
- Raises:
195
- Exception: If there is an issue during the saving process.
196
-
197
- Returns:
198
- None
199
- """
200
- try:
201
- torch.save(model, '../../notebooks/.ipynb_checkpoints/trainedModels/' + model_name + ".pth")
202
- except Exception as e:
203
- raise Exception(f"Error saving the model: {str(e)}")
204
-
205
- @staticmethod
206
- def load_model(model_path: str):
207
- """
208
- Loads a PyTorch model from a file.
209
-
210
- Args:
211
- model_path (str): Path to the model file to be loaded.
212
-
213
- Returns:
214
- torch.nn.Module: Loaded PyTorch model.
215
- """
216
- # Ensure models are loaded onto the CPU when CUDA is not available
217
- if torch.cuda.is_available():
218
- loaded_model = torch.load(model_path)
219
- else:
220
- loaded_model = torch.load(model_path, map_location=torch.device('cpu'))
221
- return loaded_model
222
-
223
-
@@ -1,14 +0,0 @@
1
- DELTA: 1.0e-05
2
- EPSILON: 5.0
3
- MAX_GRAD_NORM: 1.0
4
- diff_privacy: true
5
- lr: 0.01
6
- min_evalclient: 2
7
- num_rounds: 12
8
- optimizer: SGD
9
- path_to_master_csv: /home/local/USHERBROOKE/saho6810/MEDfl/code/MEDfl/notebooks/data/masterDataSet/Mimic_ouael.csv
10
- path_to_test_csv: /home/local/USHERBROOKE/saho6810/MEDfl/code/MEDfl/notebooks/data/masterDataSet/Mimic_train.csv
11
- task: BinaryClassification
12
- test_batch_size: 1
13
- train_batch_size: 32
14
- train_epochs: 116