attck metrics

This commit is contained in:
2026-07-08 00:25:07 +02:00
parent 026ca47800
commit eb8060fc05
37 changed files with 1649 additions and 66 deletions

View File

@@ -43,17 +43,12 @@ class CertifiedUnlearning(Strategy):
InceptionV3 auxiliary layers and tracking gradients.
"""
inner_model = getattr(model, "model", model)
# Check if the current architecture is an Inception variant
is_inception = inner_model.__class__.__name__.lower() == "inception3"
params_list = []
for name, p in inner_model.named_parameters():
if p.requires_grad:
# Discard the disconnected auxiliary training branch weights
if is_inception and "AuxLogits" in name:
continue
# CRITICAL: Append as a tuple so it can be unpacked as (name, param)
# Append as a tuple so it can be unpacked as (name, param)
params_list.append((name, p))
return params_list if named else [e[1] for e in params_list]
@@ -92,7 +87,7 @@ class CertifiedUnlearning(Strategy):
first_grads = grad(loss, params, retain_graph=True, create_graph=True)
elemwise_products = sum(torch.sum(g_elem * v_elem) for g_elem, v_elem in zip(first_grads, v))
return grad(elemwise_products, params, create_graph=False)
def _stochastic_newton_update(self, g, dataset, model, device):
model.eval()
criterion = nn.CrossEntropyLoss()
@@ -133,7 +128,6 @@ class CertifiedUnlearning(Strategy):
h_s = self._hvp(loss, params, h_estimate)
# OPTIMIZATION 4: Avoid deprecated .data, use detach() and in-place ops
with torch.no_grad():
for k in range(len(params)):
h_estimate[k].copy_(h_estimate[k] + g[k] - (h_s[k] / self.scale))
@@ -143,7 +137,7 @@ class CertifiedUnlearning(Strategy):
if global_step % step_interval == 0 and current_pct < 100:
current_pct += 1
print(f"\rProgress: {current_pct}% done", end="", flush=True)
with torch.no_grad():
for k in range(len(params)):
h_res[k] += h_estimate[k] / self.scale

View File

@@ -13,8 +13,8 @@ class LinearFiltration(Strategy):
def _run(self, model: nn.Module, forget_loader: DataLoader, retain_loader: DataLoader) -> nn.Module:
model.eval()
# Freeze internal params
for param in model.parameters():
param.requires_grad = False
#for param in model.parameters():
#param.requires_grad = False
device = next(model.parameters()).device
@@ -155,7 +155,8 @@ class LinearFiltration(Strategy):
# 12
clf = self._get_classifier(model)
clf.weight.copy_(W_Z)
with torch.no_grad():
clf.weight.copy_(W_Z)
return model

View File

@@ -1,4 +1,5 @@
import time
import os
from pathlib import Path
import torch
import torch.nn as nn
@@ -21,27 +22,51 @@ class Retrain(Strategy):
self.epochs = epochs
def _run(self, model: nn.Module, forget_loader: DataLoader, retain_loader: DataLoader) -> nn.Module:
# 1. Determine the active execution device from the running sandbox
device = next(model.parameters()).device
# we need to check if a retrained copy exists on disk
checkpoint_path = f"trained_models/class_{self.target_class_index}_retrained.pth"
if os.path.exists(checkpoint_path):
print(f"Found existing retrained model checkpoint at '{checkpoint_path}'. Loading parameters directly...")
# Load the state dict using safe configuration flags
state_dict = torch.load(checkpoint_path, map_location=device, weights_only=True)
# Safely apply the parameter weights to the model in-place
model.load_state_dict(state_dict)
print("Retrained parameter loading complete (Retraining bypassed).")
return model
# Cache Miss: Execute the standard retraining pipeline
print(f"No naive model found for class {self.target_class_index} retraining a new one")
print(f">> Triggering Exact Unlearning Baseline (Retraining {self.arch.name} from pristine state)...")
inner_model = getattr(model, "model", model)
if hasattr(inner_model, "fc"):
total_classes = inner_model.fc.out_features
elif hasattr(inner_model, "classifier"):
# Fallback for alternative architecture layout types
total_classes = inner_model.classifier[-1].out_features
else:
total_classes = self.size
# a new model with default params is created
fresh_meat = Model.create(self.arch, device, self.size)
fresh = Model.create(self.arch, device, total_classes)
# we train it with retain set
fresh_meat.train(
fresh.train(
epochs=self.epochs,
loader=retain_loader,
rate=self.lr,
mode="retrain"
)
# 4. Extract the trained nn.Module parameter state dict
# In-place copy onto the existing sandbox model structure to seamlessly retain downstream evaluations
model.load_state_dict(fresh_meat.model.state_dict())
# Extract module parameter state dict and copy in place
model.load_state_dict(fresh.model.state_dict())
print(">> Retraining pipeline finished. Pristine baseline weights successfully established.")
print("Retraining pipeline complete")
return model
def _split_data(self, dataset):
@@ -49,5 +74,5 @@ class Retrain(Strategy):
return get_unlearning_loaders(
dataset=dataset,
forget_class_idx=self.target_class_index,
batch_size=32
batch_size=16
)

View File

@@ -40,10 +40,12 @@ class Strategy:
execution_time = end_time - start_time
# Log to the strategy's specific file
'''
Util.log_metric(
log_file=log_file,
execution_time=execution_time
)
'''
print(f"[{self.strategy_name}] Completed in {execution_time:.6f} seconds. Saved to {log_file}")

View File

@@ -114,7 +114,7 @@ class WeightFiltration(Strategy):
model.eval()
if self.wf_model is None:
print(">> Initializing and compiling global WF-Net matrix (Run Once for all classes)...")
print("Initializing and compiling global WF-Net matrix (Run Once for all classes)...")
self.wf_model = self._optimise_filter(
model,
@@ -123,10 +123,10 @@ class WeightFiltration(Strategy):
device=device
)
else:
print(f">> Gating matrix loaded. Switching layout to target class index: {self.target_class_index}")
print(f"Gating matrix loaded. Switching layout to target class index: {self.target_class_index}")
self.wf_model.target_class_index = self.target_class_index
return self.wf_model
return self.wf_model.get()
def _split_data(self, dataset):
return vertical_split(