mirror of
https://github.com/luxfhe/docs.git
synced 2026-10-30 18:13:39 +00:00
35 KiB
35 KiB
In [1]:
import sys
import time
import numpy
from sklearn.datasets import fetch_california_housing
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error
from sklearn.model_selection import train_test_split
from sklearn.utils import resample
import concrete.ml
from concrete.ml.sklearn import DecisionTreeRegressor as ConcreteDecisionTreeRegressor
print(f"Using ConcreteML version {concrete.ml.version.__version__}")
print(f"With Python version {sys.version}")Using ConcreteML version 1.6.0 With Python version 3.8.18 (default, Jul 16 2024, 19:04:03) [GCC 9.4.0]
In [2]:
features_all, target_all = fetch_california_housing(return_X_y=True)
features, target = resample(features_all, target_all, replace=True, n_samples=6000, random_state=42)
# Split data in train-test groups
x_train, x_test, y_train, y_test = train_test_split(
features,
target,
test_size=0.15,
random_state=42,
)In [3]:
%matplotlib inline
import matplotlib.pyplot as plt
plt.hist(target, bins=15, density=True)
plt.show()In [4]:
# Utility functions
def print_as_dollars(x):
"""Prints the value * 100'000$"""
return f"{x * 10**5:.2f}$"
def print_compare_to_baseline(x, baseline_error):
"""Prints percentage improvement over baseline"""
return f"{(x - baseline_error) / baseline_error * 100 :.2f}% of baseline"
mean_error = mean_absolute_error(y_test, numpy.repeat([numpy.median(y_test)], y_test.shape))
print(f"Mean Absolute Overall Error : {print_as_dollars(mean_error)}")Mean Absolute Overall Error : 89922.83$
In [5]:
canary = LinearRegression()
canary.fit(x_train[:, :1], y_train)
baseline_error = mean_absolute_error(canary.predict(x_test[:, :1]), y_test)
print(f"Baseline Mean Error : {print_as_dollars(baseline_error)}")Baseline Mean Error : 62719.65$
In [6]:
default_model = ConcreteDecisionTreeRegressor(criterion="absolute_error", n_bits=6, random_state=42)
begin = time.time()
default_model.fit(x_train, y_train)
print(f"Training on {x_train.shape[0]} samples in {(time.time() - begin):.4f} seconds")Training on 5100 samples in 21.5514 seconds
In [7]:
default_error = mean_absolute_error(default_model.predict(x_test), y_test)
print(
f"Default Model Mean Error: {print_as_dollars(default_error)},"
f"{print_compare_to_baseline(default_error, baseline_error)}"
)Default Model Mean Error: 43788.10$,-30.18% of baseline
In [8]:
# Find best hyper parameters with cross validation
from sklearn.model_selection import GridSearchCV
# List of hyper parameters to tune
param_grid = {
"criterion": ["absolute_error"],
"random_state": [42],
"max_depth": [10],
"n_bits": [6, 7],
"max_features": [2, 5],
"min_samples_leaf": [2, 5],
"min_samples_split": [2, 10],
}
grid_search = GridSearchCV(
ConcreteDecisionTreeRegressor(),
param_grid,
cv=3,
scoring="neg_mean_absolute_error",
error_score="raise",
n_jobs=1,
)
gs_results = grid_search.fit(x_train, y_train)
print("Best hyper parameters:", gs_results.best_params_)
print(f"Min lost: {print_as_dollars(-gs_results.best_score_)}")Best hyper parameters: {'criterion': 'absolute_error', 'max_depth': 10, 'max_features': 5, 'min_samples_leaf': 2, 'min_samples_split': 2, 'n_bits': 7, 'random_state': 42}
Min lost: 43828.16$
In [9]:
# We fix all parameters as the best ones, except for n_bits.
best = gs_results.best_params_
cv_errors = [
{"n_bits": params["n_bits"], "score": score}
for params, score in zip(
gs_results.cv_results_["params"], gs_results.cv_results_["mean_test_score"]
)
if (params["max_depth"] == best["max_depth"])
and (params["max_features"] == best["max_features"]) # noqa: W503
and (params["min_samples_leaf"] == best["min_samples_leaf"]) # noqa: W503
and (params["min_samples_split"] == best["min_samples_split"]) # noqa: W503
]
for el in cv_errors:
print(f"Error for n_bits={el['n_bits']} is {print_as_dollars(-el['score'])}")Error for n_bits=6 is 45585.76$ Error for n_bits=7 is 43828.16$
In [10]:
# Build the model with best hyper parameters
model = ConcreteDecisionTreeRegressor(
max_depth=gs_results.best_params_["max_depth"],
max_features=gs_results.best_params_["max_features"],
min_samples_leaf=gs_results.best_params_["min_samples_leaf"],
min_samples_split=gs_results.best_params_["min_samples_split"],
n_bits=6,
random_state=42,
)In [11]:
model, sklearn_model = model.fit_benchmark(x_train, y_train)In [12]:
# Compute average precision on test
y_pred_concrete = model.predict(x_test)
y_pred_sklearn = sklearn_model.predict(x_test)
concrete_average_precision = mean_absolute_error(y_test, y_pred_concrete)
sklearn_average_precision = mean_absolute_error(y_test, y_pred_sklearn)
print(
f"Sklearn Mean Error: {print_as_dollars(sklearn_average_precision)},"
f"{print_compare_to_baseline(sklearn_average_precision, baseline_error)}"
)
print(
f"Concrete Mean Error: {print_as_dollars(concrete_average_precision)},"
f"{print_compare_to_baseline(concrete_average_precision, baseline_error)}"
)Sklearn Mean Error: 45960.28$,-26.72% of baseline Concrete Mean Error: 46691.16$,-25.56% of baseline
In [13]:
from concrete.compiler import check_gpu_available
use_gpu_if_available = False
device = "cuda" if use_gpu_if_available and check_gpu_available() else "cpu"
x_train_subset = x_train[:500]
begin = time.time()
circuit = model.compile(x_train_subset, device=device)
print(f"Compiled with {len(x_train_subset)} samples in {(time.time() - begin):.4f} seconds")Compiled with 500 samples in 1.4179 seconds
In [14]:
print(f"Generating a key for an {circuit.graph.maximum_integer_bit_width()}-bit circuit")
time_begin = time.time()
circuit.client.keygen(force=False)
print(f"Key generation time: {time.time() - time_begin:.2f} seconds")Generating a key for an 8-bit circuit Key generation time: 0.84 seconds
In [15]:
FHE_SAMPLES = 3
x_test_small = x_test[:FHE_SAMPLES]
y_pred = y_test[:FHE_SAMPLES]In [16]:
# Predict in FHE for a few examples
time_begin = time.time()
y_pred_fhe = model.predict(x_test_small, fhe="execute")
print(f"Execution time: {(time.time() - time_begin) / FHE_SAMPLES:.2f} seconds per sample")Execution time: 1.54 seconds per sample
In [17]:
# Check prediction FHE vs sklearn
print("Cipher estimates:")
print(f"{', '.join(f'{print_as_dollars(x)}' for x in y_pred_fhe)}")
print("Plain estimates:")
print(f"{', '.join(f'{print_as_dollars(x)}' for x in y_pred)}")
print("Differences:")
print(f"{', '.join(f'{print_as_dollars(x)}' for x in (y_pred_fhe - y_pred))}")Cipher estimates: 293651.38$, 277778.33$, 126984.38$ Plain estimates: 290800.00$, 214600.00$, 102800.00$ Differences: 2851.38$, 63178.33$, 24184.38$
In [18]:
# Concatenate all the steps in one function of n_bits
def evaluate(n_bits):
model = ConcreteDecisionTreeRegressor(
max_depth=gs_results.best_params_["max_depth"],
max_features=gs_results.best_params_["max_features"],
min_samples_leaf=gs_results.best_params_["min_samples_leaf"],
min_samples_split=gs_results.best_params_["min_samples_split"],
n_bits=n_bits,
random_state=42,
)
model, sklearn_model = model.fit_benchmark(x_train, y_train)
y_pred_concrete = model.predict(x_test)
y_pred_sklearn = sklearn_model.predict(x_test)
concrete_average_precision = mean_absolute_error(y_test, y_pred_concrete)
sklearn_average_precision = mean_absolute_error(y_test, y_pred_sklearn)
print(
f"Sklearn Mean Error: {print_as_dollars(sklearn_average_precision)},"
f"{print_compare_to_baseline(sklearn_average_precision, baseline_error)}"
)
print(
f"Concrete Mean Error: {print_as_dollars(concrete_average_precision)},"
f"{print_compare_to_baseline(concrete_average_precision, baseline_error)}"
)
x_train_subset = x_train[:500]
begin = time.time()
circuit = model.compile(x_train_subset)
print(
f"Circuit compiled with {len(x_train_subset)} samples in {(time.time() - begin):.4f} "
"seconds"
)
print(f"Generating a key for an {circuit.graph.maximum_integer_bit_width()}-bit circuit")
time_begin = time.time()
circuit.client.keygen(force=False)
print(f"Key generation time: {time.time() - time_begin:.2f} seconds")
time_begin = time.time()
model.predict(x_test_small, fhe="execute")
print(f"Execution time: {(time.time() - time_begin) / FHE_SAMPLES:.2f} seconds per sample")
for n_bits in [6, 7]:
header = f"N_BITS = {n_bits}"
print(header)
print("-" * len(header))
evaluate(n_bits)
print()N_BITS = 6 ----------
Sklearn Mean Error: 45960.28$,-26.72% of baseline Concrete Mean Error: 46691.16$,-25.56% of baseline
Circuit compiled with 500 samples in 1.4455 seconds Generating a key for an 8-bit circuit Key generation time: 0.82 seconds
Execution time: 1.64 seconds per sample N_BITS = 7 ----------
Sklearn Mean Error: 45960.28$,-26.72% of baseline Concrete Mean Error: 43046.38$,-31.37% of baseline
Circuit compiled with 500 samples in 1.3987 seconds Generating a key for an 9-bit circuit Key generation time: 0.58 seconds
Execution time: 1.40 seconds per sample