client_with_pgfed.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import copy
  2. import gc
  3. import logging
  4. import time
  5. from collections import Counter
  6. import numpy as np
  7. import torch
  8. import torch._utils
  9. import torch.nn as nn
  10. import torch.nn.functional as F
  11. import model
  12. import utils
  13. from communication import ONLINE, TARGET, BOTH, LOCAL, GLOBAL, DAPU, NONE, EMA, DYNAMIC_DAPU, DYNAMIC_EMA_ONLINE, SELECTIVE_EMA
  14. from easyfl.client.base import BaseClient
  15. from easyfl.distributed.distributed import CPU
  16. from client import FedSSLClient
  17. logger = logging.getLogger(__name__)
  18. L2 = "l2"
  19. def model_dot_product(w1, w2, requires_grad=True):
  20. """ Return the sum of squared difference between two models. """
  21. dot_product = 0.0
  22. for p1, p2 in zip(w1.parameters(), w2.parameters()):
  23. if requires_grad:
  24. dot_product += torch.sum(p1 * p2)
  25. else:
  26. dot_product += torch.sum(p1.data * p2.data)
  27. return dot_product
  28. class FedSSLWithPgFedClient(FedSSLClient):
  29. def __init__(self, cid, conf, train_data, test_data, device, sleep_time=0):
  30. super(FedSSLWithPgFedClient, self).__init__(cid, conf, train_data, test_data, device, sleep_time)
  31. self._local_model = None
  32. self.DAPU_predictor = LOCAL
  33. self.encoder_distance = 1
  34. self.encoder_distances = []
  35. self.previous_trained_round = -1
  36. self.weight_scaler = None
  37. self.latest_grad = None
  38. self.lambdaa = 1.0 # PGFed learning rate for a_i, Regularization weight for pFedMe
  39. self.prev_loss_minuses = {}
  40. self.prev_mean_grad = None
  41. self.prev_convex_comb_grad = None
  42. self.a_i = None
  43. def train(self, conf, device=CPU):
  44. start_time = time.time()
  45. loss_fn, optimizer = self.pretrain_setup(conf, device)
  46. if conf.model in [model.MoCo, model.MoCoV2]:
  47. self.model.reset_key_encoder()
  48. self.train_loss = []
  49. self.model.to(device)
  50. old_model = copy.deepcopy(nn.Sequential(*list(self.model.children())[:-1])).cpu()
  51. for i in range(conf.local_epoch):
  52. data_count = 0 # delete later
  53. batch_loss = []
  54. for (batched_x1, batched_x2), _ in self.train_loader:
  55. print(data_count)
  56. if data_count >= 50:
  57. break
  58. x1, x2 = batched_x1.to(device), batched_x2.to(device)
  59. data_count += x1.size(0)
  60. optimizer.zero_grad()
  61. if conf.model in [model.MoCo, model.MoCoV2]:
  62. loss = self.model(x1, x2, device)
  63. elif conf.model == model.SimCLR:
  64. images = torch.cat((x1, x2), dim=0)
  65. features = self.model(images)
  66. logits, labels = self.info_nce_loss(features)
  67. loss = loss_fn(logits, labels)
  68. else:
  69. loss = self.model(x1, x2)
  70. loss.backward()
  71. if self.prev_convex_comb_grad is not None:
  72. for p_m, p_prev_conv in zip(self.model.parameters(), self.prev_convex_comb_grad.parameters()):
  73. p_m.grad.data += p_prev_conv.data
  74. dot_prod = model_dot_product(self.model, self.prev_mean_grad, requires_grad=False)
  75. self.update_a_i(dot_prod)
  76. optimizer.step()
  77. batch_loss.append(loss.item())
  78. if conf.model in [model.BYOL, model.BYOLNoSG, model.BYOLNoPredictor] and conf.momentum_update:
  79. self.model.update_moving_average()
  80. current_epoch_loss = sum(batch_loss) / len(batch_loss)
  81. self.train_loss.append(float(current_epoch_loss))
  82. # delete later
  83. all_grads_none = True
  84. for p in self.model.parameters():
  85. if p.grad is not None:
  86. all_grads_none = False
  87. if all_grads_none:
  88. print("123All None------------------")
  89. self.loss_minus = 0.0
  90. test_num = 0
  91. optimizer.zero_grad()
  92. data_count = 0 # delete later
  93. for (batched_x1, batched_x2), _ in self.train_loader:
  94. print(data_count)
  95. if data_count >= 500:
  96. break
  97. x1, x2 = batched_x1.to(self.device), batched_x2.to(self.device)
  98. data_count += x1.size(0)
  99. test_num += x1.size(0)
  100. if conf.model in [model.MoCo, model.MoCoV2]:
  101. loss = self.model(x1, x2, device)
  102. elif conf.model == model.SimCLR:
  103. images = torch.cat((x1, x2), dim=0)
  104. features = self.model(images)
  105. logits, labels = self.info_nce_loss(features)
  106. loss = loss_fn(logits, labels)
  107. else:
  108. loss = self.model(x1, x2)
  109. self.loss_minus += loss.item() * x1.size(0)
  110. self.loss_minus /= test_num
  111. if not self.latest_grad:
  112. self.latest_grad = copy.deepcopy(self.model)
  113. # delete later
  114. all_grads_none = True
  115. for p_l, p in zip(self.latest_grad.parameters(), self.model.parameters()):
  116. if p.grad is not None:
  117. p_l.data = p.grad.data.clone() / len(self.train_loader)
  118. all_grads_none = False
  119. else:
  120. p_l.data = torch.zeros_like(p_l.data)
  121. if all_grads_none:
  122. print("All None------------------")
  123. self.loss_minus -= model_dot_product(self.latest_grad, self.model, requires_grad=False)
  124. self.train_time = time.time() - start_time
  125. # store trained model locally
  126. self._local_model = copy.deepcopy(self.model).cpu()
  127. self.previous_trained_round = conf.round_id
  128. if conf.update_predictor in [DAPU, DYNAMIC_DAPU, SELECTIVE_EMA] or conf.update_encoder in [DYNAMIC_EMA_ONLINE, SELECTIVE_EMA]:
  129. new_model = copy.deepcopy(nn.Sequential(*list(self.model.children())[:-1])).cpu()
  130. self.encoder_distance = self._calculate_divergence(old_model, new_model)
  131. self.encoder_distances.append(self.encoder_distance.item())
  132. self.DAPU_predictor = self._DAPU_predictor_usage(self.encoder_distance)
  133. if self.conf.auto_scaler == 'y' and self.conf.random_selection:
  134. self._calculate_weight_scaler()
  135. if (conf.round_id + 1) % 100 == 0:
  136. logger.info(f"Client {self.cid}, encoder distances: {self.encoder_distances}")
  137. def update_a_i(self, dot_prod):
  138. for clt_j, mu_loss_minus in self.prev_loss_minuses.items():
  139. self.a_i[clt_j] -= self.lambdaa * (mu_loss_minus + dot_prod)
  140. self.a_i[clt_j] = max(self.a_i[clt_j], 0.0)
  141. def set_prev_mean_grad(self, mean_grad):
  142. if self.prev_mean_grad is None:
  143. self.prev_mean_grad = copy.deepcopy(mean_grad)
  144. else:
  145. self.set_model(self.prev_mean_grad, mean_grad)
  146. def set_prev_convex_comb_grad(self, convex_comb_grad, momentum=0.0):
  147. if self.prev_convex_comb_grad is None:
  148. self.prev_convex_comb_grad = copy.deepcopy(convex_comb_grad)
  149. else:
  150. self.set_model(self.prev_convex_comb_grad, convex_comb_grad, momentum=momentum)
  151. def set_model(self, old_m, new_m, momentum=0.0):
  152. for p_old, p_new in zip(old_m.parameters(), new_m.parameters()):
  153. p_old.data = (1 - momentum) * p_new.data.clone() + momentum * p_old.data.clone()