From f864407fbcfd6ab84b164a7181cbecfce9f2e6d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordan=20Fr=C3=A9ry?= Date: Tue, 21 May 2024 17:46:40 +0200 Subject: [PATCH] docs: update documentation on client / server API (#663) --- docs/advanced_examples/ClientServer.ipynb | 3 - docs/guides/client_server.md | 63 ++++++++++++++++++- .../ml/deployment/fhe_client_server.py | 3 + src/concrete/ml/torch/hybrid_model.py | 3 +- tests/deployment/test_client_server.py | 1 - .../deployment/breast_cancer/client.py | 4 +- use_case_examples/deployment/cifar/client.py | 4 +- .../deployment/sentiment_analysis/client.py | 4 +- 8 files changed, 69 insertions(+), 16 deletions(-) diff --git a/docs/advanced_examples/ClientServer.ipynb b/docs/advanced_examples/ClientServer.ipynb index 1e527c6f8..42fdc911a 100644 --- a/docs/advanced_examples/ClientServer.ipynb +++ b/docs/advanced_examples/ClientServer.ipynb @@ -232,9 +232,6 @@ "fhemodel_client = FHEModelClient(network.client_dir.name, key_dir=network.client_dir.name)\n", "\n", "# The client first need to create the private and evaluation keys.\n", - "fhemodel_client.generate_private_and_evaluation_keys()\n", - "\n", - "# Get the serialized evaluation keys\n", "serialized_evaluation_keys = fhemodel_client.get_serialized_evaluation_keys()" ] }, diff --git a/docs/guides/client_server.md b/docs/guides/client_server.md index 425bda231..586bd4699 100644 --- a/docs/guides/client_server.md +++ b/docs/guides/client_server.md @@ -13,7 +13,68 @@ The diagram above shows the steps that a developer goes through to prepare a mod The compiled model (`server.zip`) is deployed to a server and the cryptographic parameters (`client.zip`) are shared with the clients. In some settings, such as a phone application, the `client.zip` can be directly deployed on the client device and the server does not need to host it. -Note that for built-in models, the server output + post-processing adheres to the following guidelines: if the model is a regressor, the output follows the format of the scikit-learn `.predict()` method; if the model is a classifier, the output follows the format of the scikit-learn `.predict_proba()` method. +> **Important Note:** In a client-server production using FHE, the server's output format depends on the model type. For regressors, the output matches the `predict()` method from scikit-learn, providing direct predictions. For classifiers, the output uses the `predict_proba()` method format, offering probability scores for each class, which allows clients to determine class membership by applying a threshold (commonly 0.5). + +### Using the API Classes + +The `FHEModelDev`, `FHEModelClient`, and `FHEModelServer` classes in the `concrete.ml.deployment` module make it easy to deploy and interact between the client and server: + +- **`FHEModelDev`**: This class is used during the development phase to prepare and save the model artifacts (`client.zip` and `server.zip`). It handles the serialization of the underlying FHE circuit as well as the crypto-parameters used for generating the keys. + +- **`FHEModelClient`**: This class is used on the client side to generate and serialize the cryptographic keys, encrypt the data before sending it to the server, and decrypt the results received from the server. It also handles the loading of quantization parameters and pre/post-processing from `serialized_processing.json`. + +- **`FHEModelServer`**: This class is used on the server side to load the FHE circuit from `server.zip` and execute the model on encrypted data received from the client. + +### Example Usage + +```python +from concrete.ml.sklearn import DecisionTreeClassifier +from concrete.ml.deployment import FHEModelDev, FHEModelClient, FHEModelServer +import numpy as np + +# Define the directory for FHE client/server files +fhe_directory = '/tmp/fhe_client_server_files/' + +# Initialize the Decision Tree model +model = DecisionTreeClassifier() + +# Generate some random data for training +X = np.random.rand(100, 20) +y = np.random.randint(0, 2, size=100) + +# Train and compile the model +model.fit(X, y) +model.compile(X) + +# Setup the development environment +dev = FHEModelDev(path_dir=fhe_directory, model=model) +dev.save() + +# Setup the client +client = FHEModelClient(path_dir=fhe_directory, key_dir="/tmp/keys_client") +serialized_evaluation_keys = client.get_serialized_evaluation_keys() + +# Client pre-processes new data +X_new = np.random.rand(1, 20) +encrypted_data = client.quantize_encrypt_serialize(X_new) + +# Setup the server +server = FHEModelServer(path_dir=fhe_directory) +server.load() + +# Server processes the encrypted data +encrypted_result = server.run(encrypted_data, serialized_evaluation_keys) + +# Client decrypts the result +result = client.deserialize_decrypt_dequantize(encrypted_result) +``` + +> **Data Transfer Overview:** +> +> - **From Client to Server:** `serialized_evaluation_keys` (once), `encrypted_data`. +> - **From Server to Client:** `encrypted_result`. + +These objects are serialized into bytes to streamline the data transfer between the client and server. ## Serving diff --git a/src/concrete/ml/deployment/fhe_client_server.py b/src/concrete/ml/deployment/fhe_client_server.py index bedc5f474..50abc7034 100644 --- a/src/concrete/ml/deployment/fhe_client_server.py +++ b/src/concrete/ml/deployment/fhe_client_server.py @@ -295,6 +295,9 @@ def get_serialized_evaluation_keys(self) -> bytes: Returns: bytes: the evaluation keys """ + # Generate private and evaluation keys if not already generated + self.generate_private_and_evaluation_keys(force=False) + return self.client.evaluation_keys.serialize() def quantize_encrypt_serialize(self, x: numpy.ndarray) -> bytes: diff --git a/src/concrete/ml/torch/hybrid_model.py b/src/concrete/ml/torch/hybrid_model.py index 29fc9b1d5..277afc9ea 100644 --- a/src/concrete/ml/torch/hybrid_model.py +++ b/src/concrete/ml/torch/hybrid_model.py @@ -190,9 +190,8 @@ def init_fhe_client( path_dir=str(path_to_client.resolve()), key_dir=str(self.path_to_keys.resolve()) ) # The client first need to create the private and evaluation keys. - client.generate_private_and_evaluation_keys() - # Get the serialized evaluation keys serialized_evaluation_keys = client.get_serialized_evaluation_keys() + if self.verbose: print(f"Evaluation keys size: {len(serialized_evaluation_keys) / (10**6):.2f} MB") assert isinstance(serialized_evaluation_keys, bytes) diff --git a/tests/deployment/test_client_server.py b/tests/deployment/test_client_server.py index 5e88f90c5..c93ca7911 100644 --- a/tests/deployment/test_client_server.py +++ b/tests/deployment/test_client_server.py @@ -278,7 +278,6 @@ def check_client_server_execution( fhe_model_server.load() # Client side : Generate all keys and serialize the evaluation keys for the server - fhe_model_client.generate_private_and_evaluation_keys() evaluation_keys = fhe_model_client.get_serialized_evaluation_keys() # Client side : Encrypt the data diff --git a/use_case_examples/deployment/breast_cancer/client.py b/use_case_examples/deployment/breast_cancer/client.py index fa2c79af5..a1b8d225b 100644 --- a/use_case_examples/deployment/breast_cancer/client.py +++ b/use_case_examples/deployment/breast_cancer/client.py @@ -48,10 +48,8 @@ client = FHEModelClient(path_dir=str(ROOT.resolve()), key_dir=str((ROOT / "keys").resolve())) # The client first need to create the private and evaluation keys. - client.generate_private_and_evaluation_keys() - - # Get the serialized evaluation keys serialized_evaluation_keys = client.get_serialized_evaluation_keys() + assert isinstance(serialized_evaluation_keys, bytes) # Evaluation keys can be quite large files but only have to be shared once with the server. diff --git a/use_case_examples/deployment/cifar/client.py b/use_case_examples/deployment/cifar/client.py index a35d669bb..68b963111 100644 --- a/use_case_examples/deployment/cifar/client.py +++ b/use_case_examples/deployment/cifar/client.py @@ -72,10 +72,8 @@ def main(): client = FHEModelClient(path_dir="./", key_dir="./keys") # The client first need to create the private and evaluation keys. - client.generate_private_and_evaluation_keys() - - # Get the serialized evaluation keys serialized_evaluation_keys = client.get_serialized_evaluation_keys() + assert isinstance(serialized_evaluation_keys, bytes) # Evaluation keys can be quite large files but only have to be shared once with the server. diff --git a/use_case_examples/deployment/sentiment_analysis/client.py b/use_case_examples/deployment/sentiment_analysis/client.py index 0d2fd1697..9b865b40e 100644 --- a/use_case_examples/deployment/sentiment_analysis/client.py +++ b/use_case_examples/deployment/sentiment_analysis/client.py @@ -51,10 +51,8 @@ def main(): client = FHEModelClient(path_dir="./", key_dir="./keys") # The client first need to create the private and evaluation keys. - client.generate_private_and_evaluation_keys() - - # Get the serialized evaluation keys serialized_evaluation_keys = client.get_serialized_evaluation_keys() + assert isinstance(serialized_evaluation_keys, bytes) # Evaluation keys can be quite large files but only have to be shared once with the server. # Check the size of the evaluation keys (in MB)