MEDfl 2.0.5.dev3__py3-none-any.whl → 2.0.5.dev6__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.
- MEDfl/NetManager/database_connector.py +1 -0
- MEDfl/rw/client.py +54 -25
- MEDfl/rw/server.py +9 -10
- MEDfl/rw/strategy.py +33 -38
- {medfl-2.0.5.dev3.dist-info → medfl-2.0.5.dev6.dist-info}/METADATA +3 -3
- {medfl-2.0.5.dev3.dist-info → medfl-2.0.5.dev6.dist-info}/RECORD +9 -9
- {medfl-2.0.5.dev3.dist-info → medfl-2.0.5.dev6.dist-info}/WHEEL +0 -0
- {medfl-2.0.5.dev3.dist-info → medfl-2.0.5.dev6.dist-info}/licenses/LICENSE +0 -0
- {medfl-2.0.5.dev3.dist-info → medfl-2.0.5.dev6.dist-info}/top_level.txt +0 -0
MEDfl/rw/client.py
CHANGED
|
@@ -35,10 +35,10 @@ class DPConfig:
|
|
|
35
35
|
|
|
36
36
|
def __init__(
|
|
37
37
|
self,
|
|
38
|
-
noise_multiplier
|
|
39
|
-
max_grad_norm
|
|
40
|
-
batch_size
|
|
41
|
-
secure_rng
|
|
38
|
+
noise_multiplier=1.0,
|
|
39
|
+
max_grad_norm=1.0,
|
|
40
|
+
batch_size=32,
|
|
41
|
+
secure_rng=False,
|
|
42
42
|
):
|
|
43
43
|
self.noise_multiplier = noise_multiplier
|
|
44
44
|
self.max_grad_norm = max_grad_norm
|
|
@@ -47,17 +47,46 @@ class DPConfig:
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
class FlowerClient(fl.client.NumPyClient):
|
|
50
|
+
"""
|
|
51
|
+
Federated learning client for MEDfl real-world pipelines.
|
|
52
|
+
|
|
53
|
+
This client:
|
|
54
|
+
- connects to a Flower server,
|
|
55
|
+
- loads local CSV data,
|
|
56
|
+
- applies server-provided schema (features/target),
|
|
57
|
+
- handles train/val/test splits (fractions or test_ids),
|
|
58
|
+
- optionally enables differential privacy with Opacus.
|
|
59
|
+
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
server_address : str
|
|
63
|
+
Address of the Flower server, e.g. ``"127.0.0.1:8080"``.
|
|
64
|
+
data_path : str, optional
|
|
65
|
+
Path to the local CSV file. Default is ``"data/data.csv"``.
|
|
66
|
+
dp_config : DPConfig, optional
|
|
67
|
+
Differential privacy configuration. If ``None``, DP is disabled.
|
|
68
|
+
val_frac : float, optional
|
|
69
|
+
Client-side validation fraction override. If ``None``, use server value.
|
|
70
|
+
test_frac : float, optional
|
|
71
|
+
Client-side test fraction override. If ``None``, use server value.
|
|
72
|
+
id_col : str, optional
|
|
73
|
+
Name of the ID column used when selecting test samples via ``test_ids``.
|
|
74
|
+
test_ids : str, optional
|
|
75
|
+
Comma-separated list of IDs (or line numbers) to use as test set.
|
|
76
|
+
seed : int, optional
|
|
77
|
+
Random seed used for splits. Default is ``42``.
|
|
78
|
+
"""
|
|
50
79
|
def __init__(
|
|
51
80
|
self,
|
|
52
|
-
server_address
|
|
53
|
-
data_path
|
|
54
|
-
dp_config
|
|
81
|
+
server_address,
|
|
82
|
+
data_path="data/data.csv",
|
|
83
|
+
dp_config=None,
|
|
55
84
|
# NEW (optional client overrides; do NOT remove old args)
|
|
56
|
-
val_frac
|
|
57
|
-
test_frac
|
|
58
|
-
id_col
|
|
59
|
-
test_ids
|
|
60
|
-
seed
|
|
85
|
+
val_frac=None,
|
|
86
|
+
test_frac=None,
|
|
87
|
+
id_col=None,
|
|
88
|
+
test_ids=None,
|
|
89
|
+
seed=42,
|
|
61
90
|
):
|
|
62
91
|
self.server_address = server_address
|
|
63
92
|
self.dp_config = dp_config
|
|
@@ -83,31 +112,31 @@ class FlowerClient(fl.client.NumPyClient):
|
|
|
83
112
|
self.y_tensor = torch.tensor(y_default, dtype=torch.float32)
|
|
84
113
|
|
|
85
114
|
# Placeholders; we lazily build loaders/model on the first fit when we see server config
|
|
86
|
-
self.train_loader
|
|
87
|
-
self.val_loader
|
|
88
|
-
self.test_loader
|
|
115
|
+
self.train_loader = None
|
|
116
|
+
self.val_loader = None
|
|
117
|
+
self.test_loader = None
|
|
89
118
|
|
|
90
|
-
self.model
|
|
119
|
+
self.model = None
|
|
91
120
|
self.criterion = nn.BCEWithLogitsLoss()
|
|
92
|
-
self.optimizer
|
|
121
|
+
self.optimizer = None
|
|
93
122
|
|
|
94
123
|
# Effective settings (filled at first fit)
|
|
95
|
-
self.effective_features
|
|
96
|
-
self.effective_target
|
|
97
|
-
self.effective_val_frac
|
|
98
|
-
self.effective_test_frac
|
|
124
|
+
self.effective_features = self.feature_names[:]
|
|
125
|
+
self.effective_target = self.target_name
|
|
126
|
+
self.effective_val_frac = float(self.client_val_frac) if self.client_val_frac is not None else 0.0
|
|
127
|
+
self.effective_test_frac = float(self.client_test_frac) if self.client_test_frac is not None else 0.0
|
|
99
128
|
|
|
100
129
|
self._initialized = False
|
|
101
130
|
self._dp_attached = False # to avoid wrapping twice
|
|
102
131
|
|
|
103
132
|
# ---------- helpers
|
|
104
133
|
|
|
105
|
-
def _mk_loader(self, X
|
|
134
|
+
def _mk_loader(self, X, y, batch_size, shuffle):
|
|
106
135
|
x_t = torch.tensor(X, dtype=torch.float32)
|
|
107
136
|
y_t = torch.tensor(y, dtype=torch.float32)
|
|
108
137
|
return DataLoader(TensorDataset(x_t, y_t), batch_size=batch_size, shuffle=shuffle)
|
|
109
138
|
|
|
110
|
-
def _lazy_init_from_server_config(self, config
|
|
139
|
+
def _lazy_init_from_server_config(self, config):
|
|
111
140
|
"""
|
|
112
141
|
Build model and (train, val, test) loaders once, using:
|
|
113
142
|
- Server-enforced schema: config['features'] (comma-separated), config['target']
|
|
@@ -226,7 +255,7 @@ class FlowerClient(fl.client.NumPyClient):
|
|
|
226
255
|
# ---------- build loaders
|
|
227
256
|
batch_size = self.dp_config.batch_size if self.dp_config else 32
|
|
228
257
|
self.train_loader = self._mk_loader(X_train, y_train, batch_size, shuffle=True)
|
|
229
|
-
self.val_loader = self._mk_loader(X_val, y_val, batch_size=
|
|
258
|
+
self.val_loader = self._mk_loader(X_val, y_val, batch_size=batch_size, shuffle=False) if len(y_val) else None
|
|
230
259
|
self.test_loader = self._mk_loader(X_test, y_test, batch_size, shuffle=False)
|
|
231
260
|
|
|
232
261
|
# ---------- model/optimizer
|
|
@@ -414,7 +443,7 @@ class FlowerClient(fl.client.NumPyClient):
|
|
|
414
443
|
"gpu_count": gpu_count,
|
|
415
444
|
}
|
|
416
445
|
|
|
417
|
-
def start(self)
|
|
446
|
+
def start(self):
|
|
418
447
|
fl.client.start_numpy_client(
|
|
419
448
|
server_address=self.server_address,
|
|
420
449
|
client=self,
|
MEDfl/rw/server.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import flwr as fl
|
|
2
2
|
from flwr.server.strategy import FedAvg
|
|
3
3
|
from flwr.server.server import ServerConfig
|
|
4
|
-
from typing import Optional, Any
|
|
5
4
|
from MEDfl.rw.strategy import Strategy
|
|
6
5
|
import asyncio
|
|
7
6
|
from flwr.server.client_manager import ClientManager
|
|
@@ -29,11 +28,11 @@ class FederatedServer:
|
|
|
29
28
|
|
|
30
29
|
def __init__(
|
|
31
30
|
self,
|
|
32
|
-
host
|
|
33
|
-
port
|
|
34
|
-
num_rounds
|
|
35
|
-
strategy
|
|
36
|
-
certificates
|
|
31
|
+
host="0.0.0.0",
|
|
32
|
+
port=8080,
|
|
33
|
+
num_rounds=3,
|
|
34
|
+
strategy=None,
|
|
35
|
+
certificates=None,
|
|
37
36
|
):
|
|
38
37
|
"""
|
|
39
38
|
Initialize the FederatedServer.
|
|
@@ -60,7 +59,7 @@ class FederatedServer:
|
|
|
60
59
|
self.connected_clients = [] # Track connected client IDs
|
|
61
60
|
|
|
62
61
|
|
|
63
|
-
def start(self)
|
|
62
|
+
def start(self):
|
|
64
63
|
"""
|
|
65
64
|
Start the Flower server with the configured strategy and track client connections.
|
|
66
65
|
"""
|
|
@@ -89,7 +88,7 @@ class TrackingClientManager(fl.server.client_manager.SimpleClientManager):
|
|
|
89
88
|
client_properties (dict): Placeholder for storing client-specific properties.
|
|
90
89
|
"""
|
|
91
90
|
|
|
92
|
-
def __init__(self, server
|
|
91
|
+
def __init__(self, server):
|
|
93
92
|
"""
|
|
94
93
|
Initialize the TrackingClientManager.
|
|
95
94
|
|
|
@@ -100,7 +99,7 @@ class TrackingClientManager(fl.server.client_manager.SimpleClientManager):
|
|
|
100
99
|
self.server = server
|
|
101
100
|
self.client_properties = {}
|
|
102
101
|
|
|
103
|
-
def register(self, client
|
|
102
|
+
def register(self, client):
|
|
104
103
|
"""
|
|
105
104
|
Register a client and log its connection.
|
|
106
105
|
|
|
@@ -116,7 +115,7 @@ class TrackingClientManager(fl.server.client_manager.SimpleClientManager):
|
|
|
116
115
|
asyncio.run(self._fetch_and_log_hostname(client))
|
|
117
116
|
return success
|
|
118
117
|
|
|
119
|
-
async def _fetch_and_log_hostname(self, client
|
|
118
|
+
async def _fetch_and_log_hostname(self, client):
|
|
120
119
|
"""
|
|
121
120
|
Asynchronously fetch and log the client's hostname or CID.
|
|
122
121
|
|
MEDfl/rw/strategy.py
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import os
|
|
4
4
|
import numpy as np
|
|
5
5
|
import flwr as fl
|
|
6
|
-
from typing import Callable, Optional, Dict, Any, List, Tuple
|
|
7
6
|
from flwr.common import GetPropertiesIns
|
|
8
7
|
from flwr.server.client_manager import ClientManager
|
|
9
8
|
from flwr.server.client_proxy import ClientProxy
|
|
@@ -14,18 +13,14 @@ import torch
|
|
|
14
13
|
# ===================================================
|
|
15
14
|
# Custom metric aggregation functions
|
|
16
15
|
# ===================================================
|
|
17
|
-
def aggregate_fit_metrics(
|
|
18
|
-
results: List[Tuple[int, Dict[str, float]]]
|
|
19
|
-
) -> Dict[str, float]:
|
|
16
|
+
def aggregate_fit_metrics(results):
|
|
20
17
|
total = sum(n for n, _ in results)
|
|
21
18
|
loss = sum(m.get("train_loss", 0.0) * n for n, m in results) / total
|
|
22
19
|
acc = sum(m.get("train_accuracy", 0.0) * n for n, m in results) / total
|
|
23
20
|
auc = sum(m.get("train_auc", 0.0) * n for n, m in results) / total
|
|
24
21
|
return {"train_loss": loss, "train_accuracy": acc, "train_auc": auc}
|
|
25
22
|
|
|
26
|
-
def aggregate_eval_metrics(
|
|
27
|
-
results: List[Tuple[int, Dict[str, float]]]
|
|
28
|
-
) -> Dict[str, float]:
|
|
23
|
+
def aggregate_eval_metrics(results):
|
|
29
24
|
total = sum(n for n, _ in results)
|
|
30
25
|
loss = sum(m.get("eval_loss", 0.0) * n for n, m in results) / total
|
|
31
26
|
acc = sum(m.get("eval_accuracy", 0.0) * n for n, m in results) / total
|
|
@@ -71,33 +66,33 @@ class Strategy:
|
|
|
71
66
|
|
|
72
67
|
def __init__(
|
|
73
68
|
self,
|
|
74
|
-
name
|
|
75
|
-
fraction_fit
|
|
76
|
-
fraction_evaluate
|
|
77
|
-
min_fit_clients
|
|
78
|
-
min_evaluate_clients
|
|
79
|
-
min_available_clients
|
|
80
|
-
initial_parameters
|
|
81
|
-
evaluate_fn
|
|
82
|
-
fit_metrics_aggregation_fn
|
|
83
|
-
evaluate_metrics_aggregation_fn
|
|
84
|
-
local_epochs
|
|
85
|
-
threshold
|
|
86
|
-
learning_rate
|
|
87
|
-
optimizer_name
|
|
88
|
-
savingPath
|
|
89
|
-
saveOnRounds
|
|
90
|
-
total_rounds
|
|
91
|
-
features
|
|
92
|
-
target
|
|
93
|
-
val_fraction
|
|
94
|
-
test_fraction
|
|
69
|
+
name="FedAvg",
|
|
70
|
+
fraction_fit=1.0,
|
|
71
|
+
fraction_evaluate=1.0,
|
|
72
|
+
min_fit_clients=2,
|
|
73
|
+
min_evaluate_clients=2,
|
|
74
|
+
min_available_clients=2,
|
|
75
|
+
initial_parameters=None,
|
|
76
|
+
evaluate_fn=None,
|
|
77
|
+
fit_metrics_aggregation_fn=None,
|
|
78
|
+
evaluate_metrics_aggregation_fn=None,
|
|
79
|
+
local_epochs=1,
|
|
80
|
+
threshold=0.5,
|
|
81
|
+
learning_rate=0.01,
|
|
82
|
+
optimizer_name="SGD",
|
|
83
|
+
savingPath="",
|
|
84
|
+
saveOnRounds=3,
|
|
85
|
+
total_rounds=3,
|
|
86
|
+
features="",
|
|
87
|
+
target="",
|
|
88
|
+
val_fraction=0.10,
|
|
89
|
+
test_fraction=0.10,
|
|
95
90
|
# NEW: splitting control (added at the end to not break existing calls)
|
|
96
|
-
split_mode
|
|
97
|
-
client_fractions
|
|
91
|
+
split_mode="global", # "global" or "per_client"
|
|
92
|
+
client_fractions=None,
|
|
98
93
|
# NEW: id column for test_ids mapping
|
|
99
|
-
id_col
|
|
100
|
-
)
|
|
94
|
+
id_col="id",
|
|
95
|
+
):
|
|
101
96
|
self.name = name
|
|
102
97
|
self.fraction_fit = fraction_fit
|
|
103
98
|
self.fraction_evaluate = fraction_evaluate
|
|
@@ -128,14 +123,14 @@ class Strategy:
|
|
|
128
123
|
self.client_fractions = client_fractions or {}
|
|
129
124
|
self.id_col = id_col
|
|
130
125
|
|
|
131
|
-
self.strategy_object
|
|
126
|
+
self.strategy_object = None
|
|
132
127
|
|
|
133
|
-
def create_strategy(self)
|
|
128
|
+
def create_strategy(self):
|
|
134
129
|
# 1) Pick the Flower Strategy class
|
|
135
130
|
StrategyClass = getattr(fl.server.strategy, self.name)
|
|
136
131
|
|
|
137
132
|
# 2) Define on_fit_config_fn _before_ instantiation (global defaults)
|
|
138
|
-
def fit_config_fn(server_round
|
|
133
|
+
def fit_config_fn(server_round):
|
|
139
134
|
return {
|
|
140
135
|
"local_epochs": self.local_epochs,
|
|
141
136
|
"threshold": self.threshold,
|
|
@@ -150,7 +145,7 @@ class Strategy:
|
|
|
150
145
|
}
|
|
151
146
|
|
|
152
147
|
# 3) Build params including on_fit_config_fn
|
|
153
|
-
params
|
|
148
|
+
params = {
|
|
154
149
|
"fraction_fit": self.fraction_fit,
|
|
155
150
|
"fraction_evaluate": self.fraction_evaluate,
|
|
156
151
|
"min_fit_clients": self.min_fit_clients,
|
|
@@ -226,9 +221,9 @@ class Strategy:
|
|
|
226
221
|
original_conf_fit = strat.configure_fit
|
|
227
222
|
|
|
228
223
|
def wrapped_conf_fit(
|
|
229
|
-
server_round
|
|
224
|
+
server_round,
|
|
230
225
|
parameters,
|
|
231
|
-
client_manager
|
|
226
|
+
client_manager
|
|
232
227
|
):
|
|
233
228
|
selected = original_conf_fit(
|
|
234
229
|
server_round=server_round,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
|
-
Name:
|
|
3
|
-
Version: 2.0.5.
|
|
2
|
+
Name: medfl
|
|
3
|
+
Version: 2.0.5.dev6
|
|
4
4
|
Summary: Python Open-source package for simulating federated learning and differential privacy
|
|
5
5
|
Home-page: https://github.com/MEDomics-UdeS/MEDfl
|
|
6
6
|
Author: MEDomics consortium
|
|
@@ -14,7 +14,7 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.8
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
-
Requires-Python: >=3.8,<3.
|
|
17
|
+
Requires-Python: >=3.8,<3.15
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
License-File: LICENSE
|
|
20
20
|
Requires-Dist: flwr~=1.18.0
|
|
@@ -12,7 +12,7 @@ MEDfl/LearningManager/server.py,sha256=oTgW3K1UT6m4SQBk23FIf23km_BDq9vvjeC6OgY8D
|
|
|
12
12
|
MEDfl/LearningManager/strategy.py,sha256=BHXpwmt7jx07y45YLUs8FZry2gYQbpiV4vNbHhsksQ4,3435
|
|
13
13
|
MEDfl/LearningManager/utils.py,sha256=zk84uKR_iSsoz1uvKD-l4_jJWV3_5pfUWMIeLNcT1mU,9621
|
|
14
14
|
MEDfl/NetManager/__init__.py,sha256=OpgsIiBg7UA6Bfnu_kqGfEPxU8JfpPxSFU98TOeDTP0,273
|
|
15
|
-
MEDfl/NetManager/database_connector.py,sha256=
|
|
15
|
+
MEDfl/NetManager/database_connector.py,sha256=PZEAesMR-k9jSuthx4wOTkMzUWB3INODOrhQgpMO0kY,1554
|
|
16
16
|
MEDfl/NetManager/dataset.py,sha256=HTV0jrJ4Qlhl2aSJzdFU1lkxGBKtmJ390eBpwfKf_4o,2777
|
|
17
17
|
MEDfl/NetManager/flsetup.py,sha256=CVu_TIU7l3G6DDnwtY6JURbhIZk7gKC3unqWnU-YtlM,11434
|
|
18
18
|
MEDfl/NetManager/net_helper.py,sha256=tyfxmpbleSdfPfo2ezKT0VOvZu660v9nhBuHCpl8pG4,6812
|
|
@@ -20,17 +20,17 @@ MEDfl/NetManager/net_manager_queries.py,sha256=j-CLQPjtTLyZuFPhIcwJStD7L7xtZpkmk
|
|
|
20
20
|
MEDfl/NetManager/network.py,sha256=5t705fzWc-BRg-QPAbAcDv5ckDGzsPwj_Q5V0iTgkx0,6829
|
|
21
21
|
MEDfl/NetManager/node.py,sha256=t90QuYZ8M1X_AG1bwTta0CnlOuodqkmpVda2K7NOgHc,6542
|
|
22
22
|
MEDfl/rw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
-
MEDfl/rw/client.py,sha256=
|
|
23
|
+
MEDfl/rw/client.py,sha256=K0cMd0GuEWX5m11fzgEkdD2JCmY2ZVThSZr0K8dCQXM,18676
|
|
24
24
|
MEDfl/rw/model.py,sha256=IvC5a1ADyn7mKeSBq1Lk_UnkSJOCErPCmpEPSOFxHpk,5343
|
|
25
|
-
MEDfl/rw/server.py,sha256=
|
|
26
|
-
MEDfl/rw/strategy.py,sha256=
|
|
25
|
+
MEDfl/rw/server.py,sha256=JBNSSX8tihrLIjgDTrGfmsXXOq_00gtS0s9qCgbqrHY,4834
|
|
26
|
+
MEDfl/rw/strategy.py,sha256=uQvd9lS_PZc234ghBKvrhwdc7ooztHzewuvwpWzBsJ8,13206
|
|
27
27
|
MEDfl/scripts/__init__.py,sha256=Pq1weevsPaU7MRMHfBYeyT0EOFeWLeVM6Y1DVz6jw1A,48
|
|
28
28
|
MEDfl/scripts/base.py,sha256=QrmG7gkiPYkAy-5tXxJgJmOSLGAKeIVH6i0jq7G9xnA,752
|
|
29
29
|
MEDfl/scripts/create_db.py,sha256=MnFtZkTueRZ-3qXPNX4JsXjOKj-4mlkxoRhSFdRcvJw,3817
|
|
30
30
|
alembic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
31
|
alembic/env.py,sha256=-aSZ6SlJeK1ZeqHgM-54hOi9LhJRFP0SZGjut-JnY-4,1588
|
|
32
|
-
medfl-2.0.5.
|
|
33
|
-
medfl-2.0.5.
|
|
34
|
-
medfl-2.0.5.
|
|
35
|
-
medfl-2.0.5.
|
|
36
|
-
medfl-2.0.5.
|
|
32
|
+
medfl-2.0.5.dev6.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
33
|
+
medfl-2.0.5.dev6.dist-info/METADATA,sha256=zznydqnDH8kncx_4RvJtfC71PmauDH7GEpDvAzsbND8,6914
|
|
34
|
+
medfl-2.0.5.dev6.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91
|
|
35
|
+
medfl-2.0.5.dev6.dist-info/top_level.txt,sha256=dIL9X8HOFuaVSzpg40DVveDPrymWRoShHtspH7kkjdI,14
|
|
36
|
+
medfl-2.0.5.dev6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|