奥胡斯大学密码学PhD、Datadog机器学习工程师Morten Dahl介绍了如何实现基于加密数据进行训练和预测的卷积神经网络。
TL;DR我们选取了一个经典的CNN深度学习模型,经过一系列步骤的改造,使其得以基于加密数据进行训练和预测。
通过卷积神经网络(CNN)分析图像在最近几年极为流行,因为CNN在图像相关任务上的表现超过了其他许多方法。
最近一个基于CNN分析图像的应用是检测皮肤癌,基于这一应用,任何人可以使用手机应用快速地拍摄一张皮肤损伤的照片,并得到“表现与专家相当的”分析(可以参考YouTube上的视频demo(youtu.be/toK1OSLep3s))。得以获取大量的临床图像在训练模型上起到了关键作用——可以将这一数据集认为是敏感信息。
这导向了隐私问题以及安全多方计算(MPC):现在有多少应用因为缺乏可供访问的数据而受到限制?在上述案例中,如果允许任何使用手机应用的人贡献数据,模型会不会得到提升?如果答案是肯定的,有多少人自愿冒暴露与个人健康相关的信息呢?
基于MPC我们可以潜在地降低暴露信息的风险,从而增强参与的动机。更具体地说,通过转而在加密数据上训练,我们不仅可以防止任何人查看个人数据,还可以防止泄露学习到的模型参数。差别隐私等其他技术也可能避免从预测中泄露信息,但我们这里暂不讨论。
本文将讨论一个简化了的图像分析案例,介绍所有需要用到的技术。GitHub上有一些和本文配套的notebook(mortendahl/privateml),其中主要的notebook提供了概念证明实现。
此外,我最近在Paris Machine Learning meetup(巴黎机器学习会议)上做了关于本文的报告,相关的幻灯片发布在GitHub仓库mortendahl/talks下的ParisML17.pdf。
非常感谢Andrew Trask、Nigel Smart、Adrià Gascón、OpenMined社区对这一话题的启发和讨论。
设定
我们假定训练数据集由一些输入提供者(input provider)共同所有,而数据由两个不同服务器(方)进行,我们信任两方不会在协议指定的范围之外协作。例如,在实践中,服务器可能是共享云环境下由两个不同组织掌握的虚拟实例。
输入提供者只需在一开始传输他们的(加密)训练数据;在此之后所有的计算只涉及两个服务器,这意味着事实上输入提供者使用手机之类的设备是可行的。训练之后,模型将保持由两个服务器共同所有的加密形式,每个人都可以使用它做出进一步的加密预测。
出于技术原因,我们同时假设有一个不同的加密生产商(crypto producer)生成计算过程中使用的特定原始材料,以提供效率;存在消除这一额外实体的方法,不过本文暂不讨论这些。
最后,就安全术语而言,我们追求的是实践中常用的典型概念,即诚实而好奇(或被动)安全(honest-but-curious (or passive) security),即假定服务器将遵循协议,但除此之外会尝试了解尽可能多的看到的信息。对服务器而言,尽管这个概念比完全恶意(或主动)安全(fully malicious (or active) security)要弱一点,它仍然针对任何可能在计算之后攻破其中一个服务器的行为提供强力的保护,不管攻击者做了什么。注意,本文事实上允许训练过程中的小部分隐私泄露,详见后文。
基于CNN进行图像分析
我们的用例是经典的MNIST手写数字识别,即学习给定图像中的阿拉伯数字,我们将使用Keras示例中的CNN模型作为基础。
feature_layers = [
Conv2D(32, (3, 3), padding='same', input_shape=(28, 28, 1)),
Activation('relu'),
Conv2D(32, (3, 3), padding='same'),
Activation('relu'),
MaxPooling2D(pool_size=(2,2)),
Dropout(.25),
Flatten()
]
classification_layers = [
Dense(128),
Activation('relu'),
Dropout(.50),
Dense(NUM_CLASSES),
Activation('softmax')
]
model = Sequential(feature_layers + classification_layers)
model.compile(
loss='categorical_crossentropy',
optimizer='adam',
metrics=['accuracy'])
model.fit(
x_train, y_train,
epochs=1,
batch_size=32,
verbose=1,
validation_data=(x_test, y_test))
这里不讨论这一模型的细节,因为网上已经有很多介绍其原理的资源了。不过基本的想法是首先让图像通过一组特征层(feature layer),将输入图像的原始像素转换为和我们的分类任务更相关的抽象属性。接着通过一组分类层(classification layer)组合这些属性,以生成可能数字的概率分布。最终输出通常直接是概率最高的数字。
我们很快将看到,使用Keras的优势是我们可以快速地在未加密数据上进行试验,看看模型本身的表现如何,同时,Keras提供了一个简单的接口,供我们之后的加密设定效仿。
基于SPDZ安全计算
CNN就绪后,我们接着来看MPC。我们将使用当前最先进的SPDZ协议,因为它允许我们只使用两个服务器,也允许我们通过将特定计算转移到离线阶段以改善在线表现。
和其他典型的安全计算协议一样,所有计算在一个域中进行,此处的域由一个质数Q表示。这意味着我们需要编码CNN使用的浮点数为以一个质数为模的整数,这给Q带来一些限制,进而对性能有所影响。
此外,在SPDZ协议这样的交互计算中,在典型的时间复杂度之外,同时还要考虑通讯和回合复杂度。通讯复杂度衡量在网络中发送的字节数,一个相对较慢的过程。回合复杂度衡量两个服务器之间的同步点数目,同步点可能阻塞其中一个服务器,使其无所事事,直到另一个服务器赶上来为止。因而两者均对总执行时间有很大的影响。
然而更重要的是,这些协议的“原生”操作只有加法和乘法。除法、比较等可以完成,但就它们的三项复杂度而言,要更昂贵。之后我们将看下如何缓解这引起的其中一些问题,而这里我们首先讨论基本的SPDZ协议。
张量操作
下面的代码为SPDZ协议实现PublicTensor和PrivateTensor两个类,分别代表两个服务器知道明文的张量和仅仅知道其秘密分享形式的加密值。
classPrivateTensor:
def __init__(self, values, shares0=None, shares1=None):
ifnot values isNone:
shares0, shares1 = share(values)
self.shares0 = shares0
self.shares1 = shares1
def reconstruct(self):
returnPublicTensor(reconstruct(self.shares0, self.shares1))
def add(x, y):
if type(y) isPublicTensor:
shares0 = (x.values + y.shares0) % Q
shares1 = y.shares1
returnPrivateTensor(None, shares0, shares1)
if type(y) isPrivateTensor:
shares0 = (x.shares0 + y.shares0) % Q
shares1 = (x.shares1 + y.shares1) % Q
returnPrivateTensor(None, shares0, shares1)
def mul(x, y):
if type(y) isPublicTensor:
shares0 = (x.shares0 * y.values) % Q
shares1 = (x.shares1 * y.values) % Q
returnPrivateTensor(None, shares0, shares1)
if type(y) isPrivateTensor:
a, b, a_mul_b = generate_mul_triple(x.shape, y.shape)
alpha = (x - a).reconstruct()
beta = (y - b).reconstruct()
return alpha.mul(beta) + \
alpha.mul(b) + \
a.mul(beta) + \
a_mul_b
代码基本上还是直截了当的。当然,其中有一些技术上的细节,详见和本文配套的notebook。
上面的代码用到的基本工具函数:
def share(secrets):
shares0 = sample_random_tensor(secrets.shape)
shares1 = (secrets - shares0) % Q
return shares0, shares1
def reconstruct(shares0, shares1):
secrets = (shares0 + shares1) % Q
return secrets
def generate_mul_triple(x_shape, y_shape):
a = sample_random_tensor(x_shape)
b = sample_random_tensor(y_shape)
c = np.multiply(a, b) % Q
returnPrivateTensor(a), PrivateTensor(b), PrivateTensor(c)
适配模型
虽然原则上基于我们现有的模型安全地计算任何函数是可能的,实践中需要做的是先考虑对MPC更友好的模型变体,以及对模型更友好的加密协议。用稍微形象一点的话说,我们经常需要打开两个黑箱,让两个技术更好地适配彼此。
这一做法的根源在于,加密操作下,有一些操作惊人地昂贵。我们之前提到过,加法和乘法相对廉价,而比较和基于私密分母的除法则不然。基于这一原因,我们对模型做了一些改动,以避免这一问题。
本节中涉及的许多改动和它们相应的性能详见配套的Python notebook。
优化器
首先涉及的是优化器:尽管许多实现基于Adam的高效而选择了它,Adam涉及对私密值取平方根,以及在除法中使用私密值作分母。尽管理论上安全地进行这些计算是可能的,在实践中它会是性能的显著瓶颈,因此需要避免使用Adam。
一个简单的补救方案是转而使用动量SGD(momentum SGD)优化器,它可能意味着较长的训练时间,但只使用简单的操作。
model.compile(
loss='categorical_crossentropy',
optimizer=SGD(clipnorm=10000, clipvalue=10000),
metrics=['accuracy'])
还有一个额外的坑,很多优化器使用裁剪(clipping)以避免梯度变得过小或过大。裁剪需要比较私密值,在加密设定下这又是一个某种程度上昂贵的操作,因此我们的目标是避免使用裁剪(在上面的代码中,我们增加了界限)。
网络层
说到比较,ReLU和最大池化层同样有这个问题。CryptoNet用一个平方函数取代了前者,用平均池化取代了后者,而SecureML实现了一个类似ReLU的激活函数(不过,这增加了复杂度,为了保持简单,本文打算避免这一点)。因此,我们这里使用了高阶sigmoid激活函数和平均池化层。注意平均池化同样用到了除法,不过这回分母是公开值,因而除法不过是对公开值取倒数,接着进行一次乘法。
feature_layers = [
Conv2D(32, (3, 3), padding='same', input_shape=(28, 28, 1)),
Activation('sigmoid'),
Conv2D(32, (3, 3), padding='same'),
Activation('sigmoid'),
AveragePooling2D(pool_size=(2,2)),
Dropout(.25),
Flatten()
]
classification_layers = [
Dense(128),
Activation('sigmoid'),
Dropout(.50),
Dense(NUM_CLASSES),
Activation('softmax')
]
model = Sequential(feature_layers + classification_layers)
模拟表明这一改动让我们需要提高epoch数,相应地减慢训练速度。学习率或动量的其他选择可能可以改善这一点。
model.fit(
x_train, y_train,
epochs=15,
batch_size=32,
verbose=1,
validation_data=(x_test, y_test))
剩下的层很好处理。dropout和平层(flatten)不在乎是加密设定还是非加密设定,密集层和卷积层是矩阵点积,只需要基本操作。
softmax和损失函数
在加密设定下,最后的softmax层同样会给训练带来复杂度,因为我们需要进行以私密值为指数的指数运算,以及基于私密分母除法的归一化。
尽管这两者都是可能达成的,我们这里选择了一个更简单的做法,允许向其中一个服务器暴露每个训练样本的预测分类的似然,该服务器接着基于暴露值计算结果。这当然导致了隐私泄露,这样的泄露可能会,也可能不会形成可接受的风险。
一个启发式的改进方案是在暴露任何值前先变换分类似然的向量,从而隐藏哪个分类对应哪个向量。然而,这可能起不到什么效果,比如,“健康”常常意味着收紧的分布,而“患病”常常意味着舒展的分布。
另一个方案是引入第三个服务器,专门进行这类小计算,其他任何训练数据的信息对该服务器而言都将是不可见的,因而无法将标签和样本数据关联起来。虽然这样仍有部分信息泄露,但这一数量难以进行推理。
最后,我们可以将这样的一对多方法替换为一对一方法,比如,使用sigmoid。如前所述,这允许我们在不解密的情况下完整地计算预测。不过我们仍然需要计算损失,我们也许可以同样考虑使用一个不同的损失函数。
注意,在之后使用训练过的网络进行预测时,这里提到的问题都不存在,因为没有损失需要计算,而服务器可以直接跳过softmax层,让预测的接收方自行计算相应值:对于接收方而言,这只不过是一个如何解释值的问题。
迁移学习
到此为止,看起来我们已经可以按现状实际训练模型并得到不错的结果了。不过,依照CNN的惯例,我们可以利用迁移学习显著加速训练过程;事实上,某种程度上而言,“极少有人从头训练他们自己的卷积网络,因为他们并不具备足够的数据”,,“实践中总是推荐使用迁移学习”,是众所周知的事实。
在我们这里的设定中,迁移学习的特定应用可能是训练分为两阶段:使用非敏感的公开数据的预训练阶段和使用敏感的隐私数据的调优阶段。例如,在检测皮肤癌的案例中,研究人员可能选择在公开照片集上进行预训练,之后请求志愿者提供额外的照片以改进模型。
除了基数的不同以外,两个数据集的主体也可能不同,因为CNN具有首先分解主体为有意义的子部分的倾向,识别哪部分是什么可以被迁移。换句话说,这一技术足够强大,预训练可以在和调优不同类型的图像上进行。
回到我们具体的字符识别用例中,我们可以让0-4作为“公开”图像,而让5-9作为“私密”图像。作为替代,让a-z作为“公开”图像,0-9作为“私密图像”看起来也没什么不合理的。
在公开数据集上进行预训练
除了避免在公开数据集上进行加密数据训练的额外开销之外,在公开数据集上进行预训练还让我们得以使用更高级的优化器。比如,这里我们可以转回去使用Adam优化器训练图像,以加快训练进度。特别地,我们可以降低所需的epoch数。
(x_train, y_train), (x_test, y_test) = public_dataset
model.compile(
loss='categorical_crossentropy',
optimizer='adam',
metrics=['accuracy'])
model.fit(
x_train, y_train,
epochs=1,
batch_size=32,
verbose=1,
validation_data=(x_test, y_test))
一旦我们对预训练的结果满意,服务器可以直接共享模型参数,转而开始训练私密数据集。
在隐私数据集上进行调优
我们开始进行加密训练时,模型的参数已经“到了中途”,因此我们可以期望,不再需要那么多epoch了。如前所述,迁移学习还有一个优势,识别子部件倾向于发生在网络的底层,在某些情形下可能可以按现状使用。因此,我们现在冻结特征层的参数,集中训练分类层。
for layer in feature_layers:
layer.trainable = False
不过,我们仍然需要让所有私密训练样本前向通过这些层;唯一的差别是在反向传播这一步我们跳过这些层,因此我们需要训练的参数减少了。
接下来的训练和前面一样,只不过现在使用较低的学习率:
(x_train, y_train), (x_test, y_test) = private_dataset
model.compile(
loss='categorical_crossentropy',
optimizer=SGD(clipnorm=10000, clipvalue=10000, lr=0.1, momentum=0.0),
metrics=['accuracy'])
model.fit(
x_train, y_train,
epochs=5,
batch_size=32,
verbose=1,
validation_data=(x_test, y_test))
最终,我们在模拟中将epoch数从25降到了5.
预处理
还有少数可以应用的预处理优化,不过这里我们不再进一步优化下去了。
第一个优化是将冻结层的计算转移到输入提供者那里,这样,和服务器共享的将是平层而不是图像的像素。在这一情形下,这些层进行的是特征提取(feature extraction),潜在地让我们可能使用更强大的层。然而,如果我们想要保持模型专有,那么这会显著增加复杂度,因为现在参数需要以某种形式分发到客户端。
另一个典型的加快训练的方法是首先应用诸如主成分分析之类的降维技术。BSS+’17的加密设定使用了这一方法。
适配协议
查看了模型之后,我们接着来看协议:同样,正如我们即将看到的,理解我们需要进行的操作有助于提升速度。
特别地,许多计算可以转移到加密提供者处,加密提供者生成的原始材料独立于私密输入,某种程度上甚至独立于模型。因此,它的计算可以在方便的时候大批量地实现完成。
回忆一下先前提到的有必要同时优化回合复杂度和通讯复杂度,而这里提议的扩展常常旨在优化这两者,不过作为代价,需要额外的本地计算。因此,需要进行实际的试验以验证它们在具体情形下的益处。
Dropout
从最简单的网络层类型开始,我们注意到没有任何和安全计算特别相关的事情发生在这一层,这一层只是确保两个服务器同意在每次训练迭代中丢弃哪些值。这可以通过直接同意一个种子值达成。
平均池化
平均池化的前向传播只需要一次累加以及随后的基于公开分母的除法。因此,它可以通过乘以一个公开值实现:由于分母是公开的,我们可以很容易找到它的倒数,然后直接相乘并截断。类似地,反向传播不过是缩放,因而两个方向的传播都完全是本地操作。
密集层
密集层的前向传播和反向传播都需要进行点积操作,该操作当然可以通过经典的乘法和加法实现。如果我们想为形状分别为(m, k)和(k, n)的矩阵x和y计算点积dot(x, y),那么这将需要m * n * k次乘法,意味着我们需要通讯同等数量的掩码后的值。尽管这些可以并发发送,所以我们仅仅需要一回合,如果我们可以使用另一种预处理的三元组,那么我们可以降低一个数量级的通讯成本。
例如,我们的模型的第二个密集层计算(32, 128)和(128, 5)这两个矩阵的点积。使用典型的方法,每个batch中需要发送32 * 5 * 128 == 22400掩码后的值,但使用下面描述的预处理的三元组,我们只需发送32 * 128 + 5 * 128 == 4736掩码后的值,几乎有5倍的改善。第一个密集层的效果还要好,大约有25倍多的改善。
技巧在于确保矩阵中的每个私密值的掩码仅发送一次。为了达成这一点,我们需要三元组(a, b, c),其中,a和b是形状合适的随机矩阵,c满足c == dot(a, b)。
def generate_dot_triple(x_shape, y_shape):
a = sample_random_tensor(x_shape)
b = sample_random_tensor(y_shape)
c = np.dot(a, b) % Q
returnPrivateTensor(a), PrivateTensor(b), PrivateTensor(c)
给定这样一个三元组,我们可以转而通讯alpha = x - a和beta = y - b的值,接着通过本地计算得到dot(x, y)。
classPrivateTensor:
...
def dot(x, y):
if type(y) isPublicTensor:
shares0 = x.shares0.dot(y.values) % Q
shares1 = x.shares1.dot(y.values) % Q
returnPrivateTensor(None, shares0, shares1)
if type(y) isPrivateTensor:
a, b, a_dot_b = generate_dot_triple(x.shape, y.shape)
alpha = (x - a).reconstruct()
beta = (y - b).reconstruct()
return alpha.dot(beta) + \
alpha.dot(b) + \
a.dot(beta) + \
a_dot_b
使用这一三元组的安全性取决于三元组乘法的安全性:通讯的掩码后的值完美地隐藏了x和y的值,而c是一个独立的新的共享值,这确保了结果无法泄露任何有关其组成的信息。
注意,SecureML使用了这类三元组,SecureML同时给出了无需加密提供者帮助、由服务器自行生成三元组的技术。
卷积
类似密集层,卷积可以被看作一系列标量乘法或矩阵乘法,尽管后者首先需要将训练样本的张量扩展为带有很多冗余的矩阵。一点也不让人惊讶的是,两者均导致通讯成本增加,通过引入另一种三元组,可以加以改进。
举个例子,第一卷积层使用32个形状为(3, 3, 1)的核将形状为(m, 28, 28, 1)的张量映射为(m, 28, 28, 32)的张量(不考虑偏置向量)。对于batch尺寸m == 32而言,如果我们仅仅使用标量乘法,这意味着7,225,344个通讯元素,如果我们使用矩阵乘法,则是226,080个通讯元素。然而,由于总共只涉及(32*28*28) + (32*3*3) == 25,376个私密值(同样不计算偏置向量,因为它们仅仅需要加法),我们看到这里大概有9倍的额外开销。换句话说,每个私密值都被掩码和发送了好几次。基于一种新的三元组,我们可以消除这一额外开销,节省通讯成本:对64位元素而言,这意味这每batch的成本为200KB,而不是相应的1.7MB和55MB。
我们这里需要的三元组(a, b, c)和点积中使用的类似,a和b具有匹配输入的形状,即(m, 28, 28, 1)和(32, 3, 3, 1),而c则匹配输出形状(m, 28, 28, 32)。
sigmoid激活
如同我们先前做的那样,我们可以使用9项多项式来逼近sigmoid激活函数至足够的精确程度。为私密值x演算这一多项式的值需要计算一系列x的次方,这些当然可以通过一系列乘法来完成——但这意味着许多回合和相应数量的通讯。
作为替代,我们一样可以使用一种新的三元组,该三元组允许我们在一个回合中计算所有需要的次方。这些“三元组”的长度不是固定的,等于最高的指数,比如对应平方的三元组包含a和a**2的独立共享,而对应立方的三元组包含a、a**2、a**3的独立共享。
一旦我们具备了这些x的次方值,演算带有公开系数的多项式就仅仅是本地的加权总和了。这一计算的安全性同样来自于三元组中的所有次方是独立共享的。
def pol_public(x, coeffs, triple):
powers = pows(x, triple)
return sum( xe * ce for xe, ce in zip(powers, coeffs) )
和先前一样,我们会遇到关于定点数精度的坑,即次方的更高精度要求更多的空间:x**n有n倍x的精度,而我们想要确保它不会在以Q为模时溢出以致我们无法正确解码。我们可以通过引入一个足够大的域P,在计算次方时临时切换过去,代价是额外的两回合通讯。
实践中的试验将表明到底是保持Q使用更多的乘法回合更好,还是进行切换支付大数转换和算术的代价更好。特别地,对低阶多项式而言,前者看起来更好。
概念证明实现
有一个不带网络的概念证明实现可供实验和重现。该实现尚未完工,目前代码支持基于加密特征训练一个新分类器,但不支持从加密图像中提取特征。换句话说,它假定输入提供者自行在特征提取层中运行图像,然后将结果以加密形式发送给服务器;因此,模型相应部分的权重目前而言并未保持私密。以后的版本将处理这一点,使特征层可以在加密数据上运行,从而直接基于图像训练和预测。
from pond.nn importSequential, Dense, Sigmoid, Dropout, Reveal, Softmax, CrossEntropy
from pond.tensor importPrivateEncodedTensor
classifier = Sequential([
Dense(128, 6272),
Sigmoid(),
Dropout(.5),
Dense(5, 128),
Reveal(),
Softmax()
])
classifier.initialize()
classifier.fit(
PrivateEncodedTensor(x_train_features),
PrivateEncodedTensor(y_train),
loss=CrossEntropy(),
epochs=3
)
代码分成几个Python notebook,带有预计算的权重,所以你也可以跳过某些步骤:
第一个notebook使用Keras处理公开数据上的预训练,并为特征提取生成模型。可以跳过这一步,转而使用仓库中的预计算权重。
第二个notebook将上面的模型应用于私密数据上的特征提取,从而生成用于训练新的加密分类器的特征。以后的版本将首先加密数据。这一步无法省略,因为提取的数据太大了。
第三个notebook接受提取的特征,并训练一个新的加密分类器。这是目前为止最昂贵的一步,可以通过使用仓库中的预计算权重跳过。
最后,第四个notebook使用新的分类器在新图像上进行加密预测。同样,特征提取目前是未加密的。
运行以上代码需要先克隆仓库
$ git clone https://github.com/mortendahl/privateml.git && \
cd privateml/image-analysis/
安装依赖
$ pip3 install jupyter numpy tensorflow keras h5py
运行notebook
$ jupyter notebook
想法
一如既往,当先前的想法和疑问得到解答后,早已有一批新的等在那里了。
推广三元组
尝试减少通讯的时候,有人可能会好奇通过使用额外的三元组,有多少工作可以转移到预处理阶段完成。
前面已经好几次提到了(同时也是BCG+’17等论文的主张),我们通常寻求确保每个私密值只发送掩码一次。所以,如果我们,比如说,同时计算dot(x, y)和dot(x, z),那么,有一个三元组(r, s, t, u, v)会是有意义的,其中,r用于掩码x,s用于掩码y,u用于掩码z,而t和u用于计算结果。比如,这一模式在训练时出现,在前向传播时计算的值有时可以被缓存下来,在反向传播时加以复用。
不过,也许更重要的是我们仅仅基于一个模型做出预测的时候,即,基于固定的私有权重进行计算。在这一情形下,我们想要只掩码权重一次然后在每次预测时加以复用。进行这样的操作意味着我们的掩码和通讯数量与通过模型的输入向量成正比,而不是与输入向量和权重成正比,JVC’18之类的论文就是这么做的。更一般地,理想情况下,我们想要通讯只与变动的值成正比,这可以通过特制的三元组(在分期付款的意义上)达成。
最后,原则上可以让三元组执行更多的功能,比如在一回合的通讯中同时演算密集层和它的激活函数,但最大的阻碍看起来是可伸缩性问题,包括三元组的储存,和重组步骤中需要进行的计算量,特别是处理张量时。
激活函数
一个自然的问题是其他哪些典型的激活函数在加密配置下比较高效。如前所述,SecureML通过临时切换到乱码电路来使用ReLU,而CryptoDL给出了sigmoid、ReLU、Tanh的低阶多项式逼近(通了提高精确度,使用了切比雪夫多项式)。
也许有必要考虑更简单的非典型激活函数,比如CryptoNet等使用的平方,如果简化计算和通讯最重要的话。
乱码电路
前文提到通过使用乱码电路更安全地演算更高级的激活函数,实际上,乱码电路还可以用于更大的部分,包括作为安全计算的主要手段,像DeepSecure等所做的那样。
和SPDZ之类的技术相比,乱码电路的优势是仅使用固定数目的通讯回合。缺点是操作常常发生在字节上,而不是相对而言较大的域元素上,这意味着涉及更多计算。
精度
大量围绕联合学习(federated learning)的研究涉及梯度压缩(gradient compression)以便减少通讯成本。接近我们设定的是BMMP’17,基于量子化应用同态加密到深度学习上,甚至未加密的生产环境就绪系统也常常考虑这一技术,以提高学习的性能。
浮点数运算
上面我们使用定点数将实数编码为有限域元素,而未加密深度学习通常使用浮点数编码。如ABZS’12和SPDZ的参考实现所展示的那样,在加密设定下使用浮点数编码也是可能的,显然浮点数编码在某些操作上性能更有优势。
出于性能考虑,今时今日深度学习通常在GPU上进行,因此很自然地就想到是否可以应用类似的加速手段到MPC计算上。乱码电路已经有这方面的工作,而SPDZ之类的安全共享设定中这看起来不是那么流行。
这里面临的最大问题可能是GPU上的任意精度算术的成熟度和实用度(不过,其实已经有一些这方面的研究了),因为在较大的域元素(比如64位之上)上的计算需要这个。不过,这里该记住两点:首先,尽管我们计算的域元素大于那些原生支持的,它们仍然是有界的(模数);其次,我们可以在环上(而不是域上)进行我们的安全计算。
-
神经网络
+关注
关注
42文章
4762浏览量
100517 -
机器学习
+关注
关注
66文章
8373浏览量
132391
原文标题:基于Keras实现加密卷积神经网络
文章出处:【微信号:jqr_AI,微信公众号:论智】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论