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.
10 KiB
10 KiB
In [1]:
import time
import numpy
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
features, classes = fetch_openml(data_id=44, as_frame=False, cache=True, return_X_y=True)
classes = classes.astype(numpy.int64)
x_train, x_test, y_train, y_test = train_test_split(
features,
classes,
test_size=0.15,
random_state=42,
)In [2]:
# Find best hyper parameters with cross validation
from sklearn.model_selection import GridSearchCV
from torus.ml.sklearn import DecisionTreeClassifier as TorusDecisionTreeClassifier
# List of hyper parameters to tune
param_grid = {
"max_features": [None, "sqrt", "log2"],
"min_samples_leaf": [1, 10, 100],
"min_samples_split": [2, 10, 100],
"max_depth": [None, 2, 4, 6, 8],
}
grid_search = GridSearchCV(
TorusDecisionTreeClassifier(),
param_grid,
cv=10,
scoring="average_precision",
error_score="raise",
n_jobs=1,
)
gs_results = grid_search.fit(x_train, y_train)
print("Best hyper parameters:", gs_results.best_params_)
print("Best score:", gs_results.best_score_)
# Build the model with best hyper parameters
model = TorusDecisionTreeClassifier(
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"],
max_depth=gs_results.best_params_["max_depth"],
n_bits=6,
)Best hyper parameters: {'max_depth': None, 'max_features': None, 'min_samples_leaf': 10, 'min_samples_split': 100}
Best score: 0.9295527168344
In [3]:
model, sklearn_model = model.fit_benchmark(x_train, y_train)In [4]:
# Compute average precision on test
from sklearn.metrics import average_precision_score
# pylint: disable=no-member
y_pred_torus = model.predict_proba(x_test)[:, 1]
y_pred_sklearn = sklearn_model.predict_proba(x_test)[:, 1]
torus_average_precision = average_precision_score(y_test, y_pred_torus)
sklearn_average_precision = average_precision_score(y_test, y_pred_sklearn)
print(f"Sklearn average precision score: {sklearn_average_precision:0.2f}")
print(f"Torus average precision score: {torus_average_precision:0.2f}")Sklearn average precision score: 0.94 Torus average precision score: 0.97
In [5]:
# Show the confusion matrix on x_test
from sklearn.metrics import confusion_matrix
y_pred = model.predict(x_test)
true_negative, false_positive, false_negative, true_positive = confusion_matrix(
y_test, y_pred, normalize="true"
).ravel()
num_samples = len(y_test)
num_spam = sum(y_test)
print(f"Number of test samples: {num_samples}")
print(f"Number of spams in test samples: {num_spam}")
print(f"True Negative (legit mail well classified) rate: {true_negative}")
print(f"False Positive (legit mail classified as spam) rate: {false_positive}")
print(f"False Negative (spam mail classified as legit) rate: {false_negative}")
print(f"True Positive (spam well classified) rate: {true_positive}")Number of test samples: 691 Number of spams in test samples: 304 True Negative (legit mail well classified) rate: 0.9612403100775194 False Positive (legit mail classified as spam) rate: 0.03875968992248062 False Negative (spam mail classified as legit) rate: 0.14473684210526316 True Positive (spam well classified) rate: 0.8552631578947368
In [6]:
from torus.compiler import check_gpu_available
use_gpu_if_available = False
device = "cuda" if use_gpu_if_available and check_gpu_available() else "cpu"
# We first compile the model with some data, here the training set
circuit = model.compile(x_train, device=device)In [7]:
print(f"Generating a key for an {circuit.graph.maximum_integer_bit_width()}-bit circuit")Generating a key for an 8-bit circuit
In [8]:
time_begin = time.time()
circuit.client.keygen(force=False)
print(f"Key generation time: {time.time() - time_begin:.2f} seconds")Key generation time: 0.78 seconds
In [9]:
# Reduce the sample size for a faster total execution time
FHE_SAMPLES = 10
x_test = x_test[:FHE_SAMPLES]
y_pred = y_pred[:FHE_SAMPLES]
y_reference = y_test[:FHE_SAMPLES]In [10]:
# Predict in FHE for a few examples
time_begin = time.time()
y_pred_fhe = model.predict(x_test, fhe="execute")
print(f"Execution time: {(time.time() - time_begin) / len(x_test):.2f} seconds per sample")Execution time: 0.52 seconds per sample
In [11]:
# Check prediction FHE vs sklearn
print(f"Ground truth: {y_reference}")
print(f"Prediction sklearn: {y_pred}")
print(f"Prediction FHE: {y_pred_fhe}")Ground truth: [0 0 0 1 0 1 0 0 0 0] Prediction sklearn: [0 0 0 1 0 1 0 0 0 0] Prediction FHE: [0 0 0 1 0 1 0 0 0 0]
In [ ]:
print(
f"{numpy.sum(y_pred_fhe == y_pred)}/"
"10 predictions are similar between the FHE model and the clear sklearn model."
)10/10 predictions are similar between the FHE model and the clear sklearn model.