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.
344 KiB
344 KiB
In [1]:
# display visualizations and plots in the notebook itself
%matplotlib inline
# import numpy and matplotlib
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.decomposition import PCA
from sklearn.metrics import accuracy_score, f1_score, make_scorer
from sklearn.model_selection import GridSearchCV, train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC as SklearnLinearSVC
# import the torus-ml LinearSVC implementation
from torus.ml.sklearn.svm import LinearSVC as TorusLinearSVCIn [2]:
def plot_decision_boundary(
clf,
X,
y,
title="LinearSVC Decision Boundary",
xlabel="First Principal Component",
ylabel="Second Principal Component",
):
# Perform PCA to reduce the dimensionality to 2
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)
# Create the mesh grid
x_min, x_max = X_pca[:, 0].min() - 1, X_pca[:, 0].max() + 1
y_min, y_max = X_pca[:, 1].min() - 1, X_pca[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02), np.arange(y_min, y_max, 0.02))
# Transform the mesh grid points back to the original feature space
mesh_points = pca.inverse_transform(np.c_[xx.ravel(), yy.ravel()])
# Make predictions using the classifier
Z = clf.predict(mesh_points)
Z = Z.reshape(xx.shape)
# Plot the decision boundary
_, ax = plt.subplots()
ax.contourf(xx, yy, Z, alpha=0.8)
ax.scatter(X_pca[:, 0], X_pca[:, 1], c=y, edgecolors="k", marker="o", s=50)
# Calculate the accuracy
accuracy = accuracy_score(y, clf.predict(X))
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.title(f"{title} (Accuracy: {accuracy:.4f})")
plt.show()In [3]:
# Get the data
df = pd.read_csv(
"https://gist.githubusercontent.com/robinstraub/72f1cb27829dba85f49f68210979f561/"
"raw/b9982ae654967028f6f4010bd235d850d38fe25b/pulsar-star-dataset.csv"
)
df.head()Out [3]:
| Mean of the integrated profile | Standard deviation of the integrated profile | Excess kurtosis of the integrated profile | Skewness of the integrated profile | Mean of the DM-SNR curve | Standard deviation of the DM-SNR curve | Excess kurtosis of the DM-SNR curve | Skewness of the DM-SNR curve | target_class | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 121.156250 | 48.372971 | 0.375485 | -0.013165 | 3.168896 | 18.399367 | 7.449874 | 65.159298 | 0.0 |
| 1 | 76.968750 | 36.175557 | 0.712898 | 3.388719 | 2.399666 | 17.570997 | 9.414652 | 102.722975 | 0.0 |
| 2 | 130.585938 | 53.229534 | 0.133408 | -0.297242 | 2.743311 | 22.362553 | 8.508364 | 74.031324 | 0.0 |
| 3 | 156.398438 | 48.865942 | -0.215989 | -0.171294 | 17.471572 | NaN | 2.958066 | 7.197842 | 0.0 |
| 4 | 84.804688 | 36.117659 | 0.825013 | 3.274125 | 2.790134 | 20.618009 | 8.405008 | 76.291128 | 0.0 |
In [4]:
# Extract the features and labels
X = df.drop(columns=["target_class"])
y = df["target_class"]
# Replace N/A values with the mean of the respective feature
X.fillna(X.mean(), inplace=True)
# Split the data into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Scale the data
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
# Convert the floating labels to integer labels for both train and test sets
y_train = y_train.astype(int)
y_test = y_test.astype(int)In [5]:
# Train a model with scikit-learn LinearSVC, perform prediction and compute the accuracy
svm_sklearn = SklearnLinearSVC(max_iter=100)
svm_sklearn.fit(X_train, y_train)
# plot the boundary
plot_decision_boundary(svm_sklearn, X_test, y_test)In [6]:
# Perform the same steps with the Torus ML LinearSVC implementation
svm_torus = TorusLinearSVC(max_iter=100, n_bits=8)
svm_torus.fit(X_train, y_train)
# plot the boundary
plot_decision_boundary(svm_torus, X_test, y_test)In [7]:
# A circuit needs to be compiled to enable FHE execution
circuit = svm_torus.compile(X_train)
# Now that a circuit is compiled, the svm_torus can predict value with FHE
y_pred = svm_torus.predict(X_test, fhe="execute")
accuracy = accuracy_score(y_test, y_pred)
# print the accuracy
print(f"FHE Accuracy: {accuracy:.4f} (bit-width: {circuit.graph.maximum_integer_bit_width()})")FHE Accuracy: 0.9765 (bit-width: 17)
In [8]:
# setup and train a scikit-learn LinearSVC model, just as before
svm_sklearn = SklearnLinearSVC()
svm_sklearn.fit(X_train, y_train)
# predict some test data and measure the model accuracy
y_pred_sklearn = svm_sklearn.predict(X_test)
accuracy_sklearn = accuracy_score(y_test, y_pred_sklearn)
print(f"Scikit-learn Accuracy: {accuracy_sklearn:.4f}")Scikit-learn Accuracy: 0.9769
In [9]:
svm = TorusLinearSVC()
# Define the parameter grid for the grid search
param_grid = param_grid = [
{
"C": np.logspace(-3, 3, 7),
"n_bits": range(2, 17),
"penalty": ["l1", "l2"],
"dual": [False, True],
},
]
# Use the F1 score as the metric to optimize, as it provides a
# balanced trade-off between precision and recall
scorer = make_scorer(f1_score, average="weighted")
# Set up the grid search with the custom scoring function
grid_search = GridSearchCV(svm, param_grid, scoring=scorer, cv=5, n_jobs=1)
# Fit the grid search to the data
grid_search.fit(X_train, y_train)
# Convert the grid search results into a pandas DataFrame
results_df = pd.DataFrame(grid_search.cv_results_)
# Define a custom function to highlight a specific row based on n_bits value
def highlight_row(row, n_bits_value=3, color="green"):
return [
f"background-color: {color}" if row["param_n_bits"] == n_bits_value else "" for _ in row
]
# Find the best hyperparameter combination for each n_bits value
best_results = results_df.loc[results_df.groupby("param_n_bits")["mean_test_score"].idxmax()]
best_results = best_results[
["param_n_bits", "param_C", "param_penalty", "param_dual", "mean_test_score"]
]
best_results.reset_index(drop=True, inplace=True)
# Display the best results DataFrame
best_results.style.apply(highlight_row, n_bits_value=3, axis=1).hide()Out [9]:
| param_n_bits | param_C | param_penalty | param_dual | mean_test_score |
|---|---|---|---|---|
| 2 | 0.001000 | l2 | False | 0.945603 |
| 3 | 1.000000 | l1 | False | 0.971102 |
| 4 | 0.010000 | l2 | False | 0.970423 |
| 5 | 10.000000 | l1 | False | 0.973795 |
| 6 | 10.000000 | l2 | True | 0.973072 |
| 7 | 1.000000 | l1 | False | 0.972945 |
| 8 | 10.000000 | l2 | True | 0.973254 |
| 9 | 0.100000 | l2 | False | 0.972720 |
| 10 | 10.000000 | l2 | True | 0.973402 |
| 11 | 100.000000 | l2 | True | 0.973878 |
| 12 | 1.000000 | l2 | False | 0.972929 |
| 13 | 100.000000 | l2 | True | 0.973529 |
| 14 | 1.000000 | l1 | False | 0.972817 |
| 15 | 100.000000 | l2 | True | 0.973064 |
| 16 | 10.000000 | l2 | True | 0.972988 |
In [10]:
svm_torus = TorusLinearSVC(n_bits=3, C=1, dual=False, penalty="l1")
svm_torus.fit(X_train, y_train)
# compile the model
circuit = svm_torus.compile(X_train)
# the model can now be executed with FHE
y_pred = svm_torus.predict(X_test, fhe="simulate")
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy with FHE simulation: {accuracy:.4f}")Accuracy with FHE simulation: 0.9745
In [11]:
# predict the test set to verify the compiled model accuracy
y_pred = svm_torus.predict(X_test, fhe="execute")
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy with FHE execution: {accuracy:.4f}")Accuracy with FHE execution: 0.9745