奧胡斯大學密碼學PhD、Datadog機器學習工程師Morten Dahl介紹了如何基于安全多方計算協(xié)議實現(xiàn)私密深度學習模型。
受最近一篇混合深度學習和同態(tài)加密的博客的啟發(fā)(見基于Numpy實現(xiàn)同態(tài)加密神經(jīng)網(wǎng)絡),我覺得使用安全多方計算(secure multi-party computation)替換同態(tài)加密實現(xiàn)深度學習會很有趣。
在本文中,我們將從頭構建一個簡單的安全多方計算協(xié)議,然后嘗試基于它訓練簡單的神經(jīng)網(wǎng)絡進行基本布爾值計算。本文的相關代碼可以通過GitHub取得(mortendahl/privateml/simple-boolean-functions)。 假設有未串通的三方P0、P1、P2,愿意一起進行計算,即訓練神經(jīng)網(wǎng)絡并使用它們進行預測;然而,出于一些理由,他們不愿意泄露學習好的模型。同時假定有些用戶愿意在保持私密的前提下提供訓練數(shù)據(jù),有些用戶也有興趣在他們的輸入保持私密的前提下使用學習好的模型。
為了能夠做到這一點,我們需要安全地在特定精度下計算有理數(shù);具體而言,對它們做加法和乘法。我們同時需要計算sigmoid函數(shù)1/(1+np.exp(-x)),這一函數(shù)的傳統(tǒng)形式在安全設定下會導致驚人沉重的運算。因此,我們將依照基于Numpy實現(xiàn)同態(tài)加密神經(jīng)網(wǎng)絡中的做法,使用多項式逼近sigmoid函數(shù),不過我們會進行一點優(yōu)化。
安全多方計算
同態(tài)加密(Homomorphic Encryption,HE)和安全多方計算(secure Multi-Party Computation,MPC)是現(xiàn)代密碼學中密切相關的兩個領域,常常互相使用對方的技術以便解決大致相同的問題:計算接受私密數(shù)據(jù)輸入的函數(shù)而不泄露任何東西,除了(可選)最終輸出。例如,在我們的私密機器學習設定下,兩種技術可以用來訓練我們的模型并進行預測(不過在HE的情形下,如果數(shù)據(jù)來自使用不同加密鑰的用戶,需要做一些專門的技術處理)。
就其本身而言,從高層看,HE經(jīng)常可以用MPC替換,反之亦然。至少就今天而言,兩者的區(qū)別大致是HE不怎么需要交互,但是需要昂貴的計算,而MPC的計算很廉價,但需要大量交互。換句話說,MCP用兩方或多方間的交互取代了昂貴的計算。
目前而言,這在實踐中提供了更好的性能,以至于人們可以主張MCP是明顯更成熟的技術——作為這一主張的依據(jù),已經(jīng)存在好幾家公司提供基于MPC的服務。
定點數(shù)算術
運算將在一個有限域上進行,因此我們首先需要決定如何將有理數(shù)r表示為域元素,即取自0, 1, ..., Q-1的整數(shù)x(Q為質數(shù))。我們將采用典型的做法,根據(jù)固定的精度放大每個有理數(shù),比如,在6位精度的情形下,我們將放大10**6倍,然后將結果的整數(shù)部分作為定點數(shù)表示。例如,Q = 10000019時,我們得到encode(0.5) == 500000和encode(-0.5) == 10000019 - 500000 == 9500019。
def encode(rational):
upscaled = int(rational * 10**6)
field_element = upscaled % Q
return field_element
def decode(field_element):
upscaled = field_element if field_element <= Q/2else field_element - Q
rational = upscaled / 10**6
return rational
注意,在這一表示下,加法是直截了當?shù)模?r * 10**6) + (s * 10**6) == (r + s) * 10**6,而乘法添加了額外的放大因子,我們需要處理掉以保持精度和避免爆掉數(shù)字:(r * 10**6) * (s * 10**6) == (r * s) * 10**6 * 10**6。
共享和重建數(shù)據(jù)
編碼輸入后,每個用戶接著需要一種和他方共享數(shù)據(jù)的方式,以便用于計算,不過,數(shù)據(jù)需要保持私密。
為了達到這一點,我們需要的配料是秘密共享(secret sharing)。秘密共享將一個值以某種方式分成三份,任何見到少于三份數(shù)據(jù)的人,無法得知關于值的任何信息;然而,一旦見到所有三份,可以輕易地重建值。
出于簡單性考慮,這里我們將使用復制秘密共享(replicated secret sharing),其中每方收到不止一份數(shù)據(jù)。具體而言,私密值x分成部分x0、x1、x2,滿足x == x0 + x1 + x2。P0方收到(x0,x1),P1收到(x1,x2),P2收到(x2,x0)。不過本教程中這一點將是隱式的,本文會直接將共享的x儲存為由三部分組成的向量[x0, x1, x2]。
def share(x):
x0 = random.randrange(Q)
x1 = random.randrange(Q)
x2 = (x - x0 - x1) % Q
return [x0, x1, x2]
當兩方以上同意將一個值表露給某人時,他們直接發(fā)送他們所有的部分,從而使重建得以進行。
def reconstruct(shares):
return sum(shares) % Q
然而,如果部分是以下小節(jié)提到的一次或多次安全運算的結果,出于私密性考慮,我們在重建前進行一次再共享。
def reshare(xs):
Y = [ share(xs[0]), share(xs[1]), share(xs[2]) ]
return [ sum(row) % Q for row in zip(*Y) ]
嚴格來說這是不必要的,但是進行這一步可以更容易地說明為什么協(xié)議是安全的;直觀地,它確保分享的部分是新鮮的,不包含關于我們用于計算結果的數(shù)據(jù)的信息。
加法和減法
這樣我們已經(jīng)可以進行安全的加法和減法運算了:每方直接加減其擁有的部分,由于(x0 + x1 + x2) + (y0 + y1 + y2) == (x0 + y0) + (x1 + y1) + (x2 + y2),通過這一操作可以得到x + y的三部分(技術上說應該是reconstruct(x) + reconstruct(y),但是隱式寫法更易讀)。
def add(x, y):
return [ (xi + yi) % Q for xi, yi in zip(x, y) ]
def sub(x, y):
return [ (xi - yi) % Q for xi, yi in zip(x, y) ]
注意這不需要進行任何通訊,因為這些都是本地運算。
乘法
由于每方擁有兩個部分,乘法可以通過類似上面提到的加法和減法的方式進行,即,每方基于已擁有的部分計算一個新部分。具體而言,對下面的代碼中定義的z0、z1、z2而言,我們有x * y == z0 + z1 + z2(技術上說……)
然而,每方擁有兩個部分的不變性沒有滿足,而像P1直接將z1發(fā)給P0這樣的做法是不安全的。一個簡單的修正是直接將每份zi當成私密輸入共享;這樣就得到了一個正確而安全的共享w(乘積)。
def mul(x, y):
# 本地運算
z0 = (x[0]*y[0] + x[0]*y[1] + x[1]*y[0]) % Q
z1 = (x[1]*y[1] + x[1]*y[2] + x[2]*y[1]) % Q
z2 = (x[2]*y[2] + x[2]*y[0] + x[0]*y[2]) % Q
# 重共享和分發(fā);這里需要通訊
Z = [ share(z0), share(z1), share(z2) ]
w = [ sum(row) % Q for row in zip(*Z) ]
# 將雙精度轉回單精度
v = truncate(w)
return v
不過還有一個問題,如前所述,reconstruct(w)具有雙精度:它編碼時使用的放大因子是10**6 * 10**6,而不是10**6。在不安全設定下,我們本可以通過標準的除法(除以10**6)來修正這一點,然而,由于我們操作的是有限域中的秘密共享元素,這變得不那么直截了當了。
除以一個公開的常量,這里是10**6,足夠簡單:我們直接將部分乘以其域中的逆元素10**(-6)。對某v和u < 10**6,如果reconstruct(w) == v * 10**6 + u,那么乘以逆元素得到v + u * 10**(-6),那么v就是我們要找到的值。在不安全設定下,殘值u * 10**(-6)足夠小,可以通過取整消除。與此不同,在安全設定下,基于有限域元素,這一語義丟失了,我們需要通過其他方法擺脫殘值。
一種方法是確保u == 0。具體而言,如果我們事先知道u,那么我們可以不對w作除法,而對w' == (w - share(u))作除法,接著我們就如愿以償,得到v' == v和u' == 0,即,沒有任何殘值。
剩下的問題當然是如何安全地得到u,以便計算w'。具體細節(jié)見CS’10,不過基本的思路是首先在w上加上一個大的掩碼,將掩碼后的值表露給其中一方,使其得以計算掩碼后的u。最后,共享和解掩碼這一掩碼后的值,然后計算w'。
def truncate(a):
# 映射到正值范圍
b = add(a, share(10**(6+6-1)))
# 應用僅有P0知道的掩碼,然后重建掩碼后的b,發(fā)送給P1或P2
mask = random.randrange(Q) % 10**(6+6+KAPPA)
mask_low = mask % 10**6
b_masked = reconstruct(add(b, share(mask)))
# 提取低位數(shù)字
b_masked_low = b_masked % 10**6
b_low = sub(share(b_masked_low), share(mask_low))
# 去除低位數(shù)字
c = sub(a, b_low)
# 除法
d = imul(c, INVERSE)
return d
注意上面的imul是本地操作,將每個共享部分乘以公開的常數(shù),這里是10**6的域中逆元素。
安全數(shù)據(jù)類型
最后,我們將以上過程包裹進一個定制的抽象數(shù)據(jù)類型,這樣我們之后表達神經(jīng)網(wǎng)絡的時候就可以使用NumPy了。
classSecureRational(object):
def __init__(self, secret=None):
self.shares = share(encode(secret)) if secret isnotNoneelse []
return z
def reveal(self):
return decode(reconstruct(reshare(self.shares)))
def __repr__(self):
return"SecureRational(%f)" % self.reveal()
def __add__(x, y):
z = SecureRational()
z.shares = add(x.shares, y.shares)
return z
def __sub__(x, y):
z = SecureRational()
z.shares = sub(x.shares, y.shares)
return z
def __mul__(x, y):
z = SecureRational()
z.shares = mul(x.shares, y.shares)
return z
def __pow__(x, e):
z = SecureRational(1)
for _ in range(e):
z = z * x
return z
基于這一類型,我們可以安全地對這樣的值進行操作:
x = SecureRational(.5)
y = SecureRational(-.25)
z = x * y
assert(z.reveal() == (.5) * (-.25))
此外,需要調試的時候,我們可以切換為不安全類型而不需要修改其余(神經(jīng)網(wǎng)絡)代碼。再比如,我們可以隔離計數(shù)器的使用,查看進行了多少次乘法,進而讓我們模擬下需要多少通訊。
深度學習
這里用“深度學習”這個術語屬于夸夸其談,因為我們只是簡單地擺弄了下基于Numpy實現(xiàn)同態(tài)加密神經(jīng)網(wǎng)絡中的神經(jīng)網(wǎng)絡學習基本布爾值函數(shù)。
一個簡單函數(shù)
第一個實驗是訓練網(wǎng)絡以識別序列中的第一位。下面的代碼中,X中的四行是輸入的訓練數(shù)據(jù),y中相應的列是所需輸出。
X = np.array([
[0,0,1],
[0,1,1],
[1,0,1],
[1,1,1]
])
y = np.array([[
0,
0,
1,
1
]]).T
我們將使用同樣的雙層網(wǎng)絡,不過我們會將下面定義的sigmoid逼近函數(shù)作為參數(shù)。secure函數(shù)是一個簡單的輔助函數(shù),將所有值轉換為我們的安全數(shù)據(jù)類型。
classTwoLayerNetwork:
def __init__(self, sigmoid):
self.sigmoid = sigmoid
def train(self, X, y, iterations=1000):
# 初始化權重
self.synapse0 = secure(2 * np.random.random((3,1)) - 1)
# 訓練
for i in range(iterations):
# 前向傳播
layer0 = X
layer1 = self.sigmoid.evaluate(np.dot(layer0, self.synapse0))
# 反向傳播
layer1_error = y - layer1
layer1_delta = layer1_error * self.sigmoid.derive(layer1)
# 更新
self.synapse0 += np.dot(layer0.T, layer1_delta)
def predict(self, X):
layer0 = X
layer1 = self.sigmoid.evaluate(np.dot(layer0, self.synapse0))
return layer1
同時,我們將使用原文提出的sigmoid逼近,即標準麥克勞林/泰勒多項式的前五項。出于可讀性考慮,我這里用了一個簡單多項式演算,有待進一步優(yōu)化,比如使用秦九韶算法減少乘法的數(shù)目。
classSigmoidMaclaurin5:
def __init__(self):
ONE = SecureRational(1)
W0 = SecureRational(1/2)
W1 = SecureRational(1/4)
W3 = SecureRational(-1/48)
W5 = SecureRational(1/480)
self.sigmoid = np.vectorize(lambda x: W0 + (x * W1) + (x**3 * W3) + (x**5 * W5))
self.sigmoid_deriv = np.vectorize(lambda x: (ONE - x) * x)
def evaluate(self, x):
return self.sigmoid(x)
def derive(self, x):
return self.sigmoid_deriv(x)
實現(xiàn)了這個之后我們就可以訓練和演算網(wǎng)絡了(細節(jié)見notebook),這里使用了10000次迭代。
# 設置隨機數(shù)種子以獲得可復現(xiàn)的結果
random.seed(1)
np.random.seed(1)
# 選擇逼近
sigmoid = SigmoidMaclaurin5()
# 訓練
network = TwoLayerNetwork(sigmoid)
network.train(secure(X), secure(y), 10000)
# 演算預測
evaluate(network)
注意訓練數(shù)據(jù)在輸入網(wǎng)絡之前是安全共享的,并且學習到的權重從未泄露。預測同理,只有網(wǎng)絡的用戶知道輸入和輸出。
Error: 0.00539115
Error: 0.0025606125
Error: 0.00167358
Error: 0.001241815
Error: 0.00098674
Error: 0.000818415
Error: 0.0006990725
Error: 0.0006100825
Error: 0.00054113
Error: 0.0004861775
Layer0 weights:
[[SecureRational(4.974135)]
[SecureRational(-0.000854)]
[SecureRational(-2.486387)]]
Prediction on [000]: 0 (0.50000000)
Prediction on [001]: 0 (0.00066431)
Prediction on [010]: 0 (0.49978657)
Prediction on [011]: 0 (0.00044076)
Prediction on [100]: 1 (5.52331855)
Prediction on [101]: 1 (0.99969213)
Prediction on [110]: 1 (5.51898314)
Prediction on [111]: 1 (0.99946841)
從上面的演算來看,神經(jīng)網(wǎng)絡確實看起來學習到了所要求的函數(shù),在未見輸入上也能給出正確的預測。
稍微高級些的函數(shù)
在下一個實驗中,神經(jīng)網(wǎng)絡無法像之前一樣鏡像三個組件的其中一個,從直觀上說,需要計算第一位和第二位的異或(第三位是偏離)。
X = np.array([
[0,0,1],
[0,1,1],
[1,0,1],
[1,1,1]
])
y = np.array([[
0,
1,
1,
0
]]).T
如Numpy實現(xiàn)神經(jīng)神經(jīng)網(wǎng)絡:反向傳播一文所解釋的,使用雙層神經(jīng)網(wǎng)絡只能給出無意義的結果,本質上是在說“讓我們扔一枚硬幣吧”。
Error: 0.500000005
Error: 0.5
Error: 0.5000000025
Error: 0.5000000025
Error: 0.5
Error: 0.5
Error: 0.5
Error: 0.5
Error: 0.5
Error: 0.5
Layer0 weights:
[[SecureRational(0.000000)]
[SecureRational(0.000000)]
[SecureRational(0.000000)]]
Prediction on [000]: 0 (0.50000000)
Prediction on [001]: 0 (0.50000000)
Prediction on [010]: 0 (0.50000000)
Prediction on [011]: 0 (0.50000000)
Prediction on [100]: 0 (0.50000000)
Prediction on [101]: 0 (0.50000000)
Prediction on [110]: 0 (0.50000000)
Prediction on [111]: 0 (0.50000000)
提議的補救措施是在網(wǎng)絡中引入另一層:
classThreeLayerNetwork:
def __init__(self, sigmoid):
self.sigmoid = sigmoid
def train(self, X, y, iterations=1000):
# 初始權重
self.synapse0 = secure(2 * np.random.random((3,4)) - 1)
self.synapse1 = secure(2 * np.random.random((4,1)) - 1)
# 訓練
for i in range(iterations):
# 前向傳播
layer0 = X
layer1 = self.sigmoid.evaluate(np.dot(layer0, self.synapse0))
layer2 = self.sigmoid.evaluate(np.dot(layer1, self.synapse1))
# 反向傳播
layer2_error = y - layer2
layer2_delta = layer2_error * self.sigmoid.derive(layer2)
layer1_error = np.dot(layer2_delta, self.synapse1.T)
layer1_delta = layer1_error * self.sigmoid.derive(layer1)
# 更新
self.synapse1 += np.dot(layer1.T, layer2_delta)
self.synapse0 += np.dot(layer0.T, layer1_delta)
def predict(self, X):
layer0 = X
layer1 = self.sigmoid.evaluate(np.dot(layer0, self.synapse0))
layer2 = self.sigmoid.evaluate(np.dot(layer1, self.synapse1))
return layer2
然而,如果我們采用之前的方式訓練網(wǎng)絡,即使僅僅迭代100次,我們都將面臨一個奇怪的現(xiàn)象:突然之間,誤差、權重、預測分數(shù)爆炸了,給出混亂的結果。
Error: 0.496326875
Error: 0.4963253375
Error: 0.50109445
Error: 4.50917445533e+22
Error: 4.20017387687e+22
Error: 4.38235385094e+22
Error: 4.65389939428e+22
Error: 4.25720845129e+22
Error: 4.50520005372e+22
Error: 4.31568874384e+22
Layer0 weights:
[[SecureRational(970463188850515564822528.000000)
SecureRational(1032362386093871682551808.000000)
SecureRational(1009706886834648285970432.000000)
SecureRational(852352894255113084862464.000000)]
[SecureRational(999182403614802557534208.000000)
SecureRational(747418473813466924711936.000000)
SecureRational(984098986255565992230912.000000)
SecureRational(865284701475152213311488.000000)]
[SecureRational(848400149667429499273216.000000)
SecureRational(871252067688430631387136.000000)
SecureRational(788722871059090631557120.000000)
SecureRational(868480811373827731750912.000000)]]
Layer1 weights:
[[SecureRational(818092877308528183738368.000000)]
[SecureRational(940782003999550335877120.000000)]
[SecureRational(909882533376693496709120.000000)]
[SecureRational(955267264038446787723264.000000)]]
Prediction on [000]: 1 (41452089757570437218304.00000000)
Prediction on [001]: 1 (46442301971509056372736.00000000)
Prediction on [010]: 1 (37164015478651618328576.00000000)
Prediction on [011]: 1 (43504970843252146044928.00000000)
Prediction on [100]: 1 (35282926617309558603776.00000000)
Prediction on [101]: 1 (47658769913438164484096.00000000)
Prediction on [110]: 1 (35957624290517111013376.00000000)
Prediction on [111]: 1 (47193714919561920249856.00000000)
導致這一切的原因很簡單,但也許乍看起來不是那么明顯(至少對我而言)。盡管(前五項)麥克勞林/泰勒逼近sigmoid函數(shù)在前面的網(wǎng)絡中表現(xiàn)良好,當我們進一步推進時,它完全崩塌了,產(chǎn)生的結果不僅不精確,而且數(shù)量級也不對。因此很快摧毀了我們可能使用的任何有窮數(shù)字表示,即使在非安全設定下也是如此,數(shù)字開始溢出了。
技術上說sigmoid函數(shù)演算的點積變得太大了,就我所知,這意味著神經(jīng)網(wǎng)絡變得非常自信。就此而言,問題在于我們的逼近不允許神經(jīng)網(wǎng)絡變得足夠自信,否則精確度會非常糟糕。
我不清楚基于Numpy實現(xiàn)同態(tài)加密神經(jīng)網(wǎng)絡是如何避免這一問題的,我最好的猜測是較低的初始權重和alpha更新參數(shù)使它可能在迭代次數(shù)較低的情形下繞過這個坑(看起來是少于300次迭代)。無比歡迎任何關于這方面的評論。
逼近sigmoid
既然是我們的sigmoid逼近阻礙了我們學習更高級的函數(shù),那么很自然地,我們接下來嘗試使用麥克勞林/泰勒多項式的更多項。
如下所示,加到第9項(而不是第5項)確實能稍微增加一點進展,但這點進展遠遠不夠。此外,它塌得更快了。
Error: 0.49546145
Error: 0.4943132225
Error: 0.49390536
Error: 0.50914575
Error: 7.29251498137e+22
Error: 7.97702462371e+22
Error: 7.01752029207e+22
Error: 7.41001528681e+22
Error: 7.33032620012e+22
Error: 7.3022511184e+22
...
或者我們該轉而使用更少的項以更好地牽制崩塌?比如,只加到第3項?這確實有點作用,能讓我們在崩塌之前訓練500次迭代而不是100次。
Error: 0.4821573275
Error: 0.46344183
Error: 0.4428059575
Error: 0.4168092675
Error: 0.388153325
Error: 0.3619875475
Error: 0.3025045425
Error: 0.2366579675
Error: 0.19651228
Error: 0.1748352775
Layer0 weights:
[[SecureRational(1.455894) SecureRational(1.376838)
SecureRational(-1.445690) SecureRational(-2.383619)]
[SecureRational(-0.794408) SecureRational(-2.069235)
SecureRational(-1.870023) SecureRational(-1.734243)]
[SecureRational(0.712099) SecureRational(-0.688947)
SecureRational(0.740605) SecureRational(2.890812)]]
Layer1 weights:
[[SecureRational(-2.893681)]
[SecureRational(6.238205)]
[SecureRational(-7.945379)]
[SecureRational(4.674321)]]
Prediction on [000]: 1 (0.50918230)
Prediction on [001]: 0 (0.16883382)
Prediction on [010]: 0 (0.40589161)
Prediction on [011]: 1 (0.82447640)
Prediction on [100]: 1 (0.83164009)
Prediction on [101]: 1 (0.83317334)
Prediction on [110]: 1 (0.74354671)
Prediction on [111]: 0 (0.18736629)
然而,誤差和預測很糟糕,也沒有多少空間供增加迭代次數(shù)了(大約在550次迭代處崩塌)。
插值
作為替代,我們可以放棄標準多項式逼近,轉而嘗試在區(qū)間上進行多項式插值。這里主要的參數(shù)是多項式的項數(shù),我們希望它保持在一個較低的值,以提高效率。不過,系數(shù)的精度也是相關參數(shù)。
# 我們想要逼近的函數(shù)
f_real = lambda x: 1/(1+np.exp(-x))
# 我們想要優(yōu)化的區(qū)間
interval = np.linspace(-10, 10, 100)
# 給定項數(shù),進行多項式插值
degree = 10
coefs = np.polyfit(interval, f_real(interval), degree)
# 降低插值系數(shù)的精度
precision = 10
coefs = [ int(x * 10**precision) / 10**precision for x in coefs ]
# 逼近函數(shù)
f_interpolated = np.poly1d(coefs)
一同繪制標準逼近和插值多項式(紅色曲線)的圖像我們看到了改進的希望:我們無法避免在某點崩塌,但它的崩塌點顯然要大很多。
當然,我們也可以嘗試其他項數(shù)、精度、區(qū)間的組合,如下所示,不過對我們的應用而言,上面的參數(shù)組合看起來已經(jīng)足夠了。
現(xiàn)在讓我們回到我們的三層網(wǎng)絡,我們定義一個新的Sigmoid逼近:
classSigmoidInterpolated10:
def __init__(self):
ONE = SecureRational(1)
W0 = SecureRational(0.5)
W1 = SecureRational(0.2159198015)
W3 = SecureRational(-0.0082176259)
W5 = SecureRational(0.0001825597)
W7 = SecureRational(-0.0000018848)
W9 = SecureRational(0.0000000072)
self.sigmoid = np.vectorize(lambda x: \
W0 + (x * W1) + (x**3 * W3) + (x**5 * W5) + (x**7 * W7) + (x**9 * W9))
self.sigmoid_deriv = np.vectorize(lambda x:(ONE - x) * x)
def evaluate(self, x):
return self.sigmoid(x)
def derive(self, x):
return self.sigmoid_deriv(x)
……然后開始訓練:
# 設置隨機數(shù)種子以獲得可復現(xiàn)的結果
random.seed(1)
np.random.seed(1)
# 選擇逼近
sigmoid = SigmoidInterpolated10()
# 訓練
network = TwoLayerNetwork(sigmoid)
network.train(secure(X), secure(y), 10000)
# 演算預測
evaluate(network)
現(xiàn)在,盡管我們運行了10000次迭代,沒有發(fā)生崩塌,預測表現(xiàn)也提升了,只有一個預測錯誤([0 1 0])。
Error: 0.0384136825
Error: 0.01946007
Error: 0.0141456075
Error: 0.0115575225
Error: 0.010008035
Error: 0.0089747225
Error: 0.0082400825
Error: 0.00769687
Error: 0.007286195
Error: 0.00697363
Layer0 weights:
[[SecureRational(3.208028) SecureRational(3.359444)
SecureRational(-3.632461) SecureRational(-4.094379)]
[SecureRational(-1.552827) SecureRational(-4.403901)
SecureRational(-3.997194) SecureRational(-3.271171)]
[SecureRational(0.695226) SecureRational(-1.560569)
SecureRational(1.758733) SecureRational(5.425429)]]
Layer1 weights:
[[SecureRational(-4.674311)]
[SecureRational(5.910466)]
[SecureRational(-9.854162)]
[SecureRational(6.508941)]]
Prediction on [000]: 0 (0.28170669)
Prediction on [001]: 0 (0.00638341)
Prediction on [010]: 0 (0.33542098)
Prediction on [011]: 1 (0.99287968)
Prediction on [100]: 1 (0.74297185)
Prediction on [101]: 1 (0.99361066)
Prediction on [110]: 0 (0.03599433)
Prediction on [111]: 0 (0.00800036)
注意錯誤情形的分數(shù)不是完全偏離,某種程度上和正確預測的零值有所不同。再運行5000次迭代看起來不會改善這一點,這時我們已經(jīng)快要崩塌了。
結語
本文重點介紹了一個簡單的安全多方計算協(xié)議,而沒有顯式地論證開頭提到的安全多方計算比同態(tài)加密更高效。我們看到,使用非?;镜牟僮鲗崿F(xiàn)私密機器學習確實是可能的。
也許更需要批評的是我們沒有測量運行協(xié)議需要的通訊量,主要是每次乘法時所需交換的一些消息?;谶@一簡單的協(xié)議進行大量計算的話,顯然讓三方通過高速局域網(wǎng)連接會比較好。不過更高級的協(xié)議不僅減少了來回收發(fā)的數(shù)據(jù)量,同時改善了其他性質,比如回合數(shù)(像亂碼電路(garbled circuits)就可以將回合數(shù)壓到一個小常數(shù))。
最后,本文基本上將協(xié)議和機器學習過程看成是正交的,讓后者僅僅以一種黑盒的方式使用前者(除了sigmoid計算)。兩者更好的配合需要兩個領域的專門知識,但可能顯著地提升總體性能。
-
神經(jīng)網(wǎng)絡
+關注
關注
42文章
4749瀏覽量
100433 -
深度學習
+關注
關注
73文章
5471瀏覽量
120903
原文標題:基于安全多方計算協(xié)議實現(xiàn)私密深度學習模型
文章出處:【微信號:jqr_AI,微信公眾號:論智】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論