-
Notifications
You must be signed in to change notification settings - Fork 260
Expand file tree
/
Copy pathclient.py
More file actions
120 lines (102 loc) · 4.02 KB
/
Copy pathclient.py
File metadata and controls
120 lines (102 loc) · 4.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import random
import warnings
import importlib
import copy
class Client:
def __init__(self, client_id, group=None, train_data={'x': [], 'y': []}, eval_data={'x': [], 'y': []},
model_info=None):
model_path = model_info['model_path']
seed = model_info['seed']
model_params = model_info['model_params']
mod = importlib.import_module(model_path)
ClientModel = getattr(mod, 'ClientModel')
model = ClientModel(seed, *model_params)
# model_path = 'femnist.cnn'
# mod = importlib.import_module(model_path)
# ClientModel = getattr(mod, 'ClientModel')
# model = ClientModel(123, *(0.06, 62))
self._model = model
self.id = client_id
self.group = group
self.train_data = train_data
self.eval_data = eval_data
def train(self, num_epochs=1, batch_size=10, minibatch=None):
"""Trains on self.model using the client's train_data.
Args:
num_epochs: Number of epochs to train. Unsupported if minibatch is provided (minibatch has only 1 epoch)
batch_size: Size of training batches.
minibatch: fraction of client's data to apply minibatch sgd,
None to use FedAvg
Return:
comp: number of FLOPs executed in training process
num_samples: number of samples used in training
update: set of weights
update_size: number of bytes in update
"""
if minibatch is None:
data = self.train_data
comp, update = self.model.train(data, num_epochs, batch_size)
else:
frac = min(1.0, minibatch)
num_data = max(1, int(frac * len(self.train_data["x"])))
xs, ys = zip(*random.sample(list(zip(self.train_data["x"], self.train_data["y"])), num_data))
data = {'x': list(xs), 'y': list(ys)}
# Minibatch trains for only 1 epoch - multiple local epochs don't make sense!
num_epochs = 1
print(id(self.model))
comp, update = self.model.train(data, num_epochs, num_data)
num_train_samples = len(data['y'])
return comp, num_train_samples, update
def test(self, set_to_use='test'):
"""Tests self.model on self.test_data.
Args:
set_to_use. Set to test on. Should be in ['train', 'test'].
Return:
dict of metrics returned by the model.
"""
assert set_to_use in ['train', 'test', 'val']
if set_to_use == 'train':
data = self.train_data
elif set_to_use == 'test' or set_to_use == 'val':
data = self.eval_data
return self.model.test(data)
@property
def num_test_samples(self):
"""Number of test samples for this client.
Return:
int: Number of test samples for this client
"""
if self.eval_data is None:
return 0
return len(self.eval_data['y'])
@property
def num_train_samples(self):
"""Number of train samples for this client.
Return:
int: Number of train samples for this client
"""
if self.train_data is None:
return 0
return len(self.train_data['y'])
@property
def num_samples(self):
"""Number samples for this client.
Return:
int: Number of samples for this client
"""
train_size = 0
if self.train_data is not None:
train_size = len(self.train_data['y'])
test_size = 0
if self.eval_data is not None:
test_size = len(self.eval_data['y'])
return train_size + test_size
@property
def model(self):
"""Returns this client reference to model being trained"""
return self._model
@model.setter
def model(self, model):
warnings.warn('The current implementation shares the model among all clients.'
'Setting it on one client will effectively modify all clients.')
self._model = model