added material
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
<meta HTTP-EQUIV="REFRESH" content="0; url=http://www.cs.toronto.edu/~kriz/cifar.html">
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
After Width: | Height: | Size: 68 KiB |
|
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
|
||||
]
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 246 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 87 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||