From f1ba37891a9e249f18d9992d09e43c237326ba57 Mon Sep 17 00:00:00 2001 From: Andrei Stoian <95410270+andrei-stoian-zama@users.noreply.github.com> Date: Tue, 14 Dec 2021 13:14:03 +0100 Subject: [PATCH] feat: Update regression benchmarks and examples (#1163) Use our quantization API, improve datasets, metrics and quantization precisions Closes #972 --- benchmarks/linear_regression.py | 340 +++-- benchmarks/logistic_regression.py | 431 +++---- .../QuantizedLinearRegression.ipynb | 974 +++++---------- .../QuantizedLogisticRegression.ipynb | 1106 ++++++----------- 4 files changed, 1012 insertions(+), 1839 deletions(-) diff --git a/benchmarks/linear_regression.py b/benchmarks/linear_regression.py index 2820c4544..e5dc084db 100644 --- a/benchmarks/linear_regression.py +++ b/benchmarks/linear_regression.py @@ -4,226 +4,176 @@ # flake8: noqa: E501 # pylint: disable=C0301 -import numpy as np -from common import BENCHMARK_CONFIGURATION +from copy import deepcopy +from typing import Any, Dict -import concrete.numpy as hnp +import numpy as np +from sklearn.datasets import make_regression +from sklearn.linear_model import LinearRegression +from sklearn.metrics import r2_score +from sklearn.model_selection import train_test_split +from tqdm import tqdm + +from concrete.quantization import QuantizedArray, QuantizedLinear, QuantizedModule + + +class QuantizedLinearRegression(QuantizedModule): + """ + Quantized Generalized Linear Model + Building on top of QuantizedModule, implement a quantized linear transformation (w.x + b) + """ + + @staticmethod + def from_sklearn(sklearn_model, calibration_data): + """Create a Quantized Linear Regression initialized from a sklearn trained model""" + weights = np.expand_dims(sklearn_model.coef_, 1) + bias = sklearn_model.intercept_ + # Quantize with 6 bits for input data, 1 for weights, 1 for the bias and 6 for the output + return QuantizedLinearRegression(6, 1, 1, 6, weights, bias, calibration_data) + + def __init__(self, q_bits, w_bits, b_bits, out_bits, weights, bias, calibration_data) -> None: + """ + Create the linear regression with different quantization bit precisions: + + Quantization Parameters - Number of bits: + q_bits (int): bits for input data, insuring that the number of bits of + the w . x + b operation does not exceed 7 for the calibration data + w_bits (int): bits for weights: in the case of a univariate regression this + can be 1 + b_bits (int): bits for bias (this is a single value so a single bit is enough) + out_bits (int): bits for the result of the linear transformation (w.x + b). + In our case since the result of the linear transformation is + directly decrypted we can use the maximum of 7 bits + + Other parameters: + weights: a numpy nd-array of weights (Nxd) where d is the data dimensionality + bias: a numpy scalar + calibration_data: a numpy nd-array of data (Nxd) + """ + self.n_bits = out_bits + + # We need to calibrate to a sufficiently low number of bits + # so that the output of the Linear layer (w . x + b) + # does not exceed 7 bits + self.q_calibration_data = QuantizedArray(q_bits, calibration_data) + + # Quantize the weights and create the quantized linear layer + q_weights = QuantizedArray(w_bits, weights) + q_bias = QuantizedArray(b_bits, bias) + q_layer = QuantizedLinear(out_bits, q_weights, q_bias) + + # Store quantized layers + quant_layers_dict: Dict[str, Any] = {} + + # Calibrate the linear layer and obtain calibration_data for the next layers + calibration_data = self._calibrate_and_store_layers_activation( + "linear", q_layer, calibration_data, quant_layers_dict + ) + + # Finally construct our Module using the quantized layers + super().__init__(quant_layers_dict) + + def _calibrate_and_store_layers_activation( + self, name, q_function, calibration_data, quant_layers_dict + ): + """ + This function calibrates a layer of a quantized module (e.g. linear, inverse-link, + activation, etc) by looking at the input data, then computes the output of the quantized + version of the layer to be used as input to the following layers + """ + + # Calibrate the output of the layer + q_function.calibrate(calibration_data) + # Store the learned quantized layer + quant_layers_dict[name] = q_function + # Create new calibration data (output of the previous layer) + q_calibration_data = QuantizedArray(self.n_bits, calibration_data) + # Dequantize to have the value in clear and ready for next calibration + return q_function(q_calibration_data).dequant() + + def quantize_input(self, x): + """Quantize an input set with the quantization parameters determined from calibration""" + q_input_arr = deepcopy(self.q_calibration_data) + q_input_arr.update_values(x) + return q_input_arr def main(): - x = np.array( - [[69], [130], [110], [100], [145], [160], [185], [200], [80], [50]], dtype=np.float32 - ) - y = np.array([181, 325, 295, 268, 400, 420, 500, 520, 220, 120], dtype=np.float32) + """ + Our linear regression benchmark. Use some synthetic data to train a regression model, + then fit a model with sklearn. We quantize the sklearn model and compile it to FHE. + We compute the training loss for the quantized and FHE models and compare them. We also + predict on a test set and compare FHE results to predictions from the quantized model + """ - class Model: - w = None - b = None - - def fit(self, x, y): - a = np.ones((x.shape[0], x.shape[1] + 1), dtype=np.float32) - a[:, 1:] = x - - regularization_contribution = np.identity(x.shape[1] + 1, dtype=np.float32) - regularization_contribution[0][0] = 0 - - parameters = np.linalg.pinv(a.T @ a + regularization_contribution) @ a.T @ y - - self.b = parameters[0] - self.w = parameters[1:].reshape(-1, 1) - - return self - - def evaluate(self, x): - return x @ self.w + self.b - - model = Model().fit(x, y) - - class QuantizationParameters: - def __init__(self, q, zp, n): - self.q = q - self.zp = zp - self.n = n - - class QuantizedArray: - def __init__(self, values, parameters): - self.values = np.array(values) - self.parameters = parameters - - @staticmethod - def of(x, n): - if not isinstance(x, np.ndarray): - x = np.array(x) - - min_x = x.min() - max_x = x.max() - - if min_x == max_x: - - if min_x == 0.0: - q_x = 1 - zp_x = 0 - x_q = np.zeros(x.shape, dtype=np.uint) - - elif min_x < 0.0: - q_x = abs(1 / min_x) - zp_x = -1 - x_q = np.zeros(x.shape, dtype=np.uint) - - else: - q_x = 1 / min_x - zp_x = 0 - x_q = np.ones(x.shape, dtype=np.uint) - - else: - q_x = (2 ** n - 1) / (max_x - min_x) - zp_x = int(round(min_x * q_x)) - x_q = ((q_x * x) - zp_x).round().astype(np.uint) - - return QuantizedArray(x_q, QuantizationParameters(q_x, zp_x, n)) - - def dequantize(self): - return (self.values.astype(np.float32) + float(self.parameters.zp)) / self.parameters.q - - def affine(self, w, b, min_y, max_y, n_y): - x_q = self.values - w_q = w.values - b_q = b.values - - q_x = self.parameters.q - q_w = w.parameters.q - q_b = b.parameters.q - - zp_x = self.parameters.zp - zp_w = w.parameters.zp - zp_b = b.parameters.zp - - q_y = (2 ** n_y - 1) / (max_y - min_y) - zp_y = int(round(min_y * q_y)) - - y_q = (q_y / (q_x * q_w)) * ( - (x_q + zp_x) @ (w_q + zp_w) + (q_x * q_w / q_b) * (b_q + zp_b) - ) - y_q -= min_y * q_y - y_q = y_q.round().clip(0, 2 ** n_y - 1).astype(np.uint) - - return QuantizedArray(y_q, QuantizationParameters(q_y, zp_y, n_y)) - - class QuantizedFunction: - def __init__(self, table): - self.table = table - - @staticmethod - def of(f, input_bits, output_bits): - domain = np.array(range(2 ** input_bits), dtype=np.uint) - table = f(domain).round().clip(0, 2 ** output_bits - 1).astype(np.uint) - return QuantizedFunction(table) - - parameter_bits = 1 - - w_q = QuantizedArray.of(model.w, parameter_bits) - b_q = QuantizedArray.of(model.b, parameter_bits) - - input_bits = 6 - - x_q = QuantizedArray.of(x, input_bits) - - output_bits = 7 - - min_y = y.min() - max_y = y.max() - - n_y = output_bits - q_y = (2 ** n_y - 1) / (max_y - min_y) - zp_y = int(round(min_y * q_y)) - y_parameters = QuantizationParameters(q_y, zp_y, n_y) - - q_x = x_q.parameters.q - q_w = w_q.parameters.q - q_b = b_q.parameters.q - - zp_x = x_q.parameters.zp - zp_w = w_q.parameters.zp - zp_b = b_q.parameters.zp - - x_q = x_q.values - w_q = w_q.values - b_q = b_q.values - - c1 = q_y / (q_x * q_w) - c2 = w_q + zp_w - c3 = (q_x * q_w / q_b) * (b_q + zp_b) - c4 = min_y * q_y - - f_q = QuantizedFunction.of( - lambda intermediate: (c1 * (intermediate + c3)) - c4, - input_bits + parameter_bits, - output_bits, + X, y, _ = make_regression( + n_samples=200, n_features=1, n_targets=1, bias=5.0, noise=30.0, random_state=42, coef=True ) - table = hnp.LookupTable([int(entry) for entry in f_q.table]) + # Split it into train/test and sort the sets for nicer visualization + x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=42) - w_0 = int(c2.flatten()[0]) + sidx = np.argsort(np.squeeze(x_train)) + x_train = x_train[sidx, :] + y_train = y_train[sidx] - def function_to_compile(x_0): - return table[(x_0 + zp_x) * w_0] + sidx = np.argsort(np.squeeze(x_test)) + x_test = x_test[sidx, :] + y_test = y_test[sidx] - inputset = [int(x_i[0]) for x_i in x_q] + # Train a linear regression with sklearn and predict on the test data + linreg = LinearRegression() + linreg.fit(x_train, y_train) + # Calibrate the model for quantization using both training and test data + calib_data = X # np.vstack((x_train, x_test)) + q_linreg = QuantizedLinearRegression.from_sklearn(linreg, calib_data) + + # Compile the quantized model to FHE # bench: Measure: Compilation Time (ms) - engine = hnp.compile_numpy_function( - function_to_compile, - {"x_0": hnp.EncryptedScalar(hnp.UnsignedInteger(input_bits))}, - inputset, - compilation_configuration=BENCHMARK_CONFIGURATION, - ) + engine = q_linreg.compile(q_linreg.quantize_input(calib_data)) # bench: Measure: End - non_homomorphic_loss = 0 - homomorphic_loss = 0 + # Measure test error using the clear-sklearn, the clear-quantized and the FHE quantized model + # as R^2 coefficient for the test data - for i, (x_i, y_i) in enumerate(zip(x_q, y)): - x_i = [int(value) for value in x_i] + # First, predict using the sklearn classifier + y_pred = linreg.predict(x_test) - non_homomorphic_prediction = ( - QuantizedArray(x_i, QuantizationParameters(q_x, zp_x, input_bits)) - .affine( - QuantizedArray.of(model.w, parameter_bits), - QuantizedArray.of(model.b, parameter_bits), - min_y, - max_y, - output_bits, - ) - .dequantize()[0] - ) + # Now that the model is quantized, predict on the test set + x_test_q = q_linreg.quantize_input(x_test) + q_y_pred = q_linreg.forward_and_dequant(x_test_q) + + # Now predict using the FHE quantized model on the testing set + y_test_pred_fhe = np.zeros_like(x_test) + + for i, x_i in enumerate(tqdm(x_test_q.qvalues)): + q_sample = np.expand_dims(x_i, 1).transpose([1, 0]).astype(np.uint8) # bench: Measure: Evaluation Time (ms) - homomorphic_prediction = QuantizedArray(engine.run(*x_i), y_parameters).dequantize() + q_pred_fhe = engine.run(q_sample) # bench: Measure: End + y_test_pred_fhe[i] = q_linreg.dequantize_output(q_pred_fhe) - non_homomorphic_loss += (non_homomorphic_prediction - y_i) ** 2 - homomorphic_loss += (homomorphic_prediction - y_i) ** 2 + # Measure the error for the three versions of the classifier + sklearn_r2 = r2_score(y_pred, y_test) + non_homomorphic_test_error = r2_score(q_y_pred, y_test) + homomorphic_test_error = r2_score(y_test_pred_fhe, y_test) - print() + # Measure the error of the FHE quantized model w.r.t the clear quantized model + difference = ( + abs(homomorphic_test_error - non_homomorphic_test_error) * 100 / non_homomorphic_test_error + ) - print(f"input = {x[i][0]}") - print(f"output = {y_i:.4f}") - - print(f"non homomorphic prediction = {non_homomorphic_prediction:.4f}") - print(f"homomorphic prediction = {homomorphic_prediction:.4f}") - - non_homomorphic_loss /= len(y) - homomorphic_loss /= len(y) - difference = abs(homomorphic_loss - non_homomorphic_loss) * 100 / non_homomorphic_loss - - print() - print(f"Non Homomorphic Loss: {non_homomorphic_loss:.4f}") - print(f"Homomorphic Loss: {homomorphic_loss:.4f}") + print(f"Sklearn R^2: {sklearn_r2:.4f}") + print(f"Non Homomorphic R^2: {non_homomorphic_test_error:.4f}") + print(f"Homomorphic R^2: {homomorphic_test_error:.4f}") print(f"Relative Difference Percentage: {difference:.2f}%") # bench: Measure: Non Homomorphic Loss = non_homomorphic_loss # bench: Measure: Homomorphic Loss = homomorphic_loss - # bench: Measure: Relative Loss Difference Between Homomorphic and Non Homomorphic Implementation (%) = difference - # bench: Alert: Relative Loss Difference Between Homomorphic and Non Homomorphic Implementation (%) > 7.5 + # bench: Measure: Relative Loss Difference (%) = difference + # bench: Measure: Homomorphic Test Error = homomorphic_test_error + # bench: Alert: Relative Loss Difference (%) > 7.5 if __name__ == "__main__": diff --git a/benchmarks/logistic_regression.py b/benchmarks/logistic_regression.py index 490990e1e..5f5faa3a7 100644 --- a/benchmarks/logistic_regression.py +++ b/benchmarks/logistic_regression.py @@ -4,295 +4,208 @@ # flake8: noqa: E501 # pylint: disable=C0301 -import numpy as np -import torch -from common import BENCHMARK_CONFIGURATION +from copy import deepcopy +from typing import Any, Dict -import concrete.numpy as hnp +import numpy as np +from numpy.random import RandomState +from sklearn.datasets import make_classification +from sklearn.linear_model import LogisticRegression +from sklearn.model_selection import train_test_split +from tqdm import tqdm + +from concrete.quantization import QuantizedArray, QuantizedLinear, QuantizedModule, QuantizedSigmoid + + +class QuantizedLogisticRegression(QuantizedModule): + """ + Quantized Logistic Regression + Building on top of QuantizedModule, this class will chain together a linear transformation + and an inverse-link function, in this case the logistic function + """ + + @staticmethod + def from_sklearn(sklearn_model, calibration_data): + """Create a Quantized Logistic Regression initialized from a sklearn trained model""" + if sklearn_model.coef_.ndim == 1: + weights = np.expand_dims(sklearn_model.coef_, 1) + else: + weights = sklearn_model.coef_.transpose() + + bias = sklearn_model.intercept_ + + # In our case we have two data dimensions, we the weights precision needs to be 2 bits, as + # for now we need the quantized values to be greater than zero for weights + # Thus, to insure a maximum of 7 bits in the output of the linear transformation, we choose + # 4 bits for the data and the minimum of 1 for the bias + return QuantizedLogisticRegression(4, 2, 1, 6, weights, bias, calibration_data) + + def __init__(self, q_bits, w_bits, b_bits, out_bits, weights, bias, calibration_data) -> None: + """ + Create the Logistic regression with different quantization bit precisions: + + Quantization Parameters - Number of bits: + q_bits (int): bits for input data, insuring that the number of bits of + the w . x + b operation does not exceed 7 for the calibration data + w_bits (int): bits for weights: in the case of a univariate regression this + can be 1 + b_bits (int): bits for bias (this is a single value so a single bit is enough) + out_bits (int): bits for the result of the linear transformation (w.x + b). + In the case of Logistic Regression the result of the linear + transformation is input to a univariate inverse-link function, so + this value can be 7 + + Other parameters: + weights: a numpy nd-array of weights (Nxd) where d is the data dimensionality + bias: a numpy scalar + calibration_data: a numpy nd-array of data (Nxd) + """ + self.n_bits = out_bits + + # We need to calibrate to a sufficiently low number of bits + # so that the output of the Linear layer (w . x + b) + # does not exceed 7 bits + self.q_calibration_data = QuantizedArray(q_bits, calibration_data) + + # Quantize the weights and create the quantized linear layer + q_weights = QuantizedArray(w_bits, weights) + q_bias = QuantizedArray(b_bits, bias) + q_layer = QuantizedLinear(out_bits, q_weights, q_bias) + + # Store quantized layers + quant_layers_dict: Dict[str, Any] = {} + + # Calibrate the linear layer and obtain calibration_data for the next layers + calibration_data = self._calibrate_and_store_layers_activation( + "linear", q_layer, calibration_data, quant_layers_dict + ) + + # Add the inverse-link for inference. + # This needs to be quantized since it's computed in FHE, + # but we can use 7 bits of output since, in this case, + # the result of the inverse-link is not processed by any further layers + # Seven bits is the maximum precision but this could be lowered to improve speed + # at the possible expense of higher deviance of the regressor + q_logit = QuantizedSigmoid(n_bits=7) + + # Now calibrate the inverse-link function with the linear layer's output data + calibration_data = self._calibrate_and_store_layers_activation( + "invlink", q_logit, calibration_data, quant_layers_dict + ) + + # Finally construct our Module using the quantized layers + super().__init__(quant_layers_dict) + + def _calibrate_and_store_layers_activation( + self, name, q_function, calibration_data, quant_layers_dict + ): + """ + This function calibrates a layer of a quantized module (e.g. linear, inverse-link, + activation, etc) by looking at the input data, then computes the output of the quantized + version of the layer to be used as input to the following layers + """ + + # Calibrate the output of the layer + q_function.calibrate(calibration_data) + # Store the learned quantized layer + quant_layers_dict[name] = q_function + # Create new calibration data (output of the previous layer) + q_calibration_data = QuantizedArray(self.n_bits, calibration_data) + # Dequantize to have the value in clear and ready for next calibration + return q_function(q_calibration_data).dequant() + + def quantize_input(self, x): + q_input_arr = deepcopy(self.q_calibration_data) + q_input_arr.update_values(x) + return q_input_arr def main(): - x = torch.tensor( - [ - [1, 1], - [1, 1.5], - [1.5, 1.2], - [1, 2], - [2, 1], - [4, 1], - [4, 1.5], - [3.5, 1.8], - [3, 2], - [4, 2], - ] - ).float() - y = torch.tensor( - [ - [0], - [0], - [0], - [0], - [0], - [1], - [1], - [1], - [1], - [1], - ] - ).float() + """Main benchmark function: generate some synthetic data for two class classification, + split train-test, train a sklearn classifier, calibrate and quantize it on the whole dataset + then compile it to FHE. Test the three versions of the classifier on the test set and + report accuracy""" - class Model(torch.nn.Module): - def __init__(self, n): - super().__init__() - self.fc = torch.nn.Linear(n, 1) - - def forward(self, x): - output = torch.sigmoid(self.fc(x)) - return output - - model = Model(x.shape[1]) - - optimizer = torch.optim.SGD(model.parameters(), lr=1) - criterion = torch.nn.BCELoss() - - epochs = 1501 - for e in range(1, epochs + 1): - optimizer.zero_grad() - - out = model(x) - loss = criterion(out, y) - - loss.backward() - optimizer.step() - - if e % 100 == 1 or e == epochs: - print("Epoch:", e, "|", "Loss:", loss.item()) - - w = np.array(model.fc.weight.flatten().tolist()).reshape((-1, 1)) - b = model.fc.bias.flatten().tolist()[0] - - x = x.detach().numpy() - y = y.detach().numpy().flatten() - - class QuantizationParameters: - def __init__(self, q, zp, n): - self.q = q - self.zp = zp - self.n = n - - def __eq__(self, other): - return self.q == other.q and self.zp == other.zp and self.n == other.n - - class QuantizedArray: - def __init__(self, values, parameters): - self.values = np.array(values) - self.parameters = parameters - - @staticmethod - def of(x, n): - if not isinstance(x, np.ndarray): - x = np.array(x) - - min_x = x.min() - max_x = x.max() - - if min_x == max_x: - - if min_x == 0.0: - q_x = 1 - zp_x = 0 - x_q = np.zeros(x.shape, dtype=np.uint) - - elif min_x < 0.0: - q_x = abs(1 / min_x) - zp_x = -1 - x_q = np.zeros(x.shape, dtype=np.uint) - - else: - q_x = 1 / min_x - zp_x = 0 - x_q = np.ones(x.shape, dtype=np.uint) - - else: - q_x = (2 ** n - 1) / (max_x - min_x) - zp_x = int(round(min_x * q_x)) - x_q = ((q_x * x) - zp_x).round().astype(np.uint) - - return QuantizedArray(x_q, QuantizationParameters(q_x, zp_x, n)) - - def dequantize(self): - return (self.values.astype(np.float32) + float(self.parameters.zp)) / self.parameters.q - - def affine(self, w, b, min_y, max_y, n_y): - x_q = self.values - w_q = w.values - b_q = b.values - - q_x = self.parameters.q - q_w = w.parameters.q - q_b = b.parameters.q - - zp_x = self.parameters.zp - zp_w = w.parameters.zp - zp_b = b.parameters.zp - - q_y = (2 ** n_y - 1) / (max_y - min_y) - zp_y = int(round(min_y * q_y)) - - y_q = (q_y / (q_x * q_w)) * ( - (x_q + zp_x) @ (w_q + zp_w) + (q_x * q_w / q_b) * (b_q + zp_b) - ) - y_q -= min_y * q_y - y_q = y_q.round().clip(0, 2 ** n_y - 1).astype(np.uint) - - return QuantizedArray(y_q, QuantizationParameters(q_y, zp_y, n_y)) - - class QuantizedFunction: - def __init__(self, table, input_parameters=None, output_parameters=None): - self.table = table - self.input_parameters = input_parameters - self.output_parameters = output_parameters - - @staticmethod - def of(f, input_bits, output_bits): - domain = np.array(range(2 ** input_bits), dtype=np.uint) - table = f(domain).round().clip(0, 2 ** output_bits - 1).astype(np.uint) - return QuantizedFunction(table) - - @staticmethod - def plain(f, input_parameters, output_bits): - n = input_parameters.n - - domain = np.array(range(2 ** n), dtype=np.uint) - inputs = QuantizedArray(domain, input_parameters).dequantize() - - outputs = f(inputs) - quantized_outputs = QuantizedArray.of(outputs, output_bits) - - table = quantized_outputs.values - output_parameters = quantized_outputs.parameters - - return QuantizedFunction(table, input_parameters, output_parameters) - - def apply(self, x): - assert x.parameters == self.input_parameters - return QuantizedArray(self.table[x.values], self.output_parameters) - - parameter_bits = 1 - - w_q = QuantizedArray.of(w, parameter_bits) - b_q = QuantizedArray.of(b, parameter_bits) - - input_bits = 5 - - x_q = QuantizedArray.of(x, input_bits) - - output_bits = 7 - - intermediate = x @ w + b - intermediate_q = x_q.affine(w_q, b_q, intermediate.min(), intermediate.max(), output_bits) - - sigmoid = QuantizedFunction.plain( - lambda x: 1 / (1 + np.exp(-x)), intermediate_q.parameters, output_bits + # Generate some data with a fixed seed + X, y = make_classification( + n_features=2, + n_redundant=0, + n_informative=2, + random_state=2, + n_clusters_per_class=1, + n_samples=100, ) - y_q = sigmoid.apply(intermediate_q) - y_parameters = y_q.parameters + # Scale the data randomly, fixing seeds for reproductibility + rng = RandomState(2) + X += 2 * rng.uniform(size=X.shape) - q_x = x_q.parameters.q - q_w = w_q.parameters.q - q_b = b_q.parameters.q - q_intermediate = intermediate_q.parameters.q + # Split it into train/test + x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=42) - zp_x = x_q.parameters.zp - zp_w = w_q.parameters.zp - zp_b = b_q.parameters.zp + # Train a logistic regression with sklearn on the training set + logreg = LogisticRegression() + logreg.fit(x_train, y_train) - x_q = x_q.values - w_q = w_q.values - b_q = b_q.values + # Calibrate the model for quantization using both training and test data + calib_data = X + q_logreg = QuantizedLogisticRegression.from_sklearn(logreg, calib_data) - c1 = q_intermediate / (q_x * q_w) - c2 = w_q + zp_w - c3 = (q_x * q_w / q_b) * (b_q + zp_b) - c4 = intermediate.min() * q_intermediate - - def f(x): - values = ((c1 * (x + c3)) - c4).round().clip(0, 2 ** output_bits - 1).astype(np.uint) - after_affine_q = QuantizedArray(values, intermediate_q.parameters) - - sigmoid = QuantizedFunction.plain( - lambda x: 1 / (1 + np.exp(-x)), - after_affine_q.parameters, - output_bits, - ) - y_q = sigmoid.apply(after_affine_q) - - return y_q.values - - f_q = QuantizedFunction.of(f, output_bits, output_bits) - - table = hnp.LookupTable([int(entry) for entry in f_q.table]) - - w_0 = int(c2.flatten()[0]) - w_1 = int(c2.flatten()[1]) - - def function_to_compile(x_0, x_1): - return table[((x_0 + zp_x) * w_0) + ((x_1 + zp_x) * w_1)] - - inputset = [(int(x_i[0]), int(x_i[1])) for x_i in x_q] + # Now, we can compile our model to FHE, taking as possible input set all of our dataset + X_q = q_logreg.quantize_input(X) # bench: Measure: Compilation Time (ms) - engine = hnp.compile_numpy_function( - function_to_compile, - { - "x_0": hnp.EncryptedScalar(hnp.UnsignedInteger(input_bits)), - "x_1": hnp.EncryptedScalar(hnp.UnsignedInteger(input_bits)), - }, - inputset, - compilation_configuration=BENCHMARK_CONFIGURATION, - ) + engine = q_logreg.compile(X_q) # bench: Measure: End + # Start classifier evaluation + + # Test the original classifier + y_pred_test = np.asarray(logreg.predict(x_test)) + + # Now that the model is quantized, predict on the test set + x_test_q = q_logreg.quantize_input(x_test) + q_y_score_test = q_logreg.forward_and_dequant(x_test_q) + q_y_pred_test = (q_y_score_test > 0.5).astype(np.int32) + non_homomorphic_correct = 0 homomorphic_correct = 0 - for i, (x_i, y_i) in enumerate(zip(x_q, y)): - x_i = [int(value) for value in x_i] + # Track the samples that are wrongly classified due to quantization issues + q_wrong_predictions = np.zeros((0, 2), dtype=X.dtype) + + # Predict the FHE quantized classifier probabilities on the test set. + # Compute FHE quantized accuracy, clear-quantized accuracy and + # keep track of samples wrongly classified due to quantization + for i, x_i in enumerate(tqdm(x_test_q.qvalues)): + y_i = y_test[i] + + fhe_in_sample = np.expand_dims(x_i, 1).transpose([1, 0]).astype(np.uint8) - non_homomorphic_prediction = round( - sigmoid.apply( - QuantizedArray(x_i, QuantizationParameters(q_x, zp_x, input_bits)).affine( - QuantizedArray.of(w, parameter_bits), - QuantizedArray.of(b, parameter_bits), - intermediate.min(), - intermediate.max(), - output_bits, - ) - ).dequantize()[0] - ) # bench: Measure: Evaluation Time (ms) - homomorphic_prediction = round(QuantizedArray(engine.run(*x_i), y_parameters).dequantize()) + q_pred_fhe = engine.run(fhe_in_sample) # bench: Measure: End + y_score_fhe = q_logreg.dequantize_output(q_pred_fhe) + homomorphic_prediction = (y_score_fhe > 0.5).astype(np.int32) + non_homomorphic_prediction = q_y_pred_test[i] if non_homomorphic_prediction == y_i: non_homomorphic_correct += 1 + elif y_pred_test[i] == y_i: + # If this was a correct prediction with the clear-sklearn classifier + q_wrong_predictions = np.vstack((q_wrong_predictions, x_test[i, :])) + if homomorphic_prediction == y_i: homomorphic_correct += 1 - print() - - print(f"input = {x[i][0]}, {x[i][1]}") - print(f"output = {y_i:.4f}") - - print(f"non homomorphic prediction = {non_homomorphic_prediction:.4f}") - print(f"homomorphic prediction = {homomorphic_prediction:.4f}") - - non_homomorphic_accuracy = (non_homomorphic_correct / len(y)) * 100 - homomorphic_accuracy = (homomorphic_correct / len(y)) * 100 + # Aggregate accuracies for all the versions of the classifier + sklearn_acc = np.sum(y_pred_test == y_test) / len(y_test) * 100 + non_homomorphic_accuracy = (non_homomorphic_correct / len(y_test)) * 100 + homomorphic_accuracy = (homomorphic_correct / len(y_test)) * 100 difference = abs(homomorphic_accuracy - non_homomorphic_accuracy) print() + print(f"Sklearn accuracy: {sklearn_acc:.4f}") print(f"Non Homomorphic Accuracy: {non_homomorphic_accuracy:.4f}") print(f"Homomorphic Accuracy: {homomorphic_accuracy:.4f}") print(f"Difference Percentage: {difference:.2f}%") diff --git a/docs/user/advanced_examples/QuantizedLinearRegression.ipynb b/docs/user/advanced_examples/QuantizedLinearRegression.ipynb index 51bb050bf..f4f8e4774 100644 --- a/docs/user/advanced_examples/QuantizedLinearRegression.ipynb +++ b/docs/user/advanced_examples/QuantizedLinearRegression.ipynb @@ -25,7 +25,36 @@ "metadata": {}, "outputs": [], "source": [ - "import numpy as np" + "from copy import deepcopy\n", + "from typing import Any, Dict\n", + "\n", + "import numpy as np\n", + "from matplotlib import pyplot as plt\n", + "from sklearn.datasets import make_regression\n", + "from sklearn.linear_model import LinearRegression\n", + "from sklearn.metrics import r2_score\n", + "from sklearn.model_selection import train_test_split\n", + "from tqdm import tqdm\n" + ] + }, + { + "cell_type": "markdown", + "id": "c8160548", + "metadata": {}, + "source": [ + "\n", + "\n", + "### Now import Concrete quantization tools " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "9dc823e0", + "metadata": {}, + "outputs": [], + "source": [ + "from concrete.quantization import QuantizedArray, QuantizedLinear, QuantizedModule" ] }, { @@ -38,7 +67,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "id": "d104c8df", "metadata": {}, "outputs": [], @@ -51,646 +80,365 @@ }, { "cell_type": "markdown", - "id": "53e676b8", + "id": "4a5ae7af", "metadata": {}, "source": [ - "### We need an inputset, a handcrafted one for simplicity" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "d451e829", - "metadata": {}, - "outputs": [], - "source": [ - "x = np.array([[69], [130], [110], [100], [145], [160], [185], [200], [80], [50]], dtype=np.float32)\n", - "y = np.array([181, 325, 295, 268, 400, 420, 500, 520, 220, 120], dtype=np.float32)" - ] - }, - { - "cell_type": "markdown", - "id": "75f4fdb7", - "metadata": {}, - "source": [ - "### Let's visualize our inputset to get a grasp of it" + "### And, finally, the FHE compiler" ] }, { "cell_type": "code", "execution_count": 4, - "id": "2a124a62", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "fig, ax = plt.subplots(1)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "edcd361b", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ax.scatter(x[:, 0], y, marker=\"x\", color=\"red\")\n", - "display(fig)" - ] - }, - { - "cell_type": "markdown", - "id": "5c8310ab", - "metadata": {}, - "source": [ - "### Now, we need a model so let's define it\n", - "\n", - "The main purpose of this tutorial is not to train a linear regression model but to use it homomorphically. So we will not discuss about how the model is trained." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "91d4a1da", - "metadata": {}, - "outputs": [], - "source": [ - "class Model:\n", - " w = None\n", - " b = None\n", - "\n", - " def fit(self, x, y):\n", - " a = np.ones((x.shape[0], x.shape[1] + 1), dtype=np.float32)\n", - " a[:, 1:] = x\n", - "\n", - " regularization_contribution = np.identity(x.shape[1] + 1, dtype=np.float32)\n", - " regularization_contribution[0][0] = 0\n", - "\n", - " parameters = np.linalg.pinv(a.T @ a + regularization_contribution) @ a.T @ y\n", - "\n", - " self.b = parameters[0]\n", - " self.w = parameters[1:].reshape(-1, 1)\n", - "\n", - " return self\n", - "\n", - " def evaluate(self, x):\n", - " return x @ self.w + self.b" - ] - }, - { - "cell_type": "markdown", - "id": "faa5247c", - "metadata": {}, - "source": [ - "### And create one" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "682fb2d8", - "metadata": {}, - "outputs": [], - "source": [ - "model = Model().fit(x, y)" - ] - }, - { - "cell_type": "markdown", - "id": "084fb296", - "metadata": {}, - "source": [ - "### Time to make some predictions" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "4953b03e", - "metadata": {}, - "outputs": [], - "source": [ - "inputs = np.linspace(40, 210, 100).reshape(-1, 1)\n", - "predictions = model.evaluate(inputs)" - ] - }, - { - "cell_type": "markdown", - "id": "f28155cf", - "metadata": {}, - "source": [ - "### Let's visualize our predictions to see how our model performs" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "111574ed", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ax.plot(inputs, predictions, color=\"blue\")\n", - "display(fig)" - ] - }, - { - "cell_type": "markdown", - "id": "23852861", - "metadata": {}, - "source": [ - "### As a bonus let's inspect the model parameters" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "7877cb2e", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[2.6698928]]\n", - "-3.2299957\n" - ] - } - ], - "source": [ - "print(model.w)\n", - "print(model.b)" - ] - }, - { - "cell_type": "markdown", - "id": "de63118c", - "metadata": {}, - "source": [ - "They are floating point numbers and we can't directly work with them!" - ] - }, - { - "cell_type": "markdown", - "id": "2d959640", - "metadata": {}, - "source": [ - "### So, let's abstract quantization\n", - "\n", - "Here is a quick summary of quantization. We have a range of values and we want to represent them using small number of bits (n). To do this, we split the range into 2^n sections and map each section to a value. Here is a visualization of the process!" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "9da2e1a4", - "metadata": {}, - "outputs": [ - { - "data": { - "image/svg+xml": "
min(x)
min(x)
max(x)
max(x)
Map
to 0
Map...
Map
to 1
Map...
Distance
Between
Consecutive
Values
Distan...
Map
to 2
Map...
Map
to 3
Map...
(when n = 2)
(when n = 2)
0
0
= 1 / scale
= 1 / q
= 1 / scale...
x = (x   + zp  ) / q
x = (x   + zp  ) / q
q
q
x
x
x
x
zero point
zp = 2
zero point...
Viewer does not support full SVG 1.1
", - "text/plain": [ - "" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from IPython.display import SVG\n", - "SVG(filename=\"figures/QuantizationVisualized.svg\")" - ] - }, - { - "cell_type": "markdown", - "id": "45d12e7a", - "metadata": {}, - "source": [ - "If you want to learn more, head to https://intellabs.github.io/distiller/algo_quantization.html" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "2541cdb7", - "metadata": {}, - "outputs": [], - "source": [ - "class QuantizationParameters:\n", - " def __init__(self, q, zp, n):\n", - " # q = scale factor = 1 / distance between consecutive values\n", - " # zp = zero point which is used to determine the beginning of the quantized range\n", - " # (quantized 0 = the beginning of the quantized range = zp * distance between consecutive values)\n", - " # n = number of bits\n", - " \n", - " # e.g.,\n", - " \n", - " # n = 2\n", - " # zp = 2\n", - " # q = 0.66\n", - " # distance between consecutive values = 1 / q = 1.5151\n", - " \n", - " # quantized 0 = zp / q = zp * distance between consecutive values = 3.0303\n", - " # quantized 1 = quantized 0 + distance between consecutive values = 4.5454\n", - " # quantized 2 = quantized 1 + distance between consecutive values = 6.0606\n", - " # quantized 3 = quantized 2 + distance between consecutive values = 7.5757\n", - " \n", - " self.q = q\n", - " self.zp = zp\n", - " self.n = n\n", - "\n", - "class QuantizedArray:\n", - " def __init__(self, values, parameters):\n", - " # values = quantized values\n", - " # parameters = parameters used during quantization\n", - " \n", - " # e.g.,\n", - " \n", - " # values = [1, 0, 2, 1]\n", - " # parameters = QuantizationParameters(q=0.66, zp=2, n=2)\n", - " \n", - " # original array = [4.5454, 3.0303, 6.0606, 4.5454]\n", - " \n", - " self.values = np.array(values)\n", - " self.parameters = parameters\n", - "\n", - " @staticmethod\n", - " def of(x, n):\n", - " if not isinstance(x, np.ndarray):\n", - " x = np.array(x)\n", - "\n", - " min_x = x.min()\n", - " max_x = x.max()\n", - "\n", - " if min_x == max_x: # encoding single valued arrays\n", - " \n", - " if min_x == 0.0: # encoding 0s\n", - " \n", - " # dequantization = (x_q + zp_x) / q_x = 0 --> q_x = 1 && zp_x = 0 && x_q = 0\n", - " q_x = 1\n", - " zp_x = 0\n", - " x_q = np.zeros(x.shape, dtype=np.uint)\n", - " \n", - " elif min_x < 0.0: # encoding negative scalars\n", - " \n", - " # dequantization = (x_q + zp_x) / q_x = -x --> q_x = 1 / x & zp_x = -1 & x_q = 0\n", - " q_x = abs(1 / min_x)\n", - " zp_x = -1\n", - " x_q = np.zeros(x.shape, dtype=np.uint)\n", - " \n", - " else: # encoding positive scalars\n", - " \n", - " # dequantization = (x_q + zp_x) / q_x = x --> q_x = 1 / x & zp_x = 0 & x_q = 1\n", - " q_x = 1 / min_x\n", - " zp_x = 0\n", - " x_q = np.ones(x.shape, dtype=np.uint)\n", - " \n", - " else: # encoding multi valued arrays\n", - " \n", - " # distance between consecutive values = range of x / number of different quantized values = (max_x - min_x) / (2^n - 1)\n", - " # q = 1 / distance between consecutive values\n", - " q_x = (2**n - 1) / (max_x - min_x)\n", - " \n", - " # zp = what should be added to 0 to get min_x -> min_x = (0 + zp) / q -> zp = min_x * q\n", - " zp_x = int(round(min_x * q_x))\n", - " \n", - " # x = (x_q + zp) / q -> x_q = (x * q) - zp\n", - " x_q = ((q_x * x) - zp_x).round().astype(np.uint)\n", - "\n", - " return QuantizedArray(x_q, QuantizationParameters(q_x, zp_x, n))\n", - "\n", - " def dequantize(self):\n", - " # x = (x_q + zp) / q\n", - " # x = (x_q + zp) / q\n", - " return (self.values.astype(np.float32) + float(self.parameters.zp)) / self.parameters.q\n", - "\n", - " def affine(self, w, b, min_y, max_y, n_y):\n", - " # the formulas used in this method was derived from the following equations\n", - " #\n", - " # x = (x_q + zp_x) / q_x\n", - " # w = (w_q + zp_w) / q_w\n", - " # b = (b_q + zp_b) / q_b\n", - " #\n", - " # (x * w) + b = ((x_q + zp_x) / q_x) * ((w_q + zp_w) / q_w) + ((b_q + zp_b) / q_b)\n", - " # = y = (y_q + zp_y) / q_y\n", - " #\n", - " # So, ((x_q + zp_x) / q_x) * ((w_q + zp_w) / q_w) + ((b_q + zp_b) / q_b) = (y_q + zp_y) / q_y\n", - " # We can calculate zp_y and q_y from min_y, max_y, n_y. So, the only unknown is y_q and it can be solved.\n", - "\n", - " x_q = self.values\n", - " w_q = w.values\n", - " b_q = b.values\n", - "\n", - " q_x = self.parameters.q\n", - " q_w = w.parameters.q\n", - " q_b = b.parameters.q\n", - "\n", - " zp_x = self.parameters.zp\n", - " zp_w = w.parameters.zp\n", - " zp_b = b.parameters.zp\n", - "\n", - " q_y = (2**n_y - 1) / (max_y - min_y)\n", - " zp_y = int(round(min_y * q_y))\n", - "\n", - " y_q = (q_y / (q_x * q_w)) * ((x_q + zp_x) @ (w_q + zp_w) + (q_x * q_w / q_b) * (b_q + zp_b))\n", - " y_q -= min_y * q_y\n", - " y_q = y_q.round().clip(0, 2**n_y - 1).astype(np.uint)\n", - "\n", - " return QuantizedArray(y_q, QuantizationParameters(q_y, zp_y, n_y))\n", - "\n", - "class QuantizedFunction:\n", - " def __init__(self, table):\n", - " self.table = table\n", - "\n", - " @staticmethod\n", - " def of(f, input_bits, output_bits):\n", - " domain = np.array(range(2**input_bits), dtype=np.uint)\n", - " table = f(domain).round().clip(0, 2**output_bits - 1).astype(np.uint)\n", - " return QuantizedFunction(table)" - ] - }, - { - "cell_type": "markdown", - "id": "ab82ae87", - "metadata": {}, - "source": [ - "### Let's quantize our model parameters\n", - "\n", - "Since the parameters only consist of scalars, we can use a single bit quantization." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "c8b08ef4", - "metadata": {}, - "outputs": [], - "source": [ - "parameter_bits = 1\n", - "\n", - "w_q = QuantizedArray.of(model.w, parameter_bits)\n", - "b_q = QuantizedArray.of(model.b, parameter_bits)" - ] - }, - { - "cell_type": "markdown", - "id": "e2528092", - "metadata": {}, - "source": [ - "### And quantize our inputs" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "affe644e", - "metadata": {}, - "outputs": [], - "source": [ - "input_bits = 6\n", - "\n", - "x_q = QuantizedArray.of(inputs, input_bits)" - ] - }, - { - "cell_type": "markdown", - "id": "a5a50eb8", - "metadata": {}, - "source": [ - "### Time to make quantized inference" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "0fdfd3d9", - "metadata": {}, - "outputs": [], - "source": [ - "output_bits = 7\n", - "\n", - "min_y = predictions.min()\n", - "max_y = predictions.max()\n", - "y_q = x_q.affine(w_q, b_q, min_y, max_y, output_bits)\n", - "\n", - "quantized_predictions = y_q.dequantize()" - ] - }, - { - "cell_type": "markdown", - "id": "5fb15eb4", - "metadata": {}, - "source": [ - "### And visualize the results" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "8076a406", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ax.plot(inputs, quantized_predictions, color=\"black\")\n", - "display(fig)" - ] - }, - { - "cell_type": "markdown", - "id": "af6bc89e", - "metadata": {}, - "source": [ - "### Now it's time to make the inference homomorphic" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "cbda8067", - "metadata": {}, - "outputs": [], - "source": [ - "q_y = (2**output_bits - 1) / (max_y - min_y)\n", - "zp_y = int(round(min_y * q_y))\n", - "\n", - "q_x = x_q.parameters.q\n", - "q_w = w_q.parameters.q\n", - "q_b = b_q.parameters.q\n", - "\n", - "zp_x = x_q.parameters.zp\n", - "zp_w = w_q.parameters.zp\n", - "zp_b = b_q.parameters.zp\n", - "\n", - "x_q = x_q.values\n", - "w_q = w_q.values\n", - "b_q = b_q.values" - ] - }, - { - "cell_type": "markdown", - "id": "b8e95e3d", - "metadata": {}, - "source": [ - "### Simplification to rescue!\n", - "\n", - "The `y_q` formula in `QuantizedArray.affine(...)` can be rewritten to make it easier to implement in homomorphically. Here is the breakdown.\n", - "```\n", - "(q_y / (q_x * q_w)) * ((x_q + zp_x) @ (w_q + zp_w) + (q_x * q_w / q_b) * (b_q + zp_b)) - (min_y * q_y)\n", - "^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^\n", - "constant (c1) can be done constant (c2) constant (c3) constant (c4)\n", - " on the circuit \n", - " \n", - " ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - " can be done on the circuit\n", - " \n", - "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - "cannot be done on the circuit because of floating point operation so will be a single table lookup\n", - "```" - ] - }, - { - "cell_type": "markdown", - "id": "c6e101ae", - "metadata": {}, - "source": [ - "### Let's import the Concrete numpy package now!" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "4da7aed5", + "id": "05cda814", "metadata": {}, "outputs": [], "source": [ "import concrete.numpy as hnp" ] }, + { + "cell_type": "markdown", + "id": "53e676b8", + "metadata": {}, + "source": [ + "### Let's define our Quantized Linear Regression module that quantizes a sklearn linear regression" + ] + }, { "cell_type": "code", - "execution_count": 19, - "id": "d3816fa5", + "execution_count": 5, + "id": "d451e829", "metadata": {}, "outputs": [], "source": [ - "c1 = q_y / (q_x * q_w)\n", - "c2 = w_q + zp_w\n", - "c3 = (q_x * q_w / q_b) * (b_q + zp_b)\n", - "c4 = min_y * q_y\n", + "class QuantizedLinearRegression(QuantizedModule):\n", + " \"\"\"\n", + " Quantized Generalized Linear Model\n", + " Building on top of QuantizedModule, this class will chain together a linear transformation\n", + " and an inverse-link function\n", + " \"\"\"\n", "\n", - "f = lambda intermediate: (c1 * (intermediate + c3)) - c4\n", - "f_q = QuantizedFunction.of(f, input_bits + parameter_bits, output_bits)\n", + " @staticmethod\n", + " def from_sklearn(sklearn_model, calibration_data):\n", + " \"\"\"Create a Quantized Linear Regression initialized from a sklearn trained model\"\"\"\n", + " weights = np.expand_dims(sklearn_model.coef_, 1)\n", + " bias = sklearn_model.intercept_\n", + " #Quantize with 6 bits for input data, 1 for weights, 1 for the bias and 6 for the output\n", + " return QuantizedLinearRegression(6, 1, 1, 6, weights, bias, calibration_data)\n", "\n", - "table = hnp.LookupTable([int(entry) for entry in f_q.table])\n", + " def __init__(self, q_bits, w_bits, b_bits, out_bits, weights, bias, calibration_data) -> None:\n", + " \"\"\"\n", + " Create the Linear regression with different quantization bit precitions:\n", "\n", - "w_0 = int(c2.flatten()[0])\n", + " Quantization Parameters - Number of bits:\n", + " q_bits (int): bits for input data, insuring that the number of bits of \n", + " the w . x + b operation does not exceed 7 for the calibration data\n", + " w_bits (int): bits for weights: in the case of a univariate regression this \n", + " can be 1 \n", + " b_bits (int): bits for bias (this is a single value so a single bit is enough)\n", + " out_bits (int): bits for the result of the linear transformation (w.x + b). \n", + " In our case since the result of the linear transformation is \n", + " directly decripted we can use the maximum of 7 bits\n", "\n", - "def infer(x_0):\n", - " return table[(x_0 + zp_x) * w_0]" + " Other parameters:\n", + " weights: a numpy nd-array of weights (Nxd) where d is the data dimensionality\n", + " bias: a numpy scalar\n", + " calibration_data: a numpy nd-array of data (Nxd)\n", + " \"\"\"\n", + " self.n_bits = out_bits\n", + "\n", + " # We need to calibrate to a sufficiently low number of bits\n", + " # so that the output of the Linear layer (w . x + b)\n", + " # does not exceed 7 bits\n", + " self.q_calibration_data = QuantizedArray(q_bits, calibration_data)\n", + "\n", + " # Quantize the weights and create the quantized linear layer\n", + " q_weights = QuantizedArray(w_bits, weights)\n", + " q_bias = QuantizedArray(b_bits, bias)\n", + " q_layer = QuantizedLinear(out_bits, q_weights, q_bias)\n", + "\n", + " # Store quantized layers\n", + " quant_layers_dict: Dict[str, Any] = {}\n", + "\n", + " # Calibrate the linear layer and obtain calibration_data for the next layers\n", + " calibration_data = self._calibrate_and_store_layers_activation(\n", + " \"linear\", q_layer, calibration_data, quant_layers_dict\n", + " )\n", + "\n", + " # Finally construct our Module using the quantized layers\n", + " super().__init__(quant_layers_dict)\n", + "\n", + " def _calibrate_and_store_layers_activation(\n", + " self, name, q_function, calibration_data, quant_layers_dict\n", + " ):\n", + " \"\"\"\n", + " This function calibrates a layer of a quantized module (e.g. linear, inverse-link,\n", + " activation, etc) by looking at the input data, then computes the output of the quantized\n", + " version of the layer to be used as input to the following layers\n", + " \"\"\"\n", + "\n", + " # Calibrate the output of the layer\n", + " q_function.calibrate(calibration_data)\n", + " # Store the learned quantized layer\n", + " quant_layers_dict[name] = q_function\n", + " # Create new calibration data (output of the previous layer)\n", + " q_calibration_data = QuantizedArray(self.n_bits, calibration_data)\n", + " # Dequantize to have the value in clear and ready for next calibration\n", + " return q_function(q_calibration_data).dequant()\n", + "\n", + " def quantize_input(self, x):\n", + " \"\"\"Quantize an input set with the quantization parameters determined from calibration\"\"\"\n", + " q_input_arr = deepcopy(self.q_calibration_data)\n", + " q_input_arr.update_values(x)\n", + " return q_input_arr" ] }, { "cell_type": "markdown", - "id": "01d67c28", + "id": "7945595f", "metadata": {}, "source": [ - "### Let's compile our quantized inference function to it's homomorphic equivalent" + "### Create a synthetic dataset" ] }, { "cell_type": "code", - "execution_count": 20, - "id": "81304aca", + "execution_count": 6, + "id": "410b90de", "metadata": {}, "outputs": [], "source": [ - "inputset = [int(x_i[0]) for x_i in x_q]\n", "\n", - "circuit = hnp.compile_numpy_function(\n", - " infer,\n", - " {\"x_0\": hnp.EncryptedScalar(hnp.Integer(input_bits, is_signed=False))},\n", - " inputset,\n", - ")" + "X, y = make_regression(\n", + " n_samples=200, n_features=1, n_targets=1, bias=5.0, noise=30.0, random_state=42\n", + ")\n", + "\n", + "# Split it into train/test and sort the sets for nicer visualization\n", + "x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=42)\n", + "\n", + "sidx = np.argsort(np.squeeze(x_train))\n", + "x_train = x_train[sidx, :]\n", + "y_train = y_train[sidx]\n", + "\n", + "sidx = np.argsort(np.squeeze(x_test))\n", + "x_test = x_test[sidx, :]\n", + "y_test = y_test[sidx]\n" ] }, { "cell_type": "markdown", - "id": "c62af039", + "id": "75f4fdb7", "metadata": {}, "source": [ - "### Here are some representations of the fhe circuit" + "### Train a linear regression on the training set and visualize predictions on the test set" ] }, { "cell_type": "code", - "execution_count": 21, - "id": "0c533af6", + "execution_count": 7, + "id": "2a124a62", + "metadata": {}, + "outputs": [], + "source": [ + "linreg = LinearRegression()\n", + "linreg.fit(x_train, y_train)\n", + "\n", + "y_pred = linreg.predict(x_test)" + ] + }, + { + "cell_type": "markdown", + "id": "a0ba5509", + "metadata": {}, + "source": [ + "### Visualize the regression line and the data set" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "edcd361b", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAs4AAAHSCAYAAAD8EE1RAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/MnkTPAAAACXBIWXMAAAsTAAALEwEAmpwYAABx/0lEQVR4nO3deXjU5bnG8e9AAhgRwuYGVkAgshi2AKKoqCyiFhUXoFDXigtIiCvBhZFiQq0aYsEq1rpUq9ZWxaUgRUtRETGCRgQRE2yBg4hIRIxAIL/zx8svmZnMvk9yf64rV5j9ncSec8+b530eh2VZFiIiIiIi4lejRC9ARERERCQVKDiLiIiIiARBwVlEREREJAgKziIiIiIiQVBwFhEREREJgoKziIiIiEgQ0hK9gGC0bduWjh07JnoZIiIiIlLPff3113z33Xdeb0uJ4NyxY0dKSkoSvQwRERERqedycnJ83qZSDRERERGRICg4i4iIiIgEQcFZRERERCQIKVHj7E1VVRVbtmxh7969iV6KJFizZs3o0KED6enpiV6KiIiI1GMpG5y3bNnCEUccQceOHXE4HIlejiSIZVns3LmTLVu20KlTp0QvR0REROqxlC3V2Lt3L23atFFobuAcDgdt2rTRXx5EREQk5lI2OAMKzQLovwMRERGJj5QOzol233330bNnT7Kzs+nTpw8ffvghYPpOe2uc3bx587iu76mnnqJdu3b06dOHE088kaKiori+fiCnnHJKopcgIiIiErSUrXFOtA8++IA33niD1atX07RpU7777jv2798fl9c+ePAgjRs3Duq+Y8eOZd68eezcuZOsrCwuueQSjjvuuIhe/8CBA6SlRf6fzooVKyJ+DhEREZF40Y5zmLZt20bbtm1p2rQpYMaCH3vssW73+fnnnxk1ahSPP/54ncf//ve/Z8CAAWRnZzNz5sya6y+88EL69+9Pz549WbBgQc31zZs355ZbbqF379588MEHNG/enDvvvJPevXtz8skns337dr/rbdOmDV26dGHbtm0APPvsswwcOJA+ffpw3XXXcfDgQQCeeOIJunXrxsCBA7n22muZMmUKAFdeeSXXX389gwYN4vbbb6esrIxzzjmH/v37c9ppp/HFF18A8NJLL9GrVy969+7N6aefDsDnn39e81rZ2dls3Lix5j2BOeB322230atXL0466SRefPFFAJYtW8bQoUO55JJLOPHEE5kwYQKWZQXz6xERERGJunqz4zx0aN3rLrsMbrwRKivh3HPr3n7llebru+/gkkvcb1u2zP/rjRgxglmzZtGtWzeGDRvG2LFjOeOMM2pu37NnD+PGjePyyy/n8ssvd3vskiVL2LhxI6tWrcKyLEaPHs3y5cs5/fTT+fOf/0zr1q35+eefGTBgABdffDFt2rThp59+YtCgQTz44IMA/PTTT5x88sncd9993H777Tz++OPcddddPtf7v//9j71795Kdnc369et58cUXef/990lPT+fGG2/kueeeY9iwYfz2t79l9erVHHHEEZx11ln07t275jm2bNnCihUraNy4MWeffTaPPvooXbt25cMPP+TGG2/knXfeYdasWbz11lu0b9+eiooKAB599FFyc3OZMGEC+/fvrwnptpdffplPPvmETz/9lO+++44BAwbUhO41a9bw+eefc+yxx3Lqqafy/vvvM2TIEP+/HBEREZEY0I5zmJo3b87HH3/MggULaNeuHWPHjuWpp56quf2CCy7gqquuqhOawQTnJUuW0LdvX/r168cXX3xRswv78MMP1+wib968ueb6xo0bc/HFF9c8R5MmTTj//PMB6N+/P19//bXXdb744otkZ2fTpUsXbrzxRpo1a8bbb7/Nxx9/zIABA+jTpw9vv/025eXlrFq1ijPOOIPWrVuTnp7OpZde6vZcl156KY0bN2bPnj2sWLGCSy+9tGbH2t7JPvXUU7nyyit5/PHHawLy4MGDKSgo4He/+x3//e9/Oeyww9ye97333mP8+PE0btyYo446ijPOOIOPPvoIgIEDB9KhQwcaNWpEnz59fL5PERERkVirNzvO/naIMzL83962beAdZm8aN27M0KFDGTp0KCeddBJPP/00V155JWAC5OLFi/nVr35Vp+uDZVnk5+dz3XXXebyHZSxdupQPPviAjIwMhg4dWtNmrVmzZm51zenp6TXP27hxYw4cOOB1jXaNc0lJCSNGjGD06NFYlsUVV1xBYWGh231fffVVv+/38MMPB6C6uprMzEw++eSTOvd59NFH+fDDD3nzzTfp378/H3/8Mb/61a8YNGgQb775Jueeey6PPfYYZ511lt/XstmlMIHep4iIiEisacc5TBs2bKjZDQb45JNPOP7442suz5o1i1atWjF58uQ6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nWDxYhg50uMOvnb+IWl2/hWcRURERCQmLAu+/BKysiAnB554AsaN890qONl3/htUjbOIiIiIxMfXX8N550HfvvDf/5rse/XVfkKzLYl3/hWcw7Rz50769OlDnz59OProo2nfvn3N5f379wd8/LJly1ixYkVQr9WxY0e+++47v/cpKCgI6rlEREREYunAAVO73LMnLF9uWgJ36JDoVUVHwynVcN3293Y5RG3atOGTTz4BzBCS5s2bc+uttwb9+GXLltG8eXNOOeWUsNfgqqCggBkzZkTluURERETCsW8fDBkCJSVw/vkwfz784heJXlX0NIwd51KneyuTGI1v/PjjjznjjDPo378/I0eOZNu2bQA8/PDD9OjRg+zsbMaNG8fXX3/No48+SlFRUc1kP1c7d+5kxIgR9OzZk9/85je4zqi58MIL6d+/Pz179mTBggUATJ8+nZ9//pk+ffowYcIEn/cTERERiQV7QHLTpnDuufDSS/Daa/UrNENDmBzor7VJlCbROJ1ODj/8cF555RUWLlxIu3btePHFF3nrrbf485//zLHHHsumTZto2rQpFRUVZGZm+t2lnjp1Km3btuWee+7hzTff5Pzzz2fHjh20bduW77//ntatW/Pzzz8zYMAA/vOf/9CmTRuaN2/Onj17ap7D1/3qK00OFBERSYxFi2DKFHj2WRg8ONGriZy/yYH1v1TDtZXJhuLaXoBRHt+4b98+1q5dy/DhwwE4ePAgxxxzDADZ2dlMmDCBCy+8kAsvvDDgcy1fvpyXX34ZgPPOO49WrVrV3Pbwww/zyiuvALB582Y2btzoNRAHez8RERGRcGzfDtOmwQsvQPfu0LhxolcUe/U/OENteHadPx/l1iaWZdGzZ08++OCDOre9+eabLF++nNdff5377ruPzz77LKzXWLZsGUuXLuWDDz4gIyODoUOHsnfv3rDvJyIiIhKOZ56B3FyorIRZs+D2202ZRn3XMGqc4zC+sWnTpuzYsaMmOFdVVfH5559TXV3N5s2bOfPMM/nd737HDz/8wJ49ezjiiCP48ccfvT7X6aefzl//+lcAFi1axK5duwD44YcfaNWqFRkZGXzxxResXLmy5jHp6elUVVUFvJ+IiIhIpLZvNyOzS0vh7rsbRmiGhhCcPWucx1eb7xuKoxqeGzVqxN///nfuuOMOevfuTZ8+fVixYgUH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dnQiIlKvde5sQqx7GUWtjAyH++G2CNue5eebA3WhhGYwlxO18zxnjgn7559vBppcf73v0AzBh+ZUPRApEg4FZxERqRc8yyhssdrl9XcQMFlau+3eDV9+af49dSq8/jq89BIce2x0nj8Z67dFYknBWURE6o1Qa5BjIfDud3xau736KvToAZdeaoaYtGxpdpujJRnrt0ViTcFZRETqlWQ43Bbv3W9XW7fCmDGmtVybNrBggf+SjHDU7V6i8CwNg/o4i4hIvZOfD2PHJvZwmx2O7YAZj9C8ZrXFGUMdVFWZmuab8yzSm0R3jHWg+m1QJw6pv7TjLCIi9VIydISI1+73zz8DpU56Vd3CrydarF0Ld9xukf5ZHpQ6o/Y6yVS/LZIICs4iIiIxFKgDRyT27jWBPCvLYtfOfaSXFTH/mjxO6GyZAS8biqGqIqRBL/4kS/22SKJEJThfffXVHHnkkfTq1avmuu+//57hw4fTtWtXhg8fzq5duwCwLIupU6fSpUsXsrOzWb16dTSWICIikrRiEST//W/Izob77oOzznJA74LaqYjPN6qdlhjCaPFgJLJ+WyTRohKcr7zyShYvXux23Zw5czj77LPZuHEjZ599NnPmzAFg0aJFbNy4kY0bN7JgwQJuuOGGaCxBRESkQdi3D666Cs46y2wkL10KTz0FrVofmoroKsqh2ZYM3UtEEiEqwfn000+ndevWbtctXLiQK664AoArrriCV199teb6yy+/HIfDwcknn0xFRQXbtm2LxjJERERiw7PUIYFDd5s0gYoKU0tcWgpnn+2yptV57ndenReztSZD9xKReItZjfP27ds55phjADj66KPZvn07AFu3buW4446ruV+HDh3YunVrrJYhIiISmdKZ7gG0utpcLnXGbQnl5XDBBbBpk9lAfvllU6Jx2GGH7mC51DRn5cL46tqyjRiH51jVb4sko7i0o3M4HDhC/FPRggULWLBgAQA7duyIxbJERET8+3Qm/N9rsOsTc7nvQ/BWf3M5K9cE0hiUQtiqqqCoCJxOSEuDtWuhUycvL+lwQHqme02zXbaRnhnTNeogoDQkMQvORx11FNu2beOYY45h27ZtHHnkkQC0b9+ezZs319xvy5YttG/fvs7jJ02axKRJkwDIycmJ1TJFRES8syw48IMJya36mN3bDcXmtlZ9TIiOYSBdtQquvdaU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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.ioff()\n", + "\n", + "plt.clf()\n", + "fig, ax = plt.subplots(1, figsize=(12,8))\n", + "fig.patch.set_facecolor(\"white\")\n", + "ax.scatter(x_train, y_train, c=\"blue\", marker=\"D\", label=\"Train data\")\n", + "ax.scatter(x_test, y_test, c=\"orange\", marker=\"x\", label=\"Test data\")\n", + "ax.plot(x_test, y_pred, c=\"blue\", marker=None, linestyle=\"dashed\", label=\"Sklearn Regression\")\n", + "ax.legend()\n", + "display(fig)" + ] + }, + { + "cell_type": "markdown", + "id": "996fbe05", + "metadata": {}, + "source": [ + "### Calibrate the model for quantization using both training and test data\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "06ed91dd", + "metadata": {}, + "outputs": [], + "source": [ + "calib_data = X \n", + "q_linreg = QuantizedLinearRegression.from_sklearn(linreg, calib_data)" + ] + }, + { + "cell_type": "markdown", + "id": "cd74c5e7", + "metadata": {}, + "source": [ + "### Now, we can compile our model to FHE, taking as possible input set all of our dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "b8f8f95b", + "metadata": {}, + "outputs": [], + "source": [ + "X_q = q_linreg.quantize_input(X)\n", + "\n", + "engine = q_linreg.compile(X_q)" + ] + }, + { + "cell_type": "markdown", + "id": "084fb296", + "metadata": {}, + "source": [ + "### Time to make some predictions, first in the clear" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "e781279a", + "metadata": {}, + "outputs": [], + "source": [ + "# Now that the model is quantized, predict on the test set\n", + "x_test_q = q_linreg.quantize_input(x_test)\n", + "q_y_pred = q_linreg.forward_and_dequant(x_test_q)" + ] + }, + { + "cell_type": "markdown", + "id": "f28155cf", + "metadata": {}, + "source": [ + "### Now let's predict using the quantized FHE classifier" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "2b6da1f6", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 80/80 [00:14<00:00, 5.57it/s]\n" + ] + } + ], + "source": [ + "# Now predict using the FHE quantized model on the testing set\n", + "y_test_pred_fhe = np.zeros_like(x_test)\n", + "\n", + "for i, x_i in enumerate(tqdm(x_test_q.qvalues)):\n", + " q_sample = np.expand_dims(x_i, 1).transpose([1, 0]).astype(np.uint8)\n", + " # bench: Measure: Evaluation Time (ms)\n", + " q_pred_fhe = engine.run(q_sample)\n", + " y_test_pred_fhe[i] = q_linreg.dequantize_output(q_pred_fhe)\n", + " # bench: Measure: End\n" + ] + }, + { + "cell_type": "markdown", + "id": "23852861", + "metadata": {}, + "source": [ + "### Evaluate all versions of the classifier" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "7b0f541f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "%0 = Constant(1) # ClearScalar>\n", - "%1 = x_0 # EncryptedScalar>\n", - "%2 = Constant(15) # ClearScalar>\n", - "%3 = Add(1, 2) # EncryptedScalar>\n", - "%4 = Mul(3, 0) # EncryptedScalar>\n", - "%5 = TLU(4) # EncryptedScalar>\n", - "return(%5)\n", - "\n" + "Sklearn R^2: 0.8758\n", + "Non Homomorphic R^2: 0.8735\n", + "Homomorphic R^2: 0.8735\n", + "Relative Difference Percentage: 0.00%\n" ] } ], "source": [ - "print(circuit)" + "# Measure the error for the three versions of the classifier\n", + "sklearn_r2 = r2_score(y_pred, y_test)\n", + "non_homomorphic_test_error = r2_score(q_y_pred, y_test)\n", + "homomorphic_test_error = r2_score(y_test_pred_fhe, y_test)\n", + "\n", + "# Measure the error of the FHE quantized model w.r.t the clear quantized model\n", + "difference = (\n", + " abs(homomorphic_test_error - non_homomorphic_test_error) * 100 / non_homomorphic_test_error\n", + ")\n", + "\n", + "\n", + "print(f\"Sklearn R^2: {sklearn_r2:.4f}\")\n", + "print(f\"Non Homomorphic R^2: {non_homomorphic_test_error:.4f}\")\n", + "print(f\"Homomorphic R^2: {homomorphic_test_error:.4f}\")\n", + "print(f\"Relative Difference Percentage: {difference:.2f}%\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "704b2f63", + "metadata": {}, + "source": [ + "### Plot the results of the originql and FHE versions of the classifier" ] }, { "cell_type": "code", - "execution_count": 22, - "id": "c1fc0f48", + "execution_count": 14, + "id": "aae3f6da", "metadata": {}, "outputs": [ { "data": { - "image/png": "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smULZ8+e1WLUmId25ihWr17NlVdeSRsPpUT3NnoGjh8T7DNz7rjjDqKjo8nIyDDbFHegZ+D4M8HszCkpKWHdunUB00QF3Uz1a4LZmfPDDz+Qm5urxajxHYLVmZOZmUmDBg1o27at2aa4DS1GPydYnTmZmZn06NGDkJDAeYQD506CmGCbmVNaWsratWsDqokKWowBQzA5c7Zt28apU6f8fmV/ebQYA4RgcuZkZmZSv359UlJSzDbFrWgxBhDB4syx9RcDbWxVizGACAZnjogEZH8RtBgDDk84c7Zu3Urfvn2Ji4sjNjaW3r17s27dOrd8d03ZsWMHv/zyS5X9xaVLl5KUlERYmP9EI9ViDEDc6czZuHEj3bp1IzY2ll27drF//36aN29OWloay5cvd4O1NSMzM5N69erRoUMHl8f37dtH//79ef755zl58qR3jaslem5qgPLss88yc+ZMfvrpp0sOgFxaWkr79u3Jyclh3759REdHA2oqWtu2bblw4QLZ2dlERka60/RKufvuuzl//jxLlixxefzee++lffv2PPPMMzRt2pQTJ05QXFxROhufQs9NDVTc4czJyspix44dDB48uEyIAKGhoQwbNozDhw+zePFid5hbLUSErKysSvuLM2fOZMKECX7VPLWhxRiguMOZs2rVKgCuv/56p2O2fStXrrx0I2vI7t27OXnyZKVitP/R8De0GAOY2jpzdu/eDcBVV13ldCwhIQGAPXv21M7IGpCZmUlMTAzXXXed167pTbQYA5zaOHNyc3MBqFvXOUdTTEwMAKdPn66VfTUhMzOT7t27Ex4e7rVrehMtxgDHUzNzbH4/bwaCqqq/6O9oMQYBl+rMiYuLA+D8+fNOx2z7bOd4mj179nDs2DEtRo1/c6nOnNatWwNw5MgRp2NHjx4F8Fq80szMTOrUqePSmRQoaDEGCZfizLHlr9i8ebPTMdu+Xr16uc/ISsjMzKRbt25ERER45XpmoMUYRNicOdOnT6/W+T179iQ5OZn58+eTn59ftr+kpIR58+aRmJhI3759PWWuA2vWrAnoJipoMQYVycnJPPnkk0ycOLFazpyQkBBmzpxJTk4Oo0eP5sSJE5w6dYpx48aRnZ3NjBkziIqK8rjd+/bt49ChQ1qMmsCips6crl27sn79es6cOUOrVq1o2rQp2dnZrF69mt///vcetlaRmZlJVFQUnTt3rvLcxYsXY7FYsFgsHD16lJKSkrLPH374oResvXT03NQgZN68eQwfPpx169bRtWtXs82pklGjRnH48OGyGUEBip6bGoz4W8wcW3KbQEeLMUipqTPHLA4dOsTBgwe1GDWBS02dOWbxzTffEBkZSZcuXcw2xeNoMQYx/hAzJzMzky5duvj1aozqosUYxMTExPh8zJxg6S+C9qZq8N1sVkeOHCExMZGvv/7aazN9TER7UzW+68xZvXo1ERERfjH84g60GDU+68zJzMykc+fOLtdTBiJajBrAN505wdRfBC1GjRV7Z85///tfs83h+PHjZGdnB5UYtQNH44CvOHPmzp3LyJEjycnJITY21jQ7vIh24GgcmTp1qk84czIzM7n++uuDRYiAbqZqytGmTRufcOYEW38RtBg1LvCmM+f06dO89NJLfP3112VxdX755Rd++umnoBOj7jNqXGJbZrV27VpuvPFGp+MlJSVu6VMWFBQQHR2NiBAaGsp1111HQkIC//73vzl8+DBNmjSp9TX8hAwtRk2FuHLm7N+/nyeffJKRI0cyaNAgt1ynXr165OXllX0ODw+nqKiI0NBQ2rdvz+9+9zt69uxJampqIPchMxCNpgJ27twp4eHh8t5778nFixflL3/5i0RERAggzz33nNuu06JFCwEqfIWHhwsgf/vb39x2TR8kXdeMmkr505/+xCeffEKdOnU4ePBg2WLk1NRUsrKy3HKNnj17VvpdYWFhXHvttWzcuNGn5s66mQz/S9Wj8RpHjhzhwIEDnDhxgpCQEEpLS8uOff/995SWlhISUnsfYGJiotP322OxWJg9e3YgCxHQ3lSNCwoLC3n33Xdp2bIlCxcuBHASyvnz58nOznbL9Ro3blxh/ozQ0FBeeeUV2rZt65Zr+TK6ZtQ4cPDgQW6++WYOHDhAZT2YkJAQvvvuO1q1alXrazZq1MjltcLCwkhKSuLZZ5+t9TX8AV0zahy4+uqr+dOf/kRISEilzcKwsDC+++47t1yzcePGLrMLiwhz5swJ2KxT5dFi1DgxduxYvvnmG2JjYyvMAFxYWMi6devccr34+HinZnBoaCjPP/88nTp1css1/AHtTdVUyL59++jTpw8HDhygqKjI6XhERATnzp2rdc21fft2UlJSyj6HhYVx9dVXs337dq9ELPcR9ERxTcW0aNGC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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAs4AAAHSCAYAAAD8EE1RAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/MnkTPAAAACXBIWXMAAAsTAAALEwEAmpwYAACPJElEQVR4nO3dd3iTZfvG8W86GGXLUAQVWWWWVYbiQpY4cP4Y4utABQGlVEUpolQEioIWFBRxIQ5wiwtEVEREZVugCNiCAoKyyqpAx/P74yZtmiZt2iZN2p6f4+hRkjxJ7if1Pd6zd6/numyWZVmIiIiIiEiegvy9ABERERGRkkDBWURERETEAwrOIiIiIiIeUHAWEREREfGAgrOIiIiIiAcUnEVEREREPBDi7wV4olatWjRo0MDfyxARERGRUm7nzp0cOHDA5WMlIjg3aNCANWvW+HsZIiIiIlLKRUZGun1MpRoiIiIiIh5QcBYRERER8YCCs4iIiIiIB0pEjbMraWlp7N69m5MnT/p7KSI+UaFCBerXr09oaKi/lyIiIiKU4OC8e/duqlSpQoMGDbDZbP5ejohXWZbFwYMH2b17NxdeeKG/lyMiIiKU4FKNkydPUrNmTYVmKZVsNhs1a9bUX1REREQCSIkNzoBCs5Rq+u9bREQksJTo4FwYycneeZ2DBw/Stm1b2rZtyznnnEO9evWybp8+fTrP565Zs4aRI0cW6P0aNGhA69atad26NS1atGDcuHH57kampKTw4osvFuh9RERERMS1MhWc4+KgUSPzvahq1qzJhg0b2LBhA/fddx/R0dFZt8uVK0d6errb50ZGRvL8888X+D2///57Nm7cyKpVq0hOTmbo0KF5Hq/gLCIiIuI9ZSY4x8XBxInm3xMneic8O7vzzju577776Ny5M4888girVq3ioosuol27dlx88cVs3boVgGXLlnHttdcCEBsby+DBg7niiito2LChR4G6cuXKzJ49m08//ZRDhw5x/PhxunfvTvv27WndujULFy4EYMyYMSQlJdG2bVtGjx7t9jgRERERyV+J7apREPbQnJpqbqemZofomBjvvtfu3btZuXIlwcHBHD16lB9//JGQkBCWLl3K2LFj+eijj3I95/fff+f777/n2LFjhIeHM2zYsHxbkFWtWpULL7yQ7du306FDBz755BOqVq3KgQMH6NKlC3379mXKlCls2rSJDRs2AJCenu7yONXSioiIiOSv1Adn59Bs56vw/H//938EBwcDcOTIEe644w62b9+OzWYjLS3N5XOuueYaypcvT/ny5alTpw7//PMP9evXz/e9LMvK+j527FiWL19OUFAQe/bs4Z9//nF5vKvjzjnnnCKcsYiIiEjZUKpLNZKTYezY3KHZLjXVPO6tCwYBKlWqlPXvxx9/nG7durFp0yY+//xztxfzlS9fPuvfwcHBedZH2x07doydO3fStGlT3nnnHfbv38/atWvZsGEDZ599tsv38vQ4EREREcmtVAfnhg1h8mQIC3P9eFiYebxhQ9+8/5EjR6hXrx4Ac+fO9drrHj9+nOHDh3PDDTdQo0YNjhw5Qp06dQgNDeX777/nzz//BKBKlSocO3Ysx3pcHSciIiIi+SvVwRlMGca4cbnDc1iYud/bNc6OHnnkEWJiYmjXrp1Hu8j56datG61ataJTp06cf/75vPzyywAMGjSINWvW0Lp1a+bNm0ezZs0A0/mja9eutGrVitGjR7s9TkRERALUmbJMt7elWNksK/B/ApGRkaxZsybHfVu2bKF58+Yev4ZjrXNxhGYRbyjof+ciIlKKJMRCWgq0jwebzYTmddEQWh0iYv26tNLMVe60K/U7znb2nWdQaBYREZEAZ1kmNG+dYcKyPTRvnWHuD/x9z1Kp1HfVcBQTA/37+66mWURERMQrbDaz0wwmLG+dYf4dHpW9Ay3FrszsONspNIuIiEiJ4Bie7RSa/arMBWcRERGREsFenuHIXrYhfqHgLCIiIhJoHGuaw6NgYKb57ljzLMWuTNU4i4iIiJQINpvpnuFY02wv2witrnINP1FwLqSDBw/SvXt3APbt20dwcDC1a9cGYNWqVZQrVy7P5y9btoxy5cpx8cUX53ps7ty5jB49mvr163P8+HEaNmzI+PHjXR7r6NNPP6Vp06a0aNGikGclIiIiASMi1uws20OyPTwrNPtN2SnV8HID8Zo1a7JhwwY2bNjAfffdR3R0dNbt/EIzmOC8cuVKt4/379+f9evXs337dsaMGcNNN93Eli1b8nzNTz/9lMTExAKfi4iIiAQo55Cs0OxXZSM4J8TmrAey1w0lxHr1bdauXcvll19Ohw4d6N27N3v37gXg+eefp0WLFkRERDBgwAB27tzJ7NmziY+Pp23btvz44495vm63bt0YMmQIc+bMAeCVV16hY8eOtGnThptvvpnU1FRWrlzJZ599xujRo2nbti1JSUkujxMRERGRwin9wbmYGohblsUDDzzAhx9+yNq1axk8eDCPPfYYAFOmTGH9+vUkJCQwe/ZsGjRokGOX+tJLL8339du3b8/vv/8OwE033cTq1av57bffaN68Oa+99hoXX3wxffv2ZerUqWzYsIFGjRq5PE5ERETc0HhryUfpr3Eupgbip06dYtOmTfTs2ROAjIwM6tatC0BERASDBg3ihhtu4IYbbijU6ztORt+0aRPjxo0jJSWF48eP07t3b5fP8fQ4ERGRMi8hVuOtJV+lf8cZiqWBuGVZtGzZMqvOeePGjSxZsgSAL7/8khEjRrBu3To6duxIenp6gV9//fr1NG/eHIA777yTmTNnsnHjRsaPH8/JkyddPsfT40RERMo0jbcOKMePWeza5XBHAH3+ZSM4F0MD8fLly7N//35+/vlnANLS0ti8eTOZmZns2rWLbt268fTTT3PkyBGOHz9OlSpVOHbsmEev/cMPPzBnzhzuvfdeAI4dO0bdunVJS0vjnXfeyTrO+TXdHSciIiIO7Bts9j7J84Oy+yeri0Wx+uKFd2nRJIXbbrNMTPPRdWmFVfqDczE1EA8KCuLDDz/k0UcfpU2bNrRt25aVK1eSkZHBbbfdRuvWrWnXrh0jR46kevXqXHfddXzyySduLw587733aNu2LU2bNmXy5Ml89NFHWTvOTz31FJ07d6Zr1640a9Ys6zkDBgxg6tSptGvXjqSkJLfHiYiIiBONt/arvXuhXz+L60beStXQPcTdPh0bgbfzb7OsAFhFPiIjI1mzZk2O+7Zs2ZIVJPOVEKu6JSmRCvTfuYiIFJ7jRpuddpyLzXPPwdix8Pg4i9GN76LcxDehL9CKYv85uMqddqX/4kBQA3ERERFxz/mv0+3jc4ZoZQaf2LLF7DRfeSU88ADc0PMEDd95Cm5/F9IwX60IqM+/bARnUANxERERcU3jrYvVyZMQF2e+mjSBjQkWoV98SsNRo+Cvv8xB3YD+Z56wLjpgwnPZCc4iIiIi7uiv08Xihx9g6FDYuhUGDYIZI5MIuu4BWLTIHBBeGwbuh2sDc+dfwVlEREQE9NdpH1u9Gq64Ai68EJZ8dpKe656Gy+Lg1CmoVg0mToSu/0Lm0YDd+VdwFhERERGfsCzYtg3CwyEyEl57DW49azEVou+HpCRz0P/+B1OnwtlnZz8pQHf+S387OhEREREpdjt3wjXXQLt28OefYNu9i8Ff3kyFG/uY0NyiBSxbBvPmZYdmCOidfwXnIti3bx8DBgygUaNGdOjQgauvvppt27axc+dOWrVq5ZP3vOKKKwgPD6dNmzZ07NiRDRs2eP097rzzTi688ELatm1LmzZt+Pbbb3MdExcXR2hoKG+99VaO+9955x0iIiJo3bo1F198Mb/99pvH73vo0CF69uxJkyZN6NmzJ4cPH3Z53COPPELLli1p3rw5I0eOxLIsjh07Rtu2bbO+atWqxahRowCYO3cutWvXznrs1VdfzfO1REREpPDS0017uZYtYflyePqp05w3/xlo3hw+/hgqVTI7zBs2wOWX+3u5BaLgXEiWZXHjjTdyxRVXkJSUxNq1a4mLi+Off/7x6ntkZmbmuv+dd97ht99+Y/jw4YwePdpr7+do6tSpbNiwgenTp3PffffleOytt95i8eLFbNmyhWeffZalS5dmPXbhhRfyww8/sHHjRh5//HGGDBni8XtOmTKF7t27s337drp3786UKVNyHbNy5Up++uknEhIS2LRpE6tXr+aHH36gSpUqWePON2zYwAUXXMBNN92U9bz+/ftnPXbPPffk+VoiIiJSOKdOwUUXwUMPmTZzSa8t44HX2hIU8yicOAG33AK//w4PPwyhof5eboEpOBfS999/T2hoaI5Q2aZNGy699NIcx2VkZDB69Gg6duxIREQEL7/8MgDHjx+ne/futG/fntatW7Nw4UIAdu7cSXh4OLfffjutWrViV45h7TlddNFF7NmzB4ATJ04wePBgOnXqRLt27bJeLzU1lX79+tGiRQtuvPFGOnfu7Lapd37vAbB06VLmzZvHV199RePGjVmyZAnjx4/P2lm++OKLqVGjBgBdunRh9+7dHr/XwoULueOOOwC44447+PTTT3MdY7PZOHnyJKdPn+bUqVOkpaVxtuOfd4Bt27bx77//5vpZFOa1REREJH/p6eZ7+fJw9dXw2Zx9fFbtNs4e0M00bG7cGBYvhg8+gPr1/bvYIig1FwdecUXu+/r1g+HDITXV/BCd3Xmn+TpwwPwC5GjZsrzfb9OmTXTo0CHfdb322mtUq1aN1atXc+rUKbp27UqvXr0477zz+OSTT6hatSoHDhygS5cu9O3bF4Dt27fz5ptv0qVLlzxfe/Hixdxwww0ATJo0iSuvvJLXX3+dlJQUOnXqRI8ePXjppZeoUaMGiYmJbNq0ibZt2+a7ZnfvAdCjRw969OiRdbtOnTr89NNPbs+9T58+WbcvvfRSjh07luu4adOm0aNHD/755x/q1q0LwDnnnONy9/6iiy6iW7du1K1bF8uyuP/++3NN1luwYAH9+/fH5lAT9dFHH7F8+XKaNm1KfHw85513nkevJSIiInlbtAjuvx/efhsu6pjOk7VegofHwdGjUKGCGQk4erT5dwlXaoJzoFqyZAkJCQl8+OGHABw5coTt27dTv359xo4dy/LlywkKCmLPnj1ZQfGCCy7IMzQPGjSI06dPc/z48awa5yVLlvDZZ58xbdo0AE6ePMlff/3FihUriIqKAqBVq1ZERER4tO7Ro0czduxYdu/ezc8//1zg8/7+++957bXXWLFiRdZ9P/74o8fPt9lsOYKv3R9//MGWLVuydrJ79uzJjz/+mGN3ecGCBTlqr6+77joGDhxI+fLlefnll7njjjv47rvvPHotERERce2ff2DUKFiwwJQvV038BYYPM7XLYK4MfP55aNjQn8v0qlITnPPaIQ4Ly/vxWrXy32F21rJly6wwnBfLsnjhhRfo3bt3jvvnzp3L/v37Wbt2LaGhoTRo0ICTJ08CUKlSpTxf85133qFDhw6MHj2aBx54gI8//hjLsvjoo48IDw8v2Im4MXXqVG655RZeeOEFBg8ezNq1az1+bkJCAvfccw+LFi2iZs2aWffnt+N89tlns3fvXurWrcvevXupU6dOrmM/+eQTunTpQuXKlQHo06cPP//8c1bY/e2330hPT8/x1wDHNdxzzz088sgjHr2WiIiIuDZvHkRFmb/qTx1zkOh/xhB8z5mL788/3wTmvn0DqiOGN6jGuZCuvPJKTp06xZw5c7LuS0hIyLWr2rt3b1566SXS0tIAU3974sQJjhw5Qp06dQgNDeX777/nzz//LND722w2nnrqKX755Rd+//13evfuzQsvvJDVFWL9+vUAdO3alffffx+AxMRENm7cmPUat99+O6tWrcrzfe6//34yMzP5+uuvPVrXX3/9xU033cRbb71F06ZNczz2448/5riAz/5lL/3o27cvb775JgBvvvkm119/fa7XP//88/nhhx9IT08nLS2NH374IUd5xfz58xk4cGCO5+zduzfr35999lnW8fm9loiIiLj2zz/QNiKTPx9/lYfnNCX4jVfNxX4xMZCYCNdfX+pCMyg4F5rNZuOTTz5h6dKlNGrUiJYtWxITE8M555yT47h77rmHFi1a0L59e1q1asXQoUNJT09n0KBBrFmzhtatWzNv3jyaNWtW4DVUrFiRhx56iKlTp/L444+TlpZGREQELVu25PHHHwdg+PDh7N+/nxYtWjBu3DhatmxJtWrVABP0zz333HzPc9y4cTzzzDMerWnChAkcPHiQ4cOH07ZtWyIjIz0+nzFjxvDNN9/QpEkTli5dypgxYwBYs2ZNVieMW265hUaNGtG6dWvatGlDmzZtuO6667Je4/33388VnJ9//nlatmxJmzZteP7555k7d65HryUiIiLGqVMwYQLY/9j+YLf1fHe6K+c8fi8cOmRaaCQkwOTJpt1cKWWzSkDj2sjIyFydILZs2aLdQQ9kZGSQlpZGhQoVSEpKokePHmzdupWTJ09y991388EHH/h7iZIH/XcuIiL+9uOPMGSI6SI3esgRnin/OMyaBZmZULeuadrcv3+p2WF2lTvtSk2Ns7iWmppKt27dSEtLw7IsXnzxRcqVK0e5cuUUmkVERMStw4dhzBiYMwcuON9i/UPv0Pbth02dRnCwuTLwySehalV/L7XYKDiXclWqVClQ32YRERERgO++g1dfhafvSOShpOEEP3tmSNjFF8OLL0KbNv5doB8oOIuIiIgIAH/9BWvXwo03wk29jrN/8FOcNfc5M+GkVi145hm44w4IKpuXySk4i4iIiJRxGRnwwgswbhyEVbS4+r+PKf/oKM7avdvULg8dai78O+ssfy/VrxScRURERMqwDRvg3nthzRq4+/I/eJ4HKD9osXmwQwd46SXo2NGvawwUCs4iIiIiZdTu3dCpE5xb4z823TyFFl88je3UKahe3ewwDxliLgQUQH2ci2T37t1cf/31NGnShIYNG3L//fdz6tQpr7/PsmXLWLlyZb7H7dy5k4oVK9K2bdusr9OnTzN37lzuv//+HMdeccUVWRcNNmjQgNatW2c9Z+TIkbleOzY2lnr16tG2bVtatGjB/PnzvXNyDubOnUvt2rVp27YtzZo1Iz4+Ptcxy5Yto3Llyll9ne02bNjARRddRMuWLYmIiOC9997z+H0ty2LkyJE0btyYiIgI1q1b5/K4+fPn07p1ayIiIrjqqqs4cOAAAP3798/67Bo0aEDbtm0BWLVqVdb9bdq04ZNPPsl6LcfPvCC9rkVERLzh99/N9/r1Ycmor0gKa0XLjyaY0HzHHbB1KwwbptDszCoBOnTokOu+xMREP6wkW2ZmptWxY0fr9ddftyzLstLT063BgwdbI0eO9Pp7jR8/3po6dWq+x+3YscNq2bJlrvvfeOMNa8SIETnuu/zyy63Vq1dblmVZF1xwgbV//36P17Bt2zarSpUq1unTpz09BY84rvPAgQNWzZo1rb/++ivr8Y0bN1rNmze3Nm7caPXr18+KjY3Nemzr1q3Wtm3bLMuyrD179ljnnHOOdfjwYY/e98svv7SuuuoqKzMz0/r555+tTp065TomLS3Nql27dtbnNHr0aGv8+PG5jnvwwQetJ5980rIsyzpx4oSVlpZmWZZl/f3331bt2rWzbnvymVuW//87FxGR0uWffyxr0CDLstksa92nf1rWDTdYFpivVq0sa/lyfy/R71zlTjvtOBfSd999R4UKFbjrrrsACA4OJj4+nnnz5nH8+PFcu7zXXnsty5YtA2DYsGFERkbSsmVLxo8fn3VMgwYNGD9+PO3bt6d169b8/vvv7Ny5k9mzZxMfH0/btm1zjfT2hyZNmhAWFsbhw4cBmDp1Kh07diQiIiLH+Tz11FOEh4dzySWXMHDgQKZNm+bxe9SsWZPGjRtnjcves2cPd999N59++imtWrXi3XffZevWrbz++usANG3alCZNmgBw7rnnUqdOHfbv3+/Rey1cuJDbb78dm81Gly5dSElJyTGmG8yutGVZnDhxAsuyOHr0aK6pi5Zl5ZhcGBYWRkiIqYY6efIktlLSGF5EREoey4I33oDmzeGT906z5MoptL21OXz6KVSuDNOmwbp1cOmluZ6bnFz86w1UpaPG2VeBJI+hips3b6ZDhw457qtatSoNGjTgjz/+yPNlJ02axFlnnUVGRgbdu3cnISGBiIgIAGrVqsW6det48cUXmTZtGq+++ir33XcflStX5uGHH853yUlJSVmlAl27dmXWrFkAvPfee6xYsSLrOOc1duvWjeAzf4654447iI6Odvse69ato0mTJtSpU4clS5awfft2Vq1ahWVZ9O3bl+XLl1OxYkU++ugjfvvtN9LS0mjfvn2uzysvf/31FydPnsz6XOrVq8evv/6a9XhwcDDvvvuuy+euWrWK06dP06hRIwCio6P5/vvvcx03YMAAxowZw549ezjvvPOy7q9fvz579uyhbt26WfeFhoby0ksv0bp1aypVqkSTJk2yPlu7H3/8kbPPPjsrwAP8+uuvDB48mD///JO33norK0jbbDZ69eqFzWZj6NChDBkyxOPPRkREpCAsC667Dr78Eh5o9T1Tqw+n/LdnajX69TOT/+rVc/ncuDgYO9aUO8fEFOOiA1TpCM4lzPvvv8+cOXNIT09n7969JCYmZgXEm266CYAOHTrw8ccfF/i1GzVqxIYNG3Ld379/f2bOnJl1+4orrsjx+Pfff0+tWrXyfO34+HjeeOMNtm3bxueffw7AkiVLWLJkCe3atQPg+PHjbN++nWPHjnH99ddToUIFKlSowHXXXefR+t977z2WL1/O77//zsyZM6lQoYJHz7Pbu3cv//vf/3jzzTcJOtNj0lWtdEGlpaXx0ksvsX79eho2bMgDDzxAXFwc48aNyzpm/vz5WbvNdp07d2bz5s1s2bKFO+64gz59+lChQgVWrFhBvXr1+Pfff+nZsyfNmjXjsssuK/I6RURE7NLSICTE7C/e0nUv0/99iMarz1yj1KSJGZvds6fb58fFwcSJ5t/272U9PBe5VGPXrl1069aNFi1a0LJlS2bMmAHAoUOH6NmzJ02aNKFnz55Zf9a3PLwQq0Cyq3O8+5WHFi1asHbt2hz3HT16lH379hEeHk5ISAiZmZlZj508eRKAHTt2MG3aNL799lsSEhK45pprsh4DKF++PGB2VNPT04v+2XhRdHQ0mzdv5qOPPuLuu+/m5MmTWJZFTEwMGzZsYMOGDfzxxx/cfffdhX6P/v37k5CQwMqVKxkzZgz79u3z+LlHjx7lmmuuYdKkSXTp0iXHuh0vmLR/TZkyBTC72bt27co6fvfu3dRz+s3b/stIo0aNsNls9OvXL8cFm+np6Xz88cf079/f5dqaN29O5cqV2bRpU9Z7AtSpU4cbb7yRVatWeXyeIiIi+fnpJ2jbFj5ckA4zZnBnXLgJzRUqmBS8caNHoTk11dxOTTW34+KKZ/2BqsjBOSQkhGeffZbExER++eUXZs2aRWJiIlOmTKF79+5s376d7t27Z4WURYsWsX37drZv386cOXMYNmxYkU/CH7p3705qairz5s0DICMjg4ceeoj777+fihUr0qBBAzZs2EBmZia7du3KCkZHjx6lUqVKVKtWjX/++YdFixbl+15VqlTh2LFjPj2fgujbty+RkZG8+eab9O7dm9dff53jx48Dphb533//pWvXrnz++eecPHmS48eP88UXX2Q9f+bMmTl2v12JjIzkf//7X9YvYvk5ffo0N954I7fffju33HJLjsfi4+Ozgr3j15gxY7LOZ968eViWxS+//EK1atVylGmACbqJiYlZddPffPMNzZs3z3p86dKlNGvWjPr162fdt2PHjqxffv78809+//13GjRowIkTJ7J+nidOnGDJkiW0atXKo/MUERHJS0qKaYZxySUQfnAlvcdFwqhRcOyYqddITITHHoMzG3WuOIdmO4VnL5Rq1K1bNytkVKlShebNm7Nnzx4WLlyYdTHcHXfcwRVXXMHTTz/t9kIs56AS6Gw2G5988gkjRozgqaeeYv/+/fTv35/HHnsMMPXFF154IS1atKB58+a0b98egDZt2tCuXTuaNWvGeeedR9euXfN9r+uuu45bbrmFhQsX8sILL3D48GHWrFnDhAkTvHY+jjXOERERWb8QuPPEE09w6623smXLFrZs2cJFF10EQOXKlXn77bfp2LEjffv2JSIigrPPPpvWrVtTrVo1AH7//XePzvvRRx+lffv2jB07lipVquR57Pvvv8/y5cs5ePAgc+fOBUx7O3u9d16uvvpqvvrqKxo3bkxYWBhvvPFG1mNt27Zlw4YNnHvuuYwfP57LLruM0NBQLrjggqz3AViwYEGuMo0VK1YwZcoUQkNDCQoK4sUXX6RWrVokJydz4403Aman+tZbb+Wqq67Kd50iIiJ5+eIL03Y5Y99+fmk5hs6bzQX0XHABPP889O2b72skJ5uaZndSU83j/ftDw4ZeWnhJ4s32HTt27LDOO+8868iRI1a1atWy7s/MzMy6fc0111g//vhj1mNXXnllVls0dwKxHZ2zn376yTr//POttWvX+nspAePYsWOWZZm2bB06dMj6bK655hrr1KlT/lxaiRFo/52LiEjgem9+hvXUeS9baVVrmKLT0FDLeuwxyzpxokCvM3myZYWFua5jDQszj5dmebWj89rFgcePH+fmm29m+vTpVK1aNcdjNputwK245syZw5w5cwA8bivmTxdffDF/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", "text/plain": [ - "" + "
" ] }, "metadata": {}, @@ -698,62 +446,22 @@ } ], "source": [ - "from PIL import Image\n", - "file = Image.open(circuit.draw())\n", - "file.show()\n", - "file.close()" - ] - }, - { - "cell_type": "markdown", - "id": "46753da7", - "metadata": {}, - "source": [ - "### Finally, let's make homomorphic inference" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "c0b246f7", - "metadata": {}, - "outputs": [], - "source": [ - "homomorphic_predictions = []\n", - "for x_i in (int(x_i[0]) for x_i in x_q):\n", - " inference = QuantizedArray(circuit.run(x_i), y_q.parameters)\n", - " homomorphic_predictions.append(inference.dequantize())\n", - "homomorphic_predictions = np.array(homomorphic_predictions, dtype=np.float32)" - ] - }, - { - "cell_type": "markdown", - "id": "68f67b3f", - "metadata": {}, - "source": [ - "### And visualize it" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "92c7f2f5", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ax.plot(inputs, homomorphic_predictions, color=\"green\")\n", - "display(fig)" + "plt.clf()\n", + "fig, ax = plt.subplots(1, figsize=(12,8))\n", + "fig.patch.set_facecolor(\"white\")\n", + "s1 = ax.scatter(x_train, y_train, c=\"blue\", marker=\"D\")\n", + "s2 = ax.scatter(x_test, y_test, c=\"orange\", marker=\"x\")\n", + "p1 = ax.plot(x_test, y_pred, c=\"blue\", marker=None, linestyle=\"dashed\")\n", + "p2 = ax.plot(x_test, y_test_pred_fhe, c=\"red\", marker=None, linewidth=2)\n", + "ax.legend([s1, s2, p1[0], p2[0]],\n", + " [\n", + " \"Train Data\",\n", + " \"Test Data\",\n", + " f\"Clear Reg, R^2={sklearn_r2:.4f}\",\n", + " f\"Quant. FHE Reg, R^2={homomorphic_test_error:.4f}\"\n", + " ]\n", + ")\n", + "display(fig)\n" ] }, { diff --git a/docs/user/advanced_examples/QuantizedLogisticRegression.ipynb b/docs/user/advanced_examples/QuantizedLogisticRegression.ipynb index ee48a14e4..53f786a22 100644 --- a/docs/user/advanced_examples/QuantizedLogisticRegression.ipynb +++ b/docs/user/advanced_examples/QuantizedLogisticRegression.ipynb @@ -26,7 +26,37 @@ "outputs": [], "source": [ "import numpy as np\n", - "import torch" + "from sklearn.linear_model import LogisticRegression\n", + "from sklearn.datasets import make_classification\n", + "from sklearn.model_selection import train_test_split\n", + "\n", + "from copy import deepcopy\n", + "from typing import Any, Dict\n", + "\n", + "from tqdm import tqdm" + ] + }, + { + "cell_type": "markdown", + "id": "86b77c19", + "metadata": {}, + "source": [ + "### Now import Concrete quantization tools " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "94df1602", + "metadata": {}, + "outputs": [], + "source": [ + "from concrete.quantization import (\n", + " QuantizedArray,\n", + " QuantizedLinear,\n", + " QuantizedModule,\n", + " QuantizedSigmoid,\n", + ")" ] }, { @@ -39,7 +69,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "id": "67330862", "metadata": {}, "outputs": [], @@ -52,79 +82,227 @@ }, { "cell_type": "markdown", - "id": "0df30d0e", + "id": "d4f43095", "metadata": {}, "source": [ - "### We need an inputset, a handcrafted one for simplicity" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "caef5aed", - "metadata": {}, - "outputs": [], - "source": [ - "x = torch.tensor(\n", - " [\n", - " [1, 1],\n", - " [1, 1.5],\n", - " [1.5, 1.2],\n", - " [1, 2],\n", - " [2, 1],\n", - " [4, 1],\n", - " [4, 1.5],\n", - " [3.5, 1.8],\n", - " [3, 2],\n", - " [4, 2],\n", - " ]\n", - ").float()\n", - "y = torch.tensor(\n", - " [\n", - " [0],\n", - " [0],\n", - " [0],\n", - " [0],\n", - " [0],\n", - " [1],\n", - " [1],\n", - " [1],\n", - " [1],\n", - " [1],\n", - " ]\n", - ").float()" - ] - }, - { - "cell_type": "markdown", - "id": "b16cd2e1", - "metadata": {}, - "source": [ - "### Let's visualize our inputset to get a grasp of it" + "### And, finally, the FHE compiler" ] }, { "cell_type": "code", "execution_count": 4, - "id": "ad72aad0", + "id": "3b76a5f6", "metadata": {}, "outputs": [], "source": [ - "plt.ioff()\n", - "fig, ax = plt.subplots(1)" + "import concrete.numpy as hnp" + ] + }, + { + "cell_type": "markdown", + "id": "34959f0a", + "metadata": {}, + "source": [ + "### Define our Quantized Logistic Regression model" ] }, { "cell_type": "code", "execution_count": 5, + "id": "a12ce041", + "metadata": {}, + "outputs": [], + "source": [ + "class QuantizedLogisticRegression(QuantizedModule):\n", + " \"\"\"\n", + " Quantized Logistic Regression\n", + " Building on top of QuantizedModule, this class will chain together a linear transformation\n", + " and an inverse-link function, in this case the logistic function\n", + " \"\"\"\n", + "\n", + " @staticmethod\n", + " def from_sklearn(sklearn_model, calibration_data):\n", + " \"\"\"Create a Quantized Logistic Regression initialized from a sklearn trained model\"\"\"\n", + " if sklearn_model.coef_.ndim == 1:\n", + " weights = np.expand_dims(sklearn_model.coef_, 1)\n", + " else:\n", + " weights = sklearn_model.coef_.transpose()\n", + "\n", + " bias = sklearn_model.intercept_\n", + " # In our case we have two data dimensions, the precision of the weights needs to be 2 bits, \n", + " # as for now we need the quantized values to be greater than zero for weights\n", + " # Thus, to insure a maximum of 7 bits in the output of the linear transformation, we choose\n", + " # 4 bits for the data and the minimum of 1 for the bias\n", + " return QuantizedLogisticRegression(4, 2, 1, 6, weights, bias, calibration_data)\n", + "\n", + " def __init__(self, q_bits, w_bits, b_bits, out_bits, weights, bias, calibration_data) -> None:\n", + " \"\"\"\n", + " Create the Logistic regression with different quantization bit precisions:\n", + "\n", + " Quantization Parameters - Number of bits:\n", + " q_bits (int): bits for input data, insuring that the number of bits of\n", + " the w . x + b operation does not exceed 7 for the calibration data\n", + " w_bits (int): bits for weights: in the case of a univariate regression this\n", + " can be 1\n", + " b_bits (int): bits for bias (this is a single value so a single bit is enough)\n", + " out_bits (int): bits for the result of the linear transformation (w.x + b).\n", + " In the case of Logistic Regression the result of the linear\n", + " transformation is input to a univariate inverse-link function, so\n", + " this value can be 7\n", + "\n", + " Other parameters:\n", + " weights: a numpy nd-array of weights (Nxd) where d is the data dimensionality\n", + " bias: a numpy scalar\n", + " calibration_data: a numpy nd-array of data (Nxd)\n", + " \"\"\"\n", + " self.n_bits = out_bits\n", + "\n", + " # We need to calibrate to a sufficiently low number of bits\n", + " # so that the output of the Linear layer (w . x + b)\n", + " # does not exceed 7 bits\n", + " self.q_calibration_data = QuantizedArray(q_bits, calibration_data)\n", + "\n", + " # Quantize the weights and create the quantized linear layer\n", + " q_weights = QuantizedArray(w_bits, weights)\n", + " q_bias = QuantizedArray(b_bits, bias)\n", + " q_layer = QuantizedLinear(out_bits, q_weights, q_bias)\n", + "\n", + " # Store quantized layers\n", + " quant_layers_dict: Dict[str, Any] = {}\n", + "\n", + " # Calibrate the linear layer and obtain calibration_data for the next layers\n", + " calibration_data = self._calibrate_and_store_layers_activation(\n", + " \"linear\", q_layer, calibration_data, quant_layers_dict\n", + " )\n", + "\n", + " # Add the inverse-link for inference.\n", + " # This needs to be quantized since it's computed in FHE,\n", + " # but we can use 7 bits of output since, in this case,\n", + " # the result of the inverse-link is not processed by any further layers\n", + " # Seven bits is the maximum precision but this could be lowered to improve speed\n", + " # at the possible expense of higher deviance of the regressor\n", + " q_logit = QuantizedSigmoid(n_bits=7)\n", + "\n", + " # Now calibrate the inverse-link function with the linear layer's output data\n", + " calibration_data = self._calibrate_and_store_layers_activation(\n", + " \"invlink\", q_logit, calibration_data, quant_layers_dict\n", + " )\n", + "\n", + " # Finally construct our Module using the quantized layers\n", + " super().__init__(quant_layers_dict)\n", + "\n", + " def _calibrate_and_store_layers_activation(\n", + " self, name, q_function, calibration_data, quant_layers_dict\n", + " ):\n", + " \"\"\"\n", + " This function calibrates a layer of a quantized module (e.g. linear, inverse-link,\n", + " activation, etc) by looking at the input data, then computes the output of the quantized\n", + " version of the layer to be used as input to the following layers\n", + " \"\"\"\n", + "\n", + " # Calibrate the output of the layer\n", + " q_function.calibrate(calibration_data)\n", + " # Store the learned quantized layer\n", + " quant_layers_dict[name] = q_function\n", + " # Create new calibration data (output of the previous layer)\n", + " q_calibration_data = QuantizedArray(self.n_bits, calibration_data)\n", + " # Dequantize to have the value in clear and ready for next calibration\n", + " return q_function(q_calibration_data).dequant()\n", + "\n", + " def quantize_input(self, x):\n", + " q_input_arr = deepcopy(self.q_calibration_data)\n", + " q_input_arr.update_values(x)\n", + " return q_input_arr\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "0df30d0e", + "metadata": {}, + "source": [ + "### We need a training set, a handcrafted one for simplicity. Let's also define a grid on which to test our classifier" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "caef5aed", + "metadata": {}, + "outputs": [], + "source": [ + "X, y = make_classification(\n", + " n_features=2,\n", + " n_redundant=0,\n", + " n_informative=2,\n", + " random_state=2,\n", + " n_clusters_per_class=1,\n", + " n_samples=100,\n", + ")\n", + "\n", + "rng = np.random.RandomState(2)\n", + "X += 2 * rng.uniform(size=X.shape)\n", + "\n", + "b_min = np.min(X, axis=0)\n", + "b_max = np.max(X, axis=0)\n", + "\n", + "x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=42)\n", + "\n", + "x_test_grid, y_test_grid = np.meshgrid(\n", + " np.linspace(b_min[0], b_max[0], 30), np.linspace(b_min[1], b_max[1], 30)\n", + ")\n", + "x_grid_test = np.vstack([x_test_grid.ravel(), y_test_grid.ravel()]).transpose()\n" + ] + }, + { + "cell_type": "markdown", + "id": "0b209247", + "metadata": {}, + "source": [ + "### Train a logistic regression with sklearn on the training set" + ] + }, + { + "cell_type": "code", + "execution_count": 7, "id": "ec57fede", "metadata": {}, "outputs": [ { "data": { - "image/png": "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\n", "text/plain": [ - "
" + "LogisticRegression()" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "logreg = LogisticRegression()\n", + "logreg.fit(x_train, y_train)" + ] + }, + { + "cell_type": "markdown", + "id": "5be6c7d5", + "metadata": {}, + "source": [ + "### Let's visualize our data set and initial classifier to get a grasp of it" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "f7076523", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" ] }, "metadata": {}, @@ -132,27 +310,24 @@ } ], "source": [ - "x_min, x_max = x[:, 0].min(), x[:, 0].max()\n", - "x_deviation = x_max - x_min\n", + "y_score_grid = logreg.predict_proba(x_grid_test)[:,1]\n", "\n", - "y_min, y_max = x[:, 1].min(), x[:, 1].max()\n", - "y_deviation = y_max - y_min\n", - "\n", - "ax.set_xlim(x_min - (x_deviation / 10), x_max + (x_deviation / 10))\n", - "ax.set_ylim(y_min - (y_deviation / 10), y_max + (y_deviation / 10))\n", - "\n", - "ax.scatter(\n", - " np.array([x_i[0] for x_i, y_i in zip(x, y) if y_i == 0], dtype=np.float32),\n", - " np.array([x_i[1] for x_i, y_i in zip(x, y) if y_i == 0], dtype=np.float32),\n", - " marker=\"x\",\n", - " color=\"red\",\n", - ")\n", - "ax.scatter(\n", - " np.array([x_i[0] for x_i, y_i in zip(x, y) if y_i == 1], dtype=np.float32),\n", - " np.array([x_i[1] for x_i, y_i in zip(x, y) if y_i == 1], dtype=np.float32),\n", - " marker=\"o\",\n", - " color=\"blue\",\n", + "plt.ioff()\n", + "plt.clf()\n", + "fig, ax = plt.subplots(1, figsize=(12,8))\n", + "fig.patch.set_facecolor('white')\n", + "ax.contourf(x_test_grid, y_test_grid, y_score_grid.reshape(x_test_grid.shape), cmap='coolwarm')\n", + "CS1 = ax.contour(\n", + " x_test_grid,\n", + " y_test_grid,\n", + " y_score_grid.reshape(x_test_grid.shape),\n", + " levels=[0.5],\n", + " linewidths=2,\n", ")\n", + "CS1.collections[0].set_label(\"Sklearn decision boundary\")\n", + "ax.scatter(x_train[:,0], x_train[:,1],c=y_train, marker=\"D\", cmap=\"jet\")\n", + "ax.scatter(x_test[:,0], x_test[:,1], c=y_test, marker=\"x\", cmap=\"jet\")\n", + "ax.legend(loc=\"upper right\")\n", "display(fig)" ] }, @@ -161,24 +336,18 @@ "id": "996fbe05", "metadata": {}, "source": [ - "### Now, we need a model so let's define it" + "### Calibrate the model for quantization using both training and test data\n" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 9, "id": "06ed91dd", "metadata": {}, "outputs": [], "source": [ - "class Model(torch.nn.Module):\n", - " def __init__(self, n):\n", - " super(Model, self).__init__()\n", - " self.fc = torch.nn.Linear(n, 1)\n", - "\n", - " def forward(self, x):\n", - " output = torch.sigmoid(self.fc(x))\n", - " return output" + "calib_data = X \n", + "q_logreg = QuantizedLogisticRegression.from_sklearn(logreg, calib_data)" ] }, { @@ -186,78 +355,19 @@ "id": "cd74c5e7", "metadata": {}, "source": [ - "### And create one\n", - "\n", - "The main purpose of this tutorial is not to train a logistic regression model but to use it homomorphically. So we will not discuss about how the model is trained." + "### Now, we can compile our model to FHE, taking as possible input set all of our dataset" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 10, "id": "b8f8f95b", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch: 1 | Loss: 0.693336546421051\n", - "Epoch: 101 | Loss: 0.1125209778547287\n", - "Epoch: 201 | Loss: 0.07049673795700073\n", - "Epoch: 301 | Loss: 0.050856731832027435\n", - "Epoch: 401 | Loss: 0.039525073021650314\n", - "Epoch: 501 | Loss: 0.0322115495800972\n", - "Epoch: 601 | Loss: 0.027129750698804855\n", - "Epoch: 701 | Loss: 0.023406751453876495\n", - "Epoch: 801 | Loss: 0.02056846395134926\n", - "Epoch: 901 | Loss: 0.018336370587348938\n", - "Epoch: 1001 | Loss: 0.01653693988919258\n", - "Epoch: 1101 | Loss: 0.015056520700454712\n", - "Epoch: 1201 | Loss: 0.013817812316119671\n", - "Epoch: 1301 | Loss: 0.012766523286700249\n", - "Epoch: 1401 | Loss: 0.01186333317309618\n", - "Epoch: 1501 | Loss: 0.011079175397753716\n", - "Epoch: 1601 | Loss: 0.010392050258815289\n", - "Epoch: 1701 | Loss: 0.009785104542970657\n", - "Epoch: 1801 | Loss: 0.009245104156434536\n", - "Epoch: 1901 | Loss: 0.008761593140661716\n", - "Epoch: 2001 | Loss: 0.00832616537809372\n", - "Epoch: 2101 | Loss: 0.007932038977742195\n", - "Epoch: 2201 | Loss: 0.007573576178401709\n", - "Epoch: 2301 | Loss: 0.007246167398989201\n", - "Epoch: 2401 | Loss: 0.006945951841771603\n", - "Epoch: 2501 | Loss: 0.006669704802334309\n", - "Epoch: 2601 | Loss: 0.006414605770260096\n", - "Epoch: 2701 | Loss: 0.006178391166031361\n", - "Epoch: 2801 | Loss: 0.00595900509506464\n", - "Epoch: 2901 | Loss: 0.005754708778113127\n", - "Epoch: 3001 | Loss: 0.005564006045460701\n", - "Epoch: 3101 | Loss: 0.0053855921141803265\n", - "Epoch: 3201 | Loss: 0.005218283273279667\n", - "Epoch: 3301 | Loss: 0.005061114672571421\n", - "Epoch: 3401 | Loss: 0.004913175944238901\n", - "Epoch: 3501 | Loss: 0.004773670341819525\n" - ] - } - ], + "outputs": [], "source": [ - "model = Model(x.shape[1])\n", + "X_q = q_logreg.quantize_input(X)\n", "\n", - "optimizer = torch.optim.SGD(model.parameters(), lr=1)\n", - "criterion = torch.nn.BCELoss()\n", - "\n", - "epochs = 3501\n", - "for e in range(1, epochs + 1):\n", - " optimizer.zero_grad()\n", - "\n", - " out = model(x)\n", - " loss = criterion(out, y)\n", - "\n", - " loss.backward()\n", - " optimizer.step()\n", - "\n", - " if e % 100 == 1 or e == epochs:\n", - " print(\"Epoch:\", e, \"|\", \"Loss:\", loss.item())" + "engine = q_logreg.compile(X_q)" ] }, { @@ -265,22 +375,31 @@ "id": "b608faef", "metadata": {}, "source": [ - "### Time to make some predictions" + "### Time to make some predictions, first in the clear" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 11, "id": "97eaf932", "metadata": {}, "outputs": [], "source": [ - "contour_plot_x_data = np.linspace(x_min - (x_deviation / 10), x_max + 2 * (x_deviation / 10), 100)\n", - "contour_plot_y_data = np.linspace(y_min - (y_deviation / 10), y_max + 2 * (y_deviation / 10), 100)\n", - "contour_plot_x_data, contour_plot_y_data = np.meshgrid(contour_plot_x_data, contour_plot_y_data)\n", + "# Test the original classifier\n", + "y_pred_test = np.asarray(logreg.predict(x_test))\n", "\n", - "inputs = np.stack((contour_plot_x_data.flatten(), contour_plot_y_data.flatten()), axis=1)\n", - "predictions = model(torch.tensor(inputs).float()).detach().numpy()" + "# Now that the model is quantized, predict on the test set\n", + "x_test_q = q_logreg.quantize_input(x_test)\n", + "q_y_score_test = q_logreg.forward_and_dequant(x_test_q)\n", + "q_y_pred_test = (q_y_score_test > 0.5).astype(np.int32)\n", + "\n", + "# Predict sklearn classifier probabilities on the domain\n", + "y_score_grid = logreg.predict_proba(x_grid_test)[:, 0]\n", + "\n", + "# Predict quantized classifier probabilities on the whole domain to plot contours\n", + "grid_test_q = q_logreg.quantize_input(x_grid_test)\n", + "q_y_score_grid = q_logreg.forward_and_dequant(grid_test_q)\n", + "q_y_pred_test = (q_y_score_test > 0.5).astype(np.int32)\n" ] }, { @@ -288,48 +407,63 @@ "id": "8fb62d52", "metadata": {}, "source": [ - "### Let's visualize our predictions to see how our model performs" + "### Now let's predict using the quantized FHE classifier" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 12, "id": "bc999411", "metadata": {}, "outputs": [ { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 40/40 [01:11<00:00, 1.80s/it]\n" + ] } ], "source": [ - "contour = ax.contourf(\n", - " contour_plot_x_data,\n", - " contour_plot_y_data,\n", - " predictions.round().reshape(contour_plot_x_data.shape),\n", - " cmap=\"gray\",\n", - " alpha=0.50,\n", - ")\n", - "display(fig)" + "non_homomorphic_correct = 0\n", + "homomorphic_correct = 0\n", + "\n", + "# Track the samples that are wrongly classified due to quantization issues\n", + "q_wrong_predictions = np.zeros((0, 2), dtype=X.dtype)\n", + "\n", + "# Predict the FHE quantized classifier probabilities on the test set.\n", + "# Compute FHE quantized accuracy, clear-quantized accuracy and \n", + "# keep track of samples wrongly classified due to quantization\n", + "for i, x_i in enumerate(tqdm(x_test_q.qvalues)):\n", + " y_i = y_test[i]\n", + "\n", + " fhe_in_sample = np.expand_dims(x_i, 1).transpose([1, 0]).astype(np.uint8)\n", + "\n", + " q_pred_fhe = engine.run(fhe_in_sample)\n", + " y_score_fhe = q_logreg.dequantize_output(q_pred_fhe)\n", + " homomorphic_prediction = (y_score_fhe > 0.5).astype(np.int32)\n", + "\n", + " non_homomorphic_prediction = q_y_pred_test[i]\n", + " if non_homomorphic_prediction == y_i:\n", + " non_homomorphic_correct += 1\n", + " elif y_pred_test[i] == y_i:\n", + " q_wrong_predictions = np.vstack((q_wrong_predictions, x_test[i, :]))\n", + "\n", + " if homomorphic_prediction == y_i:\n", + " homomorphic_correct += 1" ] }, { "cell_type": "markdown", - "id": "cf05e044", + "id": "f8c1d98a", "metadata": {}, "source": [ - "### As a bonus let's inspect the model parameters" + "### Aggregate accuracies for all the versions of the classifier" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 13, "id": "8f3236fb", "metadata": {}, "outputs": [ @@ -337,321 +471,44 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[5.28773165]\n", - " [2.6065383 ]]\n", - "-16.89045524597168\n" + "Sklearn accuracy: 90.0000\n", + "Non Homomorphic Accuracy: 85.0000\n", + "Homomorphic Accuracy: 85.0000\n", + "Difference Percentage: 0.00%\n" ] } ], "source": [ - "w = np.array(model.fc.weight.flatten().tolist()).reshape((-1, 1))\n", - "b = model.fc.bias.flatten().tolist()[0]\n", + "sklearn_acc = np.sum(y_pred_test == y_test) / len(y_test) * 100\n", + "non_homomorphic_accuracy = (non_homomorphic_correct / len(y_test)) * 100\n", + "homomorphic_accuracy = (homomorphic_correct / len(y_test)) * 100\n", + "difference = abs(homomorphic_accuracy - non_homomorphic_accuracy)\n", "\n", - "print(w)\n", - "print(b)" + "print(f\"Sklearn accuracy: {sklearn_acc:.4f}\")\n", + "print(f\"Non Homomorphic Accuracy: {non_homomorphic_accuracy:.4f}\")\n", + "print(f\"Homomorphic Accuracy: {homomorphic_accuracy:.4f}\")\n", + "print(f\"Difference Percentage: {difference:.2f}%\")\n" ] }, { "cell_type": "markdown", - "id": "44f1af11", + "id": "4810fdaf", "metadata": {}, "source": [ - "They are floating point numbers and we can't directly work with them!" - ] - }, - { - "cell_type": "markdown", - "id": "dd440b8d", - "metadata": {}, - "source": [ - "### So, let's abstract quantization\n", - "\n", - "Here is a quick summary of quantization. We have a range of values and we want to represent them using small number of bits (n). To do this, we split the range into 2^n sections and map each section to a value. Here is a visualization of the process!" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "6314bb91", - "metadata": {}, - "outputs": [ - { - "data": { - "image/svg+xml": [ - "
min(x)
min(x)
max(x)
max(x)
Map
to 0
Map...
Map
to 1
Map...
Distance
Between
Consecutive
Values
Distan...
Map
to 2
Map...
Map
to 3
Map...
(when n = 2)
(when n = 2)
0
0
= 1 / scale
= 1 / q
= 1 / scale...
x = (x   + zp  ) / q
x = (x   + zp  ) / q
q
q
x
x
x
x
zero point
zp = 2
zero point...
Viewer does not support full SVG 1.1
" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from IPython.display import SVG\n", - "SVG(filename=\"figures/QuantizationVisualized.svg\")" - ] - }, - { - "cell_type": "markdown", - "id": "2c33faf9", - "metadata": {}, - "source": [ - "If you want to learn more, head to https://intellabs.github.io/distiller/algo_quantization.html" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "9013f7e0", - "metadata": {}, - "outputs": [], - "source": [ - "class QuantizationParameters:\n", - " def __init__(self, q, zp, n):\n", - " # q = scale factor = 1 / distance between consecutive values\n", - " # zp = zero point which is used to determine the beginning of the quantized range\n", - " # (quantized 0 = the beginning of the quantized range = zp * distance between consecutive values)\n", - " # n = number of bits\n", - " \n", - " # e.g.,\n", - " \n", - " # n = 2\n", - " # zp = 2\n", - " # q = 0.66\n", - " # distance between consecutive values = 1 / q = 1.5151\n", - " \n", - " # quantized 0 = zp / q = zp * distance between consecutive values = 3.0303\n", - " # quantized 1 = quantized 0 + distance between consecutive values = 4.5454\n", - " # quantized 2 = quantized 1 + distance between consecutive values = 6.0606\n", - " # quantized 3 = quantized 2 + distance between consecutive values = 7.5757\n", - " \n", - " self.q = q\n", - " self.zp = zp\n", - " self.n = n\n", - "\n", - "class QuantizedArray:\n", - " def __init__(self, values, parameters):\n", - " # values = quantized values\n", - " # parameters = parameters used during quantization\n", - " \n", - " # e.g.,\n", - " \n", - " # values = [1, 0, 2, 1]\n", - " # parameters = QuantizationParameters(q=0.66, zp=2, n=2)\n", - " \n", - " # original array = [4.5454, 3.0303, 6.0606, 4.5454]\n", - " \n", - " self.values = np.array(values)\n", - " self.parameters = parameters\n", - "\n", - " @staticmethod\n", - " def of(x, n):\n", - " if not isinstance(x, np.ndarray):\n", - " x = np.array(x)\n", - "\n", - " min_x = x.min()\n", - " max_x = x.max()\n", - "\n", - " if min_x == max_x: # encoding single valued arrays\n", - " \n", - " if min_x == 0.0: # encoding 0s\n", - " \n", - " # dequantization = (x_q + zp_x) / q_x = 0 --> q_x = 1 && zp_x = 0 && x_q = 0\n", - " q_x = 1\n", - " zp_x = 0\n", - " x_q = np.zeros(x.shape, dtype=np.uint)\n", - " \n", - " elif min_x < 0.0: # encoding negative scalars\n", - " \n", - " # dequantization = (x_q + zp_x) / q_x = -x --> q_x = 1 / x & zp_x = -1 & x_q = 0\n", - " q_x = abs(1 / min_x)\n", - " zp_x = -1\n", - " x_q = np.zeros(x.shape, dtype=np.uint)\n", - " \n", - " else: # encoding positive scalars\n", - " \n", - " # dequantization = (x_q + zp_x) / q_x = x --> q_x = 1 / x & zp_x = 0 & x_q = 1\n", - " q_x = 1 / min_x\n", - " zp_x = 0\n", - " x_q = np.ones(x.shape, dtype=np.uint)\n", - " \n", - " else: # encoding multi valued arrays\n", - " \n", - " # distance between consecutive values = range of x / number of different quantized values = (max_x - min_x) / (2^n - 1)\n", - " # q = 1 / distance between consecutive values\n", - " q_x = (2**n - 1) / (max_x - min_x)\n", - " \n", - " # zp = what should be added to 0 to get min_x -> min_x = (0 + zp) / q -> zp = min_x * q\n", - " zp_x = int(round(min_x * q_x))\n", - " \n", - " # x = (x_q + zp) / q -> x_q = (x * q) - zp\n", - " x_q = ((q_x * x) - zp_x).round().astype(np.uint)\n", - "\n", - " return QuantizedArray(x_q, QuantizationParameters(q_x, zp_x, n))\n", - "\n", - " def dequantize(self):\n", - " # x = (x_q + zp) / q\n", - " # x = (x_q + zp) / q\n", - " return (self.values.astype(np.float32) + float(self.parameters.zp)) / self.parameters.q\n", - "\n", - " def affine(self, w, b, min_y, max_y, n_y):\n", - " # the formulas used in this method was derived from the following equations\n", - " #\n", - " # x = (x_q + zp_x) / q_x\n", - " # w = (w_q + zp_w) / q_w\n", - " # b = (b_q + zp_b) / q_b\n", - " #\n", - " # (x * w) + b = ((x_q + zp_x) / q_x) * ((w_q + zp_w) / q_w) + ((b_q + zp_b) / q_b)\n", - " # = y = (y_q + zp_y) / q_y\n", - " #\n", - " # So, ((x_q + zp_x) / q_x) * ((w_q + zp_w) / q_w) + ((b_q + zp_b) / q_b) = (y_q + zp_y) / q_y\n", - " # We can calculate zp_y and q_y from min_y, max_y, n_y. So, the only unknown is y_q and it can be solved.\n", - "\n", - " x_q = self.values\n", - " w_q = w.values\n", - " b_q = b.values\n", - "\n", - " q_x = self.parameters.q\n", - " q_w = w.parameters.q\n", - " q_b = b.parameters.q\n", - "\n", - " zp_x = self.parameters.zp\n", - " zp_w = w.parameters.zp\n", - " zp_b = b.parameters.zp\n", - "\n", - " q_y = (2**n_y - 1) / (max_y - min_y)\n", - " zp_y = int(round(min_y * q_y))\n", - "\n", - " y_q = (q_y / (q_x * q_w)) * ((x_q + zp_x) @ (w_q + zp_w) + (q_x * q_w / q_b) * (b_q + zp_b))\n", - " y_q -= min_y * q_y\n", - " y_q = y_q.round().clip(0, 2**n_y - 1).astype(np.uint)\n", - "\n", - " return QuantizedArray(y_q, QuantizationParameters(q_y, zp_y, n_y))\n", - "\n", - "class QuantizedFunction:\n", - " def __init__(self, table, input_parameters=None, output_parameters=None):\n", - " self.table = table\n", - " self.input_parameters = input_parameters\n", - " self.output_parameters = output_parameters\n", - "\n", - " @staticmethod\n", - " def of(f, input_bits, output_bits):\n", - " domain = np.array(range(2**input_bits), dtype=np.uint)\n", - " table = f(domain).round().clip(0, 2**output_bits - 1).astype(np.uint)\n", - " return QuantizedFunction(table)\n", - "\n", - " @staticmethod\n", - " def plain(f, input_parameters, output_bits):\n", - " n = input_parameters.n\n", - "\n", - " domain = np.array(range(2**n), dtype=np.uint)\n", - " inputs = QuantizedArray(domain, input_parameters).dequantize()\n", - "\n", - " outputs = f(inputs)\n", - " quantized_outputs = QuantizedArray.of(outputs, output_bits)\n", - "\n", - " table = quantized_outputs.values\n", - " output_parameters = quantized_outputs.parameters\n", - "\n", - " return QuantizedFunction(table, input_parameters, output_parameters)\n", - "\n", - " def apply(self, x):\n", - " assert x.parameters == self.input_parameters\n", - " return QuantizedArray(self.table[x.values], self.output_parameters)" - ] - }, - { - "cell_type": "markdown", - "id": "477c431f", - "metadata": {}, - "source": [ - "### Let's quantize our model parameters\n", - "\n", - "Since the parameters only consist of scalars, we can use a single bit quantization." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "9a4dc030", - "metadata": {}, - "outputs": [], - "source": [ - "parameter_bits = 1\n", - "\n", - "w_q = QuantizedArray.of(w, parameter_bits)\n", - "b_q = QuantizedArray.of(b, parameter_bits)" - ] - }, - { - "cell_type": "markdown", - "id": "4592996c", - "metadata": {}, - "source": [ - "### And quantize our inputs" + "### Plot the results of the original and FHE versions of the classifier, showing classification errors induced by quantization with a red circle" ] }, { "cell_type": "code", "execution_count": 14, - "id": "5ba001fe", - "metadata": {}, - "outputs": [], - "source": [ - "input_bits = 5\n", - "\n", - "x = inputs\n", - "x_q = QuantizedArray.of(inputs, input_bits)" - ] - }, - { - "cell_type": "markdown", - "id": "5da2a8b9", - "metadata": {}, - "source": [ - "### Time to make quantized inference" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "3f899676", - "metadata": {}, - "outputs": [], - "source": [ - "output_bits = 7\n", - "\n", - "intermediate = x @ w + b\n", - "intermediate_q = x_q.affine(w_q, b_q, intermediate.min(), intermediate.max(), output_bits)\n", - "\n", - "sigmoid = QuantizedFunction.plain(lambda x: 1 / (1 + np.exp(-x)), intermediate_q.parameters, output_bits)\n", - "y_q = sigmoid.apply(intermediate_q)\n", - "\n", - "quantized_predictions = y_q.dequantize()" - ] - }, - { - "cell_type": "markdown", - "id": "4ea9b4fa", - "metadata": {}, - "source": [ - "### And visualize the results" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "5d46b7d8", + "id": "41b274ed", "metadata": {}, "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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", "text/plain": [ - "
" + "
" ] }, "metadata": {}, @@ -659,291 +516,36 @@ } ], "source": [ - "for column in contour.collections:\n", - " plt.gca().collections.remove(column)\n", - " \n", - "contour = ax.contourf(\n", - " contour_plot_x_data,\n", - " contour_plot_y_data,\n", - " quantized_predictions.round().reshape(contour_plot_x_data.shape),\n", - " cmap=\"gray\",\n", - " alpha=0.50,\n", + "plt.clf()\n", + "fig, ax = plt.subplots(1,figsize=(12,8))\n", + "fig.patch.set_facecolor('white')\n", + "ax.contourf(x_test_grid, y_test_grid, q_y_score_grid.reshape(x_test_grid.shape), cmap=\"coolwarm\")\n", + "CS1 = ax.contour(\n", + " x_test_grid,\n", + " y_test_grid,\n", + " q_y_score_grid.reshape(x_test_grid.shape),\n", + " levels=[0.5],\n", + " linewidths=2,\n", ")\n", - "display(fig)" - ] - }, - { - "cell_type": "markdown", - "id": "483c5c17", - "metadata": {}, - "source": [ - "### Now it's time to make the inference homomorphic" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "f15f9e12", - "metadata": {}, - "outputs": [], - "source": [ - "q_y = (2**output_bits - 1) / (intermediate.max() - intermediate.min())\n", - "zp_y = int(round(intermediate.min() * q_y))\n", - "\n", - "q_x = x_q.parameters.q\n", - "q_w = w_q.parameters.q\n", - "q_b = b_q.parameters.q\n", - "\n", - "zp_x = x_q.parameters.zp\n", - "zp_w = w_q.parameters.zp\n", - "zp_b = b_q.parameters.zp\n", - "\n", - "x_q = x_q.values\n", - "w_q = w_q.values\n", - "b_q = b_q.values" - ] - }, - { - "cell_type": "markdown", - "id": "be208937", - "metadata": {}, - "source": [ - "### Simplification to rescue!\n", - "\n", - "The `y_q` formula in `QuantizedArray.affine(...)` can be rewritten to make it easier to implement in homomorphically. Here is the breakdown.\n", - "```\n", - "(q_y / (q_x * q_w)) * ((x_q + zp_x) @ (w_q + zp_w) + (q_x * q_w / q_b) * (b_q + zp_b)) - (min_y * q_y)\n", - "^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^\n", - "constant (c1) can be done constant (c2) constant (c3) constant (c4)\n", - " on the circuit \n", - " \n", - " ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - " can be done on the circuit\n", - " \n", - "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - "cannot be done on the circuit because of floating point operation so will be a single table lookup\n", - "```" - ] - }, - { - "cell_type": "markdown", - "id": "34c675ed", - "metadata": {}, - "source": [ - "### Let's import the Concrete numpy package now!" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "72a84cac", - "metadata": {}, - "outputs": [], - "source": [ - "import concrete.numpy as hnp" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "f8f197a2", - "metadata": {}, - "outputs": [], - "source": [ - "c1 = q_y / (q_x * q_w)\n", - "c2 = w_q + zp_w\n", - "c3 = (q_x * q_w / q_b) * (b_q + zp_b)\n", - "c4 = intermediate.min() * q_y\n", - "\n", - "def f(x):\n", - " values = ((c1 * (x + c3)) - c4).round().clip(0, 2**output_bits - 1).astype(np.uint)\n", - " after_affine_q = QuantizedArray(values, intermediate_q.parameters)\n", - " \n", - " sigmoid = QuantizedFunction.plain(lambda x: 1 / (1 + np.exp(-x)), after_affine_q.parameters, output_bits)\n", - " y_q = sigmoid.apply(after_affine_q)\n", - " \n", - " return y_q.values\n", - "\n", - "f_q = QuantizedFunction.of(f, output_bits, output_bits)\n", - "\n", - "table = hnp.LookupTable([int(entry) for entry in f_q.table])\n", - "\n", - "w_0 = int(c2.flatten()[0])\n", - "w_1 = int(c2.flatten()[1])\n", - "\n", - "def infer(x_0, x_1):\n", - " return table[((x_0 + zp_x) * w_0) + ((x_1 + zp_x) * w_1)]" - ] - }, - { - "cell_type": "markdown", - "id": "babb1a98", - "metadata": {}, - "source": [ - "### Let's compile our quantized inference function to its homomorphic equivalent" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "b3a1d948", - "metadata": {}, - "outputs": [], - "source": [ - "inputset = []\n", - "for x_i in x_q:\n", - " inputset.append((int(x_i[0]), int(x_i[1])))\n", - " \n", - "circuit = hnp.compile_numpy_function(\n", - " infer,\n", - " {\n", - " \"x_0\": hnp.EncryptedScalar(hnp.Integer(input_bits, is_signed=False)),\n", - " \"x_1\": hnp.EncryptedScalar(hnp.Integer(input_bits, is_signed=False)),\n", - " },\n", - " inputset,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "ab5ba39e", - "metadata": {}, - "source": [ - "### Here are some representations of the fhe circuit" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "13ac665b", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "%0 = Constant(2) # ClearScalar>\n", - "%1 = Constant(1) # ClearScalar>\n", - "%2 = x_0 # EncryptedScalar>\n", - "%3 = Constant(6) # ClearScalar>\n", - "%4 = x_1 # EncryptedScalar>\n", - "%5 = Constant(6) # ClearScalar>\n", - "%6 = Add(2, 3) # EncryptedScalar>\n", - "%7 = Add(4, 5) # EncryptedScalar>\n", - "%8 = Mul(6, 0) # EncryptedScalar>\n", - "%9 = Mul(7, 1) # EncryptedScalar>\n", - "%10 = Add(8, 9) # EncryptedScalar>\n", - "%11 = TLU(10) # EncryptedScalar>\n", - "return(%11)\n", - "\n" - ] - } - ], - "source": [ - "print(circuit)" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "52101260", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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cz2TijvbU0aWLl3K7NmzGTt2rE4J5kE02WXDJ598QlJSEq+88kq2/22hQoUAMlyR2f6c/T2utHv3bi5duqTJTnmkevXqZWn4gae0p+tdvXqVQYMG0aVLF1q3bu32/avMabLLhiJFijB69GgmTpzIunXrsvVvK1euDJBhJ5cTJ04AEBkZmfsgb2Lz5s3kyZOHatWquXxfSmVXvXr1+PPPP2969cRT2tP1Xn31VS5evMjo0aPdvm91Y5rssqlr1660atWKp556KltTid13330AbN26Nd1r9ueaN2/unCBvYMuWLdx9993azVl5pPr16yMiGbaT1DylPaW2ZcsWxo4dy8cff0zx4sXdum91czYRH52MzoWOHj1K1apVefHFF7O8JlVSUhLVq1fnwoULHDx4kLCwMAASExOpXr060dHR7Nu3L+V5V6lVqxbNmzfn448/zva/LV26NKdOnSIhIcEFkSlllClThmeeeeaGtws8pT3ZJSQkUK9ePQoUKMDKlStzfL9Q25jLzNUzuxy47bbbePPNN3n33XfZs2dPlv5NQEAAU6ZMISoqit69e3Pq1CnOnTvHoEGD2L9/P5MmTXJ5w7x27Rq7d+/WyZ+VR6tXr95NO6l4QntK7eOPP2bPnj2MHz/eso4x6sY02eXQ0KFDqVGjBn379iUpKSlL/6ZBgwZs2LCBixcvUqlSJcqWLcv+/ftZtWoVDz30kIsjNpd34uPjs9U5ZfHixdhsNmw2GydOnCAxMTHl8eTJk10YrfJX9erVY+PGjTd9n9Xtye7IkSO88847jBw5MuVeYnZoG3MPvYyZC3/88Qd16tRhzJgx9O/f3+pwburTTz/l/fff58yZM1aHolSmVq5cyf3338+xY8cyHDTuaR588EFOnTrF1q1bbzhTirKUXsbMjRo1avDcc8/x0ksvpfQA82RZXelAKSvVqVOHwMBAr1jMddq0aSxfvvymU4Ip62myy6U333yTokWLMnToUKtDualNmzZpslMeL3/+/FSuXDnXg8td7ezZs7z00ksMHjyYe+65x+pw1E1ossul8PBwJk2axIIFC1i4cKHV4WTqn3/+4fDhw5rslFeoX79+tta2s8Jzzz1HWFgY77zzjtWhqCzQZOcE9913H926deOZZ55x+lRizrJ582ZsNpv2xFReoW7duvz2228kJiZaHUqGfv75Z7755hvG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lFI+T5OdUkopn6fJTimllM/TZKeUUsrnabJTSinl8zTZKaWU8nma7JRSSvk8TXZKKaV8niY7pZRSPk+TnVJKKZ+nyU4ppZTP02SnlFLK52myU0op5fM02SmllPJ5muyUUkr5PE12Sqmb2rFjB61ataJQoULkz5+fFi1asH79eqvDUirLNNkppW5o06ZNNGzYkPz587N3714OHz5M+fLladasGb/88ovV4SmVJTYREauDUEpZY86cOXTq1InM/gwkJSVRo0YNoqKiOHjwIOHh4QAkJiZStWpVrl69yv79+wkNDXVn2Epl11w9s1NKZWrNmjXs3r2bJ554IiXRAQQGBtKlSxeOHTvG4sWLLYxQqazRZKeUytSKFSsAqFOnTrrX7M8tX77crTEplROa7JRSmfrzzz8BKF26dLrXIiIiANi3b59bY1IqJzTZKaUydeHCBQDy5s2b7rV8+fIBcP78eXeGpFSOaLJTSuWIvVOLzWazOBKlbk6TnVIqU4UKFQLgypUr6V6zP2d/j1KeTJOdUipTlStXBuD48ePpXjtx4gQAkZGRbo1JqZzQZKeUytR9990HwNatW9O9Zn+uefPmbo1JqZzQZKeUylTTpk2pUqUK8+bNIyYmJuX5xMREZs2aRZkyZWjVqpWFESqVNZrslFKZCggIYMqUKURFRdG7d29OnTrFuXPnGDRoEPv372fSpEmEhYVZHaZSN6XJTil1Qw0aNGDDhg1cvHiRSpUqUbZsWfbv38+qVat46KGHrA5PqSwJsjoApZTnq1WrFkuWLLE6DKVyTM/slFJK+TxNdkoppXyeJjullFI+T5OdUkopn6fJTimllM/TZKeUUsrnabJTSinl8zTZKaWU8nma7JRSSvk8TXZKKaV8niY7pZRSPk+TnVJKKZ+nyU4ppZTP02SnlFLK5+kSP0r5iRMnTlC9enXi4+PTPJ8nTx7y58+f8thms3HPPffw888/uztEpVxGk51SfiIiIoI77riD3377DRHJ9H02m42WLVu6MTKlXE8vYyrlR3r06EFAwM2bfYcOHdwQjVLuo8lOKT/SqVOnG74eEBBAkyZNiIiIcFNESrmHJjul/EjRokVp1qwZgYGBGb5us9no3r27m6NSyvU02SnlZ7p3757pPTubzcZjjz3m5oiUcj1Ndkr5mccee4ygoPR904KCgmjZsiVFihSxICql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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from PIL import Image\n", - "file = Image.open(circuit.draw())\n", - "file.show()\n", - "file.close()" - ] - }, - { - "cell_type": "markdown", - "id": "972edbb0", - "metadata": {}, - "source": [ - "### Finally, let's make homomorphic inference" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "c83f68cd", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "7de68790a0584e96aa7e8e30a535275a", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - " 0%| | 0/10000 [00:00" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "for column in contour.collections:\n", - " plt.gca().collections.remove(column)\n", - " \n", - "contour = ax.contourf(\n", - " contour_plot_x_data,\n", - " contour_plot_y_data,\n", - " homomorphic_predictions.round().reshape(contour_plot_x_data.shape),\n", - " cmap=\"gray\",\n", - " alpha=0.50,\n", + "ax.scatter(x_train[:, 0], x_train[:, 1], c=y_train, cmap=\"jet\", marker=\"D\")\n", + "ax.scatter(\n", + " q_wrong_predictions[:, 0], q_wrong_predictions[:, 1], c=\"red\", marker=\"o\", edgecolors=\"k\", s=32\n", ")\n", + "ax.scatter(x_test[:, 0], x_test[:, 1], c=q_y_pred_test, cmap=\"jet\", marker=\"x\")\n", + "CS2 = ax.contour(\n", + " x_test_grid,\n", + " y_test_grid,\n", + " y_score_grid.reshape(x_test_grid.shape),\n", + " levels=[0.5],\n", + " linewidths=2,\n", + " linestyles=\"dashed\",\n", + " cmap=\"hot\",\n", + ")\n", + "ax.clabel(CS1, CS1.levels, inline=True, fontsize=10)\n", + "ax.clabel(CS2, CS2.levels, inline=True, fontsize=10)\n", + "CS1.collections[0].set_label(f\"Quantized FHE decision boundary, acc={homomorphic_accuracy:.1f}\")\n", + "CS2.collections[0].set_label(f\"Sklearn decision boundary, acc={sklearn_acc:.1f}\")\n", + "ax.legend(loc=\"upper right\")\n", "display(fig)" ] },