chore: be closer to our template
Browse files- README.md +44 -0
- compiled_model/client.zip +1 -1
- compiled_model/server.zip +1 -1
- create_zipfiles_and_check_local_endpoint.py +0 -73
- creating_models.py +37 -0
- handler.py +11 -0
- play_with_endpoint.py +50 -15
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
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:
|
3 |
size 7496
|
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:21eed25172b9c4988774dbe04306e22f38f2672f8ec9100860bdf0a60baaaa4b
|
3 |
size 7496
|
compiled_model/server.zip
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:
|
3 |
size 1258
|
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:60f49990dd019d24fcadc03ab25dc7788f38ecb38d2230f02a6ff874cb370489
|
3 |
size 1258
|
create_zipfiles_and_check_local_endpoint.py
DELETED
@@ -1,73 +0,0 @@
|
|
1 |
-
from handler import EndpointHandler
|
2 |
-
import numpy as np
|
3 |
-
import shutil
|
4 |
-
|
5 |
-
from pathlib import Path
|
6 |
-
|
7 |
-
from sklearn.datasets import make_classification
|
8 |
-
from sklearn.model_selection import train_test_split
|
9 |
-
|
10 |
-
from concrete.ml.sklearn import LogisticRegression
|
11 |
-
from concrete.ml.deployment import FHEModelClient, FHEModelDev
|
12 |
-
|
13 |
-
# Fit a model. In the future, we should find an existing model on HF repository
|
14 |
-
path_to_model = Path("compiled_model")
|
15 |
-
do_training_and_compilation = True
|
16 |
-
|
17 |
-
x, y = make_classification(n_samples=1000, class_sep=2, n_features=30, random_state=42)
|
18 |
-
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)
|
19 |
-
|
20 |
-
if do_training_and_compilation:
|
21 |
-
model_dev = LogisticRegression()
|
22 |
-
model_dev.fit(X_train, y_train)
|
23 |
-
|
24 |
-
# Compile into FHE
|
25 |
-
model_dev.compile(X_train)
|
26 |
-
|
27 |
-
# Saving the model
|
28 |
-
shutil.rmtree(path_to_model, ignore_errors=True)
|
29 |
-
fhemodel_dev = FHEModelDev(path_to_model, model_dev)
|
30 |
-
fhemodel_dev.save(via_mlir=True)
|
31 |
-
|
32 |
-
# Init the handler (compilation of the model is done on HF side)
|
33 |
-
my_handler = EndpointHandler(path=".")
|
34 |
-
|
35 |
-
# Recover parameters for client side
|
36 |
-
fhemodel_client = FHEModelClient(path_to_model)
|
37 |
-
|
38 |
-
# Generate the keys
|
39 |
-
fhemodel_client.generate_private_and_evaluation_keys()
|
40 |
-
evaluation_keys = fhemodel_client.get_serialized_evaluation_keys()
|
41 |
-
|
42 |
-
# Test the handler
|
43 |
-
nb_good = 0
|
44 |
-
nb_samples = len(X_test)
|
45 |
-
verbose = False
|
46 |
-
|
47 |
-
for i in range(nb_samples):
|
48 |
-
|
49 |
-
# Quantize the input and encrypt it
|
50 |
-
encrypted_inputs = fhemodel_client.quantize_encrypt_serialize([X_test[i]])
|
51 |
-
|
52 |
-
# Prepare the payload, including the evaluation keys which are needed server side
|
53 |
-
payload = {
|
54 |
-
"inputs": "fake",
|
55 |
-
"encrypted_inputs": encrypted_inputs,
|
56 |
-
"evaluation_keys": evaluation_keys,
|
57 |
-
}
|
58 |
-
|
59 |
-
# Run the inference on HF servers
|
60 |
-
encrypted_prediction = my_handler(payload)
|
61 |
-
encrypted_prediction = encrypted_prediction
|
62 |
-
|
63 |
-
# Decrypt the result and dequantize
|
64 |
-
prediction_proba = fhemodel_client.deserialize_decrypt_dequantize(encrypted_prediction)[0]
|
65 |
-
prediction = np.argmax(prediction_proba)
|
66 |
-
|
67 |
-
if verbose:
|
68 |
-
print(f"for i-th input, {prediction=} with expected {y_test[i]}")
|
69 |
-
|
70 |
-
# Measure accuracy
|
71 |
-
nb_good += y_test[i] == prediction
|
72 |
-
|
73 |
-
print(f"Accuracy on {nb_samples} samples is {nb_good * 1. / nb_samples}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
creating_models.py
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
|
22 |
+
# BEGIN: insert your ML task here
|
23 |
+
# Typically
|
24 |
+
from sklearn.datasets import make_classification
|
25 |
+
from sklearn.model_selection import train_test_split
|
26 |
+
|
27 |
+
from concrete.ml.sklearn import LogisticRegression
|
28 |
+
|
29 |
+
x, y = make_classification(n_samples=1000, class_sep=2, n_features=30, random_state=42)
|
30 |
+
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)
|
31 |
+
|
32 |
+
model_dev = LogisticRegression()
|
33 |
+
model_dev.fit(X_train, y_train)
|
34 |
+
# END: insert your ML task here
|
35 |
+
|
36 |
+
compile_and_make_it_deployable(model_dev, X_train)
|
37 |
+
print("Your model is ready to be deployable.")
|
handler.py
CHANGED
@@ -49,6 +49,17 @@ class EndpointHandler:
|
|
49 |
|
50 |
return {"uid": uid}
|
51 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
elif method == "inference":
|
53 |
|
54 |
uid = data.pop("uid", data)
|
|
|
49 |
|
50 |
return {"uid": uid}
|
51 |
|
52 |
+
elif method == "append_key":
|
53 |
+
|
54 |
+
# Get key piece
|
55 |
+
evaluation_keys = from_json(data.pop("evaluation_keys", data))
|
56 |
+
|
57 |
+
uid = data.pop("uid", data)
|
58 |
+
|
59 |
+
self.key_database[uid] += evaluation_keys
|
60 |
+
|
61 |
+
return
|
62 |
+
|
63 |
elif method == "inference":
|
64 |
|
65 |
uid = data.pop("uid", data)
|
play_with_endpoint.py
CHANGED
@@ -53,21 +53,56 @@ fhemodel_client.generate_private_and_evaluation_keys()
|
|
53 |
evaluation_keys = fhemodel_client.get_serialized_evaluation_keys()
|
54 |
|
55 |
# Save the key in the database
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
|
65 |
# Test the handler
|
66 |
nb_good = 0
|
67 |
nb_samples = len(X_test)
|
68 |
-
verbose =
|
69 |
time_start = time.time()
|
70 |
duration = 0
|
|
|
71 |
|
72 |
for i in range(nb_samples):
|
73 |
|
@@ -82,8 +117,9 @@ for i in range(nb_samples):
|
|
82 |
"uid": uid,
|
83 |
}
|
84 |
|
85 |
-
if
|
86 |
-
print(f"Size of the payload: {sys.getsizeof(payload) / 1024} kilobytes")
|
|
|
87 |
|
88 |
# Run the inference on HF servers
|
89 |
duration -= time.time()
|
@@ -98,7 +134,7 @@ for i in range(nb_samples):
|
|
98 |
prediction_proba = fhemodel_client.deserialize_decrypt_dequantize(encrypted_prediction)[0]
|
99 |
prediction = np.argmax(prediction_proba)
|
100 |
|
101 |
-
if verbose
|
102 |
print(
|
103 |
f"for {i}-th input, {prediction=} with expected {Y_test[i]} in {duration_inference:.3f} seconds"
|
104 |
)
|
@@ -107,6 +143,5 @@ for i in range(nb_samples):
|
|
107 |
nb_good += Y_test[i] == prediction
|
108 |
|
109 |
print(f"Accuracy on {nb_samples} samples is {nb_good * 1. / nb_samples}")
|
110 |
-
print(f"Total time: {time.time() - time_start} seconds")
|
111 |
-
print(f"Duration
|
112 |
-
print(f"Duration per inference: {duration / nb_samples} seconds")
|
|
|
53 |
evaluation_keys = fhemodel_client.get_serialized_evaluation_keys()
|
54 |
|
55 |
# Save the key in the database
|
56 |
+
evaluation_keys_remaining = evaluation_keys[:]
|
57 |
+
uid = None
|
58 |
+
is_first = True
|
59 |
+
is_finished = False
|
60 |
+
i = 0
|
61 |
+
packet_size = 1024 * 1024 * 100
|
62 |
+
|
63 |
+
while not is_finished:
|
64 |
+
|
65 |
+
# Send by packets of 100M
|
66 |
+
if sys.getsizeof(evaluation_keys_remaining) > packet_size:
|
67 |
+
evaluation_keys_piece = evaluation_keys_remaining[:packet_size]
|
68 |
+
evaluation_keys_remaining = evaluation_keys_remaining[packet_size:]
|
69 |
+
else:
|
70 |
+
evaluation_keys_piece = evaluation_keys_remaining
|
71 |
+
is_finished = True
|
72 |
+
|
73 |
+
print(
|
74 |
+
f"Sending {i}-th piece of the key (remaining size is {sys.getsizeof(evaluation_keys_remaining) / 1024:.2f} kbytes)"
|
75 |
+
)
|
76 |
+
i += 1
|
77 |
+
|
78 |
+
if is_first:
|
79 |
+
is_first = False
|
80 |
+
payload = {
|
81 |
+
"inputs": "fake",
|
82 |
+
"evaluation_keys": to_json(evaluation_keys_piece),
|
83 |
+
"method": "save_key",
|
84 |
+
}
|
85 |
+
|
86 |
+
uid = query(payload)["uid"]
|
87 |
+
print(f"Storing the key in the database under {uid=}")
|
88 |
+
|
89 |
+
else:
|
90 |
+
payload = {
|
91 |
+
"inputs": "fake",
|
92 |
+
"evaluation_keys": to_json(evaluation_keys_piece),
|
93 |
+
"method": "append_key",
|
94 |
+
"uid": uid,
|
95 |
+
}
|
96 |
+
|
97 |
+
query(payload)
|
98 |
|
99 |
# Test the handler
|
100 |
nb_good = 0
|
101 |
nb_samples = len(X_test)
|
102 |
+
verbose = True
|
103 |
time_start = time.time()
|
104 |
duration = 0
|
105 |
+
is_first = True
|
106 |
|
107 |
for i in range(nb_samples):
|
108 |
|
|
|
117 |
"uid": uid,
|
118 |
}
|
119 |
|
120 |
+
if is_first:
|
121 |
+
print(f"Size of the payload: {sys.getsizeof(payload) / 1024:.2f} kilobytes")
|
122 |
+
is_first = False
|
123 |
|
124 |
# Run the inference on HF servers
|
125 |
duration -= time.time()
|
|
|
134 |
prediction_proba = fhemodel_client.deserialize_decrypt_dequantize(encrypted_prediction)[0]
|
135 |
prediction = np.argmax(prediction_proba)
|
136 |
|
137 |
+
if verbose:
|
138 |
print(
|
139 |
f"for {i}-th input, {prediction=} with expected {Y_test[i]} in {duration_inference:.3f} seconds"
|
140 |
)
|
|
|
143 |
nb_good += Y_test[i] == prediction
|
144 |
|
145 |
print(f"Accuracy on {nb_samples} samples is {nb_good * 1. / nb_samples}")
|
146 |
+
print(f"Total time: {time.time() - time_start:.3f} seconds")
|
147 |
+
print(f"Duration per inference: {duration / nb_samples:.3f} seconds")
|
|