在 TensorFlow.org 上查看 | 在 Google Colab 中运行 | 在 GitHub 上查看源代码 | 下载笔记本 |
概述
差分隐私 (DP) 是一个用于衡量算法提供的隐私保证的框架。通过差分隐私的视角,您可以设计负责任地训练私有数据模型的机器学习算法。使用差分隐私学习提供了可衡量的隐私保证,有助于减轻机器学习中暴露敏感训练数据的风险。直观地说,使用差分隐私训练的模型不应受其数据集中任何单个训练示例或一小部分训练示例的影响。这有助于减轻 ML 中暴露敏感训练数据的风险。
这种方法的基本思想,称为差分私有随机梯度下降 (DP-SGD),是对随机梯度下降 (SGD) 中使用的梯度进行修改,而 SGD 是几乎所有深度学习算法的核心。使用 DP-SGD 训练的模型对其输入数据提供了可证明的差分隐私保证。对普通 SGD 算法进行了两种修改
- 首先,需要对每个梯度的敏感度进行限制。换句话说,您需要限制在小批量中采样的每个单个训练点对梯度计算和应用于模型参数的更新的影响。这可以通过对每个训练点计算的每个梯度进行裁剪来完成。
- 随机噪声被采样并添加到裁剪后的梯度中,以使统计上不可能通过比较 SGD 在训练数据集中包含或不包含此特定数据点时应用的更新来知道特定数据点是否包含在训练数据集中。
本教程使用 tf.keras 训练一个卷积神经网络 (CNN) 以使用 TensorFlow Privacy 库提供的 DP-SGD 优化器识别手写数字。TensorFlow Privacy 提供了包装现有 TensorFlow 优化器的代码,以创建一个实现 DP-SGD 的变体。
设置
首先导入必要的库
import tensorflow as tf
tf.compat.v1.disable_v2_behavior()
import numpy as np
tf.get_logger().setLevel('ERROR')
2022-12-12 10:43:10.411464: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory 2022-12-12 10:43:10.411665: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory 2022-12-12 10:43:10.411681: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly. WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.9/site-packages/tensorflow/python/compat/v2_compat.py:107: disable_resource_variables (from tensorflow.python.ops.variable_scope) is deprecated and will be removed in a future version. Instructions for updating: non-resource variables are not supported in the long term
安装 TensorFlow Privacy。
pip install tensorflow-privacy
import tensorflow_privacy
from tensorflow_privacy.privacy.analysis import compute_dp_sgd_privacy
加载和预处理数据集
加载 MNIST 数据集并准备数据进行训练。
train, test = tf.keras.datasets.mnist.load_data()
train_data, train_labels = train
test_data, test_labels = test
train_data = np.array(train_data, dtype=np.float32) / 255
test_data = np.array(test_data, dtype=np.float32) / 255
train_data = train_data.reshape(train_data.shape[0], 28, 28, 1)
test_data = test_data.reshape(test_data.shape[0], 28, 28, 1)
train_labels = np.array(train_labels, dtype=np.int32)
test_labels = np.array(test_labels, dtype=np.int32)
train_labels = tf.keras.utils.to_categorical(train_labels, num_classes=10)
test_labels = tf.keras.utils.to_categorical(test_labels, num_classes=10)
assert train_data.min() == 0.
assert train_data.max() == 1.
assert test_data.min() == 0.
assert test_data.max() == 1.
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz 11490434/11490434 [==============================] - 0s 0us/step
定义超参数
设置学习模型超参数值。
epochs = 3
batch_size = 250
DP-SGD 有三个特定于隐私的超参数和一个您必须调整的现有超参数
l2_norm_clip
(float) - 应用于更新模型参数的每个梯度的最大欧几里得 (L2) 范数。此超参数用于限制优化器对单个训练点的敏感度。noise_multiplier
(float) - 在训练期间采样并添加到梯度的噪声量。通常,更多噪声会导致更好的隐私(通常,但不一定,以降低效用为代价)。microbatches
(int) - 每个数据批次被分成更小的单元,称为微批次。默认情况下,每个微批次应包含一个训练示例。这使我们能够在每个示例的基础上裁剪梯度,而不是在它们在小批量中平均之后进行裁剪。这反过来又减少了裁剪对梯度中发现的信号的(负面)影响,并且通常最大限度地提高了效用。但是,可以通过增加微批次的大小以包含多个训练示例来减少计算开销。然后对这些多个训练示例的平均梯度进行裁剪。在批次中消耗的示例总数(即梯度下降的一步)保持不变。微批次的数量应能被批次大小整除。learning_rate
(float) - 此超参数已存在于普通 SGD 中。学习率越高,每次更新的影响就越大。如果更新是有噪声的(例如,当附加噪声与裁剪阈值相比很大时),较低的学习率可能有助于训练过程收敛。
使用以下超参数值以获得一个相当准确的模型(95% 的测试准确率)
l2_norm_clip = 1.5
noise_multiplier = 1.3
num_microbatches = 250
learning_rate = 0.25
if batch_size % num_microbatches != 0:
raise ValueError('Batch size should be an integer multiple of the number of microbatches')
构建模型
定义一个卷积神经网络作为学习模型。
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(16, 8,
strides=2,
padding='same',
activation='relu',
input_shape=(28, 28, 1)),
tf.keras.layers.MaxPool2D(2, 1),
tf.keras.layers.Conv2D(32, 4,
strides=2,
padding='valid',
activation='relu'),
tf.keras.layers.MaxPool2D(2, 1),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(32, activation='relu'),
tf.keras.layers.Dense(10)
])
为学习模型定义优化器和损失函数。将损失计算为每个示例的损失向量,而不是作为小批量的平均值,以支持对每个训练点的梯度操作。
optimizer = tensorflow_privacy.DPKerasSGDOptimizer(
l2_norm_clip=l2_norm_clip,
noise_multiplier=noise_multiplier,
num_microbatches=num_microbatches,
learning_rate=learning_rate)
loss = tf.keras.losses.CategoricalCrossentropy(
from_logits=True, reduction=tf.losses.Reduction.NONE)
训练模型
model.compile(optimizer=optimizer, loss=loss, metrics=['accuracy'])
model.fit(train_data, train_labels,
epochs=epochs,
validation_data=(test_data, test_labels),
batch_size=batch_size)
Train on 60000 samples, validate on 10000 samples Epoch 1/3 2022-12-12 10:43:20.126016: W tensorflow/c/c_api.cc:291] Operation '{name:'training/SGD/momentum/Assign' id:500 op device:{requested: '', assigned: ''} def:{ { {node training/SGD/momentum/Assign} } = AssignVariableOp[_has_manual_control_dependencies=true, dtype=DT_FLOAT, validate_shape=false](training/SGD/momentum, training/SGD/momentum/Initializer/initial_value)} }' was changed by setting attribute after it was run by a session. This mutation will have no effect, and will trigger an error in the future. Either don't modify nodes after running them or create a new session. 60000/60000 [==============================] - 78s 1ms/sample - loss: 1.0220 - acc: 0.6809 - val_loss: 0.5134 - val_acc: 0.8517 Epoch 2/3 /tmpfs/src/tf_docs_env/lib/python3.9/site-packages/keras/engine/training_v1.py:2333: UserWarning: `Model.state_updates` will be removed in a future version. This property should not be used in TensorFlow 2.0, as `updates` are applied automatically. updates = self.state_updates 2022-12-12 10:44:37.590033: W tensorflow/c/c_api.cc:291] Operation '{name:'loss/mul' id:157 op device:{requested: '', assigned: ''} def:{ { {node loss/mul} } = Mul[T=DT_FLOAT, _has_manual_control_dependencies=true](loss/mul/x, loss/dense_1_loss/weighted_loss/Mul)} }' was changed by setting attribute after it was run by a session. This mutation will have no effect, and will trigger an error in the future. Either don't modify nodes after running them or create a new session. 60000/60000 [==============================] - 75s 1ms/sample - loss: 0.4747 - acc: 0.8756 - val_loss: 0.4018 - val_acc: 0.9025 Epoch 3/3 60000/60000 [==============================] - 76s 1ms/sample - loss: 0.3969 - acc: 0.9075 - val_loss: 0.3698 - val_acc: 0.9160 <keras.callbacks.History at 0x7f2abb6b4fd0>
衡量差分隐私保证
执行隐私分析以衡量训练算法达成的 DP 保证。了解达成的 DP 水平可以客观地比较两次训练运行,以确定两者中哪一个更能保护隐私。从高层次上讲,隐私分析衡量了潜在的攻击者通过观察训练过程的结果(例如,模型更新和参数)来提高他们对任何单个训练点的属性的猜测程度。
这种保证有时被称为隐私预算。较低的隐私预算更严格地限制了攻击者提高其猜测的能力。这确保了更强的隐私保证。直观地说,这是因为单个训练点更难影响学习结果:例如,ML 算法无法记住训练点中包含的信息,并且贡献此训练点到数据集的个人的隐私得到保护。
在本教程中,隐私分析是在 Rényi 差分隐私 (RDP) 框架下进行的,RDP 是基于 这篇论文 对纯 DP 的放松,特别适合 DP-SGD。
使用两个指标来表达 ML 算法的 DP 保证
- Delta (\(\delta\)) - 限制隐私保证不成立的概率。经验法则是将其设置为小于训练数据集大小的倒数。在本教程中,将其设置为 **10^-5**,因为 MNIST 数据集有 60,000 个训练点。
- Epsilon (\(\epsilon\)) - 这是隐私预算。它通过限制包含(或排除)单个训练点后特定模型输出的概率变化量来衡量隐私保证的强度。\(\epsilon\) 的值越小,意味着更好的隐私保证。但是,\(\epsilon\) 值只是一个上限,在实践中,较大的值仍然可能意味着良好的隐私。
Tensorflow Privacy 提供了一个工具,compute_dp_sgd_privacy
,用于在给定 \(\delta\) 的固定值和训练过程中的以下超参数的情况下计算 \(\epsilon\) 的值
- 训练数据中的总点数,
n
。 batch_size
。noise_multiplier
。- 训练的
epochs
数量。
compute_dp_sgd_privacy.compute_dp_sgd_privacy(n=train_data.shape[0],
batch_size=batch_size,
noise_multiplier=noise_multiplier,
epochs=epochs,
delta=1e-5)
DP-SGD with sampling rate = 0.417% and noise_multiplier = 1.3 iterated over 720 steps satisfies differential privacy with eps = 0.563 and delta = 1e-05. The optimal RDP order is 18.0. (0.5631726490328062, 18.0)
该工具报告说,对于上面选择的超参数,训练后的模型的 \(\epsilon\) 值为 1.18。
总结
在本教程中,您了解了差分隐私 (DP) 以及如何在现有的 ML 算法中实现 DP 原则,以提供对训练数据的隐私保证。特别是,您学习了如何
- 使用 TensorFlow Privacy 将现有的优化器(例如 SGD、Adam)包装到它们的差分隐私对应物中
- 调整差分隐私机器学习引入的超参数
- 使用 TensorFlow Privacy 中包含的分析工具来衡量提供的隐私保证