在 TensorFlow.org 上查看
|
在 Google Colab 中运行
|
在 GitHub 上查看源码
|
下载笔记本
|
概览
差分隐私 (DP) 是一种用于衡量算法所提供的隐私保障的框架。通过差分隐私的视角,您可以设计机器学习算法,在私有数据上负责任地训练模型。利用差分隐私进行学习可以提供可衡量的隐私保障,有助于降低机器学习中暴露敏感训练数据的风险。直观地说,使用差分隐私训练的模型不应受到其数据集中任何单个训练样本或一小部分训练样本的影响。这有助于降低在机器学习中暴露敏感训练数据的风险。
这种方法的基本思想称为差分隐私随机梯度下降 (DP-SGD),即修改随机梯度下降 (SGD) 中使用的梯度,而 SGD 是几乎所有深度学习算法的核心。使用 DP-SGD 训练的模型为其输入数据提供了可证明的差分隐私保障。对原始 SGD 算法进行了两处修改:
- 首先,需要限制每个梯度的敏感度。换句话说,您需要限制小批量数据中采样的每个个体训练点对梯度计算及随后的模型参数更新的影响程度。这可以通过对每个训练点计算出的梯度进行裁剪 (clipping) 来实现。
- 对裁剪后的梯度进行随机噪声采样并将其添加进去,使得在统计学上无法通过比较 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-privacyimport 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) - 每批数据被拆分为更小的单元,称为微批次 (microbatches)。默认情况下,每个微批次应包含一个训练样本。这使我们能够按样本进行梯度裁剪,而不是在跨小批量数据平均后再裁剪。这反过来减少了裁剪对梯度中信号的(负面)影响,通常能最大化效用。但是,可以通过增加微批次的大小以包含多个训练样本来降低计算开销。然后对这些多个训练样本的平均梯度进行裁剪。批次中消耗的样本总数(即梯度下降的一步)保持不变。微批次数量应能整除批次大小。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) 框架下进行的,这是基于本文对纯 DP 的一种松弛,特别适用于 DP-SGD。
使用两个指标来表示 ML 算法的 DP 保障:
- Delta (\(\delta\)) - 限制隐私保障不成立的概率。经验法则是将其设置为小于训练数据集大小的倒数。在本教程中,由于 MNIST 数据集有 60,000 个训练点,因此将其设置为 10^-5。
- 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),以及如何将 DP 原则应用到现有 ML 算法中,为训练数据提供隐私保障。特别是,您学习了如何:
- 使用 TensorFlow Privacy 将现有优化器(如 SGD、Adam)封装为差分隐私对应版本
- 调整由差分隐私机器学习引入的超参数
- 使用 TensorFlow Privacy 中包含的分析工具衡量所提供的隐私保障
在 TensorFlow.org 上查看
在 Google Colab 中运行
在 GitHub 上查看源码
下载笔记本