From 45fcec427893897249d439c350644688815c0a9a Mon Sep 17 00:00:00 2001 From: George Muraru Date: Tue, 12 May 2020 23:41:17 +0300 Subject: [PATCH 1/3] Tutorials fix with set_all_parameters --- test/test_crypten.py | 13 +- ...ation_with_Encrypted_Neural_Networks.ipynb | 90 ++---- ...Under_the_hood_of_Encrypted_Networks.ipynb | 290 +++--------------- 3 files changed, 65 insertions(+), 328 deletions(-) diff --git a/test/test_crypten.py b/test/test_crypten.py index c8744dd2..4ef7c04a 100644 --- a/test/test_crypten.py +++ b/test/test_crypten.py @@ -283,12 +283,8 @@ def forward(self, input): return out def set_all_parameters(self, value): - self.fc1.weight.data.fill_(value) - self.fc1.bias.data.fill_(value) - self.fc2.weight.data.fill_(value) - self.fc2.bias.data.fill_(value) - self.fc3.weight.data.fill_(value) - self.fc3.bias.data.fill_(value) + for p in self.parameters(): + torch.nn.init.constant_(p, value) class NestedTestModule(nn.Module): @@ -302,9 +298,8 @@ def forward(self, input): out = self.nested(out) def set_all_parameters(self, value): - self.fc1.weight.data.fill_(value) - self.fc1.bias.data.fill_(value) - self.nested.set_all_parameters(value) + for p in self.parameters(): + torch.nn.init.constant_(p, value) # This code only runs when executing the file outside the test harness diff --git a/tutorials/Tutorial_4_Classification_with_Encrypted_Neural_Networks.ipynb b/tutorials/Tutorial_4_Classification_with_Encrypted_Neural_Networks.ipynb index 04cff3ef..035fb8ac 100644 --- a/tutorials/Tutorial_4_Classification_with_Encrypted_Neural_Networks.ipynb +++ b/tutorials/Tutorial_4_Classification_with_Encrypted_Neural_Networks.ipynb @@ -19,7 +19,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -36,7 +36,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -53,7 +53,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -74,7 +74,11 @@ " out = self.fc2(out)\n", " out = F.relu(out)\n", " out = self.fc3(out)\n", - " return out" + " return out\n", + " \n", + " def set_all_parameters(self, value):\n", + " for p in self.parameters():\n", + " nn.init.constant_(p, value)" ] }, { @@ -86,7 +90,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -109,7 +113,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -132,17 +136,9 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Model successfully encrypted: True\n" - ] - } - ], + "outputs": [], "source": [ "# Load pre-trained model to Alice\n", "dummy_model = AliceNet()\n", @@ -174,28 +170,9 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\tAccuracy: 99.0000\n", - "\tAccuracy: 99.0000\n" - ] - }, - { - "data": { - "text/plain": [ - "[None, None]" - ] - }, - "execution_count": 0, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "import crypten.mpc as mpc\n", "import crypten.communicator as comm\n", @@ -243,40 +220,9 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Output tensor encrypted: True\n", - "Output tensor encrypted: True\n", - "Decrypted labels:\n", - "Decrypted labels:\n", - " tensor([7, 2, 1, 0, 4, 1, 4, 9, 6, 9, 0, 6, 9, 0, 1, 5, 9, 7, 3, 4, 9, 6, 6, 5,\n", - " 4, 0, 7, 4, 0, 1, 3, 1, 3, 4, 7, 2, 7, 1, 2, 1, 1, 7, 4, 2, 3, 5, 1, 2,\n", - " 4, 4, 6, 3, 5, 5, 6, 0, 4, 1, 9, 5, 7, 8, 9, 3, 7, 4, 6, 4, 3, 0, 7, 0,\n", - " 2, 9, 1, 7, 3, 2, 9, 7, 7, 6, 2, 7, 8, 4, 7, 3, 6, 1, 3, 6, 9, 3, 1, 4,\n", - " 1, 7, 6, 9])\n", - " tensor([7, 2, 1, 0, 4, 1, 4, 9, 6, 9, 0, 6, 9, 0, 1, 5, 9, 7, 3, 4, 9, 6, 6, 5,\n", - " 4, 0, 7, 4, 0, 1, 3, 1, 3, 4, 7, 2, 7, 1, 2, 1, 1, 7, 4, 2, 3, 5, 1, 2,\n", - " 4, 4, 6, 3, 5, 5, 6, 0, 4, 1, 9, 5, 7, 8, 9, 3, 7, 4, 6, 4, 3, 0, 7, 0,\n", - " 2, 9, 1, 7, 3, 2, 9, 7, 7, 6, 2, 7, 8, 4, 7, 3, 6, 1, 3, 6, 9, 3, 1, 4,\n", - " 1, 7, 6, 9])\n" - ] - }, - { - "data": { - "text/plain": [ - "[None, None]" - ] - }, - "execution_count": 0, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "@mpc.run_multiprocess(world_size=2)\n", "def encrypt_model_and_data():\n", @@ -323,7 +269,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -383,7 +329,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.4" + "version": "3.7.6" } }, "nbformat": 4, diff --git a/tutorials/Tutorial_5_Under_the_hood_of_Encrypted_Networks.ipynb b/tutorials/Tutorial_5_Under_the_hood_of_Encrypted_Networks.ipynb index 563187c1..fa901426 100644 --- a/tutorials/Tutorial_5_Under_the_hood_of_Encrypted_Networks.ipynb +++ b/tutorials/Tutorial_5_Under_the_hood_of_Encrypted_Networks.ipynb @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -41,7 +41,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -52,27 +52,24 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Plaintext Weights: Parameter containing:\n", - "tensor([[ 0.1231, -0.3265, 0.2590, -0.0752],\n", - " [-0.2957, 0.1935, 0.2226, -0.0387]], requires_grad=True)\n", - "Plaintext Bias: Parameter containing:\n", - "tensor([ 0.1789, -0.0787], requires_grad=True)\n" - ] - } - ], + "outputs": [], "source": [ "import torch.nn as nn\n", + "import types\n", "\n", "# Instantiate single Linear layer\n", "layer_linear = nn.Linear(4, 2)\n", "\n", + "def set_all_parameters(module, value):\n", + " for p in module.parameters():\n", + " nn.init.constant_(p, value)\n", + "\n", + "# Make sure we have the set_all_parameters method added to the\n", + "# Linear Module since this is not native to PyTorch\n", + "nn.Linear.set_all_parameters = set_all_parameters\n", + "\n", "# The weights and the bias are initialized to small random values\n", "print(\"Plaintext Weights:\", layer_linear._parameters['weight'])\n", "print(\"Plaintext Bias:\", layer_linear._parameters['bias'])\n", @@ -95,38 +92,16 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Weights:\n", - " tensor([[ 2898316751075970645, -4579388503044133723, 5038242173536375749,\n", - " -1490107348288071119],\n", - " [-1557770914353334546, -2564120523467162291, -6444290604699277632,\n", - " -1291727171320069465]])\n", - "Bias:\n", - " tensor([2530322031011295021, 3780145476852328879])\n", - "\n", - "Decrypted result:\n", - " tensor([[ 0.4359, -0.0401],\n", - " [ 0.3171, -0.1922],\n", - " [ 0.2721, -0.2406]])\n" - ] - }, - { - "data": { - "text/plain": [ - "[None, None]" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "import crypten.mpc as mpc\n", "import crypten.communicator as comm\n", @@ -173,7 +148,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -203,26 +178,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Weights:\n", - " tensor([[ 3824570129683350441, -8540765741594476771, -3033476021071207225],\n", - " [-3600177681891051605, -5879382445495720845, 981762740632649929],\n", - " [-8080559122370361369, -1390179875616073746, 5513490018807349512]])\n", - "Bias:\n", - " tensor([-1285976053388686819, -5042828109819508063, -6784293398454048581])\n", - "\n", - "Plaintext result:\n", - " tensor([[5., 5., 5.],\n", - " [5., 5., 5.]])\n" - ] - } - ], + "outputs": [], "source": [ "@mpc.run_multiprocess(world_size=2)\n", "def forward_scaling_layer():\n", @@ -279,7 +237,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -307,7 +265,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -325,26 +283,18 @@ " out = self.fc1(x)\n", " out = F.relu(out)\n", " out = self.fc2(out)\n", - " return out" + " return out\n", + " \n", + " def set_all_parameters(self, value):\n", + " for p in self.parameters():\n", + " nn.init.constant_(p, value)" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch 99 Loss: 0.2470429241657257\n", - "Epoch 199 Loss: 0.08965438604354858\n", - "Epoch 299 Loss: 0.05166155472397804\n", - "Epoch 399 Loss: 0.03510778397321701\n", - "Epoch 499 Loss: 0.026072446256875992\n" - ] - } - ], + "outputs": [], "source": [ "# Train and save Alice's network\n", "model = AliceNet()\n", @@ -385,19 +335,9 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Name: 5 \tModule: \n", - "Name: 6 \tModule: \n", - "Name: output \tModule: \n" - ] - } - ], + "outputs": [], "source": [ "# Load the trained network to Alice\n", "model_plaintext = crypten.load(sample_trained_model_file, dummy_model=AliceNet(), src=ALICE)\n", @@ -423,7 +363,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -436,122 +376,9 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Rank: 0 First Linear Layer: Output Encrypted: True\n", - "Rank: 1 First Linear Layer: Output Encrypted: True\n", - "\n", - "\n", - "Rank: 1 Shares after First Linear Layer:tensor([[-6301967327924596124, 1441054799224171566, 6544478469984047445,\n", - " 5552031352886356103, 5272638317470990646, 7596515253675258249,\n", - " -69400202746828565, 5828418225954818579, -4749428913547144580,\n", - " 6606576454575994085, -8611449871174003033, -6879516893031986420,\n", - " -6036166085005125285, -4328313888449389705, -1251365986125740619,\n", - " 1495196102692730834, 6980508649455116950, -3898827469711590256,\n", - " -3534438480830590527, 7371155115760810984],\n", - " [-6302093306442423781, 1441068736631097953, 6544305404511755054,\n", - " 5552029867695634565, 5272413795589744532, 7596469503207299393,\n", - " -69427899322696412, 5828641617418962017, -4749404070611225973,\n", - " 6606436629940671374, -8611581346037569523, -6879276243271746212,\n", - " -6036089440109381038, -4328277524632069842, -1251216352309478973,\n", - " 1495192104950105102, 6980505109848126085, -3898641388919762689,\n", - " -3534384514954890154, 7371263553023027417],\n", - " [-6302196016530670846, 1441068422199275460, 6544443190226594007,\n", - " 5551897792045937027, 5272509097568418791, 7596546678139037730,\n", - " -69306306479188599, 5828478682830348811, -4749419202150460350,\n", - " 6606494462688830463, -8611676291652022053, -6879452222434676411,\n", - " -6035926685963266004, -4328281958910070566, -1251144220136015792,\n", - " 1495257173537946749, 6980624771196506827, -3898680292457191596,\n", - " -3534469608370045576, 7371273537554122402]])\n", - "Rank: 0 Shares after First Linear Layer:tensor([[ 6301967327924594800, -1441054799224176132, -6544478469984052362,\n", - " -5552031352886337494, -5272638317470974057, -7596515253675304938,\n", - " 69400202746838963, -5828418225954796072, 4749428913547168229,\n", - " -6606576454575990257, 8611449871174013243, 6879516893031998421,\n", - " 6036166085005223019, 4328313888449341972, 1251365986125818451,\n", - " -1495196102692711430, -6980508649455074221, 3898827469711585822,\n", - " 3534438480830525249, -7371155115760827632],\n", - " [ 6302093306442492083, -1441068736631147091, -6544305404511679859,\n", - " -5552029867695630280, -5272413795589733129, -7596469503207352552,\n", - " 69427899322731120, -5828641617418886389, 4749404070611239923,\n", - " -6606436629940739146, 8611581346037528727, 6879276243271785375,\n", - " 6036089440109297098, 4328277524632063893, 1251216352309547208,\n", - " -1495192104950139917, -6980505109848110538, 3898641388919730573,\n", - " 3534384514954863793, -7371263553022910235],\n", - " [ 6302196016530668683, -1441068422199236029, -6544443190226441867,\n", - " -5551897792045847705, -5272509097568441345, -7596546678139042775,\n", - " 69306306479163138, -5828478682830264817, 4749419202150444357,\n", - " -6606494462688755085, 8611676291652106198, 6879452222434649489,\n", - " 6035926685963163807, 4328281958910026926, 1251144220135913003,\n", - " -1495257173538037216, -6980624771196606705, 3898680292457184428,\n", - " 3534469608370108188, -7371273537554142112]])\n", - "\n", - "\n", - "Rank: 1 ReLU:\n", - " Output Encrypted: True\n", - "Rank: 0 ReLU:\n", - " Output Encrypted: True\n", - "\n", - "\n", - "Rank: 1 Shares after ReLU: tensor([[ 20335834269325, -1862188223396, -22422596809382, -122992649562725,\n", - " -10942144055156, -92661931292462, 36370634722858, -4303917317055,\n", - " -95427716802747, 29753629293586, -132682053296116, 4382837271338,\n", - " 43569228086006, 56871160109942, -96186304271390, -91283597225481,\n", - " 52829263593360, 55452337381297, 126612973214354, 15568818343686],\n", - " [ -17053860036317, 104771133945462, -60277585643193, 35958852287441,\n", - " -566669716469, -132755904050627, 76984972926220, -86073992441543,\n", - " 22045980827559, 4491870038315, 6641766124207, -55543264340776,\n", - " -41736636970115, 23671078654926, -24869162319256, 16410704404076,\n", - " -9730287511525, 94768289625541, 109861819356995, -70891427648082],\n", - " [ 116804010453248, 60503265100564, -112710955152682, 126888227914592,\n", - " -104831560868555, 12800177436538, -67091481533778, -121809366681463,\n", - " 33047461688871, 124048872586605, 131754259871630, -132092660148971,\n", - " -104098037363125, 133067523930206, 63709774860683, 117051494575337,\n", - " -84071442093666, -134046287225625, -25504913510647, 124269544358526]])\n", - "Rank: 0 Shares after ReLU: tensor([[ -20335834269325, 1862188223396, 22422596809382, 122992649581334,\n", - " 10942144071745, 92661931292462, -36370634712460, 4303917339562,\n", - " 95427716826396, -29753629289758, 132682053306326, -4382837259337,\n", - " -43569227988272, -56871160109942, 96186304349222, 91283597244885,\n", - " -52829263550631, -55452337381297, -126612973214354, -15568818343686],\n", - " [ 17053860104619, -104771133945462, 60277585718388, -35958852283156,\n", - " 566669727872, 132755904050627, -76984972891512, 86073992517171,\n", - " -22045980813609, -4491870038315, -6641766124207, 55543264379939,\n", - " 41736636970115, -23671078654926, 24869162387491, -16410704404076,\n", - " 9730287527072, -94768289625541, -109861819356995, 70891427765264],\n", - " [-116804010453248, -60503265061133, 112710955304822, -126888227825270,\n", - " 104831560868555, -12800177436538, 67091481533778, 121809366765457,\n", - " -33047461688871, -124048872511227, -131754259787485, 132092660148971,\n", - " 104098037363125, -133067523930206, -63709774860683, -117051494575337,\n", - " 84071442093666, 134046287225625, 25504913573259, -124269544358526]])\n", - "\n", - "\n", - "Rank: 0 Second Linear layer:\n", - " Output Encrypted: True\n", - "Rank: 1 Second Linear layer:\n", - " Output Encrypted: True\n", - "\n", - "\n", - "Rank: 1 Shares after Second Linear layer:tensor([[1882203379329708255, 177137915967743634],\n", - " [1882366010665404070, 177083972450471081],\n", - " [1882280922776987770, 177180435755488709]])\n", - "Rank: 0 Shares after Second Linear layer:tensor([[-1882203379329880952, -177137915967574144],\n", - " [-1882366010665264043, -177083972450557034],\n", - " [-1882280922776701637, -177180435755712083]])\n", - "\n", - "\n", - "Decrypted output:\n", - " Output Encrypted: False\n", - "Tensors:\n", - " tensor([[-2.6351, 2.5862],\n", - " [ 2.1366, -1.3115],\n", - " [ 4.3660, -3.4084]])\n" - ] - } - ], + "outputs": [], "source": [ "@mpc.run_multiprocess(world_size=2)\n", "def step_through_two_layers(): \n", @@ -604,40 +431,9 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Name: 24 \tModule: \n", - "Name: 25 \tModule: \n", - "Name: 26 \tModule: \n", - "Name: 27 \tModule: \n", - "Name: 28 \tModule: \n", - "Name: 29 \tModule: \n", - "Name: 30 \tModule: \n", - "Name: 31 \tModule: \n", - "Name: 32 \tModule: \n", - "Name: 33 \tModule: \n", - "Name: 34 \tModule: \n", - "Name: 35 \tModule: \n", - "Name: 36 \tModule: \n", - "Name: 37 \tModule: \n", - "Name: 38 \tModule: \n", - "Name: 39 \tModule: \n", - "Name: 40 \tModule: \n", - "Name: 41 \tModule: \n", - "Name: 42 \tModule: \n", - "Name: 43 \tModule: \n", - "Name: 44 \tModule: \n", - "Name: 45 \tModule: \n", - "Name: 46 \tModule: \n", - "Name: output \tModule: \n" - ] - } - ], + "outputs": [], "source": [ "# Define Alice's network\n", "class AliceNet2(nn.Module):\n", @@ -693,7 +489,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -719,7 +515,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.4" + "version": "3.7.6" } }, "nbformat": 4, From b112ab1e254be0bc5b15fc25463264e1e785db13 Mon Sep 17 00:00:00 2001 From: George Muraru Date: Wed, 13 May 2020 09:01:58 +0300 Subject: [PATCH 2/3] Change load/save to save/load from party --- .../Introduction-checkpoint.ipynb | 85 +++ ...Basics_of_CrypTen_Tensors-checkpoint.ipynb | 347 ++++++++++++ ...rial_2_Inside_CrypTensors-checkpoint.ipynb | 239 ++++++++ ...duction_to_Access_Control-checkpoint.ipynb | 365 ++++++++++++ ...Encrypted_Neural_Networks-checkpoint.ipynb | 337 +++++++++++ ...ood_of_Encrypted_Networks-checkpoint.ipynb | 523 ++++++++++++++++++ ..._CrypTen_on_AWS_instances-checkpoint.ipynb | 76 +++ ..._Encrypted_Neural_Network-checkpoint.ipynb | 320 +++++++++++ tutorials/Introduction.ipynb | 2 +- ...Tutorial_1_Basics_of_CrypTen_Tensors.ipynb | 137 +---- tutorials/Tutorial_2_Inside_CrypTensors.ipynb | 99 +--- ...ial_3_Introduction_to_Access_Control.ipynb | 266 ++------- ...ation_with_Encrypted_Neural_Networks.ipynb | 12 +- ...Under_the_hood_of_Encrypted_Networks.ipynb | 14 +- .../Tutorial_6_CrypTen_on_AWS_instances.ipynb | 220 +------- ...Training_an_Encrypted_Neural_Network.ipynb | 91 +-- 16 files changed, 2395 insertions(+), 738 deletions(-) create mode 100644 tutorials/.ipynb_checkpoints/Introduction-checkpoint.ipynb create mode 100644 tutorials/.ipynb_checkpoints/Tutorial_1_Basics_of_CrypTen_Tensors-checkpoint.ipynb create mode 100644 tutorials/.ipynb_checkpoints/Tutorial_2_Inside_CrypTensors-checkpoint.ipynb create mode 100644 tutorials/.ipynb_checkpoints/Tutorial_3_Introduction_to_Access_Control-checkpoint.ipynb create mode 100644 tutorials/.ipynb_checkpoints/Tutorial_4_Classification_with_Encrypted_Neural_Networks-checkpoint.ipynb create mode 100644 tutorials/.ipynb_checkpoints/Tutorial_5_Under_the_hood_of_Encrypted_Networks-checkpoint.ipynb create mode 100644 tutorials/.ipynb_checkpoints/Tutorial_6_CrypTen_on_AWS_instances-checkpoint.ipynb create mode 100644 tutorials/.ipynb_checkpoints/Tutorial_7_Training_an_Encrypted_Neural_Network-checkpoint.ipynb diff --git a/tutorials/.ipynb_checkpoints/Introduction-checkpoint.ipynb b/tutorials/.ipynb_checkpoints/Introduction-checkpoint.ipynb new file mode 100644 index 00000000..80fb30a7 --- /dev/null +++ b/tutorials/.ipynb_checkpoints/Introduction-checkpoint.ipynb @@ -0,0 +1,85 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Introduction to CrypTen\n", + "\n", + "CrypTen is a machine learning framework built on PyTorch that enables you to easily study and develop machine learning models using secure computing techniques. CrypTen allows you to develop models with the PyTorch API while performing computations on encrypted data -- without revealing the protected information. CrypTen ensures that sensitive or otherwise private data remains private, while still allowing model inference and training on encrypted data that may be aggregated across various organizations or users. \n", + "\n", + "CrypTen currently uses secure multiparty computation (MPC) (see (1)) as its cryptographic paradigm. To use CrypTen effectively, it is helpful to understand the secure MPC abstraction as well as the semi-honest adversarial model in which CrypTen operates. This introduction explains the basic concepts of secure MPC and the threat model. It then explores a few different use cases in which CrypTen may be applied. \n", + "\n", + "\n", + "## Secure Multi-party Computation\n", + "\n", + "Secure multi-party computation is an abstraction that allows multiple parties to compute a function on encrypted data, with protocols designed such that every party is able to access only their own data and data that all parties agree to reveal.\n", + "\n", + "Let's look at a concrete example to better understand this abstraction. Suppose we have three parties, A, B and C that each have a private number, and together want to compute the sum of all their private numbers. A secure MPC protocol for this computation will allow each party to learn the final sum; however, they will not learn any information about the other 2 parties' individual private numbers other than the aggregate sum.\n", + "\n", + "In secure MPC, the data owner encrypts its data by splitting it using random masks into n random shares that can be combined to reconstruct the original data. These n shares are then distributed between n parties. This process is called secret sharing. The parties can compute functions on the data by operating on the secret shares and can decrypt the final result by communicating the resulting shares amongst each other.\n", + "\n", + "\n", + "## Use Cases\n", + "In the tutorials, we show how to use CrypTen to four main scenarios:\n", + "
    \n", + "
  • Feature Aggregation: In the first scenario, multiple parties hold distinct sets of features, and want to perform computations over the joint feature set without sharing data. For example, different health providers may each have part of a patient's medical history, but may wish to use the patient's entire medical history to make better predictions while still protecting patient privacy.
  • \n", + " \n", + "
  • Data Labeling: Here, one party holds feature data while the another party holds corresponding labels, and the parties would like to learn a relationship without sharing data. This is similar to the feature aggregation with the exception that one party has labels rather than other features. For example, suppose that in previous healthcare scenario, one healthcare provider had access to health outcomes data, while other parties had access to health features. The parties may want to train a model that predicts health outcomes as a function of features, without exposing any health infomration between parties.
  • \n", + " \n", + "
  • Dataset Augmentation: In this scenario, several parties each hold a small number of samples, but would like to use all the examples in order to improve the statistical power of a measurement or model. \n", + " For example, when studying wage statistics across companies in a particular region, individual companies may not have enough data to make statistically significant hypotheses about the population. Wage data may be too sensitive to share openly, but privacy-preserving methods can be used to aggregate data across companies to make statistically significant measurements / models without exposing any individual company's data.
  • \n", + " \n", + "
  • Model Hiding: In the final scenario, one party has access to a trained model, while another party would like to apply that model to its own data. However, the data and model need to be kept private. \n", + " This can happen in cases where a model is proprietary, expensive to produce, and/or susceptible to white-box attacks, but has value to more than one party. Previously, this would have required the second party to send its data to the first to apply the model, but privacy-preserving techniques can be used when the data can't be exposed.
  • \n", + "
\n", + " \n", + "The tutorials illustrate how CrypTen can be used to model each of these scenarios.\n", + "\n", + "\n", + "## Threat Model\n", + "When determining whether MPC is appropriate for a certain computation, we must assess the assumptions we make about the actions that different parties can take. The threat model we use defines the assumptions we are allowed to make. CrypTen uses the \"honest-but-curious\" threat model (see (1, 2)). (This is sometimes also referred to as the \"semi-honest\" threat model.) It is specified by the following assumptions: \n", + "
    \n", + "
  • Every party faithfully follows the protocol: i.e., it performs all the computations specified in the program, and communicates the correct results to the appropriate parties specified in the program.
  • \n", + "
  • The communication channel is secure: no party can see any data that is not directly communicated to it.
  • \n", + "
  • Every party has access to a private source of randomness, e.g., a private coin to toss
  • \n", + "
  • Parties may use any data they have already seen and perform arbitrary processing to infer information.
  • \n", + "
\n", + "\n", + "\n", + "\n", + "## References\n", + "(1) Goldreich Oded. 2009. Foundations of Cryptography: Volume 2, Basic Applications (1st ed.). Cambridge University Press, New York, NY, USA.
\n", + "(2) Andrew C. Yao. 1982. Protocols for secure computations. In Proceedings of the 23rd Annual Symposium on Foundations of Computer Science (SFCS '82). IEEE Computer Society, Washington, DC, USA, 160-164." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorials/.ipynb_checkpoints/Tutorial_1_Basics_of_CrypTen_Tensors-checkpoint.ipynb b/tutorials/.ipynb_checkpoints/Tutorial_1_Basics_of_CrypTen_Tensors-checkpoint.ipynb new file mode 100644 index 00000000..80471c67 --- /dev/null +++ b/tutorials/.ipynb_checkpoints/Tutorial_1_Basics_of_CrypTen_Tensors-checkpoint.ipynb @@ -0,0 +1,347 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "# Tutorial 1: Basics of CrypTen Tensors\n", + "\n", + "We now have a high-level understanding of how secure MPC works. Through these tutorials, we will explain how to use CrypTen to carry out secure operations on encrypted tensors. In this tutorial, we will introduce a fundamental building block in CrypTen, called a ```CrypTensor```. ```CrypTensor```s are encrypted ```torch``` tensors that can be used for computing securely on data. \n", + "\n", + "CrypTen currently only supports secure MPC protocols (though we intend to add support for other advanced encryption protocols). Using the ```mpc``` backend, ```CrypTensor```s act as ```torch``` tensors whose values are encrypted using secure MPC protocols. Tensors created using the ```mpc``` backend are called ```MPCTensor```s. We will go into greater detail about ```MPCTensors``` in Tutorial 2. \n", + "\n", + "Let's begin by importing ```crypten``` and ```torch``` libraries. (If the imports fail, please see the installation instructions in the README.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import crypten\n", + "\n", + "crypten.init()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Creating Encrypted Tensors\n", + "CrypTen provides a ```crypten.cryptensor``` factory function, similar to ```torch.tensor```, to make creating ```CrypTensors``` easy. \n", + "\n", + "Let's begin by creating a ```torch``` tensor and encrypting it using ```crypten.cryptensor```. To decrypt a ```CrypTensor```, use ```get_plain_text()``` to return the original tensor. (```CrypTensor```s can also be created directly from a list or an array.)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Create torch tensor\n", + "x = torch.tensor([1.0, 2.0, 3.0])\n", + "\n", + "# Encrypt x\n", + "x_enc = crypten.cryptensor(x)\n", + "\n", + "# Decrypt x\n", + "x_dec = x_enc.get_plain_text() \n", + "print(x_dec)\n", + "\n", + "\n", + "# Create python list\n", + "y = [4.0, 5.0, 6.0]\n", + "\n", + "# Encrypt x\n", + "y_enc = crypten.cryptensor(y)\n", + "\n", + "# Decrypt x\n", + "y_dec = y_enc.get_plain_text()\n", + "print(y_dec)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Operations on Encrypted Tensors\n", + "Now let's look at what we can do with our ```CrypTensors```.\n", + "\n", + "#### Arithmetic Operations\n", + "We can carry out regular arithmetic operations between ```CrypTensors```, as well as between ```CrypTensors``` and plaintext tensors. Note that these operations never reveal any information about encrypted tensors (internally or externally) and return an encrypted tensor output." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#Arithmetic operations between CrypTensors and plaintext tensors\n", + "x_enc = crypten.cryptensor([1.0, 2.0, 3.0])\n", + "\n", + "y = 2.0\n", + "y_enc = crypten.cryptensor(2.0)\n", + "\n", + "\n", + "# Addition\n", + "z_enc1 = x_enc + y # Public\n", + "z_enc2 = x_enc + y_enc # Private\n", + "print(\"\\nPublic addition:\", z_enc1.get_plain_text())\n", + "print(\"Private addition:\", z_enc2.get_plain_text())\n", + "\n", + "\n", + "# Subtraction\n", + "z_enc1 = x_enc - y # Public\n", + "z_enc2 = x_enc - y_enc # Private\n", + "print(\"\\nPublic subtraction:\", z_enc1.get_plain_text())\n", + "print(\"Private subtraction:\", z_enc2.get_plain_text())\n", + "\n", + "# Multiplication\n", + "z_enc1 = x_enc * y # Public\n", + "z_enc2 = x_enc * y_enc # Private\n", + "print(\"\\nPublic multiplication:\", z_enc1.get_plain_text())\n", + "print(\"Private multiplication:\", z_enc2.get_plain_text())\n", + "\n", + "# Division\n", + "z_enc1 = x_enc / y # Public\n", + "z_enc2 = x_enc / y_enc # Private\n", + "print(\"\\nPublic division:\", z_enc1.get_plain_text())\n", + "print(\"Private division:\", z_enc2.get_plain_text())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Comparisons\n", + "Similarly, we can compute element-wise comparisons on ```CrypTensors```. Like arithmetic operations, comparisons performed on ```CrypTensor```s will return a ```CrypTensor``` result. Decrypting these result ```CrypTensor```s will evaluate to 0's and 1's corresponding to ```False``` and ```True``` values respectively." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#Construct two example CrypTensors\n", + "x_enc = crypten.cryptensor([1.0, 2.0, 3.0, 4.0, 5.0])\n", + "\n", + "y = torch.tensor([5.0, 4.0, 3.0, 2.0, 1.0])\n", + "y_enc = crypten.cryptensor(y)\n", + "\n", + "# Print values:\n", + "print(\"x: \", x_enc.get_plain_text())\n", + "print(\"y: \", y_enc.get_plain_text())\n", + "\n", + "# Less than\n", + "z_enc1 = x_enc < y # Public\n", + "z_enc2 = x_enc < y_enc # Private\n", + "print(\"\\nPublic (x < y) :\", z_enc1.get_plain_text())\n", + "print(\"Private (x < y) :\", z_enc2.get_plain_text())\n", + "\n", + "# Less than or equal\n", + "z_enc1 = x_enc <= y # Public\n", + "z_enc2 = x_enc <= y_enc # Private\n", + "print(\"\\nPublic (x <= y):\", z_enc1.get_plain_text())\n", + "print(\"Private (x <= y):\", z_enc2.get_plain_text())\n", + "\n", + "# Greater than\n", + "z_enc1 = x_enc > y # Public\n", + "z_enc2 = x_enc > y_enc # Private\n", + "print(\"\\nPublic (x > y) :\", z_enc1.get_plain_text())\n", + "print(\"Private (x > y) :\", z_enc2.get_plain_text())\n", + "\n", + "# Greater than or equal\n", + "z_enc1 = x_enc >= y # Public\n", + "z_enc2 = x_enc >= y_enc # Private\n", + "print(\"\\nPublic (x >= y):\", z_enc1.get_plain_text())\n", + "print(\"Private (x >= y):\", z_enc2.get_plain_text())\n", + "\n", + "# Equal\n", + "z_enc1 = x_enc == y # Public\n", + "z_enc2 = x_enc == y_enc # Private\n", + "print(\"\\nPublic (x == y):\", z_enc1.get_plain_text())\n", + "print(\"Private (x == y):\", z_enc2.get_plain_text())\n", + "\n", + "# Not Equal\n", + "z_enc1 = x_enc != y # Public\n", + "z_enc2 = x_enc != y_enc # Private\n", + "print(\"\\nPublic (x != y):\", z_enc1.get_plain_text())\n", + "print(\"Private (x != y):\", z_enc2.get_plain_text())\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Advanced mathematics\n", + "We are also able to compute more advanced mathematical functions on ```CrypTensors``` using iterative approximations. CrypTen provides MPC support for functions like reciprocal, exponential, logarithm, square root, tanh, etc. Notice that these are subject to numerical error due to the approximations used. \n", + "\n", + "Additionally, note that some of these functions will fail silently when input values are outside of the range of convergence for the approximations used. These do not produce errors because value are encrypted and cannot be checked without decryption. Exercise caution when using these functions. (It is good practice here to normalize input values for certain models.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "torch.set_printoptions(sci_mode=False)\n", + "\n", + "#Construct example input CrypTensor\n", + "x = torch.tensor([0.1, 0.3, 0.5, 1.0, 1.5, 2.0, 2.5])\n", + "x_enc = crypten.cryptensor(x)\n", + "\n", + "# Reciprocal\n", + "z = x.reciprocal() # Public\n", + "z_enc = x_enc.reciprocal() # Private\n", + "print(\"\\nPublic reciprocal:\", z)\n", + "print(\"Private reciprocal:\", z_enc.get_plain_text())\n", + "\n", + "# Logarithm\n", + "z = x.log() # Public\n", + "z_enc = x_enc.log() # Private\n", + "print(\"\\nPublic logarithm:\", z)\n", + "print(\"Private logarithm:\", z_enc.get_plain_text())\n", + "\n", + "# Exp\n", + "z = x.exp() # Public\n", + "z_enc = x_enc.exp() # Private\n", + "print(\"\\nPublic exponential:\", z)\n", + "print(\"Private exponential:\", z_enc.get_plain_text())\n", + "\n", + "# Sqrt\n", + "z = x.sqrt() # Public\n", + "z_enc = x_enc.sqrt() # Private\n", + "print(\"\\nPublic square root:\", z)\n", + "print(\"Private square root:\", z_enc.get_plain_text())\n", + "\n", + "# Tanh\n", + "z = x.tanh() # Public\n", + "z_enc = x_enc.tanh() # Private\n", + "print(\"\\nPublic tanh:\", z)\n", + "print(\"Private tanh:\", z_enc.get_plain_text())\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Control Flow using Encrypted Tensors\n", + "\n", + "Note that ```CrypTensors``` cannot be used directly in conditional expressions. Because the tensor is encrypted, the boolean expression cannot be evaluated unless the tensor is decrypted first. Attempting to execute control flow using an encrypted condition will result in an error.\n", + "\n", + "Some control flow can still be executed without decrypting, but must be executed using mathematical expressions. We have provided the function ```crypten.where(condition, x, y)``` to abstract this kind of conditional value setting.\n", + "\n", + "The following example illustrates how to write this kind conditional logic for ```CrypTensors```." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x_enc = crypten.cryptensor(2.0)\n", + "y_enc = crypten.cryptensor(4.0)\n", + "\n", + "a, b = 2, 3\n", + "\n", + "# Normal Control-flow code will raise an error\n", + "try:\n", + " if x_enc < y_enc:\n", + " z = a\n", + " else:\n", + " z = b\n", + "except RuntimeError as error:\n", + " print(f\"RuntimeError caught: \\\"{error}\\\"\\n\")\n", + "\n", + " \n", + "# Instead use a mathematical expression\n", + "use_a = (x_enc < y_enc)\n", + "z_enc = use_a * a + (1 - use_a) * b\n", + "print(\"z:\", z_enc.get_plain_text())\n", + " \n", + " \n", + "# Or use the `where` function\n", + "z_enc = crypten.where(x_enc < y_enc, a, b)\n", + "print(\"z:\", z_enc.get_plain_text())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Advanced Indexing\n", + "CrypTen supports many of the operations that work on ```torch``` tensors. Encrypted tensors can be indexed, concatenated, stacked, reshaped, etc. For a full list of operations, see the CrypTen documentation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x_enc = crypten.cryptensor([1.0, 2.0, 3.0])\n", + "y_enc = crypten.cryptensor([4.0, 5.0, 6.0])\n", + "\n", + "# Indexing\n", + "z_enc = x_enc[:-1]\n", + "print(\"Indexing:\\n\", z_enc.get_plain_text())\n", + "\n", + "# Concatenation\n", + "z_enc = crypten.cat([x_enc, y_enc])\n", + "print(\"\\nConcatenation:\\n\", z_enc.get_plain_text())\n", + "\n", + "# Stacking\n", + "z_enc = crypten.stack([x_enc, y_enc])\n", + "print('\\nStacking:\\n', z_enc.get_plain_text())\n", + "\n", + "# Reshaping\n", + "w_enc = z_enc.reshape((-1, 6))\n", + "print('\\nReshaping:\\n', w_enc.get_plain_text())\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### Implementation Note\n", + "\n", + "Due to internal implementation details, ```CrypTensors``` must be the first operand of operations that combine ```CrypTensor```s and ```torch``` tensors. That is, for a ```CrypTensor``` ```x_enc``` and a plaintext tensor ```y```:\n", + "- The expression ```x_enc < y``` is valid, but the equivalent expression ```y > x_enc``` will result in an error.\n", + "- The expression ```x_enc + y``` is valid, but the equivalent expression ```y + x_enc``` will result in an error.\n", + "\n", + "We intend to add support for both expressions in the future." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorials/.ipynb_checkpoints/Tutorial_2_Inside_CrypTensors-checkpoint.ipynb b/tutorials/.ipynb_checkpoints/Tutorial_2_Inside_CrypTensors-checkpoint.ipynb new file mode 100644 index 00000000..17cc3660 --- /dev/null +++ b/tutorials/.ipynb_checkpoints/Tutorial_2_Inside_CrypTensors-checkpoint.ipynb @@ -0,0 +1,239 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Tutorial 2: Inside CrypTensors\n", + "\n", + "Note: This tutorial is optional, and can be skipped without any loss of continuity to the following tutorials.\n", + "\n", + "\n", + "In this tutorial, we will take a brief look at the internals of ```CrypTensors```. \n", + "\n", + "Using the `mpc` backend, a `CrypTensor` is a tensor encrypted using secure MPC protocols, called an `MPCTensor`. In order to support the mathematical operations required by the `MPCTensor`, CrypTen implements two kinds of secret-sharing protocols: arithmetic secret-sharing and binary secret-sharing. Arithmetic secret sharing forms the basis for most of the mathematical operations implemented by `MPCTensor`. Similarly, binary secret-sharing allows for the evaluation of logical expressions.\n", + "\n", + "In this tutorial, we'll first introduce the concept of a `CrypTensor` ptype (i.e. private-type), and show how to use it to obtain `MPCTensors` that use arithmetic and binary secret shares. We will also describe how each of these ptypes is used, and how they can be combined to implement desired functionality." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#import the libraries\n", + "import crypten\n", + "import torch\n", + "\n", + "#initialize crypten\n", + "crypten.init()\n", + "#Disables OpenMP threads -- needed by @mpc.run_multiprocess which uses fork\n", + "torch.set_num_threads(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "## ptype in CrypTen\n", + "CrypTen defines the `ptype` (for private-type) attribute of an `MPCTensor` to denote the kind of secret-sharing protocol used in the `CrypTensor`. The `ptype` is, in many ways, analogous to the `dtype` of PyTorch. The `ptype` may have two values: \n", + "\n", + "- `crypten.arithmetic` for `ArithmeticSharedTensors`\n", + "- `crypten.binary` for `BinarySharedTensors`\n", + "\n", + "We can use the `ptype` attribute to create a `CrypTensor` with the appropriate secret-sharing protocol. For example: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#Constructing CrypTensors with ptype attribute\n", + "\n", + "#arithmetic secret-shared tensors\n", + "x_enc = crypten.cryptensor([1.0, 2.0, 3.0], ptype=crypten.arithmetic)\n", + "print(\"x_enc internal type:\", x_enc.ptype)\n", + "\n", + "#binary secret-shared tensors\n", + "y = torch.tensor([1, 2, 1], dtype=torch.int32)\n", + "y_enc = crypten.cryptensor(y, ptype=crypten.binary)\n", + "print(\"y_enc internal type:\", y_enc.ptype)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Arithmetic secret-sharing\n", + "Let's look more closely at the `crypten.arithmetic` ptype. Most of the mathematical operations implemented by `CrypTensors` are implemented using arithmetic secret sharing. As such, `crypten.arithmetic` is the default ptype for newly generated `CrypTensor`s. \n", + "\n", + "Let's begin by creating a new `CrypTensor` using `ptype=crypten.arithmetic` to enforce that the encryption is done via arithmetic secret sharing. We can print values of each share to confirm that values are being encrypted properly. \n", + "\n", + "To do so, we will need to create multiple parties to hold each share. We do this here using the `@mpc.run_multiprocess` function decorator, which we developed to execute crypten code from a single script (as we have in a Jupyter notebook). CrypTen follows the standard MPI programming model: it runs a separate process for each party, but each process runs an identical (complete) program. Each process has a `rank` variable to identify itself.\n", + "\n", + "Note that the sum of the two `_tensor` attributes below is equal to a scaled representation of the input. (Because MPC requires values to be integers, we scale input floats to a fixed-point encoding before encryption.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import crypten.mpc as mpc\n", + "import crypten.communicator as comm \n", + "\n", + "@mpc.run_multiprocess(world_size=2)\n", + "def examine_arithmetic_shares():\n", + " x_enc = crypten.cryptensor([1, 2, 3], ptype=crypten.arithmetic)\n", + " \n", + " rank = comm.get().get_rank()\n", + " print(f\"Rank {rank}:\\n {x_enc}\")\n", + " \n", + "x = examine_arithmetic_shares()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Binary secret-sharing\n", + "The second type of secret-sharing implemented in CrypTen is binary or XOR secret-sharing. This type of secret-sharing allows greater efficiency in evaluating logical expressions. \n", + "\n", + "Let's look more closely at the `crypten.binary` ptype. Most of the logical operations implemented by `CrypTensors` are implemented using arithmetic secret sharing. We typically use this type of secret-sharing when we want to evaluate binary operators (i.e. `^ & | >> <<`, etc.) or logical operations (like comparitors).\n", + "\n", + "Let's begin by creating a new `CrypTensor` using `ptype=crypten.binary` to enforce that the encryption is done via binary secret sharing. We can print values of each share to confirm that values are being encrypted properly, as we did for arithmetic secret-shares.\n", + "\n", + "(Note that an xor of the two `_tensor` attributes below is equal to an unscaled version of input.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "@mpc.run_multiprocess(world_size=2)\n", + "def examine_binary_shares():\n", + " x_enc = crypten.cryptensor([2, 3], ptype=crypten.binary)\n", + " \n", + " rank = comm.get().get_rank()\n", + " print(f\"Rank {rank}:\\n {x_enc}\")\n", + " \n", + "x = examine_binary_shares()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Using Both Secret-sharing Protocols\n", + "Quite often a mathematical function may need to use both additive and XOR secret sharing for efficient evaluation. Functions that require conversions between sharing types include comparators (`>, >=, <, <=, ==, !=`) as well as functions derived from them (`abs, sign, relu`, etc.). For a full list of supported functions, please see the CrypTen documentation.\n", + "\n", + "CrypTen provides functionality that allows for the conversion of between ptypes. Conversion between ptypes can be done using the `.to()` function with a `crypten.ptype` input, or by calling the `.arithmetic()` and `.binary()` conversion functions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from crypten.mpc import MPCTensor\n", + "\n", + "@mpc.run_multiprocess(world_size=2)\n", + "def examine_conversion():\n", + " x = torch.tensor([1, 2, 3])\n", + " rank = comm.get().get_rank()\n", + "\n", + " # create an MPCTensor with arithmetic secret sharing\n", + " x_enc_arithmetic = MPCTensor(x, ptype=crypten.arithmetic)\n", + " \n", + " # To binary\n", + " x_enc_binary = x_enc_arithmetic.to(crypten.binary)\n", + " x_from_binary = x_enc_binary.get_plain_text()\n", + " \n", + " if rank == 0: # only print once\n", + " print(\"to(crypten.binary):\")\n", + " print(f\" ptype: {x_enc_binary.ptype}\\n plaintext: {x_from_binary}\\n\")\n", + "\n", + " \n", + " # To arithmetic\n", + " x_enc_arithmetic = x_enc_arithmetic.to(crypten.arithmetic)\n", + " x_from_arithmetic = x_enc_arithmetic.get_plain_text()\n", + " \n", + " if rank == 0: # only print once\n", + " print(\"to(crypten.arithmetic):\")\n", + " print(f\" ptype: {x_enc_arithmetic.ptype}\\n plaintext: {x_from_arithmetic}\\n\")\n", + "\n", + " \n", + "z = examine_conversion()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Data Sources\n", + "CrypTen follows the standard MPI programming model: it runs a separate process for each party, but each process runs an identical (complete) program. Each process has a `rank` variable to identify itself.\n", + "\n", + "If the process with rank `i` is the source of data `x`, then `x` gets encrypted with `i` as its source value (denoted as `src`). However, MPI protocols require that both processes to provide a tensor with the same size as their input. CrypTen ignores all data provided from non-source processes when encrypting.\n", + "\n", + "In the next example, we'll show how to use the `rank` and `src` values to encrypt tensors. Here, we will have each of 3 parties generate a value `x` which is equal to its own `rank` value. Within the loop, 3 encrypted tensors are created, each with a different source. When these tensors are decrypted, we can verify that the tensors are generated using the tensor provided by the source process.\n", + "\n", + "(Note that `crypten.cryptensor` uses rank 0 as the default source if none is provided.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "@mpc.run_multiprocess(world_size=3)\n", + "def examine_sources():\n", + " # Create a different tensor on each rank\n", + " rank = comm.get().get_rank()\n", + " x = torch.tensor(rank)\n", + " print(f\"Rank {rank}: {x}\")\n", + " \n", + " # \n", + " world_size = comm.get().get_world_size()\n", + " for i in range(world_size):\n", + " x_enc = crypten.cryptensor(x, src=i)\n", + " z = x_enc.get_plain_text()\n", + " \n", + " # Only print from one process to avoid duplicates\n", + " if rank == 0: print(f\"Source {i}: {z}\")\n", + " \n", + "x = examine_sources()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorials/.ipynb_checkpoints/Tutorial_3_Introduction_to_Access_Control-checkpoint.ipynb b/tutorials/.ipynb_checkpoints/Tutorial_3_Introduction_to_Access_Control-checkpoint.ipynb new file mode 100644 index 00000000..da7aa1e2 --- /dev/null +++ b/tutorials/.ipynb_checkpoints/Tutorial_3_Introduction_to_Access_Control-checkpoint.ipynb @@ -0,0 +1,365 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "# Introduction to Access Control\n", + "\n", + "We can now start using CrypTen to carry out private computations in some common use cases. In this tutorial, we will demonstrate how CrypTen would apply for the scenarios described in the Introduction. In all scenarios, we'll use a simple two-party setting and demonstrate how we can learn a linear SVM. In the process, we will see how access control works in CrypTen.\n", + "\n", + "As usual, we'll begin by importing the `crypten` and `torch` libraries, and initialize `crypten` with `crypten.init()`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import crypten\n", + "import torch\n", + "\n", + "crypten.init()\n", + "torch.set_num_threads(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Setup\n", + "In this tutorial, we will train a Linear SVM to perform binary classification. We will first generate 1000 ground truth samples using 100 features and a randomly generated hyperplane to separate positive and negative examples. \n", + "\n", + "(Note: this will cause our classes to be linearly separable, so a linear SVM will be able to classify with perfect accuracy given the right parameters.)\n", + "\n", + "We will also include a test set of examples (that are also linearly separable by the same hyperplane) to show that the model learns a general hyperplane rather than memorizing the training data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "num_features = 100\n", + "num_train_examples = 1000\n", + "num_test_examples = 100\n", + "epochs = 40\n", + "lr = 3.0\n", + "\n", + "# Set random seed for reproducibility\n", + "torch.manual_seed(1)\n", + "\n", + "features = torch.randn(num_features, num_train_examples)\n", + "w_true = torch.randn(1, num_features)\n", + "b_true = torch.randn(1)\n", + "\n", + "labels = w_true.matmul(features).add(b_true).sign()\n", + "\n", + "test_features = torch.randn(num_features, num_test_examples)\n", + "test_labels = w_true.matmul(test_features).add(b_true).sign()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now that we have generated our dataset, we will train our SVM in four different access control scenarios across two parties, Alice and Bob:\n", + "\n", + "- Data Labeling: Alice has access to features, while Bob has access to labels\n", + "- Feature Aggregation: Alice has access to the first 50 features, while Bob has access to the last 50 features\n", + "- Data Augmentation: Alice has access to the first 500 examples, while Bob has access to the last 500 examples\n", + "- Model Hiding: Alice has access to `w_true` and `b_true`, while Bob has access to data samples to be classified\n", + "\n", + "Throughout this tutorial, we will assume Alice is using the rank 0 process, while Bob is using the rank 1 process. Additionally we will initialize our weights using random values." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ALICE = 0\n", + "BOB = 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In each example, we will use the same code to train our linear SVM once the features and labels are properly encrypted. This code is contained in `examples/mpc_linear_svm`, but it is unnecessary to understand the training code to properly use access control. The training process itself is discussed in depth in later tutorials.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from examples.mpc_linear_svm.mpc_linear_svm import train_linear_svm, evaluate_linear_svm" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Saving / Loading Data\n", + "\n", + "We have now generated features and labels for our model to learn. In the scenarios we explore in this tutorial, we would like to ensure that each party only has access to some subset of the data we have generated. To do so, we will use special save / load methods that CrypTen provides to handle loading only to a specified party and synchronizing across processes. \n", + "\n", + "We will use `crypten.save_from_party()` here to save data from a particular source, then we will load using `crypten.load_from_party()` in each example to load on a particular source. The following code will save all data we will use to files, then each example will load its data as necessary.\n", + "\n", + "(Note that because we are operating on a single machine, all processes will have access to all of the files we are using. However, this still will work as expected when operating across machines.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from crypten import mpc\n", + "\n", + "# Specify file locations to save each piece of data\n", + "filenames = {\n", + " \"features\": \"/tmp/features.pth\",\n", + " \"labels\": \"/tmp/labels.pth\",\n", + " \"features_alice\": \"/tmp/features_alice.pth\",\n", + " \"features_bob\": \"/tmp/features_bob.pth\",\n", + " \"samples_alice\": \"/tmp/samples_alice.pth\",\n", + " \"samples_bob\": \"/tmp/samples_bob.pth\",\n", + " \"w_true\": \"/tmp/w_true.pth\",\n", + " \"b_true\": \"/tmp/b_true.pth\",\n", + " \"test_features\": \"/tmp/test_features.pth\",\n", + " \"test_labels\": \"/tmp/test_labels.pth\",\n", + "}\n", + "\n", + "\n", + "@mpc.run_multiprocess(world_size=2)\n", + "def save_all_data():\n", + " # Save features, labels for Data Labeling example\n", + " crypten.save_from_party(features, filenames[\"features\"])\n", + " crypten.save_from_party(labels, filenames[\"labels\"])\n", + " \n", + " # Save split features for Feature Aggregation example\n", + " features_alice = features[:50]\n", + " features_bob = features[50:]\n", + " \n", + " crypten.save_from_party(features_alice, filenames[\"features_alice\"], src=ALICE)\n", + " crypten.save_from_party(features_bob, filenames[\"features_bob\"], src=BOB)\n", + " \n", + " # Save split dataset for Dataset Aggregation example\n", + " samples_alice = features[:, :500]\n", + " samples_bob = features[:, 500:]\n", + " crypten.save_from_party(samples_alice, filenames[\"samples_alice\"], src=ALICE)\n", + " crypten.save_from_party(samples_bob, filenames[\"samples_bob\"], src=BOB)\n", + " \n", + " # Save true model weights and biases for Model Hiding example\n", + " crypten.save_from_party(w_true, filenames[\"w_true\"], src=ALICE)\n", + " crypten.save_from_party(b_true, filenames[\"b_true\"], src=ALICE)\n", + " \n", + " crypten.save_from_party(test_features, filenames[\"test_features\"], src=BOB)\n", + " crypten.save_from_party(test_labels, filenames[\"test_labels\"], src=BOB)\n", + " \n", + "save_all_data()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Scenario 1: Data Labeling\n", + "\n", + "Our first example will focus on the Data Labeling scenario. In this example, Alice has access to features, while Bob has access to the labels. We will train our linear svm by encrypting the features from Alice and the labels from Bob, then training our SVM using an aggregation of the encrypted data.\n", + "\n", + "In order to indicate the source of a given encrypted tensor, we encrypt our tensor using `crypten.load()` (from a file) or `crypten.cryptensor()` (from a tensor) using a keyword argument `src`. This `src` argument takes the rank of the party we want to encrypt from (recall that ALICE is 0 and BOB is 1). \n", + "\n", + "(If the `src` is not specified, it will default to the rank 0 party. We will use the default when encrypting public values since the source is irrelevant in this case.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from crypten import mpc\n", + "\n", + "@mpc.run_multiprocess(world_size=2)\n", + "def data_labeling_example():\n", + " \"\"\"Apply data labeling access control model\"\"\"\n", + " # Alice loads features, Bob loads labels\n", + " features_enc = crypten.load_from_party(filenames[\"features\"], src=ALICE)\n", + " labels_enc = crypten.load_from_party(filenames[\"labels\"], src=BOB)\n", + " \n", + " # Execute training\n", + " w, b = train_linear_svm(features_enc, labels_enc, epochs=epochs, lr=lr)\n", + " \n", + " # Evaluate model\n", + " evaluate_linear_svm(test_features, test_labels, w, b)\n", + " \n", + "data_labeling_example()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Scenario 2: Feature Aggregation\n", + "\n", + "Next, we'll show how we can use CrypTen in the Feature Aggregation scenario. Here Alice and Bob each have 50 features for each sample, and would like to use their combined features to train a model. As before, Alice and Bob wish to keep their respective data private. This scenario can occur when multiple parties measure different features of a similar system, and their measurements may be proprietary or otherwise sensitive.\n", + "\n", + "Unlike the last scenario, one of our variables is split among two parties. This means we will have to concatenate the tensors encrypted from each party before passing them to the training code." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "@mpc.run_multiprocess(world_size=2)\n", + "def feature_aggregation_example():\n", + " \"\"\"Apply feature aggregation access control model\"\"\"\n", + " # Alice loads some features, Bob loads other features\n", + " features_alice_enc = crypten.load_from_party(filenames[\"features_alice\"], src=ALICE)\n", + " features_bob_enc = crypten.load_from_party(filenames[\"features_bob\"], src=BOB)\n", + " \n", + " # Concatenate features\n", + " features_enc = crypten.cat([features_alice_enc, features_bob_enc], dim=0)\n", + " \n", + " # Encrypt labels\n", + " labels_enc = crypten.cryptensor(labels)\n", + " \n", + " # Execute training\n", + " w, b = train_linear_svm(features_enc, labels_enc, epochs=epochs, lr=lr)\n", + " \n", + " # Evaluate model\n", + " evaluate_linear_svm(test_features, test_labels, w, b)\n", + " \n", + "feature_aggregation_example()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Scenario 3: Dataset Augmentation\n", + "\n", + "The next example shows how we can use CrypTen in a Data Augmentation scenario. Here Alice and Bob each have 500 samples, and would like to learn a classifier over their combined sample data. This scenario can occur in applications where several parties may each have access to a small amount of sensitive data, where no individual party has enough data to train an accurate model.\n", + "\n", + "Like the last scenario, one of our variables is split amongst parties, so we will have to concatenate tensors from encrypted from different parties. The main difference from the last scenario is that we are concatenating over the other dimension (the sample dimension rather than the feature dimension)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "@mpc.run_multiprocess(world_size=2)\n", + "def dataset_augmentation_example():\n", + " \"\"\"Apply dataset augmentation access control model\"\"\" \n", + " # Alice loads some samples, Bob loads other samples\n", + " samples_alice_enc = crypten.load_from_party(filenames[\"samples_alice\"], src=ALICE)\n", + " samples_bob_enc = crypten.load_from_party(filenames[\"samples_bob\"], src=BOB)\n", + " \n", + " # Concatenate features\n", + " samples_enc = crypten.cat([samples_alice_enc, samples_bob_enc], dim=1)\n", + " \n", + " labels_enc = crypten.cryptensor(labels)\n", + " \n", + " # Execute training\n", + " w, b = train_linear_svm(samples_enc, labels_enc, epochs=epochs, lr=lr)\n", + " \n", + " # Evaluate model\n", + " evaluate_linear_svm(test_features, test_labels, w, b)\n", + " \n", + "dataset_augmentation_example()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Scenario 4: Model Hiding\n", + "\n", + "The last scenario we will explore involves model hiding. Here, Alice has a pre-trained model that cannot be revealed, while Bob would like to use this model to evaluate on private data sample(s). This scenario can occur when a pre-trained model is proprietary or contains sensitive information, but can provide value to other parties with sensitive data.\n", + "\n", + "This scenario is somewhat different from the previous examples because we are not interested in training the model. Therefore, we do not need labels. Instead, we will demonstrate this example by encrypting the true model parameters (`w_true` and `b_true`) from Alice and encrypting the test set from Bob for evaluation.\n", + "\n", + "(Note: Because we are using the true weights and biases used to generate the test labels, we will get 100% accuracy.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "@mpc.run_multiprocess(world_size=2)\n", + "def model_hiding_example():\n", + " \"\"\"Apply model hiding access control model\"\"\"\n", + " # Alice loads the model\n", + " w_true_enc = crypten.load_from_party(filenames[\"w_true\"], src=ALICE)\n", + " b_true_enc = crypten.load_from_party(filenames[\"b_true\"], src=ALICE)\n", + " \n", + " # Bob loads the features to be evaluated\n", + " test_features_enc = crypten.load_from_party(filenames[\"test_features\"], src=BOB)\n", + " \n", + " # Evaluate model\n", + " evaluate_linear_svm(test_features_enc, test_labels, w_true_enc, b_true_enc)\n", + " \n", + "model_hiding_example()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this tutorial we have reviewed four techniques where CrypTen can be used to perform encrypted training / inference. Each of these techniques can be used to facilitate computations in different privacy-preserving scenarios. However, these techniques can also be combined to increase the amount of scenarios where CrypTen can maintain privacy.\n", + "\n", + "For example, we can combine feature aggregation and data labeling to train a model on data split between three parties, where two parties each have access to a subset of features, and the third party has access to labels.\n", + "\n", + "Before exiting this tutorial, please clean up the files generated using the following code." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "for fn in filenames.values():\n", + " if os.path.exists(fn): os.remove(fn)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorials/.ipynb_checkpoints/Tutorial_4_Classification_with_Encrypted_Neural_Networks-checkpoint.ipynb b/tutorials/.ipynb_checkpoints/Tutorial_4_Classification_with_Encrypted_Neural_Networks-checkpoint.ipynb new file mode 100644 index 00000000..2cc63a77 --- /dev/null +++ b/tutorials/.ipynb_checkpoints/Tutorial_4_Classification_with_Encrypted_Neural_Networks-checkpoint.ipynb @@ -0,0 +1,337 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "# Classification with Encrypted Neural Networks\n", + "\n", + "In this tutorial, we'll look at how we can achieve the Model Hiding application we discussed in the Introduction. That is, suppose say Alice has a trained model she wishes to keep private, and Bob has some data he wishes to classify while keeping it private. We will see how CrypTen allows Alice and Bob to coordinate and classify the data, while achieving their privacy requirements.\n", + "\n", + "To simulate this scenario, we will begin with Alice training a simple neural network on MNIST data. Then we'll see how Alice and Bob encrypt their network and data respectively, classify the encrypted data and finally decrypt the labels.\n", + "\n", + "## Setup\n", + "\n", + "We first import the `torch` and `crypten` libraries, and initialize `crypten`. We will use a helper script `mnist_utils.py` to split the public MNIST data into Alice's portion and Bob's portion. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import crypten\n", + "import torch\n", + "\n", + "crypten.init()\n", + "torch.set_num_threads(1)\n", + "\n", + "#ignore warnings\n", + "import warnings; \n", + "warnings.filterwarnings(\"ignore\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run script that downloads the publicly available MNIST data, and splits the data as required.\n", + "%run ./mnist_utils.py --option train_v_test" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we will define the structure of Alice's network as a class. Even though Alice has a pre-trained model, the CrypTen will require this structure as input." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define Alice's network\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "\n", + "class AliceNet(nn.Module):\n", + " def __init__(self):\n", + " super(AliceNet, self).__init__()\n", + " self.fc1 = nn.Linear(784, 128)\n", + " self.fc2 = nn.Linear(128, 128)\n", + " self.fc3 = nn.Linear(128, 10)\n", + " \n", + " def forward(self, x):\n", + " out = self.fc1(x)\n", + " out = F.relu(out)\n", + " out = self.fc2(out)\n", + " out = F.relu(out)\n", + " out = self.fc3(out)\n", + " return out\n", + " \n", + " def set_all_parameters(self, value):\n", + " for p in self.parameters():\n", + " nn.init.constant_(p, value)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will also define a helper routine `compute_accuracy` to make it easy to compute the accuracy of the output we get." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def compute_accuracy(output, labels):\n", + " pred = output.argmax(1)\n", + " correct = pred.eq(labels)\n", + " correct_count = correct.sum(0, keepdim=True).float()\n", + " accuracy = correct_count.mul_(100.0 / output.size(0))\n", + " return accuracy" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Encrypting a Pre-trained Model\n", + "\n", + "Assume that Alice has a pre-trained network ready to classify data. Let's see how we can use CrypTen to encrypt this network, so it can be used to classify data without revealing its parameters. We'll use the pre-trained model in `models/tutorial4_alice_model.pth` in this tutorial. As in Tutorial 3, we will assume Alice is using the rank 0 process, while Bob is using the rank 1 process. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ALICE = 0\n", + "BOB = 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In CrypTen, encrypting PyTorch network is straightforward: we load a PyTorch model from file to the appropriate source, convert it to a CrypTen model and then encrypt it. Let us understand each of these steps.\n", + "\n", + "As we did with CrypTensors in Tutorial 3, we will use CrypTen's load functionality (i.e., `crypten.load_from_party`) to read a model from file to a particular source. The source is indicated by the keyword argument `src`. As in Tutorial 3, this src argument tells us the rank of the party we want to load the model to (and later, encrypt the model from). In addition, here we also need to provide a dummy model to tell CrypTen the model's structure. The dummy model is indicated by the keyword argument `dummy_model`. Note that unlike loading a tensor, the result from `crypten.load_from_party` is not encrypted. Instead, only the `src` party's model is populated from the file.\n", + "\n", + "Once the model is loaded, we call the function `from_pytorch`: this function sets up a CrypTen network from the PyTorch network. It takes the plaintext network as input as well as dummy input. The dummy input must be a `torch` tensor of the same shape as a potential input to the network, however the values inside the tensor do not matter. \n", + "\n", + "Finally, we call `encrypt` on the CrypTen network to encrypt its parameters. Once we call the `encrypt` function, the models `encrypted` property will verify that the model parameters have been encrypted. (Encrypted CrypTen networks can also be decrypted using the `decrypt` function)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Load pre-trained model to Alice\n", + "dummy_model = AliceNet()\n", + "plaintext_model = crypten.load_from_party('models/tutorial4_alice_model.pth', dummy_model=dummy_model, src=ALICE)\n", + "\n", + "# Encrypt the model from Alice: \n", + "\n", + "# 1. Create a dummy input with the same shape as the model input\n", + "dummy_input = torch.empty((1, 784))\n", + "\n", + "# 2. Construct a CrypTen network with the trained model and dummy_input\n", + "private_model = crypten.nn.from_pytorch(plaintext_model, dummy_input)\n", + "\n", + "# 3. Encrypt the CrypTen network with src=ALICE\n", + "private_model.encrypt(src=ALICE)\n", + "\n", + "#Check that model is encrypted:\n", + "print(\"Model successfully encrypted:\", private_model.encrypted)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Classifying Encrypted Data with Encrypted Model\n", + "\n", + "We can now use Alice's encrypted network to classify Bob's data. For this, we need to encrypt Bob's data as well, as we did in Tutorial 3 (recall that Bob has the rank 1 process). Once Alice's network and Bob's data are both encrypted, CrypTen inference is performed with essentially identical steps as in PyTorch. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import crypten.mpc as mpc\n", + "import crypten.communicator as comm\n", + "\n", + "labels = torch.load('/tmp/bob_test_labels.pth').long()\n", + "count = 100 # For illustration purposes, we'll use only 100 samples for classification\n", + "\n", + "@mpc.run_multiprocess(world_size=2)\n", + "def encrypt_model_and_data():\n", + " # Load pre-trained model to Alice\n", + " model = crypten.load_from_party('models/tutorial4_alice_model.pth', dummy_model=dummy_model, src=ALICE)\n", + " \n", + " # Encrypt model from Alice \n", + " dummy_input = torch.empty((1, 784))\n", + " private_model = crypten.nn.from_pytorch(model, dummy_input)\n", + " private_model.encrypt(src=ALICE)\n", + " \n", + " # Load data to Bob\n", + " data_enc = crypten.load_from_party('/tmp/bob_test.pth', src=BOB)\n", + " data_enc2 = data_enc[:count]\n", + " data_flatten = data_enc2.flatten(start_dim=1)\n", + "\n", + " # Classify the encrypted data\n", + " private_model.eval()\n", + " output_enc = private_model(data_flatten)\n", + " \n", + " # Compute the accuracy\n", + " output = output_enc.get_plain_text()\n", + " accuracy = compute_accuracy(output, labels[:count])\n", + " print(\"\\tAccuracy: {0:.4f}\".format(accuracy.item()))\n", + " \n", + "encrypt_model_and_data()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Validating Encrypted Classification\n", + "\n", + "Finally, we will verify that CrypTen classification results in encrypted output, and that this output can be decrypted into meaningful labels. \n", + "\n", + "To see this, in this tutorial, we will just check whether the result is an encrypted tensor; in the next tutorial, we will look into the values of tensor and confirm the encryption. We will also decrypt the result. As we discussed before, Alice and Bob both have access to the decrypted output of the model, and can both use this to obtain the labels. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "@mpc.run_multiprocess(world_size=2)\n", + "def encrypt_model_and_data():\n", + " # Load pre-trained model to Alice\n", + " plaintext_model = crypten.load_from_party('models/tutorial4_alice_model.pth', dummy_model=dummy_model, src=ALICE)\n", + " \n", + " # Encrypt model from Alice \n", + " dummy_input = torch.empty((1, 784))\n", + " private_model = crypten.nn.from_pytorch(plaintext_model, dummy_input)\n", + " private_model.encrypt(src=ALICE)\n", + " \n", + " # Load data to Bob\n", + " data_enc = crypten.load_from_party('/tmp/bob_test.pth', src=BOB)\n", + " data_enc2 = data_enc[:count]\n", + " data_flatten = data_enc2.flatten(start_dim=1)\n", + "\n", + " # Classify the encrypted data\n", + " private_model.eval()\n", + " output_enc = private_model(data_flatten)\n", + " \n", + " # Verify the results are encrypted: \n", + " print(\"Output tensor encrypted:\", crypten.is_encrypted_tensor(output_enc)) \n", + "\n", + " # Decrypting the result\n", + " output = output_enc.get_plain_text()\n", + "\n", + " # Obtaining the labels\n", + " pred = output.argmax(dim=1)\n", + " print(\"Decrypted labels:\\n\", pred)\n", + " \n", + "encrypt_model_and_data()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "This completes our tutorial. While we have used a simple network here to illustrate the concepts, CrypTen provides primitives to allow for encryption of substantially more complex networks. In our examples section, we demonstrate how CrypTen can be used to encrypt LeNet and ResNet, among others. \n", + "\n", + "Before exiting this tutorial, please clean up the files generated using the following code." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "filenames = ['/tmp/alice_train.pth', \n", + " '/tmp/alice_train_labels.pth', \n", + " '/tmp/bob_test.pth', \n", + " '/tmp/bob_test_labels.pth']\n", + "\n", + "for fn in filenames:\n", + " if os.path.exists(fn): os.remove(fn)" + ] + } + ], + "metadata": { + "bento_stylesheets": { + "bento/extensions/flow/main.css": true, + "bento/extensions/kernel_selector/main.css": true, + "bento/extensions/kernel_ui/main.css": true, + "bento/extensions/new_kernel/main.css": true, + "bento/extensions/system_usage/main.css": true, + "bento/extensions/theme/main.css": true + }, + "disseminate_notebook_id": { + "notebook_id": "390894444956881" + }, + "disseminate_notebook_info": { + "bento_version": "20190826-030256", + "description": "", + "hide_code": false, + "hipster_group": "", + "kernel_build_info": { + "error": "The file located at '/data/users/shobha/fbsource/fbcode/bento/kernels/local/cryptenk/TARGETS' could not be found." + }, + "no_uii": true, + "notebook_number": "139932", + "others_can_edit": true, + "reviewers": "", + "revision_id": "375902760006757", + "tags": "", + "tasks": "", + "title": "Tutorial 4 -- Classification with Encrypted Neural Networks" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorials/.ipynb_checkpoints/Tutorial_5_Under_the_hood_of_Encrypted_Networks-checkpoint.ipynb b/tutorials/.ipynb_checkpoints/Tutorial_5_Under_the_hood_of_Encrypted_Networks-checkpoint.ipynb new file mode 100644 index 00000000..f79e0c30 --- /dev/null +++ b/tutorials/.ipynb_checkpoints/Tutorial_5_Under_the_hood_of_Encrypted_Networks-checkpoint.ipynb @@ -0,0 +1,523 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Under the Hood of Encrypted Neural Networks\n", + "\n", + "This tutorial is optional, and can be skipped without loss of continuity.\n", + "\n", + "In this tutorial, we'll take a look at how CrypTen performs inference with an encrypted neural network on encrypted data. We'll see how the data remains encrypted through all the operations, and yet is able to obtain accurate results after the computation. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import crypten\n", + "import torch\n", + "\n", + "crypten.init() \n", + "torch.set_num_threads(1)\n", + "\n", + "# Ignore warnings\n", + "import warnings; \n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "# Keep track of all created temporary files so that we can clean up at the end\n", + "temp_files = []" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## A Simple Linear Layer\n", + "We'll start by examining how a single Linear layer works in CrypTen. We'll instantiate a torch Linear layer, convert to CrypTen layer, encrypt it, and step through some toy data with it. As in earlier tutorials, we'll assume Alice has the rank 0 process and Bob has the rank 1 process. We'll also assume Alice has the layer and Bob has the data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define ALICE and BOB src values\n", + "ALICE = 0\n", + "BOB = 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import torch.nn as nn\n", + "import types\n", + "\n", + "# Instantiate single Linear layer\n", + "layer_linear = nn.Linear(4, 2)\n", + "\n", + "def set_all_parameters(module, value):\n", + " for p in module.parameters():\n", + " nn.init.constant_(p, value)\n", + "\n", + "# Make sure we have the set_all_parameters method added to the\n", + "# Linear Module since this is not native to PyTorch\n", + "nn.Linear.set_all_parameters = set_all_parameters\n", + "\n", + "# The weights and the bias are initialized to small random values\n", + "print(\"Plaintext Weights:\", layer_linear._parameters['weight'])\n", + "print(\"Plaintext Bias:\", layer_linear._parameters['bias'])\n", + "\n", + "# Save the plaintext layer\n", + "layer_linear_file = \"/tmp/tutorial5_layer_alice1.pth\"\n", + "crypten.save(layer_linear, layer_linear_file)\n", + "temp_files.append(layer_linear_file) \n", + "\n", + "# Generate some toy data\n", + "features = 4\n", + "examples = 3\n", + "toy_data = torch.rand(examples, features)\n", + "\n", + "# Save the plaintext toy data\n", + "toy_data_file = \"/tmp/tutorial5_data_bob1.pth\"\n", + "crypten.save(toy_data, toy_data_file)\n", + "temp_files.append(toy_data_file)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import crypten.mpc as mpc\n", + "import crypten.communicator as comm\n", + "\n", + "@mpc.run_multiprocess(world_size=2)\n", + "def forward_single_encrypted_layer():\n", + " rank = comm.get().get_rank()\n", + " \n", + " # Load and encrypt the layer\n", + " layer = crypten.load_from_party(layer_linear_file, dummy_model=nn.Linear(4, 2), src=ALICE)\n", + " layer_enc = crypten.nn.from_pytorch(layer, dummy_input=torch.empty((1,4)))\n", + " layer_enc.encrypt(src=ALICE)\n", + " \n", + " # Note that layer parameters are encrypted:\n", + " if rank == 0: # Print once for readability\n", + " print(\"Weights:\\n\", layer_enc.weight.share)\n", + " print(\"Bias:\\n\", layer_enc.bias.share)\n", + " print()\n", + " \n", + " # Load and encrypt data\n", + " data_enc = crypten.load_from_party(toy_data_file, src=BOB)\n", + " \n", + " # Apply the encrypted layer (linear transformation):\n", + " result_enc = layer_enc.forward(data_enc)\n", + " \n", + " # Decrypt the result:\n", + " result = result_enc.get_plain_text()\n", + " \n", + " # Examine the result\n", + " if rank == 0: # Print once for readability\n", + " print(\"Decrypted result:\\n\", result)\n", + " \n", + "forward_single_encrypted_layer()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that the application of the encrypted linear layer on the encrypted data produces an encrypted result, which we can then decrypt to get the values in plaintext.\n", + "\n", + "Let's look at a second linear transformation, to give a flavor of how accuracy is preserved even when the data and the layer are encrypted. We'll look at a uniform scaling transformation, in which all tensor elements are multiplied by the same scalar factor. Again, we'll assume Alice has the layer and the rank 0 process, and Bob has the data and the rank 1 process." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Initialize a linear layer with random weights\n", + "layer_scale = nn.Linear(3, 3)\n", + "\n", + "# Construct a uniform scaling matrix: we'll scale by factor 5\n", + "factor = 5\n", + "layer_scale._parameters['weight'] = torch.eye(3)*factor\n", + "layer_scale._parameters['bias'] = torch.zeros_like(layer_scale._parameters['bias'])\n", + "\n", + "# Save the plaintext layer\n", + "layer_scale_file = \"/tmp/tutorial5_layer_alice2.pth\"\n", + "crypten.save(layer_scale, layer_scale_file)\n", + "temp_files.append(layer_scale_file)\n", + "\n", + "# Construct some toy data\n", + "features = 3\n", + "examples = 2\n", + "toy_data = torch.ones(examples, features)\n", + "\n", + "# Save the plaintext toy data\n", + "toy_data_file = \"/tmp/tutorial5_data_bob2.pth\"\n", + "crypten.save(toy_data, toy_data_file)\n", + "temp_files.append(toy_data_file)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "@mpc.run_multiprocess(world_size=2)\n", + "def forward_scaling_layer():\n", + " rank = comm.get().get_rank()\n", + " \n", + " # Load and encrypt the layer\n", + " layer = crypten.load_from_party(layer_scale_file, dummy_model=nn.Linear(3, 3), src=ALICE)\n", + " layer_enc = crypten.nn.from_pytorch(layer, dummy_input=torch.empty((1,3)))\n", + " layer_enc.encrypt(src=ALICE)\n", + " \n", + " # Load and encrypt data\n", + " data_enc = crypten.load_from_party(toy_data_file, src=BOB) \n", + " \n", + " # Note that layer parameters are (still) encrypted:\n", + " if rank == 0: # Print once for readability\n", + " print(\"Weights:\\n\", layer_enc.weight.share)\n", + " print(\"Bias:\\n\", layer_enc.bias.share)\n", + " print()\n", + "\n", + " # Apply the encrypted scaling transformation\n", + " result_enc = layer_enc.forward(data_enc)\n", + "\n", + " # Decrypt the result:\n", + " result = result_enc.get_plain_text()\n", + " \n", + " # Since both parties have the same decrypted values, print only rank 0 for readability\n", + " if rank == 0:\n", + " print(\"Plaintext result:\\n\", (result))\n", + " \n", + "z = forward_scaling_layer()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The resulting plaintext tensor is correctly scaled, even though we applied the encrypted transformation on the encrypted input! " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Multi-layer Neural Networks\n", + "Let's now look at how the encrypted input moves through an encrypted multi-layer neural network. \n", + "\n", + "For ease of explanation, we'll first step through a network with only two linear layers and ReLU activations. Again, we'll assume Alice has a network and Bob has some data, and they wish to run encrypted inference. \n", + "\n", + "To simulate this, we'll once again generate some toy data and train Alice's network on it. Then we'll encrypt Alice's network, Bob's data, and step through every layer in the network with the encrypted data. Through this, we'll see how the computations get applied although the network and the data are encrypted.\n", + "\n", + "### Setup\n", + "As in Tutorial 3, we will first generate 1000 ground truth samples using 50 features and a randomly generated hyperplane to separate positive and negative examples. We will then modify the labels so that they are all non-negative. Finally, we will split the data so that the first 900 samples belong to Alice and the last 100 samples belong to Bob." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Setup\n", + "features = 50\n", + "examples = 1000\n", + "\n", + "# Set random seed for reproducibility\n", + "torch.manual_seed(1)\n", + "\n", + "# Generate toy data and separating hyperplane\n", + "data = torch.randn(examples, features)\n", + "w_true = torch.randn(1, features)\n", + "b_true = torch.randn(1)\n", + "labels = w_true.matmul(data.t()).add(b_true).sign()\n", + "\n", + "# Change labels to non-negative values\n", + "labels_nn = torch.where(labels==-1, torch.zeros(labels.size()), labels)\n", + "labels_nn = labels_nn.squeeze().long()\n", + "\n", + "# Split data into Alice's and Bob's portions:\n", + "data_alice, labels_alice = data[:900], labels_nn[:900]\n", + "data_bob, labels_bob = data[900:], labels_nn[900:]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define Alice's network\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "\n", + "class AliceNet(nn.Module):\n", + " def __init__(self):\n", + " super(AliceNet, self).__init__()\n", + " self.fc1 = nn.Linear(50, 20)\n", + " self.fc2 = nn.Linear(20, 2)\n", + " \n", + " def forward(self, x):\n", + " out = self.fc1(x)\n", + " out = F.relu(out)\n", + " out = self.fc2(out)\n", + " return out\n", + " \n", + " def set_all_parameters(self, value):\n", + " for p in self.parameters():\n", + " nn.init.constant_(p, value)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Train and save Alice's network\n", + "model = AliceNet()\n", + "criterion = nn.CrossEntropyLoss()\n", + "optimizer = torch.optim.SGD(model.parameters(), lr=0.1)\n", + "\n", + "for i in range(500): \n", + " #forward pass: compute prediction\n", + " output = model(data_alice)\n", + " \n", + " #compute and print loss\n", + " loss = criterion(output, labels_alice)\n", + " if i % 100 == 99:\n", + " print(\"Epoch\", i, \"Loss:\", loss.item())\n", + " \n", + " #zero gradients for learnable parameters\n", + " optimizer.zero_grad()\n", + " \n", + " #backward pass: compute gradient with respect to model parameters\n", + " loss.backward()\n", + " \n", + " #update model parameters\n", + " optimizer.step()\n", + "\n", + "sample_trained_model_file = '/tmp/tutorial5_alice_model.pth'\n", + "torch.save(model, sample_trained_model_file)\n", + "temp_files.append(sample_trained_model_file)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Stepping through a Multi-layer Network\n", + "\n", + "Let's now look at what happens when we load the network Alice's has trained and encrypt it. First, we'll look at how the network structure changes when we convert it from a PyTorch network to CrypTen network." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Load the trained network to Alice\n", + "model_plaintext = crypten.load_from_party(sample_trained_model_file, dummy_model=AliceNet(), src=ALICE)\n", + "\n", + "# Convert the trained network to CrypTen network \n", + "private_model = crypten.nn.from_pytorch(model_plaintext, dummy_input=torch.empty((1, 50)))\n", + "# Encrypt the network\n", + "private_model.encrypt(src=ALICE)\n", + "\n", + "# Examine the structure of the encrypted CrypTen network\n", + "for name, curr_module in private_model._modules.items():\n", + " print(\"Name:\", name, \"\\tModule:\", curr_module)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We see that the encrypted network has 3 modules, named '5', '6' and 'output', denoting the first Linear layer, the ReLU activation, and the second Linear layer respectively. These modules are encrypted just as the layers in the previous section were. \n", + "\n", + "Now let's encrypt Bob's data, and step it through each encrypted module. For readability, we will use only 3 examples from Bob's data to illustrate the inference. Note how Bob's data remains encrypted after each individual layer's computation!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Pre-processing: Select only the first three examples in Bob's data for readability\n", + "data = data_bob[:3]\n", + "sample_data_bob_file = '/tmp/tutorial5_data_bob3.pth'\n", + "torch.save(data, sample_data_bob_file)\n", + "temp_files.append(sample_data_bob_file)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "@mpc.run_multiprocess(world_size=2)\n", + "def step_through_two_layers(): \n", + " rank = comm.get().get_rank()\n", + "\n", + " # Load and encrypt the network\n", + " model = crypten.load_from_party(sample_trained_model_file, dummy_model=AliceNet(), src=ALICE)\n", + " private_model = crypten.nn.from_pytorch(model, dummy_input=torch.empty((1, 50)))\n", + " private_model.encrypt(src=ALICE)\n", + "\n", + " # Load and encrypt the data\n", + " data_enc = crypten.load_from_party(sample_data_bob_file, src=BOB)\n", + "\n", + " # Forward through the first layer\n", + " out_enc = private_model._modules['5'].forward(data_enc)\n", + " print(\"Rank: {} First Linear Layer: Output Encrypted: {}\\n\".format(rank, crypten.is_encrypted_tensor(out_enc)))\n", + " print(\"Rank: {} Shares after First Linear Layer:{}\\n\".format(rank, out_enc.share))\n", + "\n", + " # Apply ReLU activation\n", + " out_enc = private_model._modules['6'].forward(out_enc)\n", + " print(\"Rank: {} ReLU:\\n Output Encrypted: {}\\n\".format(rank, crypten.is_encrypted_tensor(out_enc)))\n", + " print(\"Rank: {} Shares after ReLU: {}\\n\".format(rank, out_enc.share))\n", + "\n", + " # Forward through the second Linear layer\n", + " out_enc = private_model._modules['output'].forward(out_enc)\n", + " print(\"Rank: {} Second Linear layer:\\n Output Encrypted: {}\\n\".format(rank, crypten.is_encrypted_tensor(out_enc))), \n", + " print(\"Rank: {} Shares after Second Linear layer:{}\\n\".format(rank, out_enc.share))\n", + "\n", + " # Decrypt the output\n", + " out_dec = out_enc.get_plain_text()\n", + " # Since both parties have same decrypted results, only print the rank 0 output\n", + " if rank == 0:\n", + " print(\"Decrypted output:\\n Output Encrypted:\", crypten.is_encrypted_tensor(out_dec))\n", + " print(\"Tensors:\\n\", out_dec)\n", + " \n", + "z = step_through_two_layers()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Again, we emphasize that the output of each layer is an encrypted tensor. Only after the final call to `get_plain_text` do we get the plaintext tensor.\n", + "\n", + "### From PyTorch to CrypTen: Structural Changes in Network Architecture \n", + "\n", + "We have used a simple two-layer network in the above example, but the same ideas apply to more complex networks and operations. However, in more complex networks, there may not always be a one-to-one mapping between the PyTorch layers and the CrypTen layers. This is because we use PyTorch's onnx implementation to convert PyTorch models to CrypTen models. \n", + "As an example, we'll take a typical network used to classify digits in MNIST data, and look at what happens to its structure we convert it to a CrypTen module. (As we only wish to illustrate the structural changes in layers, we will not train this network on data; we will just use it with its randomly initialized weights). " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define Alice's network\n", + "class AliceNet2(nn.Module):\n", + " def __init__(self):\n", + " super(AliceNet2, self).__init__()\n", + " self.conv1 = nn.Conv2d(1, 16, kernel_size=5, padding=0)\n", + " self.conv2 = nn.Conv2d(16, 16, kernel_size=5, padding=0)\n", + " self.fc1 = nn.Linear(16 * 4 * 4, 100)\n", + " self.fc2 = nn.Linear(100, 10)\n", + " self.batchnorm1 = nn.BatchNorm2d(16)\n", + " self.batchnorm2 = nn.BatchNorm2d(16)\n", + " self.batchnorm3 = nn.BatchNorm1d(100)\n", + " \n", + " def forward(self, x):\n", + " out = self.conv1(x)\n", + " out = self.batchnorm1(out)\n", + " out = F.relu(out)\n", + " out = F.avg_pool2d(out, 2)\n", + " out = self.conv2(out)\n", + " out = self.batchnorm2(out)\n", + " out = F.relu(out)\n", + " out = F.avg_pool2d(out, 2)\n", + " out = out.view(out.size(0), -1)\n", + " out = self.fc1(out)\n", + " out = self.batchnorm3(out)\n", + " out = F.relu(out)\n", + " out = self.fc2(out)\n", + " return out\n", + " \n", + "model = AliceNet2()\n", + "\n", + "# Let's encrypt the complex network. \n", + "# Create dummy input of the correct input shape for the model\n", + "dummy_input = torch.empty((1, 1, 28, 28))\n", + "\n", + "# Encrypt the network\n", + "private_model = crypten.nn.from_pytorch(model, dummy_input)\n", + "private_model.encrypt(src=ALICE)\n", + "\n", + "# Examine the structure of the encrypted network\n", + "for name, curr_module in private_model._modules.items():\n", + " print(\"Name:\", name, \"\\tModule:\", curr_module)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notice how the CrypTen network has split some the layers in the PyTorch module into several CrypTen modules. Each PyTorch operation may correspond to one or more operations in CrypTen. However, during the conversion, these are sometimes split due to limitations intorduced by onnx.\n", + "\n", + "Before exiting this tutorial, please clean up the files generated using the following code." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "for fn in temp_files:\n", + " if os.path.exists(fn): os.remove(fn)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorials/.ipynb_checkpoints/Tutorial_6_CrypTen_on_AWS_instances-checkpoint.ipynb b/tutorials/.ipynb_checkpoints/Tutorial_6_CrypTen_on_AWS_instances-checkpoint.ipynb new file mode 100644 index 00000000..4ff9f945 --- /dev/null +++ b/tutorials/.ipynb_checkpoints/Tutorial_6_CrypTen_on_AWS_instances-checkpoint.ipynb @@ -0,0 +1,76 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# CrypTen on AWS Instances\n", + "\n", + "Our previous tutorials have covered the essentials of using CrypTen on our local machines. We also provides a script `aws_launcher.py` in the `scripts` directory that will allow you to compute on encrypted data on multiple AWS instances. \n", + "\n", + "For example, if Alice has a classifier on one AWS instance and Bob has data on another AWS instance, `aws_launcher.py` will allow Alice and Bob to classify the data without revealing their respective private information (just as we did in Tutorial 4). \n", + "\n", + "## Using the Launcher Script\n", + "\n", + "The steps to follow are:\n", + "
    \n", + "
  1. First, create multiple AWS instances with public AMI \"Deep Learning AMI (Ubuntu) Version 24.0\", and record the instance IDs.
  2. \n", + "
  3. Install PyTorch, CrypTen and dependencies of the program to be run on all AWS instances.
  4. \n", + "
  5. Run `aws_launcher.py` on your local machine, as we explain below.
  6. \n", + "
\n", + "The results are left on the AWS instances. Log messages will be printed on your local machine by launcher script.\n", + "\n", + "## Sample Run\n", + "\n", + "The following cell shows a sample usage of the `aws_launcher.py` script. Note, however, that the command in the cell will not work as is: please replace the parameters used with appropriate ones with your own AWS instances, usernames, and `.ssh` keys (see documentation in the `aws_launcher.py` script). " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%script bash\n", + "python3 [PATH_TO_CRYPTEN]/CrypTen/scripts/aws_launcher.py \\\n", + "--ssh_key_file [SSH_KEY_FILE] --instances=[AWS_INSTANCE1, AWS_INSTANCE2...] \\\n", + "--region [AWS_REGION] \\\n", + "--ssh_user [AWS_USERNAME] \\\n", + "--aux_files=[PATH_TO_CRYPTEN]/CrypTen/examples/mpc_linear_svm/mpc_linear_svm.py [PATH_TO_CRYPTEN]/CrypTen/examples/mpc_linear_svm/launcher.py \\\n", + "--features 50 \\\n", + "--examples 100 \\\n", + "--epochs 50 \\\n", + "--lr 0.5 \\\n", + "--skip_plaintext" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorials/.ipynb_checkpoints/Tutorial_7_Training_an_Encrypted_Neural_Network-checkpoint.ipynb b/tutorials/.ipynb_checkpoints/Tutorial_7_Training_an_Encrypted_Neural_Network-checkpoint.ipynb new file mode 100644 index 00000000..89bec05a --- /dev/null +++ b/tutorials/.ipynb_checkpoints/Tutorial_7_Training_an_Encrypted_Neural_Network-checkpoint.ipynb @@ -0,0 +1,320 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Training an Encrypted Neural Network\n", + "\n", + "In this tutorial, we will walk through an example of how we can train a neural network with CrypTen. This is particularly relevant for the Feature Aggregation, Data Labeling and Data Augmentation use cases. We will focus on the usual two-party setting and show how we can train an accurate neural network for digit classification on the MNIST data.\n", + "\n", + "For concreteness, this tutorial will step through the Feature Aggregation use cases: Alice and Bob each have part of the features of the data set, and wish to train a neural network on their combined data, while keeping their data private. \n", + "\n", + "## Setup\n", + "As usual, we'll begin by importing and initializing the `crypten` and `torch` libraries. \n", + "\n", + "We will use the MNIST dataset to demonstrate how Alice and Bob can learn without revealing protected information. For reference, the feature size of each example in the MNIST data is `28 x 28`. Let's assume Alice has the first `28 x 20` features and Bob has last `28 x 8` features. One way to think of this split is that Alice has the (roughly) top 2/3rds of each image, while Bob has the bottom 1/3rd of each image. We'll again use our helper script `mnist_utils.py` that downloads the publicly available MNIST data, and splits the data as required.\n", + "\n", + "For simplicity, we will restrict our problem to binary classification: we'll simply learn how to distinguish between 0 and non-zero digits. For speed of execution in the notebook, we will only create a dataset of a 100 examples." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import crypten\n", + "import torch\n", + "\n", + "crypten.init()\n", + "torch.set_num_threads(1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%run ./mnist_utils.py --option features --reduced 100 --binary" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we'll define the network architecture below, and then describe how to train it on encrypted data in the next section. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "\n", + "#Define an example network\n", + "class ExampleNet(nn.Module):\n", + " def __init__(self):\n", + " super(ExampleNet, self).__init__()\n", + " self.conv1 = nn.Conv2d(1, 16, kernel_size=5, padding=0)\n", + " self.fc1 = nn.Linear(16 * 12 * 12, 100)\n", + " self.fc2 = nn.Linear(100, 2) # For binary classification, final layer needs only 2 outputs\n", + " \n", + " def forward(self, x):\n", + " out = self.conv1(x)\n", + " out = F.relu(out)\n", + " out = F.max_pool2d(out, 2)\n", + " out = out.view(out.size(0), -1)\n", + " out = self.fc1(out)\n", + " out = F.relu(out)\n", + " out = self.fc2(out)\n", + " return out" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Encrypted Training\n", + "\n", + "After all the material we've covered in earlier tutorials, we only need to know a few additional items for encrypted training. We'll first discuss how the training loop in CrypTen differs from PyTorch. Then, we'll go through a complete example to illustrate training on encrypted data from end-to-end.\n", + "\n", + "### How does CrypTen training differ from PyTorch training?\n", + "\n", + "There are two main ways implementing a CrypTen training loop differs from a PyTorch training loop. We'll describe these items first, and then illustrate them with small examples below.\n", + "\n", + "(1) Use one-hot encoding: CrypTen training requires all labels to use one-hot encoding. This means that when using standard datasets such as MNIST, we need to modify the labels to use one-hot encoding.\n", + "\n", + "(2) Directly update parameters: CrypTen does not use the PyTorch optimizers. Instead, CrypTen implements encrypted SGD by implementing its own `backward` function, followed by directly updating the parameters. As we will see below, using SGD in CrypTen is very similar to using the PyTorch optimizers.\n", + "\n", + "We now show some small examples to illustrate these differences. As before, we will assume Alice has the rank 0 process and Bob has the rank 1 process." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define source argument values for Alice and Bob\n", + "ALICE = 0\n", + "BOB = 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Load Alice's data \n", + "data_alice_enc = crypten.load_from_party('/tmp/alice_train.pth', src=ALICE)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# We'll now set up the data for our small example below\n", + "# For illustration purposes, we will create toy data\n", + "# and encrypt all of it from source ALICE\n", + "x_small = torch.rand(100, 1, 28, 28)\n", + "y_small = torch.randint(1, (100,))\n", + "\n", + "# Transform labels into one-hot encoding\n", + "label_eye = torch.eye(2)\n", + "y_one_hot = label_eye[y_small]\n", + "\n", + "# Transform all data to CrypTensors\n", + "x_train = crypten.cryptensor(x_small, src=ALICE)\n", + "y_train = crypten.cryptensor(y_one_hot)\n", + "\n", + "# Instantiate and encrypt a CrypTen model\n", + "model_plaintext = ExampleNet()\n", + "dummy_input = torch.empty(1, 1, 28, 28)\n", + "model = crypten.nn.from_pytorch(model_plaintext, dummy_input)\n", + "model.encrypt()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Example: Stochastic Gradient Descent in CrypTen\n", + "\n", + "model.train() # Change to training mode\n", + "loss = crypten.nn.MSELoss() # Choose loss functions\n", + "\n", + "# Set parameters: learning rate, num_epochs\n", + "learning_rate = 0.001\n", + "num_epochs = 2\n", + "\n", + "# Train the model: SGD on encrypted data\n", + "for i in range(num_epochs):\n", + "\n", + " # forward pass\n", + " output = model(x_train)\n", + " loss_value = loss(output, y_train)\n", + " \n", + " # set gradients to zero\n", + " model.zero_grad()\n", + "\n", + " # perform backward pass\n", + " loss_value.backward()\n", + "\n", + " # update parameters\n", + " model.update_parameters(learning_rate) \n", + " \n", + " # examine the loss after each epoch\n", + " print(\"Epoch: {0:d} Loss: {1:.4f}\".format(i, loss_value.get_plain_text()))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### A Complete Example\n", + "\n", + "We now put these pieces together for a complete example of training a network in a multi-party setting. \n", + "\n", + "As in Tutorial 3, we'll assume Alice has the rank 0 process, and Bob has the rank 1 process; so we'll load and encrypt Alice's data with `src=0`, and load and encrypt Bob's data with `src=1`. We'll then initialize a plaintext model and convert it to an encrypted model, just as we did in Tutorial 4. We'll finally define our loss function, training parameters, and run SGD on the encrypted data. For the purposes of this tutorial we train on 100 samples; training should complete in ~3 minutes per epoch." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import crypten.mpc as mpc\n", + "import crypten.communicator as comm\n", + "\n", + "# Convert labels to one-hot encoding\n", + "# Since labels are public in this use case, we will simply use them from loaded torch tensors\n", + "labels = torch.load('/tmp/train_labels.pth')\n", + "labels = labels.long()\n", + "labels_one_hot = label_eye[labels]\n", + "\n", + "@mpc.run_multiprocess(world_size=2)\n", + "def run_encrypted_training():\n", + " # Load data:\n", + " x_alice_enc = crypten.load_from_party('/tmp/alice_train.pth', src=ALICE)\n", + " x_bob_enc = crypten.load_from_party('/tmp/bob_train.pth', src=BOB)\n", + " \n", + " # Combine the feature sets: identical to Tutorial 3\n", + " x_combined_enc = crypten.cat([x_alice_enc, x_bob_enc], dim=2)\n", + " \n", + " # Reshape to match the network architecture\n", + " x_combined_enc = x_combined_enc.unsqueeze(1)\n", + " \n", + " # Initialize a plaintext model and convert to CrypTen model\n", + " model = crypten.nn.from_pytorch(ExampleNet(), dummy_input)\n", + " model.encrypt()\n", + " \n", + " # Set train mode\n", + " model.train()\n", + " \n", + " # Define a loss function\n", + " loss = crypten.nn.MSELoss()\n", + "\n", + " # Define training parameters\n", + " learning_rate = 0.001\n", + " num_epochs = 2\n", + " batch_size = 10\n", + " num_batches = x_combined_enc.size(0) // batch_size\n", + " \n", + " rank = comm.get().get_rank()\n", + " for i in range(num_epochs): \n", + " # Print once for readability\n", + " if rank == 0:\n", + " print(f\"Epoch {i} in progress:\") \n", + " \n", + " for batch in range(num_batches):\n", + " # define the start and end of the training mini-batch\n", + " start, end = batch * batch_size, (batch + 1) * batch_size\n", + " \n", + " # construct CrypTensors out of training examples / labels\n", + " x_train = x_combined_enc[start:end]\n", + " y_batch = labels_one_hot[start:end]\n", + " y_train = crypten.cryptensor(y_batch, requires_grad=True)\n", + " \n", + " # perform forward pass:\n", + " output = model(x_train)\n", + " loss_value = loss(output, y_train)\n", + " \n", + " # set gradients to \"zero\" \n", + " model.zero_grad()\n", + "\n", + " # perform backward pass: \n", + " loss_value.backward()\n", + "\n", + " # update parameters\n", + " model.update_parameters(learning_rate)\n", + " \n", + " # Print progress every batch:\n", + " batch_loss = loss_value.get_plain_text()\n", + " if rank == 0:\n", + " print(f\"\\tBatch {(batch + 1)} of {num_batches} Loss {batch_loss.item():.4f}\")\n", + "\n", + "run_encrypted_training()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We see that the average batch loss decreases across the epochs, as we expect during training.\n", + "\n", + "This completes our tutorial. Before exiting this tutorial, please clean up the files generated using the following code." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "filenames = ['/tmp/alice_train.pth', \n", + " '/tmp/bob_train.pth', \n", + " '/tmp/alice_test.pth',\n", + " '/tmp/bob_test.pth', \n", + " '/tmp/train_labels.pth',\n", + " '/tmp/test_labels.pth']\n", + "\n", + "for fn in filenames:\n", + " if os.path.exists(fn): os.remove(fn)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorials/Introduction.ipynb b/tutorials/Introduction.ipynb index 544cd859..80fb30a7 100644 --- a/tutorials/Introduction.ipynb +++ b/tutorials/Introduction.ipynb @@ -77,7 +77,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.4" + "version": "3.7.6" } }, "nbformat": 4, diff --git a/tutorials/Tutorial_1_Basics_of_CrypTen_Tensors.ipynb b/tutorials/Tutorial_1_Basics_of_CrypTen_Tensors.ipynb index d5bfeabe..80471c67 100644 --- a/tutorials/Tutorial_1_Basics_of_CrypTen_Tensors.ipynb +++ b/tutorials/Tutorial_1_Basics_of_CrypTen_Tensors.ipynb @@ -17,7 +17,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -39,18 +39,9 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "tensor([1., 2., 3.])\n", - "tensor([4., 5., 6.])\n" - ] - } - ], + "outputs": [], "source": [ "# Create torch tensor\n", "x = torch.tensor([1.0, 2.0, 3.0])\n", @@ -87,28 +78,9 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Public addition: tensor([3., 4., 5.])\n", - "Private addition: tensor([3., 4., 5.])\n", - "\n", - "Public subtraction: tensor([-1., 0., 1.])\n", - "Private subtraction: tensor([-1., 0., 1.])\n", - "\n", - "Public multiplication: tensor([2., 4., 6.])\n", - "Private multiplication: tensor([2., 4., 6.])\n", - "\n", - "Public division: tensor([0.5000, 1.0000, 1.5000])\n", - "Private division: tensor([0.5000, 1.0000, 1.5000])\n" - ] - } - ], + "outputs": [], "source": [ "#Arithmetic operations between CrypTensors and plaintext tensors\n", "x_enc = crypten.cryptensor([1.0, 2.0, 3.0])\n", @@ -153,36 +125,9 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "x: tensor([1., 2., 3., 4., 5.])\n", - "y: tensor([5., 4., 3., 2., 1.])\n", - "\n", - "Public (x < y) : tensor([1., 1., 0., 0., 0.])\n", - "Private (x < y) : tensor([1., 1., 0., 0., 0.])\n", - "\n", - "Public (x <= y): tensor([1., 1., 1., 0., 0.])\n", - "Private (x <= y): tensor([1., 1., 1., 0., 0.])\n", - "\n", - "Public (x > y) : tensor([0., 0., 0., 1., 1.])\n", - "Private (x > y) : tensor([0., 0., 0., 1., 1.])\n", - "\n", - "Public (x >= y): tensor([0., 0., 1., 1., 1.])\n", - "Private (x >= y): tensor([0., 0., 1., 1., 1.])\n", - "\n", - "Public (x == y): tensor([0., 0., 1., 0., 0.])\n", - "Private (x == y): tensor([0., 0., 1., 0., 0.])\n", - "\n", - "Public (x != y): tensor([1., 1., 0., 1., 1.])\n", - "Private (x != y): tensor([1., 1., 0., 1., 1.])\n" - ] - } - ], + "outputs": [], "source": [ "#Construct two example CrypTensors\n", "x_enc = crypten.cryptensor([1.0, 2.0, 3.0, 4.0, 5.0])\n", @@ -243,31 +188,9 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Public reciprocal: tensor([10.0000, 3.3333, 2.0000, 1.0000, 0.6667, 0.5000, 0.4000])\n", - "Private reciprocal: tensor([10.0009, 3.3335, 2.0000, 1.0000, 0.6667, 0.5000, 0.4000])\n", - "\n", - "Public logarithm: tensor([-2.3026, -1.2040, -0.6931, 0.0000, 0.4055, 0.6931, 0.9163])\n", - "Private logarithm: tensor([-2.3181, -1.2110, -0.6997, 0.0004, 0.4038, 0.6918, 0.9150])\n", - "\n", - "Public exponential: tensor([ 1.1052, 1.3499, 1.6487, 2.7183, 4.4817, 7.3891, 12.1825])\n", - "Private exponential: tensor([ 1.1021, 1.3440, 1.6468, 2.7121, 4.4574, 7.3280, 12.0188])\n", - "\n", - "Public square root: tensor([0.3162, 0.5477, 0.7071, 1.0000, 1.2247, 1.4142, 1.5811])\n", - "Private square root: tensor([0.3135, 0.5445, 0.7051, 1.0000, 1.2195, 1.4080, 1.5762])\n", - "\n", - "Public tanh: tensor([0.0997, 0.2913, 0.4621, 0.7616, 0.9051, 0.9640, 0.9866])\n", - "Private tanh: tensor([0.1019, 0.2892, 0.4613, 0.7642, 0.9081, 0.9689, 0.9922])\n" - ] - } - ], + "outputs": [], "source": [ "torch.set_printoptions(sci_mode=False)\n", "\n", @@ -321,20 +244,9 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "RuntimeError caught: \"Cannot evaluate MPCTensors to boolean values\"\n", - "\n", - "z: tensor(2.)\n", - "z: tensor(2.)\n" - ] - } - ], + "outputs": [], "source": [ "x_enc = crypten.cryptensor(2.0)\n", "y_enc = crypten.cryptensor(4.0)\n", @@ -372,28 +284,9 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Indexing:\n", - " tensor([1., 2.])\n", - "\n", - "Concatenation:\n", - " tensor([1., 2., 3., 4., 5., 6.])\n", - "\n", - "Stacking:\n", - " tensor([[1., 2., 3.],\n", - " [4., 5., 6.]])\n", - "\n", - "Reshaping:\n", - " tensor([[1., 2., 3., 4., 5., 6.]])\n" - ] - } - ], + "outputs": [], "source": [ "x_enc = crypten.cryptensor([1.0, 2.0, 3.0])\n", "y_enc = crypten.cryptensor([4.0, 5.0, 6.0])\n", @@ -411,7 +304,7 @@ "print('\\nStacking:\\n', z_enc.get_plain_text())\n", "\n", "# Reshaping\n", - "w_enc = z_enc.reshape(-1, 6)\n", + "w_enc = z_enc.reshape((-1, 6))\n", "print('\\nReshaping:\\n', w_enc.get_plain_text())\n" ] }, @@ -446,7 +339,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.4" + "version": "3.7.6" } }, "nbformat": 4, diff --git a/tutorials/Tutorial_2_Inside_CrypTensors.ipynb b/tutorials/Tutorial_2_Inside_CrypTensors.ipynb index 50821d5f..17cc3660 100644 --- a/tutorials/Tutorial_2_Inside_CrypTensors.ipynb +++ b/tutorials/Tutorial_2_Inside_CrypTensors.ipynb @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -49,18 +49,9 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "x_enc internal type: ptype.arithmetic\n", - "y_enc internal type: ptype.binary\n" - ] - } - ], + "outputs": [], "source": [ "#Constructing CrypTensors with ptype attribute\n", "\n", @@ -90,28 +81,9 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Rank 0:\n", - " MPCTensor(\n", - "\t_tensor=tensor([1559458991263271971, 464556655590487085, 3264140948701540070])\n", - "\tplain_text=HIDDEN\n", - "\tptype=ptype.arithmetic\n", - ")Rank 1:\n", - " MPCTensor(\n", - "\t_tensor=tensor([-1559458991263206435, -464556655590356013, -3264140948701343462])\n", - "\tplain_text=HIDDEN\n", - "\tptype=ptype.arithmetic\n", - ")\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "import crypten.mpc as mpc\n", "import crypten.communicator as comm \n", @@ -142,28 +114,9 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Rank 0:\n", - " MPCTensor(\n", - "\t_tensor=tensor([ 125494978816570963, 7944957613546045877])\n", - "\tplain_text=HIDDEN\n", - "\tptype=ptype.binary\n", - ")Rank 1:\n", - " MPCTensor(\n", - "\t_tensor=tensor([ 125494978816570961, 7944957613546045878])\n", - "\tplain_text=HIDDEN\n", - "\tptype=ptype.binary\n", - ")\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "@mpc.run_multiprocess(world_size=2)\n", "def examine_binary_shares():\n", @@ -187,24 +140,9 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "to(crypten.binary):\n", - " ptype: ptype.binary\n", - " plaintext: tensor([1., 2., 3.])\n", - "\n", - "to(crypten.arithmetic):\n", - " ptype: ptype.arithmetic\n", - " plaintext: tensor([1., 2., 3.])\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "from crypten.mpc import MPCTensor\n", "\n", @@ -253,22 +191,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Rank 0: 0\n", - "Rank 2: 2\n", - "Rank 1: 1\n", - "Source 0: 0.0\n", - "Source 1: 1.0\n", - "Source 2: 2.0\n" - ] - } - ], + "outputs": [], "source": [ "@mpc.run_multiprocess(world_size=3)\n", "def examine_sources():\n", @@ -306,7 +231,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.4" + "version": "3.7.6" } }, "nbformat": 4, diff --git a/tutorials/Tutorial_3_Introduction_to_Access_Control.ipynb b/tutorials/Tutorial_3_Introduction_to_Access_Control.ipynb index 6e594add..da7aa1e2 100644 --- a/tutorials/Tutorial_3_Introduction_to_Access_Control.ipynb +++ b/tutorials/Tutorial_3_Introduction_to_Access_Control.ipynb @@ -15,7 +15,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -40,7 +40,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -79,7 +79,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -96,7 +96,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -111,27 +111,16 @@ "\n", "We have now generated features and labels for our model to learn. In the scenarios we explore in this tutorial, we would like to ensure that each party only has access to some subset of the data we have generated. To do so, we will use special save / load methods that CrypTen provides to handle loading only to a specified party and synchronizing across processes. \n", "\n", - "We will use `crypten.save()` here to save data from a particular source, then we will load using `crypten.load()` in each example to load on a particular source. The following code will save all data we will use to files, then each example will load its data as necessary.\n", + "We will use `crypten.save_from_party()` here to save data from a particular source, then we will load using `crypten.load_from_party()` in each example to load on a particular source. The following code will save all data we will use to files, then each example will load its data as necessary.\n", "\n", "(Note that because we are operating on a single machine, all processes will have access to all of the files we are using. However, this still will work as expected when operating across machines.)" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[None, None]" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "from crypten import mpc\n", "\n", @@ -153,28 +142,28 @@ "@mpc.run_multiprocess(world_size=2)\n", "def save_all_data():\n", " # Save features, labels for Data Labeling example\n", - " crypten.save(features, filenames[\"features\"])\n", - " crypten.save(labels, filenames[\"labels\"])\n", + " crypten.save_from_party(features, filenames[\"features\"])\n", + " crypten.save_from_party(labels, filenames[\"labels\"])\n", " \n", " # Save split features for Feature Aggregation example\n", " features_alice = features[:50]\n", " features_bob = features[50:]\n", " \n", - " crypten.save(features_alice, filenames[\"features_alice\"], src=ALICE)\n", - " crypten.save(features_bob, filenames[\"features_bob\"], src=BOB)\n", + " crypten.save_from_party(features_alice, filenames[\"features_alice\"], src=ALICE)\n", + " crypten.save_from_party(features_bob, filenames[\"features_bob\"], src=BOB)\n", " \n", " # Save split dataset for Dataset Aggregation example\n", " samples_alice = features[:, :500]\n", " samples_bob = features[:, 500:]\n", - " crypten.save(samples_alice, filenames[\"samples_alice\"], src=ALICE)\n", - " crypten.save(samples_bob, filenames[\"samples_bob\"], src=BOB)\n", + " crypten.save_from_party(samples_alice, filenames[\"samples_alice\"], src=ALICE)\n", + " crypten.save_from_party(samples_bob, filenames[\"samples_bob\"], src=BOB)\n", " \n", " # Save true model weights and biases for Model Hiding example\n", - " crypten.save(w_true, filenames[\"w_true\"], src=ALICE)\n", - " crypten.save(b_true, filenames[\"b_true\"], src=ALICE)\n", + " crypten.save_from_party(w_true, filenames[\"w_true\"], src=ALICE)\n", + " crypten.save_from_party(b_true, filenames[\"b_true\"], src=ALICE)\n", " \n", - " crypten.save(test_features, filenames[\"test_features\"], src=BOB)\n", - " crypten.save(test_labels, filenames[\"test_labels\"], src=BOB)\n", + " crypten.save_from_party(test_features, filenames[\"test_features\"], src=BOB)\n", + " crypten.save_from_party(test_labels, filenames[\"test_labels\"], src=BOB)\n", " \n", "save_all_data()" ] @@ -194,64 +183,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch 0 --- Training Accuracy 53.40%\n", - "Epoch 1 --- Training Accuracy 58.70%\n", - "Epoch 2 --- Training Accuracy 63.80%\n", - "Epoch 3 --- Training Accuracy 68.30%\n", - "Epoch 4 --- Training Accuracy 73.60%\n", - "Epoch 6 --- Training Accuracy 81.00%\n", - "Epoch 7 --- Training Accuracy 84.60%\n", - "Epoch 8 --- Training Accuracy 87.00%\n", - "Epoch 10 --- Training Accuracy 91.50%\n", - "Epoch 11 --- Training Accuracy 92.90%\n", - "Epoch 12 --- Training Accuracy 93.80%\n", - "Epoch 13 --- Training Accuracy 94.30%\n", - "Epoch 14 --- Training Accuracy 95.50%\n", - "Epoch 16 --- Training Accuracy 96.30%\n", - "Epoch 17 --- Training Accuracy 96.60%\n", - "Epoch 18 --- Training Accuracy 96.80%\n", - "Epoch 19 --- Training Accuracy 97.60%\n", - "Epoch 20 --- Training Accuracy 97.70%\n", - "Epoch 21 --- Training Accuracy 97.90%\n", - "Epoch 22 --- Training Accuracy 98.20%\n", - "Epoch 23 --- Training Accuracy 98.10%\n", - "Epoch 24 --- Training Accuracy 98.90%\n", - "Epoch 25 --- Training Accuracy 99.20%\n", - "Epoch 26 --- Training Accuracy 99.20%\n", - "Epoch 27 --- Training Accuracy 99.50%\n", - "Epoch 28 --- Training Accuracy 99.80%\n", - "Epoch 29 --- Training Accuracy 99.60%\n", - "Epoch 30 --- Training Accuracy 99.60%\n", - "Epoch 31 --- Training Accuracy 99.50%\n", - "Epoch 32 --- Training Accuracy 99.90%\n", - "Epoch 33 --- Training Accuracy 99.90%\n", - "Epoch 34 --- Training Accuracy 100.00%\n", - "Epoch 35 --- Training Accuracy 100.00%\n", - "Epoch 36 --- Training Accuracy 100.00%\n", - "Epoch 37 --- Training Accuracy 100.00%\n", - "Epoch 38 --- Training Accuracy 100.00%\n", - "Epoch 39 --- Training Accuracy 100.00%\n", - "Test accuracy 92.00%\n" - ] - }, - { - "data": { - "text/plain": [ - "[None, None]" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "from crypten import mpc\n", "\n", @@ -259,8 +193,8 @@ "def data_labeling_example():\n", " \"\"\"Apply data labeling access control model\"\"\"\n", " # Alice loads features, Bob loads labels\n", - " features_enc = crypten.load(filenames[\"features\"], src=ALICE)\n", - " labels_enc = crypten.load(filenames[\"labels\"], src=BOB)\n", + " features_enc = crypten.load_from_party(filenames[\"features\"], src=ALICE)\n", + " labels_enc = crypten.load_from_party(filenames[\"labels\"], src=BOB)\n", " \n", " # Execute training\n", " w, b = train_linear_svm(features_enc, labels_enc, epochs=epochs, lr=lr)\n", @@ -284,71 +218,16 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch 0 --- Training Accuracy 53.40%\n", - "Epoch 1 --- Training Accuracy 58.70%\n", - "Epoch 2 --- Training Accuracy 63.80%\n", - "Epoch 3 --- Training Accuracy 68.30%\n", - "Epoch 4 --- Training Accuracy 73.60%\n", - "Epoch 5 --- Training Accuracy 78.00%\n", - "Epoch 6 --- Training Accuracy 81.00%\n", - "Epoch 7 --- Training Accuracy 84.60%\n", - "Epoch 8 --- Training Accuracy 87.00%\n", - "Epoch 10 --- Training Accuracy 91.50%\n", - "Epoch 11 --- Training Accuracy 92.90%\n", - "Epoch 12 --- Training Accuracy 93.80%\n", - "Epoch 13 --- Training Accuracy 94.30%\n", - "Epoch 14 --- Training Accuracy 95.50%\n", - "Epoch 16 --- Training Accuracy 96.30%\n", - "Epoch 17 --- Training Accuracy 96.60%\n", - "Epoch 18 --- Training Accuracy 96.70%\n", - "Epoch 19 --- Training Accuracy 97.40%\n", - "Epoch 20 --- Training Accuracy 98.30%\n", - "Epoch 21 --- Training Accuracy 98.00%\n", - "Epoch 22 --- Training Accuracy 98.10%\n", - "Epoch 23 --- Training Accuracy 98.00%\n", - "Epoch 24 --- Training Accuracy 98.70%\n", - "Epoch 25 --- Training Accuracy 98.70%\n", - "Epoch 26 --- Training Accuracy 99.30%\n", - "Epoch 27 --- Training Accuracy 99.70%\n", - "Epoch 28 --- Training Accuracy 99.60%\n", - "Epoch 29 --- Training Accuracy 99.50%\n", - "Epoch 30 --- Training Accuracy 99.70%\n", - "Epoch 31 --- Training Accuracy 99.50%\n", - "Epoch 32 --- Training Accuracy 99.60%\n", - "Epoch 33 --- Training Accuracy 99.90%\n", - "Epoch 34 --- Training Accuracy 100.00%\n", - "Epoch 35 --- Training Accuracy 100.00%\n", - "Epoch 36 --- Training Accuracy 100.00%\n", - "Epoch 37 --- Training Accuracy 100.00%\n", - "Epoch 38 --- Training Accuracy 100.00%\n", - "Epoch 39 --- Training Accuracy 100.00%\n" - ] - }, - { - "data": { - "text/plain": [ - "[None, None]" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "@mpc.run_multiprocess(world_size=2)\n", "def feature_aggregation_example():\n", " \"\"\"Apply feature aggregation access control model\"\"\"\n", " # Alice loads some features, Bob loads other features\n", - " features_alice_enc = crypten.load(filenames[\"features_alice\"], src=ALICE)\n", - " features_bob_enc = crypten.load(filenames[\"features_bob\"], src=BOB)\n", + " features_alice_enc = crypten.load_from_party(filenames[\"features_alice\"], src=ALICE)\n", + " features_bob_enc = crypten.load_from_party(filenames[\"features_bob\"], src=BOB)\n", " \n", " # Concatenate features\n", " features_enc = crypten.cat([features_alice_enc, features_bob_enc], dim=0)\n", @@ -378,71 +257,16 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch 0 --- Training Accuracy 53.40%\n", - "Epoch 1 --- Training Accuracy 58.70%\n", - "Epoch 2 --- Training Accuracy 63.80%\n", - "Epoch 3 --- Training Accuracy 68.30%\n", - "Epoch 5 --- Training Accuracy 78.00%\n", - "Epoch 6 --- Training Accuracy 81.00%\n", - "Epoch 7 --- Training Accuracy 84.60%\n", - "Epoch 8 --- Training Accuracy 87.00%\n", - "Epoch 9 --- Training Accuracy 90.40%\n", - "Epoch 10 --- Training Accuracy 91.50%\n", - "Epoch 11 --- Training Accuracy 92.90%\n", - "Epoch 12 --- Training Accuracy 93.80%\n", - "Epoch 13 --- Training Accuracy 94.40%\n", - "Epoch 14 --- Training Accuracy 95.30%\n", - "Epoch 15 --- Training Accuracy 96.30%\n", - "Epoch 16 --- Training Accuracy 96.20%\n", - "Epoch 17 --- Training Accuracy 96.80%\n", - "Epoch 19 --- Training Accuracy 97.20%\n", - "Epoch 20 --- Training Accuracy 97.90%\n", - "Epoch 21 --- Training Accuracy 97.80%\n", - "Epoch 22 --- Training Accuracy 98.00%\n", - "Epoch 23 --- Training Accuracy 98.90%\n", - "Epoch 24 --- Training Accuracy 99.20%\n", - "Epoch 25 --- Training Accuracy 99.40%\n", - "Epoch 26 --- Training Accuracy 99.40%\n", - "Epoch 27 --- Training Accuracy 99.60%\n", - "Epoch 28 --- Training Accuracy 99.00%\n", - "Epoch 29 --- Training Accuracy 99.30%\n", - "Epoch 30 --- Training Accuracy 99.30%\n", - "Epoch 31 --- Training Accuracy 99.40%\n", - "Epoch 32 --- Training Accuracy 99.50%\n", - "Epoch 33 --- Training Accuracy 99.90%\n", - "Epoch 34 --- Training Accuracy 99.70%\n", - "Epoch 35 --- Training Accuracy 99.70%\n", - "Epoch 36 --- Training Accuracy 99.90%\n", - "Epoch 38 --- Training Accuracy 100.00%\n", - "Epoch 39 --- Training Accuracy 100.00%\n", - "Test accuracy 92.00%\n" - ] - }, - { - "data": { - "text/plain": [ - "[None, None]" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "@mpc.run_multiprocess(world_size=2)\n", "def dataset_augmentation_example():\n", " \"\"\"Apply dataset augmentation access control model\"\"\" \n", " # Alice loads some samples, Bob loads other samples\n", - " samples_alice_enc = crypten.load(filenames[\"samples_alice\"], src=ALICE)\n", - " samples_bob_enc = crypten.load(filenames[\"samples_bob\"], src=BOB)\n", + " samples_alice_enc = crypten.load_from_party(filenames[\"samples_alice\"], src=ALICE)\n", + " samples_bob_enc = crypten.load_from_party(filenames[\"samples_bob\"], src=BOB)\n", " \n", " # Concatenate features\n", " samples_enc = crypten.cat([samples_alice_enc, samples_bob_enc], dim=1)\n", @@ -473,37 +297,19 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Test accuracy 100.00%\n" - ] - }, - { - "data": { - "text/plain": [ - "[None, None]" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "@mpc.run_multiprocess(world_size=2)\n", "def model_hiding_example():\n", " \"\"\"Apply model hiding access control model\"\"\"\n", " # Alice loads the model\n", - " w_true_enc = crypten.load(filenames[\"w_true\"], src=ALICE)\n", - " b_true_enc = crypten.load(filenames[\"b_true\"], src=ALICE)\n", + " w_true_enc = crypten.load_from_party(filenames[\"w_true\"], src=ALICE)\n", + " b_true_enc = crypten.load_from_party(filenames[\"b_true\"], src=ALICE)\n", " \n", " # Bob loads the features to be evaluated\n", - " test_features_enc = crypten.load(filenames[\"test_features\"], src=BOB)\n", + " test_features_enc = crypten.load_from_party(filenames[\"test_features\"], src=BOB)\n", " \n", " # Evaluate model\n", " evaluate_linear_svm(test_features_enc, test_labels, w_true_enc, b_true_enc)\n", @@ -524,7 +330,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -551,7 +357,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.4" + "version": "3.7.6" } }, "nbformat": 4, diff --git a/tutorials/Tutorial_4_Classification_with_Encrypted_Neural_Networks.ipynb b/tutorials/Tutorial_4_Classification_with_Encrypted_Neural_Networks.ipynb index 035fb8ac..2cc63a77 100644 --- a/tutorials/Tutorial_4_Classification_with_Encrypted_Neural_Networks.ipynb +++ b/tutorials/Tutorial_4_Classification_with_Encrypted_Neural_Networks.ipynb @@ -127,7 +127,7 @@ "source": [ "In CrypTen, encrypting PyTorch network is straightforward: we load a PyTorch model from file to the appropriate source, convert it to a CrypTen model and then encrypt it. Let us understand each of these steps.\n", "\n", - "As we did with CrypTensors in Tutorial 3, we will use CrypTen's load functionality (i.e., `crypten.load`) to read a model from file to a particular source. The source is indicated by the keyword argument `src`. As in Tutorial 3, this src argument tells us the rank of the party we want to load the model to (and later, encrypt the model from). In addition, here we also need to provide a dummy model to tell CrypTen the model's structure. The dummy model is indicated by the keyword argument `dummy_model`. Note that unlike loading a tensor, the result from `crypten.load` is not encrypted. Instead, only the `src` party's model is populated from the file.\n", + "As we did with CrypTensors in Tutorial 3, we will use CrypTen's load functionality (i.e., `crypten.load_from_party`) to read a model from file to a particular source. The source is indicated by the keyword argument `src`. As in Tutorial 3, this src argument tells us the rank of the party we want to load the model to (and later, encrypt the model from). In addition, here we also need to provide a dummy model to tell CrypTen the model's structure. The dummy model is indicated by the keyword argument `dummy_model`. Note that unlike loading a tensor, the result from `crypten.load_from_party` is not encrypted. Instead, only the `src` party's model is populated from the file.\n", "\n", "Once the model is loaded, we call the function `from_pytorch`: this function sets up a CrypTen network from the PyTorch network. It takes the plaintext network as input as well as dummy input. The dummy input must be a `torch` tensor of the same shape as a potential input to the network, however the values inside the tensor do not matter. \n", "\n", @@ -142,7 +142,7 @@ "source": [ "# Load pre-trained model to Alice\n", "dummy_model = AliceNet()\n", - "plaintext_model = crypten.load('models/tutorial4_alice_model.pth', dummy_model=dummy_model, src=ALICE)\n", + "plaintext_model = crypten.load_from_party('models/tutorial4_alice_model.pth', dummy_model=dummy_model, src=ALICE)\n", "\n", "# Encrypt the model from Alice: \n", "\n", @@ -183,7 +183,7 @@ "@mpc.run_multiprocess(world_size=2)\n", "def encrypt_model_and_data():\n", " # Load pre-trained model to Alice\n", - " model = crypten.load('models/tutorial4_alice_model.pth', dummy_model=dummy_model, src=ALICE)\n", + " model = crypten.load_from_party('models/tutorial4_alice_model.pth', dummy_model=dummy_model, src=ALICE)\n", " \n", " # Encrypt model from Alice \n", " dummy_input = torch.empty((1, 784))\n", @@ -191,7 +191,7 @@ " private_model.encrypt(src=ALICE)\n", " \n", " # Load data to Bob\n", - " data_enc = crypten.load('/tmp/bob_test.pth', src=BOB)\n", + " data_enc = crypten.load_from_party('/tmp/bob_test.pth', src=BOB)\n", " data_enc2 = data_enc[:count]\n", " data_flatten = data_enc2.flatten(start_dim=1)\n", "\n", @@ -227,7 +227,7 @@ "@mpc.run_multiprocess(world_size=2)\n", "def encrypt_model_and_data():\n", " # Load pre-trained model to Alice\n", - " plaintext_model = crypten.load('models/tutorial4_alice_model.pth', dummy_model=dummy_model, src=ALICE)\n", + " plaintext_model = crypten.load_from_party('models/tutorial4_alice_model.pth', dummy_model=dummy_model, src=ALICE)\n", " \n", " # Encrypt model from Alice \n", " dummy_input = torch.empty((1, 784))\n", @@ -235,7 +235,7 @@ " private_model.encrypt(src=ALICE)\n", " \n", " # Load data to Bob\n", - " data_enc = crypten.load('/tmp/bob_test.pth', src=BOB)\n", + " data_enc = crypten.load_from_party('/tmp/bob_test.pth', src=BOB)\n", " data_enc2 = data_enc[:count]\n", " data_flatten = data_enc2.flatten(start_dim=1)\n", "\n", diff --git a/tutorials/Tutorial_5_Under_the_hood_of_Encrypted_Networks.ipynb b/tutorials/Tutorial_5_Under_the_hood_of_Encrypted_Networks.ipynb index fa901426..f79e0c30 100644 --- a/tutorials/Tutorial_5_Under_the_hood_of_Encrypted_Networks.ipynb +++ b/tutorials/Tutorial_5_Under_the_hood_of_Encrypted_Networks.ipynb @@ -111,7 +111,7 @@ " rank = comm.get().get_rank()\n", " \n", " # Load and encrypt the layer\n", - " layer = crypten.load(layer_linear_file, dummy_model=nn.Linear(4, 2), src=ALICE)\n", + " layer = crypten.load_from_party(layer_linear_file, dummy_model=nn.Linear(4, 2), src=ALICE)\n", " layer_enc = crypten.nn.from_pytorch(layer, dummy_input=torch.empty((1,4)))\n", " layer_enc.encrypt(src=ALICE)\n", " \n", @@ -122,7 +122,7 @@ " print()\n", " \n", " # Load and encrypt data\n", - " data_enc = crypten.load(toy_data_file, src=BOB)\n", + " data_enc = crypten.load_from_party(toy_data_file, src=BOB)\n", " \n", " # Apply the encrypted layer (linear transformation):\n", " result_enc = layer_enc.forward(data_enc)\n", @@ -187,12 +187,12 @@ " rank = comm.get().get_rank()\n", " \n", " # Load and encrypt the layer\n", - " layer = crypten.load(layer_scale_file, dummy_model=nn.Linear(3, 3), src=ALICE)\n", + " layer = crypten.load_from_party(layer_scale_file, dummy_model=nn.Linear(3, 3), src=ALICE)\n", " layer_enc = crypten.nn.from_pytorch(layer, dummy_input=torch.empty((1,3)))\n", " layer_enc.encrypt(src=ALICE)\n", " \n", " # Load and encrypt data\n", - " data_enc = crypten.load(toy_data_file, src=BOB) \n", + " data_enc = crypten.load_from_party(toy_data_file, src=BOB) \n", " \n", " # Note that layer parameters are (still) encrypted:\n", " if rank == 0: # Print once for readability\n", @@ -340,7 +340,7 @@ "outputs": [], "source": [ "# Load the trained network to Alice\n", - "model_plaintext = crypten.load(sample_trained_model_file, dummy_model=AliceNet(), src=ALICE)\n", + "model_plaintext = crypten.load_from_party(sample_trained_model_file, dummy_model=AliceNet(), src=ALICE)\n", "\n", "# Convert the trained network to CrypTen network \n", "private_model = crypten.nn.from_pytorch(model_plaintext, dummy_input=torch.empty((1, 50)))\n", @@ -385,12 +385,12 @@ " rank = comm.get().get_rank()\n", "\n", " # Load and encrypt the network\n", - " model = crypten.load(sample_trained_model_file, dummy_model=AliceNet(), src=ALICE)\n", + " model = crypten.load_from_party(sample_trained_model_file, dummy_model=AliceNet(), src=ALICE)\n", " private_model = crypten.nn.from_pytorch(model, dummy_input=torch.empty((1, 50)))\n", " private_model.encrypt(src=ALICE)\n", "\n", " # Load and encrypt the data\n", - " data_enc = crypten.load(sample_data_bob_file, src=BOB)\n", + " data_enc = crypten.load_from_party(sample_data_bob_file, src=BOB)\n", "\n", " # Forward through the first layer\n", " out_enc = private_model._modules['5'].forward(data_enc)\n", diff --git a/tutorials/Tutorial_6_CrypTen_on_AWS_instances.ipynb b/tutorials/Tutorial_6_CrypTen_on_AWS_instances.ipynb index 5948fa9e..4ff9f945 100644 --- a/tutorials/Tutorial_6_CrypTen_on_AWS_instances.ipynb +++ b/tutorials/Tutorial_6_CrypTen_on_AWS_instances.ipynb @@ -27,216 +27,9 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Running world size 2 with instances: [ec2.Instance(id='i-0615648acb65e87ed'), ec2.Instance(id='i-0c9ae083275ab0b73')]\n", - "Connecting to i-0615648acb65e87ed...\n", - "Connected to i-0615648acb65e87ed\n", - "Connecting to i-0c9ae083275ab0b73...\n", - "Connected to i-0c9ae083275ab0b73\n", - "Uploading `/private/home/shobha/CrypTen/examples/mpc_linear_svm/launcher.py` to i-0615648acb65e87ed...\n", - "Uploading `/private/home/shobha/CrypTen/examples/mpc_linear_svm/mpc_linear_svm.py` to i-0615648acb65e87ed...\n", - "Uploading `/private/home/shobha/CrypTen/examples/mpc_linear_svm/launcher.py` to i-0c9ae083275ab0b73...\n", - "Uploading `/private/home/shobha/CrypTen/examples/mpc_linear_svm/mpc_linear_svm.py` to i-0c9ae083275ab0b73...\n", - "`/private/home/shobha/CrypTen/examples/mpc_linear_svm/launcher.py` uploaded to i-0615648acb65e87ed.\n", - "`/private/home/shobha/CrypTen/examples/mpc_linear_svm/mpc_linear_svm.py` uploaded to i-0615648acb65e87ed.\n", - "`/private/home/shobha/CrypTen/examples/mpc_linear_svm/launcher.py` uploaded to i-0c9ae083275ab0b73.\n", - "`/private/home/shobha/CrypTen/examples/mpc_linear_svm/mpc_linear_svm.py` uploaded to i-0c9ae083275ab0b73.\n", - "[i-0615648acb65e87ed] total 16\r\n", - "[i-0615648acb65e87ed] drwxrwxr-x 2 ec2-user ec2-user 4096 Sep 4 20:41 .\r\n", - "[i-0615648acb65e87ed] drwx------ 22 ec2-user ec2-user 4096 Sep 4 20:41 ..\r\n", - "[i-0615648acb65e87ed] -rwxrwxr-x 1 ec2-user ec2-user 2698 Sep 4 20:41 launcher.py\r\n", - "[i-0615648acb65e87ed] -rw-rw-r-- 1 ec2-user ec2-user 3397 Sep 4 20:41 mpc_linear_svm.py\r\n", - "[i-0615648acb65e87ed] \n", - "[i-0c9ae083275ab0b73] total 16\r\n", - "[i-0c9ae083275ab0b73] drwxrwxr-x 2 ec2-user ec2-user 4096 Sep 4 20:41 .\r\n", - "[i-0c9ae083275ab0b73] drwx------ 22 ec2-user ec2-user 4096 Sep 4 20:41 ..\r\n", - "[i-0c9ae083275ab0b73] -rwxrwxr-x 1 ec2-user ec2-user 2698 Sep 4 20:41 launcher.py\r\n", - "[i-0c9ae083275ab0b73] -rw-rw-r-- 1 ec2-user ec2-user 3397 Sep 4 20:41 mpc_linear_svm.py\r\n", - "[i-0c9ae083275ab0b73] \n", - "Run command: export WORLD_SIZE=2; export RENDEZVOUS=env://; export MASTER_ADDR=172.31.67.210; export MASTER_PORT=29500; export RANK=0; cd aws-launcher-tmp-57f5182c-cf54-11e9-8e9f-90e2badb2844 ; ./launcher.py --features 50 --examples 100 --epochs 50 --lr 0.5 --skip_plaintext\n", - "Run command: export WORLD_SIZE=2; export RENDEZVOUS=env://; export MASTER_ADDR=172.31.67.210; export MASTER_PORT=29500; export RANK=1; cd aws-launcher-tmp-57f5182c-cf54-11e9-8e9f-90e2badb2844 ; ./launcher.py --features 50 --examples 100 --epochs 50 --lr 0.5 --skip_plaintext\n", - "[i-0c9ae083275ab0b73] INFO:root:==================\r\n", - "[i-0c9ae083275ab0b73] INFO:root:DistributedCommunicator with rank 1\r\n", - "[i-0c9ae083275ab0b73] INFO:root:==================\r\n", - "[i-0c9ae083275ab0b73] INFO:root:World size = 2\r\n", - "[i-0c9ae083275ab0b73] \n", - "[i-0615648acb65e87ed] INFO:root:==================\r\n", - "[i-0615648acb65e87ed] INFO:root:DistributedCommunicator with rank 0\r\n", - "[i-0615648acb65e87ed] INFO:root:==================\r\n", - "[i-0615648acb65e87ed] INFO:root:World size = 2\r\n", - "[i-0615648acb65e87ed] INFO:root:==================\r\n", - "[i-0615648acb65e87ed] INFO:root:CrypTen Training\r\n", - "[i-0615648acb65e87ed] INFO:root:==================\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 1\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 46.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.028704 (0.028704)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 2\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 49.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015724 (0.022214)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 3\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 49.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015827 (0.020085)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 4\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 50.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015229 (0.018871)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 5\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 53.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.014997 (0.018096)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 6\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 54.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015650 (0.017688)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 7\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 54.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015314 (0.017349)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 8\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 55.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015085 (0.017066)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 9\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 58.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015701 (0.016915)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 10\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 59.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015011 (0.016724)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 11\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 60.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.014846 (0.016553)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 12\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 63.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015544 (0.016469)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 13\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 64.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015662 (0.016407)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 14\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 66.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015975 (0.016376)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 15\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 68.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015527 (0.016320)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 16\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 70.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015474 (0.016267)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 17\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 71.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.016068 (0.016255)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 18\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 73.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015703 (0.016224)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 19\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 73.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015182 (0.016170)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 20\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 77.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015349 (0.016129)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 21\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 74.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015506 (0.016099)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 22\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 76.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015579 (0.016075)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 23\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 78.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015471 (0.016049)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 24\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 79.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015229 (0.016015)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 25\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 78.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.014884 (0.015970)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 26\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 80.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015489 (0.015951)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 27\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 79.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015419 (0.015931)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 28\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 83.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015446 (0.015914)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 29\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 85.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015117 (0.015887)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 30\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 85.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015447 (0.015872)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 31\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 88.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015865 (0.015872)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 32\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 89.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015109 (0.015848)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 33\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 87.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015537 (0.015838)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 34\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 93.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015223 (0.015820)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 35\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 89.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.014812 (0.015792)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 36\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 95.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015444 (0.015782)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 37\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 98.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015514 (0.015775)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 38\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 94.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015790 (0.015775)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 39\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 95.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015150 (0.015759)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 40\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 97.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015664 (0.015757)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 41\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 98.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015070 (0.015740)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 42\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 98.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015453 (0.015733)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 43\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 98.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.014809 (0.015712)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 44\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 97.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015203 (0.015700)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 45\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 100.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015045 (0.015685)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 46\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 100.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015024 (0.015671)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 47\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 100.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015204 (0.015661)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 48\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 100.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015640 (0.015661)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 49\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 100.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.014968 (0.015647)\r\n", - "[i-0615648acb65e87ed] INFO:root:Epoch 50\r\n", - "[i-0615648acb65e87ed] INFO:root:--- Accuracy 100.00%\r\n", - "[i-0615648acb65e87ed] INFO:root: Time 0.015066 (0.015635)\r\n", - "[i-0615648acb65e87ed] INFO:root:CrypTen Weights:\r\n", - "[i-0615648acb65e87ed] INFO:root:tensor([[ 0.6266, 0.0708, -0.8273, 1.1054, 2.2259, 1.0269, -0.2454, 0.3465,\r\n", - "[i-0615648acb65e87ed] -0.2064, 1.1096, -0.4470, -0.9675, 0.8815, -0.0456, -0.3211, -0.4172,\r\n", - "[i-0615648acb65e87ed] -0.1068, -0.4309, 1.6191, 0.9273, 1.3629, -0.4860, -0.4709, 0.1397,\r\n", - "[i-0615648acb65e87ed] -0.1461, -1.2914, 0.5050, -0.2048, 0.1133, -0.6340, 0.1254, -0.7410,\r\n", - "[i-0615648acb65e87ed] 0.1307, 1.9361, -1.3594, -0.3963, -1.2906, 1.4484, -0.7685, 0.2926,\r\n", - "[i-0615648acb65e87ed] 0.8602, 0.3248, 0.0922, -0.4171, 0.7996, 0.2740, 0.5352, 0.6995,\r\n", - "[i-0615648acb65e87ed] 0.6882, 0.6521]])\r\n", - "[i-0615648acb65e87ed] INFO:root:CrypTen Bias:\r\n", - "[i-0615648acb65e87ed] INFO:root:tensor([0.2052])\r\n", - "[i-0615648acb65e87ed] \n" - ] - } - ], + "outputs": [], "source": [ "%%script bash\n", "python3 [PATH_TO_CRYPTEN]/CrypTen/scripts/aws_launcher.py \\\n", @@ -250,6 +43,13 @@ "--lr 0.5 \\\n", "--skip_plaintext" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -268,7 +68,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.4" + "version": "3.7.6" } }, "nbformat": 4, diff --git a/tutorials/Tutorial_7_Training_an_Encrypted_Neural_Network.ipynb b/tutorials/Tutorial_7_Training_an_Encrypted_Neural_Network.ipynb index a0ffacac..89bec05a 100644 --- a/tutorials/Tutorial_7_Training_an_Encrypted_Neural_Network.ipynb +++ b/tutorials/Tutorial_7_Training_an_Encrypted_Neural_Network.ipynb @@ -20,7 +20,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -33,7 +33,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -49,7 +49,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -96,7 +96,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -107,30 +107,19 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Load Alice's data \n", - "data_alice_enc = crypten.load('/tmp/alice_train.pth', src=ALICE)" + "data_alice_enc = crypten.load_from_party('/tmp/alice_train.pth', src=ALICE)" ] }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 0, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# We'll now set up the data for our small example below\n", "# For illustration purposes, we will create toy data\n", @@ -155,18 +144,9 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch: 0 Loss: 0.5206\n", - "Epoch: 1 Loss: 0.4807\n" - ] - } - ], + "outputs": [], "source": [ "# Example: Stochastic Gradient Descent in CrypTen\n", "\n", @@ -210,48 +190,9 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch 0 in progress:\n", - "\tBatch 1 of 10 Loss 0.4977\n", - "\tBatch 2 of 10 Loss 0.4340\n", - "\tBatch 3 of 10 Loss 0.3488\n", - "\tBatch 4 of 10 Loss 0.3358\n", - "\tBatch 5 of 10 Loss 0.2479\n", - "\tBatch 6 of 10 Loss 0.2365\n", - "\tBatch 7 of 10 Loss 0.2739\n", - "\tBatch 8 of 10 Loss 0.2007\n", - "\tBatch 9 of 10 Loss 0.2303\n", - "\tBatch 10 of 10 Loss 0.1538\n", - "Epoch 1 in progress:\n", - "\tBatch 1 of 10 Loss 0.1557\n", - "\tBatch 2 of 10 Loss 0.1142\n", - "\tBatch 3 of 10 Loss 0.1306\n", - "\tBatch 4 of 10 Loss 0.1756\n", - "\tBatch 5 of 10 Loss 0.0804\n", - "\tBatch 6 of 10 Loss 0.1284\n", - "\tBatch 7 of 10 Loss 0.2113\n", - "\tBatch 8 of 10 Loss 0.1103\n", - "\tBatch 9 of 10 Loss 0.1578\n", - "\tBatch 10 of 10 Loss 0.0928\n" - ] - }, - { - "data": { - "text/plain": [ - "[None, None]" - ] - }, - "execution_count": 0, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "import crypten.mpc as mpc\n", "import crypten.communicator as comm\n", @@ -265,8 +206,8 @@ "@mpc.run_multiprocess(world_size=2)\n", "def run_encrypted_training():\n", " # Load data:\n", - " x_alice_enc = crypten.load('/tmp/alice_train.pth', src=ALICE)\n", - " x_bob_enc = crypten.load('/tmp/bob_train.pth', src=BOB)\n", + " x_alice_enc = crypten.load_from_party('/tmp/alice_train.pth', src=ALICE)\n", + " x_bob_enc = crypten.load_from_party('/tmp/bob_train.pth', src=BOB)\n", " \n", " # Combine the feature sets: identical to Tutorial 3\n", " x_combined_enc = crypten.cat([x_alice_enc, x_bob_enc], dim=2)\n", @@ -337,7 +278,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -371,7 +312,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.5" + "version": "3.7.6" } }, "nbformat": 4, From f0b847bc247e7bf85d7c4ac98fc94d030d766089 Mon Sep 17 00:00:00 2001 From: George Muraru Date: Wed, 13 May 2020 09:16:21 +0300 Subject: [PATCH 3/3] Remove checkpoints --- .../Introduction-checkpoint.ipynb | 85 --- ...Basics_of_CrypTen_Tensors-checkpoint.ipynb | 347 ------------ ...rial_2_Inside_CrypTensors-checkpoint.ipynb | 239 -------- ...duction_to_Access_Control-checkpoint.ipynb | 365 ------------ ...Encrypted_Neural_Networks-checkpoint.ipynb | 337 ----------- ...ood_of_Encrypted_Networks-checkpoint.ipynb | 523 ------------------ ..._CrypTen_on_AWS_instances-checkpoint.ipynb | 76 --- ..._Encrypted_Neural_Network-checkpoint.ipynb | 320 ----------- 8 files changed, 2292 deletions(-) delete mode 100644 tutorials/.ipynb_checkpoints/Introduction-checkpoint.ipynb delete mode 100644 tutorials/.ipynb_checkpoints/Tutorial_1_Basics_of_CrypTen_Tensors-checkpoint.ipynb delete mode 100644 tutorials/.ipynb_checkpoints/Tutorial_2_Inside_CrypTensors-checkpoint.ipynb delete mode 100644 tutorials/.ipynb_checkpoints/Tutorial_3_Introduction_to_Access_Control-checkpoint.ipynb delete mode 100644 tutorials/.ipynb_checkpoints/Tutorial_4_Classification_with_Encrypted_Neural_Networks-checkpoint.ipynb delete mode 100644 tutorials/.ipynb_checkpoints/Tutorial_5_Under_the_hood_of_Encrypted_Networks-checkpoint.ipynb delete mode 100644 tutorials/.ipynb_checkpoints/Tutorial_6_CrypTen_on_AWS_instances-checkpoint.ipynb delete mode 100644 tutorials/.ipynb_checkpoints/Tutorial_7_Training_an_Encrypted_Neural_Network-checkpoint.ipynb diff --git a/tutorials/.ipynb_checkpoints/Introduction-checkpoint.ipynb b/tutorials/.ipynb_checkpoints/Introduction-checkpoint.ipynb deleted file mode 100644 index 80fb30a7..00000000 --- a/tutorials/.ipynb_checkpoints/Introduction-checkpoint.ipynb +++ /dev/null @@ -1,85 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Introduction to CrypTen\n", - "\n", - "CrypTen is a machine learning framework built on PyTorch that enables you to easily study and develop machine learning models using secure computing techniques. CrypTen allows you to develop models with the PyTorch API while performing computations on encrypted data -- without revealing the protected information. CrypTen ensures that sensitive or otherwise private data remains private, while still allowing model inference and training on encrypted data that may be aggregated across various organizations or users. \n", - "\n", - "CrypTen currently uses secure multiparty computation (MPC) (see (1)) as its cryptographic paradigm. To use CrypTen effectively, it is helpful to understand the secure MPC abstraction as well as the semi-honest adversarial model in which CrypTen operates. This introduction explains the basic concepts of secure MPC and the threat model. It then explores a few different use cases in which CrypTen may be applied. \n", - "\n", - "\n", - "## Secure Multi-party Computation\n", - "\n", - "Secure multi-party computation is an abstraction that allows multiple parties to compute a function on encrypted data, with protocols designed such that every party is able to access only their own data and data that all parties agree to reveal.\n", - "\n", - "Let's look at a concrete example to better understand this abstraction. Suppose we have three parties, A, B and C that each have a private number, and together want to compute the sum of all their private numbers. A secure MPC protocol for this computation will allow each party to learn the final sum; however, they will not learn any information about the other 2 parties' individual private numbers other than the aggregate sum.\n", - "\n", - "In secure MPC, the data owner encrypts its data by splitting it using random masks into n random shares that can be combined to reconstruct the original data. These n shares are then distributed between n parties. This process is called secret sharing. The parties can compute functions on the data by operating on the secret shares and can decrypt the final result by communicating the resulting shares amongst each other.\n", - "\n", - "\n", - "## Use Cases\n", - "In the tutorials, we show how to use CrypTen to four main scenarios:\n", - "
    \n", - "
  • Feature Aggregation: In the first scenario, multiple parties hold distinct sets of features, and want to perform computations over the joint feature set without sharing data. For example, different health providers may each have part of a patient's medical history, but may wish to use the patient's entire medical history to make better predictions while still protecting patient privacy.
  • \n", - " \n", - "
  • Data Labeling: Here, one party holds feature data while the another party holds corresponding labels, and the parties would like to learn a relationship without sharing data. This is similar to the feature aggregation with the exception that one party has labels rather than other features. For example, suppose that in previous healthcare scenario, one healthcare provider had access to health outcomes data, while other parties had access to health features. The parties may want to train a model that predicts health outcomes as a function of features, without exposing any health infomration between parties.
  • \n", - " \n", - "
  • Dataset Augmentation: In this scenario, several parties each hold a small number of samples, but would like to use all the examples in order to improve the statistical power of a measurement or model. \n", - " For example, when studying wage statistics across companies in a particular region, individual companies may not have enough data to make statistically significant hypotheses about the population. Wage data may be too sensitive to share openly, but privacy-preserving methods can be used to aggregate data across companies to make statistically significant measurements / models without exposing any individual company's data.
  • \n", - " \n", - "
  • Model Hiding: In the final scenario, one party has access to a trained model, while another party would like to apply that model to its own data. However, the data and model need to be kept private. \n", - " This can happen in cases where a model is proprietary, expensive to produce, and/or susceptible to white-box attacks, but has value to more than one party. Previously, this would have required the second party to send its data to the first to apply the model, but privacy-preserving techniques can be used when the data can't be exposed.
  • \n", - "
\n", - " \n", - "The tutorials illustrate how CrypTen can be used to model each of these scenarios.\n", - "\n", - "\n", - "## Threat Model\n", - "When determining whether MPC is appropriate for a certain computation, we must assess the assumptions we make about the actions that different parties can take. The threat model we use defines the assumptions we are allowed to make. CrypTen uses the \"honest-but-curious\" threat model (see (1, 2)). (This is sometimes also referred to as the \"semi-honest\" threat model.) It is specified by the following assumptions: \n", - "
    \n", - "
  • Every party faithfully follows the protocol: i.e., it performs all the computations specified in the program, and communicates the correct results to the appropriate parties specified in the program.
  • \n", - "
  • The communication channel is secure: no party can see any data that is not directly communicated to it.
  • \n", - "
  • Every party has access to a private source of randomness, e.g., a private coin to toss
  • \n", - "
  • Parties may use any data they have already seen and perform arbitrary processing to infer information.
  • \n", - "
\n", - "\n", - "\n", - "\n", - "## References\n", - "(1) Goldreich Oded. 2009. Foundations of Cryptography: Volume 2, Basic Applications (1st ed.). Cambridge University Press, New York, NY, USA.
\n", - "(2) Andrew C. Yao. 1982. Protocols for secure computations. In Proceedings of the 23rd Annual Symposium on Foundations of Computer Science (SFCS '82). IEEE Computer Society, Washington, DC, USA, 160-164." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tutorials/.ipynb_checkpoints/Tutorial_1_Basics_of_CrypTen_Tensors-checkpoint.ipynb b/tutorials/.ipynb_checkpoints/Tutorial_1_Basics_of_CrypTen_Tensors-checkpoint.ipynb deleted file mode 100644 index 80471c67..00000000 --- a/tutorials/.ipynb_checkpoints/Tutorial_1_Basics_of_CrypTen_Tensors-checkpoint.ipynb +++ /dev/null @@ -1,347 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "# Tutorial 1: Basics of CrypTen Tensors\n", - "\n", - "We now have a high-level understanding of how secure MPC works. Through these tutorials, we will explain how to use CrypTen to carry out secure operations on encrypted tensors. In this tutorial, we will introduce a fundamental building block in CrypTen, called a ```CrypTensor```. ```CrypTensor```s are encrypted ```torch``` tensors that can be used for computing securely on data. \n", - "\n", - "CrypTen currently only supports secure MPC protocols (though we intend to add support for other advanced encryption protocols). Using the ```mpc``` backend, ```CrypTensor```s act as ```torch``` tensors whose values are encrypted using secure MPC protocols. Tensors created using the ```mpc``` backend are called ```MPCTensor```s. We will go into greater detail about ```MPCTensors``` in Tutorial 2. \n", - "\n", - "Let's begin by importing ```crypten``` and ```torch``` libraries. (If the imports fail, please see the installation instructions in the README.)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import torch\n", - "import crypten\n", - "\n", - "crypten.init()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Creating Encrypted Tensors\n", - "CrypTen provides a ```crypten.cryptensor``` factory function, similar to ```torch.tensor```, to make creating ```CrypTensors``` easy. \n", - "\n", - "Let's begin by creating a ```torch``` tensor and encrypting it using ```crypten.cryptensor```. To decrypt a ```CrypTensor```, use ```get_plain_text()``` to return the original tensor. (```CrypTensor```s can also be created directly from a list or an array.)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Create torch tensor\n", - "x = torch.tensor([1.0, 2.0, 3.0])\n", - "\n", - "# Encrypt x\n", - "x_enc = crypten.cryptensor(x)\n", - "\n", - "# Decrypt x\n", - "x_dec = x_enc.get_plain_text() \n", - "print(x_dec)\n", - "\n", - "\n", - "# Create python list\n", - "y = [4.0, 5.0, 6.0]\n", - "\n", - "# Encrypt x\n", - "y_enc = crypten.cryptensor(y)\n", - "\n", - "# Decrypt x\n", - "y_dec = y_enc.get_plain_text()\n", - "print(y_dec)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Operations on Encrypted Tensors\n", - "Now let's look at what we can do with our ```CrypTensors```.\n", - "\n", - "#### Arithmetic Operations\n", - "We can carry out regular arithmetic operations between ```CrypTensors```, as well as between ```CrypTensors``` and plaintext tensors. Note that these operations never reveal any information about encrypted tensors (internally or externally) and return an encrypted tensor output." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#Arithmetic operations between CrypTensors and plaintext tensors\n", - "x_enc = crypten.cryptensor([1.0, 2.0, 3.0])\n", - "\n", - "y = 2.0\n", - "y_enc = crypten.cryptensor(2.0)\n", - "\n", - "\n", - "# Addition\n", - "z_enc1 = x_enc + y # Public\n", - "z_enc2 = x_enc + y_enc # Private\n", - "print(\"\\nPublic addition:\", z_enc1.get_plain_text())\n", - "print(\"Private addition:\", z_enc2.get_plain_text())\n", - "\n", - "\n", - "# Subtraction\n", - "z_enc1 = x_enc - y # Public\n", - "z_enc2 = x_enc - y_enc # Private\n", - "print(\"\\nPublic subtraction:\", z_enc1.get_plain_text())\n", - "print(\"Private subtraction:\", z_enc2.get_plain_text())\n", - "\n", - "# Multiplication\n", - "z_enc1 = x_enc * y # Public\n", - "z_enc2 = x_enc * y_enc # Private\n", - "print(\"\\nPublic multiplication:\", z_enc1.get_plain_text())\n", - "print(\"Private multiplication:\", z_enc2.get_plain_text())\n", - "\n", - "# Division\n", - "z_enc1 = x_enc / y # Public\n", - "z_enc2 = x_enc / y_enc # Private\n", - "print(\"\\nPublic division:\", z_enc1.get_plain_text())\n", - "print(\"Private division:\", z_enc2.get_plain_text())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Comparisons\n", - "Similarly, we can compute element-wise comparisons on ```CrypTensors```. Like arithmetic operations, comparisons performed on ```CrypTensor```s will return a ```CrypTensor``` result. Decrypting these result ```CrypTensor```s will evaluate to 0's and 1's corresponding to ```False``` and ```True``` values respectively." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#Construct two example CrypTensors\n", - "x_enc = crypten.cryptensor([1.0, 2.0, 3.0, 4.0, 5.0])\n", - "\n", - "y = torch.tensor([5.0, 4.0, 3.0, 2.0, 1.0])\n", - "y_enc = crypten.cryptensor(y)\n", - "\n", - "# Print values:\n", - "print(\"x: \", x_enc.get_plain_text())\n", - "print(\"y: \", y_enc.get_plain_text())\n", - "\n", - "# Less than\n", - "z_enc1 = x_enc < y # Public\n", - "z_enc2 = x_enc < y_enc # Private\n", - "print(\"\\nPublic (x < y) :\", z_enc1.get_plain_text())\n", - "print(\"Private (x < y) :\", z_enc2.get_plain_text())\n", - "\n", - "# Less than or equal\n", - "z_enc1 = x_enc <= y # Public\n", - "z_enc2 = x_enc <= y_enc # Private\n", - "print(\"\\nPublic (x <= y):\", z_enc1.get_plain_text())\n", - "print(\"Private (x <= y):\", z_enc2.get_plain_text())\n", - "\n", - "# Greater than\n", - "z_enc1 = x_enc > y # Public\n", - "z_enc2 = x_enc > y_enc # Private\n", - "print(\"\\nPublic (x > y) :\", z_enc1.get_plain_text())\n", - "print(\"Private (x > y) :\", z_enc2.get_plain_text())\n", - "\n", - "# Greater than or equal\n", - "z_enc1 = x_enc >= y # Public\n", - "z_enc2 = x_enc >= y_enc # Private\n", - "print(\"\\nPublic (x >= y):\", z_enc1.get_plain_text())\n", - "print(\"Private (x >= y):\", z_enc2.get_plain_text())\n", - "\n", - "# Equal\n", - "z_enc1 = x_enc == y # Public\n", - "z_enc2 = x_enc == y_enc # Private\n", - "print(\"\\nPublic (x == y):\", z_enc1.get_plain_text())\n", - "print(\"Private (x == y):\", z_enc2.get_plain_text())\n", - "\n", - "# Not Equal\n", - "z_enc1 = x_enc != y # Public\n", - "z_enc2 = x_enc != y_enc # Private\n", - "print(\"\\nPublic (x != y):\", z_enc1.get_plain_text())\n", - "print(\"Private (x != y):\", z_enc2.get_plain_text())\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Advanced mathematics\n", - "We are also able to compute more advanced mathematical functions on ```CrypTensors``` using iterative approximations. CrypTen provides MPC support for functions like reciprocal, exponential, logarithm, square root, tanh, etc. Notice that these are subject to numerical error due to the approximations used. \n", - "\n", - "Additionally, note that some of these functions will fail silently when input values are outside of the range of convergence for the approximations used. These do not produce errors because value are encrypted and cannot be checked without decryption. Exercise caution when using these functions. (It is good practice here to normalize input values for certain models.)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "torch.set_printoptions(sci_mode=False)\n", - "\n", - "#Construct example input CrypTensor\n", - "x = torch.tensor([0.1, 0.3, 0.5, 1.0, 1.5, 2.0, 2.5])\n", - "x_enc = crypten.cryptensor(x)\n", - "\n", - "# Reciprocal\n", - "z = x.reciprocal() # Public\n", - "z_enc = x_enc.reciprocal() # Private\n", - "print(\"\\nPublic reciprocal:\", z)\n", - "print(\"Private reciprocal:\", z_enc.get_plain_text())\n", - "\n", - "# Logarithm\n", - "z = x.log() # Public\n", - "z_enc = x_enc.log() # Private\n", - "print(\"\\nPublic logarithm:\", z)\n", - "print(\"Private logarithm:\", z_enc.get_plain_text())\n", - "\n", - "# Exp\n", - "z = x.exp() # Public\n", - "z_enc = x_enc.exp() # Private\n", - "print(\"\\nPublic exponential:\", z)\n", - "print(\"Private exponential:\", z_enc.get_plain_text())\n", - "\n", - "# Sqrt\n", - "z = x.sqrt() # Public\n", - "z_enc = x_enc.sqrt() # Private\n", - "print(\"\\nPublic square root:\", z)\n", - "print(\"Private square root:\", z_enc.get_plain_text())\n", - "\n", - "# Tanh\n", - "z = x.tanh() # Public\n", - "z_enc = x_enc.tanh() # Private\n", - "print(\"\\nPublic tanh:\", z)\n", - "print(\"Private tanh:\", z_enc.get_plain_text())\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Control Flow using Encrypted Tensors\n", - "\n", - "Note that ```CrypTensors``` cannot be used directly in conditional expressions. Because the tensor is encrypted, the boolean expression cannot be evaluated unless the tensor is decrypted first. Attempting to execute control flow using an encrypted condition will result in an error.\n", - "\n", - "Some control flow can still be executed without decrypting, but must be executed using mathematical expressions. We have provided the function ```crypten.where(condition, x, y)``` to abstract this kind of conditional value setting.\n", - "\n", - "The following example illustrates how to write this kind conditional logic for ```CrypTensors```." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x_enc = crypten.cryptensor(2.0)\n", - "y_enc = crypten.cryptensor(4.0)\n", - "\n", - "a, b = 2, 3\n", - "\n", - "# Normal Control-flow code will raise an error\n", - "try:\n", - " if x_enc < y_enc:\n", - " z = a\n", - " else:\n", - " z = b\n", - "except RuntimeError as error:\n", - " print(f\"RuntimeError caught: \\\"{error}\\\"\\n\")\n", - "\n", - " \n", - "# Instead use a mathematical expression\n", - "use_a = (x_enc < y_enc)\n", - "z_enc = use_a * a + (1 - use_a) * b\n", - "print(\"z:\", z_enc.get_plain_text())\n", - " \n", - " \n", - "# Or use the `where` function\n", - "z_enc = crypten.where(x_enc < y_enc, a, b)\n", - "print(\"z:\", z_enc.get_plain_text())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Advanced Indexing\n", - "CrypTen supports many of the operations that work on ```torch``` tensors. Encrypted tensors can be indexed, concatenated, stacked, reshaped, etc. For a full list of operations, see the CrypTen documentation." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x_enc = crypten.cryptensor([1.0, 2.0, 3.0])\n", - "y_enc = crypten.cryptensor([4.0, 5.0, 6.0])\n", - "\n", - "# Indexing\n", - "z_enc = x_enc[:-1]\n", - "print(\"Indexing:\\n\", z_enc.get_plain_text())\n", - "\n", - "# Concatenation\n", - "z_enc = crypten.cat([x_enc, y_enc])\n", - "print(\"\\nConcatenation:\\n\", z_enc.get_plain_text())\n", - "\n", - "# Stacking\n", - "z_enc = crypten.stack([x_enc, y_enc])\n", - "print('\\nStacking:\\n', z_enc.get_plain_text())\n", - "\n", - "# Reshaping\n", - "w_enc = z_enc.reshape((-1, 6))\n", - "print('\\nReshaping:\\n', w_enc.get_plain_text())\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### Implementation Note\n", - "\n", - "Due to internal implementation details, ```CrypTensors``` must be the first operand of operations that combine ```CrypTensor```s and ```torch``` tensors. That is, for a ```CrypTensor``` ```x_enc``` and a plaintext tensor ```y```:\n", - "- The expression ```x_enc < y``` is valid, but the equivalent expression ```y > x_enc``` will result in an error.\n", - "- The expression ```x_enc + y``` is valid, but the equivalent expression ```y + x_enc``` will result in an error.\n", - "\n", - "We intend to add support for both expressions in the future." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tutorials/.ipynb_checkpoints/Tutorial_2_Inside_CrypTensors-checkpoint.ipynb b/tutorials/.ipynb_checkpoints/Tutorial_2_Inside_CrypTensors-checkpoint.ipynb deleted file mode 100644 index 17cc3660..00000000 --- a/tutorials/.ipynb_checkpoints/Tutorial_2_Inside_CrypTensors-checkpoint.ipynb +++ /dev/null @@ -1,239 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Tutorial 2: Inside CrypTensors\n", - "\n", - "Note: This tutorial is optional, and can be skipped without any loss of continuity to the following tutorials.\n", - "\n", - "\n", - "In this tutorial, we will take a brief look at the internals of ```CrypTensors```. \n", - "\n", - "Using the `mpc` backend, a `CrypTensor` is a tensor encrypted using secure MPC protocols, called an `MPCTensor`. In order to support the mathematical operations required by the `MPCTensor`, CrypTen implements two kinds of secret-sharing protocols: arithmetic secret-sharing and binary secret-sharing. Arithmetic secret sharing forms the basis for most of the mathematical operations implemented by `MPCTensor`. Similarly, binary secret-sharing allows for the evaluation of logical expressions.\n", - "\n", - "In this tutorial, we'll first introduce the concept of a `CrypTensor` ptype (i.e. private-type), and show how to use it to obtain `MPCTensors` that use arithmetic and binary secret shares. We will also describe how each of these ptypes is used, and how they can be combined to implement desired functionality." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#import the libraries\n", - "import crypten\n", - "import torch\n", - "\n", - "#initialize crypten\n", - "crypten.init()\n", - "#Disables OpenMP threads -- needed by @mpc.run_multiprocess which uses fork\n", - "torch.set_num_threads(1)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "## ptype in CrypTen\n", - "CrypTen defines the `ptype` (for private-type) attribute of an `MPCTensor` to denote the kind of secret-sharing protocol used in the `CrypTensor`. The `ptype` is, in many ways, analogous to the `dtype` of PyTorch. The `ptype` may have two values: \n", - "\n", - "- `crypten.arithmetic` for `ArithmeticSharedTensors`\n", - "- `crypten.binary` for `BinarySharedTensors`\n", - "\n", - "We can use the `ptype` attribute to create a `CrypTensor` with the appropriate secret-sharing protocol. For example: " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#Constructing CrypTensors with ptype attribute\n", - "\n", - "#arithmetic secret-shared tensors\n", - "x_enc = crypten.cryptensor([1.0, 2.0, 3.0], ptype=crypten.arithmetic)\n", - "print(\"x_enc internal type:\", x_enc.ptype)\n", - "\n", - "#binary secret-shared tensors\n", - "y = torch.tensor([1, 2, 1], dtype=torch.int32)\n", - "y_enc = crypten.cryptensor(y, ptype=crypten.binary)\n", - "print(\"y_enc internal type:\", y_enc.ptype)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Arithmetic secret-sharing\n", - "Let's look more closely at the `crypten.arithmetic` ptype. Most of the mathematical operations implemented by `CrypTensors` are implemented using arithmetic secret sharing. As such, `crypten.arithmetic` is the default ptype for newly generated `CrypTensor`s. \n", - "\n", - "Let's begin by creating a new `CrypTensor` using `ptype=crypten.arithmetic` to enforce that the encryption is done via arithmetic secret sharing. We can print values of each share to confirm that values are being encrypted properly. \n", - "\n", - "To do so, we will need to create multiple parties to hold each share. We do this here using the `@mpc.run_multiprocess` function decorator, which we developed to execute crypten code from a single script (as we have in a Jupyter notebook). CrypTen follows the standard MPI programming model: it runs a separate process for each party, but each process runs an identical (complete) program. Each process has a `rank` variable to identify itself.\n", - "\n", - "Note that the sum of the two `_tensor` attributes below is equal to a scaled representation of the input. (Because MPC requires values to be integers, we scale input floats to a fixed-point encoding before encryption.)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import crypten.mpc as mpc\n", - "import crypten.communicator as comm \n", - "\n", - "@mpc.run_multiprocess(world_size=2)\n", - "def examine_arithmetic_shares():\n", - " x_enc = crypten.cryptensor([1, 2, 3], ptype=crypten.arithmetic)\n", - " \n", - " rank = comm.get().get_rank()\n", - " print(f\"Rank {rank}:\\n {x_enc}\")\n", - " \n", - "x = examine_arithmetic_shares()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Binary secret-sharing\n", - "The second type of secret-sharing implemented in CrypTen is binary or XOR secret-sharing. This type of secret-sharing allows greater efficiency in evaluating logical expressions. \n", - "\n", - "Let's look more closely at the `crypten.binary` ptype. Most of the logical operations implemented by `CrypTensors` are implemented using arithmetic secret sharing. We typically use this type of secret-sharing when we want to evaluate binary operators (i.e. `^ & | >> <<`, etc.) or logical operations (like comparitors).\n", - "\n", - "Let's begin by creating a new `CrypTensor` using `ptype=crypten.binary` to enforce that the encryption is done via binary secret sharing. We can print values of each share to confirm that values are being encrypted properly, as we did for arithmetic secret-shares.\n", - "\n", - "(Note that an xor of the two `_tensor` attributes below is equal to an unscaled version of input.)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "@mpc.run_multiprocess(world_size=2)\n", - "def examine_binary_shares():\n", - " x_enc = crypten.cryptensor([2, 3], ptype=crypten.binary)\n", - " \n", - " rank = comm.get().get_rank()\n", - " print(f\"Rank {rank}:\\n {x_enc}\")\n", - " \n", - "x = examine_binary_shares()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using Both Secret-sharing Protocols\n", - "Quite often a mathematical function may need to use both additive and XOR secret sharing for efficient evaluation. Functions that require conversions between sharing types include comparators (`>, >=, <, <=, ==, !=`) as well as functions derived from them (`abs, sign, relu`, etc.). For a full list of supported functions, please see the CrypTen documentation.\n", - "\n", - "CrypTen provides functionality that allows for the conversion of between ptypes. Conversion between ptypes can be done using the `.to()` function with a `crypten.ptype` input, or by calling the `.arithmetic()` and `.binary()` conversion functions." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from crypten.mpc import MPCTensor\n", - "\n", - "@mpc.run_multiprocess(world_size=2)\n", - "def examine_conversion():\n", - " x = torch.tensor([1, 2, 3])\n", - " rank = comm.get().get_rank()\n", - "\n", - " # create an MPCTensor with arithmetic secret sharing\n", - " x_enc_arithmetic = MPCTensor(x, ptype=crypten.arithmetic)\n", - " \n", - " # To binary\n", - " x_enc_binary = x_enc_arithmetic.to(crypten.binary)\n", - " x_from_binary = x_enc_binary.get_plain_text()\n", - " \n", - " if rank == 0: # only print once\n", - " print(\"to(crypten.binary):\")\n", - " print(f\" ptype: {x_enc_binary.ptype}\\n plaintext: {x_from_binary}\\n\")\n", - "\n", - " \n", - " # To arithmetic\n", - " x_enc_arithmetic = x_enc_arithmetic.to(crypten.arithmetic)\n", - " x_from_arithmetic = x_enc_arithmetic.get_plain_text()\n", - " \n", - " if rank == 0: # only print once\n", - " print(\"to(crypten.arithmetic):\")\n", - " print(f\" ptype: {x_enc_arithmetic.ptype}\\n plaintext: {x_from_arithmetic}\\n\")\n", - "\n", - " \n", - "z = examine_conversion()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Data Sources\n", - "CrypTen follows the standard MPI programming model: it runs a separate process for each party, but each process runs an identical (complete) program. Each process has a `rank` variable to identify itself.\n", - "\n", - "If the process with rank `i` is the source of data `x`, then `x` gets encrypted with `i` as its source value (denoted as `src`). However, MPI protocols require that both processes to provide a tensor with the same size as their input. CrypTen ignores all data provided from non-source processes when encrypting.\n", - "\n", - "In the next example, we'll show how to use the `rank` and `src` values to encrypt tensors. Here, we will have each of 3 parties generate a value `x` which is equal to its own `rank` value. Within the loop, 3 encrypted tensors are created, each with a different source. When these tensors are decrypted, we can verify that the tensors are generated using the tensor provided by the source process.\n", - "\n", - "(Note that `crypten.cryptensor` uses rank 0 as the default source if none is provided.)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "@mpc.run_multiprocess(world_size=3)\n", - "def examine_sources():\n", - " # Create a different tensor on each rank\n", - " rank = comm.get().get_rank()\n", - " x = torch.tensor(rank)\n", - " print(f\"Rank {rank}: {x}\")\n", - " \n", - " # \n", - " world_size = comm.get().get_world_size()\n", - " for i in range(world_size):\n", - " x_enc = crypten.cryptensor(x, src=i)\n", - " z = x_enc.get_plain_text()\n", - " \n", - " # Only print from one process to avoid duplicates\n", - " if rank == 0: print(f\"Source {i}: {z}\")\n", - " \n", - "x = examine_sources()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tutorials/.ipynb_checkpoints/Tutorial_3_Introduction_to_Access_Control-checkpoint.ipynb b/tutorials/.ipynb_checkpoints/Tutorial_3_Introduction_to_Access_Control-checkpoint.ipynb deleted file mode 100644 index da7aa1e2..00000000 --- a/tutorials/.ipynb_checkpoints/Tutorial_3_Introduction_to_Access_Control-checkpoint.ipynb +++ /dev/null @@ -1,365 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "# Introduction to Access Control\n", - "\n", - "We can now start using CrypTen to carry out private computations in some common use cases. In this tutorial, we will demonstrate how CrypTen would apply for the scenarios described in the Introduction. In all scenarios, we'll use a simple two-party setting and demonstrate how we can learn a linear SVM. In the process, we will see how access control works in CrypTen.\n", - "\n", - "As usual, we'll begin by importing the `crypten` and `torch` libraries, and initialize `crypten` with `crypten.init()`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import crypten\n", - "import torch\n", - "\n", - "crypten.init()\n", - "torch.set_num_threads(1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Setup\n", - "In this tutorial, we will train a Linear SVM to perform binary classification. We will first generate 1000 ground truth samples using 100 features and a randomly generated hyperplane to separate positive and negative examples. \n", - "\n", - "(Note: this will cause our classes to be linearly separable, so a linear SVM will be able to classify with perfect accuracy given the right parameters.)\n", - "\n", - "We will also include a test set of examples (that are also linearly separable by the same hyperplane) to show that the model learns a general hyperplane rather than memorizing the training data." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "num_features = 100\n", - "num_train_examples = 1000\n", - "num_test_examples = 100\n", - "epochs = 40\n", - "lr = 3.0\n", - "\n", - "# Set random seed for reproducibility\n", - "torch.manual_seed(1)\n", - "\n", - "features = torch.randn(num_features, num_train_examples)\n", - "w_true = torch.randn(1, num_features)\n", - "b_true = torch.randn(1)\n", - "\n", - "labels = w_true.matmul(features).add(b_true).sign()\n", - "\n", - "test_features = torch.randn(num_features, num_test_examples)\n", - "test_labels = w_true.matmul(test_features).add(b_true).sign()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that we have generated our dataset, we will train our SVM in four different access control scenarios across two parties, Alice and Bob:\n", - "\n", - "- Data Labeling: Alice has access to features, while Bob has access to labels\n", - "- Feature Aggregation: Alice has access to the first 50 features, while Bob has access to the last 50 features\n", - "- Data Augmentation: Alice has access to the first 500 examples, while Bob has access to the last 500 examples\n", - "- Model Hiding: Alice has access to `w_true` and `b_true`, while Bob has access to data samples to be classified\n", - "\n", - "Throughout this tutorial, we will assume Alice is using the rank 0 process, while Bob is using the rank 1 process. Additionally we will initialize our weights using random values." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ALICE = 0\n", - "BOB = 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In each example, we will use the same code to train our linear SVM once the features and labels are properly encrypted. This code is contained in `examples/mpc_linear_svm`, but it is unnecessary to understand the training code to properly use access control. The training process itself is discussed in depth in later tutorials.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from examples.mpc_linear_svm.mpc_linear_svm import train_linear_svm, evaluate_linear_svm" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Saving / Loading Data\n", - "\n", - "We have now generated features and labels for our model to learn. In the scenarios we explore in this tutorial, we would like to ensure that each party only has access to some subset of the data we have generated. To do so, we will use special save / load methods that CrypTen provides to handle loading only to a specified party and synchronizing across processes. \n", - "\n", - "We will use `crypten.save_from_party()` here to save data from a particular source, then we will load using `crypten.load_from_party()` in each example to load on a particular source. The following code will save all data we will use to files, then each example will load its data as necessary.\n", - "\n", - "(Note that because we are operating on a single machine, all processes will have access to all of the files we are using. However, this still will work as expected when operating across machines.)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from crypten import mpc\n", - "\n", - "# Specify file locations to save each piece of data\n", - "filenames = {\n", - " \"features\": \"/tmp/features.pth\",\n", - " \"labels\": \"/tmp/labels.pth\",\n", - " \"features_alice\": \"/tmp/features_alice.pth\",\n", - " \"features_bob\": \"/tmp/features_bob.pth\",\n", - " \"samples_alice\": \"/tmp/samples_alice.pth\",\n", - " \"samples_bob\": \"/tmp/samples_bob.pth\",\n", - " \"w_true\": \"/tmp/w_true.pth\",\n", - " \"b_true\": \"/tmp/b_true.pth\",\n", - " \"test_features\": \"/tmp/test_features.pth\",\n", - " \"test_labels\": \"/tmp/test_labels.pth\",\n", - "}\n", - "\n", - "\n", - "@mpc.run_multiprocess(world_size=2)\n", - "def save_all_data():\n", - " # Save features, labels for Data Labeling example\n", - " crypten.save_from_party(features, filenames[\"features\"])\n", - " crypten.save_from_party(labels, filenames[\"labels\"])\n", - " \n", - " # Save split features for Feature Aggregation example\n", - " features_alice = features[:50]\n", - " features_bob = features[50:]\n", - " \n", - " crypten.save_from_party(features_alice, filenames[\"features_alice\"], src=ALICE)\n", - " crypten.save_from_party(features_bob, filenames[\"features_bob\"], src=BOB)\n", - " \n", - " # Save split dataset for Dataset Aggregation example\n", - " samples_alice = features[:, :500]\n", - " samples_bob = features[:, 500:]\n", - " crypten.save_from_party(samples_alice, filenames[\"samples_alice\"], src=ALICE)\n", - " crypten.save_from_party(samples_bob, filenames[\"samples_bob\"], src=BOB)\n", - " \n", - " # Save true model weights and biases for Model Hiding example\n", - " crypten.save_from_party(w_true, filenames[\"w_true\"], src=ALICE)\n", - " crypten.save_from_party(b_true, filenames[\"b_true\"], src=ALICE)\n", - " \n", - " crypten.save_from_party(test_features, filenames[\"test_features\"], src=BOB)\n", - " crypten.save_from_party(test_labels, filenames[\"test_labels\"], src=BOB)\n", - " \n", - "save_all_data()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Scenario 1: Data Labeling\n", - "\n", - "Our first example will focus on the Data Labeling scenario. In this example, Alice has access to features, while Bob has access to the labels. We will train our linear svm by encrypting the features from Alice and the labels from Bob, then training our SVM using an aggregation of the encrypted data.\n", - "\n", - "In order to indicate the source of a given encrypted tensor, we encrypt our tensor using `crypten.load()` (from a file) or `crypten.cryptensor()` (from a tensor) using a keyword argument `src`. This `src` argument takes the rank of the party we want to encrypt from (recall that ALICE is 0 and BOB is 1). \n", - "\n", - "(If the `src` is not specified, it will default to the rank 0 party. We will use the default when encrypting public values since the source is irrelevant in this case.)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from crypten import mpc\n", - "\n", - "@mpc.run_multiprocess(world_size=2)\n", - "def data_labeling_example():\n", - " \"\"\"Apply data labeling access control model\"\"\"\n", - " # Alice loads features, Bob loads labels\n", - " features_enc = crypten.load_from_party(filenames[\"features\"], src=ALICE)\n", - " labels_enc = crypten.load_from_party(filenames[\"labels\"], src=BOB)\n", - " \n", - " # Execute training\n", - " w, b = train_linear_svm(features_enc, labels_enc, epochs=epochs, lr=lr)\n", - " \n", - " # Evaluate model\n", - " evaluate_linear_svm(test_features, test_labels, w, b)\n", - " \n", - "data_labeling_example()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Scenario 2: Feature Aggregation\n", - "\n", - "Next, we'll show how we can use CrypTen in the Feature Aggregation scenario. Here Alice and Bob each have 50 features for each sample, and would like to use their combined features to train a model. As before, Alice and Bob wish to keep their respective data private. This scenario can occur when multiple parties measure different features of a similar system, and their measurements may be proprietary or otherwise sensitive.\n", - "\n", - "Unlike the last scenario, one of our variables is split among two parties. This means we will have to concatenate the tensors encrypted from each party before passing them to the training code." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "@mpc.run_multiprocess(world_size=2)\n", - "def feature_aggregation_example():\n", - " \"\"\"Apply feature aggregation access control model\"\"\"\n", - " # Alice loads some features, Bob loads other features\n", - " features_alice_enc = crypten.load_from_party(filenames[\"features_alice\"], src=ALICE)\n", - " features_bob_enc = crypten.load_from_party(filenames[\"features_bob\"], src=BOB)\n", - " \n", - " # Concatenate features\n", - " features_enc = crypten.cat([features_alice_enc, features_bob_enc], dim=0)\n", - " \n", - " # Encrypt labels\n", - " labels_enc = crypten.cryptensor(labels)\n", - " \n", - " # Execute training\n", - " w, b = train_linear_svm(features_enc, labels_enc, epochs=epochs, lr=lr)\n", - " \n", - " # Evaluate model\n", - " evaluate_linear_svm(test_features, test_labels, w, b)\n", - " \n", - "feature_aggregation_example()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Scenario 3: Dataset Augmentation\n", - "\n", - "The next example shows how we can use CrypTen in a Data Augmentation scenario. Here Alice and Bob each have 500 samples, and would like to learn a classifier over their combined sample data. This scenario can occur in applications where several parties may each have access to a small amount of sensitive data, where no individual party has enough data to train an accurate model.\n", - "\n", - "Like the last scenario, one of our variables is split amongst parties, so we will have to concatenate tensors from encrypted from different parties. The main difference from the last scenario is that we are concatenating over the other dimension (the sample dimension rather than the feature dimension)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "@mpc.run_multiprocess(world_size=2)\n", - "def dataset_augmentation_example():\n", - " \"\"\"Apply dataset augmentation access control model\"\"\" \n", - " # Alice loads some samples, Bob loads other samples\n", - " samples_alice_enc = crypten.load_from_party(filenames[\"samples_alice\"], src=ALICE)\n", - " samples_bob_enc = crypten.load_from_party(filenames[\"samples_bob\"], src=BOB)\n", - " \n", - " # Concatenate features\n", - " samples_enc = crypten.cat([samples_alice_enc, samples_bob_enc], dim=1)\n", - " \n", - " labels_enc = crypten.cryptensor(labels)\n", - " \n", - " # Execute training\n", - " w, b = train_linear_svm(samples_enc, labels_enc, epochs=epochs, lr=lr)\n", - " \n", - " # Evaluate model\n", - " evaluate_linear_svm(test_features, test_labels, w, b)\n", - " \n", - "dataset_augmentation_example()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Scenario 4: Model Hiding\n", - "\n", - "The last scenario we will explore involves model hiding. Here, Alice has a pre-trained model that cannot be revealed, while Bob would like to use this model to evaluate on private data sample(s). This scenario can occur when a pre-trained model is proprietary or contains sensitive information, but can provide value to other parties with sensitive data.\n", - "\n", - "This scenario is somewhat different from the previous examples because we are not interested in training the model. Therefore, we do not need labels. Instead, we will demonstrate this example by encrypting the true model parameters (`w_true` and `b_true`) from Alice and encrypting the test set from Bob for evaluation.\n", - "\n", - "(Note: Because we are using the true weights and biases used to generate the test labels, we will get 100% accuracy.)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "@mpc.run_multiprocess(world_size=2)\n", - "def model_hiding_example():\n", - " \"\"\"Apply model hiding access control model\"\"\"\n", - " # Alice loads the model\n", - " w_true_enc = crypten.load_from_party(filenames[\"w_true\"], src=ALICE)\n", - " b_true_enc = crypten.load_from_party(filenames[\"b_true\"], src=ALICE)\n", - " \n", - " # Bob loads the features to be evaluated\n", - " test_features_enc = crypten.load_from_party(filenames[\"test_features\"], src=BOB)\n", - " \n", - " # Evaluate model\n", - " evaluate_linear_svm(test_features_enc, test_labels, w_true_enc, b_true_enc)\n", - " \n", - "model_hiding_example()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this tutorial we have reviewed four techniques where CrypTen can be used to perform encrypted training / inference. Each of these techniques can be used to facilitate computations in different privacy-preserving scenarios. However, these techniques can also be combined to increase the amount of scenarios where CrypTen can maintain privacy.\n", - "\n", - "For example, we can combine feature aggregation and data labeling to train a model on data split between three parties, where two parties each have access to a subset of features, and the third party has access to labels.\n", - "\n", - "Before exiting this tutorial, please clean up the files generated using the following code." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "\n", - "for fn in filenames.values():\n", - " if os.path.exists(fn): os.remove(fn)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tutorials/.ipynb_checkpoints/Tutorial_4_Classification_with_Encrypted_Neural_Networks-checkpoint.ipynb b/tutorials/.ipynb_checkpoints/Tutorial_4_Classification_with_Encrypted_Neural_Networks-checkpoint.ipynb deleted file mode 100644 index 2cc63a77..00000000 --- a/tutorials/.ipynb_checkpoints/Tutorial_4_Classification_with_Encrypted_Neural_Networks-checkpoint.ipynb +++ /dev/null @@ -1,337 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "# Classification with Encrypted Neural Networks\n", - "\n", - "In this tutorial, we'll look at how we can achieve the Model Hiding application we discussed in the Introduction. That is, suppose say Alice has a trained model she wishes to keep private, and Bob has some data he wishes to classify while keeping it private. We will see how CrypTen allows Alice and Bob to coordinate and classify the data, while achieving their privacy requirements.\n", - "\n", - "To simulate this scenario, we will begin with Alice training a simple neural network on MNIST data. Then we'll see how Alice and Bob encrypt their network and data respectively, classify the encrypted data and finally decrypt the labels.\n", - "\n", - "## Setup\n", - "\n", - "We first import the `torch` and `crypten` libraries, and initialize `crypten`. We will use a helper script `mnist_utils.py` to split the public MNIST data into Alice's portion and Bob's portion. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import crypten\n", - "import torch\n", - "\n", - "crypten.init()\n", - "torch.set_num_threads(1)\n", - "\n", - "#ignore warnings\n", - "import warnings; \n", - "warnings.filterwarnings(\"ignore\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run script that downloads the publicly available MNIST data, and splits the data as required.\n", - "%run ./mnist_utils.py --option train_v_test" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we will define the structure of Alice's network as a class. Even though Alice has a pre-trained model, the CrypTen will require this structure as input." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define Alice's network\n", - "import torch.nn as nn\n", - "import torch.nn.functional as F\n", - "\n", - "class AliceNet(nn.Module):\n", - " def __init__(self):\n", - " super(AliceNet, self).__init__()\n", - " self.fc1 = nn.Linear(784, 128)\n", - " self.fc2 = nn.Linear(128, 128)\n", - " self.fc3 = nn.Linear(128, 10)\n", - " \n", - " def forward(self, x):\n", - " out = self.fc1(x)\n", - " out = F.relu(out)\n", - " out = self.fc2(out)\n", - " out = F.relu(out)\n", - " out = self.fc3(out)\n", - " return out\n", - " \n", - " def set_all_parameters(self, value):\n", - " for p in self.parameters():\n", - " nn.init.constant_(p, value)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We will also define a helper routine `compute_accuracy` to make it easy to compute the accuracy of the output we get." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def compute_accuracy(output, labels):\n", - " pred = output.argmax(1)\n", - " correct = pred.eq(labels)\n", - " correct_count = correct.sum(0, keepdim=True).float()\n", - " accuracy = correct_count.mul_(100.0 / output.size(0))\n", - " return accuracy" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Encrypting a Pre-trained Model\n", - "\n", - "Assume that Alice has a pre-trained network ready to classify data. Let's see how we can use CrypTen to encrypt this network, so it can be used to classify data without revealing its parameters. We'll use the pre-trained model in `models/tutorial4_alice_model.pth` in this tutorial. As in Tutorial 3, we will assume Alice is using the rank 0 process, while Bob is using the rank 1 process. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ALICE = 0\n", - "BOB = 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In CrypTen, encrypting PyTorch network is straightforward: we load a PyTorch model from file to the appropriate source, convert it to a CrypTen model and then encrypt it. Let us understand each of these steps.\n", - "\n", - "As we did with CrypTensors in Tutorial 3, we will use CrypTen's load functionality (i.e., `crypten.load_from_party`) to read a model from file to a particular source. The source is indicated by the keyword argument `src`. As in Tutorial 3, this src argument tells us the rank of the party we want to load the model to (and later, encrypt the model from). In addition, here we also need to provide a dummy model to tell CrypTen the model's structure. The dummy model is indicated by the keyword argument `dummy_model`. Note that unlike loading a tensor, the result from `crypten.load_from_party` is not encrypted. Instead, only the `src` party's model is populated from the file.\n", - "\n", - "Once the model is loaded, we call the function `from_pytorch`: this function sets up a CrypTen network from the PyTorch network. It takes the plaintext network as input as well as dummy input. The dummy input must be a `torch` tensor of the same shape as a potential input to the network, however the values inside the tensor do not matter. \n", - "\n", - "Finally, we call `encrypt` on the CrypTen network to encrypt its parameters. Once we call the `encrypt` function, the models `encrypted` property will verify that the model parameters have been encrypted. (Encrypted CrypTen networks can also be decrypted using the `decrypt` function)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Load pre-trained model to Alice\n", - "dummy_model = AliceNet()\n", - "plaintext_model = crypten.load_from_party('models/tutorial4_alice_model.pth', dummy_model=dummy_model, src=ALICE)\n", - "\n", - "# Encrypt the model from Alice: \n", - "\n", - "# 1. Create a dummy input with the same shape as the model input\n", - "dummy_input = torch.empty((1, 784))\n", - "\n", - "# 2. Construct a CrypTen network with the trained model and dummy_input\n", - "private_model = crypten.nn.from_pytorch(plaintext_model, dummy_input)\n", - "\n", - "# 3. Encrypt the CrypTen network with src=ALICE\n", - "private_model.encrypt(src=ALICE)\n", - "\n", - "#Check that model is encrypted:\n", - "print(\"Model successfully encrypted:\", private_model.encrypted)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Classifying Encrypted Data with Encrypted Model\n", - "\n", - "We can now use Alice's encrypted network to classify Bob's data. For this, we need to encrypt Bob's data as well, as we did in Tutorial 3 (recall that Bob has the rank 1 process). Once Alice's network and Bob's data are both encrypted, CrypTen inference is performed with essentially identical steps as in PyTorch. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import crypten.mpc as mpc\n", - "import crypten.communicator as comm\n", - "\n", - "labels = torch.load('/tmp/bob_test_labels.pth').long()\n", - "count = 100 # For illustration purposes, we'll use only 100 samples for classification\n", - "\n", - "@mpc.run_multiprocess(world_size=2)\n", - "def encrypt_model_and_data():\n", - " # Load pre-trained model to Alice\n", - " model = crypten.load_from_party('models/tutorial4_alice_model.pth', dummy_model=dummy_model, src=ALICE)\n", - " \n", - " # Encrypt model from Alice \n", - " dummy_input = torch.empty((1, 784))\n", - " private_model = crypten.nn.from_pytorch(model, dummy_input)\n", - " private_model.encrypt(src=ALICE)\n", - " \n", - " # Load data to Bob\n", - " data_enc = crypten.load_from_party('/tmp/bob_test.pth', src=BOB)\n", - " data_enc2 = data_enc[:count]\n", - " data_flatten = data_enc2.flatten(start_dim=1)\n", - "\n", - " # Classify the encrypted data\n", - " private_model.eval()\n", - " output_enc = private_model(data_flatten)\n", - " \n", - " # Compute the accuracy\n", - " output = output_enc.get_plain_text()\n", - " accuracy = compute_accuracy(output, labels[:count])\n", - " print(\"\\tAccuracy: {0:.4f}\".format(accuracy.item()))\n", - " \n", - "encrypt_model_and_data()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Validating Encrypted Classification\n", - "\n", - "Finally, we will verify that CrypTen classification results in encrypted output, and that this output can be decrypted into meaningful labels. \n", - "\n", - "To see this, in this tutorial, we will just check whether the result is an encrypted tensor; in the next tutorial, we will look into the values of tensor and confirm the encryption. We will also decrypt the result. As we discussed before, Alice and Bob both have access to the decrypted output of the model, and can both use this to obtain the labels. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "@mpc.run_multiprocess(world_size=2)\n", - "def encrypt_model_and_data():\n", - " # Load pre-trained model to Alice\n", - " plaintext_model = crypten.load_from_party('models/tutorial4_alice_model.pth', dummy_model=dummy_model, src=ALICE)\n", - " \n", - " # Encrypt model from Alice \n", - " dummy_input = torch.empty((1, 784))\n", - " private_model = crypten.nn.from_pytorch(plaintext_model, dummy_input)\n", - " private_model.encrypt(src=ALICE)\n", - " \n", - " # Load data to Bob\n", - " data_enc = crypten.load_from_party('/tmp/bob_test.pth', src=BOB)\n", - " data_enc2 = data_enc[:count]\n", - " data_flatten = data_enc2.flatten(start_dim=1)\n", - "\n", - " # Classify the encrypted data\n", - " private_model.eval()\n", - " output_enc = private_model(data_flatten)\n", - " \n", - " # Verify the results are encrypted: \n", - " print(\"Output tensor encrypted:\", crypten.is_encrypted_tensor(output_enc)) \n", - "\n", - " # Decrypting the result\n", - " output = output_enc.get_plain_text()\n", - "\n", - " # Obtaining the labels\n", - " pred = output.argmax(dim=1)\n", - " print(\"Decrypted labels:\\n\", pred)\n", - " \n", - "encrypt_model_and_data()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "This completes our tutorial. While we have used a simple network here to illustrate the concepts, CrypTen provides primitives to allow for encryption of substantially more complex networks. In our examples section, we demonstrate how CrypTen can be used to encrypt LeNet and ResNet, among others. \n", - "\n", - "Before exiting this tutorial, please clean up the files generated using the following code." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "\n", - "filenames = ['/tmp/alice_train.pth', \n", - " '/tmp/alice_train_labels.pth', \n", - " '/tmp/bob_test.pth', \n", - " '/tmp/bob_test_labels.pth']\n", - "\n", - "for fn in filenames:\n", - " if os.path.exists(fn): os.remove(fn)" - ] - } - ], - "metadata": { - "bento_stylesheets": { - "bento/extensions/flow/main.css": true, - "bento/extensions/kernel_selector/main.css": true, - "bento/extensions/kernel_ui/main.css": true, - "bento/extensions/new_kernel/main.css": true, - "bento/extensions/system_usage/main.css": true, - "bento/extensions/theme/main.css": true - }, - "disseminate_notebook_id": { - "notebook_id": "390894444956881" - }, - "disseminate_notebook_info": { - "bento_version": "20190826-030256", - "description": "", - "hide_code": false, - "hipster_group": "", - "kernel_build_info": { - "error": "The file located at '/data/users/shobha/fbsource/fbcode/bento/kernels/local/cryptenk/TARGETS' could not be found." - }, - "no_uii": true, - "notebook_number": "139932", - "others_can_edit": true, - "reviewers": "", - "revision_id": "375902760006757", - "tags": "", - "tasks": "", - "title": "Tutorial 4 -- Classification with Encrypted Neural Networks" - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tutorials/.ipynb_checkpoints/Tutorial_5_Under_the_hood_of_Encrypted_Networks-checkpoint.ipynb b/tutorials/.ipynb_checkpoints/Tutorial_5_Under_the_hood_of_Encrypted_Networks-checkpoint.ipynb deleted file mode 100644 index f79e0c30..00000000 --- a/tutorials/.ipynb_checkpoints/Tutorial_5_Under_the_hood_of_Encrypted_Networks-checkpoint.ipynb +++ /dev/null @@ -1,523 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Under the Hood of Encrypted Neural Networks\n", - "\n", - "This tutorial is optional, and can be skipped without loss of continuity.\n", - "\n", - "In this tutorial, we'll take a look at how CrypTen performs inference with an encrypted neural network on encrypted data. We'll see how the data remains encrypted through all the operations, and yet is able to obtain accurate results after the computation. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import crypten\n", - "import torch\n", - "\n", - "crypten.init() \n", - "torch.set_num_threads(1)\n", - "\n", - "# Ignore warnings\n", - "import warnings; \n", - "warnings.filterwarnings(\"ignore\")\n", - "\n", - "# Keep track of all created temporary files so that we can clean up at the end\n", - "temp_files = []" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## A Simple Linear Layer\n", - "We'll start by examining how a single Linear layer works in CrypTen. We'll instantiate a torch Linear layer, convert to CrypTen layer, encrypt it, and step through some toy data with it. As in earlier tutorials, we'll assume Alice has the rank 0 process and Bob has the rank 1 process. We'll also assume Alice has the layer and Bob has the data." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define ALICE and BOB src values\n", - "ALICE = 0\n", - "BOB = 1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import torch.nn as nn\n", - "import types\n", - "\n", - "# Instantiate single Linear layer\n", - "layer_linear = nn.Linear(4, 2)\n", - "\n", - "def set_all_parameters(module, value):\n", - " for p in module.parameters():\n", - " nn.init.constant_(p, value)\n", - "\n", - "# Make sure we have the set_all_parameters method added to the\n", - "# Linear Module since this is not native to PyTorch\n", - "nn.Linear.set_all_parameters = set_all_parameters\n", - "\n", - "# The weights and the bias are initialized to small random values\n", - "print(\"Plaintext Weights:\", layer_linear._parameters['weight'])\n", - "print(\"Plaintext Bias:\", layer_linear._parameters['bias'])\n", - "\n", - "# Save the plaintext layer\n", - "layer_linear_file = \"/tmp/tutorial5_layer_alice1.pth\"\n", - "crypten.save(layer_linear, layer_linear_file)\n", - "temp_files.append(layer_linear_file) \n", - "\n", - "# Generate some toy data\n", - "features = 4\n", - "examples = 3\n", - "toy_data = torch.rand(examples, features)\n", - "\n", - "# Save the plaintext toy data\n", - "toy_data_file = \"/tmp/tutorial5_data_bob1.pth\"\n", - "crypten.save(toy_data, toy_data_file)\n", - "temp_files.append(toy_data_file)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import crypten.mpc as mpc\n", - "import crypten.communicator as comm\n", - "\n", - "@mpc.run_multiprocess(world_size=2)\n", - "def forward_single_encrypted_layer():\n", - " rank = comm.get().get_rank()\n", - " \n", - " # Load and encrypt the layer\n", - " layer = crypten.load_from_party(layer_linear_file, dummy_model=nn.Linear(4, 2), src=ALICE)\n", - " layer_enc = crypten.nn.from_pytorch(layer, dummy_input=torch.empty((1,4)))\n", - " layer_enc.encrypt(src=ALICE)\n", - " \n", - " # Note that layer parameters are encrypted:\n", - " if rank == 0: # Print once for readability\n", - " print(\"Weights:\\n\", layer_enc.weight.share)\n", - " print(\"Bias:\\n\", layer_enc.bias.share)\n", - " print()\n", - " \n", - " # Load and encrypt data\n", - " data_enc = crypten.load_from_party(toy_data_file, src=BOB)\n", - " \n", - " # Apply the encrypted layer (linear transformation):\n", - " result_enc = layer_enc.forward(data_enc)\n", - " \n", - " # Decrypt the result:\n", - " result = result_enc.get_plain_text()\n", - " \n", - " # Examine the result\n", - " if rank == 0: # Print once for readability\n", - " print(\"Decrypted result:\\n\", result)\n", - " \n", - "forward_single_encrypted_layer()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can see that the application of the encrypted linear layer on the encrypted data produces an encrypted result, which we can then decrypt to get the values in plaintext.\n", - "\n", - "Let's look at a second linear transformation, to give a flavor of how accuracy is preserved even when the data and the layer are encrypted. We'll look at a uniform scaling transformation, in which all tensor elements are multiplied by the same scalar factor. Again, we'll assume Alice has the layer and the rank 0 process, and Bob has the data and the rank 1 process." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Initialize a linear layer with random weights\n", - "layer_scale = nn.Linear(3, 3)\n", - "\n", - "# Construct a uniform scaling matrix: we'll scale by factor 5\n", - "factor = 5\n", - "layer_scale._parameters['weight'] = torch.eye(3)*factor\n", - "layer_scale._parameters['bias'] = torch.zeros_like(layer_scale._parameters['bias'])\n", - "\n", - "# Save the plaintext layer\n", - "layer_scale_file = \"/tmp/tutorial5_layer_alice2.pth\"\n", - "crypten.save(layer_scale, layer_scale_file)\n", - "temp_files.append(layer_scale_file)\n", - "\n", - "# Construct some toy data\n", - "features = 3\n", - "examples = 2\n", - "toy_data = torch.ones(examples, features)\n", - "\n", - "# Save the plaintext toy data\n", - "toy_data_file = \"/tmp/tutorial5_data_bob2.pth\"\n", - "crypten.save(toy_data, toy_data_file)\n", - "temp_files.append(toy_data_file)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "@mpc.run_multiprocess(world_size=2)\n", - "def forward_scaling_layer():\n", - " rank = comm.get().get_rank()\n", - " \n", - " # Load and encrypt the layer\n", - " layer = crypten.load_from_party(layer_scale_file, dummy_model=nn.Linear(3, 3), src=ALICE)\n", - " layer_enc = crypten.nn.from_pytorch(layer, dummy_input=torch.empty((1,3)))\n", - " layer_enc.encrypt(src=ALICE)\n", - " \n", - " # Load and encrypt data\n", - " data_enc = crypten.load_from_party(toy_data_file, src=BOB) \n", - " \n", - " # Note that layer parameters are (still) encrypted:\n", - " if rank == 0: # Print once for readability\n", - " print(\"Weights:\\n\", layer_enc.weight.share)\n", - " print(\"Bias:\\n\", layer_enc.bias.share)\n", - " print()\n", - "\n", - " # Apply the encrypted scaling transformation\n", - " result_enc = layer_enc.forward(data_enc)\n", - "\n", - " # Decrypt the result:\n", - " result = result_enc.get_plain_text()\n", - " \n", - " # Since both parties have the same decrypted values, print only rank 0 for readability\n", - " if rank == 0:\n", - " print(\"Plaintext result:\\n\", (result))\n", - " \n", - "z = forward_scaling_layer()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The resulting plaintext tensor is correctly scaled, even though we applied the encrypted transformation on the encrypted input! " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Multi-layer Neural Networks\n", - "Let's now look at how the encrypted input moves through an encrypted multi-layer neural network. \n", - "\n", - "For ease of explanation, we'll first step through a network with only two linear layers and ReLU activations. Again, we'll assume Alice has a network and Bob has some data, and they wish to run encrypted inference. \n", - "\n", - "To simulate this, we'll once again generate some toy data and train Alice's network on it. Then we'll encrypt Alice's network, Bob's data, and step through every layer in the network with the encrypted data. Through this, we'll see how the computations get applied although the network and the data are encrypted.\n", - "\n", - "### Setup\n", - "As in Tutorial 3, we will first generate 1000 ground truth samples using 50 features and a randomly generated hyperplane to separate positive and negative examples. We will then modify the labels so that they are all non-negative. Finally, we will split the data so that the first 900 samples belong to Alice and the last 100 samples belong to Bob." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Setup\n", - "features = 50\n", - "examples = 1000\n", - "\n", - "# Set random seed for reproducibility\n", - "torch.manual_seed(1)\n", - "\n", - "# Generate toy data and separating hyperplane\n", - "data = torch.randn(examples, features)\n", - "w_true = torch.randn(1, features)\n", - "b_true = torch.randn(1)\n", - "labels = w_true.matmul(data.t()).add(b_true).sign()\n", - "\n", - "# Change labels to non-negative values\n", - "labels_nn = torch.where(labels==-1, torch.zeros(labels.size()), labels)\n", - "labels_nn = labels_nn.squeeze().long()\n", - "\n", - "# Split data into Alice's and Bob's portions:\n", - "data_alice, labels_alice = data[:900], labels_nn[:900]\n", - "data_bob, labels_bob = data[900:], labels_nn[900:]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define Alice's network\n", - "import torch.nn as nn\n", - "import torch.nn.functional as F\n", - "\n", - "class AliceNet(nn.Module):\n", - " def __init__(self):\n", - " super(AliceNet, self).__init__()\n", - " self.fc1 = nn.Linear(50, 20)\n", - " self.fc2 = nn.Linear(20, 2)\n", - " \n", - " def forward(self, x):\n", - " out = self.fc1(x)\n", - " out = F.relu(out)\n", - " out = self.fc2(out)\n", - " return out\n", - " \n", - " def set_all_parameters(self, value):\n", - " for p in self.parameters():\n", - " nn.init.constant_(p, value)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Train and save Alice's network\n", - "model = AliceNet()\n", - "criterion = nn.CrossEntropyLoss()\n", - "optimizer = torch.optim.SGD(model.parameters(), lr=0.1)\n", - "\n", - "for i in range(500): \n", - " #forward pass: compute prediction\n", - " output = model(data_alice)\n", - " \n", - " #compute and print loss\n", - " loss = criterion(output, labels_alice)\n", - " if i % 100 == 99:\n", - " print(\"Epoch\", i, \"Loss:\", loss.item())\n", - " \n", - " #zero gradients for learnable parameters\n", - " optimizer.zero_grad()\n", - " \n", - " #backward pass: compute gradient with respect to model parameters\n", - " loss.backward()\n", - " \n", - " #update model parameters\n", - " optimizer.step()\n", - "\n", - "sample_trained_model_file = '/tmp/tutorial5_alice_model.pth'\n", - "torch.save(model, sample_trained_model_file)\n", - "temp_files.append(sample_trained_model_file)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Stepping through a Multi-layer Network\n", - "\n", - "Let's now look at what happens when we load the network Alice's has trained and encrypt it. First, we'll look at how the network structure changes when we convert it from a PyTorch network to CrypTen network." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Load the trained network to Alice\n", - "model_plaintext = crypten.load_from_party(sample_trained_model_file, dummy_model=AliceNet(), src=ALICE)\n", - "\n", - "# Convert the trained network to CrypTen network \n", - "private_model = crypten.nn.from_pytorch(model_plaintext, dummy_input=torch.empty((1, 50)))\n", - "# Encrypt the network\n", - "private_model.encrypt(src=ALICE)\n", - "\n", - "# Examine the structure of the encrypted CrypTen network\n", - "for name, curr_module in private_model._modules.items():\n", - " print(\"Name:\", name, \"\\tModule:\", curr_module)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We see that the encrypted network has 3 modules, named '5', '6' and 'output', denoting the first Linear layer, the ReLU activation, and the second Linear layer respectively. These modules are encrypted just as the layers in the previous section were. \n", - "\n", - "Now let's encrypt Bob's data, and step it through each encrypted module. For readability, we will use only 3 examples from Bob's data to illustrate the inference. Note how Bob's data remains encrypted after each individual layer's computation!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Pre-processing: Select only the first three examples in Bob's data for readability\n", - "data = data_bob[:3]\n", - "sample_data_bob_file = '/tmp/tutorial5_data_bob3.pth'\n", - "torch.save(data, sample_data_bob_file)\n", - "temp_files.append(sample_data_bob_file)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "@mpc.run_multiprocess(world_size=2)\n", - "def step_through_two_layers(): \n", - " rank = comm.get().get_rank()\n", - "\n", - " # Load and encrypt the network\n", - " model = crypten.load_from_party(sample_trained_model_file, dummy_model=AliceNet(), src=ALICE)\n", - " private_model = crypten.nn.from_pytorch(model, dummy_input=torch.empty((1, 50)))\n", - " private_model.encrypt(src=ALICE)\n", - "\n", - " # Load and encrypt the data\n", - " data_enc = crypten.load_from_party(sample_data_bob_file, src=BOB)\n", - "\n", - " # Forward through the first layer\n", - " out_enc = private_model._modules['5'].forward(data_enc)\n", - " print(\"Rank: {} First Linear Layer: Output Encrypted: {}\\n\".format(rank, crypten.is_encrypted_tensor(out_enc)))\n", - " print(\"Rank: {} Shares after First Linear Layer:{}\\n\".format(rank, out_enc.share))\n", - "\n", - " # Apply ReLU activation\n", - " out_enc = private_model._modules['6'].forward(out_enc)\n", - " print(\"Rank: {} ReLU:\\n Output Encrypted: {}\\n\".format(rank, crypten.is_encrypted_tensor(out_enc)))\n", - " print(\"Rank: {} Shares after ReLU: {}\\n\".format(rank, out_enc.share))\n", - "\n", - " # Forward through the second Linear layer\n", - " out_enc = private_model._modules['output'].forward(out_enc)\n", - " print(\"Rank: {} Second Linear layer:\\n Output Encrypted: {}\\n\".format(rank, crypten.is_encrypted_tensor(out_enc))), \n", - " print(\"Rank: {} Shares after Second Linear layer:{}\\n\".format(rank, out_enc.share))\n", - "\n", - " # Decrypt the output\n", - " out_dec = out_enc.get_plain_text()\n", - " # Since both parties have same decrypted results, only print the rank 0 output\n", - " if rank == 0:\n", - " print(\"Decrypted output:\\n Output Encrypted:\", crypten.is_encrypted_tensor(out_dec))\n", - " print(\"Tensors:\\n\", out_dec)\n", - " \n", - "z = step_through_two_layers()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Again, we emphasize that the output of each layer is an encrypted tensor. Only after the final call to `get_plain_text` do we get the plaintext tensor.\n", - "\n", - "### From PyTorch to CrypTen: Structural Changes in Network Architecture \n", - "\n", - "We have used a simple two-layer network in the above example, but the same ideas apply to more complex networks and operations. However, in more complex networks, there may not always be a one-to-one mapping between the PyTorch layers and the CrypTen layers. This is because we use PyTorch's onnx implementation to convert PyTorch models to CrypTen models. \n", - "As an example, we'll take a typical network used to classify digits in MNIST data, and look at what happens to its structure we convert it to a CrypTen module. (As we only wish to illustrate the structural changes in layers, we will not train this network on data; we will just use it with its randomly initialized weights). " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define Alice's network\n", - "class AliceNet2(nn.Module):\n", - " def __init__(self):\n", - " super(AliceNet2, self).__init__()\n", - " self.conv1 = nn.Conv2d(1, 16, kernel_size=5, padding=0)\n", - " self.conv2 = nn.Conv2d(16, 16, kernel_size=5, padding=0)\n", - " self.fc1 = nn.Linear(16 * 4 * 4, 100)\n", - " self.fc2 = nn.Linear(100, 10)\n", - " self.batchnorm1 = nn.BatchNorm2d(16)\n", - " self.batchnorm2 = nn.BatchNorm2d(16)\n", - " self.batchnorm3 = nn.BatchNorm1d(100)\n", - " \n", - " def forward(self, x):\n", - " out = self.conv1(x)\n", - " out = self.batchnorm1(out)\n", - " out = F.relu(out)\n", - " out = F.avg_pool2d(out, 2)\n", - " out = self.conv2(out)\n", - " out = self.batchnorm2(out)\n", - " out = F.relu(out)\n", - " out = F.avg_pool2d(out, 2)\n", - " out = out.view(out.size(0), -1)\n", - " out = self.fc1(out)\n", - " out = self.batchnorm3(out)\n", - " out = F.relu(out)\n", - " out = self.fc2(out)\n", - " return out\n", - " \n", - "model = AliceNet2()\n", - "\n", - "# Let's encrypt the complex network. \n", - "# Create dummy input of the correct input shape for the model\n", - "dummy_input = torch.empty((1, 1, 28, 28))\n", - "\n", - "# Encrypt the network\n", - "private_model = crypten.nn.from_pytorch(model, dummy_input)\n", - "private_model.encrypt(src=ALICE)\n", - "\n", - "# Examine the structure of the encrypted network\n", - "for name, curr_module in private_model._modules.items():\n", - " print(\"Name:\", name, \"\\tModule:\", curr_module)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice how the CrypTen network has split some the layers in the PyTorch module into several CrypTen modules. Each PyTorch operation may correspond to one or more operations in CrypTen. However, during the conversion, these are sometimes split due to limitations intorduced by onnx.\n", - "\n", - "Before exiting this tutorial, please clean up the files generated using the following code." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "for fn in temp_files:\n", - " if os.path.exists(fn): os.remove(fn)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tutorials/.ipynb_checkpoints/Tutorial_6_CrypTen_on_AWS_instances-checkpoint.ipynb b/tutorials/.ipynb_checkpoints/Tutorial_6_CrypTen_on_AWS_instances-checkpoint.ipynb deleted file mode 100644 index 4ff9f945..00000000 --- a/tutorials/.ipynb_checkpoints/Tutorial_6_CrypTen_on_AWS_instances-checkpoint.ipynb +++ /dev/null @@ -1,76 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# CrypTen on AWS Instances\n", - "\n", - "Our previous tutorials have covered the essentials of using CrypTen on our local machines. We also provides a script `aws_launcher.py` in the `scripts` directory that will allow you to compute on encrypted data on multiple AWS instances. \n", - "\n", - "For example, if Alice has a classifier on one AWS instance and Bob has data on another AWS instance, `aws_launcher.py` will allow Alice and Bob to classify the data without revealing their respective private information (just as we did in Tutorial 4). \n", - "\n", - "## Using the Launcher Script\n", - "\n", - "The steps to follow are:\n", - "
    \n", - "
  1. First, create multiple AWS instances with public AMI \"Deep Learning AMI (Ubuntu) Version 24.0\", and record the instance IDs.
  2. \n", - "
  3. Install PyTorch, CrypTen and dependencies of the program to be run on all AWS instances.
  4. \n", - "
  5. Run `aws_launcher.py` on your local machine, as we explain below.
  6. \n", - "
\n", - "The results are left on the AWS instances. Log messages will be printed on your local machine by launcher script.\n", - "\n", - "## Sample Run\n", - "\n", - "The following cell shows a sample usage of the `aws_launcher.py` script. Note, however, that the command in the cell will not work as is: please replace the parameters used with appropriate ones with your own AWS instances, usernames, and `.ssh` keys (see documentation in the `aws_launcher.py` script). " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%script bash\n", - "python3 [PATH_TO_CRYPTEN]/CrypTen/scripts/aws_launcher.py \\\n", - "--ssh_key_file [SSH_KEY_FILE] --instances=[AWS_INSTANCE1, AWS_INSTANCE2...] \\\n", - "--region [AWS_REGION] \\\n", - "--ssh_user [AWS_USERNAME] \\\n", - "--aux_files=[PATH_TO_CRYPTEN]/CrypTen/examples/mpc_linear_svm/mpc_linear_svm.py [PATH_TO_CRYPTEN]/CrypTen/examples/mpc_linear_svm/launcher.py \\\n", - "--features 50 \\\n", - "--examples 100 \\\n", - "--epochs 50 \\\n", - "--lr 0.5 \\\n", - "--skip_plaintext" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tutorials/.ipynb_checkpoints/Tutorial_7_Training_an_Encrypted_Neural_Network-checkpoint.ipynb b/tutorials/.ipynb_checkpoints/Tutorial_7_Training_an_Encrypted_Neural_Network-checkpoint.ipynb deleted file mode 100644 index 89bec05a..00000000 --- a/tutorials/.ipynb_checkpoints/Tutorial_7_Training_an_Encrypted_Neural_Network-checkpoint.ipynb +++ /dev/null @@ -1,320 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Training an Encrypted Neural Network\n", - "\n", - "In this tutorial, we will walk through an example of how we can train a neural network with CrypTen. This is particularly relevant for the Feature Aggregation, Data Labeling and Data Augmentation use cases. We will focus on the usual two-party setting and show how we can train an accurate neural network for digit classification on the MNIST data.\n", - "\n", - "For concreteness, this tutorial will step through the Feature Aggregation use cases: Alice and Bob each have part of the features of the data set, and wish to train a neural network on their combined data, while keeping their data private. \n", - "\n", - "## Setup\n", - "As usual, we'll begin by importing and initializing the `crypten` and `torch` libraries. \n", - "\n", - "We will use the MNIST dataset to demonstrate how Alice and Bob can learn without revealing protected information. For reference, the feature size of each example in the MNIST data is `28 x 28`. Let's assume Alice has the first `28 x 20` features and Bob has last `28 x 8` features. One way to think of this split is that Alice has the (roughly) top 2/3rds of each image, while Bob has the bottom 1/3rd of each image. We'll again use our helper script `mnist_utils.py` that downloads the publicly available MNIST data, and splits the data as required.\n", - "\n", - "For simplicity, we will restrict our problem to binary classification: we'll simply learn how to distinguish between 0 and non-zero digits. For speed of execution in the notebook, we will only create a dataset of a 100 examples." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import crypten\n", - "import torch\n", - "\n", - "crypten.init()\n", - "torch.set_num_threads(1)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%run ./mnist_utils.py --option features --reduced 100 --binary" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we'll define the network architecture below, and then describe how to train it on encrypted data in the next section. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import torch.nn as nn\n", - "import torch.nn.functional as F\n", - "\n", - "#Define an example network\n", - "class ExampleNet(nn.Module):\n", - " def __init__(self):\n", - " super(ExampleNet, self).__init__()\n", - " self.conv1 = nn.Conv2d(1, 16, kernel_size=5, padding=0)\n", - " self.fc1 = nn.Linear(16 * 12 * 12, 100)\n", - " self.fc2 = nn.Linear(100, 2) # For binary classification, final layer needs only 2 outputs\n", - " \n", - " def forward(self, x):\n", - " out = self.conv1(x)\n", - " out = F.relu(out)\n", - " out = F.max_pool2d(out, 2)\n", - " out = out.view(out.size(0), -1)\n", - " out = self.fc1(out)\n", - " out = F.relu(out)\n", - " out = self.fc2(out)\n", - " return out" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Encrypted Training\n", - "\n", - "After all the material we've covered in earlier tutorials, we only need to know a few additional items for encrypted training. We'll first discuss how the training loop in CrypTen differs from PyTorch. Then, we'll go through a complete example to illustrate training on encrypted data from end-to-end.\n", - "\n", - "### How does CrypTen training differ from PyTorch training?\n", - "\n", - "There are two main ways implementing a CrypTen training loop differs from a PyTorch training loop. We'll describe these items first, and then illustrate them with small examples below.\n", - "\n", - "(1) Use one-hot encoding: CrypTen training requires all labels to use one-hot encoding. This means that when using standard datasets such as MNIST, we need to modify the labels to use one-hot encoding.\n", - "\n", - "(2) Directly update parameters: CrypTen does not use the PyTorch optimizers. Instead, CrypTen implements encrypted SGD by implementing its own `backward` function, followed by directly updating the parameters. As we will see below, using SGD in CrypTen is very similar to using the PyTorch optimizers.\n", - "\n", - "We now show some small examples to illustrate these differences. As before, we will assume Alice has the rank 0 process and Bob has the rank 1 process." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define source argument values for Alice and Bob\n", - "ALICE = 0\n", - "BOB = 1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Load Alice's data \n", - "data_alice_enc = crypten.load_from_party('/tmp/alice_train.pth', src=ALICE)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# We'll now set up the data for our small example below\n", - "# For illustration purposes, we will create toy data\n", - "# and encrypt all of it from source ALICE\n", - "x_small = torch.rand(100, 1, 28, 28)\n", - "y_small = torch.randint(1, (100,))\n", - "\n", - "# Transform labels into one-hot encoding\n", - "label_eye = torch.eye(2)\n", - "y_one_hot = label_eye[y_small]\n", - "\n", - "# Transform all data to CrypTensors\n", - "x_train = crypten.cryptensor(x_small, src=ALICE)\n", - "y_train = crypten.cryptensor(y_one_hot)\n", - "\n", - "# Instantiate and encrypt a CrypTen model\n", - "model_plaintext = ExampleNet()\n", - "dummy_input = torch.empty(1, 1, 28, 28)\n", - "model = crypten.nn.from_pytorch(model_plaintext, dummy_input)\n", - "model.encrypt()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Example: Stochastic Gradient Descent in CrypTen\n", - "\n", - "model.train() # Change to training mode\n", - "loss = crypten.nn.MSELoss() # Choose loss functions\n", - "\n", - "# Set parameters: learning rate, num_epochs\n", - "learning_rate = 0.001\n", - "num_epochs = 2\n", - "\n", - "# Train the model: SGD on encrypted data\n", - "for i in range(num_epochs):\n", - "\n", - " # forward pass\n", - " output = model(x_train)\n", - " loss_value = loss(output, y_train)\n", - " \n", - " # set gradients to zero\n", - " model.zero_grad()\n", - "\n", - " # perform backward pass\n", - " loss_value.backward()\n", - "\n", - " # update parameters\n", - " model.update_parameters(learning_rate) \n", - " \n", - " # examine the loss after each epoch\n", - " print(\"Epoch: {0:d} Loss: {1:.4f}\".format(i, loss_value.get_plain_text()))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### A Complete Example\n", - "\n", - "We now put these pieces together for a complete example of training a network in a multi-party setting. \n", - "\n", - "As in Tutorial 3, we'll assume Alice has the rank 0 process, and Bob has the rank 1 process; so we'll load and encrypt Alice's data with `src=0`, and load and encrypt Bob's data with `src=1`. We'll then initialize a plaintext model and convert it to an encrypted model, just as we did in Tutorial 4. We'll finally define our loss function, training parameters, and run SGD on the encrypted data. For the purposes of this tutorial we train on 100 samples; training should complete in ~3 minutes per epoch." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import crypten.mpc as mpc\n", - "import crypten.communicator as comm\n", - "\n", - "# Convert labels to one-hot encoding\n", - "# Since labels are public in this use case, we will simply use them from loaded torch tensors\n", - "labels = torch.load('/tmp/train_labels.pth')\n", - "labels = labels.long()\n", - "labels_one_hot = label_eye[labels]\n", - "\n", - "@mpc.run_multiprocess(world_size=2)\n", - "def run_encrypted_training():\n", - " # Load data:\n", - " x_alice_enc = crypten.load_from_party('/tmp/alice_train.pth', src=ALICE)\n", - " x_bob_enc = crypten.load_from_party('/tmp/bob_train.pth', src=BOB)\n", - " \n", - " # Combine the feature sets: identical to Tutorial 3\n", - " x_combined_enc = crypten.cat([x_alice_enc, x_bob_enc], dim=2)\n", - " \n", - " # Reshape to match the network architecture\n", - " x_combined_enc = x_combined_enc.unsqueeze(1)\n", - " \n", - " # Initialize a plaintext model and convert to CrypTen model\n", - " model = crypten.nn.from_pytorch(ExampleNet(), dummy_input)\n", - " model.encrypt()\n", - " \n", - " # Set train mode\n", - " model.train()\n", - " \n", - " # Define a loss function\n", - " loss = crypten.nn.MSELoss()\n", - "\n", - " # Define training parameters\n", - " learning_rate = 0.001\n", - " num_epochs = 2\n", - " batch_size = 10\n", - " num_batches = x_combined_enc.size(0) // batch_size\n", - " \n", - " rank = comm.get().get_rank()\n", - " for i in range(num_epochs): \n", - " # Print once for readability\n", - " if rank == 0:\n", - " print(f\"Epoch {i} in progress:\") \n", - " \n", - " for batch in range(num_batches):\n", - " # define the start and end of the training mini-batch\n", - " start, end = batch * batch_size, (batch + 1) * batch_size\n", - " \n", - " # construct CrypTensors out of training examples / labels\n", - " x_train = x_combined_enc[start:end]\n", - " y_batch = labels_one_hot[start:end]\n", - " y_train = crypten.cryptensor(y_batch, requires_grad=True)\n", - " \n", - " # perform forward pass:\n", - " output = model(x_train)\n", - " loss_value = loss(output, y_train)\n", - " \n", - " # set gradients to \"zero\" \n", - " model.zero_grad()\n", - "\n", - " # perform backward pass: \n", - " loss_value.backward()\n", - "\n", - " # update parameters\n", - " model.update_parameters(learning_rate)\n", - " \n", - " # Print progress every batch:\n", - " batch_loss = loss_value.get_plain_text()\n", - " if rank == 0:\n", - " print(f\"\\tBatch {(batch + 1)} of {num_batches} Loss {batch_loss.item():.4f}\")\n", - "\n", - "run_encrypted_training()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We see that the average batch loss decreases across the epochs, as we expect during training.\n", - "\n", - "This completes our tutorial. Before exiting this tutorial, please clean up the files generated using the following code." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "\n", - "filenames = ['/tmp/alice_train.pth', \n", - " '/tmp/bob_train.pth', \n", - " '/tmp/alice_test.pth',\n", - " '/tmp/bob_test.pth', \n", - " '/tmp/train_labels.pth',\n", - " '/tmp/test_labels.pth']\n", - "\n", - "for fn in filenames:\n", - " if os.path.exists(fn): os.remove(fn)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -}