added material

This commit is contained in:
Jeremy Janella
2026-07-26 22:53:03 -04:00
commit 95b1c6bf27
369 changed files with 3468754 additions and 0 deletions
+127
View File
@@ -0,0 +1,127 @@
#!/usr/bin/env python3
import os
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
from tqdm import tqdm
from opacus import PrivacyEngine
from opacus.validators import ModuleValidator
# =============================================================================
# STABLE CONFIGURATION
# =============================================================================
BATCH_SIZE = 64 # Smaller batch = more stable memory on CPU
DP_EPOCHS = 10 # Fewer epochs to start; check stability first
DP_LR = 5e-4 # Lower LR to prevent 'nan'
MAX_GRAD_NORM = 1.0
DELTA = 1e-5
TARGET_EPSILON = 6.0
DEVICE = torch.device('cpu')
# =============================================================================
# MODEL (MINIMAL & STABLE)
# =============================================================================
class SimpleSVHN(nn.Module):
def __init__(self):
super().__init__()
# We use GroupNorm with small groups for maximum stability
self.conv1 = nn.Conv2d(3, 16, 3, padding=1)
self.gn1 = nn.GroupNorm(2, 16)
self.conv2 = nn.Conv2d(16, 32, 3, padding=1)
self.gn2 = nn.GroupNorm(4, 32)
self.pool = nn.MaxPool2d(2, 2)
self.fc = nn.Linear(32 * 8 * 8, 10)
def forward(self, x):
x = self.pool(F.relu(self.gn1(self.conv1(x)))) # 16x16
x = self.pool(F.relu(self.gn2(self.conv2(x)))) # 8x8
x = torch.flatten(x, 1)
return self.fc(x)
# =============================================================================
# TRAINING SCRIPT
# =============================================================================
def run_stable_dp_training():
# 1. Data Loading
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.4377, 0.4438, 0.4728), (0.1980, 0.2010, 0.1970))
])
train_ds = datasets.SVHN("data", split='train', download=True, transform=transform)
test_ds = datasets.SVHN("data", split='test', download=True, transform=transform)
train_loader = DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True)
test_loader = DataLoader(test_ds, batch_size=BATCH_SIZE, shuffle=False)
# 2. Model Setup
model = SimpleSVHN().to(DEVICE)
model = ModuleValidator.fix(model)
optimizer = optim.Adam(model.parameters(), lr=DP_LR)
# 3. Privacy Engine (THE STABILITY FIX)
# Using grad_sample_mode="hooks" prevents the C++ level SegFaults
privacy_engine = PrivacyEngine()
model, optimizer, train_loader = privacy_engine.make_private_with_epsilon(
module=model,
optimizer=optimizer,
data_loader=train_loader,
target_epsilon=TARGET_EPSILON,
target_delta=DELTA,
epochs=DP_EPOCHS,
max_grad_norm=MAX_GRAD_NORM,
grad_sample_mode="hooks",
)
criterion = nn.CrossEntropyLoss()
print(f"Starting Training on {DEVICE}...")
for epoch in range(DP_EPOCHS):
model.train()
total_loss = 0
pbar = tqdm(train_loader, desc=f"Epoch {epoch+1}")
for images, labels in pbar:
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
if torch.isnan(loss):
print("\n[!] Warning: NaN detected. Skipping batch.")
continue
loss.backward()
optimizer.step()
total_loss += loss.item()
pbar.set_postfix({'loss': f'{loss.item():.2f}'})
epsilon = privacy_engine.get_epsilon(DELTA)
print(f"Epoch {epoch+1} complete. Current ε: {epsilon:.2f}")
# 4. Final Evaluation
model.eval()
correct = 0
total = 0
with torch.no_grad():
for images, labels in test_loader:
images, labels = images.to(DEVICE), labels.to(DEVICE)
outputs = model(images)
_, predicted = outputs.max(1)
total += labels.size(0)
correct += predicted.eq(labels).sum().item()
print(f"\nFinal Test Accuracy: {100.*correct/total:.2f}%")
print(f"Final Epsilon: {epsilon:.2f}")
if __name__ == "__main__":
# Force single-thread for math if your CPU is still fighting the memory allocation
torch.set_num_threads(1)
run_stable_dp_training()
+134
View File
@@ -0,0 +1,134 @@
#!/usr/bin/env python3
import os
import requests
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
from tqdm import tqdm
from opacus import PrivacyEngine
from opacus.validators import ModuleValidator
from safetensors.torch import save_file
# =============================================================================
# CONFIGURATION
# =============================================================================
BATCH_SIZE = 64
DP_EPOCHS = 20 # Increased to reach 55% accuracy target
DP_LR = 1e-3 # Adam LR
MAX_GRAD_NORM = 1.0
DELTA = 1e-5
TARGET_EPSILON = 6.0
DEVICE = torch.device('cpu')
MODELS_DIR = "models"
os.makedirs(MODELS_DIR, exist_ok=True)
# =============================================================================
# STABLE MODEL ARCHITECTURE
# =============================================================================
class ChallengeSVHN(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(3, 32, 3, padding=1)
self.gn1 = nn.GroupNorm(4, 32)
self.conv2 = nn.Conv2d(32, 64, 3, padding=1)
self.gn2 = nn.GroupNorm(8, 64)
self.pool = nn.MaxPool2d(2, 2)
self.fc1 = nn.Linear(64 * 8 * 8, 64)
self.fc2 = nn.Linear(64, 10)
def forward(self, x):
x = self.pool(F.relu(self.gn1(self.conv1(x))))
x = self.pool(F.relu(self.gn2(self.conv2(x))))
x = torch.flatten(x, 1)
x = F.relu(self.fc1(x))
return self.fc2(x)
# =============================================================================
# MAIN PROCESS
# =============================================================================
def run_challenge():
# 1. Data Setup
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.4377, 0.4438, 0.4728), (0.1980, 0.2010, 0.1970))
])
train_ds = datasets.SVHN("data", split='train', download=True, transform=transform)
test_ds = datasets.SVHN("data", split='test', download=True, transform=transform)
train_loader = DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True)
test_loader = DataLoader(test_ds, batch_size=BATCH_SIZE, shuffle=False)
# 2. Model & Optimizer
model = ChallengeSVHN().to(DEVICE)
model = ModuleValidator.fix(model)
optimizer = optim.Adam(model.parameters(), lr=DP_LR)
# 3. Privacy Engine (Stability Mode)
privacy_engine = PrivacyEngine()
model, optimizer, train_loader_dp = privacy_engine.make_private_with_epsilon(
module=model,
optimizer=optimizer,
data_loader=train_loader,
target_epsilon=TARGET_EPSILON,
target_delta=DELTA,
epochs=DP_EPOCHS,
max_grad_norm=MAX_GRAD_NORM,
grad_sample_mode="hooks", # CRITICAL: Prevents AMD CPU Segfault
)
criterion = nn.CrossEntropyLoss()
# 4. Training Loop
print(f"Starting Training for {DP_EPOCHS} epochs...")
for epoch in range(DP_EPOCHS):
model.train()
pbar = tqdm(train_loader_dp, desc=f"Epoch {epoch+1}")
for images, labels in pbar:
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
pbar.set_postfix({'eps': f'{privacy_engine.get_epsilon(DELTA):.2f}'})
# 5. Final Evaluation
model.eval()
correct, total = 0, 0
with torch.no_grad():
for images, labels in test_loader:
outputs = model(images.to(DEVICE))
_, predicted = outputs.max(1)
total += labels.size(0)
correct += predicted.eq(labels.to(DEVICE)).sum().item()
acc = 100.0 * correct / total
eps = privacy_engine.get_epsilon(DELTA)
print(f"\nTraining Complete. Final Accuracy: {acc:.2f}% | Final Epsilon: {eps:.2f}")
# 6. Save Model
# We save model._module because 'model' is wrapped by Opacus
model_path = os.path.join(MODELS_DIR, "dp_model.safetensors")
save_file(model._module.state_dict(), model_path)
print(f"Model saved to {model_path}")
# 7. Submit to Server
if acc >= 55.0:
print("Accuracy requirement met. Submitting for flag...")
with open(model_path, "rb") as f:
response = requests.post(
"http://154.57.164.77:32346/validate",
files={"model": ("dp_model.safetensors", f, "application/octet-stream")}
)
print("\n--- SERVER RESPONSE ---")
print(response.text)
print("-----------------------")
else:
print("Accuracy was below 55%. Try running again or adjusting DP_LR.")
if __name__ == "__main__":
# Prevent memory collision on CPU
torch.set_num_threads(1)
run_challenge()
+307
View File
@@ -0,0 +1,307 @@
#!/usr/bin/env python3
import os
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
from safetensors.torch import save_file
from tqdm import tqdm
# =============================================================================
# CONFIGURATION
# =============================================================================
RANDOM_SEED = 1337
BATCH_SIZE = 256
DP_EPOCHS = 10
DP_LR = 0.1
MAX_GRAD_NORM = 1.0
DELTA = 1e-5
# The key parameter: use ε=6 to balance accuracy and privacy
TARGET_EPSILON = 6.0
# Set reproducibility
torch.manual_seed(RANDOM_SEED)
np.random.seed(RANDOM_SEED)
if torch.cuda.is_available():
torch.cuda.manual_seed_all(RANDOM_SEED)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# Output directories
MODELS_DIR = "models"
os.makedirs(MODELS_DIR, exist_ok=True)
# SVHN normalization values
SVHN_MEAN = (0.4377, 0.4438, 0.4728)
SVHN_STD = (0.1980, 0.2010, 0.1970)
# =============================================================================
# MODEL ARCHITECTURE
# =============================================================================
class SVHNCNN(nn.Module):
"""CNN for SVHN classification (compatible with Opacus)."""
def __init__(self):
super(SVHNCNN, self).__init__()
self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)
self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
self.conv3 = nn.Conv2d(64, 64, kernel_size=3, padding=1)
self.pool = nn.MaxPool2d(2, 2)
self.fc1 = nn.Linear(64 * 4 * 4, 64)
self.fc2 = nn.Linear(64, 10)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = self.pool(F.relu(self.conv3(x)))
x = x.view(-1, 64 * 4 * 4)
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
# =============================================================================
# DATA LOADING
# =============================================================================
def get_svhn_loaders(batch_size=256, download=True):
"""Load SVHN dataset."""
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(SVHN_MEAN, SVHN_STD)
])
train_dataset = datasets.SVHN(
"data", split='train', download=download, transform=transform
)
test_dataset = datasets.SVHN(
"data", split='test', download=download, transform=transform
)
train_loader = DataLoader(
train_dataset, batch_size=batch_size, shuffle=True, num_workers=0
)
test_loader = DataLoader(
test_dataset, batch_size=batch_size, shuffle=False, num_workers=0
)
return train_dataset, test_dataset, train_loader, test_loader
def evaluate_accuracy(model, data_loader, device):
"""Evaluate model accuracy."""
model.eval()
correct = 0
total = 0
with torch.no_grad():
for images, labels in data_loader:
images, labels = images.to(device), labels.to(device)
outputs = model(images)
_, predicted = outputs.max(1)
total += labels.size(0)
correct += predicted.eq(labels).sum().item()
return 100.0 * correct / total
def compute_mia_advantage(model, train_loader, test_loader, device, num_samples=2000):
"""Compute MIA advantage using confidence threshold attack."""
model.eval()
def get_confidences(loader, max_samples):
all_conf = []
count = 0
with torch.no_grad():
for images, _ in loader:
if count >= max_samples:
break
images = images.to(device)
outputs = model(images)
probs = F.softmax(outputs, dim=1)
conf = probs.max(dim=1).values.cpu().numpy()
all_conf.extend(conf)
count += len(conf)
return np.array(all_conf[:max_samples])
member_conf = get_confidences(train_loader, num_samples)
nonmember_conf = get_confidences(test_loader, num_samples)
n = min(len(member_conf), len(nonmember_conf))
all_conf = np.concatenate([member_conf[:n], nonmember_conf[:n]])
all_labels = np.concatenate([np.ones(n), np.zeros(n)])
thresholds = np.percentile(all_conf, np.linspace(0, 100, 500))
best_acc = 0.5
for t in thresholds:
acc = max(
np.mean((all_conf >= t) == all_labels),
np.mean((all_conf < t) == all_labels)
)
best_acc = max(best_acc, acc)
return best_acc, best_acc - 0.5
# =============================================================================
# TRAINING
# =============================================================================
print("=" * 80)
print(" DP-SGD PRIVACY (SVHN)")
print("=" * 80)
print(f"\nDevice: {device}")
print(f"Target epsilon: {TARGET_EPSILON}")
print(f"Required: accuracy >= 55%, MIA advantage <= 5%")
print("\nLoading SVHN dataset...")
train_dataset, test_dataset, train_loader, test_loader = get_svhn_loaders(
batch_size=BATCH_SIZE, download=True
)
print(f"Training samples: {len(train_dataset):,}")
print(f"Test samples: {len(test_dataset):,}")
# Import Opacus
from opacus import PrivacyEngine
from opacus.validators import ModuleValidator
print("\n" + "=" * 80)
print(f" TRAINING: DP-SGD MODEL (Target ε={TARGET_EPSILON})")
print("=" * 80)
# Fresh data loaders for DP training
_, _, train_loader_dp, test_loader_dp = get_svhn_loaders(
batch_size=BATCH_SIZE, download=False
)
# Initialize model
dp_model = SVHNCNN().to(device)
dp_model = ModuleValidator.fix(dp_model)
optimizer_dp = optim.Adam(dp_model.parameters(), lr=1e-3)
#optimizer_dp = optim.SGD(dp_model.parameters(), lr=DP_LR, momentum=0.9)
# Attach privacy engine
privacy_engine = PrivacyEngine(accountant="rdp")
dp_model, optimizer_dp, train_loader_dp = privacy_engine.make_private_with_epsilon(
module=dp_model,
optimizer=optimizer_dp,
data_loader=train_loader_dp,
target_epsilon=TARGET_EPSILON,
target_delta=DELTA,
epochs=DP_EPOCHS,
max_grad_norm=MAX_GRAD_NORM,
)
print(f"\nConfiguration:")
print(f" Target epsilon: {TARGET_EPSILON}")
print(f" Delta: {DELTA}")
print(f" Max gradient norm: {MAX_GRAD_NORM}")
print(f" Epochs: {DP_EPOCHS}")
# Training loop
criterion = nn.CrossEntropyLoss()
dp_model.train()
for epoch in range(DP_EPOCHS):
running_loss = 0.0
correct = 0
total = 0
pbar = tqdm(train_loader_dp, desc=f"Epoch {epoch+1}/{DP_EPOCHS}")
for images, labels in pbar:
images, labels = images.to(device), labels.to(device)
optimizer_dp.zero_grad()
outputs = dp_model(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer_dp.step()
running_loss += loss.item()
_, predicted = outputs.max(1)
total += labels.size(0)
correct += predicted.eq(labels).sum().item()
pbar.set_postfix({
'loss': f'{running_loss/total:.4f}',
'acc': f'{100.*correct/total:.2f}%'
})
# Get current epsilon
epsilon = privacy_engine.get_epsilon(DELTA)
print(f" Epoch {epoch+1}: ε = {epsilon:.2f}")
final_epsilon = privacy_engine.get_epsilon(DELTA)
print(f"\nFinal privacy guarantee: (ε={final_epsilon:.2f}, δ={DELTA})")
# Evaluate
train_acc = evaluate_accuracy(dp_model, train_loader, device)
#train_acc = evaluate_accuracy(dp_model, train_loader_dp, device)
test_acc = evaluate_accuracy(dp_model, test_loader_dp, device)
print(f"\nDP Model Performance:")
print(f" Training accuracy: {train_acc:.2f}%")
print(f" Test accuracy: {test_acc:.2f}%")
print(f" Overfitting gap: {train_acc - test_acc:.2f}%")
# MIA evaluation
print("\n" + "=" * 80)
print(" MEMBERSHIP INFERENCE EVALUATION")
print("=" * 80)
mia_acc, mia_adv = compute_mia_advantage(
dp_model, train_loader_dp, test_loader_dp, device
)
print(f"\nMIA Results:")
print(f" Attack accuracy: {mia_acc:.4f}")
print(f" Attack advantage: {mia_adv:.4f} ({mia_adv*100:.2f}%)")
# Save model
model_path = os.path.join(MODELS_DIR, "dp_model.safetensors")
save_file(dp_model._module.state_dict(), model_path)
print(f"\nModel saved to {model_path}")
# =============================================================================
# VALIDATION CHECK
# =============================================================================
print("\n" + "=" * 80)
print(" VALIDATION CHECK")
print("=" * 80)
accuracy_pass = test_acc >= 55.0
mia_pass = mia_adv <= 0.05
print(f"\nChallenge Requirements:")
print(f" Accuracy >= 55%: {test_acc:.2f}% {'✓ PASS' if accuracy_pass else '✗ FAIL'}")
print(f" MIA advantage <= 5%: {mia_adv*100:.2f}% {'✓ PASS' if mia_pass else '✗ FAIL'}")
if accuracy_pass and mia_pass:
print("\n" + "=" * 80)
print(" SOLUTION READY FOR SUBMISSION")
print("=" * 80)
print(f"\nSubmit {model_path} to the validator to get the flag!")
else:
print("\n" + "=" * 80)
print(" SOLUTION NOT YET PASSING")
print("=" * 80)
print("\nConsider adjusting epsilon or other hyperparameters.")
import requests
with open("./models/dp_model.safetensors", "rb") as f:
response = requests.post(
"http://154.57.164.72:32303/validate",
files={"model": ("dp_model.safetensors", f, "application/octet-stream")}
)
print(response.text)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.
@@ -0,0 +1 @@
<meta HTTP-EQUIV="REFRESH" content="0; url=http://www.cs.toronto.edu/~kriz/cifar.html">
Binary file not shown.
Binary file not shown.
+276
View File
@@ -0,0 +1,276 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "15680588-0a0e-4b6d-87db-0fdaa37f03c4",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [],
"source": [
"import os\n",
"import json\n",
"import torch\n",
"import torch.optim as optim\n",
"from safetensors.torch import save_file\n",
"\n",
"from htb_ai_library import (\n",
" set_reproducibility, use_htb_style,\n",
" CIFAR10CNN,\n",
" get_cifar10_loaders,\n",
" train_baseline_sgd,\n",
" train_dp_sgd,\n",
" evaluate_accuracy,\n",
" compute_mia_advantage,\n",
" plot_accuracy_comparison,\n",
" plot_privacy_utility_tradeoff,\n",
")\n",
"RANDOM_SEED = 1337\n",
"BATCH_SIZE = 256\n",
"BASELINE_EPOCHS = 20\n",
"BASELINE_LR = 0.1\n",
"DP_EPOCHS = 20\n",
"DP_LR = 0.1\n",
"MAX_GRAD_NORM = 1.0\n",
"DELTA = 1e-5\n",
"\n",
"set_reproducibility(RANDOM_SEED)\n",
"use_htb_style()\n",
"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
"\n",
"os.makedirs(\"figs\", exist_ok=True)\n",
"os.makedirs(\"output\", exist_ok=True)\n",
"os.makedirs(\"models\", exist_ok=True)"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "61bc8506-b6e5-401e-9d8b-def2eaa5ff94",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"================================================================================\n",
" DP-SGD PRIVACY MITIGATION DEMONSTRATION\n",
"================================================================================\n",
"\n",
"Device: cpu\n",
"Random seed: 1337\n",
"\n",
"Loading CIFAR-10 dataset...\n",
"Files already downloaded and verified\n",
"Files already downloaded and verified\n",
"Training samples: 50,000\n",
"Test samples: 10,000\n",
"Batch size: 256\n",
"\n",
"================================================================================\n",
" TRAINING: BASELINE MODEL (No Privacy Protection)\n",
"================================================================================\n",
"Epoch [1/20] Loss: nan | Acc: 37.60%\n",
"Epoch [2/20] Loss: nan | Acc: 10.00%\n"
]
},
{
"ename": "KeyboardInterrupt",
"evalue": "",
"output_type": "error",
"traceback": [
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
"\u001b[31mKeyboardInterrupt\u001b[39m Traceback (most recent call last)",
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 19\u001b[39m\n\u001b[32m 16\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33m=\u001b[39m\u001b[33m\"\u001b[39m * \u001b[32m80\u001b[39m)\n\u001b[32m 18\u001b[39m baseline_model = CIFAR10CNN().to(device)\n\u001b[32m---> \u001b[39m\u001b[32m19\u001b[39m baseline_model = \u001b[43mtrain_baseline_sgd\u001b[49m\u001b[43m(\u001b[49m\u001b[43mbaseline_model\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtrain_loader\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdevice\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mepochs\u001b[49m\u001b[43m=\u001b[49m\u001b[43mBASELINE_EPOCHS\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mlearning_rate\u001b[49m\u001b[43m=\u001b[49m\u001b[43mBASELINE_LR\u001b[49m\u001b[43m)\u001b[49m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/.conda/envs/ai/lib/python3.11/site-packages/htb_ai_library/training/loops.py:244\u001b[39m, in \u001b[36mtrain_baseline_sgd\u001b[39m\u001b[34m(model, train_loader, device, epochs, learning_rate, momentum)\u001b[39m\n\u001b[32m 242\u001b[39m output = model(data)\n\u001b[32m 243\u001b[39m loss = criterion(output, target)\n\u001b[32m--> \u001b[39m\u001b[32m244\u001b[39m \u001b[43mloss\u001b[49m\u001b[43m.\u001b[49m\u001b[43mbackward\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 245\u001b[39m optimizer.step()\n\u001b[32m 246\u001b[39m running_loss += loss.item()\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/.conda/envs/ai/lib/python3.11/site-packages/torch/_tensor.py:581\u001b[39m, in \u001b[36mTensor.backward\u001b[39m\u001b[34m(self, gradient, retain_graph, create_graph, inputs)\u001b[39m\n\u001b[32m 571\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m has_torch_function_unary(\u001b[38;5;28mself\u001b[39m):\n\u001b[32m 572\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m handle_torch_function(\n\u001b[32m 573\u001b[39m Tensor.backward,\n\u001b[32m 574\u001b[39m (\u001b[38;5;28mself\u001b[39m,),\n\u001b[32m (...)\u001b[39m\u001b[32m 579\u001b[39m inputs=inputs,\n\u001b[32m 580\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m581\u001b[39m \u001b[43mtorch\u001b[49m\u001b[43m.\u001b[49m\u001b[43mautograd\u001b[49m\u001b[43m.\u001b[49m\u001b[43mbackward\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 582\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mgradient\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mretain_graph\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcreate_graph\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minputs\u001b[49m\u001b[43m=\u001b[49m\u001b[43minputs\u001b[49m\n\u001b[32m 583\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/.conda/envs/ai/lib/python3.11/site-packages/torch/autograd/__init__.py:347\u001b[39m, in \u001b[36mbackward\u001b[39m\u001b[34m(tensors, grad_tensors, retain_graph, create_graph, grad_variables, inputs)\u001b[39m\n\u001b[32m 342\u001b[39m retain_graph = create_graph\n\u001b[32m 344\u001b[39m \u001b[38;5;66;03m# The reason we repeat the same comment below is that\u001b[39;00m\n\u001b[32m 345\u001b[39m \u001b[38;5;66;03m# some Python versions print out the first line of a multi-line function\u001b[39;00m\n\u001b[32m 346\u001b[39m \u001b[38;5;66;03m# calls in the traceback and some print out the last line\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m347\u001b[39m \u001b[43m_engine_run_backward\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 348\u001b[39m \u001b[43m \u001b[49m\u001b[43mtensors\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 349\u001b[39m \u001b[43m \u001b[49m\u001b[43mgrad_tensors_\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 350\u001b[39m \u001b[43m \u001b[49m\u001b[43mretain_graph\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 351\u001b[39m \u001b[43m \u001b[49m\u001b[43mcreate_graph\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 352\u001b[39m \u001b[43m \u001b[49m\u001b[43minputs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 353\u001b[39m \u001b[43m \u001b[49m\u001b[43mallow_unreachable\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[32m 354\u001b[39m \u001b[43m \u001b[49m\u001b[43maccumulate_grad\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[32m 355\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/.conda/envs/ai/lib/python3.11/site-packages/torch/autograd/graph.py:825\u001b[39m, in \u001b[36m_engine_run_backward\u001b[39m\u001b[34m(t_outputs, *args, **kwargs)\u001b[39m\n\u001b[32m 823\u001b[39m unregister_hooks = _register_logging_hooks_on_whole_graph(t_outputs)\n\u001b[32m 824\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m825\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mVariable\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_execution_engine\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun_backward\u001b[49m\u001b[43m(\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# Calls into the C++ engine to run the backward pass\u001b[39;49;00m\n\u001b[32m 826\u001b[39m \u001b[43m \u001b[49m\u001b[43mt_outputs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\n\u001b[32m 827\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# Calls into the C++ engine to run the backward pass\u001b[39;00m\n\u001b[32m 828\u001b[39m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[32m 829\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m attach_logging_hooks:\n",
"\u001b[31mKeyboardInterrupt\u001b[39m: "
]
}
],
"source": [
"print(\"=\" * 80)\n",
"print(\" DP-SGD PRIVACY MITIGATION DEMONSTRATION\")\n",
"print(\"=\" * 80)\n",
"print(f\"\\nDevice: {device}\")\n",
"print(f\"Random seed: {RANDOM_SEED}\")\n",
"\n",
"print(\"\\nLoading CIFAR-10 dataset...\")\n",
"train_dataset, test_dataset, train_loader, test_loader = get_cifar10_loaders(batch_size=BATCH_SIZE, download=True)\n",
"\n",
"print(f\"Training samples: {len(train_dataset):,}\")\n",
"print(f\"Test samples: {len(test_dataset):,}\")\n",
"print(f\"Batch size: {BATCH_SIZE}\")\n",
"\n",
"print(\"\\n\" + \"=\" * 80)\n",
"print(\" TRAINING: BASELINE MODEL (No Privacy Protection)\")\n",
"print(\"=\" * 80)\n",
"\n",
"baseline_model = CIFAR10CNN().to(device)\n",
"baseline_model = train_baseline_sgd(baseline_model, train_loader, device, epochs=BASELINE_EPOCHS, learning_rate=BASELINE_LR)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "5a235f8e-0bef-495b-8c98-5ec97dbe545c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Baseline Model Performance:\n",
" Training accuracy: 10.00%\n",
" Test accuracy: 10.00%\n",
" Overfitting gap: 0.00%\n",
"\n",
"Saved baseline model to output/baseline_model.pth\n",
"\n",
"================================================================================\n",
" MEMBERSHIP INFERENCE MEASUREMENT: Baseline Model\n",
"================================================================================\n",
"\n",
"MIA Results (Baseline):\n",
" Attack accuracy: 0.5000\n",
" Attack advantage: 0.0000\n",
" Random baseline: 0.5000\n"
]
}
],
"source": [
"train_acc_baseline = evaluate_accuracy(baseline_model, train_loader, device)\n",
"test_acc_baseline = evaluate_accuracy(baseline_model, test_loader, device)\n",
"\n",
"print(\"\\nBaseline Model Performance:\")\n",
"print(f\" Training accuracy: {train_acc_baseline:.2f}%\")\n",
"print(f\" Test accuracy: {test_acc_baseline:.2f}%\")\n",
"print(f\" Overfitting gap: {train_acc_baseline - test_acc_baseline:.2f}%\")\n",
"\n",
"torch.save(baseline_model.state_dict(), \"output/baseline_model.pth\")\n",
"print(\"\\nSaved baseline model to output/baseline_model.pth\")\n",
"\n",
"print(\"\\n\" + \"=\" * 80)\n",
"print(\" MEMBERSHIP INFERENCE MEASUREMENT: Baseline Model\")\n",
"print(\"=\" * 80)\n",
"\n",
"mia_acc_baseline, mia_adv_baseline = compute_mia_advantage(\n",
" baseline_model, train_loader, test_loader, device\n",
")\n",
"\n",
"print(\"\\nMIA Results (Baseline):\")\n",
"print(f\" Attack accuracy: {mia_acc_baseline:.4f}\")\n",
"print(f\" Attack advantage: {mia_adv_baseline:.4f}\")\n",
"print(\" Random baseline: 0.5000\")"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "589e1168-8c86-405b-bbbf-5fbcdce4851a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"================================================================================\n",
" TRAINING: DP-SGD MODEL (Target ε=10)\n",
"================================================================================\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/jeremy/.conda/envs/ai/lib/python3.11/site-packages/opacus/privacy_engine.py:96: UserWarning: Secure RNG turned off. This is perfectly fine for experimentation as it allows for much faster training performance, but remember to turn it on and retrain one last time before production with ``secure_mode`` turned on.\n",
" warnings.warn(\n",
"/home/jeremy/.conda/envs/ai/lib/python3.11/site-packages/opacus/accountants/analysis/rdp.py:332: UserWarning: Optimal order is the largest alpha. Please consider expanding the range of alphas to get a tighter privacy bound.\n",
" warnings.warn(\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Configuration:\n",
" Target epsilon: 10.0\n",
" Delta: 1e-05\n",
" Max gradient norm: 1.0\n"
]
}
],
"source": [
"from opacus import PrivacyEngine\n",
"from opacus.validators import ModuleValidator\n",
"\n",
"print(\"\\n\" + \"=\" * 80)\n",
"print(\" TRAINING: DP-SGD MODEL (Target ε=10)\")\n",
"print(\"=\" * 80)\n",
"\n",
"TARGET_EPSILON_10 = 10.0\n",
"\n",
"_, _, train_loader_dp, test_loader_dp = get_cifar10_loaders(batch_size=BATCH_SIZE, download=False)\n",
"\n",
"dp_model_10 = CIFAR10CNN().to(device)\n",
"dp_model_10 = ModuleValidator.fix(dp_model_10)\n",
"optimizer_dp = optim.SGD(dp_model_10.parameters(), lr=DP_LR, momentum=0.9)\n",
"\n",
"privacy_engine = PrivacyEngine(accountant=\"rdp\")\n",
"dp_model_10, optimizer_dp, train_loader_dp = privacy_engine.make_private_with_epsilon(\n",
" module=dp_model_10,\n",
" optimizer=optimizer_dp,\n",
" data_loader=train_loader_dp,\n",
" target_epsilon=TARGET_EPSILON_10,\n",
" target_delta=DELTA,\n",
" epochs=DP_EPOCHS,\n",
" max_grad_norm=MAX_GRAD_NORM,\n",
")\n",
"\n",
"print(f\"\\nConfiguration:\")\n",
"print(f\" Target epsilon: {TARGET_EPSILON_10}\")\n",
"print(f\" Delta: {DELTA}\")\n",
"print(f\" Max gradient norm: {MAX_GRAD_NORM}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "23b1fc5c-ce8f-4b2c-a24d-02c11b69ede2",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+500
View File
@@ -0,0 +1,500 @@
name: ai
channels:
- conda-forge
- nvidia
- pytorch
- defaults
dependencies:
- _libgcc_mutex=0.1
- _openmp_mutex=4.5
- accelerate=1.12.0
- aiofiles=24.1.0
- aiohappyeyeballs=2.6.1
- aiohttp=3.13.3
- aiosignal=1.4.0
- alsa-lib=1.2.8
- annotated-doc=0.0.4
- annotated-types=0.7.0
- anyio=4.12.1
- argon2-cffi=25.1.0
- argon2-cffi-bindings=25.1.0
- arrow=1.4.0
- arrow-cpp=11.0.0
- asttokens=3.0.1
- async-lru=2.1.0
- attr=2.5.2
- attrs=25.4.0
- authlib=1.6.9
- aws-c-auth=0.6.26
- aws-c-cal=0.5.21
- aws-c-common=0.8.14
- aws-c-compression=0.2.16
- aws-c-event-stream=0.2.20
- aws-c-http=0.7.6
- aws-c-io=0.13.19
- aws-c-mqtt=0.8.6
- aws-c-s3=0.2.7
- aws-c-sdkutils=0.1.9
- aws-checksums=0.1.14
- aws-crt-cpp=0.19.8
- aws-sdk-cpp=1.10.57
- babel=2.17.0
- beautifulsoup4=4.14.3
- binaryornot=0.4.4
- blas=1.0
- bleach=6.3.0
- bleach-with-css=6.3.0
- brotli=1.0.9
- brotli-bin=1.0.9
- brotli-python=1.0.9
- bzip2=1.0.8
- c-ares=1.34.6
- ca-certificates=2026.2.25
- cached-property=1.5.2
- cached_property=1.5.2
- cairo=1.16.0
- category_encoders=2.9.0
- certifi=2026.2.25
- cffi=2.0.0
- chardet=5.2.0
- charset-normalizer=3.4.4
- click=8.3.1
- comm=0.2.3
- contourpy=1.3.3
- cookiecutter=2.6.0
- cpython=3.11.14
- cuda-cudart=12.4.127
- cuda-cudart_linux-64=12.4.127
- cuda-cupti=12.4.127
- cuda-libraries=12.4.1
- cuda-nvrtc=12.4.127
- cuda-nvtx=12.4.127
- cuda-opencl=12.4.127
- cuda-runtime=12.4.1
- cuda-version=12.4
- cycler=0.12.1
- dataclasses=0.8
- datasets=2.16.1
- dbus=1.13.6
- debugpy=1.8.20
- decorator=5.2.1
- defusedxml=0.7.1
- dill=0.3.7
- dnspython=2.6.1
- email-validator=2.3.0
- email_validator=2.3.0
- evaluate=0.4.6
- exceptiongroup=1.3.1
- executing=2.2.1
- expat=2.7.3
- fastapi=0.128.0
- fastapi-cli=0.0.20
- fastapi-core=0.128.0
- fastmcp=2.10.2
- ffmpeg=4.3
- ffmpy=0.3.0
- fftw=3.3.10
- filelock=3.20.3
- font-ttf-dejavu-sans-mono=2.37
- font-ttf-inconsolata=3.000
- font-ttf-source-code-pro=2.038
- font-ttf-ubuntu=0.83
- fontconfig=2.14.2
- fonts-conda-ecosystem=1
- fonts-conda-forge=1
- fonttools=4.61.1
- fqdn=1.5.1
- freetype=2.12.1
- frozenlist=1.7.0
- fsspec=2023.10.0
- gettext=0.25.1
- gettext-tools=0.25.1
- gflags=2.2.2
- giflib=5.2.2
- glib=2.80.2
- glib-tools=2.80.2
- glog=0.6.0
- gmp=6.3.0
- gmpy2=2.2.1
- gnutls=3.6.13
- gradio=6.5.1
- gradio-client=2.0.3
- graphite2=1.3.14
- groovy-python=0.1.2
- gst-plugins-base=1.22.0
- gstreamer=1.22.0
- gstreamer-orc=0.4.42
- h11=0.16.0
- h2=4.3.0
- harfbuzz=6.0.0
- hpack=4.1.0
- httpcore=1.0.9
- httptools=0.7.1
- httpx=0.28.1
- httpx-sse=0.4.3
- huggingface_hub=0.31.4
- hyperframe=6.1.0
- icu=70.1
- idna=3.11
- importlib-metadata=8.7.0
- importlib_metadata=8.7.0
- importlib_resources=6.5.2
- ipykernel=7.1.0
- ipython=9.9.0
- ipython_pygments_lexers=1.1.1
- ipywidgets=8.1.8
- isoduration=20.11.0
- jack=1.9.22
- jedi=0.19.2
- jinja2=3.1.6
- joblib=1.5.3
- jpeg=9e
- json5=0.13.0
- jsonpointer=3.0.0
- jsonschema=4.26.0
- jsonschema-specifications=2025.9.1
- jsonschema-with-format-nongpl=4.26.0
- jupyter=1.1.1
- jupyter-lsp=2.3.0
- jupyter_client=8.8.0
- jupyter_console=6.6.3
- jupyter_core=5.9.1
- jupyter_events=0.12.0
- jupyter_server=2.17.0
- jupyter_server_terminals=0.5.4
- jupyterlab=4.5.3
- jupyterlab_pygments=0.3.0
- jupyterlab_server=2.28.0
- jupyterlab_widgets=3.0.16
- keyutils=1.6.3
- kiwisolver=1.4.9
- krb5=1.20.1
- lame=3.100
- lark=1.3.1
- lcms2=2.15
- ld_impl_linux-64=2.45
- lerc=4.0.0
- libabseil=20230125.0
- libarrow=11.0.0
- libasprintf=0.25.1
- libasprintf-devel=0.25.1
- libblas=3.9.0
- libbrotlicommon=1.0.9
- libbrotlidec=1.0.9
- libbrotlienc=1.0.9
- libcap=2.67
- libcblas=3.9.0
- libclang=15.0.7
- libclang13=15.0.7
- libcrc32c=1.1.2
- libcublas=12.4.5.8
- libcufft=11.2.1.3
- libcufile=1.9.1.3
- libcups=2.3.3
- libcurand=10.3.5.147
- libcurl=8.1.2
- libcusolver=11.6.1.9
- libcusparse=12.3.1.170
- libdb=6.2.32
- libdeflate=1.17
- libedit=3.1.20250104
- libev=4.33
- libevent=2.1.10
- libexpat=2.7.3
- libffi=3.5.2
- libflac=1.5.0
- libgcc=15.2.0
- libgcc-ng=15.2.0
- libgcrypt=1.11.1
- libgcrypt-devel=1.11.1
- libgcrypt-lib=1.11.1
- libgcrypt-tools=1.11.1
- libgettextpo=0.25.1
- libgettextpo-devel=0.25.1
- libgfortran=15.2.0
- libgfortran5=15.2.0
- libglib=2.80.2
- libgomp=15.2.0
- libgoogle-cloud=2.8.0
- libgpg-error=1.58
- libgrpc=1.52.1
- libhwloc=2.9.1
- libiconv=1.18
- libjpeg-turbo=2.0.0
- liblapack=3.9.0
- libllvm15=15.0.7
- libltdl=2.4.3a
- liblzma=5.8.2
- liblzma-devel=5.8.2
- libnghttp2=1.58.0
- libnl=3.11.0
- libnpp=12.2.5.30
- libnsl=2.0.1
- libnvfatbin=12.4.127
- libnvjitlink=12.4.127
- libnvjpeg=12.3.1.117
- libogg=1.3.5
- libopus=1.6.1
- libpng=1.6.43
- libpq=15.3
- libprotobuf=3.21.12
- libsentencepiece=0.1.97
- libsndfile=1.2.2
- libsodium=1.0.18
- libsqlite=3.46.0
- libssh2=1.11.0
- libstdcxx=15.2.0
- libstdcxx-ng=15.2.0
- libsystemd0=253
- libthrift=0.18.1
- libtiff=4.5.0
- libtool=2.5.4
- libudev1=253
- libutf8proc=2.8.0
- libuuid=2.41.3
- libuv=1.51.0
- libvorbis=1.3.7
- libwebp=1.2.4
- libwebp-base=1.2.4
- libxcb=1.13
- libxcrypt=4.4.36
- libxkbcommon=1.5.0
- libxml2=2.10.3
- libzlib=1.2.13
- llvm-openmp=15.0.7
- lz4-c=1.9.4
- markdown-it-py=4.0.0
- markupsafe=3.0.3
- matplotlib=3.9.1
- matplotlib-base=3.9.1
- matplotlib-inline=0.2.1
- mcp=1.26.0
- mdurl=0.1.2
- mistune=3.2.0
- mkl=2023.2.0
- mpc=1.3.1
- mpfr=4.2.1
- mpg123=1.32.9
- mpmath=1.3.0
- multidict=6.7.0
- multiprocess=0.70.15
- munkres=1.1.4
- mysql-common=8.0.33
- mysql-libs=8.0.33
- nbclient=0.10.4
- nbconvert-core=7.17.0
- nbformat=5.10.4
- ncurses=6.5
- nest-asyncio=1.6.0
- nettle=3.6
- networkx=3.6.1
- nltk=3.9.2
- notebook=7.5.3
- notebook-shim=0.2.4
- nspr=4.38
- nss=3.100
- numpy=1.26.4
- ocl-icd=2.3.3
- openapi-pydantic=0.5.1
- opencl-headers=2025.06.13
- openh264=2.1.1
- openjpeg=2.5.0
- openssl=3.1.8
- optimum=2.1.0
- orc=1.8.3
- orjson=3.11.5
- overrides=7.7.0
- packaging=26.0
- pandas=2.3.3
- pandocfilters=1.5.0
- parquet-cpp=1.5.1
- parso=0.8.5
- patsy=1.0.2
- pcre2=10.43
- pexpect=4.9.0
- pillow=9.4.0
- pip=26.0
- pixman=0.46.4
- platformdirs=4.5.1
- ply=3.11
- prometheus_client=0.24.1
- prompt-toolkit=3.0.52
- prompt_toolkit=3.0.52
- propcache=0.3.1
- protobuf=4.21.12
- psutil=7.2.2
- pthread-stubs=0.4
- ptyprocess=0.7.0
- pulseaudio=16.1
- pulseaudio-client=16.1
- pulseaudio-daemon=16.1
- pure_eval=0.2.3
- pyarrow=11.0.0
- pyarrow-hotfix=0.7
- pycparser=2.22
- pydantic=2.11.10
- pydantic-core=2.33.2
- pydantic-extra-types=2.11.0
- pydantic-settings=2.12.0
- pydub=0.25.1
- pygments=2.19.2
- pyjwt=2.11.0
- pyparsing=3.3.2
- pyqt=5.15.9
- pyqt5-sip=12.12.2
- pysocks=1.7.1
- python=3.11.6
- python-dateutil=2.9.0.post0
- python-dotenv=1.2.1
- python-fastjsonschema=2.21.2
- python-json-logger=2.0.7
- python-multipart=0.0.22
- python-slugify=8.0.4
- python-tzdata=2025.3
- python-xxhash=3.6.0
- python_abi=3.11
- pytorch=2.5.1
- pytorch-cuda=12.4
- pytorch-mutex=1.0
- pytz=2025.2
- pywin32-on-windows=0.1.0
- pyyaml=6.0.3
- pyzmq=27.1.0
- qhull=2020.2
- qt-main=5.15.8
- rdma-core=54.0
- re2=2023.02.02
- readline=8.3
- referencing=0.37.0
- regex=2026.1.15
- requests=2.32.5
- rfc3339-validator=0.1.4
- rfc3986-validator=0.1.1
- rfc3987-syntax=1.1.0
- rich=14.3.1
- rich-toolkit=0.18.0
- rpds-py=0.30.0
- ruff=0.14.14
- s2n=1.3.41
- sacremoses=0.0.53
- safehttpx=0.1.7
- safetensors=0.7.0
- scikit-learn=1.8.0
- scipy=1.17.0
- seaborn=0.13.2
- seaborn-base=0.13.2
- semantic_version=2.10.0
- send2trash=2.1.0
- sentencepiece=0.1.97
- sentencepiece-python=0.1.97
- sentencepiece-spm=0.1.97
- setuptools=80.10.2
- shellingham=1.5.4
- sip=6.7.12
- six=1.17.0
- snappy=1.1.10
- sniffio=1.3.1
- soupsieve=2.8.3
- sse-starlette=3.3.2
- stack_data=0.6.3
- starlette=0.50.0
- statsmodels=0.14.6
- tbb=2021.9.0
- terminado=0.18.1
- text-unidecode=1.3
- threadpoolctl=3.6.0
- tinycss2=1.5.1
- tk=8.6.13
- tokenizers=0.13.3
- toml=0.10.2
- tomli=2.4.0
- tomlkit=0.13.3
- torchaudio=2.5.1
- torchtriton=3.1.0
- torchvision=0.20.1
- tornado=6.5.4
- tqdm=4.67.2
- traitlets=5.14.3
- transformers=4.33.3
- typer=0.21.1
- typer-slim=0.21.1
- typer-slim-standard=0.21.1
- typing-extensions=4.15.0
- typing-inspection=0.4.2
- typing_extensions=4.15.0
- typing_utils=0.1.0
- tzdata=2025c
- ucx=1.17.0
- unicodedata2=17.0.0
- uri-template=1.3.0
- urllib3=2.5.0
- uvicorn=0.40.0
- uvicorn-standard=0.40.0
- uvloop=0.22.1
- watchfiles=1.1.1
- wcwidth=0.5.3
- webcolors=25.10.0
- webencodings=0.5.1
- websocket-client=1.9.0
- websockets=15.0.1
- wheel=0.46.3
- widgetsnbextension=4.0.15
- xcb-util=0.4.0
- xcb-util-image=0.4.0
- xcb-util-keysyms=0.4.0
- xcb-util-renderutil=0.3.9
- xcb-util-wm=0.4.1
- xkeyboard-config=2.38
- xorg-kbproto=1.0.7
- xorg-libice=1.1.2
- xorg-libsm=1.2.6
- xorg-libx11=1.8.4
- xorg-libxau=1.0.12
- xorg-libxdmcp=1.1.5
- xorg-libxext=1.3.4
- xorg-libxrender=0.9.10
- xorg-renderproto=0.11.1
- xorg-xextproto=7.3.0
- xorg-xproto=7.0.31
- xxhash=0.8.3
- xz=5.8.2
- xz-gpl-tools=5.8.2
- xz-tools=5.8.2
- yaml=0.2.5
- yarl=1.22.0
- zeromq=4.3.5
- zipp=3.23.0
- zlib=1.2.13
- zstandard=0.23.0
- zstd=1.5.6
- pip:
- aiofile==3.9.0
- backports-tarfile==1.2.0
- beartype==0.22.9
- cachetools==7.0.2
- caio==0.9.25
- cryptography==46.0.5
- cyclopts==4.6.0
- docstring-parser==0.17.0
- docutils==0.22.4
- htb-ai-library==0.4.0
- jaraco-classes==3.4.0
- jaraco-context==6.1.0
- jaraco-functools==4.4.0
- jeepney==0.9.0
- jsonref==1.1.0
- jsonschema-path==0.4.4
- keyring==25.7.0
- more-itertools==10.8.0
- opacus==1.5.4
- opentelemetry-api==1.39.1
- opt-einsum==3.4.0
- pathable==0.5.0
- py-key-value-aio==0.4.4
- pyperclip==1.11.0
- requests-toolbelt==1.0.0
- rich-rst==1.3.2
- secretstorage==3.5.0
- sympy==1.13.1
- torch-workflow-archiver==0.2.15
prefix: /home/jeremy/.conda/envs/ai
Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

@@ -0,0 +1,27 @@
{
"target_model": {
"train_accuracy": 0.9372671061791081,
"test_accuracy": 0.8247481778724102,
"overfitting_gap": 0.11251892830669796
},
"attack_results": {
"accuracy": 0.6892061585499017,
"precision": 0.6897525473071324,
"recall": 0.9701895909258426,
"f1_score": 0.8062820098347825,
"auc": 0.5731226081310534,
"advantage": 0.18920615854990175
},
"configuration": {
"random_seed": 1337,
"num_shadow_models": 5,
"target_architecture": [
256,
128
],
"attack_architecture": [
64,
32
]
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

+22
View File
@@ -0,0 +1,22 @@
{
"configuration": {
"dataset": "mnist",
"num_teachers": 250,
"noise_scale": 20.0,
"num_queries": 5000
},
"privacy": {
"per_query_epsilon": 0.1,
"total_epsilon": 11.647065596072498
},
"utility": {
"student_accuracy": 87.71,
"label_accuracy": 87.44,
"clean_ensemble_accuracy": 0.8782
},
"information": {
"private_data_bytes": 150528000,
"student_info_bytes": 20000,
"compression_ratio": 7526.4
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+495
View File
@@ -0,0 +1,495 @@
# This file may be used to create an environment using:
# $ conda create --name <env> --file <this file>
# platform: linux-64
# created-by: conda 25.9.1
_libgcc_mutex=0.1=conda_forge
_openmp_mutex=4.5=7_kmp_llvm
accelerate=1.12.0=pyhcf101f3_0
aiofile=3.9.0=pypi_0
aiofiles=24.1.0=pyhd8ed1ab_1
aiohappyeyeballs=2.6.1=pyhd8ed1ab_0
aiohttp=3.13.3=py311h55b9665_0
aiosignal=1.4.0=pyhd8ed1ab_0
alsa-lib=1.2.8=h166bdaf_0
annotated-doc=0.0.4=pyhcf101f3_0
annotated-types=0.7.0=pyhd8ed1ab_1
anyio=4.12.1=pyhcf101f3_0
argon2-cffi=25.1.0=pyhd8ed1ab_0
argon2-cffi-bindings=25.1.0=py311h49ec1c0_2
arrow=1.4.0=pyhcf101f3_0
arrow-cpp=11.0.0=ha770c72_12_cpu
asttokens=3.0.1=pyhd8ed1ab_0
async-lru=2.1.0=pyhcf101f3_0
attr=2.5.2=h39aace5_0
attrs=25.4.0=pyhcf101f3_1
authlib=1.6.9=pyhd8ed1ab_0
aws-c-auth=0.6.26=h987a71b_2
aws-c-cal=0.5.21=h48707d8_2
aws-c-common=0.8.14=h0b41bf4_0
aws-c-compression=0.2.16=h03acc5a_5
aws-c-event-stream=0.2.20=h00877a2_4
aws-c-http=0.7.6=hf342b9f_0
aws-c-io=0.13.19=h5b20300_3
aws-c-mqtt=0.8.6=hc4349f7_12
aws-c-s3=0.2.7=h909e904_1
aws-c-sdkutils=0.1.9=h03acc5a_0
aws-checksums=0.1.14=h03acc5a_5
aws-crt-cpp=0.19.8=hf7fbfca_12
aws-sdk-cpp=1.10.57=h17c43bd_8
babel=2.17.0=pyhd8ed1ab_0
backports-tarfile=1.2.0=pypi_0
beartype=0.22.9=pypi_0
beautifulsoup4=4.14.3=pyha770c72_0
binaryornot=0.4.4=pyhd8ed1ab_2
blas=1.0=mkl
bleach=6.3.0=pyhcf101f3_0
bleach-with-css=6.3.0=h5f6438b_0
brotli=1.0.9=h166bdaf_9
brotli-bin=1.0.9=h166bdaf_9
brotli-python=1.0.9=py311ha362b79_9
bzip2=1.0.8=hda65f42_8
c-ares=1.34.6=hb03c661_0
ca-certificates=2026.2.25=hbd8a1cb_0
cached-property=1.5.2=hd8ed1ab_1
cached_property=1.5.2=pyha770c72_1
cachetools=7.0.2=pypi_0
caio=0.9.25=pypi_0
cairo=1.16.0=ha61ee94_1014
category_encoders=2.9.0=pyhcf101f3_0
certifi=2026.2.25=pyhd8ed1ab_0
cffi=2.0.0=py311h03d9500_1
chardet=5.2.0=pyhd8ed1ab_3
charset-normalizer=3.4.4=pyhd8ed1ab_0
click=8.3.1=pyh8f84b5b_1
comm=0.2.3=pyhe01879c_0
contourpy=1.3.3=py311h724c32c_4
cookiecutter=2.6.0=pyhd8ed1ab_1
cpython=3.11.14=py311hd8ed1ab_3
cryptography=46.0.5=pypi_0
cuda-cudart=12.4.127=he02047a_2
cuda-cudart_linux-64=12.4.127=h85509e4_2
cuda-cupti=12.4.127=he02047a_2
cuda-libraries=12.4.1=ha770c72_1
cuda-nvrtc=12.4.127=he02047a_2
cuda-nvtx=12.4.127=he02047a_2
cuda-opencl=12.4.127=he02047a_1
cuda-runtime=12.4.1=0
cuda-version=12.4=h3060b56_3
cycler=0.12.1=pyhcf101f3_2
cyclopts=4.6.0=pypi_0
dataclasses=0.8=pyhc8e2a94_3
datasets=2.16.1=pyhd8ed1ab_0
dbus=1.13.6=h5008d03_3
debugpy=1.8.20=py311hc665b79_0
decorator=5.2.1=pyhd8ed1ab_0
defusedxml=0.7.1=pyhd8ed1ab_0
dill=0.3.7=pyhd8ed1ab_0
dnspython=2.6.1=pyhd8ed1ab_1
docstring-parser=0.17.0=pypi_0
docutils=0.22.4=pypi_0
email-validator=2.3.0=pyhd8ed1ab_0
email_validator=2.3.0=hd8ed1ab_0
evaluate=0.4.6=pyhcf101f3_0
exceptiongroup=1.3.1=pyhd8ed1ab_0
executing=2.2.1=pyhd8ed1ab_0
expat=2.7.3=hecca717_0
fastapi=0.128.0=h0ea4129_1
fastapi-cli=0.0.20=pyhcf101f3_0
fastapi-core=0.128.0=pyhcf101f3_1
fastmcp=2.10.2=pyhe01879c_0
ffmpeg=4.3=hf484d3e_0
ffmpy=0.3.0=pyhb6f538c_0
fftw=3.3.10=nompi_h3b011a4_111
filelock=3.20.3=pyhd8ed1ab_0
font-ttf-dejavu-sans-mono=2.37=hab24e00_0
font-ttf-inconsolata=3.000=h77eed37_0
font-ttf-source-code-pro=2.038=h77eed37_0
font-ttf-ubuntu=0.83=h77eed37_3
fontconfig=2.14.2=h14ed4e7_0
fonts-conda-ecosystem=1=0
fonts-conda-forge=1=hc364b38_1
fonttools=4.61.1=py311h3778330_0
fqdn=1.5.1=pyhd8ed1ab_1
freetype=2.12.1=h267a509_2
frozenlist=1.7.0=py311h52bc045_0
fsspec=2023.10.0=pyhca7485f_0
gettext=0.25.1=h3f43e3d_1
gettext-tools=0.25.1=h3f43e3d_1
gflags=2.2.2=h5888daf_1005
giflib=5.2.2=hd590300_0
glib=2.80.2=hf974151_0
glib-tools=2.80.2=hb6ce0ca_0
glog=0.6.0=h6f12383_0
gmp=6.3.0=hac33072_2
gmpy2=2.2.1=py311h92a432a_2
gnutls=3.6.13=h85f3911_1
gradio=6.5.1=pyhd8ed1ab_0
gradio-client=2.0.3=pyhd8ed1ab_0
graphite2=1.3.14=hecca717_2
groovy-python=0.1.2=pyhd8ed1ab_0
gst-plugins-base=1.22.0=h4243ec0_2
gstreamer=1.22.0=h25f0c4b_2
gstreamer-orc=0.4.42=h82d0256_0
h11=0.16.0=pyhcf101f3_1
h2=4.3.0=pyhcf101f3_0
harfbuzz=6.0.0=h8e241bc_0
hpack=4.1.0=pyhd8ed1ab_0
htb-ai-library=0.4.0=pypi_0
httpcore=1.0.9=pyh29332c3_0
httptools=0.7.1=py311h49ec1c0_1
httpx=0.28.1=pyhd8ed1ab_0
httpx-sse=0.4.3=pyhd8ed1ab_0
huggingface_hub=0.31.4=pyhd8ed1ab_0
hyperframe=6.1.0=pyhd8ed1ab_0
icu=70.1=h27087fc_0
idna=3.11=pyhd8ed1ab_0
importlib-metadata=8.7.0=pyhe01879c_1
importlib_metadata=8.7.0=h40b2b14_1
importlib_resources=6.5.2=pyhd8ed1ab_0
ipykernel=7.1.0=pyha191276_0
ipython=9.9.0=pyh53cf698_0
ipython_pygments_lexers=1.1.1=pyhd8ed1ab_0
ipywidgets=8.1.8=pyhd8ed1ab_0
isoduration=20.11.0=pyhd8ed1ab_1
jack=1.9.22=h11f4161_0
jaraco-classes=3.4.0=pypi_0
jaraco-context=6.1.0=pypi_0
jaraco-functools=4.4.0=pypi_0
jedi=0.19.2=pyhd8ed1ab_1
jeepney=0.9.0=pypi_0
jinja2=3.1.6=pyhcf101f3_1
joblib=1.5.3=pyhd8ed1ab_0
jpeg=9e=h166bdaf_2
json5=0.13.0=pyhd8ed1ab_0
jsonpointer=3.0.0=pyhcf101f3_3
jsonref=1.1.0=pypi_0
jsonschema=4.26.0=pyhcf101f3_0
jsonschema-path=0.4.4=pypi_0
jsonschema-specifications=2025.9.1=pyhcf101f3_0
jsonschema-with-format-nongpl=4.26.0=hcf101f3_0
jupyter=1.1.1=pyhd8ed1ab_1
jupyter-lsp=2.3.0=pyhcf101f3_0
jupyter_client=8.8.0=pyhcf101f3_0
jupyter_console=6.6.3=pyhd8ed1ab_1
jupyter_core=5.9.1=pyhc90fa1f_0
jupyter_events=0.12.0=pyh29332c3_0
jupyter_server=2.17.0=pyhcf101f3_0
jupyter_server_terminals=0.5.4=pyhcf101f3_0
jupyterlab=4.5.3=pyhd8ed1ab_0
jupyterlab_pygments=0.3.0=pyhd8ed1ab_2
jupyterlab_server=2.28.0=pyhcf101f3_0
jupyterlab_widgets=3.0.16=pyhcf101f3_1
keyring=25.7.0=pypi_0
keyutils=1.6.3=hb9d3cd8_0
kiwisolver=1.4.9=py311h724c32c_2
krb5=1.20.1=h81ceb04_0
lame=3.100=h166bdaf_1003
lark=1.3.1=pyhd8ed1ab_0
lcms2=2.15=hfd0df8a_0
ld_impl_linux-64=2.45=bootstrap_ha15bf96_5
lerc=4.0.0=h0aef613_1
libabseil=20230125.0=cxx17_hcb278e6_1
libarrow=11.0.0=h51ec05e_12_cpu
libasprintf=0.25.1=h3f43e3d_1
libasprintf-devel=0.25.1=h3f43e3d_1
libblas=3.9.0=20_linux64_mkl
libbrotlicommon=1.0.9=h166bdaf_9
libbrotlidec=1.0.9=h166bdaf_9
libbrotlienc=1.0.9=h166bdaf_9
libcap=2.67=he9d0100_0
libcblas=3.9.0=20_linux64_mkl
libclang=15.0.7=default_h127d8a8_5
libclang13=15.0.7=default_h5d6823c_5
libcrc32c=1.1.2=h9c3ff4c_0
libcublas=12.4.5.8=he02047a_2
libcufft=11.2.1.3=he02047a_2
libcufile=1.9.1.3=he02047a_2
libcups=2.3.3=h36d4200_3
libcurand=10.3.5.147=he02047a_2
libcurl=8.1.2=h409715c_0
libcusolver=11.6.1.9=he02047a_2
libcusparse=12.3.1.170=he02047a_2
libdb=6.2.32=h9c3ff4c_0
libdeflate=1.17=h0b41bf4_0
libedit=3.1.20250104=pl5321h7949ede_0
libev=4.33=hd590300_2
libevent=2.1.10=h28343ad_4
libexpat=2.7.3=hecca717_0
libffi=3.5.2=h3435931_0
libflac=1.5.0=he200343_1
libgcc=15.2.0=he0feb66_16
libgcc-ng=15.2.0=h69a702a_16
libgcrypt=1.11.1=ha770c72_0
libgcrypt-devel=1.11.1=hb9d3cd8_0
libgcrypt-lib=1.11.1=hb9d3cd8_0
libgcrypt-tools=1.11.1=hb9d3cd8_0
libgettextpo=0.25.1=h3f43e3d_1
libgettextpo-devel=0.25.1=h3f43e3d_1
libgfortran=15.2.0=h69a702a_16
libgfortran5=15.2.0=h68bc16d_16
libglib=2.80.2=hf974151_0
libgomp=15.2.0=he0feb66_16
libgoogle-cloud=2.8.0=h0bc5f78_1
libgpg-error=1.58=h54a6638_0
libgrpc=1.52.1=hcf146ea_1
libhwloc=2.9.1=hd6dc26d_0
libiconv=1.18=h3b78370_2
libjpeg-turbo=2.0.0=h9bf148f_0
liblapack=3.9.0=20_linux64_mkl
libllvm15=15.0.7=hadd5161_1
libltdl=2.4.3a=h5888daf_0
liblzma=5.8.2=hb03c661_0
liblzma-devel=5.8.2=hb03c661_0
libnghttp2=1.58.0=h47da74e_0
libnl=3.11.0=hb9d3cd8_0
libnpp=12.2.5.30=he02047a_2
libnsl=2.0.1=hb9d3cd8_1
libnvfatbin=12.4.127=he02047a_2
libnvjitlink=12.4.127=he02047a_2
libnvjpeg=12.3.1.117=he02047a_2
libogg=1.3.5=hd0c01bc_1
libopus=1.6.1=h280c20c_0
libpng=1.6.43=h2797004_0
libpq=15.3=hbcd7760_1
libprotobuf=3.21.12=hfc55251_2
libsentencepiece=0.1.97=h47aad16_1
libsndfile=1.2.2=hc7d488a_2
libsodium=1.0.18=h36c2ea0_1
libsqlite=3.46.0=hde9e2c9_0
libssh2=1.11.0=h0841786_0
libstdcxx=15.2.0=h934c35e_16
libstdcxx-ng=15.2.0=hdf11a46_16
libsystemd0=253=h8c4010b_1
libthrift=0.18.1=h5e4af38_0
libtiff=4.5.0=h6adf6a1_2
libtool=2.5.4=h5888daf_0
libudev1=253=h0b41bf4_1
libutf8proc=2.8.0=hf23e847_1
libuuid=2.41.3=h5347b49_0
libuv=1.51.0=hb03c661_1
libvorbis=1.3.7=h54a6638_2
libwebp=1.2.4=h1daa5a0_1
libwebp-base=1.2.4=h166bdaf_0
libxcb=1.13=h7f98852_1004
libxcrypt=4.4.36=hd590300_1
libxkbcommon=1.5.0=h79f4944_1
libxml2=2.10.3=hca2bb57_4
libzlib=1.2.13=h4ab18f5_6
llvm-openmp=15.0.7=h0cdce71_0
lz4-c=1.9.4=hcb278e6_0
markdown-it-py=4.0.0=pyhd8ed1ab_0
markupsafe=3.0.3=py311h3778330_0
matplotlib=3.9.1=py311h38be061_1
matplotlib-base=3.9.1=py311h74b4f7c_2
matplotlib-inline=0.2.1=pyhd8ed1ab_0
mcp=1.26.0=pyhd8ed1ab_0
mdurl=0.1.2=pyhd8ed1ab_1
mistune=3.2.0=pyhcf101f3_0
mkl=2023.2.0=ha770c72_50498
more-itertools=10.8.0=pypi_0
mpc=1.3.1=h24ddda3_1
mpfr=4.2.1=h90cbb55_3
mpg123=1.32.9=hc50e24c_0
mpmath=1.3.0=pyhd8ed1ab_1
multidict=6.7.0=py311h3778330_0
multiprocess=0.70.15=py311h459d7ec_1
munkres=1.1.4=pyhd8ed1ab_1
mysql-common=8.0.33=hf1915f5_6
mysql-libs=8.0.33=hca2cd23_6
nbclient=0.10.4=pyhd8ed1ab_0
nbconvert-core=7.17.0=pyhcf101f3_0
nbformat=5.10.4=pyhd8ed1ab_1
ncurses=6.5=h2d0b736_3
nest-asyncio=1.6.0=pyhd8ed1ab_1
nettle=3.6=he412f7d_0
networkx=3.6.1=pyhcf101f3_0
nltk=3.9.2=pyhcf101f3_1
notebook=7.5.3=pyhcf101f3_0
notebook-shim=0.2.4=pyhd8ed1ab_1
nspr=4.38=h29cc59b_0
nss=3.100=hca3bf56_0
numpy=1.26.4=py311h64a7726_0
ocl-icd=2.3.3=hb9d3cd8_0
opacus=1.5.4=pypi_0
openapi-pydantic=0.5.1=pyh3cfb1c2_0
opencl-headers=2025.06.13=h5888daf_0
openh264=2.1.1=h780b84a_0
openjpeg=2.5.0=hfec8fc6_2
openssl=3.1.8=h7b32b05_0
opentelemetry-api=1.39.1=pypi_0
opt-einsum=3.4.0=pypi_0
optimum=2.1.0=pyhd8ed1ab_0
orc=1.8.3=h2f23424_1
orjson=3.11.5=py311h7098c77_0
overrides=7.7.0=pyhd8ed1ab_1
packaging=26.0=pyhcf101f3_0
pandas=2.3.3=py311hed34c8f_2
pandocfilters=1.5.0=pyhd8ed1ab_0
parquet-cpp=1.5.1=2
parso=0.8.5=pyhcf101f3_0
pathable=0.5.0=pypi_0
patsy=1.0.2=pyhcf101f3_0
pcre2=10.43=hcad00b1_0
pexpect=4.9.0=pyhd8ed1ab_1
pillow=9.4.0=py311h50def17_1
pip=26.0=pyh8b19718_0
pixman=0.46.4=h54a6638_1
platformdirs=4.5.1=pyhcf101f3_0
ply=3.11=pyhd8ed1ab_3
prometheus_client=0.24.1=pyhd8ed1ab_0
prompt-toolkit=3.0.52=pyha770c72_0
prompt_toolkit=3.0.52=hd8ed1ab_0
propcache=0.3.1=py311h2dc5d0c_0
protobuf=4.21.12=py311hcafe171_0
psutil=7.2.2=py311haee01d2_0
pthread-stubs=0.4=hb9d3cd8_1002
ptyprocess=0.7.0=pyhd8ed1ab_1
pulseaudio=16.1=hcb278e6_3
pulseaudio-client=16.1=h5195f5e_3
pulseaudio-daemon=16.1=ha8d29e2_3
pure_eval=0.2.3=pyhd8ed1ab_1
py-key-value-aio=0.4.4=pypi_0
pyarrow=11.0.0=py311hbdf6286_12_cpu
pyarrow-hotfix=0.7=pyhd8ed1ab_0
pycparser=2.22=pyh29332c3_1
pydantic=2.11.10=pyh3cfb1c2_0
pydantic-core=2.33.2=py311hdae7d1d_0
pydantic-extra-types=2.11.0=pyhd8ed1ab_0
pydantic-settings=2.12.0=pyh3cfb1c2_0
pydub=0.25.1=pyhd8ed1ab_1
pygments=2.19.2=pyhd8ed1ab_0
pyjwt=2.11.0=pyhd8ed1ab_0
pyparsing=3.3.2=pyhcf101f3_0
pyperclip=1.11.0=pypi_0
pyqt=5.15.9=py311hf0fb5b6_5
pyqt5-sip=12.12.2=py311hb755f60_5
pysocks=1.7.1=pyha55dd90_7
python=3.11.6=hab00c5b_0_cpython
python-dateutil=2.9.0.post0=pyhe01879c_2
python-dotenv=1.2.1=pyhcf101f3_0
python-fastjsonschema=2.21.2=pyhe01879c_0
python-json-logger=2.0.7=pyhd8ed1ab_0
python-multipart=0.0.22=pyhcf101f3_0
python-slugify=8.0.4=pyhd8ed1ab_1
python-tzdata=2025.3=pyhd8ed1ab_0
python-xxhash=3.6.0=py311h041eb40_1
python_abi=3.11=8_cp311
pytorch=2.5.1=py3.11_cuda12.4_cudnn9.1.0_0
pytorch-cuda=12.4=hc786d27_7
pytorch-mutex=1.0=cuda
pytz=2025.2=pyhd8ed1ab_0
pywin32-on-windows=0.1.0=pyh1179c8e_3
pyyaml=6.0.3=py311h3778330_0
pyzmq=27.1.0=py311h2315fbb_0
qhull=2020.2=h434a139_5
qt-main=5.15.8=h5d23da1_6
rdma-core=54.0=h5888daf_0
re2=2023.02.02=hcb278e6_0
readline=8.3=h853b02a_0
referencing=0.37.0=pyhcf101f3_0
regex=2026.1.15=py311h49ec1c0_0
requests=2.32.5=pyhcf101f3_1
requests-toolbelt=1.0.0=pypi_0
rfc3339-validator=0.1.4=pyhd8ed1ab_1
rfc3986-validator=0.1.1=pyh9f0ad1d_0
rfc3987-syntax=1.1.0=pyhe01879c_1
rich=14.3.1=pyhcf101f3_0
rich-rst=1.3.2=pypi_0
rich-toolkit=0.18.0=pyhcf101f3_0
rpds-py=0.30.0=py311h902ca64_0
ruff=0.14.14=h40fa522_1
s2n=1.3.41=h3358134_0
sacremoses=0.0.53=pyhd8ed1ab_0
safehttpx=0.1.7=pyhd8ed1ab_0
safetensors=0.7.0=py311hc8fb587_0
scikit-learn=1.8.0=np2py311ha15b03d_1
scipy=1.17.0=py311hbe70eeb_1
seaborn=0.13.2=hd8ed1ab_3
seaborn-base=0.13.2=pyhd8ed1ab_3
secretstorage=3.5.0=pypi_0
semantic_version=2.10.0=pyhd8ed1ab_0
send2trash=2.1.0=pyha191276_0
sentencepiece=0.1.97=h38be061_1
sentencepiece-python=0.1.97=py311h1d730ea_1
sentencepiece-spm=0.1.97=h47aad16_1
setuptools=80.10.2=pyh332efcf_0
shellingham=1.5.4=pyhd8ed1ab_2
sip=6.7.12=py311hb755f60_0
six=1.17.0=pyhe01879c_1
snappy=1.1.10=hdb0a2a9_1
sniffio=1.3.1=pyhd8ed1ab_2
soupsieve=2.8.3=pyhd8ed1ab_0
sse-starlette=3.3.2=pyhd8ed1ab_0
stack_data=0.6.3=pyhd8ed1ab_1
starlette=0.50.0=pyhfdc7a7d_0
statsmodels=0.14.6=py311h0372a8f_0
sympy=1.13.1=pypi_0
tbb=2021.9.0=hf52228f_0
terminado=0.18.1=pyhc90fa1f_1
text-unidecode=1.3=pyhd8ed1ab_2
threadpoolctl=3.6.0=pyhecae5ae_0
tinycss2=1.5.1=pyhcf101f3_0
tk=8.6.13=noxft_h4845f30_101
tokenizers=0.13.3=py311h1b04a43_0
toml=0.10.2=pyhcf101f3_3
tomli=2.4.0=pyhcf101f3_0
tomlkit=0.13.3=pyha770c72_0
torch-workflow-archiver=0.2.15=pypi_0
torchaudio=2.5.1=py311_cu124
torchtriton=3.1.0=py311
torchvision=0.20.1=py311_cu124
tornado=6.5.4=py311h41d9c34_0
tqdm=4.67.2=pyh8f84b5b_2
traitlets=5.14.3=pyhd8ed1ab_1
transformers=4.33.3=pyhd8ed1ab_0
typer=0.21.1=pyhf8876ea_0
typer-slim=0.21.1=pyhcf101f3_0
typer-slim-standard=0.21.1=h378290b_0
typing-extensions=4.15.0=h396c80c_0
typing-inspection=0.4.2=pyhd8ed1ab_1
typing_extensions=4.15.0=pyhcf101f3_0
typing_utils=0.1.0=pyhd8ed1ab_1
tzdata=2025c=hc9c84f9_1
ucx=1.17.0=h05e919c_3
unicodedata2=17.0.0=py311h49ec1c0_1
uri-template=1.3.0=pyhd8ed1ab_1
urllib3=2.5.0=pyhd8ed1ab_0
uvicorn=0.40.0=pyhc90fa1f_0
uvicorn-standard=0.40.0=h4cd5af1_0
uvloop=0.22.1=py311h49ec1c0_1
watchfiles=1.1.1=py311hc8fb587_0
wcwidth=0.5.3=pyhd8ed1ab_0
webcolors=25.10.0=pyhd8ed1ab_0
webencodings=0.5.1=pyhd8ed1ab_3
websocket-client=1.9.0=pyhd8ed1ab_0
websockets=15.0.1=py311haee01d2_2
wheel=0.46.3=pyhd8ed1ab_0
widgetsnbextension=4.0.15=pyhd8ed1ab_0
xcb-util=0.4.0=h516909a_0
xcb-util-image=0.4.0=h166bdaf_0
xcb-util-keysyms=0.4.0=h516909a_0
xcb-util-renderutil=0.3.9=h166bdaf_0
xcb-util-wm=0.4.1=h516909a_0
xkeyboard-config=2.38=h0b41bf4_0
xorg-kbproto=1.0.7=hb9d3cd8_1003
xorg-libice=1.1.2=hb9d3cd8_0
xorg-libsm=1.2.6=he73a12e_0
xorg-libx11=1.8.4=h0b41bf4_0
xorg-libxau=1.0.12=hb03c661_1
xorg-libxdmcp=1.1.5=hb03c661_1
xorg-libxext=1.3.4=h0b41bf4_2
xorg-libxrender=0.9.10=h7f98852_1003
xorg-renderproto=0.11.1=hb9d3cd8_1003
xorg-xextproto=7.3.0=hb9d3cd8_1004
xorg-xproto=7.0.31=hb9d3cd8_1008
xxhash=0.8.3=hb47aa4a_0
xz=5.8.2=ha02ee65_0
xz-gpl-tools=5.8.2=ha02ee65_0
xz-tools=5.8.2=hb03c661_0
yaml=0.2.5=h280c20c_3
yarl=1.22.0=py311h3778330_0
zeromq=4.3.5=h59595ed_1
zipp=3.23.0=pyhcf101f3_1
zlib=1.2.13=h4ab18f5_6
zstandard=0.23.0=py311h49ec1c0_3
zstd=1.5.6=ha6fb4c9_0
+11856
View File
File diff suppressed because it is too large Load Diff
+882
View File
@@ -0,0 +1,882 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "36ec729e-fdba-4cd3-9a54-a255dff0b1c6",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"# Setup "
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "f04bdbd1-41c5-4c6e-b067-899d258c5863",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import json\n",
"import numpy as np\n",
"import torch\n",
"import torch.nn.functional as F\n",
"from sklearn.model_selection import train_test_split\n",
"from sklearn.preprocessing import StandardScaler\n",
"from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, classification_report"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "607dd542-f8c4-463a-942f-833529b36283",
"metadata": {},
"outputs": [],
"source": [
"from htb_ai_library import (\n",
" set_reproducibility, use_htb_style,\n",
" MLP, AttackModel,\n",
" load_adult_census,\n",
" train_fixed_epochs, train_with_early_stopping, evaluate_model,\n",
" get_model_predictions, prepare_attack_data, create_dataloader,\n",
" plot_training_history, plot_overfitting_gap, plot_confidence_distributions,\n",
" plot_shadow_confidence_distributions, plot_attack_roc_curve, plot_precision_recall_curve,\n",
" plot_attack_accuracy_comparison, analyze_attack_decision_boundary, plot_decision_boundary,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "c3dd5b87-6ce5-429c-aee4-7bb74906e42f",
"metadata": {},
"outputs": [],
"source": [
"RANDOM_SEED = 1337\n",
"DEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
"set_reproducibility(RANDOM_SEED)\n",
"use_htb_style()"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "91df6714-97d6-42f4-be6d-cb13c129ce54",
"metadata": {},
"outputs": [],
"source": [
"OUTPUT_DIR = \"output\"\n",
"MODEL_DIR = f\"{OUTPUT_DIR}/models\"\n",
"FIGS_DIR = \"figs\"\n",
"FIG_PREFIX = \"Introduction_\"\n",
"os.makedirs(MODEL_DIR, exist_ok=True)\n",
"os.makedirs(FIGS_DIR, exist_ok=True)\n",
"\n",
"DATASET_CONFIG = {\n",
" \"num_classes\": 2,\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "b886c546-96d9-4e6d-a624-e04ba13135cd",
"metadata": {},
"outputs": [],
"source": [
"TARGET_MODEL_CONFIG = {\n",
" \"hidden_layers\": [256, 128],\n",
" \"dropout\": 0.0, # No dropout to maximize overfitting\n",
" \"epochs\": 100,\n",
" \"batch_size\": 32,\n",
" \"learning_rate\": 0.001,\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "3a8e92bd-28b2-4fee-98d3-52e046d6460a",
"metadata": {},
"outputs": [],
"source": [
"SHADOW_MODEL_CONFIG = {\n",
" \"num_shadow_models\": 5,\n",
" \"hidden_layers\": [128, 64],\n",
" \"dropout\": 0.3,\n",
" \"epochs\": 100,\n",
" \"batch_size\": 64,\n",
" \"learning_rate\": 0.001,\n",
" \"early_stopping_patience\": 10,\n",
" \"shadow_data_size\": 0.5,\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "9d74286a-4dd8-433d-83b2-cedea64f286b",
"metadata": {},
"outputs": [],
"source": [
"ATTACK_MODEL_CONFIG = {\n",
" \"hidden_layers\": [64, 32],\n",
" \"dropout\": 0.2,\n",
" \"epochs\": 100,\n",
" \"batch_size\": 128,\n",
" \"learning_rate\": 0.001,\n",
" \"early_stopping_patience\": 15,\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "7c71ab82-08fa-4d58-8dcf-d788348118d7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loading Adult Census dataset...\n",
"Dataset loaded: 14 features\n",
" Target training (members): 24421 samples\n",
" Shadow training: 12210 samples\n",
" Attack evaluation (non-members): 12211 samples\n"
]
}
],
"source": [
"print(\"Loading Adult Census dataset...\")\n",
"X_target, y_target, X_shadow, y_shadow, X_attack_eval, y_attack_eval, num_features = load_adult_census(\n",
" random_state=RANDOM_SEED\n",
")\n",
"\n",
"print(f\"Dataset loaded: {num_features} features\")\n",
"print(f\" Target training (members): {len(X_target)} samples\")\n",
"print(f\" Shadow training: {len(X_shadow)} samples\")\n",
"print(f\" Attack evaluation (non-members): {len(X_attack_eval)} samples\")"
]
},
{
"cell_type": "markdown",
"id": "955d799e-85d0-4a64-b80c-6b6de8a78e9f",
"metadata": {},
"source": [
"# Training the Target Model\n",
"\n",
"The target model is the victim we will attack. We train it to deliberately overfit, starting with data preparation:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "62eee3d5-7cf0-4879-8903-8863920a1ee6",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"============================================================\n",
"Training Target Model\n",
"============================================================\n",
"Architecture: 14 -> [256, 128] -> 2\n",
"Training for 100 epochs (no early stopping)\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Training: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 100/100 [00:47<00:00, 2.08it/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Target Model Performance:\n",
" Training Accuracy: 0.9373\n",
" Test Accuracy: 0.8247\n",
" Overfitting Gap: 0.1125\n"
]
}
],
"source": [
"print(\"\\n\" + \"=\" * 60)\n",
"print(\"Training Target Model\")\n",
"print(\"=\" * 60)\n",
"\n",
"scaler = StandardScaler()\n",
"X_target_norm = scaler.fit_transform(X_target)\n",
"X_attack_eval_norm = scaler.transform(X_attack_eval)\n",
"\n",
"train_loader = create_dataloader(X_target_norm, y_target, TARGET_MODEL_CONFIG['batch_size'])\n",
"test_loader = create_dataloader(X_attack_eval_norm, y_attack_eval,\n",
" TARGET_MODEL_CONFIG['batch_size'], shuffle=False)\n",
"\n",
"target_model = MLP(\n",
" input_size=num_features,\n",
" hidden_layers=TARGET_MODEL_CONFIG['hidden_layers'],\n",
" num_classes=DATASET_CONFIG['num_classes'],\n",
" dropout=TARGET_MODEL_CONFIG['dropout']\n",
")\n",
"\n",
"print(f\"Architecture: {num_features} -> {TARGET_MODEL_CONFIG['hidden_layers']} -> 2\")\n",
"print(f\"Training for {TARGET_MODEL_CONFIG['epochs']} epochs (no early stopping)\")\n",
"\n",
"history = train_fixed_epochs(\n",
" target_model, train_loader, test_loader,\n",
" device=DEVICE,\n",
" epochs=TARGET_MODEL_CONFIG['epochs'],\n",
" learning_rate=TARGET_MODEL_CONFIG['learning_rate']\n",
")\n",
"\n",
"train_acc, _, _ = evaluate_model(target_model, train_loader, DEVICE)\n",
"test_acc, _, _ = evaluate_model(target_model, test_loader, DEVICE)\n",
"\n",
"print(f\"\\nTarget Model Performance:\")\n",
"print(f\" Training Accuracy: {train_acc:.4f}\")\n",
"print(f\" Test Accuracy: {test_acc:.4f}\")\n",
"print(f\" Overfitting Gap: {train_acc - test_acc:.4f}\")\n",
"\n",
"plot_overfitting_gap(train_acc, test_acc,\n",
" save_path=os.path.join(FIGS_DIR, f\"{FIG_PREFIX}overfitting_gap.png\"))\n",
"\n",
"def predict_proba(self, x):\n",
" logits = self.forward(x)\n",
" return F.softmax(logits, dim=1)"
]
},
{
"cell_type": "markdown",
"id": "e0792f6a-cac3-44a7-9ea4-3f4e3d8d8f84",
"metadata": {},
"source": [
"# Traing Shadow Models"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "dc440eff-452b-4712-9e4b-844a7e0aa543",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"============================================================\n",
"Training Shadow Models\n",
"============================================================\n",
"\n",
"Created 5 shadow model data splits\n",
"Samples per shadow model: ~6105 in, ~6105 out\n",
"\n",
"Training Shadow Model 1/5\n",
" Shadow 1 - Train Acc: 0.8572, Out Acc: 0.8488\n",
"\n",
"Training Shadow Model 2/5\n",
" Shadow 2 - Train Acc: 0.8624, Out Acc: 0.8491\n",
"\n",
"Training Shadow Model 3/5\n",
" Shadow 3 - Train Acc: 0.8598, Out Acc: 0.8531\n",
"\n",
"Training Shadow Model 4/5\n",
" Shadow 4 - Train Acc: 0.8678, Out Acc: 0.8460\n",
"\n",
"Training Shadow Model 5/5\n",
" Shadow 5 - Train Acc: 0.8690, Out Acc: 0.8450\n",
"\n",
"Total attack training samples: 61050\n",
" Members: 30525\n",
" Non-members: 30525\n"
]
}
],
"source": [
"print(\"\\n\" + \"=\" * 60)\n",
"print(\"Training Shadow Models\")\n",
"print(\"=\" * 60)\n",
"\n",
"shadow_splits = []\n",
"for i in range(SHADOW_MODEL_CONFIG['num_shadow_models']):\n",
" seed = RANDOM_SEED + i\n",
" X_train_s, X_out_s, y_train_s, y_out_s = train_test_split(\n",
" X_shadow, y_shadow, train_size=SHADOW_MODEL_CONFIG['shadow_data_size'],\n",
" random_state=seed, stratify=y_shadow\n",
" )\n",
" shadow_splits.append((X_train_s, X_out_s, y_train_s, y_out_s))\n",
"\n",
"print(f\"\\nCreated {len(shadow_splits)} shadow model data splits\")\n",
"print(f\"Samples per shadow model: ~{len(shadow_splits[0][0])} in, ~{len(shadow_splits[0][1])} out\")\n",
"\n",
"all_attack_X = []\n",
"all_attack_y = []\n",
"all_preds_in = []\n",
"all_preds_out = []\n",
"\n",
"for i, (X_train_s, X_out_s, y_train_s, y_out_s) in enumerate(shadow_splits):\n",
" print(f\"\\nTraining Shadow Model {i+1}/{SHADOW_MODEL_CONFIG['num_shadow_models']}\")\n",
"\n",
" # Normalize using target scaler for transferability\n",
" X_train_s_norm = scaler.transform(X_train_s)\n",
" X_out_s_norm = scaler.transform(X_out_s)\n",
"\n",
" # Create validation split for early stopping\n",
" X_tr_s, X_val_s, y_tr_s, y_val_s = train_test_split(\n",
" X_train_s_norm, y_train_s, test_size=0.2,\n",
" random_state=RANDOM_SEED + i, stratify=y_train_s\n",
" )\n",
" train_loader_s = create_dataloader(X_tr_s, y_tr_s, SHADOW_MODEL_CONFIG['batch_size'])\n",
" val_loader_s = create_dataloader(X_val_s, y_val_s, SHADOW_MODEL_CONFIG['batch_size'], shuffle=False)\n",
"\n",
" # Initialize and train shadow model\n",
" shadow_model = MLP(\n",
" input_size=num_features,\n",
" hidden_layers=SHADOW_MODEL_CONFIG['hidden_layers'],\n",
" num_classes=DATASET_CONFIG['num_classes'],\n",
" dropout=SHADOW_MODEL_CONFIG['dropout']\n",
" )\n",
" train_with_early_stopping(\n",
" shadow_model, train_loader_s, val_loader_s,\n",
" device=DEVICE,\n",
" epochs=SHADOW_MODEL_CONFIG['epochs'],\n",
" learning_rate=SHADOW_MODEL_CONFIG['learning_rate'],\n",
" patience=SHADOW_MODEL_CONFIG['early_stopping_patience'],\n",
" verbose=False\n",
" )\n",
"\n",
" # Collect predictions on members and non-members\n",
" preds_in = get_model_predictions(shadow_model, X_train_s_norm, DEVICE)\n",
" preds_out = get_model_predictions(shadow_model, X_out_s_norm, DEVICE)\n",
"\n",
" # Transform to attack features and accumulate\n",
" attack_X_s, attack_y_s = prepare_attack_data(preds_in, preds_out, y_train_s, y_out_s)\n",
" all_attack_X.append(attack_X_s)\n",
" all_attack_y.append(attack_y_s)\n",
" all_preds_in.append(preds_in)\n",
" all_preds_out.append(preds_out)\n",
"\n",
" # Verify overfitting gap exists\n",
" full_train_loader_s = create_dataloader(X_train_s_norm, y_train_s,\n",
" SHADOW_MODEL_CONFIG['batch_size'], shuffle=False)\n",
" full_out_loader_s = create_dataloader(X_out_s_norm, y_out_s,\n",
" SHADOW_MODEL_CONFIG['batch_size'], shuffle=False)\n",
" train_acc_s, _, _ = evaluate_model(shadow_model, full_train_loader_s, DEVICE)\n",
" out_acc_s, _, _ = evaluate_model(shadow_model, full_out_loader_s, DEVICE)\n",
" print(f\" Shadow {i+1} - Train Acc: {train_acc_s:.4f}, Out Acc: {out_acc_s:.4f}\")\n",
"\n",
"attack_X = np.concatenate(all_attack_X, axis=0)\n",
"attack_y = np.concatenate(all_attack_y, axis=0)\n",
"\n",
"print(f\"\\nTotal attack training samples: {len(attack_X)}\")\n",
"print(f\" Members: {np.sum(attack_y == 1)}\")\n",
"print(f\" Non-members: {np.sum(attack_y == 0)}\")"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "40179133-a5a9-404c-be03-2e156e8422d4",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Attack Data Statistics:\n",
" Member confidence - Mean: 0.8553, Std: 0.1597\n",
" Non-member confidence - Mean: 0.8553, Std: 0.1600\n",
" Confidence gap: -0.0000\n"
]
}
],
"source": [
"member_confidences = attack_X[attack_y == 1, :2].max(axis=1)\n",
"non_member_confidences = attack_X[attack_y == 0, :2].max(axis=1)\n",
"\n",
"print(f\"\\nAttack Data Statistics:\")\n",
"print(f\" Member confidence - Mean: {member_confidences.mean():.4f}, Std: {member_confidences.std():.4f}\")\n",
"print(f\" Non-member confidence - Mean: {non_member_confidences.mean():.4f}, Std: {non_member_confidences.std():.4f}\")\n",
"print(f\" Confidence gap: {member_confidences.mean() - non_member_confidences.mean():.4f}\")"
]
},
{
"cell_type": "markdown",
"id": "9ab6b609-2648-4ae0-8a0d-807c782c1887",
"metadata": {},
"source": [
"# Building the Attack Classifier"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "e3bd9859-2567-4807-a8d7-f77cd4bb101f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"============================================================\n",
"Training Attack Model\n",
"============================================================\n",
"\n",
"Attack data split:\n",
" Training + Validation: 48840 samples\n",
" Test: 12210 samples\n"
]
}
],
"source": [
"print(\"\\n\" + \"=\" * 60)\n",
"print(\"Training Attack Model\")\n",
"print(\"=\" * 60)\n",
"\n",
"X_attack_train, X_attack_test, y_attack_train, y_attack_test = train_test_split(\n",
" attack_X, attack_y, test_size=0.2, random_state=RANDOM_SEED, stratify=attack_y\n",
")\n",
"\n",
"print(f\"\\nAttack data split:\")\n",
"print(f\" Training + Validation: {len(X_attack_train)} samples\")\n",
"print(f\" Test: {len(X_attack_test)} samples\")"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "9df5f8d5-ad89-431b-bec3-a8722f08d727",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" Training: 39072 samples\n",
" Validation: 9768 samples\n"
]
}
],
"source": [
"X_attack_tr, X_attack_val, y_attack_tr, y_attack_val = train_test_split(\n",
" X_attack_train, y_attack_train, test_size=0.2, random_state=RANDOM_SEED, stratify=y_attack_train\n",
")\n",
"\n",
"print(f\" Training: {len(X_attack_tr)} samples\")\n",
"print(f\" Validation: {len(X_attack_val)} samples\")"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "1b62b351-1697-4f77-8671-52cf37ac0348",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"DataLoaders created with batch size 128\n"
]
}
],
"source": [
"attack_train_loader = create_dataloader(X_attack_tr, y_attack_tr, ATTACK_MODEL_CONFIG['batch_size'])\n",
"attack_val_loader = create_dataloader(X_attack_val, y_attack_val, ATTACK_MODEL_CONFIG['batch_size'], shuffle=False)\n",
"attack_test_loader = create_dataloader(X_attack_test, y_attack_test, ATTACK_MODEL_CONFIG['batch_size'], shuffle=False)\n",
"\n",
"print(f\"\\nDataLoaders created with batch size {ATTACK_MODEL_CONFIG['batch_size']}\")"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "61b56511-d196-484b-8ce9-569342585af3",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Attack model architecture: 4 -> [64, 32] -> 2\n",
"Dropout: 0.2\n",
"\n",
"Training attack model...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Training: 34%|██████████████████████████████████████████████████████████████▌ | 34/100 [00:08<00:17, 3.80it/s, train_loss=0.6926, val_loss=0.6923, val_acc=0.5081]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Early stopping at epoch 35\n"
]
}
],
"source": [
"attack_input_size = attack_X.shape[1]\n",
"attack_model = AttackModel(\n",
" input_size=attack_input_size,\n",
" hidden_layers=ATTACK_MODEL_CONFIG['hidden_layers'],\n",
" dropout=ATTACK_MODEL_CONFIG['dropout']\n",
")\n",
"\n",
"print(f\"\\nAttack model architecture: {attack_input_size} -> {ATTACK_MODEL_CONFIG['hidden_layers']} -> 2\")\n",
"print(f\"Dropout: {ATTACK_MODEL_CONFIG['dropout']}\")\n",
"\n",
"print(\"\\nTraining attack model...\")\n",
"\n",
"history_attack = train_with_early_stopping(\n",
" attack_model, attack_train_loader, attack_val_loader,\n",
" device=DEVICE,\n",
" epochs=ATTACK_MODEL_CONFIG['epochs'],\n",
" learning_rate=ATTACK_MODEL_CONFIG['learning_rate'],\n",
" patience=ATTACK_MODEL_CONFIG['early_stopping_patience']\n",
")\n",
"\n",
"plot_training_history(\n",
" history_attack,\n",
" \"Attack Model Training\",\n",
" save_path=os.path.join(FIGS_DIR, f\"{FIG_PREFIX}attack_training.png\")\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "5fd7747d-3b7b-4856-847a-dccf698567aa",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Attack Model Test Performance:\n",
" Accuracy: 0.5033\n",
" Samples: 12210\n",
"\n",
"Detailed Classification Report:\n",
" precision recall f1-score support\n",
"\n",
" Non-Member 0.5292 0.0593 0.1066 6105\n",
" Member 0.5017 0.9473 0.6560 6105\n",
"\n",
" accuracy 0.5033 12210\n",
" macro avg 0.5155 0.5033 0.3813 12210\n",
"weighted avg 0.5155 0.5033 0.3813 12210\n",
"\n",
"\n",
"Attack model saved to output/models/attack_model.pt\n"
]
}
],
"source": [
"attack_test_acc, attack_test_predictions, attack_test_probs = evaluate_model(attack_model, attack_test_loader, DEVICE)\n",
"\n",
"print(f\"\\nAttack Model Test Performance:\")\n",
"print(f\" Accuracy: {attack_test_acc:.4f}\")\n",
"print(f\" Samples: {len(attack_test_predictions)}\")\n",
"\n",
"print(\"\\nDetailed Classification Report:\")\n",
"print(classification_report(\n",
" y_attack_test,\n",
" attack_test_predictions,\n",
" target_names=['Non-Member', 'Member'],\n",
" digits=4\n",
"))\n",
"\n",
"# Save the attack model\n",
"attack_model_path = os.path.join(MODEL_DIR, \"attack_model.pt\")\n",
"torch.save(attack_model.state_dict(), attack_model_path)\n",
"print(f\"\\nAttack model saved to {attack_model_path}\")"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "8b97af19-12db-47d7-ac45-18d315636fc2",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Decision Boundary Analysis:\n",
" Class 0: Membership threshold at confidence ~0.535\n",
" Class 1: Membership threshold at confidence ~0.500\n"
]
}
],
"source": [
"boundary_analysis = analyze_attack_decision_boundary(attack_model, DEVICE)\n",
"\n",
"print(\"\\nDecision Boundary Analysis:\")\n",
"for cls, data in boundary_analysis.items():\n",
" threshold_idx = np.argmin(np.abs(data['membership_probs'] - 0.5))\n",
" threshold_conf = data['confidences'][threshold_idx]\n",
" print(f\" Class {cls}: Membership threshold at confidence ~{threshold_conf:.3f}\")"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "e2400dfe-c68b-4226-aea9-927f675a539a",
"metadata": {},
"outputs": [],
"source": [
"plot_decision_boundary(\n",
" boundary_analysis,\n",
" save_path=os.path.join(FIGS_DIR, f\"{FIG_PREFIX}decision_boundary.png\")\n",
")"
]
},
{
"cell_type": "markdown",
"id": "7eb34d7a-5a50-4daa-8dcc-596462be8ade",
"metadata": {},
"source": [
"# Executing and Evaluating the Attack"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "54fc818c-3c91-4013-a9a7-8d5f44b77db0",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"============================================================\n",
"Executing Membership Inference Attack\n",
"============================================================\n",
"\n",
"Target model predictions collected:\n",
" Members: 24421 samples\n",
" Non-members: 12211 samples\n",
"\n",
"Attack input prepared:\n",
" Member features: (24421, 4)\n",
" Non-member features: (12211, 4)\n",
"\n",
"Total attack evaluation samples: 36632\n",
" Members: 24421\n",
" Non-members: 12211\n",
"\n",
"Attack predictions generated\n",
" Mean membership probability: 0.5000\n",
"\n",
"Membership Inference Attack Results:\n",
" Attack Accuracy: 0.6892\n",
" Attack Precision: 0.6898\n",
" Attack Recall: 0.9702\n",
" Attack F1 Score: 0.8063\n"
]
}
],
"source": [
"print(\"\\n\" + \"=\" * 60)\n",
"print(\"Executing Membership Inference Attack\")\n",
"print(\"=\" * 60)\n",
"\n",
"preds_members = get_model_predictions(target_model, X_target_norm, DEVICE)\n",
"preds_non_members = get_model_predictions(target_model, X_attack_eval_norm, DEVICE)\n",
"\n",
"print(f\"\\nTarget model predictions collected:\")\n",
"print(f\" Members: {len(preds_members)} samples\")\n",
"print(f\" Non-members: {len(preds_non_members)} samples\")\n",
"\n",
"attack_X_members, attack_y_members = prepare_attack_data(\n",
" preds_members, np.zeros((0, preds_members.shape[1])),\n",
" y_target, np.array([], dtype=np.int64)\n",
")\n",
"\n",
"attack_X_non_members, attack_y_non_members = prepare_attack_data(\n",
" np.zeros((0, preds_non_members.shape[1])), preds_non_members,\n",
" np.array([], dtype=np.int64), y_attack_eval\n",
")\n",
"\n",
"print(f\"\\nAttack input prepared:\")\n",
"print(f\" Member features: {attack_X_members.shape}\")\n",
"print(f\" Non-member features: {attack_X_non_members.shape}\")\n",
"\n",
"attack_X_eval = np.concatenate([attack_X_members, attack_X_non_members], axis=0)\n",
"attack_y_eval = np.concatenate([attack_y_members, attack_y_non_members], axis=0)\n",
"\n",
"print(f\"\\nTotal attack evaluation samples: {len(attack_X_eval)}\")\n",
"print(f\" Members: {np.sum(attack_y_eval == 1)}\")\n",
"print(f\" Non-members: {np.sum(attack_y_eval == 0)}\")\n",
"\n",
"attack_eval_loader = create_dataloader(attack_X_eval, attack_y_eval, ATTACK_MODEL_CONFIG['batch_size'], shuffle=False)\n",
"\n",
"_, attack_predictions, attack_probs = evaluate_model(attack_model, attack_eval_loader, DEVICE)\n",
"\n",
"membership_probs = attack_probs[:, 1]\n",
"\n",
"print(f\"\\nAttack predictions generated\")\n",
"print(f\" Mean membership probability: {membership_probs.mean():.4f}\")\n",
"\n",
"attack_accuracy = accuracy_score(attack_y_eval, attack_predictions)\n",
"attack_precision = precision_score(attack_y_eval, attack_predictions)\n",
"attack_recall = recall_score(attack_y_eval, attack_predictions)\n",
"attack_f1 = f1_score(attack_y_eval, attack_predictions)\n",
"\n",
"print(f\"\\nMembership Inference Attack Results:\")\n",
"print(f\" Attack Accuracy: {attack_accuracy:.4f}\")\n",
"print(f\" Attack Precision: {attack_precision:.4f}\")\n",
"print(f\" Attack Recall: {attack_recall:.4f}\")\n",
"print(f\" Attack F1 Score: {attack_f1:.4f}\")"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "4aeaf5c4-87f3-4d42-a503-2b1768a28747",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Results stored for visualization\n",
"\n",
"============================================================\n",
"Generating Visualizations\n",
"============================================================\n",
"Attack AUC: 0.5731\n",
"\n",
"Results saved to figs/Introduction_attack_results.json\n"
]
}
],
"source": [
"results = {\n",
" 'attack_accuracy': attack_accuracy,\n",
" 'attack_precision': attack_precision,\n",
" 'attack_recall': attack_recall,\n",
" 'attack_f1': attack_f1,\n",
" 'attack_y_true': attack_y_eval,\n",
" 'attack_y_pred': attack_predictions,\n",
" 'attack_probs': membership_probs,\n",
" 'confidence_members': np.max(preds_members, axis=1),\n",
" 'confidence_non_members': np.max(preds_non_members, axis=1),\n",
"}\n",
"\n",
"print(\"\\nResults stored for visualization\")\n",
"\n",
"print(\"\\n\" + \"=\" * 60)\n",
"print(\"Generating Visualizations\")\n",
"print(\"=\" * 60)\n",
"\n",
"auc_score = plot_attack_roc_curve(\n",
" results['attack_y_true'],\n",
" results['attack_probs'],\n",
" save_path=os.path.join(FIGS_DIR, f\"{FIG_PREFIX}attack_roc.png\")\n",
")\n",
"results['attack_auc'] = auc_score\n",
"\n",
"print(f\"Attack AUC: {auc_score:.4f}\")\n",
"\n",
"plot_precision_recall_curve(\n",
" results['attack_y_true'],\n",
" results['attack_probs'],\n",
" save_path=os.path.join(FIGS_DIR, f\"{FIG_PREFIX}attack_pr.png\")\n",
")\n",
"\n",
"plot_attack_accuracy_comparison(\n",
" results,\n",
" save_path=os.path.join(FIGS_DIR, f\"{FIG_PREFIX}attack_metrics.png\")\n",
")\n",
"\n",
"output = {\n",
" 'target_model': {\n",
" 'train_accuracy': float(train_acc),\n",
" 'test_accuracy': float(test_acc),\n",
" 'overfitting_gap': float(train_acc - test_acc),\n",
" },\n",
" 'attack_results': {\n",
" 'accuracy': float(results['attack_accuracy']),\n",
" 'precision': float(results['attack_precision']),\n",
" 'recall': float(results['attack_recall']),\n",
" 'f1_score': float(results['attack_f1']),\n",
" 'auc': float(results['attack_auc']),\n",
" 'advantage': float(results['attack_accuracy'] - 0.5),\n",
" },\n",
" 'configuration': {\n",
" 'random_seed': RANDOM_SEED,\n",
" 'num_shadow_models': SHADOW_MODEL_CONFIG['num_shadow_models'],\n",
" 'target_architecture': TARGET_MODEL_CONFIG['hidden_layers'],\n",
" 'attack_architecture': ATTACK_MODEL_CONFIG['hidden_layers'],\n",
" }\n",
"}\n",
"\n",
"results_path = os.path.join(FIGS_DIR, f\"{FIG_PREFIX}attack_results.json\")\n",
"with open(results_path, 'w') as f:\n",
" json.dump(output, f, indent=2)\n",
"\n",
"print(f\"\\nResults saved to {results_path}\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}