Exemple #1
0
        public void TestNNBinary_NoEncryption()
        {
            // this test exists simply to demonstrate that we have the capability of evaluating the ML model on some test data without the homomorphic encryption scheme
            // essentially, it serves to make sure the computations we will be performing on the encrypted data will be correct

            double[][] testX = new double[5][];
            testX[0] = new double[] { 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0 };
            testX[1] = new double[] { 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 };
            testX[2] = new double[] { 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0 };
            testX[3] = new double[] { 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0 };
            testX[4] = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0 };

            bool[] testYs = { true, false, true, false, false };

            var weights = NNConstants.GetNNBinaryWeights();
            var biases  = NNConstants.GetNNBinaryBiases();
            List <double[][]> TestFFOutputs        = new List <double[][]>();
            List <double[][]> TestActivationInputs = new List <double[][]>();

            for (int testI = 0; testI < testX.Length; testI++)
            {
                var FFOutputs        = NNConstants.GetEmptyFFVectors(weights);
                var ActivationInputs = NNConstants.GetEmptyFFVectors(weights);
                for (int layer = 0; layer < weights.Length; layer++)
                {
                    var numLayerInputs  = weights[layer].Length;
                    var numLayerOutputs = weights[layer][0].Length;
                    for (int ffOutputIndex = 0; ffOutputIndex < numLayerOutputs; ffOutputIndex++)
                    {
                        double nodeOutput = 0.0;
                        for (int inputIndex = 0; inputIndex < numLayerInputs; inputIndex++)
                        {
                            if (layer == 0)
                            {
                                nodeOutput += testX[testI][inputIndex] * weights[layer][inputIndex][ffOutputIndex];
                            }
                            else
                            {
                                nodeOutput += FFOutputs[layer - 1][inputIndex] * weights[layer][inputIndex][ffOutputIndex];
                            }
                        }
                        nodeOutput += biases[layer][ffOutputIndex];
                        if (layer < (weights.Length - 1))
                        {
                            ActivationInputs[layer][ffOutputIndex] = nodeOutput;
                            nodeOutput *= nodeOutput;
                        }
                        else
                        {
                            ActivationInputs[layer][ffOutputIndex] = nodeOutput;
                            nodeOutput = NNConstants.Sigmoid(nodeOutput);
                        }
                        FFOutputs[layer][ffOutputIndex] = nodeOutput;
                    }
                }
                TestFFOutputs.Add(FFOutputs);
                TestActivationInputs.Add(ActivationInputs);
                Assert.AreEqual(FFOutputs[weights.Length - 1][0] >= 0.5, testYs[testI]);
            }
        }
Exemple #2
0
        public void TestNNBinary_Encryption()
        {
            double[][] testX = new double[5][];
            testX[0] = new double[] { 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0 };
            testX[1] = new double[] { 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 };
            testX[2] = new double[] { 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0 };
            testX[3] = new double[] { 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0 };
            testX[4] = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0 };

            bool[] testYs = { true, false, true, false, false };

            // setup the encryptor and other various components
            EncryptionParameters parms = new EncryptionParameters(SchemeType.CKKS);

            parms.PolyModulusDegree = 32768;
            parms.CoeffModulus      = DefaultParams.CoeffModulus256(polyModulusDegree: 32768);
            SEALContext  context   = SEALContext.Create(parms);
            CKKSEncoder  encoder   = new CKKSEncoder(context);
            KeyGenerator keygen    = new KeyGenerator(context);
            PublicKey    publicKey = keygen.PublicKey;
            SecretKey    secretKey = keygen.SecretKey;
            RelinKeys    relinKeys = keygen.RelinKeys(decompositionBitCount: DefaultParams.DBCmax);
            Encryptor    encryptor = new Encryptor(context, publicKey);
            Evaluator    evaluator = new Evaluator(context);
            Decryptor    decryptor = new Decryptor(context, secretKey);
            double       scale     = Math.Pow(2.0, 30);

            // encrypt the input features
            List <List <Ciphertext> > featureCiphers = new List <List <Ciphertext> >();

            for (int i = 0; i < testX.Length; i++)
            {
                List <Ciphertext> curFeatureCiphers = new List <Ciphertext>();
                foreach (var featureVal in testX[i])
                {
                    List <double> featureVector = new double[] { featureVal }.ToList();
                    Plaintext     plain = new Plaintext();
                    encoder.Encode(featureVal, scale, plain);
                    Ciphertext encrypted = new Ciphertext();
                    encryptor.Encrypt(plain, encrypted);
                    curFeatureCiphers.Add(encrypted);
                }
                featureCiphers.Add(curFeatureCiphers);
            }

            /*
             *
             * This represents the initial border between the client and the server
             * The only data that should cross this line is the public key computed above, and the cipher texts of the ML model features
             *
             * */


            var weights        = NNConstants.GetNNBinaryWeights();
            var weightsEncoded = NNConstants.GetWeightsPlaintext(encoder, scale, weights);
            var biases         = NNConstants.GetNNBinaryBiases();
            List <Ciphertext[][]> TestFFOutputs        = new List <Ciphertext[][]>();
            List <Ciphertext[][]> TestActivationInputs = new List <Ciphertext[][]>();

            for (int testI = 0; testI < testX.Length; testI++)
            {
                var FFOutputs        = NNConstants.GetEmptyFFCipherVectors(weights);
                var ActivationInputs = NNConstants.GetEmptyFFCipherVectors(weights);
                for (int layer = 0; layer < weights.Length; layer++)
                {
                    var numLayerInputs  = weights[layer].Length;
                    var numLayerOutputs = weights[layer][0].Length;
                    for (int ffOutputIndex = 0; ffOutputIndex < numLayerOutputs; ffOutputIndex++)
                    {
                        List <Ciphertext> sumInputs = new List <Ciphertext>();
                        //double nodeOutput = 0.0;
                        for (int inputIndex = 0; inputIndex < numLayerInputs; inputIndex++)
                        {
                            if (layer == 0)
                            {
                                Ciphertext multResult = new Ciphertext();
                                evaluator.MultiplyPlain(featureCiphers[testI][inputIndex], weightsEncoded[layer][inputIndex][ffOutputIndex], multResult);
                                sumInputs.Add(multResult);
                            }
                            else
                            {
                                Ciphertext multResult = new Ciphertext();
                                bool       success    = false;
                                int        numTries   = 0;
                                while (!success && numTries < 4)
                                {
                                    try
                                    {
                                        evaluator.MultiplyPlain(FFOutputs[layer - 1][inputIndex], weightsEncoded[layer][inputIndex][ffOutputIndex], multResult);
                                        success = true;
                                    }
                                    catch (Exception ex)
                                    {
                                        evaluator.RelinearizeInplace(FFOutputs[layer - 1][inputIndex], relinKeys);
                                        evaluator.RescaleToNextInplace(FFOutputs[layer - 1][inputIndex]);
                                        numTries++;
                                        if (numTries >= 4)
                                        {
                                            throw ex;
                                        }
                                    }
                                }
                                sumInputs.Add(multResult);
                            }
                        }
                        Ciphertext activationInput = new Ciphertext();
                        evaluator.AddMany(sumInputs, activationInput);
                        Plaintext biasPT = new Plaintext();
                        encoder.Encode(biases[layer][ffOutputIndex], activationInput.Scale, biasPT);
                        evaluator.AddPlainInplace(activationInput, biasPT);
                        Ciphertext activationOutput = new Ciphertext();
                        if (layer < (weights.Length - 1))
                        {
                            ActivationInputs[layer][ffOutputIndex] = activationInput;
                            bool success  = false;
                            int  numTries = 0;
                            while (!success && numTries < 4)
                            {
                                try
                                {
                                    evaluator.Square(activationInput, activationOutput);
                                    success = true;
                                }
                                catch (Exception ex)
                                {
                                    evaluator.RelinearizeInplace(activationInput, relinKeys);
                                    evaluator.RescaleToNextInplace(activationInput);
                                    numTries++;
                                    if (numTries >= 4)
                                    {
                                        throw ex;
                                    }
                                }
                            }
                        }
                        else
                        {
                            ActivationInputs[layer][ffOutputIndex] = activationInput;
                            // note that normally, we would do a sigmoid on this - however, we cant do a sigmoid
                            // in the encrypted space (we are limited to simple add/multiply
                            // so we just copy it to the output - the client will use a >/< check to determine class
                            activationOutput = activationInput;
                        }
                        FFOutputs[layer][ffOutputIndex] = activationOutput;
                    }
                }
                TestFFOutputs.Add(FFOutputs);
                TestActivationInputs.Add(ActivationInputs);
            }

            /*
             *
             * This represents the next border between the client and the server
             * The only data that should cross this line is the encrypted output of running the ML model
             * In this case, the output decrypts to the input of a sigmoid - so the client will determine class membership
             * based on a > or < 0 boolean
             *
             * */

            List <List <double> > predictions = new List <List <double> >();

            for (int testI = 0; testI < TestFFOutputs.Count; testI++)
            {
                Plaintext plainResult     = new Plaintext();
                var       encryptedResult = TestFFOutputs[testI][2][0];
                decryptor.Decrypt(encryptedResult, plainResult);
                List <double> result = new List <double>();
                encoder.Decode(plainResult, result);
                predictions.Add(result);
            }

            for (int testI = 0; testI < TestFFOutputs.Count; testI++)
            {
                var  avgResult  = predictions[testI].Average();
                bool prediction = false;
                if (avgResult >= 0)
                {
                    prediction = true;
                }
                Assert.AreEqual(prediction, testYs[testI]);
            }
        }