mirror of
https://github.com/luxfi/torus-ml.git
synced 2026-10-30 15:15:23 +00:00
The distribution called itself torus-ml while shipping a package directory named concrete, so the name a user installs and the name they import disagreed — and most of the tree already said torus.ml, which could not resolve against src/concrete. Moved src/concrete to src/torus and pointed the include at it. The dependency was Zama's concrete-python from their index. It is torus-fhe from ours, which is what luxfhe/torus already ships: that frontend declares torus-fhe and ships a torus/fhe module, so every 'from torus.fhe' in this tree was already naming our compiler and only the dependency lagged. Two distributions share one namespace, which is the shape the upstream this forked from used and the reason the split works: torus-fhe gives torus.fhe, torus-ml gives torus.ml. Swept every other spelling — identifiers, enum members, workflow variables, filenames, notebooks. LICENSE and NOTICE keep the upstream attribution a fork owes; the figures carry the old name in JPEG metadata a text sweep cannot reach.
226 KiB
226 KiB
In [1]:
import time
import numpy as np
import pandas as pd
from sklearn.datasets import load_diabetes
from sklearn.metrics import make_scorer, mean_squared_error
from sklearn.model_selection import GridSearchCV, KFold, train_test_split
from sklearn.svm import LinearSVR as SklearnLinearSVR
from torus.ml.sklearn.svm import LinearSVR as TorusLinearSVRIn [2]:
%matplotlib inline
import matplotlib.pyplot as plt
from IPython.display import display
train_plot_config = {"c": "black", "marker": "D", "s": 15, "label": "Train data"}
test_plot_config = {"c": "red", "marker": "x", "s": 15, "label": "Test data"}
def get_sklearn_plot_config(mse_score=None):
label = "scikit-learn"
if mse_score is not None:
label += f", {'$MSE$'}={mse_score:.4f}"
return {"c": "blue", "linewidth": 2.5, "label": label}
def get_torus_plot_config(mse_score=None):
label = "Torus ML"
if mse_score is not None:
label += f", {'$MSE$'}={mse_score:.4f}"
return {"c": "orange", "linewidth": 2.5, "label": label}In [3]:
# Load the diabetes data-set
X, y = load_diabetes(return_X_y=True)
# Use only one feature for educational purpose
X = X[:, np.newaxis, 2]
# We split the data-set into a training and a testing set
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=23)
# We sort the test set for a better visualization
sorted_indexes = np.argsort(np.squeeze(X_test))
X_test = X_test[sorted_indexes, :]
y_test = y_test[sorted_indexes]In [4]:
plt.ioff()
plt.clf()
fig, ax = plt.subplots(1, figsize=(10, 5))
fig.patch.set_facecolor("white")
ax.scatter(X_train, y_train, **train_plot_config)
ax.scatter(X_test, y_test, **test_plot_config)
ax.legend()
display(fig)In [5]:
grid_scorer = make_scorer(mean_squared_error, greater_is_better=False)In [6]:
param_grid = {
"epsilon": [0.0, 1.0, 10.0, 20.0],
"C": [0.1, 100.0, 10000.0, 100000.0],
}
sklearn_rgs = SklearnLinearSVR()
kfold_cv = KFold(n_splits=5, shuffle=True, random_state=13)
gs_sklearn = GridSearchCV(
sklearn_rgs,
param_grid,
cv=kfold_cv,
scoring=grid_scorer,
verbose=1,
).fit(X_train, y_train)Fitting 5 folds for each of 16 candidates, totalling 80 fits
In [7]:
param_grid = {
"n_bits": [6, 8, 12],
"epsilon": [0.0, 1.0, 10.0, 20.0],
"C": [0.1, 100.0, 10000.0, 100000.0],
}
torus_rgs = TorusLinearSVR()
gs_torus = GridSearchCV(
torus_rgs,
param_grid,
cv=kfold_cv,
scoring=grid_scorer,
verbose=1,
).fit(X_train, y_train)Fitting 5 folds for each of 48 candidates, totalling 240 fits
In [8]:
plt.ioff()
results_df = pd.DataFrame(gs_torus.cv_results_)
fig, ax = plt.subplots(1, figsize=(12, 8))
(l1,) = ax.plot(
np.arange(16), -results_df.loc[results_df["param_n_bits"] == 6, "mean_test_score"], "-o"
)
(l2,) = ax.plot(
np.arange(16), -results_df.loc[results_df["param_n_bits"] == 8, "mean_test_score"], "-o"
)
(l3,) = ax.plot(
np.arange(16), -results_df.loc[results_df["param_n_bits"] == 12, "mean_test_score"], "-o"
)
ax.legend((l1, l2, l3), ("n_bits = 6", "n_bits = 8", "n_bits = 12"), loc="upper right", shadow=True)
ax.set_xlabel("Different models with fixed values of C and epsilon")
ax.set_ylabel("Mean MSE accros CV folds")
ax.set_title("Impact of `n_bits` on Cross Validation performances")
display(fig)In [9]:
# Print mean time fit and std time fit for both models
print(
f"Mean time fit sklearn: {np.mean(gs_sklearn.cv_results_['mean_fit_time']):.3f}s,"
f" std time fit sklearn: {np.std(gs_sklearn.cv_results_['mean_fit_time']):.3f}s"
)
print(
f"Mean time fit torus: {np.mean(gs_torus.cv_results_['mean_fit_time']):.3f}s,"
f"std time fit torus: {np.std(gs_torus.cv_results_['mean_fit_time']):.3f}s"
)
# Print best score for both models
print(f"Best MSE score sklearn: {-gs_sklearn.best_score_:.2f}")
print(f"Best MSE score torus: {-gs_torus.best_score_:.2f}")Mean time fit sklearn: 0.002s, std time fit sklearn: 0.002s Mean time fit torus: 0.027s,std time fit torus: 0.002s Best MSE score sklearn: 4069.06 Best MSE score torus: 4053.61
In [10]:
# Get best hyperparameters out of gs_torus
best_params_torus = gs_torus.best_params_
print(f"Best parameters for Torus: {best_params_torus}")
best_params_sklearn = gs_sklearn.best_params_
print(f"Best parameters for Sklearn: {best_params_sklearn}")Best parameters for Torus: {'C': 10000.0, 'epsilon': 20.0, 'n_bits': 6}
Best parameters for Sklearn: {'C': 10000.0, 'epsilon': 20.0}
In [11]:
# Train torus and sklearn LinearSVR with best hyper parameters
torus_rgs = TorusLinearSVR(**best_params_torus)
torus_rgs, sklearn_rgs = torus_rgs.fit_benchmark(X_train, y_train)In [12]:
# Compile the model using the training data
circuit = torus_rgs.compile(X_train)In [13]:
# Generate the key
print(f"Generating a key for an {circuit.graph.maximum_integer_bit_width()}-bit circuit")Generating a key for an 7-bit circuit
In [14]:
time_begin = time.time()
circuit.client.keygen(force=False)
print(f"Key generation time: {time.time() - time_begin:.2f} seconds")Key generation time: 0.00 seconds
In [15]:
# Now predict using the FHE-quantized model on the testing set
time_begin = time.time()
y_pred_fhe = torus_rgs.predict(X_test, fhe="execute")
print(f"Execution time: {(time.time() - time_begin) / len(X_test):.4f} seconds per sample")
# Now predict using the Sklearn model on the testing set
time_begin = time.time()
y_pred_sklearn = sklearn_rgs.predict(X_test)
print(f"Execution time: {(time.time() - time_begin) / len(X_test):.4f} seconds per sample")
# Now predict using clear quantized Torus ML model on testing set
time_begin = time.time()
y_preds_quantized = torus_rgs.predict(X_test)
print(f"Execution time: {(time.time() - time_begin) / len(X_test):.4f} seconds per sample")Execution time: 0.0026 seconds per sample Execution time: 0.0000 seconds per sample Execution time: 0.0000 seconds per sample
In [16]:
# Print all MSE a string to explain
mse_sklearn = mean_squared_error(y_test, y_pred_sklearn)
mse_clear = mean_squared_error(y_test, y_preds_quantized)
mse_fhe = mean_squared_error(y_test, y_pred_fhe)
print(
f"Clear FP32 sklearn model MSE: {mse_sklearn:.3f}\n"
f"Clear quantized model MSE: {mse_clear:.3f}\n"
f"FHE model MSE: {mse_fhe:.3f}"
)
# Measure the error of the FHE-quantized model with respect to quantized clear Torus ML model
torus_score_difference = abs(mse_fhe - mse_clear) * 100 / mse_clear
print(
"\nRelative difference between Torus-ml (quantized clear) and Torus-ml (FHE) scores:",
f"{torus_score_difference:.2f}%",
)
# Measure the error of the FHE quantized model with respect to the sklearn float model
score_difference = abs(mse_fhe - mse_sklearn) * 100 / mse_sklearn
print(
"Relative difference between scikit-learn (clear) and Torus-ml (FHE) scores:",
f"{score_difference:.2f}%",
)Clear FP32 sklearn model MSE: 3852.260 Clear quantized model MSE: 3846.509 FHE model MSE: 3846.509 Relative difference between Torus-ml (quantized clear) and Torus-ml (FHE) scores: 0.00% Relative difference between scikit-learn (clear) and Torus-ml (FHE) scores: 0.15%
In [17]:
# We densify the space representation of the original X,
# to better visualize the resulting step function in the following figure
x_space = np.linspace(X_test.min(), X_test.max(), num=300)
x_space = x_space[:, np.newaxis]
y_pred_q_space = torus_rgs.predict(x_space)In [18]:
plt.ioff()
plt.clf()
fig, ax = plt.subplots(1, figsize=(12, 8))
fig.patch.set_facecolor("white")
ax.scatter(X_train, y_train, **train_plot_config)
ax.scatter(X_test, y_test, **test_plot_config)
ax.plot(X_test, y_pred_sklearn, **get_sklearn_plot_config(mse_sklearn))
ax.plot(x_space, y_pred_q_space, **get_torus_plot_config(mse_clear))
ax.legend()
display(fig)