unlearning CR

This commit is contained in:
2026-06-07 13:49:28 +02:00
parent 61c3447150
commit bc7fd3850d
6 changed files with 126 additions and 9 deletions

17
Tune.py
View File

@@ -13,6 +13,8 @@ from IdentitySubset import IdentitySubset
from architectures.Model import Model, Architecture from architectures.Model import Model, Architecture
from unlearning.LinearFiltration import LinearFiltration from unlearning.LinearFiltration import LinearFiltration
import Util
# WeightFiltration, CertifiedRemoval # WeightFiltration, CertifiedRemoval
# numbre of classes # numbre of classes
@@ -134,10 +136,11 @@ for i in range(0,CLASS_SIZE):
print(f"Total test images for these {CLASS_SIZE} classes: {len(test_data)}") print(f"Total test images for these {CLASS_SIZE} classes: {len(test_data)}")
# Evaluate # Evaluate
model.evaluate( mode, accuracy, report_dict = model.evaluate(
loader = test_loader, loader = test_loader,
mode="finetunned" mode="finetunned"
) )
Util._log_to_csv(model=reloaded, mode = "finetuned", accuracy=accuracy, report_dict=report_dict, strategy="base")
# test again # test again
reloaded = Model.create( reloaded = Model.create(
@@ -169,7 +172,9 @@ for i in range(0,CLASS_SIZE):
# 4. Final Performance Analysis # 4. Final Performance Analysis
print("\n--- Performance on Retained Classes") print("\n--- Performance on Retained Classes")
reloaded.evaluate(loader=retain_test_loader, mode="retain") mode, accuracy, report_dict = reloaded.evaluate(loader=retain_test_loader, mode="retain")
Util._log_to_csv(model=reloaded, mode = "retain", accuracy=accuracy, report_dict=report_dict, strategy="linearFiltration")
print("\n--- Performance on Forgotten Class") print("\n--- Performance on Forgotten Class")
reloaded.evaluate(loader=forget_test_loader,mode="forget") mode, accuracy, report_dict = reloaded.evaluate(loader=forget_test_loader,mode="forget")
Util._log_to_csv(model=reloaded, mode = "forgotten", accuracy=accuracy, report_dict=report_dict, strategy="linearFiltration")

34
Util.py Normal file
View File

@@ -0,0 +1,34 @@
from pathlib import Path
from architectures.Model import Model
def _log_to_csv(model:Model, mode, accuracy, report_dict, strategy):
"""Handles directory structures, file setups, and distinct CSV column formatting."""
arch_name = model.__class__.__name__.lower()
save_dir = Path(f"reports/{strategy}")
save_dir.mkdir(parents=True, exist_ok=True)
csv_path = save_dir / f"{arch_name}-{mode}.csv"
file_exists = csv_path.exists()
headers = [
"accuracy",
"macro_precision", "macro_recall", "macro_f1",
"weighted_precision", "weighted_recall", "weighted_f1"
]
row = [
f"{accuracy / 100.0:.4f}",
f"{report_dict['macro avg']['precision']:.4f}",
f"{report_dict['macro avg']['recall']:.4f}",
f"{report_dict['macro avg']['f1-score']:.4f}",
f"{report_dict['weighted avg']['precision']:.4f}",
f"{report_dict['weighted avg']['recall']:.4f}",
f"{report_dict['weighted avg']['f1-score']:.4f}"
]
with open(csv_path, "a") as f:
if not file_exists:
f.write(",".join(headers) + "\n")
f.write(",".join(row) + "\n")
print(f">> Direct CSV metrics appended to {csv_path}")

View File

@@ -149,10 +149,10 @@ class Model(ABC):
) )
# 3. Delegate file tracking to isolated helper method # 3. Delegate file tracking to isolated helper method
self._log_to_csv(mode, accuracy, classes, report_dict) self._log_to_csv(mode, accuracy,report_dict)
def _log_to_csv(self, mode, accuracy, classes, report_dict): def _log_to_csv(self, mode, accuracy, report_dict):
"""Handles directory structures, file setups, and distinct CSV column formatting.""" """Handles directory structures, file setups, and distinct CSV column formatting."""
arch_name = self.__class__.__name__.lower() arch_name = self.__class__.__name__.lower()
save_dir = Path("reports") save_dir = Path("reports")

View File

@@ -19,3 +19,4 @@ accuracy,macro_precision,macro_recall,macro_f1,weighted_precision,weighted_recal
0.9667,0.9750,0.9667,0.9657,0.9750,0.9667,0.9657 0.9667,0.9750,0.9667,0.9657,0.9750,0.9667,0.9657
0.9333,0.9500,0.9333,0.9314,0.9500,0.9333,0.9314 0.9333,0.9500,0.9333,0.9314,0.9500,0.9333,0.9314
0.9000,0.9350,0.9000,0.8957,0.9350,0.9000,0.8957 0.9000,0.9350,0.9000,0.8957,0.9350,0.9000,0.8957
0.9000,0.9350,0.9000,0.9007,0.9350,0.9000,0.9007
1 accuracy macro_precision macro_recall macro_f1 weighted_precision weighted_recall weighted_f1
19 0.9667 0.9750 0.9667 0.9657 0.9750 0.9667 0.9657
20 0.9333 0.9500 0.9333 0.9314 0.9500 0.9333 0.9314
21 0.9000 0.9350 0.9000 0.8957 0.9350 0.9000 0.8957
22 0.9000 0.9350 0.9000 0.9007 0.9350 0.9000 0.9007

View File

@@ -0,0 +1,77 @@
import torch
import numpy as np
from scipy.optimize import minimize
from .Strategy import Strategy
import torch.nn as nn
class CertifiedRemoval(Strategy):
"""Implements Certified Removal for machine unlearning."""
def __init__(self, model, data, labels, removal_bound, epsilon):
super().__init__()
self.model = model
self.data = data
self.labels = labels
self.removal_bound = removal_bound
self.epsilon = epsilon
def _run(self, model: nn.Module) -> nn.Module:
"""Runs the certified removal algorithm."""
# 1. Linear Model Creation
# This is a simplification for demonstration purposes. In a real implementation,
# you'd use more sophisticated methods to learn the parameters of the
# 'removal' model based on the example being removed.
def linear_model(x):
return torch.dot(x, torch.tensor([1, 1])) # Simplified Linear Model
# 2. Optimization for Parameter Adjustment
# Optimize the parameter values to minimize the loss while staying within bounds.
original_params = torch.tensor([0.0, 0.0]) # Initial parameters for linear model
def objective_function(params):
new_model = linear_model #use same function as defined above
return torch.sum(((new_model(self.data[0]) - self.labels)**2))
result = minimize(objective_function, original_params, method='L-BFGS-B', bounds=[(-self.removal_bound, self.removal_bound)], options={'maxiter': 100})
if not result.success:
print("Warning: Optimization failed!")
print(result.message)
return model #Return original if optimization fails
new_params = result.x
# 3. New Model Creation
new_model = lambda x: torch.dot(x, new_params)
return new_model
if __name__ == '__main__':
# Example Usage - Synthetic Data for Demonstration
np.random.seed(42) # For reproducibility
n_samples = 100
X = np.random.randn(n_samples, 2)
y = (X[:, 0] + X[:, 1] > 0).astype(int)
# Create a simple linear model for demonstration
model = nn.Linear(2, 1) # Simple linear classifier - PyTorch Version
optimizer = torch.optim.SGD(model.parameters(), lr=0.01) # Optimizer for training the linear model
# Train a Linear Model
for _ in range(100): #training loop
optimizer.zero_grad()
predictions = model(X)
loss = torch.sum((predictions - y)**2)
loss.backward()
optimizer.step()
# Define parameters for Certified Removal
removal_bound = 1.0
epsilon = 0.1
# Create the CertifiedRemoval object with the trained model, data and labels
certified_removal_obj = CertifiedRemoval(model, X, y, removal_bound, epsilon)
# Run Certified Removal
new_model = certified_removal_obj.apply(model)

View File

@@ -9,7 +9,7 @@ class Strategy:
def __init__(self): def __init__(self):
# Dynamically set file name based on the class name (e.g., 'NormalizingLinearFiltration.txt') # Dynamically set file name based on the class name (e.g., 'NormalizingLinearFiltration.txt')
self.strategy_name = self.__class__.__name__ self.strategy_name = self.__class__.__name__
self.log_file = f"reports/{self.strategy_name}_metrics.txt" self.log_file = f"reports/{self.strategy_name}/metrics.txt"
self._initialize_log_file() self._initialize_log_file()
def _initialize_log_file(self): def _initialize_log_file(self):