注意:这篇文章首先出现在RStudio AI博客(以前是RStudio TensorFlow博客)上,致力于R的所有深度学习,概率建模和分布式计算。假定原始读者熟悉R包tensorflow和keras,旨在允许以惯用的,类似于R的方式设计和训练TensorFlow / Keras模型,以及网状结构(这种巧妙的助手),它使我们能够将Python功能直接集成到R中。
深度学习不必与隐私保护保持一致。联邦学习可实现设备上的分布式模型训练;加密使模型和渐变更新不公开;差分隐私可防止训练数据泄漏。到目前为止,私有和安全的深度学习是一种新兴技术。在本文中,我们介绍Syft,这是一个与PyTorch和TensorFlow集成的开源框架。在一个示例用例中,我们从Keras模型获得私有预测。
在深度学习(或机器学习或“ AI”)的背景下,尤其是与安全性结合使用时,“ 隐私 ”一词听起来可能是一个流行语:隐私,安全性,安全性 -像liberté, fraternité,égalité。实际上,应该有这样的口头禅。但这是另一个主题,就像刚才引用的另一个流行短语一样,并不是每个人都以相同的方式解释这些术语。
因此,让我们考虑一下隐私,以一种更具技术性的方式,将其范围缩小到培训或使用深度学习模型中。由于隐私(或更确切地说是侵犯隐私)可能以各种方式出现,因此不同的侵犯将要求采取不同的对策。当然,最后,我们希望看到它们全部集成在一起-但是关于隐私相关技术,该领域实际上才刚刚开始。因此,我们能做的最重要的事情是了解概念,调查正在开发的实现的前景,并可能决定参与其中。
这篇文章试图做所有这些的一点点。
假设您在医院工作,并且有兴趣训练深度学习模型来帮助通过脑部扫描诊断某些疾病。在您工作的地方,您没有很多这种疾病的患者。而且,它们通常受相同的子类型的影响:如果要创建一个训练集,则不能很好地反映总体分布。因此,与其他医院合作是有意义的;但这并不是那么容易,因为收集的数据受到隐私法规的保护。因此,第一个要求是:数据必须保留在原位置;例如,它可能不会发送到中央服务器。
联邦学习[1]解决了这个第一个必要条件。出于隐私原因,联邦学习不是“公正”的。相反,在许多用例中,这可能是唯一可行的方法(例如使用智能手机或传感器来收集大量数据)。在联邦学习中,每个参与者都收到模型的副本,训练自己的数据,然后将获得的梯度发送回中央服务器,在中央服务器中对梯度进行平均并应用于模型。
只要数据从不离开单个设备,这是很好的。但是,仍然可以从明文梯度中提取很多信息。想象一下一个智能手机应用程序,它为短信提供可训练的自动完成功能。即使平均许多迭代的梯度更新,它们的分布在各个人之间也会有很大的不同。需要某种形式的加密。但是服务器如何理解加密的渐变呢?
实现此目的的一种方法依赖于安全的多方计算(SMPC)。
在SMPC中,我们需要一个由多个代理组成的系统,这些系统协作以提供单个代理无法单独提供的结果:“秘密”(加密)数据的“常规”计算(如加法,乘法等)。假定这些代理人“诚实但好奇” –诚实,因为他们不会篡改其数据份额。从某种意义上说,它们是好奇的,也就是说,如果它们是好奇的,他们将无法检查数据,因为它们是加密的。
其背后的原理是秘密共享。单个数据(例如工资)被“分解”为无意义的(因此,经过加密)部分,再次组合在一起便产生原始数据。这是一个例子。
说涉及的当事人是朱莉娅,格雷格和我。以下函数加密一个值,为我们每个人分配其“无意义”的份额:
# a big prime number
# all computations are performed in a finite field, for example, the integers modulo that prime
Q <- 78090573363827
encrypt <- function(x) {
# all but the very last share are random
julias <- runif(1, min = -Q, max = Q)
gregs <- runif(1, min = -Q, max = Q)
mine <- (x - julias - gregs) %% Q
list (julias, gregs, mine)
}
# some top secret value no-one may get to see
value <- 77777
encrypted <- encrypt(value)
encrypted
[[1]]
[1] 7467283737857
[[2]]
[1] 36307804406429
[[3]]
[1] 34315485297318
一旦我们三个人把股份放在一起,就可以很容易地获得原本的价值:
decrypt <- function(shares) {
Reduce(sum, shares) %% Q
}
decrypt(encrypted)
77777
作为一个如何对加密数据进行计算的示例,这里有补充。(其他操作将不那么简单。)要添加两个数字,只需让每个人都添加各自的数据即可:
add <- function(x, y) {
list(
# julia
(x[[1]] + y[[1]]) %% Q,
# greg
(x[[2]] + y[[2]]) %% Q,
# me
(x[[3]] + y[[3]]) %% Q
)
}
x <- encrypt(11)
y <- encrypt(122)
decrypt(add(x, y))
133
返回到深度学习的设置以及要解决的当前任务:让服务器应用渐变更新而不会看到它们。使用秘密共享,它将像这样工作:
朱莉娅,格雷格和我每个人都想训练我们自己的私人数据。我们将共同负责梯度平均,也就是说,我们将组成一个在该任务中统一起来的工作节点。现在,模型所有者的秘密共享模型,我们开始对每个模型进行训练,每个模型都使用自己的数据。经过几次迭代后,我们使用安全平均来合并我们各自的梯度。然后,服务器只看到平均梯度,就无法确定我们各自的贡献。
令人惊讶的是,甚至可以使用相同的秘密共享技术对加密数据进行训练。当然,这必须负面影响训练速度。但很高兴知道,如果一个用例需要它,那将是可行的。(一个可能的用例是,仅对一方数据进行培训没有任何意义,但数据很敏感,因此除非加密,否则其他人将不允许您访问其数据。)
因此,根据您的需要提供加密功能,我们在隐私保护方面完全安全吗?答案是不。该模型仍然可以泄漏信息。例如,在某些情况下,可以执行模型反演 [2],即仅对模型进行黑盒访问即可训练允许重建某些原始训练数据的攻击模型。不用说,这种泄漏必须避免。差异性隐私 [ 3 ] [ 4 ]要求通过查询模型获得的结果与用于训练的数据集中是否存在单个个体无关。通常,这是通过在每个查询的答案中添加噪音来确保的。在训练深度学习模型时,我们向梯度添加了噪声,并根据一些选定的范数对其进行裁剪。
然后,在某个时候,我们将所有这些结合在一起:联邦学习,加密和差分隐私。
Syft是一个非常有前途,非常积极开发的框架,旨在提供所有这些框架。我也许应该写“规定”,而不是“为……而瞄准”,这取决于。我们需要更多背景信息。
Syft(也称为PySyft),因为到目前为止,它最成熟的实现是用Python编写的,并且是为Python编写的,由OpenMined(一个致力于启用隐私保护AI的开源社区)维护。在这里复制他们的使命声明是值得的:
设计用于人工智能的行业标准工具时,有以下几个假设:将数据集中到单个计算集群中,该集群存在于安全的云中,并且最终的模型将归中央机构所有。我们设想了一个不限于此场景的世界-AI工具将隐私,安全和多所有者治理视为头等公民的世界。[…] OpenMined社区的使命是为私有,安全,多所有者控制的AI创建易于访问的工具生态系统。
虽然PySyft远非唯一,但仍是其最成熟的开发框架。它的作用是提供安全的联合学习,包括加密和差异隐私。对于深度学习,它依赖于现有框架。
到目前为止,PyTorch集成似乎是最成熟的。通过PyTorch,加密和差分私人培训已经可用。与TensorFlow的集成涉及更多。它尚未包含TensorFlow联合和TensorFlow隐私。对于加密,它依赖于TensorFlow Encrypted(TFE),在撰写本文时,它不是官方的TensorFlow子项目。
但是,即使到现在,也已经可以秘密共享 Keras模型并管理私人预测。让我们看看如何。
我们的介绍性示例将展示如何使用外部提供的模型对私人数据进行分类-在模型所有者从未看到过该数据且用户从未拥有(例如下载)模型的情况下。(考虑一下模型所有者也希望隐藏自己的劳动成果)。
换句话说:模型是加密的,数据也是。您可能会想到,这涉及一组代理,一起执行安全的多方计算。
该用例以已经训练有素的模型为前提,我们首先快速创建一个模型。这里没有什么特别的事情。
前奏:在MNIST上训练一个简单的模型
# create_model.R
library(tensorflow)
library(keras)
mnist <- dataset_mnist()
mnist$train$x <- mnist$train$x/255
mnist$test$x <- mnist$test$x/255
dim(mnist$train$x) <- c(dim(mnist$train$x), 1)
dim(mnist$test$x) <- c(dim(mnist$test$x), 1)
input_shape <- c(28, 28, 1)
model <- keras_model_sequential() %>%
layer_conv_2d(filters = 16, kernel_size = c(3, 3), input_shape = input_shape) %>%
layer_average_pooling_2d(pool_size = c(2, 2)) %>%
layer_activation("relu") %>%
layer_conv_2d(filters = 32, kernel_size = c(3, 3)) %>%
layer_average_pooling_2d(pool_size = c(2, 2)) %>%
layer_activation("relu") %>%
layer_conv_2d(filters = 64, kernel_size = c(3, 3)) %>%
layer_average_pooling_2d(pool_size = c(2, 2)) %>%
layer_activation("relu") %>%
layer_flatten() %>%
layer_dense(units = 10, activation = "linear")
model %>% compile(
loss = "sparse_categorical_crossentropy",
optimizer = "adam",
metrics = "accuracy"
)
model %>% fit(
x = mnist$train$x,
y = mnist$train$y,
epochs = 1,
validation_split = 0.3,
verbose = 2
)
model$save(filepath = "model.hdf5")
获取所有必需软件包的最简单方法是,将集成的OpenMined安装到其Udacity课程中,该课程通过PySyft引入联合学习和差异性隐私。这将安装TensorFlow 1.15和TensorFlow Encrypted等。
以下几行代码应放在一个文件中。我发现从控制台选项卡中运行的R进程“获取”该脚本很实用。
首先,我们再次定义模型,现在有两点不同。首先,出于技术原因,我们需要传递batch_input_shape而不是input_shape。第二,最后一层是“缺少” softmax激活。这不是疏忽– SMPC softmax尚未实施。(取决于您何时阅读,该陈述可能不再成立。)如果我们在秘密共享模式下训练该模型,这当然是一个问题;对于分类,我们只关心最大分数。
在定义模型之后,我们将从上一步中训练的模型中加载实际权重。然后,动作开始。我们创建了一个TFE工作人员集合,它们一起运行分布式TensorFlow集群。该模型是与工作节点秘密 共享的,也就是说,模型权重被拆分为多个份额,每个单独检查都无法使用。最终,该模型被提供服务,即可供请求预测的客户使用。
Keras模型如何共享和服务?这些不是Keras本身提供的方法。魔术来自Syft 钩入Keras ,扩展了model对象:cf. hook <- sy K e r a s H o o k ( t f KerasHook(tf KerasHook(tfkeras)在我们导入Syft之后。
# serve.R
# you could start R on the console and "source" this file
# do this just once
reticulate::py_install("syft[udacity]")
library(tensorflow)
library(keras)
sy <- reticulate::import(("syft"))
hook <- sy$KerasHook(tf$keras)
batch_input_shape <- c(1, 28, 28, 1)
model <- keras_model_sequential() %>%
layer_conv_2d(filters = 16, kernel_size = c(3, 3), batch_input_shape = batch_input_shape) %>%
layer_average_pooling_2d(pool_size = c(2, 2)) %>%
layer_activation("relu") %>%
layer_conv_2d(filters = 32, kernel_size = c(3, 3)) %>%
layer_average_pooling_2d(pool_size = c(2, 2)) %>%
layer_activation("relu") %>%
layer_conv_2d(filters = 64, kernel_size = c(3, 3)) %>%
layer_average_pooling_2d(pool_size = c(2, 2)) %>%
layer_activation("relu") %>%
layer_flatten() %>%
layer_dense(units = 10)
pre_trained_weights <- "model.hdf5"
model$load_weights(pre_trained_weights)
# create and start TFE cluster
AUTO <- TRUE
julia <- sy$TFEWorker(host = 'localhost:4000', auto_managed = AUTO)
greg <- sy$TFEWorker(host = 'localhost:4001', auto_managed = AUTO)
me <- sy$TFEWorker(host = 'localhost:4002', auto_managed = AUTO)
cluster <- sy$TFECluster(julia, greg, me)
cluster$start()
# split up model weights into shares
model$share(cluster)
# serve model (limiting number of requests)
model$serve(num_requests = 3L)
一旦提供了所需数量的请求,我们就可以进入此R流程,停止模型共享,并关闭集群:
# stop model sharing
model$stop()
# stop cluster
cluster$stop()
现在,转到客户端。
请求有关私密数据的预测
在我们的示例中,我们有一个客户。客户端是TFE工作节点,就像组成集群的代理一样。
我们也在客户端定义集群。创建客户;并将客户端连接到模型。这将设置一个排队服务器,该服务器在将所有输入数据提交进行预测之前会进行秘密共享。
最后,我们让客户要求对前三个MNIST图像进行分类。
随着服务器以不同的R进程运行,我们可以方便地在RStudio中运行它:
# client.R
library(tensorflow)
library(keras)
sy <- reticulate::import(("syft"))
hook <- sy$KerasHook(tf$keras)
mnist <- dataset_mnist()
mnist$train$x <- mnist$train$x/255
mnist$test$x <- mnist$test$x/255
dim(mnist$train$x) <- c(dim(mnist$train$x), 1)
dim(mnist$test$x) <- c(dim(mnist$test$x), 1)
batch_input_shape <- c(1, 28, 28, 1)
batch_output_shape <- c(1, 10)
# define the same TFE cluster
AUTO <- TRUE
julia <- sy$TFEWorker(host = 'localhost:4000', auto_managed = AUTO)
greg <- sy$TFEWorker(host = 'localhost:4001', auto_managed = AUTO)
me <- sy$TFEWorker(host = 'localhost:4002', auto_managed = AUTO)
cluster <- sy$TFECluster(julia, greg, me)
# create the client
client <- sy$TFEWorker()
# create a queueing server on the client that secret shares the data
# before submitting a prediction request
client$connect_to_model(batch_input_shape, batch_output_shape, cluster)
num_tests <- 3
images <- mnist$test$x[1: num_tests, , , , drop = FALSE]
expected_labels <- mnist$test$y[1: num_tests]
for (i in 1:num_tests) {
res <- client$query_model(images[i, , , , drop = FALSE])
predicted_label <- which.max(res) - 1
cat("Actual: ", expected_labels[i], ", predicted: ", predicted_label)
}
Actual: 7 , predicted: 7
Actual: 2 , predicted: 2
Actual: 1 , predicted: 1
好了 模型和数据的确是秘密的,但是我们能够对数据进行分类。
结束吧。
结论
我们的示例用例并没有太大的野心–我们从训练有素的模型开始,因此撇开了联合学习。在简化设置过程中,我们能够专注于基本原理:秘密共享作为一种加密手段,并建立了一个Syft / TFE工人集群,这些集群共同提供了用于加密模型权重和客户端数据的基础结构。
如果您已经阅读了我们之前关于TensorFlow Federated的文章(也正在开发一个框架),那么您可能会得到与我类似的印象:设置Syft更加简单明了,概念易于掌握,令人惊讶的是,几乎不需要任何代码。正如我们可能从最近的博客文章中收集到的那样,Syft与TensorFlow Federated和TensorFlow Privacy的集成正在发展中。我期待着很多这种情况发生。
谢谢阅读!
本文翻译自OpenMined官方博客:https://blog.openmined.org/towards-privacy-encrypted-deep-learning-with-syft-and-keras/
这篇文章的作者:
西格丽德·凯达娜(Sigrid Keydana)
Sigrid是PBC RStudio的一名应用研究员。她帮助维护RStudio的深度学习相关软件包,并为RStudio AI博客(https://blogs.rstudio.com/ai/)撰写文章。