numpy_layer.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. import torch
  2. import numpy as np
  3. from federatedml.util import consts
  4. from federatedml.secureprotol.paillier_tensor import PaillierTensor
  5. class NumpyDenseLayer(object):
  6. """
  7. NumpyDenseLayer is designed for Pailler Tensor compute
  8. """
  9. def __init__(self):
  10. self.input = None
  11. self.model_weight = None
  12. self.model_shape = None
  13. self.bias = None
  14. self.lr = 1.0
  15. self.role = None
  16. self.is_empty_model = False
  17. self.activation_input = None
  18. self.input_cached = np.array([])
  19. self.activation_cached = np.array([])
  20. self.do_backward_selective_strategy = False
  21. self.batch_size = None
  22. def set_backward_selective_strategy(self):
  23. self.do_backward_selective_strategy = True
  24. def set_batch(self, batch_size):
  25. self.batch_size = batch_size
  26. def build(self, torch_linear: torch.nn.Linear):
  27. if torch_linear is None:
  28. if self.role == "host":
  29. raise ValueError("host input is empty!")
  30. self.is_empty_model = True
  31. return
  32. assert isinstance(
  33. torch_linear, torch.nn.Linear), 'must use a torch Linear to build this class, but got {}' .format(torch_linear)
  34. self.model_weight = torch_linear.weight.cpu().detach().numpy().transpose()
  35. if torch_linear.bias is not None:
  36. self.bias = torch_linear.bias.cpu().detach().numpy()
  37. def export_model(self):
  38. if self.is_empty_model:
  39. return "".encode()
  40. layer_weights = [self.model_weight]
  41. return layer_weights
  42. def get_selective_activation_input(self):
  43. self.activation_input = self.activation_cached[: self.batch_size]
  44. self.activation_cached = self.activation_cached[self.batch_size:]
  45. return self.activation_input
  46. def get_weight(self):
  47. return self.model_weight.transpose()
  48. def get_bias(self):
  49. return self.bias
  50. def set_learning_rate(self, lr):
  51. self.lr = lr
  52. def forward(self, x, **kwargs):
  53. pass
  54. def get_weight_gradient(self, delta):
  55. pass
  56. def restore_model(self, model_bytes):
  57. pass
  58. def update_weight(self, delta):
  59. pass
  60. def update_bias(self, delta):
  61. pass
  62. @property
  63. def empty(self):
  64. return self.is_empty_model
  65. @property
  66. def output_shape(self):
  67. return self.model_weight.shape[1:]
  68. def __repr__(self):
  69. return 'model weights: {}, model bias {}'.format(
  70. self.model_weight, self.bias)
  71. def __call__(self, *args, **kwargs):
  72. return self.forward(*args, **kwargs)
  73. class NumpyDenseLayerGuest(NumpyDenseLayer):
  74. def __init__(self):
  75. super(NumpyDenseLayerGuest, self).__init__()
  76. self.role = consts.GUEST
  77. def forward(self, x):
  78. if self.empty:
  79. return None
  80. self.input = x
  81. output = np.matmul(x, self.model_weight)
  82. if self.bias is not None:
  83. output += self.bias
  84. return output
  85. def select_backward_sample(self, selective_ids):
  86. if self.input_cached.shape[0] == 0:
  87. self.input_cached = self.input[selective_ids]
  88. else:
  89. self.input_cached = np.vstack(
  90. (self.input_cached, self.input[selective_ids])
  91. )
  92. def get_input_gradient(self, delta):
  93. if self.empty:
  94. return None
  95. error = np.matmul(delta, self.model_weight.T)
  96. return error
  97. def get_weight_gradient(self, delta):
  98. if self.empty:
  99. return None
  100. if self.do_backward_selective_strategy:
  101. self.input = self.input_cached[: self.batch_size]
  102. self.input_cached = self.input_cached[self.batch_size:]
  103. delta_w = np.matmul(delta.T, self.input)
  104. return delta_w
  105. def update_weight(self, delta):
  106. if self.empty:
  107. return None
  108. self.model_weight -= self.lr * delta.T
  109. def update_bias(self, delta):
  110. if self.bias is not None:
  111. self.bias -= np.sum(delta, axis=0) * self.lr
  112. class NumpyDenseLayerHost(NumpyDenseLayer):
  113. """
  114. This dense layer can directly compute pallier-tensor forward
  115. """
  116. def __init__(self):
  117. super(NumpyDenseLayerHost, self).__init__()
  118. self.role = consts.HOST
  119. def select_backward_sample(self, selective_ids):
  120. cached_shape = self.input_cached.shape[0]
  121. offsets = [i + cached_shape for i in range(len(selective_ids))]
  122. id_map = dict(zip(selective_ids, offsets))
  123. if cached_shape == 0:
  124. self.input_cached = (
  125. self.input.get_obj()
  126. .filter(lambda k, v: k in id_map)
  127. .map(lambda k, v: (id_map[k], v))
  128. )
  129. self.input_cached = PaillierTensor(self.input_cached)
  130. self.activation_cached = self.activation_input[selective_ids]
  131. else:
  132. selective_input = (
  133. self.input.get_obj()
  134. .filter(lambda k, v: k in id_map)
  135. .map(lambda k, v: (id_map[k], v))
  136. )
  137. self.input_cached = PaillierTensor(
  138. self.input_cached.get_obj().union(selective_input)
  139. )
  140. self.activation_cached = np.vstack(
  141. (self.activation_cached, self.activation_input[selective_ids])
  142. )
  143. def forward(self, x, encoder=None):
  144. self.input = x
  145. if encoder is not None:
  146. output = x * encoder.encode(self.model_weight)
  147. else:
  148. output = x * self.model_weight
  149. if self.bias is not None:
  150. if encoder is not None:
  151. output += encoder.encode(self.bias)
  152. else:
  153. output += self.bias
  154. return output
  155. def get_input_gradient(self, delta, acc_noise, encoder=None):
  156. if not encoder:
  157. error = delta * self.model_weight.T + delta * acc_noise.T
  158. else:
  159. error = delta.encode(encoder) * (self.model_weight + acc_noise).T
  160. return error
  161. def get_weight_gradient(self, delta, encoder=None):
  162. if self.do_backward_selective_strategy:
  163. batch_size = self.batch_size
  164. self.input = PaillierTensor(
  165. self.input_cached.get_obj().filter(lambda k, v: k < batch_size)
  166. )
  167. self.input_cached = PaillierTensor(
  168. self.input_cached.get_obj()
  169. .filter(lambda k, v: k >= batch_size)
  170. .map(lambda k, v: (k - batch_size, v))
  171. )
  172. if encoder:
  173. delta_w = self.input.fast_matmul_2d(encoder.encode(delta))
  174. else:
  175. delta_w = self.input.fast_matmul_2d(delta)
  176. return delta_w
  177. def update_weight(self, delta):
  178. self.model_weight -= delta * self.lr
  179. def update_bias(self, delta):
  180. if self.bias is not None:
  181. self.bias -= np.sum(delta, axis=0) * self.lr