OpenMined / TenSEAL

A library for doing homomorphic encryption operations on tensors
Apache License 2.0
811 stars 156 forks source link

FR: Investigate Microsoft EVA support #279

Open bcebere opened 3 years ago

bcebere commented 3 years ago

Feature Description

We need to investigate the support for HE compilers.

Microsoft EVA is a compiler for SEAL. https://github.com/microsoft/EVA Paper: https://arxiv.org/pdf/1912.11951.pdf

Update this task with any findings related to the topic.

comidan commented 3 years ago

Hello @bcebere, I've been trying to use EVA for a while in order to understand it better and because I think it could be useful for my thesis, I don't know if this will be useful or if I maybe misunderstood the request.

EVA is not very well documented but they provide access through Python to their compiler via PyEVA and there are some provide examples. This makes quite easy to write, for example, a ML model and to make it evaluated by EVA which will provide the minimum required CKKS HE paramaters (EVA is only CKKS compatible) by doing graph search on a graph built from the code which is provided to EVA.

Here is a toy example just to get some HE parameters:

from eva import EvaProgram, Input, Output, evaluate
from eva import evaluate
from eva.ckks import CKKSCompiler
from eva.seal import generate_keys
from eva.metric import valuation_mse
from common import *
from random import uniform
import numpy as np
import unittest
import math

batch = 2
dim = 4
dim1 = 4

def linear(x, y):
    return np.matmul(x, y) # + bias

def attention(x):
    temp = np.array([i for i in range(dim*dim1)]).reshape(dim, dim1) / 1000
    query = linear(x, temp)
    key = linear(x, temp)
    value = linear(x, temp)
    return query, key, value

def self_attention(query, key, value):
    temp = linear(query, key)

    temp = linear(temp, np.arange(dim*dim).reshape(dim,dim))

    temp = linear(temp, value)
    return temp

def norm(x):
    return linear(x, np.arange(dim*dim).reshape(dim,dim))

def ff(x):
    return linear(linear(x, np.arange(dim*dim).reshape(dim, dim)/1000), np.arange(dim*dim).reshape(dim, dim)/1000)

def classifier(x):
    v = np.arange(dim*2).reshape(dim,2)/1000
    w = np.arange(2*2).reshape(2,2)/1000
    return np.dot(np.dot(x, v), w)

transformer = EvaProgram('transformer', vec_size=dim*dim1)
with transformer:
    x = np.array([Input(f'x{i}') for i in range(batch*dim*dim1)]).reshape(batch, dim, dim1)
    print(x.shape)
    query, key, value = attention(x)
    attn = self_attention(query, key, value)
    out = norm(attn)
    out = ff(out)
    out = out ** 2
    out = norm(out)
    out = out ** 2
    out = classifier(out)
    print(out.shape)
    for i in range(out.shape[0]):
        for j in range(out.shape[1]):
            for k in range(out.shape[2]):
                Output(f'y{i+j}', out[i][j][k])

transformer.set_input_scales(32)
transformer.set_output_ranges(32)

if __name__ == "__main__":
    compiler = CKKSCompiler(config={'security_level':'128', 'warn_vec_size':'false'})
    compiled, params, signature = compiler.compile(transformer)
    print("poly degree: ", params.poly_modulus_degree)
    print("prime bits: ", params.prime_bits)
    print("rotations: ", params.rotations)

As you can see it can be just written any kind of code under the EvaProgram class which will be what EVA will use to know which code needs evaluation, and you can also use further libraries as numpy to ease the coding.

EVA also let you confiugure both the input and output scales, even with different values. At the end of the computation you can access the parameters as it is shown in the image. No documentation is provided for that, I just looked at how the C++ - Python bindings have been made.

There are also some provided functions like valuation_mse which will let you effectively evaluate how much approximated error there is using these parameters.

The output parameters will look like these:

image

One interesting thing is the rotations parameter, which is usually an empty set but when you try to use bit shift operations it will fill with values (I didn't know they were needed).

One sad thing is scalability, I really can't truly evaluate a model with such small input values but I need to scale it a bit more as input size. The problem is that EVA by itself seems to not be exploiting multiple cores of a CPU (by looking at the usage via htop) and so it's slow for slightly bigger models to be evaluated in HE parameters, even if libraries such as Numpy are being used. I opened this but so far no one has answered me and I don't know whether the will, unfortunately they are not as fast as you! :)

Anyway EVA is really good, you are able to obtain CKKS HE parameters automatically without having an extensive knowledge of the CKKS HE cipher, as it can be seen from this picture taken from their presentation where the general architecture is shown:

image

bcebere commented 3 years ago

@comidan Thank you for the detailed explanation!

Yes, we are very interested in EVA, and we plan to integrate TenSEAL with EVA in the following weeks. It is a major improvement in usability, for sure.

Regarding multithreading - the EVA docs mention that you can enable multicore support by recompiling the library with cmake -DUSE_GALOIS=ON .

Maybe that helps!

Also, you can use the generated parameters with TenSEAL as well.

comidan commented 3 years ago

I agree!

Thank you very much @bcebere, I don't know how I didn't see it.

Yes exactly, that's what I've been doing to test the multiplicative depth via TenSEAL with the given parameters found by EVA

olsaarik commented 3 years ago

Hi! The -DUSE_GALOIS=ON support had a bug that I recently fixed in the main branch of EVA. It'll now use all the cores available to the process by default.