总结:我从可以玩的玩具代码中学习得最好。本教程使用 PyTorch 最近发布的名为 Opacus 的库(此处提供完整代码示例)来教授差异化私有深度学习。有关差异隐私的更多信息,您可以在 Twitter 上关注@kritipraks或@openminedorg。
我们 OpenMined 正在与 PyTorch 团队合作,以实现我们的共同使命,即让保护隐私的 AI 更容易构建。我们积极支持在我们的社区中使用 Opacus 以及它与我们正在构建的工具的集成。
差分隐私是隐私保护 AI 中最重要的工具之一,因为它允许我们训练 AI 模型,这些模型可以学习高级模式(例如如何识别癌性肿瘤),而无需记住敏感的个人数据(特定患者的独特属性) )。
这项技术成为主流意味着可用于对人类最重要和最敏感问题进行科学探究的训练数据量迅速增加。
这是关于如何使用PyTorch Opacus 库在 20 行代码中使用差异私有随机梯度下降优化器在 MNIST 数据集上训练简单 PyTorch 分类模型的分步教程。
Opacus是一个库,可以训练具有差异隐私的 PyTorch 模型。它支持对客户端进行最少代码更改的培训,对培训性能几乎没有影响,并允许客户端在线跟踪在任何给定时刻花费的隐私预算。
这篇博文将涵盖:
步骤 1:导入 PyTorch 和 Opacus
步骤 2:加载 MNIST 数据
步骤 3:创建 PyTorch 神经网络分类模型和优化器
步骤 4:将差分隐私引擎附加到优化器
步骤 5:在多个时期训练私有模型
先决知识:
1. PyTorch 中的基本深度学习(教程)
2. 安装 PyTorch
3. 安装PyTorch Opacus
第 1 步:导入 PyTorch 和 Opacus
对于 MNIST 上的私有图像分类,我们将需要使用 PyTorch、numpy
Opacus 和 Opacus。我们需要tqdm
可视化我们在训练过程中取得的进展。
import torch
from torchvision import datasets, transforms
import numpy as np
from opacus import PrivacyEngine
from tqdm import tqdm
第 2 步:加载 MNIST 数据
我们使用 a 加载 MNIST 数据并将其DataLoader
拆分为训练和测试数据集。数据被打乱,并使用数据集的平均值 (0.1307) 和标准偏差 (0.3081) 进行归一化。训练集分为每批 64 张图像,而测试集分为每批 1024 张图像。
train_loader = torch.utils.data.DataLoader(datasets.MNIST('../mnist',
train=True, download=True,
transform=transforms.Compose([transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,)),]),),
batch_size=64, shuffle=True, num_workers=1,
pin_memory=True)
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=1,
pin_memory=True)
第 3 步:创建 PyTorch 神经网络分类模型和优化器
现在,让我们创建一个Sequential
PyTorch 神经网络模型,该模型从我们的 MNIST 数据集中预测图像的标签。我们创建了一个由 2 个卷积层组成的简单网络,然后是 2 个完全连接的层,穿插有多个ReLu
和MaxPooling
层。第一层将大小的图像(28 x 28)
作为输入。最后一层返回一个大小为 的概率向量(1 x 10)
,以便我们可以预测图像代表哪个数字(从 0 到 9)。
最后,我们使用随机梯度下降 (SGD) 优化器来改进我们的分类模型,并将学习率设置为 0.05。
model = torch.nn.Sequential(torch.nn.Conv2d(1, 16, 8, 2, padding=3),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2, 1),
torch.nn.Conv2d(16, 32, 4, 2),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2, 1),
torch.nn.Flatten(),
torch.nn.Linear(32 * 4 * 4, 32),
torch.nn.ReLU(),
torch.nn.Linear(32, 10))
optimizer = torch.optim.SGD(model.parameters(), lr=0.05)
步骤 4:将差分隐私引擎附加到优化器
到目前为止,我们有一个常规的 PyTorch 分类模型。现在,我们想让我们的模型差异化私有。因此,我们创建PrivacyEngine
了 opacus 库中的一个并将其附加到我们的模型中,这将使我们的模型具有不同的私密性并帮助我们跟踪我们的隐私预算。
在sample_size
这里是训练集的大小,名单alphas
是在其仁义微分隐私计算不同的订单。
的noise_multiplier
是高斯噪声分布的标准偏差的比L2
其所加入的函数的灵敏度。
max_grad_norm
表示L2
损失梯度范数的上界值。
privacy_engine = PrivacyEngine(model,
batch_size=64,
sample_size=60000,
alphas=range(2,32),
noise_multiplier=1.3,
max_grad_norm=1.0,)
privacy_engine.attach(optimizer)
第 5 步:在多个时期训练私有模型
最后,我们创建了一个使用差分私有 SGD 优化器训练模型的函数。我们希望最小化交叉熵损失。
在单个训练步骤中,我们迭代所有训练数据批次,并使我们的模型预测数据的标签。基于这个预测,我们得到一个损失,我们使用 反向传播其梯度loss.backward()
。
现在隐私引擎带来的主要区别是向后传播的梯度的范数被剪裁为小于max_grad_norm
。这样做是为了确保数据的整体敏感性是有界的。
之后,对批次的梯度进行平均,并根据 的值将随机采样的高斯噪声添加到其中noise_multiplier
。这样做是为了确保该过程满足指定 alpha 订单值的 Renyi Differential Privacy 的保证。
最后,优化器使用 向与最大噪声梯度相反的方向迈出一步optimizer.step()
。我们将整个训练迭代重复 10 个时期,以减少整体损失。
def train(model, train_loader, optimizer, epoch, device, delta):
model.train()
criterion = torch.nn.CrossEntropyLoss()
losses = []
for _batch_idx, (data, target) in enumerate(tqdm(train_loader)):
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, best_alpha =
optimizer.privacy_engine.get_privacy_spent(delta)
print(
f"Train Epoch: {epoch} \t"
f"Loss: {np.mean(losses):.6f} "
f"(ε = {epsilon:.2f}, δ = {delta}) for α = {best_alpha}")
for epoch in range(1, 11):
train(model, train_loader, optimizer, epoch, device="cpu", delta=1e-5)
epsilon 和 delta 参数帮助我们确定高斯噪声分布的形状和大小。epsilon、delta 和 alpha 值共同为我们提供了 Renyi 差分隐私保证和我们花费的隐私预算的量化。
这就是 opacus 如何巧妙地为 PyTorch 模型添加对差分隐私的支持,通过将谨慎的隐私引擎附加到其模型,确保整个模型创建和训练过程完全相同。
把这一切放在一起
# Step 1: Importing PyTorch and Opacus
import torch
from torchvision import datasets, transforms
import numpy as np
from opacus import PrivacyEngine
from tqdm import tqdm
# Step 2: Loading MNIST Data
train_loader = torch.utils.data.DataLoader(datasets.MNIST('../mnist', train=True, download=True,
transform=transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,),
(0.3081,)),]),), batch_size=64, shuffle=True, num_workers=1, pin_memory=True)
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=1, pin_memory=True)
# Step 3: Creating a PyTorch Neural Network Classification Model and Optimizer
model = torch.nn.Sequential(torch.nn.Conv2d(1, 16, 8, 2, padding=3), torch.nn.ReLU(), torch.nn.MaxPool2d(2, 1),
torch.nn.Conv2d(16, 32, 4, 2), torch.nn.ReLU(), torch.nn.MaxPool2d(2, 1), torch.nn.Flatten(),
torch.nn.Linear(32 * 4 * 4, 32), torch.nn.ReLU(), torch.nn.Linear(32, 10))
optimizer = torch.optim.SGD(model.parameters(), lr=0.05)
# Step 4: Attaching a Differential Privacy Engine to the Optimizer
privacy_engine = PrivacyEngine(model, batch_size=64, sample_size=60000, alphas=range(2,32),
noise_multiplier=1.3, max_grad_norm=1.0,)
privacy_engine.attach(optimizer)
# Step 5: Training the private model over multiple epochs
def train(model, train_loader, optimizer, epoch, device, delta):
model.train()
criterion = torch.nn.CrossEntropyLoss()
losses = []
for _batch_idx, (data, target) in enumerate(tqdm(train_loader)):
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, best_alpha = optimizer.privacy_engine.get_privacy_spent(delta)
print(
f"Train Epoch: {epoch} \t"
f"Loss: {np.mean(losses):.6f} "
f"(ε = {epsilon:.2f}, δ = {delta}) for α = {best_alpha}")
for epoch in range(1, 11):
train(model, train_loader, optimizer, epoch, device="cpu", delta=1e-5)
- 这篇文章的作者是:
-
克里蒂卡·普拉卡什
OpenMined 的差分隐私负责人
https://blog.openmined.org/differentially-private-deep-learning-using-opacus-in-20-lines-of-code/