binoua commited on
Commit
d29187e
1 Parent(s): a6d6517

chore: adding an example from our template with a NeuralNetClassifier.

Browse files
README.md CHANGED
@@ -1,3 +1,47 @@
1
  ---
2
  license: apache-2.0
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  license: apache-2.0
3
  ---
4
+
5
+ # Template for Concrete ML
6
+
7
+ Concrete ML is Zama's open-source privacy-preserving ML package, based on fully homomorphic encryption (FHE). We refer the reader to fhe.org or Zama's websites for more information on FHE.
8
+
9
+ This directory is used:
10
+ - by ML practicioners, to create Concrete ML FHE-friendly models, and make them available to HF users
11
+ - by companies, institutions or people to deploy those models over HF inference endpoints
12
+ - by developers, to use these entry points to make applications on privacy-preserving ML
13
+
14
+ ## Creating models and making them available on HF
15
+
16
+ This is quite easy. Fork this template (maybe use this experimental tool https://huggingface.co/spaces/huggingface-projects/repo_duplicator for that), and then:
17
+ - install everything with: `pip install -r requirements.txt`
18
+ - edit `creating_models.py`, and fill the part between "# BEGIN: insert your ML task here" and
19
+ "# END: insert your ML task here"
20
+ - run the python file: `python creating_models.py`
21
+
22
+ At the end, if the script is successful, you'll have your compiled model ready in `compiled_model`. Now you can commit and push your repository (with in particular `compiled_model`, `handler.py`, `play_with_endpoint.py` and `requirements.txt`, but you can include the other files as well).
23
+
24
+ We recommend you to tag your Concrete ML compiled repository with `Concrete ML FHE friendly` tag, such that people can find them easily.
25
+
26
+ ## Deploying a compiled model on HF inference endpoint
27
+
28
+ If you find an `Concrete ML FHE friendly` repository that you would like to deploy, it is very easy.
29
+ - click on 'Deploy' button in HF interface
30
+ - chose "Inference endpoints"
31
+ - chose the right model repository
32
+ - (the rest of the options are classical to HF end points; we refer you to their documentation for more information)
33
+ and then click on 'Create endpoint'
34
+
35
+ And now, your model should be deployed, after few secunds of installation.
36
+
37
+ ## Using HF entry points on privacy-preserving models
38
+
39
+ Now, this is the final step: using the entry point. You should:
40
+ - if your inference endpoint is private, set an environment variable HF_TOKEN with your HF token
41
+ - edit `play_with_endpoint.py`
42
+ - replace `API_URL` by your entry point URL
43
+ - replace the part between "# BEGIN: replace this part with your privacy-preserving application" and
44
+ "# END: replace this part with your privacy-preserving application" with your application
45
+
46
+ Finally, you'll be able to launch your application with `python play_with_endpoint.py`.
47
+
compiled_model/client.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:52473c839cbf2951945693fb255e48214c888e8e6ef513c3a06ccf866bc66aa4
3
+ size 3403
compiled_model/server.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:697fe8a7e1a6c5133dd29553c7f90457f4c2a388888051654e84d189d01fc9f4
3
+ size 1781
compiled_model/versions.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"concrete-python": "2.5.0rc1", "concrete-ml": "1.3.0", "python": "3.9.15"}
creating_models.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import shutil
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ from concrete.ml.deployment import FHEModelDev
6
+ from concrete.ml.deployment import FHEModelClient
7
+
8
+
9
+ def compile_and_make_it_deployable(model_dev, X_train):
10
+
11
+ path_to_model = Path("compiled_model")
12
+
13
+ # Compile into FHE
14
+ model_dev.compile(X_train)
15
+
16
+ # Saving the model
17
+ shutil.rmtree(path_to_model, ignore_errors=True)
18
+ fhemodel_dev = FHEModelDev(path_to_model, model_dev)
19
+ fhemodel_dev.save(via_mlir=True)
20
+
21
+ # To see the size of the key
22
+ fhemodel_client = FHEModelClient(path_to_model)
23
+
24
+ # Generate the keys
25
+ fhemodel_client.generate_private_and_evaluation_keys()
26
+ evaluation_keys = fhemodel_client.get_serialized_evaluation_keys()
27
+
28
+ print(f"Your keys will be {sys.getsizeof(evaluation_keys) / 1024 / 1024}-megabytes long")
29
+
30
+ # Check accuracy with p_error
31
+ y_pred_simulated = model_dev.predict(X_test, fhe="simulate")
32
+ simulated_accuracy = accuracy_score(Y_test, y_pred_simulated)
33
+
34
+ print(f"Concrete average precision score (simulate): {simulated_accuracy:0.2f}")
35
+
36
+
37
+ # BEGIN: insert your ML task here
38
+ # Typically
39
+ from concrete.ml.sklearn import NeuralNetClassifier
40
+ from sklearn.metrics import accuracy_score
41
+ from sklearn.model_selection import train_test_split
42
+ from sklearn.datasets import load_iris
43
+ from torch import nn
44
+
45
+ # Get iris data-set
46
+ X, y = load_iris(return_X_y=True)
47
+
48
+ # Split into train and test
49
+ X_train, X_test, Y_train, Y_test = train_test_split(X, y, test_size=0.25, random_state=42)
50
+
51
+ # Scikit-Learn and Concrete ML neural networks only handle float32 input values
52
+ X_train, X_test = X_train.astype("float32"), X_test.astype("float32")
53
+
54
+ params = {
55
+ "module__n_layers": 3,
56
+ "module__activation_function": nn.ReLU,
57
+ "max_epochs": 1000,
58
+ "verbose": 0,
59
+ }
60
+
61
+ model_dev = NeuralNetClassifier(**params)
62
+
63
+ model_dev = model_dev.fit(X=X_train, y=Y_train)
64
+
65
+ # Evaluate the Concrete ML model in the clear
66
+ y_pred_simulated = model_dev.predict(X_test)
67
+
68
+ simulated_accuracy = accuracy_score(Y_test, y_pred_simulated)
69
+ print(f"The test accuracy of the trained Concrete ML simulated model is {simulated_accuracy:.2f}")
70
+
71
+ # END: insert your ML task here
72
+
73
+ compile_and_make_it_deployable(model_dev, X_train)
74
+ print("Your model is ready to be deployable.")
handler.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Dict, List, Any
2
+ import numpy as np
3
+ from concrete.ml.deployment import FHEModelServer
4
+
5
+
6
+ def from_json(python_object):
7
+ if "__class__" in python_object:
8
+ return bytes(python_object["__value__"])
9
+
10
+
11
+ def to_json(python_object):
12
+ if isinstance(python_object, bytes):
13
+ return {"__class__": "bytes", "__value__": list(python_object)}
14
+ raise TypeError(repr(python_object) + " is not JSON serializable")
15
+
16
+
17
+ class EndpointHandler:
18
+ def __init__(self, path=""):
19
+
20
+ # For server
21
+ self.fhemodel_server = FHEModelServer(path + "/compiled_model")
22
+
23
+ # Simulate a database of keys
24
+ self.key_database = {}
25
+
26
+ def __call__(self, data: Dict[str, Any]) -> List[Dict[str, Any]]:
27
+ """
28
+ data args:
29
+ inputs (:obj: `str`)
30
+ date (:obj: `str`)
31
+ Return:
32
+ A :obj:`list` | `dict`: will be serialized and returned
33
+ """
34
+
35
+ # Get method
36
+ method = data.pop("method", data)
37
+
38
+ if method == "save_key":
39
+
40
+ # Get keys
41
+ evaluation_keys = from_json(data.pop("evaluation_keys", data))
42
+
43
+ uid = np.random.randint(2**32)
44
+
45
+ while uid in self.key_database.keys():
46
+ uid = np.random.randint(2**32)
47
+
48
+ self.key_database[uid] = evaluation_keys
49
+
50
+ return {"uid": uid}
51
+
52
+ elif method == "inference":
53
+
54
+ uid = data.pop("uid", data)
55
+
56
+ assert uid in self.key_database.keys(), f"{uid} not in DB, {self.key_database.keys()=}"
57
+
58
+ # Get inputs
59
+ encrypted_inputs = from_json(data.pop("encrypted_inputs", data))
60
+
61
+ # Find key in the database
62
+ evaluation_keys = self.key_database[uid]
63
+
64
+ # Run CML prediction
65
+ encrypted_prediction = self.fhemodel_server.run(encrypted_inputs, evaluation_keys)
66
+
67
+ return to_json(encrypted_prediction)
68
+
69
+ else:
70
+
71
+ return
play_with_endpoint.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import time
3
+ import os, sys
4
+
5
+ from pathlib import Path
6
+
7
+ from concrete.ml.deployment import FHEModelClient
8
+
9
+ import requests
10
+
11
+
12
+ def to_json(python_object):
13
+ if isinstance(python_object, bytes):
14
+ return {"__class__": "bytes", "__value__": list(python_object)}
15
+ raise TypeError(repr(python_object) + " is not JSON serializable")
16
+
17
+
18
+ def from_json(python_object):
19
+ if "__class__" in python_object:
20
+ return bytes(python_object["__value__"])
21
+
22
+
23
+ # TODO: put the right link `API_URL` for your entryp point
24
+ API_URL = "https://XXXXXXX.us-east-1.aws.endpoints.huggingface.cloud"
25
+ headers = {
26
+ "Authorization": "Bearer " + os.environ.get("HF_TOKEN"),
27
+ "Content-Type": "application/json",
28
+ }
29
+
30
+
31
+ def query(payload):
32
+ response = requests.post(API_URL, headers=headers, json=payload)
33
+
34
+ if "error" in response:
35
+ assert False, f"Got an error: {response=}"
36
+
37
+ return response.json()
38
+
39
+
40
+ path_to_model = Path("compiled_model")
41
+
42
+ # BEGIN: replace this part with your privacy-preserving application
43
+ from sklearn.datasets import make_classification
44
+ from sklearn.model_selection import train_test_split
45
+
46
+ x, y = make_classification(n_samples=1000, class_sep=2, n_features=30, random_state=42)
47
+ _, X_test, _, Y_test = train_test_split(x, y, test_size=0.2, random_state=42)
48
+
49
+ # Recover parameters for client side
50
+ fhemodel_client = FHEModelClient(path_to_model)
51
+
52
+ # Generate the keys
53
+ fhemodel_client.generate_private_and_evaluation_keys()
54
+ evaluation_keys = fhemodel_client.get_serialized_evaluation_keys()
55
+
56
+ # Save the key in the database
57
+ payload = {
58
+ "inputs": "fake",
59
+ "evaluation_keys": to_json(evaluation_keys),
60
+ "method": "save_key",
61
+ }
62
+
63
+ uid = query(payload)["uid"]
64
+ print(f"Storing the key in the database under {uid=}")
65
+
66
+ # Test the handler
67
+ nb_good = 0
68
+ nb_samples = len(X_test)
69
+ verbose = True
70
+ time_start = time.time()
71
+ duration = 0
72
+ is_first = True
73
+
74
+ for i in range(nb_samples):
75
+
76
+ # Quantize the input and encrypt it
77
+ encrypted_inputs = fhemodel_client.quantize_encrypt_serialize([X_test[i]])
78
+
79
+ # Prepare the payload
80
+ payload = {
81
+ "inputs": "fake",
82
+ "encrypted_inputs": to_json(encrypted_inputs),
83
+ "method": "inference",
84
+ "uid": uid,
85
+ }
86
+
87
+ if is_first:
88
+ print(f"Size of the payload: {sys.getsizeof(payload) / 1024} kilobytes")
89
+ is_first = False
90
+
91
+ # Run the inference on HF servers
92
+ duration -= time.time()
93
+ duration_inference = -time.time()
94
+ encrypted_prediction = query(payload)
95
+ duration += time.time()
96
+ duration_inference += time.time()
97
+
98
+ encrypted_prediction = from_json(encrypted_prediction)
99
+
100
+ # Decrypt the result and dequantize
101
+ prediction_proba = fhemodel_client.deserialize_decrypt_dequantize(encrypted_prediction)[0]
102
+ prediction = np.argmax(prediction_proba)
103
+
104
+ if verbose:
105
+ print(
106
+ f"for {i}-th input, {prediction=} with expected {Y_test[i]} in {duration_inference:.3f} seconds"
107
+ )
108
+
109
+ # Measure accuracy
110
+ nb_good += Y_test[i] == prediction
111
+
112
+ print(f"Accuracy on {nb_samples} samples is {nb_good * 1. / nb_samples}")
113
+ print(f"Total time: {time.time() - time_start:.3f} seconds")
114
+ print(f"Duration per inference: {duration / nb_samples:.3f} seconds")
115
+ # END: replace this part with your privacy-preserving application
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ concrete-ml==1.3.0