diff --git "a/examples/tutorials/translations/portugu\303\252s/Parte 06 - Aprendizado Federado com MNIST usando uma CNN.ipynb" "b/examples/tutorials/translations/portugu\303\252s/Parte 06 - Aprendizado Federado com MNIST usando uma CNN.ipynb" index 38f6b27e34e..c37b5f112e8 100644 --- "a/examples/tutorials/translations/portugu\303\252s/Parte 06 - Aprendizado Federado com MNIST usando uma CNN.ipynb" +++ "b/examples/tutorials/translations/portugu\303\252s/Parte 06 - Aprendizado Federado com MNIST usando uma CNN.ipynb" @@ -300,8 +300,6 @@ "\n", "Obviamente, existem dezenas de melhorias em que poderíamos pensar. Gostaríamos que o cálculo operasse em paralelo com os *workers* e realizasse a média federada (i.e. federated averaging), atualizando o modelo central a cada `n` *batches* apenas, para reduzir o número de mensagens que usamos para nos comunicarmos entre os *workers*, etc. Essas são características em que estamos trabalhando para tornar o Federated Learning pronto para um ambiente de produção e escreverêmos sobre assim que forem lançadas!\n", "\n", - "You should now be able to do Federated Learning by yourself! If you enjoyed this and would like to join the movement toward privacy preserving, decentralized ownership of AI and the AI supply chain (data), you can do so in the following ways! \n", - "\n", "Agora você deve poder fazer Aprendizado Federado sozinho! Se você gostou disso e gostaria de se juntar ao movimento em direção à preservação da privacidade, propriedade descentralizada da IA e da cadeia de suprimentos da AI (dados), você pode fazê-lo das seguintes maneiras!\n", "\n", "\n", diff --git "a/examples/tutorials/translations/portugu\303\252s/Parte 09 - Introdu\303\247\303\243o \303\240 Programas Criptografados.ipynb" "b/examples/tutorials/translations/portugu\303\252s/Parte 09 - Introdu\303\247\303\243o \303\240 Programas Criptografados.ipynb" new file mode 100644 index 00000000000..d2a295a4201 --- /dev/null +++ "b/examples/tutorials/translations/portugu\303\252s/Parte 09 - Introdu\303\247\303\243o \303\240 Programas Criptografados.ipynb" @@ -0,0 +1,857 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Parte 09 - Introdução à Programas Criptografados\n", + "\n", + "\n", + "Acredite ou não, é possível realizar cálculos com dados criptografados. Em outras palavras, é possível executar um programa onde **TODAS as variáveis** estão **criptografadas**! \n", + "\n", + "Neste tutorial, nós vamos falar sobre as ferramentas básicas de computação criptografada. Em particular, nós vamos focar em uma abordagem popular chamada Computação Multiparte Segura (i.e. **Secure Multi-Party Computation ou SMPC**). Nesta lição, você aprenderá como criar uma calculadora criptografada que pode executar cálculos com números criptografados.\n", + "\n", + "Autores:\n", + "- Andrew Trask - Twitter: [@iamtrask](https://twitter.com/iamtrask)\n", + "- Théo Ryffel - GitHub: [@LaRiffle](https://github.com/LaRiffle)\n", + "\n", + "Tradução:\n", + "- Jeferson Silva - Github: [@jefersonf](https://github.com/jefersonf)\n", + "\n", + "Referências: \n", + "- Morten Dahl - [Blog](https://mortendahl.github.io) - Twitter: [@mortendahlcs](https://twitter.com/mortendahlcs)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Passo 1: Criptografia Usando Computação Multiparte Segura\n", + "\n", + "SMPC é, à primeira vista, uma forma bastante estranha de \"criptografia\". Em vez de usar uma chave pública/privada para criptografar uma variável, cada valor é dividido em várias `partes`, cada uma das quais opera como uma chave privada. Normalmente, essas `partes` serão distribuídas entre 2 ou mais proprietários. Assim, para decifrar a variável, todos os proprietários devem concordar em permitir a sua descriptografia. Em essência, todos têm uma chave privada.\n", + "\n", + "### Encrypt()\n", + "\n", + "Então, digamos que queríamos \"encriptar\" uma variável `x`, podíamos fazê-lo da seguinte forma.\n", + "\n", + "> A criptografia não utiliza números reais ou de ponto flutuante mas um espaço matemático chamado de [integer quotient ring](http://mathworld.wolfram.com/QuotientRing.html) (anel quociente inteiro) que são basicamente os inteiros entre `0` e `Q-1`, onde `Q` é primo e \"grande o suficiente\" tal que o espaço contenha todos os números que nós usamos em nossos experimentos. Na prática, dado um valor `x` inteiro, fazemos `x % Q` (resto da divisão inteira) para que esteja contido no \"anel\". (É por isso que evitamos usar o número `x` > `Q`)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "Q = 1234567891011" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "x = 25" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "\n", + "def encrypt(x):\n", + " share_a = random.randint(-Q,Q) # Entenda share_a como parte A de x, idem para B, e C.\n", + " share_b = random.randint(-Q,Q)\n", + " share_c = (x - share_a - share_b) % Q\n", + " return (share_a, share_b, share_c)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(890804432397, -2305631655, 346069090294)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "encrypt(x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Como você pode ver aqui, nós dividimos nossa variável `x` em 3 partes diferentes, que poderiam ser enviadas para 3 diferentes proprietários de dados.\n", + "\n", + "### Decrypt()\n", + "\n", + "Se quiséssemos decifrar essas três partes, poderíamos simplesmente juntá-las e pegar o módulo do resultado (mod Q)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def decrypt(*shares):\n", + " return sum(shares) % Q" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "a,b,c = encrypt(25)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "25" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "decrypt(a, b, c)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "É importante notar que se tentarmos decifrar com apenas duas partes, a descriptografia não funciona!" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "778460474681" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "decrypt(a, b)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Assim, precisamos que todos os proprietários participem para decifrar o valor. É desta forma que as partes agem como chaves privadas, todas as quais devem estar presentes para decifrar um valor." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Passo 2: Aritmética Básica Usando SMPC\n", + "\n", + "No entanto, a propriedade verdadeiramente extraordinária da Computação Multiparte Segura é a capacidade de realizar cálculos **enquanto as variáveis ainda estão criptografadas**. Vamos demonstrar uma simples adição abaixo." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "x = encrypt(25)\n", + "y = encrypt(5)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "def add(x, y):\n", + " z = list()\n", + " # o primeiro worker adiciona as suas partes\n", + " z.append((x[0] + y[0]) % Q)\n", + " \n", + " # o segundo worker adiciona as suas partes\n", + " z.append((x[1] + y[1]) % Q)\n", + " \n", + " # o terceiro worker adiciona as suas partes\n", + " z.append((x[2] + y[2]) % Q)\n", + " \n", + " return z" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "30" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "decrypt(*add(x,y))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sucesso!!!\n", + "\n", + "E aqui o temos! Se cada _worker_ (separadamente) somar suas partes, então as partes resultantes serão decifradas para o valor correto (25 + 5 === 30). \n", + "\n", + "Acontece que, existem protocolos SMPC que podem permitir este cálculo criptografado para as seguintes operações:\n", + "- Adição (que acabamos de ver)\n", + "- Multiplicação\n", + "- Comparação\n", + "\n", + "\n", + "e usando estas três operações básicas, podemos fazer cálculos arbitrários!!!\n", + "\n", + "Na próxima seção, vamos aprender como usar a biblioteca PySyft para realizar estas operações!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Passo 3: SMPC Usando PySyft\n", + "\n", + "Nas seções anteriores, nós demostramos algumas intuições básicas em torno do SMPC e como o método deve funcionar. Contudo, na prática, não queremos ter de ser nós próprios a escrever à mão todas as operações primitivas ao escrever os nossos programas criptografados. Então, nesta seção vamos passar pelo básico de como fazer computação criptografada usando o PySyft. Em particular, vamos focar em como fazer as 3 operações básicas mencionados anteriormente: adição, multiplicação, e comparação.\n", + "\n", + "Primeiro, precisamos criar alguns _Workers Virtuais_ (esperamos que você já esteja familiarizado com os nossos tutoriais anteriores). " + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import syft as sy\n", + "hook = sy.TorchHook(torch)\n", + "\n", + "bob = sy.VirtualWorker(hook, id=\"bob\")\n", + "alice = sy.VirtualWorker(hook, id=\"alice\")\n", + "bill = sy.VirtualWorker(hook, id=\"bill\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Criptografia e Descriptografia Básica\n", + "\n", + "Criptografar é tão simples como tomar qualquer tensor PySyft e chamar `.share()`. Descriptografar é tão simples como chamar `.get()` na variável compartilhada." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "x = torch.tensor([25])" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([25])" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "encrypted_x = x.share(bob, alice, bill)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([25])" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "encrypted_x.get()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Considerações sobre os Valores Criptografados\n", + "\n", + "Se olharmos mais de perto para os _workers_ Alice, Bob e Bill, podemos ver as partes que são criadas em cada um deles!" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{}" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bob._objects" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "x = torch.tensor([25]).share(bob, alice, bill)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([3212861001891376707])" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Parte de Bob\n", + "bobs_share = list(bob._objects.values())[0]\n", + "bobs_share" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([61371170032936135])" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Parte de Alice\n", + "alices_share = list(alice._objects.values())[0]\n", + "alices_share" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([1337453846503075087])" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Parte de Bill\n", + "bills_share = list(bill._objects.values())[0]\n", + "bills_share" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "E se quiséssemos, poderíamos decifrar esses valores usando a mesma abordagem de que falamos anteriormente!!!" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([25])" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Q = x.child.field\n", + "\n", + "(bobs_share + alices_share + bills_share) % Q" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see, when we called `.share()` it simply split the value into 3 shares and sent one share to each of the parties!\n", + "\n", + "Como você pode ver, quando chamamos `.share()` simplesmente dividimos o valor em 3 partes e o enviamos para cada um dos _workers_ !" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Aritmética Criptografada\n", + "\n", + "E agora você vê que nós podemos fazer aritmética sobre os valores subjacentes! A API é construída para que possamos simplesmente executar a aritmética como faríamos com os tensores PyTorch normais." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "x = torch.tensor([25]).share(bob,alice)\n", + "y = torch.tensor([5]).share(bob,alice)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([30])" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "z = x + y\n", + "z.get()" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([20])" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "z = x - y\n", + "z.get()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Multiplicação Criptografada\n", + "\n", + "Para a multiplicação, precisamos de uma parte adicional que seja responsável pela geração consistente de números aleatórios (e que não colida com nenhuma das outras partes). Nós chamamos essa terceira parte de \"provedor de criptografia\". Para todos os propósitos, o provedor de criptografia é apenas um \"VirtualWorker\" adicional, mas é importante reconhecer que o provedor de criptografia não é um \"dono/proprietário\", pois ele não possui partes, mas é alguém que precisa ser confiável para não entrar em conluio com nenhuma das partes existentes." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "crypto_provider = sy.VirtualWorker(hook, id=\"crypto_provider\")" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [], + "source": [ + "x = torch.tensor([25]).share(bob,alice, crypto_provider=crypto_provider)\n", + "y = torch.tensor([5]).share(bob,alice, crypto_provider=crypto_provider)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([125])" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# multiplicação\n", + "\n", + "z = x * y\n", + "z.get()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Você também pode fazer multiplicação matricial" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "x = torch.tensor([[1, 2],[3,4]]).share(bob,alice, crypto_provider=crypto_provider)\n", + "y = torch.tensor([[2, 0],[0,2]]).share(bob,alice, crypto_provider=crypto_provider)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([[2, 4],\n", + " [6, 8]])" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# multiplicação de matrizes\n", + "\n", + "z = x.mm(y)\n", + "z.get()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Comparação Criptografada\n", + "\n", + "Também é possível fazer comparações privadas entre valores privados. Contamos aqui com o protocolo _SecureNN_ , cujos detalhes podem ser encontrados [aqui](https://eprint.iacr.org/2018/442.pdf). O resultado da comparação é também um tensor privado compartilhado." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "x = torch.tensor([25]).share(bob,alice, crypto_provider=crypto_provider)\n", + "y = torch.tensor([5]).share(bob,alice, crypto_provider=crypto_provider)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([1])" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "z = x > y\n", + "z.get()" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([0])" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "z = x <= y\n", + "z.get()" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([0])" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "z = x == y\n", + "z.get()" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([1])" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "z = x == y + 20\n", + "z.get()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Você também pode realizar operações de _maximum._" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([4])" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x = torch.tensor([2, 3, 4, 1]).share(bob,alice, crypto_provider=crypto_provider)\n", + "x.max().get()" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([4, 3])" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x = torch.tensor([[2, 3], [4, 1]]).share(bob,alice, crypto_provider=crypto_provider)\n", + "max_values, max_ids = x.max(dim=0)\n", + "max_values.get()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Parabéns!!! - Hora de se juntar a comunidade!\n", + "\n", + "Parabéns por concluir esta etapa do tutorial! Se você gostou e gostaria de se juntar ao movimento em direção à proteção de privacidade, propriedade descentralizada e geração, demanda em cadeia, de dados em IA, você pode fazê-lo das seguintes maneiras!\n", + "\n", + "### Dê-nos uma estrela em nosso repo do PySyft no GitHub\n", + "\n", + "A maneira mais fácil de ajudar nossa comunidade é adicionando uma estrela nos nossos repositórios! Isso ajuda a aumentar a conscientização sobre essas ferramentas legais que estamos construindo.\n", + "\n", + "- [Star PySyft](https://github.com/OpenMined/PySyft)\n", + "\n", + "### Junte-se ao Slack!\n", + "\n", + "A melhor maneira de manter-se atualizado sobre os últimos avanços é se juntar à nossa comunidade! Você pode fazer isso preenchendo o formulário em [http://slack.openmined.org](http://slack.openmined.org)\n", + "\n", + "### Contribua com o projeto!\n", + "\n", + "A melhor maneira de contribuir para a nossa comunidade é se tornando um contribuidor do código! A qualquer momento, você pode acessar a página de *Issues* (problemas) do PySyft no GitHub e filtrar por \"Projetos\". Isso mostrará todas as etiquetas (tags) na parte superior, com uma visão geral de quais projetos você pode participar! Se você não deseja ingressar em um projeto, mas gostaria de codificar um pouco, também pode procurar mais mini-projetos \"independentes\" pesquisando problemas no GitHub marcados como \"good first issue\".\n", + "\n", + "- [Projetos do PySyft](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n", + "- [Etiquetados como Good First Issue](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", + "\n", + "### Doar\n", + "\n", + "Se você não tem tempo para contribuir com nossa base de códigos, mas ainda deseja nos apoiar, também pode se tornar um Apoiador em nosso Open Collective. Todas as doações vão para hospedagem na web e outras despesas da comunidade, como hackathons e meetups!\n", + "\n", + "[Página do Open Collective do OpenMined](https://opencollective.com/openmined)" + ] + }, + { + "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.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git "a/examples/tutorials/translations/portugu\303\252s/Parte 10 - Aprendizagem Federada com Agrega\303\247\303\243o Segura.ipynb" "b/examples/tutorials/translations/portugu\303\252s/Parte 10 - Aprendizagem Federada com Agrega\303\247\303\243o Segura.ipynb" new file mode 100644 index 00000000000..b879a050e62 --- /dev/null +++ "b/examples/tutorials/translations/portugu\303\252s/Parte 10 - Aprendizagem Federada com Agrega\303\247\303\243o Segura.ipynb" @@ -0,0 +1,580 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Parte 10: Aprendizagem Federada com Agregação de Gradientes Criptografada\n", + "\n", + "Nas últimas seções, temos aprendido sobre computação criptografada através da construção de vários programas simples. Nesta seção, vamos voltar à [demonstração de Aprendizagem Federada da Parte 4](https://github.com/OpenMined/PySyft/blob/master/examples/tutorials/translations/portugu%C3%AAs/Parte%2004%20-%20Aprendizado%20Federado%20por%20meio%20de%20Agregador%20Confi%C3%A1vel.ipynb), onde tivemos um \"agregador de confiança\" que foi responsável pela média das atualizações do modelo de vários _vorkers_.\n", + "\n", + "Vamos agora usar nossas novas ferramentas de computação criptografada para remover esse agregador confiável, pois ele é menos do que ideal, pois pressupõe que podemos encontrar alguém confiável o suficiente para ter acesso a essas informações confidenciais. Este nem sempre é o caso.\n", + "\n", + "Assim, nesta parte do tutorial, mostraremos como se pode usar o SMPC para realizar uma agregação segura, de modo a não precisarmos de um \"agregador de confiança\".\n", + "\n", + "Autores:\n", + "- Theo Ryffel - Twitter: [@theoryffel](https://twitter.com/theoryffel)\n", + "- Andrew Trask - Twitter: [@iamtrask](https://twitter.com/iamtrask)\n", + "\n", + "Tradução:\n", + "- Jeferson Silva - Github: [@jefersonf](https://github.com/jefersonf)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Seção 1: Aprendizagem Federada Usual\n", + "\n", + "Primeiro, aqui está um código que realiza o clássico aprendizado federado no _Boston Housing Dataset_. Esta seção do código é dividida em várias partes.\n", + "\n", + "### Configuração" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pickle\n", + "\n", + "import torch\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "import torch.optim as optim\n", + "from torch.utils.data import TensorDataset, DataLoader\n", + "\n", + "class Parser:\n", + " \"\"\"Parameters for training\"\"\"\n", + " def __init__(self):\n", + " self.epochs = 10\n", + " self.lr = 0.001\n", + " self.test_batch_size = 8\n", + " self.batch_size = 8\n", + " self.log_interval = 10\n", + " self.seed = 1\n", + " \n", + "args = Parser()\n", + "\n", + "torch.manual_seed(args.seed)\n", + "kwargs = {}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Carregando o Conjunto de Dados" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open('../data/BostonHousing/boston_housing.pickle','rb') as f:\n", + " ((X, y), (X_test, y_test)) = pickle.load(f)\n", + "\n", + "X = torch.from_numpy(X).float()\n", + "y = torch.from_numpy(y).float()\n", + "X_test = torch.from_numpy(X_test).float()\n", + "y_test = torch.from_numpy(y_test).float()\n", + "# preprocessing\n", + "mean = X.mean(0, keepdim=True)\n", + "dev = X.std(0, keepdim=True)\n", + "mean[:, 3] = 0. # the feature at column 3 is binary,\n", + "dev[:, 3] = 1. # so we don't standardize it\n", + "X = (X - mean) / dev\n", + "X_test = (X_test - mean) / dev\n", + "train = TensorDataset(X, y)\n", + "test = TensorDataset(X_test, y_test)\n", + "train_loader = DataLoader(train, batch_size=args.batch_size, shuffle=True, **kwargs)\n", + "test_loader = DataLoader(test, batch_size=args.test_batch_size, shuffle=True, **kwargs)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Estrutura da Rede Neural## Neural Network Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Net(nn.Module):\n", + " def __init__(self):\n", + " super(Net, self).__init__()\n", + " self.fc1 = nn.Linear(13, 32)\n", + " self.fc2 = nn.Linear(32, 24)\n", + " self.fc3 = nn.Linear(24, 1)\n", + "\n", + " def forward(self, x):\n", + " x = x.view(-1, 13)\n", + " x = F.relu(self.fc1(x))\n", + " x = F.relu(self.fc2(x))\n", + " x = self.fc3(x)\n", + " return x\n", + "\n", + "model = Net()\n", + "optimizer = optim.SGD(model.parameters(), lr=args.lr)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Criando Gancho do PyTorch" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import syft as sy\n", + "\n", + "hook = sy.TorchHook(torch)\n", + "bob = sy.VirtualWorker(hook, id=\"bob\")\n", + "alice = sy.VirtualWorker(hook, id=\"alice\")\n", + "james = sy.VirtualWorker(hook, id=\"james\")\n", + "\n", + "compute_nodes = [bob, alice]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Envie dados para os workers**
\n", + "Normalmente eles já o teriam, isto é apenas para fins de demonstração que nós o enviamos manualmente." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "train_distributed_dataset = []\n", + "\n", + "for batch_idx, (data,target) in enumerate(train_loader):\n", + " data = data.send(compute_nodes[batch_idx % len(compute_nodes)])\n", + " target = target.send(compute_nodes[batch_idx % len(compute_nodes)])\n", + " train_distributed_dataset.append((data, target))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Função de Treinamento" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def train(epoch):\n", + " model.train()\n", + " for batch_idx, (data,target) in enumerate(train_distributed_dataset):\n", + " worker = data.location\n", + " model.send(worker)\n", + "\n", + " optimizer.zero_grad()\n", + " # update the model\n", + " pred = model(data)\n", + " loss = F.mse_loss(pred.view(-1), target)\n", + " loss.backward()\n", + " optimizer.step()\n", + " model.get()\n", + " \n", + " if batch_idx % args.log_interval == 0:\n", + " loss = loss.get()\n", + " print('Train Epoch: {} [{}/{} ({:.0f}%)]\\tLoss: {:.6f}'.format(\n", + " epoch, batch_idx * data.shape[0], len(train_loader),\n", + " 100. * batch_idx / len(train_loader), loss.item()))\n", + " \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Função de Teste" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def test():\n", + " model.eval()\n", + " test_loss = 0\n", + " for data, target in test_loader:\n", + " output = model(data)\n", + " test_loss += F.mse_loss(output.view(-1), target, reduction='sum').item() # sum up batch loss\n", + " pred = output.data.max(1, keepdim=True)[1] # get the index of the max log-probability\n", + " \n", + " test_loss /= len(test_loader.dataset)\n", + " print('\\nTest set: Average loss: {:.4f}\\n'.format(test_loss))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Treinando o Modelo" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import time" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "t = time.time()\n", + "\n", + "for epoch in range(1, args.epochs + 1):\n", + " train(epoch)\n", + "\n", + " \n", + "total_time = time.time() - t\n", + "print('Total', round(total_time, 2), 's')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Calculando a Performance" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "test()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Seção 2: Adicionando Agregação Criptografada\n", + "\n", + "Agora vamos modificar um pouco este exemplo para agregar gradientes usando criptografia. A diferença principal é uma ou duas linhas de código na função `train()`, que nós vamos destacar. Por enquanto, vamos reprocessar nossos dados e inicializar um modelo para bob e alice." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "remote_dataset = (list(),list())\n", + "\n", + "train_distributed_dataset = []\n", + "\n", + "for batch_idx, (data,target) in enumerate(train_loader):\n", + " data = data.send(compute_nodes[batch_idx % len(compute_nodes)])\n", + " target = target.send(compute_nodes[batch_idx % len(compute_nodes)])\n", + " remote_dataset[batch_idx % len(compute_nodes)].append((data, target))\n", + "\n", + "def update(data, target, model, optimizer):\n", + " model.send(data.location)\n", + " optimizer.zero_grad()\n", + " pred = model(data)\n", + " loss = F.mse_loss(pred.view(-1), target)\n", + " loss.backward()\n", + " optimizer.step()\n", + " return model\n", + "\n", + "bobs_model = Net()\n", + "alices_model = Net()\n", + "\n", + "bobs_optimizer = optim.SGD(bobs_model.parameters(), lr=args.lr)\n", + "alices_optimizer = optim.SGD(alices_model.parameters(), lr=args.lr)\n", + "\n", + "models = [bobs_model, alices_model]\n", + "params = [list(bobs_model.parameters()), list(alices_model.parameters())]\n", + "optimizers = [bobs_optimizer, alices_optimizer]\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Construindo nossa Lógica de Treinamento\n", + "\n", + "A única diferença **real** está dentro deste método de treino. Vamos vê-lo passo-a-passo.\n", + "\n", + "### Parte A: Treino:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# this is selecting which batch to train on\n", + "data_index = 0\n", + "# update remote models\n", + "# we could iterate this multiple times before proceeding, but we're only iterating once per worker here\n", + "for remote_index in range(len(compute_nodes)):\n", + " data, target = remote_dataset[remote_index][data_index]\n", + " models[remote_index] = update(data, target, models[remote_index], optimizers[remote_index])\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Parte B: Agregação Criptografada" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# create a list where we'll deposit our encrypted model average\n", + "new_params = list()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# iterate through each parameter\n", + "for param_i in range(len(params[0])):\n", + "\n", + " # for each worker\n", + " spdz_params = list()\n", + " for remote_index in range(len(compute_nodes)):\n", + " \n", + " # select the identical parameter from each worker and copy it\n", + " copy_of_parameter = params[remote_index][param_i].copy()\n", + " \n", + " # since SMPC can only work with integers (not floats), we need\n", + " # to use Integers to store decimal information. In other words,\n", + " # we need to use \"Fixed Precision\" encoding.\n", + " fixed_precision_param = copy_of_parameter.fix_precision()\n", + " \n", + " # now we encrypt it on the remote machine. Note that \n", + " # fixed_precision_param is ALREADY a pointer. Thus, when\n", + " # we call share, it actually encrypts the data that the\n", + " # data is pointing TO. This returns a POINTER to the \n", + " # MPC secret shared object, which we need to fetch.\n", + " encrypted_param = fixed_precision_param.share(bob, alice, crypto_provider=james)\n", + " \n", + " # now we fetch the pointer to the MPC shared value\n", + " param = encrypted_param.get()\n", + " \n", + " # save the parameter so we can average it with the same parameter\n", + " # from the other workers\n", + " spdz_params.append(param)\n", + "\n", + " # average params from multiple workers, fetch them to the local machine\n", + " # decrypt and decode (from fixed precision) back into a floating point number\n", + " new_param = (spdz_params[0] + spdz_params[1]).get().float_precision()/2\n", + " \n", + " # save the new averaged parameter\n", + " new_params.append(new_param)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Parte C: Limpeza" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with torch.no_grad():\n", + " for model in params:\n", + " for param in model:\n", + " param *= 0\n", + "\n", + " for model in models:\n", + " model.get()\n", + "\n", + " for remote_index in range(len(compute_nodes)):\n", + " for param_index in range(len(params[remote_index])):\n", + " params[remote_index][param_index].set_(new_params[param_index])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Vamos Juntar Tudo!!\n", + "\n", + "E agora que conhecemos cada etapa, nós podemos colocar todas juntas em mesmo ciclo de treinamento." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def train(epoch):\n", + " for data_index in range(len(remote_dataset[0])-1):\n", + " # update remote models\n", + " for remote_index in range(len(compute_nodes)):\n", + " data, target = remote_dataset[remote_index][data_index]\n", + " models[remote_index] = update(data, target, models[remote_index], optimizers[remote_index])\n", + "\n", + " # encrypted aggregation\n", + " new_params = list()\n", + " for param_i in range(len(params[0])):\n", + " spdz_params = list()\n", + " for remote_index in range(len(compute_nodes)):\n", + " spdz_params.append(params[remote_index][param_i].copy().fix_precision().share(bob, alice, crypto_provider=james).get())\n", + "\n", + " new_param = (spdz_params[0] + spdz_params[1]).get().float_precision()/2\n", + " new_params.append(new_param)\n", + "\n", + " # cleanup\n", + " with torch.no_grad():\n", + " for model in params:\n", + " for param in model:\n", + " param *= 0\n", + "\n", + " for model in models:\n", + " model.get()\n", + "\n", + " for remote_index in range(len(compute_nodes)):\n", + " for param_index in range(len(params[remote_index])):\n", + " params[remote_index][param_index].set_(new_params[param_index])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def test():\n", + " models[0].eval()\n", + " test_loss = 0\n", + " for data, target in test_loader:\n", + " output = models[0](data)\n", + " test_loss += F.mse_loss(output.view(-1), target, reduction='sum').item() # sum up batch loss\n", + " pred = output.data.max(1, keepdim=True)[1] # get the index of the max log-probability\n", + " \n", + " test_loss /= len(test_loader.dataset)\n", + " print('Test set: Average loss: {:.4f}\\n'.format(test_loss))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "t = time.time()\n", + "\n", + "for epoch in range(args.epochs):\n", + " print(f\"Epoch {epoch + 1}\")\n", + " train(epoch)\n", + " test()\n", + "\n", + " \n", + "total_time = time.time() - t\n", + "print('Total', round(total_time, 2), 's')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Parabéns!!! - Hora de se juntar a comunidade!\n", + "\n", + "Parabéns por concluir esta etapa do tutorial! Se você gostou e gostaria de se juntar ao movimento em direção à proteção de privacidade, propriedade descentralizada e geração, demanda em cadeia, de dados em IA, você pode fazê-lo das seguintes maneiras!\n", + "\n", + "### Dê-nos uma estrela em nosso repo do PySyft no GitHub\n", + "\n", + "A maneira mais fácil de ajudar nossa comunidade é adicionando uma estrela nos nossos repositórios! Isso ajuda a aumentar a conscientização sobre essas ferramentas legais que estamos construindo.\n", + "\n", + "- [Star PySyft](https://github.com/OpenMined/PySyft)\n", + "\n", + "### Junte-se ao Slack!\n", + "\n", + "A melhor maneira de manter-se atualizado sobre os últimos avanços é se juntar à nossa comunidade! Você pode fazer isso preenchendo o formulário em [http://slack.openmined.org](http://slack.openmined.org)\n", + "\n", + "### Contribua com o projeto!\n", + "\n", + "A melhor maneira de contribuir para a nossa comunidade é se tornando um contribuidor do código! A qualquer momento, você pode acessar a página de *Issues* (problemas) do PySyft no GitHub e filtrar por \"Projetos\". Isso mostrará todas as etiquetas (tags) na parte superior, com uma visão geral de quais projetos você pode participar! Se você não deseja ingressar em um projeto, mas gostaria de codificar um pouco, também pode procurar mais mini-projetos \"independentes\" pesquisando problemas no GitHub marcados como \"good first issue\".\n", + "\n", + "- [Projetos do PySyft](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n", + "- [Etiquetados como Good First Issue](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", + "\n", + "### Doar\n", + "\n", + "Se você não tem tempo para contribuir com nossa base de códigos, mas ainda deseja nos apoiar, também pode se tornar um Apoiador em nosso Open Collective. Todas as doações vão para hospedagem na web e outras despesas da comunidade, como hackathons e meetups!\n", + "\n", + "[Página do Open Collective do OpenMined](https://opencollective.com/openmined)" + ] + }, + { + "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.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git "a/examples/tutorials/translations/portugu\303\252s/Parte 11 - Classifica\303\247\303\243o Segura Usando Aprendizagem Profunda.ipynb" "b/examples/tutorials/translations/portugu\303\252s/Parte 11 - Classifica\303\247\303\243o Segura Usando Aprendizagem Profunda.ipynb" new file mode 100644 index 00000000000..8830b00ecba --- /dev/null +++ "b/examples/tutorials/translations/portugu\303\252s/Parte 11 - Classifica\303\247\303\243o Segura Usando Aprendizagem Profunda.ipynb" @@ -0,0 +1,441 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "parameters" + ] + }, + "outputs": [], + "source": [ + "epochs = 10\n", + "n_test_batches = 200" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Parte 11 - Classificação Segura Usando Aprendizagem Profunda\n", + "\n", + "\n", + "## Os seus dados são importantes, o seu modelo também.\n", + "\n", + "Os dados são o carro-chefe por trás da Aprendizagem de Máquina (Machine Learning). As organizações que criam e coletam dados são capazes de construir e treinar seus próprios modelos de aprendizagem de máquina. Isto permite-lhes oferecer o uso de tais modelos como um serviço (MLaaS) a organizações externas. Isto é útil para organizações que podem não ser capazes de criar esses modelos por si próprias, mas que ainda gostariam de usar esses modelos para fazer predições sobre os seus próprios dados.\n", + "\n", + "No entanto, um modelo hospedado na nuvem ainda apresenta um problema de privacidade/IP. Para que organizações externas possam usá-lo - elas devem carregar seus dados de entrada (como imagens a serem classificadas) ou fazer o _download_ do modelo. O _upload_ dos dados de entrada pode ser problemático do ponto de vista da privacidade, mas o _download_ do modelo pode não ser uma opção se a organização que criou/possui o modelo estiver preocupada em preservar seu IP.\n", + "\n", + "\n", + "\n", + "## Computação sobre dados criptografados\n", + "\n", + "Neste contexto, uma possível solução seria criptografar tanto o modelo como os dados de uma forma que permita a uma organização utilizar um modelo de outra organização sem revelar o seu IP. Existem várias estratégias de criptografia que permitem o cálculo sobre dados criptografados, entre os quais _Secure Multi-Party Computation_ (SMPC), criptografia Homomórfica (FHE/SHE) e criptografia Funcional (FE) são os tipos mais conhecidos. Vamos focar aqui na Computação Multiparte Segura ([introduzida em detalhe aqui no tutorial 5](https://github.com/OpenMined/PySyft/blob/master/examples/tutorials/translations/portugu%C3%AAs/Parte%2005%20-%20Bem-vindo%20ao%20Sandbox.ipynb)) que consiste na soma de partes compartilhadas de forma privada. E que se baseia em protocolos de criptografia como o SecureNN e SPDZ, cujos detalhes são dados [nesta excelente publicação de de blog](https://mortendahl.github.io/2017/09/19/private-image-analysis-with-mpc/). \n", + "\n", + "Estes protocolos alcançam desempenhos notáveis sobre dados criptografados, e nos últimos meses temos trabalhado para tornar estes protocolos fáceis de usar. Em específico, estamos construindo ferramentas para permitir que você use esses protocolos sem ter que reimplementá-los novamente (ou se quer conhecer a criptografia por trás de seu funcionamento). Vamos direto ao assunto.\n", + "\n", + "## Cenário\n", + "\n", + "Neste tutorial considere o seguinte cenário: considere que você é o servidor e que você tem alguns dados. Primeiro, você define e treina um modelo com esses dados de treinamento privado. Depois, você entra em contato com um cliente que possui alguns dos seus próprios dados e que gostaria de acessar o seu modelo para fazer algumas previsões/predições. \n", + "\n", + "Você criptografa o seu modelo (uma rede neural). O cliente criptografa os seus dados. Ambos usam esses dois recursos criptografados para usar o modelo para classificar os dados. Finalmente, o resultado da predição é enviado de volta para o cliente de forma criptografada para que o servidor (ou seja, você) não aprenda nada sobre os dados do cliente (ou seja, você não aprende nem as entradas nem as predições).\n", + "\n", + "O ideal seria que, de maneira incremental, compartilhássemos as `entradas dos clientes` entre eles próprios e o `servidor` e vice-versa para o modelo. Por uma questão de simplicidade, as entradas (i.e dados de treino) serão mantidas por outros dois _workers_ , `alice` e `bob`. Se você considerar que alice é de propriedade do cliente e bob do servidor, é completamente equivalente.\n", + "\n", + "O cálculo é seguro no modelo HBC (i.e. Honest-But-Curious adversary), que é padrão em [muitas ferramentas MPC](https://arxiv.org/pdf/1801.03239.pdf) (Multi-Party Computation).\n", + "\n", + "**Agora temos tudo o que precisamos!**\n", + "\n", + "\n", + "Autor:\n", + "- Théo Ryffel - Twitter: [@theoryffel](https://twitter.com/theoryffel) · GitHub: [@LaRiffle](https://github.com/LaRiffle)\n", + "\n", + "Tradução:\n", + "- Jeferson Silva - Github: [@jefersonf](https://github.com/jefersonf)\n", + "\n", + "**Vamos começar!**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Imports e especificações do modelo" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "import torch.optim as optim\n", + "from torchvision import datasets, transforms" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " Nós também precisamos executar comandos específicos para importar/iniciar o PySyft. Nós criamos alguns _workers_ (chamados `client`, `bob` e `alice`). Por fim, nós definimos o provedor de criptografia (`cripto_provider`) que nos dá todos as ferramentas básicas de criptografia que podemos precisar ([Veja nosso tutorial sobre SMPC para mais detalhes](https://github.com/OpenMined/PySyft/blob/master/examples/tutorials/Part%2009%20-%20Intro%20to%20Encrypted%20Programs.ipynb))." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import syft as sy\n", + "hook = sy.TorchHook(torch) \n", + "client = sy.VirtualWorker(hook, id=\"client\")\n", + "bob = sy.VirtualWorker(hook, id=\"bob\")\n", + "alice = sy.VirtualWorker(hook, id=\"alice\")\n", + "crypto_provider = sy.VirtualWorker(hook, id=\"crypto_provider\") " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Nós definimos o cenário da tarefa de aprendizagem" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Arguments():\n", + " def __init__(self):\n", + " self.batch_size = 64\n", + " self.test_batch_size = 50\n", + " self.epochs = epochs\n", + " self.lr = 0.001\n", + " self.log_interval = 100\n", + "\n", + "args = Arguments()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Carregamento e envio de dados aos Workers\n", + "\n", + "Na nosso cenário, assumimos que o servidor tem acesso a alguns dados de treino para que possa treinar seu modelo. Aqui temos o conjunto de treinamento do MNIST." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "train_loader = torch.utils.data.DataLoader(\n", + " datasets.MNIST('../data', train=True, download=True,\n", + " transform=transforms.Compose([\n", + " transforms.ToTensor(),\n", + " transforms.Normalize((0.1307,), (0.3081,))\n", + " ])),\n", + " batch_size=args.batch_size, shuffle=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Em segundo lugar, o cliente tem alguns dados e gostaria de ter predições sobre eles usando o modelo do servidor. Este cliente criptografa seus dados, compartilhando-os entre os dois _workers_ , `alice` e `bob`.\n", + "> SMPC usa protocolos de criptografia que requerem o uso de inteiros. Nós aproveitamos aqui a abstração do tensor PySyft para converter os tensores de ponto flutuante do PyTorch em tensores de precisão fixa utilizando `.fix_precision()`. Por exemplo 0.123 com precisão 2 faz um arredondamento com o segundo dígito decimal para que o número armazenado seja o número inteiro 12.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "test_loader = torch.utils.data.DataLoader(\n", + " datasets.MNIST('../data', train=False,\n", + " transform=transforms.Compose([\n", + " transforms.ToTensor(),\n", + " transforms.Normalize((0.1307,), (0.3081,))\n", + " ])),\n", + " batch_size=args.test_batch_size, shuffle=True)\n", + "\n", + "private_test_loader = []\n", + "for data, target in test_loader:\n", + " private_test_loader.append((\n", + " data.fix_precision().share(alice, bob, crypto_provider=crypto_provider),\n", + " target.fix_precision().share(alice, bob, crypto_provider=crypto_provider)\n", + " ))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Especificação de uma Rede Neural (Feed Forward Neural Network)\n", + "\n", + "Aqui está a especificação da rede utilizada pelo servidor" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Net(nn.Module):\n", + " def __init__(self):\n", + " super(Net, self).__init__()\n", + " self.fc1 = nn.Linear(784, 500)\n", + " self.fc2 = nn.Linear(500, 10)\n", + "\n", + " def forward(self, x):\n", + " x = x.view(-1, 784)\n", + " x = self.fc1(x)\n", + " x = F.relu(x)\n", + " x = self.fc2(x)\n", + " return x" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Iniciar o treinamento\n", + "O treinamento é feito localmente, então este é um treinamento no PyTorch inteiramente local, nada de especial aqui!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def train(args, model, train_loader, optimizer, epoch):\n", + " model.train()\n", + " for batch_idx, (data, target) in enumerate(train_loader):\n", + " optimizer.zero_grad()\n", + " output = model(data)\n", + " output = F.log_softmax(output, dim=1)\n", + " loss = F.nll_loss(output, target)\n", + " loss.backward()\n", + " optimizer.step()\n", + " if batch_idx % args.log_interval == 0:\n", + " print('Train Epoch: {} [{}/{} ({:.0f}%)]\\tLoss: {:.6f}'.format(\n", + " epoch, batch_idx * args.batch_size, len(train_loader) * args.batch_size,\n", + " 100. * batch_idx / len(train_loader), loss.item()))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model = Net()\n", + "optimizer = torch.optim.Adam(model.parameters(), lr=args.lr)\n", + "\n", + "for epoch in range(1, args.epochs + 1):\n", + " train(args, model, train_loader, optimizer, epoch)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def test(args, model, test_loader):\n", + " model.eval()\n", + " test_loss = 0\n", + " correct = 0\n", + " with torch.no_grad():\n", + " for data, target in test_loader:\n", + " output = model(data)\n", + " output = F.log_softmax(output, dim=1)\n", + " test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss\n", + " pred = output.argmax(1, keepdim=True) # get the index of the max log-probability \n", + " correct += pred.eq(target.view_as(pred)).sum().item()\n", + "\n", + " test_loss /= len(test_loader.dataset)\n", + "\n", + " print('\\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\\n'.format(\n", + " test_loss, correct, len(test_loader.dataset),\n", + " 100. * correct / len(test_loader.dataset)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "test(args, model, test_loader)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "O nosso modelo está agora treinado e pronto para ser fornecido como um serviço!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Avaliação segura" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Agora, como servidor, enviamos o modelo para os _workers_ que têm os dados. Como o modelo é informação sensível (você gastou tempo otimizando-o!), você não quer revelar seus pesos então você compartilha o modelo, de forma secreta/criptografada, como nós fizemos com o conjunto de dados anteriormente." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.fix_precision().share(alice, bob, crypto_provider=crypto_provider)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A seguinte função de teste realiza uma avaliação criptografada. Os pesos do modelo, as entradas de dados, a predição e o alvo (target) utilizado para a pontuação são criptografados!\n", + "\n", + "No entanto, a sintaxe é muito semelhante ao teste de modelos usual do PyTorch, não é legal/Fixe?!\n", + "\n", + "A única coisa que deciframos/descriptografamos no lado do servidor é a pontuação final dos nossos 200 batches de itens (`n_test_batches`) para verificar se as previsões eram em média boas." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def test(args, model, test_loader):\n", + " model.eval()\n", + " n_correct_priv = 0\n", + " n_total = 0\n", + " with torch.no_grad():\n", + " for data, target in test_loader[:n_test_batches]:\n", + " output = model(data)\n", + " pred = output.argmax(dim=1) \n", + " n_correct_priv += pred.eq(target.view_as(pred)).sum()\n", + " n_total += args.test_batch_size\n", + "\n", + " n_correct = n_correct_priv.copy().get().float_precision().long().item()\n", + " \n", + " print('Test set: Accuracy: {}/{} ({:.0f}%)'.format(\n", + " n_correct, n_total,\n", + " 100. * n_correct / n_total))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "test(args, model, private_test_loader)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Et voilà! Aqui está, você aprendeu como fazer previsões/predições seguras de ponta a ponta: os pesos do modelo do servidor não vazaram para o cliente e o servidor não tem informações sobre os dados de entrada nem sobre a saída da classificação!\n", + "\n", + "Quanto ao desempenho, classificar uma imagem leva **menos de 0,1 segundo**, aproximadamente **33ms** no meu computador portátil (2,7 GHz Intel Core i7, 16GB RAM). Entretanto, utilizei uma comunicação muito rápida (todos os _workers_ estão na minha máquina local). O desempenho irá variar dependendo da rapidez com que os diferentes _workers_ podem se comunicar uns com os outros.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Conclusão\n", + "\n", + "Você já viu como é fácil usar o PyTorch e o PySyft para realizar Aprendizagem de Máquina segura (i.e Secure Machine Learning) na prática e proteger os dados dos usuários, sem ter que ser um especialista em criptografia!\n", + "\n", + "Mais informações sobre este tópico virão em breve, incluindo camadas convolucionais para avaliar adequadamente o desempenho do PySyft em relação a outros casos de uso, bem como o treinamento criptografado privado de redes neurais, que é necessário quando uma organização recorre a dados sensíveis externos para treinar seu próprio modelo. Fique atento!\n", + "\n", + "Se você gostou disso e gostaria de participar do movimento em prol da preservação da privacidade, da propriedade descentralizada da IA e da cadeia de fornecimento de IA (dados), você pode fazer isso das seguintes maneiras! \n", + "\n", + "\n", + "### Dê-nos uma estrela em nosso repo do PySyft no GitHub\n", + "\n", + "A maneira mais fácil de ajudar nossa comunidade é adicionando uma estrela nos nossos repositórios! Isso ajuda a aumentar a conscientização sobre essas ferramentas legais que estamos construindo.\n", + "\n", + "- [Star PySyft](https://github.com/OpenMined/PySyft)\n", + "\n", + "### Veja nossos tutoriais no GitHub!\n", + "\n", + "Fizemos tutoriais muito bons para entender melhor como deve ser a Aprendizagem Federada e a proteção de Privacidade, e como estamos construindo as coisas básicas que precisamos para fazer com que isso aconteça.\n", + "\n", + "- [Tutoriais do PySyft](https://github.com/OpenMined/PySyft/tree/master/examples/tutorials)\n", + "\n", + "### Junte-se ao Slack!\n", + "\n", + "A melhor maneira de manter-se atualizado sobre os últimos avanços é se juntar à nossa comunidade! \n", + "\n", + "- [http://slack.openmined.org](http://slack.openmined.org)\n", + "\n", + "### Contribua com o projeto!\n", + "\n", + "A melhor maneira de contribuir para a nossa comunidade é se tornando um contribuidor do código! A qualquer momento, você pode acessar a página de *Issues* (problemas) do PySyft no GitHub e filtrar por \"Projetos\". Isso mostrará todas as etiquetas (tags) na parte superior, com uma visão geral de quais projetos você pode participar! Se você não deseja ingressar em um projeto, mas gostaria de codificar um pouco, também pode procurar mais mini-projetos \"independentes\" pesquisando problemas no GitHub marcados como \"good first issue\".\n", + "\n", + "- [Etiquetados como Good First Issue](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", + "\n", + "### Doar\n", + "\n", + "Se você não tem tempo para contribuir com nossa base de códigos, mas ainda deseja nos apoiar, também pode se tornar um Apoiador em nosso Open Collective. Todas as doações vão para hospedagem na web e outras despesas da comunidade, como hackathons e meetups!\n", + "\n", + "[Página do Open Collective do OpenMined](https://opencollective.com/openmined)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "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.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}