第一部分:代码实现
(1)基础配置
python:3.8,opacus1.1.1,torch1.12.1
pip install opacus==1.1.1
具体实现方式是参考opacus官网以及(4)的参考文献。
opacus官网为:
GitHub - pytorch/opacus:使用差分隐私训练 PyTorch 模型
(2)完整代码
# -*- coding: utf-8 -*-
# Step 1: 导入必要的库和模块
import torch # 导入PyTorch深度学习框架
import os # 导入操作系统模块
# 设置环境变量解决某些系统下的库冲突问题
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'
# 从torchvision导入数据集和数据处理工具
from torchvision import datasets, transforms
import numpy as np # 导入数值计算库
# 从Opacus导入差分隐私引擎
from opacus import PrivacyEngine
# 导入进度条工具
from tqdm import tqdm
# Step 2: 加载MNIST数据集
# 创建训练数据加载器
train_loader = torch.utils.data.DataLoader(
datasets.MNIST(
'../mnist', # 数据集存储路径
train=True, # 使用训练集
download=True, # 如果不存在则自动下载
transform=transforms.Compose([ # 数据预处理管道
transforms.ToTensor(), # 将PIL图像转换为Tensor
# 标准化处理(使用MNIST的均值和标准差)
transforms.Normalize((0.1307,), (0.3081,))
]),
),
batch_size=64, # 每批加载64个样本
shuffle=True, # 打乱数据顺序
num_workers=0, # 使用0个子进程加载数据
pin_memory=True # 将数据缓存在CUDA固定内存中(如果可用)
)
# 创建测试数据加载器(参数与训练集类似)
test_loader = torch.utils.data.DataLoader(
datasets.MNIST(
'../mnist',
train=False, # 使用测试集
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
]),
),
batch_size=1024, # 测试集使用更大的批大小
shuffle=True,
num_workers=0,
pin_memory=True
)
# Step 3: 构建神经网络模型和优化器
# 使用Sequential容器构建卷积神经网络
model = torch.nn.Sequential(
# 第一个卷积层:输入通道1,输出通道16,卷积核8x8,步长2,填充3
torch.nn.Conv2d(1, 16, 8, 2, padding=3),
torch.nn.ReLU(), # ReLU激活函数
# 最大池化层:窗口大小2x2,步长1
torch.nn.MaxPool2d(2, 1),
# 第二个卷积层:输入通道16,输出通道32,卷积核4x4,步长2
torch.nn.Conv2d(16, 32, 4, 2),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2, 1),
torch.nn.Flatten(), # 展平多维特征图为一维
# 全连接层:输入特征数32*4*4,输出32
torch.nn.Linear(32 * 4 * 4, 32),
torch.nn.ReLU(),
# 输出层:输入32,输出10(对应10个数字类别)
torch.nn.Linear(32, 10)
)
# 创建随机梯度下降优化器,学习率0.05
optimizer = torch.optim.SGD(model.parameters(), lr=0.05)
# Step 4: 添加差分隐私引擎
# 初始化隐私引擎,配置差分隐私参数
privacy_engine = PrivacyEngine()
model, optimizer, train_loader = privacy_engine.make_private(
module=model,
optimizer=optimizer,
data_loader=train_loader,
noise_multiplier=1.3, # 高斯噪声的乘数
max_grad_norm=1.0, # 梯度裁剪的阈值
)
# Step 5: 定义训练函数
def train(model, train_loader, optimizer, epoch, device, delta):
model.train() # 设置模型为训练模式
criterion = torch.nn.CrossEntropyLoss() # 定义交叉熵损失函数
losses = [] # 存储每批损失的列表
# 使用tqdm包装数据加载器以显示进度条
for _batch_idx, (data, target) in enumerate(tqdm(train_loader)):
# 将数据移动到指定设备(CPU/GPU)
data, target = data.to(device), target.to(device)
optimizer.zero_grad() # 清空梯度缓存
output = model(data) # 前向传播
loss = criterion(output, target) # 计算损失
loss.backward() # 反向传播计算梯度
optimizer.step() # 更新模型参数
losses.append(loss.item()) # 记录当前批的损失值
# 计算隐私预算(ε, δ)
epsilon = privacy_engine.accountant.get_epsilon(delta)
# 打印训练结果
print(
f"Train Epoch: {epoch} \t"
f"Loss: {np.mean(losses):.6f} "
f"(ε = {epsilon:.2f}, δ = {delta})"
)
# 进行10个epoch的训练
for epoch in range(1, 11):
train(model, train_loader, optimizer, epoch, device="cpu", delta=1e-5)
Secure RNG 警告:
E:\14anaconda\envs\ZjcPaper\lib\site-packages\opacus\privacy_engine.py:114: 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. warnings.warn(
这个警告表明安全随机数生成器(RNG)处于关闭状态。在实验阶段这是可以的,因为它能让训练速度更快,但在生产环境中使用时,需要开启安全模式(
secure_mode
)并重训模型以确保安全性。非完整反向传播钩子警告:
这个警告指出在正向传播包含多个自动求导节点时使用非完整的反向传播钩子已经被弃用,未来版本将会移除。
训练进度信息:
100%|██████████| 938/938 [00:25<00:00, 36.12it/s] Train Epoch: 1 Loss: 1.211915 (ε = 0.36, δ = 1e-05) ... Train Epoch: 10 Loss: 0.479933 (ε = 0.45, δ = 1e-05)
这些信息显示模型在每个轮次的训练情况,随着训练轮次的增加,损失值有所下降,这通常是一个好的迹象,表明模型在不断学习数据中的模式。同时,隐私预算(ε)随着轮次的增加而逐渐增大,这是差分隐私训练中的正常现象,因为随着训练的进行,隐私泄露的风险会逐渐增加。
ε和 δ含义:
参数 含义 与隐私效果的关系 ε(epsilon) 在差分隐私中,ε 是用来衡量隐私预算的参数,直观上表示两个相邻数据集在算法输出上的差异程度。它限制了攻击者通过观察输出结果来推断出关于特定个体数据信息的能力。 一般来说,ε 越小,隐私保护效果越好。因为较小的 ε 意味着即使攻击者获取了算法的输出,也很难区分不同数据集之间的差异,从而更难推断出个体数据的信息。 δ(delta) δ 也是差分隐私中的一个参数,它表示在差分隐私定义中允许的 “失败概率”。即在某些情况下,可能会稍微超出 ε 所规定的隐私保护范围,但这种情况发生的概率被限制在 δ 以内。 δ 越小,说明算法违反严格隐私保证的概率越低,通常也意味着更好的隐私效果。不过,δ 通常是一个很小的值,比如 1e - 5 等,在实际应用中需要结合 ε 一起考虑来评估隐私保护的强度。
(3)具体代码讲解
①导入必要的库和模块
# -*- coding: utf-8 -*-
# Step 1: 导入必要的库和模块
import torch # 导入PyTorch深度学习框架
import os # 导入操作系统模块
# 设置环境变量解决某些系统下的库冲突问题
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'
# 从torchvision导入数据集和数据处理工具
from torchvision import datasets, transforms
import numpy as np # 导入数值计算库
# 从Opacus导入差分隐私引擎
from opacus import PrivacyEngine
# 导入进度条工具
from tqdm import tqdm
②加载MNIST数据集
# Step 2: 加载MNIST数据集
# 创建训练数据加载器
train_loader = torch.utils.data.DataLoader(
datasets.MNIST(
'../mnist', # 数据集存储路径
train=True, # 使用训练集
download=True, # 如果不存在则自动下载
transform=transforms.Compose([ # 数据预处理管道
transforms.ToTensor(), # 将PIL图像转换为Tensor
# 标准化处理(使用MNIST的均值和标准差)
transforms.Normalize((0.1307,), (0.3081,))
]),
),
batch_size=64, # 每批加载64个样本
shuffle=True, # 打乱数据顺序
num_workers=0, # 使用0个子进程加载数据
pin_memory=True # 将数据缓存在CUDA固定内存中(如果可用)
)
# 创建测试数据加载器(参数与训练集类似)
test_loader = torch.utils.data.DataLoader(
datasets.MNIST(
'../mnist',
train=False, # 使用测试集
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
]),
),
batch_size=1024, # 测试集使用更大的批大小
shuffle=True,
num_workers=0,
pin_memory=True
)
③构建神经网络模型和优化器
# Step 3: 构建神经网络模型和优化器
# 使用Sequential容器构建卷积神经网络
model = torch.nn.Sequential(
# 第一个卷积层:输入通道1,输出通道16,卷积核8x8,步长2,填充3
torch.nn.Conv2d(1, 16, 8, 2, padding=3),
torch.nn.ReLU(), # ReLU激活函数
# 最大池化层:窗口大小2x2,步长1
torch.nn.MaxPool2d(2, 1),
# 第二个卷积层:输入通道16,输出通道32,卷积核4x4,步长2
torch.nn.Conv2d(16, 32, 4, 2),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2, 1),
torch.nn.Flatten(), # 展平多维特征图为一维
# 全连接层:输入特征数32*4*4,输出32
torch.nn.Linear(32 * 4 * 4, 32),
torch.nn.ReLU(),
# 输出层:输入32,输出10(对应10个数字类别)
torch.nn.Linear(32, 10)
)
# 创建随机梯度下降优化器,学习率0.05
optimizer = torch.optim.SGD(model.parameters(), lr=0.05)
④添加差分隐私引擎
# Step 4: 添加差分隐私引擎
# 初始化隐私引擎,配置差分隐私参数
privacy_engine = PrivacyEngine()
model, optimizer, train_loader = privacy_engine.make_private(
module=model,
optimizer=optimizer,
data_loader=train_loader,
noise_multiplier=1.3, # 高斯噪声的乘数
max_grad_norm=1.0, # 梯度裁剪的阈值
)
⑤定义训练函数
# Step 5: 定义训练函数
def train(model, train_loader, optimizer, epoch, device, delta):
model.train() # 设置模型为训练模式
criterion = torch.nn.CrossEntropyLoss() # 定义交叉熵损失函数
losses = [] # 存储每批损失的列表
# 使用tqdm包装数据加载器以显示进度条
for _batch_idx, (data, target) in enumerate(tqdm(train_loader)):
# 将数据移动到指定设备(CPU/GPU)
data, target = data.to(device), target.to(device)
optimizer.zero_grad() # 清空梯度缓存
output = model(data) # 前向传播
loss = criterion(output, target) # 计算损失
loss.backward() # 反向传播计算梯度
optimizer.step() # 更新模型参数
losses.append(loss.item()) # 记录当前批的损失值
# 计算隐私预算(ε, δ)
epsilon = privacy_engine.accountant.get_epsilon(delta)
# 打印训练结果
print(
f"Train Epoch: {epoch} \t"
f"Loss: {np.mean(losses):.6f} "
f"(ε = {epsilon:.2f}, δ = {delta})"
)
⑥主函数开始训练
# step6:主函数开始训练
# 进行10个epoch的训练
for epoch in range(1, 11):
train(model, train_loader, optimizer, epoch, device="cpu", delta=1e-5)