def generate_gradient(self, input, target): target = TensorBase(np.array(target).astype('float64')) input = TensorBase(np.array(input).astype('float64')) pred = self.forward(input) target_v = target if(self.pubkey is not None and self.encrypted): target_v = self.pubkey.encrypt(target_v) output_grad = (pred - target_v) weight_grad = TensorBase(np.zeros_like(self.weights.data)) if(self.encrypted): weight_grad = weight_grad.encrypt(self.pubkey) for i in range(len(input)): if(input[i] != 1 and input[i] != 0): weight_grad[i] += (output_grad[0] * input[i]) elif(input[i] == 1): weight_grad[i] += output_grad[0] else: "doesn't matter... input == 0" return weight_grad
def batch_update(self, minibatch, alpha): """Updates a minibatch of input and target prediction. Should be called through learn() for default parameters """ weight_update = TensorBase(np.zeros(self.weights.data.shape)) if (self.encrypted): weight_update = weight_update.encrypt(self.pubkey) for (x, y) in zip(*minibatch): weight_update += self.generate_gradient(x, y) self.weights -= weight_update * (alpha / len(minibatch[0]))
def __init__(self, n_inputs=4, n_labels=2, desc=""): self.desc = desc self.n_inputs = n_inputs self.n_labels = n_labels self.weights = TensorBase(np.zeros((n_inputs, n_labels))) self.pubkey = None self.encrypted = False
def __init__(self, n_inputs=4, n_labels=2, desc="", capsule_client=None): self.desc = desc self.n_inputs = n_inputs self.n_labels = n_labels self.weights = TensorBase(np.random.rand(n_inputs, n_labels)) self.pubkey = None self.encrypted = False self.capsule = capsule_client
def evaluate_gradient_from_avg(self,addr,avg,prikey,pubkey,inputs,targets,alpha=0.5): num_epochs = 50 if (self.syft_obj.encrypted): self.syft_obj.decrypt(prikey) candidate = self.syft_obj #candidate.weights=candidate.weights.decrypt(prikey) avg_grad_values=TensorBase(avg) print("type i wanna know",type(avg_grad_values)) for e in range (num_epochs): #alpha = alpha * 0.09 candidate.weights -= (avg_grad_values * alpha) #candidate.weights -= (avg * alpha) #candidate.weights = candidate.weights.decrypt(prikey) candidate.encrypted = False candidate.weights = TensorBase(candidate.weights) candidate.weights = candidate.weights.clamp_(-2,2) #candidate.weights=candidate.weights.clamp_(-2,2) print("type i wanna know2",type(candidate.weights)) new_model_error = candidate.evaluate(inputs, targets) print("new_model_error_iwannaknow",new_model_error) """if (new_model_error < self.best_error and self.target_error < self.best_error): totalError = self.initial_error - self.target_error solvedError = self.best_error - new_model_error self.best_error = new_model_error #self.syft_obj = candidate print("self.best_error",self.best_error) #self.syft_obj = candidate #tx = self.repo.get_transaction(from_addr=addr) #ipfs_address = self.repo.ipfs.store(candidate.encrypt(pubkey)) #ip=IPFSAddress().to_ethereum(ipfs_address) #tx.evalGradientfromAvg(self.model_id,new_model_error,ip) owner = self.owner bounty=self.bounty init_error=self.initial_error target_error=self.target_error model_id=self.model_id syft_ob=candidate best_error=self.best_error print("best_error", best_error) #updatedModel=Model(owner=owner,syft_obj=syft_ob,bounty=bounty,initial_error=init_error,best_error=best_error,target_error=target_error,model_id=model_id) print("updatedmodel.best_error", updatedModel.best_error)""" return new_model_error,candidate
def learn(self, inputs, targets, i): """Updates weights based on input and target prediction. Note, updating weights increases the noise in the encrypted weights and will eventually require the weights to be re-encrypted. TODO: minibatching TODO: instead of storing weights, store aggregated weight updates (and optionally use them in "forward"). """ inputs = inputs[50 * i:] targets = targets[50 * i:] weight_update = TensorBase(np.zeros_like(self.weights.data)) s_weight_update = time.time() for i, row in enumerate(inputs): weight_update += self.generate_gradient(row, targets[i]) #elf.weights -= weight_update * alpha e_weight_update = time.time() print("client update time", e_weight_update - s_weight_update) weight_update = weight_update / len(inputs) length = int(len(inputs)) return weight_update, length
class LinearClassifier(): """This class is a basic linear classifier with functionality to encrypt/decrypt weights according to any of the homomorphic encryption schemes in syft.he. It also contains the logic to make predictions when in an encrypted state. TODO: create a generic interface for ML models that this class implements. TODO: minibatching in the "learn" API. The greater the batch size, the more iterations should be doable before the homomorphic encryption noise grows too much to be decrypted. """ def __init__(self, n_inputs=4, n_labels=2, desc=""): self.desc = desc self.n_inputs = n_inputs self.n_labels = n_labels self.weights = TensorBase(np.zeros((n_inputs, n_labels))) self.pubkey = None self.encrypted = False def encrypt(self, pubkey): """iterates through each weight and encrypts it TODO: check that weights are actually decrypted """ self.encrypted = True self.pubkey = pubkey self.weights = self.weights.encrypt(pubkey) return self def decrypt(self, seckey): """iterates through each weight and decrypts it TODO: check that weights are actually encrypted """ self.encrypted = False self.weights = self.weights.decrypt(seckey) return self def forward(self, input): """Makes a prediction based on input. If the network is encrypted, the prediction is also encrypted and vise versa.""" pred = self.weights[0] * input[0] for j, each_inp in enumerate(input[1:]): if(each_inp == 1): pred = pred + self.weights[j + 1] elif(each_inp != 0): pred = pred + (self.weights[j + 1] * input[j + 1]) return pred def learn(self, input, target, alpha=0.5): """Updates weights based on input and target prediction. Note, updating weights increases the noise in the encrypted weights and will eventually require the weights to be re-encrypted. TODO: minibatching TODO: instead of storing weights, store aggregated weight updates (and optionally use them in "forward"). """ weight_update = self.generate_gradient(input, target) self.weights -= weight_update * alpha return weight_update def evaluate(self, inputs, targets): """accepts a list of inputs and a list of targets - returns the mean squared error scaled by a fixed amount and converted to an integer.""" scale = 1000 if(self.encrypted): return "not yet supported... but will in the future" else: loss = 0 for i, row in enumerate(inputs): pred = self.forward(row) true = targets[i] diff = (pred - true) loss += (diff * diff).to_numpy() return int((loss[0] * scale) / float(len(inputs))) def __str__(self): left = "Linear Model (" + str(self.n_inputs) + "," return left + str(self.n_labels) + "): " + str(self.desc) def __repr__(self): return self.__str__() def generate_gradient(self, input, target): target = TensorBase(np.array(target).astype('float64')) input = TensorBase(np.array(input).astype('float64')) pred = self.forward(input) target_v = target if(self.pubkey is not None and self.encrypted): target_v = self.pubkey.encrypt(target_v) output_grad = (pred - target_v) weight_grad = TensorBase(np.zeros_like(self.weights.data)) if(self.encrypted): weight_grad = weight_grad.encrypt(self.pubkey) for i in range(len(input)): if(input[i] != 1 and input[i] != 0): weight_grad[i] += (output_grad[0] * input[i]) elif(input[i] == 1): weight_grad[i] += output_grad[0] else: "doesn't matter... input == 0" return weight_grad
class LinearClassifier(AbstractModel): """ This class is a basic linear classifier with functionality to encrypt/decrypt weights according to any of the homomorphic encryption schemes in syft.he. It also contains the logic to make predictions when in an encrypted state. """ def __init__(self, n_inputs=4, n_labels=2, desc="", capsule_client=None): self.desc = desc self.n_inputs = n_inputs self.n_labels = n_labels self.weights = TensorBase(np.random.rand(n_inputs, n_labels)) self.pubkey = None self.encrypted = False self.capsule = capsule_client def encrypt(self): """iterates through each weight and encrypts it """ if self.encrypted: return self self.pubkey = self.capsule.keygen() self.weights = self.weights.encrypt(self.pubkey) self.encrypted = self.weights.encrypted return self def decrypt(self): """iterates through each weight and decrypts it """ if not self.pubkey: raise KeyException("Public key was not generated.") if not self.encrypted: return self self.weights = self.capsule.decrypt(self.weights, self.pubkey.id) self.encrypted = self.weights.encrypted return self def forward(self, input): """Makes a prediction based on input. If the network is encrypted, the prediction is also encrypted and vise versa. """ pred = self.weights[0] * input[0] for j, each_inp in enumerate(input[1:]): if (each_inp == 1): pred = pred + self.weights[j + 1] elif (each_inp != 0): pred = pred + (self.weights[j + 1] * input[j + 1]) return pred def learn(self, input, target, alpha=0.5, batchsize=32, encrypt_interval=16): """Updates weights based on input and target prediction. Note, updating weights increases the noise in the encrypted weights and will eventually require the weights to be re-encrypted. TODO: instead of storing weights, store aggregated weight updates (and optionally use them in "forward"). """ input_batches = [ input[i:i + batchsize] for i in range(0, len(input), batchsize) ] target_batches = [ target[i:i + batchsize] for i in range(0, len(target), batchsize) ] for epoch_count, minibatch in enumerate( zip(input_batches, target_batches)): self.batch_update(minibatch, alpha) if self.encrypted and (epoch_count > encrypt_interval) and ( epoch_count % encrypt_interval == 0): self.weights = self.capsule.bootstrap(self.weights, self.pubkey.id) def batch_update(self, minibatch, alpha): """Updates a minibatch of input and target prediction. Should be called through learn() for default parameters """ weight_update = TensorBase(np.zeros(self.weights.data.shape)) if (self.encrypted): weight_update = weight_update.encrypt(self.pubkey) for (x, y) in zip(*minibatch): weight_update += self.generate_gradient(x, y) self.weights -= weight_update * (alpha / len(minibatch[0])) def evaluate(self, inputs, targets): """accepts a list of inputs and a list of targets - returns the mean squared error scaled by a fixed amount and converted to an integer. """ scale = 1000 if (self.encrypted): return "not yet supported... but will in the future" else: loss = 0 for i, row in enumerate(inputs): pred = self.forward(row) true = targets[i] diff = (pred - true) loss += (diff * diff).to_numpy() return int((loss[0] * scale) / float(len(inputs))) def __str__(self): left = "Linear Model (" + str(self.n_inputs) + "," return left + str(self.n_labels) + "): " + str(self.desc) def generate_gradient(self, input, target): target = TensorBase(np.array(target).astype('float64')) input = TensorBase(np.array(input).astype('float64')) pred = self.forward(input) target_v = target if (self.pubkey is not None and self.encrypted): target_v = self.pubkey.encrypt(target_v) output_grad = (pred - target_v) weight_grad = TensorBase(np.zeros_like(self.weights.data)) if (self.encrypted): weight_grad = weight_grad.encrypt(self.pubkey) for i in range(len(input)): if (input[i] != 1 and input[i] != 0): weight_grad[i] += (output_grad[0] * input[i]) elif (input[i] == 1): weight_grad[i] += output_grad[0] else: "doesn't matter... input == 0" return weight_grad