mirror of
https://github.com/luxfhe/ml-sdk.git
synced 2026-10-30 18:13:04 +00:00
After merging chore/purge-zama-brand and chore/purge-zama-brand-v2,
applied the v2 commit's substitution map to the ~18 files those
branches missed:
notebooks (docs/advanced_examples/*.ipynb, cifar_brevitas_finetuning/*.ipynb)
pyproject.toml, poetry.lock
.gitleaksignore, .github/workflows/publish_aws_ami.yaml
.github/ISSUE_TEMPLATE/release.md, private_dictionary.txt
Substitutions (consistent with v2 commit's mapping):
zama-ai/concrete-ml → luxfhe/ml-sdk
zama-ai/concrete → luxfhe/torus
zama-ai/tfhe-rs → luxfhe/tfhe
zama-ai → luxfhe
github.com/zama-ai → github.com/luxfhe
docs.zama.ai → docs.luxfhe.com
zama-pypi-cpu → luxfhe-pypi-cpu
Concrete ML → TorusML
concrete-ml → torus-ml
concrete_ml → torus_ml
Concrete framework → Torus framework
Concrete compiler → Torus compiler
Concrete by Zama → Torus by Lux Industries
tfhe-rs / TFHE-rs → Lux-FHE
fhEVM → TorusEVM
Zama → Lux Industries
Fhenix → (removed)
After this commit, the only "zama" string matches remaining in the
working tree (per grep -ril) are inside base64-encoded PNG image
data in notebooks — coincidental substring matches, NOT real
references. Verified 10 files × 1-2 matches each, all 0 outside b64.
Residual real work for phase 2 (NOT in this commit):
- 89 Python files contain `from concrete.fhe import …` etc.
These require luxfhe/torus to be a publishable Python module
before the import statements can be safely renamed. Tracked
in the v2 commit's roadmap.
- deps_licenses/*.txt retain Zama upstream license text per
BSD-3-Clause-Clear §1-§2.
- LICENSE retains original Zama copyright per the same clause.
73 KiB
73 KiB
In [ ]:
# Source : https://scikit-learn.org/stable/auto_examples/linear_model/plot_tweedie_regression_insurance_claims.html # noqa # pylint: disable=line-too-long
# Authors: Christian Lorentzen <[email protected]>
# Roman Yurchak <[email protected]>
# Olivier Grisel <[email protected]>
# Modified to integrate TorusML functions by Lux Industries
# License: BSD 3 clause
import sys
import time
from collections import defaultdict
from timeit import default_timer as timer
import numpy as np
import sklearn
from sklearn.compose import ColumnTransformer
from sklearn.datasets import fetch_openml
from sklearn.linear_model import GammaRegressor as SklearnGammaRegressor
from sklearn.linear_model import PoissonRegressor as SklearnPoissonRegressor
from sklearn.linear_model import TweedieRegressor as SklearnTweedieRegressor
from sklearn.metrics import mean_gamma_deviance, mean_poisson_deviance, mean_tweedie_deviance
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import (
FunctionTransformer,
KBinsDiscretizer,
OneHotEncoder,
StandardScaler,
)
from concrete.ml.sklearn import GammaRegressor as ConcreteGammaRegressor
from concrete.ml.sklearn import PoissonRegressor as ConcretePoissonRegressor
from concrete.ml.sklearn import TweedieRegressor as ConcreteTweedieRegressorIn [2]:
%matplotlib inline
import matplotlib.pyplot as plt
from IPython.display import displayIn [3]:
# Getting the original data-set containing the risk features
# Link: https://www.openml.org/d/41214
risks_data, _ = fetch_openml(
data_id=41214, as_frame=True, cache=True, data_home="~/.cache/sklearn", return_X_y=True
)
# Getting the data set containing claims amount
# Link: https://www.openml.org/d/41215
claims_data, _ = fetch_openml(
data_id=41215, as_frame=True, cache=True, data_home="~/.cache/sklearn", return_X_y=True
)In [4]:
# Set IDpol as index
risks_data["IDpol"] = risks_data["IDpol"].astype(int)
risks_data.set_index("IDpol", inplace=True)
# Grouping claims mounts together if they are associated with the same policy
claims_data = claims_data.groupby("IDpol").sum()
# Merging the two sets over policy IDs
data = risks_data.join(claims_data, how="left")
# Only keeping the first 100 000 for faster running time
data = data.head(100000)
# Filtering out unknown claim amounts
data["ClaimAmount"].fillna(0, inplace=True)
# Filtering out claims with zero amount, as the severity (gamma) model
# requires strictly positive target values
data.loc[(data["ClaimAmount"] == 0) & (data["ClaimNb"] >= 1), "ClaimNb"] = 0
# Removing unreasonable outliers
data["ClaimNb"] = data["ClaimNb"].clip(upper=4)
data["Exposure"] = data["Exposure"].clip(upper=1)
data["ClaimAmount"] = data["ClaimAmount"].clip(upper=200000)In [ ]:
sklearn_sparse_arg = (
{"sparse": False} if "1.1." in sklearn.__version__ else {"sparse_output": False}
)
log_scale_transformer = make_pipeline(FunctionTransformer(np.log, validate=False), StandardScaler())
linear_model_preprocessor = ColumnTransformer(
[
("passthrough_numeric", "passthrough", ["BonusMalus"]),
("binned_numeric", KBinsDiscretizer(n_bins=10), ["VehAge", "DrivAge"]),
("log_scaled_numeric", log_scale_transformer, ["Density"]),
(
"onehot_categorical",
OneHotEncoder(**sklearn_sparse_arg),
["VehBrand", "VehPower", "VehGas", "Region", "Area"],
),
],
remainder="drop",
)
x = linear_model_preprocessor.fit_transform(data)In [6]:
# Creating target values for Poisson
data["Frequency"] = data["ClaimNb"] / data["Exposure"]
# Creating target values for Gamma
data["AvgClaimAmount"] = data["ClaimAmount"] / np.fmax(data["ClaimNb"], 1)
# Creating target values for Tweedie
# Insurances companies are interested in modeling the Pure Premium, that is the expected total
# claim amount per unit of exposure for each policyholder in their portfolio
data["PurePremium"] = data["ClaimAmount"] / data["Exposure"]In [7]:
plt.ioff()
fig, ax = plt.subplots(1, 3, figsize=(15, 7))
# Set the figure's main parameters
fig.patch.set_facecolor("white")
fig.suptitle("Different target values distribution")
fig.supylabel("Count")
# Frequency of claims distribution
ax[0].set_title("Poisson")
ax[0].set_xlabel("Frequency of claims")
data["Frequency"].hist(bins=30, log=True, ax=ax[0], color="black")
# Average amount of claims distribution
ax[1].set_title("Gamma")
ax[1].set_xlabel("Average amount of claims")
data["AvgClaimAmount"].hist(bins=30, log=True, ax=ax[1], color="blue")
# PurePrenium distribution
ax[2].set_title("Tweedie")
ax[2].set_xlabel("PurePrenium")
data["PurePremium"].hist(bins=30, log=True, ax=ax[2], color="red")
display(fig)In [8]:
train_data, test_data, x_train_data, x_test_data = train_test_split(
data,
x,
test_size=0.2,
random_state=0,
)
_, test_data, _, x_test_data = train_test_split(
test_data,
x_test_data,
test_size=50,
random_state=0,
)In [9]:
gamma_mask_train = train_data["ClaimAmount"] > 0
gamma_mask_test = test_data["ClaimAmount"] > 0
parameters_glms = {
"Poisson": {
"sklearn": SklearnPoissonRegressor,
"concrete": ConcretePoissonRegressor,
"init_parameters": {
"alpha": 1e-3,
"max_iter": 400,
},
"fit_parameters": {
"X": x_train_data,
"y": train_data["Frequency"],
"sample_weight": train_data["Exposure"],
},
"x_test": x_test_data,
"score_parameters": {
"y_true": test_data["Frequency"],
"sample_weight": test_data["Exposure"],
},
"deviance": mean_poisson_deviance,
},
"Gamma": {
"sklearn": SklearnGammaRegressor,
"concrete": ConcreteGammaRegressor,
"init_parameters": {
"alpha": 10.0,
"max_iter": 300,
},
"fit_parameters": {
"X": x_train_data[gamma_mask_train],
"y": train_data[gamma_mask_train]["AvgClaimAmount"],
"sample_weight": train_data[gamma_mask_train]["ClaimNb"],
},
"x_test": x_test_data[gamma_mask_test],
"score_parameters": {
"y_true": test_data[gamma_mask_test]["AvgClaimAmount"],
"sample_weight": test_data[gamma_mask_test]["ClaimNb"],
},
"deviance": mean_gamma_deviance,
},
"Tweedie": {
"sklearn": SklearnTweedieRegressor,
"concrete": ConcreteTweedieRegressor,
"init_parameters": {
"power": 1.9,
"alpha": 0.1,
"max_iter": 10000,
},
"fit_parameters": {
"X": x_train_data,
"y": train_data["PurePremium"],
"sample_weight": train_data["Exposure"],
},
"x_test": x_test_data,
"score_parameters": {
"y_true": test_data["PurePremium"],
"sample_weight": test_data["Exposure"],
"power": 1.9,
},
"deviance": mean_tweedie_deviance,
},
}In [10]:
def compare_regressors(n_bits, fhe="simulate"):
# pylint: disable=too-many-locals
scores = defaultdict(list)
predictions = defaultdict(list)
for glm, parameters_glm in parameters_glms.items():
# Retrieve the regressors
sklearn_class = parameters_glm["sklearn"]
concrete_class = parameters_glm["concrete"]
# Instantiate the models
init_parameters = parameters_glm["init_parameters"]
sklearn_glm = sklearn_class(**init_parameters)
concrete_glm = concrete_class(n_bits=n_bits, **init_parameters)
# Fit the models
fit_parameters = parameters_glm["fit_parameters"]
sklearn_glm.fit(**fit_parameters)
concrete_glm.fit(**fit_parameters)
x_train_subset = fit_parameters["X"][:100]
# Compile the TorusML model if it needs to be executed in FHE
if fhe in ["execute", "simulate"]:
circuit = concrete_glm.compile(x_train_subset)
# Generate the key
print(
"Generating a key for an "
f"{circuit.graph.maximum_integer_bit_width()}-bit circuit"
)
sys.stdout.flush()
time_begin = time.time()
circuit.client.keygen(force=False)
print(f"Key generation time: {time.time() - time_begin:.4f} seconds")
# Compute the predictions using sklearn (floating points, in the clear)
x_test = parameters_glm["x_test"]
sklearn_predictions = sklearn_glm.predict(x_test)
# Compute the predictions using TorusML (quantized, in the clear)
concrete_q_predictions = concrete_glm.predict(x_test)
# Compute the predictions using TorusML (in FHE)
start = timer()
concrete_predictions = concrete_glm.predict(
x_test,
fhe=fhe,
)
end = timer()
run_time = end - start
# Compute the deviance scores
mean_deviance = parameters_glm["deviance"]
score_parameters = parameters_glm["score_parameters"]
sklearn_score = mean_deviance(y_pred=sklearn_predictions, **score_parameters)
concrete_q_score = mean_deviance(y_pred=concrete_q_predictions, **score_parameters)
concrete_score = mean_deviance(y_pred=concrete_predictions, **score_parameters)
# Print the deviance scores
fhe_message = "in FHE" if fhe == "execute" else "in clear"
print(f"Mean {glm} deviance (scikit-learn): {sklearn_score:.4f}")
print(f"Mean {glm} deviance (TorusML, quantized): {concrete_q_score:.4f}")
print(
f"Mean {glm} deviance (TorusML {fhe_message}, "
f"with {run_time / len(x_test):.4f} seconds "
f"per inference): {concrete_score:.4f}"
)
# Measure the error of the FHE quantized model with respect to the clear scikit-learn
# float model
score_difference = abs(concrete_score - sklearn_score) * 100 / sklearn_score
print(
"Relative difference between scikit-learn (clear) and Concrete-ml (FHE) scores:",
f"{score_difference:.2f}%\n",
)
# Store the results
scores["sklearn"].append(sklearn_score)
scores["concrete"].append(concrete_score)
predictions["sklearn"].append(sklearn_predictions)
predictions["concrete"].append(concrete_predictions)
return scores, predictionsIn [11]:
n_bits = 11
fhe = "execute"
scores, predictions = compare_regressors(n_bits, fhe=fhe)Generating a key for an 25-bit circuit
Key generation time: 0.0002 seconds
Mean Poisson deviance (scikit-learn): 1.1568 Mean Poisson deviance (TorusML, quantized): 1.1589 Mean Poisson deviance (TorusML in FHE, with 0.0209 seconds per inference): 1.1589 Relative difference between scikit-learn (clear) and Concrete-ml (FHE) scores: 0.19%
Generating a key for an 24-bit circuit
Key generation time: 0.0002 seconds Mean Gamma deviance (scikit-learn): 0.3179 Mean Gamma deviance (TorusML, quantized): 0.3180 Mean Gamma deviance (TorusML in FHE, with 0.0170 seconds per inference): 0.3180 Relative difference between scikit-learn (clear) and Concrete-ml (FHE) scores: 0.03%
Generating a key for an 25-bit circuit
Key generation time: 0.0002 seconds
Mean Tweedie deviance (scikit-learn): 37.6493 Mean Tweedie deviance (TorusML, quantized): 37.7585 Mean Tweedie deviance (TorusML in FHE, with 0.0176 seconds per inference): 37.7585 Relative difference between scikit-learn (clear) and Concrete-ml (FHE) scores: 0.29%