18 Lab 18: Ethical AI: The Rights of the Mind
18.1 Learning Objectives
- Understand core concepts from Chapter 18
- Implement key privacy-preserving algorithms and techniques
- Apply methods to practical neural data protection problems
- Analyze results and draw insights about privacy-utility tradeoffs
- Connect to broader NeuroAI themes
18.2 Prerequisites
- Reading: Chapter 18: Ethical AI
- Libraries: NumPy, Matplotlib, hashlib, uuid
- Concepts: Fairness and responsibility, differential privacy, de-identification
18.3 Setup
import numpy as np
import matplotlib.pyplot as plt
import hashlib
import uuid
np.random.seed(42)
plt.rcParams['figure.figsize'] = (12, 8)18.4 Part 1: Neural Data Protection Pipeline
This section implements a comprehensive privacy-preserving pipeline for neural data, demonstrating the three key steps: consent verification, de-identification, and differential privacy.
18.4.1 Exercise 1: Complete Neural Data Protection
Run this cell to implement the full neural data protection pipeline from Chapter 18.
def demonstrate_neural_data_protection(neural_data, metadata, consent_given=True, purpose="medical_diagnosis"):
"""
A simplified demonstration of key privacy-preserving steps for neural data.
This function implements the three core privacy protection steps:
1. Consent and purpose verification
2. De-identification using Safe Harbor method
3. Differential privacy with Laplace mechanism
"""
# 1. Verify Consent and Purpose
if not consent_given:
raise PermissionError("Consent not given for data processing.")
authorized_purposes = ["medical_diagnosis", "approved_research"]
if purpose not in authorized_purposes:
raise ValueError(f"Purpose '{purpose}' is not authorized.")
print(f"Step 1: Consent and purpose verified for '{purpose}'.")
# 2. De-identify the data using the "Safe Harbor" method
# Remove direct personal identifiers from metadata
deidentified_metadata = metadata.copy()
phi_fields = ["name", "mrn", "dob", "address", "ssn"]
for field in phi_fields:
if field in deidentified_metadata:
del deidentified_metadata[field]
# Create a new, irreversible research ID
if "patient_id" in deidentified_metadata:
salt = uuid.uuid4().hex
hash_obj = hashlib.sha256((str(deidentified_metadata["patient_id"]) + salt).encode())
deidentified_metadata["research_id"] = hash_obj.hexdigest()
del deidentified_metadata["patient_id"]
print("Step 2: Metadata de-identified. Personal identifiers removed.")
# 3. Apply Differential Privacy to the neural data itself
# This adds mathematical noise to ensure an individual's data cannot be re-identified
epsilon = 0.1 # Privacy budget (lower is more private)
sensitivity = np.max(neural_data) - np.min(neural_data)
scale = sensitivity / epsilon
noise = np.random.laplace(0, scale, neural_data.shape)
private_data = neural_data + noise
print(f"Step 3: Differential privacy applied with epsilon={epsilon}.")
return private_data, deidentified_metadata
# Example Usage
# Raw data
raw_eeg_data = np.random.randn(10, 128) # 10 seconds of 128-channel EEG
patient_metadata = {
"name": "Jane Doe", "mrn": "12345", "patient_id": 9876, "age": 45,
"diagnosis": "Epilepsy"
}
# Apply protection
private_eeg, private_meta = demonstrate_neural_data_protection(raw_eeg_data, patient_metadata)
print("Final Private Metadata:", private_meta)Discussion Questions: 1. Why is consent verification the first step in the pipeline? 2. What is the purpose of the salt in the SHA-256 hash? 3. How does the epsilon parameter affect the privacy-utility tradeoff?
18.5 Part 2: Differential Privacy Deep Dive
This section explores differential privacy in more detail, focusing on the mathematical foundations of adding Laplace noise to neural data.
18.5.1 Exercise 2: Differential Privacy Implementation
Run this cell to implement the core differential privacy mechanism.
def compute_global_sensitivity(neural_data):
"""
Compute the global sensitivity of neural data.
Sensitivity is the maximum change that a single individual's data
can have on the output of a query.
"""
return np.max(neural_data) - np.min(neural_data)
def add_differential_privacy(neural_data, epsilon=0.1):
"""
Add Laplace noise to neural data to achieve differential privacy.
Args:
neural_data: Raw EEG/fMRI/spike train data
epsilon: Privacy budget (smaller = more private)
Returns:
Privatized neural data
"""
sensitivity = compute_global_sensitivity(neural_data)
scale = sensitivity / epsilon
noise = np.random.laplace(0, scale, neural_data.shape)
return neural_data + noise
# Test the function with different epsilon values
print("Testing differential privacy with different epsilon values:\n")
epsilons = [0.01, 0.1, 1.0, 10.0]
test_data = np.random.randn(5, 10)
for eps in epsilons:
private_data = add_differential_privacy(test_data, epsilon=eps)
noise_added = np.mean(np.abs(private_data - test_data))
print(f"Epsilon = {eps:5.2f}: Mean absolute noise = {noise_added:.4f}")18.5.2 Exercise 3: Epsilon Tradeoff Analysis
Visualize the privacy-utility tradeoff by varying epsilon.
def analyze_epsilon_tradeoff(neural_data, epsilon_values=None):
"""
Analyze the tradeoff between privacy and data utility.
Args:
neural_data: Raw neural data
epsilon_values: List of epsilon values to test
Returns:
Dictionary with analysis results
"""
if epsilon_values is None:
epsilon_values = [0.01, 0.05, 0.1, 0.5, 1.0, 2.0, 5.0, 10.0]
results = {'epsilon': [], 'noise_magnitude': [], 'snr': []}
signal_power = np.mean(neural_data ** 2)
for eps in epsilon_values:
private_data = add_differential_privacy(neural_data, epsilon=eps)
noise = private_data - neural_data
noise_magnitude = np.mean(np.abs(noise))
noise_power = np.mean(noise ** 2)
snr = signal_power / noise_power if noise_power > 0 else float('inf')
results['epsilon'].append(eps)
results['noise_magnitude'].append(noise_magnitude)
results['snr'].append(snr)
return results
# Generate test data and analyze
test_eeg = np.random.randn(100, 64) # 100 samples, 64 channels
results = analyze_epsilon_tradeoff(test_eeg)
# Plot the results
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Plot 1: Noise magnitude vs epsilon
axes[0].semilogx(results['epsilon'], results['noise_magnitude'], 'b-o', linewidth=2)
axes[0].set_xlabel('Epsilon (Privacy Budget)', fontsize=12)
axes[0].set_ylabel('Mean Absolute Noise', fontsize=12)
axes[0].set_title('Noise Magnitude vs. Privacy Budget', fontsize=14)
axes[0].grid(True, alpha=0.3)
# Plot 2: Signal-to-Noise Ratio vs epsilon
axes[1].semilogx(results['epsilon'], results['snr'], 'r-o', linewidth=2)
axes[1].set_xlabel('Epsilon (Privacy Budget)', fontsize=12)
axes[1].set_ylabel('Signal-to-Noise Ratio (dB)', fontsize=12)
axes[1].set_title('Data Utility vs. Privacy Budget', fontsize=14)
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print("\nKey Insight: Lower epsilon = stronger privacy but more noise (lower utility)")18.6 Part 3: Hands-On Implementation
Practical exercises exploring fairness and responsibility in NeuroAI.
18.6.1 Exercise 4: Bias Audit Simulation
Create a synthetic dataset and simulate bias in neural diagnostic outcomes.
def simulate_bias_audit(n_samples=1000, bias_factor=0.2):
"""
Simulate a bias audit for a neural diagnostic model.
Args:
n_samples: Total number of samples
bias_factor: How much bias to introduce (0 = no bias)
Returns:
Dictionary with bias audit results
"""
# Create two demographic groups (e.g., Group A and Group B)
n_group_a = int(n_samples * 0.7) # 70% in majority group
n_group_b = n_samples - n_group_a
# Generate true labels (balanced)
y_true_a = np.random.randint(0, 2, n_group_a)
y_true_b = np.random.randint(0, 2, n_group_b)
# Generate predictions with bias against Group B
accuracy_a = 0.90
accuracy_b = 0.90 - bias_factor # Lower accuracy for minority group
y_pred_a = np.where(np.random.random(n_group_a) < accuracy_a,
y_true_a, 1 - y_true_a)
y_pred_b = np.where(np.random.random(n_group_b) < accuracy_b,
y_true_b, 1 - y_true_b)
# Calculate metrics
def calc_metrics(y_true, y_pred):
tp = np.sum((y_true == 1) & (y_pred == 1))
tn = np.sum((y_true == 0) & (y_pred == 0))
fp = np.sum((y_true == 0) & (y_pred == 1))
fn = np.sum((y_true == 1) & (y_pred == 0))
accuracy = (tp + tn) / (tp + tn + fp + fn)
fpr = fp / (fp + tn) if (fp + tn) > 0 else 0
fnr = fn / (fn + tp) if (fn + tp) > 0 else 0
return {'accuracy': accuracy, 'fpr': fpr, 'fnr': fnr}
metrics_a = calc_metrics(y_true_a, y_pred_a)
metrics_b = calc_metrics(y_true_b, y_pred_b)
return {
'Group A (Majority)': metrics_a,
'Group B (Minority)': metrics_b,
'Accuracy Gap': metrics_a['accuracy'] - metrics_b['accuracy']
}
# Run bias audit simulation
print("=== Bias Audit Simulation ===\n")
results = simulate_bias_audit(bias_factor=0.15)
for group, metrics in results.items():
if isinstance(metrics, dict):
print(f"{group}:")
print(f" Accuracy: {metrics['accuracy']:.3f}")
print(f" False Positive Rate: {metrics['fpr']:.3f}")
print(f" False Negative Rate: {metrics['fnr']:.3f}\n")
else:
print(f"{group}: {metrics:.3f}")18.6.2 Exercise 5: Consent Management System
Implement a simple consent management system for BCI applications.
class ConsentManager:
"""
A simple consent management system for neural data.
Tracks consent for different purposes and enforces purpose limitation.
"""
def __init__(self):
self.consents = {} # user_id -> {purpose: granted}
self.access_log = [] # List of access attempts
def grant_consent(self, user_id, purpose):
"""Grant consent for a specific purpose."""
if user_id not in self.consents:
self.consents[user_id] = {}
self.consents[user_id][purpose] = True
print(f"Consent granted for user {user_id}: {purpose}")
def revoke_consent(self, user_id, purpose):
"""Revoke consent for a specific purpose."""
if user_id in self.consents and purpose in self.consents[user_id]:
self.consents[user_id][purpose] = False
print(f"Consent revoked for user {user_id}: {purpose}")
def check_consent(self, user_id, purpose):
"""Check if consent is granted for a specific purpose."""
has_consent = (user_id in self.consents and
self.consents[user_id].get(purpose, False))
# Log the access attempt
self.access_log.append({
'user_id': user_id,
'purpose': purpose,
'granted': has_consent,
'timestamp': np.datetime64('now')
})
return has_consent
def get_access_log(self, user_id=None):
"""Get access log, optionally filtered by user."""
if user_id is None:
return self.access_log
return [log for log in self.access_log if log['user_id'] == user_id]
# Test the consent manager
print("=== Consent Management System Demo ===\n")
cm = ConsentManager()
# Grant consents
cm.grant_consent("patient_001", "medical_diagnosis")
cm.grant_consent("patient_001", "research")
cm.grant_consent("patient_002", "medical_diagnosis")
print("\n--- Checking Access ---")
print(f"Patient 001 - Medical: {cm.check_consent('patient_001', 'medical_diagnosis')}")
print(f"Patient 001 - Commercial: {cm.check_consent('patient_001', 'commercial')}")
print(f"Patient 002 - Research: {cm.check_consent('patient_002', 'research')}")
print("\n--- Revoking Consent ---")
cm.revoke_consent("patient_001", "research")
print(f"Patient 001 - Research (after revoke): {cm.check_consent('patient_001', 'research')}")
print("\n--- Access Log ---")
for entry in cm.get_access_log():
print(f" {entry['user_id']}: {entry['purpose']} -> {'GRANTED' if entry['granted'] else 'DENIED'}")18.7 Exercises
18.7.1 Exercise 1
Implement the basic differential privacy algorithm and test with different epsilon values.
18.7.2 Exercise 2
Explore parameter variations - how does the noise scale change with different data sensitivities?
18.7.3 Exercise 3
Apply to real or simulated neural data and analyze the privacy-utility tradeoff.
18.7.4 Exercise 4
Compare different approaches: differential privacy vs. k-anonymity vs. federated learning.
18.7.5 Exercise 5
Discuss NeuroAI connections - how do these privacy techniques apply to brain-computer interfaces?
18.8 Challenge Problems
18.8.1 Challenge 1: Advanced Privacy Techniques
Implement a federated learning simulation where multiple “hospitals” collaboratively train a model without sharing raw data.
18.8.2 Challenge 2: Reproduce Research Findings
Implement the complete bias detection and mitigation pipeline from the chapter’s Figure 18.2.
18.8.3 Challenge 3: Novel Application
Design a privacy-preserving neural data sharing system that combines differential privacy with secure multi-party computation.
18.9 Discussion Questions
18.9.1 Question 1
How does neural data privacy differ from traditional data privacy? What makes brain data unique?
18.9.2 Question 2
What are the AI implications of privacy-preserving techniques? How do they affect model performance?
18.9.3 Question 3
Future research directions - how might advances in quantum computing affect privacy guarantees?
18.10 Summary
Explored fairness and responsibility through theory and practice, implementing: - Complete neural data protection pipeline - Differential privacy with Laplace mechanism - Bias audit simulation - Consent management system
Key Takeaways: - Core privacy concepts mastered (consent, de-identification, differential privacy) - Practical implementation skills developed - NeuroAI connections understood (ethics, privacy, bias mitigation) - Privacy-utility tradeoffs analyzed and visualized