mirror of
https://github.com/luxfhe/docs.git
synced 2026-10-30 18:13:39 +00:00
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 concrete-ml LinearSVC implementation
from concrete.ml.sklearn.svm import LinearSVC as ConcreteLinearSVCIn [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 Concrete ML LinearSVC implementation
svm_concrete = ConcreteLinearSVC(max_iter=100, n_bits=8)
svm_concrete.fit(X_train, y_train)
# plot the boundary
plot_decision_boundary(svm_concrete, X_test, y_test)In [7]:
# A circuit needs to be compiled to enable FHE execution
circuit = svm_concrete.compile(X_train)
# Now that a circuit is compiled, the svm_concrete can predict value with FHE
y_pred = svm_concrete.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 = ConcreteLinearSVC()
# 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_concrete = ConcreteLinearSVC(n_bits=3, C=1, dual=False, penalty="l1")
svm_concrete.fit(X_train, y_train)
# compile the model
circuit = svm_concrete.compile(X_train)
# the model can now be executed with FHE
y_pred = svm_concrete.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_concrete.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