diff --git "a/examples/tutorials/translations/portugu\303\252s/Parte 12 bis - Treinamento criptografado no MNIST.ipynb" "b/examples/tutorials/translations/portugu\303\252s/Parte 12 bis - Treinamento criptografado no MNIST.ipynb" new file mode 100644 index 00000000000..52ceebc05c7 --- /dev/null +++ "b/examples/tutorials/translations/portugu\303\252s/Parte 12 bis - Treinamento criptografado no MNIST.ipynb" @@ -0,0 +1,518 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "parameters" + ] + }, + "outputs": [], + "source": [ + "epochs = 10\n", + "# Não usamos todo o conjunto de dados para fins de eficiência, mas fique à vontade para aumentar esses números\n", + "n_train_items = 640\n", + "n_test_items = 640" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Parte X - Treinamento e Avaliação Seguros no MNIST\n", + "\n", + "Ao criar soluções de Aprendizado de Máquina como Serviço (MLaaS), uma empresa pode precisar solicitar acesso a dados de outros parceiros para treinar seu modelo. Na saúde ou nas finanças, o modelo e os dados são extremamente críticos: os parâmetros do modelo são um ativo de negócios, enquanto os dados são dados pessoais que são rigorosamente regulamentados.\n", + "\n", + "Nesse contexto, uma solução possível é criptografar o modelo e os dados e treinar o modelo de aprendizado de máquina sobre os valores criptografados. Isso garante que a empresa não acesse os registros médicos dos pacientes, por exemplo, e que os estabelecimentos de saúde não possam observar o modelo para o qual contribuem. Existem vários esquemas de criptografia que permitem a computação sobre dados criptografados, entre os quais a Computação Multipartidária Segura (SMPC - Secure Multipart Computation), a Criptografia Homomórfica (FHE / SHE) e a Criptografia Funcional (FE). Vamos nos concentrar aqui na Computação Multipartidária (que foi introduzida no Tutorial 5), que consiste no compartilhamento de aditivos privados e depende dos protocolos de criptografia SecureNN e SPDZ.\n", + "\n", + "A configuração exata deste tutorial é a seguinte: considere que você é o servidor e gostaria de treinar seu modelo em alguns dados mantidos por $n$ workers. O segredo do servidor compartilha seu modelo e envia cada compartilhamento a um worker. Os workers também compartilham seus dados em segredo e os trocam entre eles. Na configuração que iremos estudar, existem 2 workers: alice e bob. Após a troca de ações, cada um deles agora possui uma de suas próprias ações, uma ação do outro worker e uma ação do modelo. A computação agora pode começar a treinar o modelo em particular, usando os protocolos criptográficos apropriados. Depois que o modelo é treinado, todos os compartilhamentos podem ser enviados de volta ao servidor para descriptografá-lo. Isso é ilustrado com a figura a seguir:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![SMPC Illustration](https://github.com/OpenMined/PySyft/raw/11c85a121a1a136e354945686622ab3731246084/examples/tutorials/material/smpc_illustration.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Para dar um exemplo desse processo, vamos assumir que alice e bob mantêm uma parte do conjunto de dados MNIST e vamos treinar um modelo para executar a classificação de dígitos!\n", + "\n", + "Autor:\n", + "- Théo Ryffel - Twitter: [@theoryffel](https://twitter.com/theoryffel) · GitHub: [@LaRiffle](https://github.com/LaRiffle)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 1. Demonstração de treinamento criptografado no MNIST" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Configuração de importações e treinamento" + ] + }, + { + "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\n", + "\n", + "import time" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Esta classe descreve todos os hiperparâmetros para o treinamento. Observe que todos eles são públicos aqui." + ] + }, + { + "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 = 64\n", + " self.epochs = epochs\n", + " self.lr = 0.02\n", + " self.seed = 1\n", + " self.log_interval = 1 # Informações de log em cada lote\n", + " self.precision_fractional = 3\n", + "\n", + "args = Arguments()\n", + "\n", + "_ = torch.manual_seed(args.seed)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Aqui estão as importações do PySyft. Nós nos conectamos a dois workers remotos que são chamados `alice` e `bob` e solicitamos outro worker chamado `crypto_provider`, que fornece todas as primitivas criptográficas que precisamos." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import syft as sy # importe a biblioteca PySyft\n", + "hook = sy.TorchHook(torch) # conecte o PyTorch para adicionar funcionalidades extras, como Federated e Encrypted Learning\n", + "\n", + "# funções de simulação\n", + "def connect_to_workers(n_workers):\n", + " return [\n", + " sy.VirtualWorker(hook, id=f\"worker{i+1}\")\n", + " for i in range(n_workers)\n", + " ]\n", + "def connect_to_crypto_provider():\n", + " return sy.VirtualWorker(hook, id=\"crypto_provider\")\n", + "\n", + "workers = connect_to_workers(n_workers=2)\n", + "crypto_provider = connect_to_crypto_provider()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Obtendo acesso e compartilhamento dados secretos\n", + "\n", + "Aqui, estamos usando uma função de utilitário que simula o seguinte comportamento: assumimos que o conjunto de dados MNIST é distribuído em partes, cada uma das quais mantida por um de nossos workers. Os workers então dividem seus dados em lotes e compartilham seus dados em segredo. O objeto final retornado é iterável nesses lotes secretos compartilhados, que chamamos de **carregador de dados privados**. Observe que durante o processo o trabalhador local (assim nós) nunca teve acesso aos dados.\n", + "\n", + "Obtemos, como de costume, um conjunto de dados privados de treinamento e teste, e as entradas e os rótulos são compartilhados em segredo." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_private_data_loaders(precision_fractional, workers, crypto_provider):\n", + " \n", + " def one_hot_of(index_tensor):\n", + " \"\"\"\n", + " Transformar em one hot tensor\n", + " \n", + " Examplo:\n", + " [0, 3, 9]\n", + " =>\n", + " [[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]]\n", + " \n", + " \"\"\"\n", + " onehot_tensor = torch.zeros(*index_tensor.shape, 10) # 10 classes para o MNIST\n", + " onehot_tensor = onehot_tensor.scatter(1, index_tensor.view(-1, 1), 1)\n", + " return onehot_tensor\n", + " \n", + " def secret_share(tensor):\n", + " \"\"\"\n", + " Transformar em precisão fixa and compartilhar secretamente um tensor\n", + " \"\"\"\n", + " return (\n", + " tensor\n", + " .fix_precision(precision_fractional=precision_fractional)\n", + " .share(*workers, crypto_provider=crypto_provider, requires_grad=True)\n", + " )\n", + " \n", + " transformation = transforms.Compose([\n", + " transforms.ToTensor(),\n", + " transforms.Normalize((0.1307,), (0.3081,))\n", + " ])\n", + " \n", + " train_loader = torch.utils.data.DataLoader(\n", + " datasets.MNIST('../data', train=True, download=True, transform=transformation),\n", + " batch_size=args.batch_size\n", + " )\n", + " \n", + " private_train_loader = [\n", + " (secret_share(data), secret_share(one_hot_of(target)))\n", + " for i, (data, target) in enumerate(train_loader)\n", + " if i < n_train_items / args.batch_size\n", + " ]\n", + " \n", + " test_loader = torch.utils.data.DataLoader(\n", + " datasets.MNIST('../data', train=False, download=True, transform=transformation),\n", + " batch_size=args.test_batch_size\n", + " )\n", + " \n", + " private_test_loader = [\n", + " (secret_share(data), secret_share(target.float()))\n", + " for i, (data, target) in enumerate(test_loader)\n", + " if i < n_test_items / args.test_batch_size\n", + " ]\n", + " \n", + " return private_train_loader, private_test_loader\n", + " \n", + " \n", + "private_train_loader, private_test_loader = get_private_data_loaders(\n", + " precision_fractional=args.precision_fractional,\n", + " workers=workers,\n", + " crypto_provider=crypto_provider\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Especificação do modelo\n", + "\n", + "Aqui está o modelo que usaremos, é bastante simples, mas [provou ter um desempenho razoavelmente bom no MNIST](https://towardsdatascience.com/handwritten-digit-mnist-pytorch-977b5338e627)" + ] + }, + { + "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(28 * 28, 128)\n", + " self.fc2 = nn.Linear(128, 64)\n", + " self.fc3 = nn.Linear(64, 10)\n", + "\n", + " def forward(self, x):\n", + " x = x.view(-1, 28 * 28)\n", + " x = F.relu(self.fc1(x))\n", + " x = F.relu(self.fc2(x))\n", + " x = self.fc3(x)\n", + " return x" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Funções de treinamento e teste\n", + "\n", + "O treinamento é realizado quase como de costume, a diferença real é que não podemos usar funções de custo como negative log-likelihood (`F.nll_loss` no PyTorch) porque é bastante complicado reproduzir essas funções com o SMPC. Em vez disso, usamos uma função mais simples de erro médio quadrático." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def train(args, model, private_train_loader, optimizer, epoch):\n", + " model.train()\n", + " for batch_idx, (data, target) in enumerate(private_train_loader): # <-- agora é um conjunto de dados privado\n", + " start_time = time.time()\n", + " \n", + " optimizer.zero_grad()\n", + " \n", + " output = model(data)\n", + " \n", + " # loss = F.nll_loss(output, target) <-- não é possível aqui\n", + " batch_size = output.shape[0]\n", + " loss = ((output - target)**2).sum().refresh()/batch_size\n", + " \n", + " loss.backward()\n", + " \n", + " optimizer.step()\n", + "\n", + " if batch_idx % args.log_interval == 0:\n", + " loss = loss.get().float_precision()\n", + " print('Train Epoch: {} [{}/{} ({:.0f}%)]\\tLoss: {:.6f}\\tTime: {:.3f}s'.format(\n", + " epoch, batch_idx * args.batch_size, len(private_train_loader) * args.batch_size,\n", + " 100. * batch_idx / len(private_train_loader), loss.item(), time.time() - start_time))\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A função de teste não muda!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def test(args, model, private_test_loader):\n", + " model.eval()\n", + " test_loss = 0\n", + " correct = 0\n", + " with torch.no_grad():\n", + " for data, target in private_test_loader:\n", + " start_time = time.time()\n", + " \n", + " output = model(data)\n", + " pred = output.argmax(dim=1)\n", + " correct += pred.eq(target.view_as(pred)).sum()\n", + "\n", + " correct = correct.get().float_precision()\n", + " print('\\nTest set: Accuracy: {}/{} ({:.0f}%)\\n'.format(\n", + " correct.item(), len(private_test_loader)* args.test_batch_size,\n", + " 100. * correct.item() / (len(private_test_loader) * args.test_batch_size)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Vamos iniciar o treinamento!\n", + "\n", + "Algumas notas sobre o que está acontecendo aqui. Primeiro, compartilhamos secretamente todos os parâmetros do modelo entre nossos funcionários. Segundo, convertemos os hiperparâmetros do otimizador em precisão fixa. Observe que não precisamos compartilhá-los em segredo porque eles são públicos em nosso contexto, mas como os valores compartilhados em segredo vivem em campos finitos, ainda precisamos movê-los em campos finitos usando `.fix_precision`, para executar operações consistentes como a atualização de peso $W \\leftarrow W - \\alpha * \\Delta W$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model = Net()\n", + "model = model.fix_precision().share(*workers, crypto_provider=crypto_provider, requires_grad=True)\n", + "\n", + "optimizer = optim.SGD(model.parameters(), lr=args.lr)\n", + "optimizer = optimizer.fix_precision() \n", + "\n", + "for epoch in range(1, args.epochs + 1):\n", + " train(args, model, private_train_loader, optimizer, epoch)\n", + " test(args, model, private_test_loader)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Aí está você! Você obtém 75% de precisão usando uma pequena fração do conjunto de dados MNIST, usando treinamento 100% criptografado!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 2. Discussão\n", + "\n", + "Vamos dar uma olhada no poder do treinamento criptografado analisando o que acabamos de fazer." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2.1 Tempo de computação\n", + "\n", + "A primeira coisa é obviamente o tempo de execução! Como você certamente notou, é muito mais lento que o treinamento em texto simples. Em particular, uma iteração sobre 1 lote de 64 itens leva 3,2s, enquanto apenas 13ms no PyTorch puro. Embora isso possa parecer um bloqueador, lembre-se de que aqui tudo aconteceu remotamente e no mundo criptografado: nenhum item de dados foi divulgado. Mais especificamente, o tempo para processar um item é de 50 ms, o que não é tão ruim assim. A verdadeira questão é analisar quando o treinamento criptografado é necessário e quando apenas a previsão criptografada é suficiente. 50ms para realizar uma previsão é completamente aceitável em um cenário pronto para produção, por exemplo!\n", + "\n", + "Um dos principais gargalos é o uso de funções de ativação dispendiosas: a ativação da relu com o SMPC é muito dispendiosa porque utiliza comparação privada e o protocolo SecureNN. Como ilustração, se substituirmos relu por uma ativação quadrática, como é feito em vários trabalhos sobre computação criptografada como CryptoNets, passamos de 3,2s para 1,2s.\n", + "\n", + "Como regra geral, a principal idéia a ser retirada é criptografar apenas o necessário, e este tutorial mostra como pode ser simples." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2.2 Backpropagation com SMPC\n", + "\n", + "Você pode se perguntar como realizamos atualizações de retropropagação e gradiente, embora estejamos trabalhando com números inteiros em campos finitos. Para isso, desenvolvemos um novo tensor de eixo chamado AutogradTensor. Este tutorial o utilizou intensivamente, embora você talvez não o tenha visto! Vamos verificar isso imprimindo o peso de um modelo:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.fc3.bias" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "E um item dos dados:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "first_batch, input_data = 0, 0\n", + "private_train_loader[first_batch][input_data]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Como você observa, o AutogradTensor está lá! Ele vive entre o wrapper do torch e o FixedPrecisionTensor, que indica que os valores agora estão em campos finitos. O objetivo deste AutogradTensor é armazenar o gráfico de computação quando operações são feitas com valores criptografados. Isso é útil porque, ao chamar para trás a backpropagation, esse AutogradTensor substitui todas as funções anteriores que não são compatíveis com a computação criptografada e indica como calcular esses gradientes. Por exemplo, em relação à multiplicação que é feita usando o truque Beaver triples, não queremos diferenciar ainda mais o fato de que diferenciar uma multiplicação deve ser muito fácil: $\\partial_b (a \\cdot b) = a \\cdot \\partial b$. Aqui está como descrevemos como calcular esses gradientes, por exemplo:\n", + "\n", + "```python\n", + "class MulBackward(GradFunc):\n", + " def __init__(self, self_, other):\n", + " super().__init__(self, self_, other)\n", + " self.self_ = self_\n", + " self.other = other\n", + "\n", + " def gradient(self, grad):\n", + " grad_self_ = grad * self.other\n", + " grad_other = grad * self.self_ if type(self.self_) == type(self.other) else None\n", + " return (grad_self_, grad_other)\n", + "```\n", + "\n", + "Você pode dar uma olhada em `tensors/interpreters/gradients.py` se estiver curioso para ver como implementamos mais gradientes.\n", + "\n", + "Em termos de grafos de computação, significa que uma cópia do grafo permanece local e que o servidor que coordena a passagem para frente também fornece instruções sobre como fazer a passagem para trás. Essa é uma hipótese completamente válida em nosso cenário." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2.3 Garantias de segurança\n", + "\n", + "\n", + "Por fim, vamos dar algumas dicas sobre a segurança que estamos alcançando aqui: os adversários que estamos considerando aqui são **honestos, mas curiosos**: isso significa que um adversário não pode aprender nada sobre os dados executando este protocolo, mas um adversário mal-intencionado ainda pode se desviar do protocolo e, por exemplo, tentar corromper os compartilhamentos para sabotar a computação. A segurança contra adversários mal-intencionados em tais cálculos SMPC, incluindo comparação privada, ainda é um problema em aberto.\n", + "\n", + "Além disso, mesmo que a Computação Multipartidária Segura (SMPC) garanta que os dados de treinamento não foram acessados, muitas ameaças do mundo do texto sem formatação ainda estão presentes aqui. Por exemplo, como você pode fazer uma solicitação ao modelo (no contexto do MLaaS), é possível obter previsões que podem divulgar informações sobre o conjunto de dados de treinamento. Em particular, você não tem proteção contra ataques de associação, um ataque comum a serviços de aprendizado de máquina em que o adversário deseja determinar se um item específico foi usado no conjunto de dados. Além disso, outros ataques, como processos de memorização não intencional (modelos que aprendem recursos específicos sobre um item de dados), inversão ou extração de modelos ainda são possíveis.\n", + "\n", + "Uma solução geral que é eficaz para muitas das ameaças mencionadas acima é adicionar Privacidade Diferencial. Ele pode ser combinado com a Computação Multipartidária Segura e pode fornecer garantias de segurança muito interessantes. No momento, estamos trabalhando em várias implementações e esperamos propor um exemplo que combine as duas em breve!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Conclusão\n", + "\n", + "Como você viu, o treinamento de um modelo usando SMPC não é complicado do ponto de vista do código, mesmo usando objetos bastante complexos por debaixo dos panos. Com isso em mente, agora você deve analisar seus casos de uso para ver quando a computação criptografada é necessária para treinamento ou avaliação. Se a computação criptografada for muito mais lenta em geral, também poderá ser usada com cuidado para reduzir a sobrecarga geral da computação.\n", + "\n", + "Se você gostou disso e gostaria de participar do movimento de 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", + "### 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 Preservante de Privacidade e como estamos construindo os tijolos para que isso aconteça.\n", + "\n", + "- [Confira os tutoriais do PySyft](https://github.com/OpenMined/PySyft/tree/master/examples/tutorials)\n", + "\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\n", + "\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": { + "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": 4 +} diff --git "a/examples/tutorials/translations/portugu\303\252s/Parte 13b - Classifica\303\247\303\243o segura com Syft Keras e TFE - Servindo o modelo de forma segura.ipynb" "b/examples/tutorials/translations/portugu\303\252s/Parte 13b - Classifica\303\247\303\243o segura com Syft Keras e TFE - Servindo o modelo de forma segura.ipynb" new file mode 100644 index 00000000000..2cb0d1409d8 --- /dev/null +++ "b/examples/tutorials/translations/portugu\303\252s/Parte 13b - Classifica\303\247\303\243o segura com Syft Keras e TFE - Servindo o modelo de forma segura.ipynb" @@ -0,0 +1,259 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Parte 2: Servindo o Modelo de forma segura com Syft Keras" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Agora que você tem um modelo treinado com Keras normal, está pronto para servir algumas previsões privadas. Podemos fazer isso usando o Syft Keras.\n", + "\n", + "Para proteger e servir esse modelo, precisaremos de três TFEWorkers (servidores). Isso ocorre porque o TF Encrypted por debaixo dos panos usa uma técnica de criptografia chamada [multi-party computation (MPC)](https://en.wikipedia.org/wiki/Secure_multi-party_computation). A idéia é dividir os pesos do modelo e dados de entrada em partições, depois enviar uma parte de cada valor para os diferentes servidores. A propriedade-chave é que, se você observar a parte recebida em um servidor, ele não revelará nada sobre o valor original (dados de entrada ou pesos do modelo).\n", + "\n", + "Definiremos um modelo Syft Keras como fizemos no notebook anterior. No entanto, há um truque: antes de instanciar esse modelo, executaremos `hook = sy.KerasHook(tf.keras)`. Isso adicionará três novos métodos importantes à classe Sequencial Keras:\n", + " - `share`: protegerá seu modelo via compartilhamento secreto; por padrão, ele usará o protocolo SecureNN do TF Encrypted para compartilhar seu modelo em segredo entre cada um dos três TFEWorkers. Mais importante, isso adicionará a capacidade de fornecer previsões sobre dados criptografados.\n", + " - `serve`: esta função irá lançar uma fila para servir, de modo que os TFEWorkers pode aceitar pedidos de previsão sobre o modelo seguro de clientes externos.\n", + " - `shutdown_workers`: depois de fornecer previsões particulares, você pode desligar o modelo executando esta função. Ele o instruirá a encerrar os processos do servidor manualmente, se você optar por gerenciar manualmente cada worker.\n", + "\n", + "Se você quiser saber mais sobre o MPC, pode ler este excelente [blog](https://mortendahl.github.io/2017/04/17/private-deep-learning-with-mpc/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import tensorflow as tf\n", + "from tensorflow.keras import Sequential\n", + "from tensorflow.keras.layers import AveragePooling2D, Conv2D, Dense, Activation, Flatten, ReLU, Activation\n", + "\n", + "import syft as sy\n", + "hook = sy.KerasHook(tf.keras)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Modelo" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Como você pode ver, definimos quase exatamente o mesmo modelo de antes, exceto que fornecemos um `batch_input_shape`. Isso permite que o TF Encrypted otimize melhor os cálculos seguros através de formas predefinidas de tensores. Para esta demonstração MNIST, enviaremos dados de entrada com a forma (1, 28, 28, 1). \n", + "Também retornamos o logit em vez de softmax porque essa operação é complexa para executar usando MPC e não precisamos dela para atender a solicitações de previsão." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "num_classes = 10\n", + "input_shape = (1, 28, 28, 1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model = Sequential()\n", + "\n", + "model.add(Conv2D(10, (3, 3), batch_input_shape=input_shape))\n", + "model.add(AveragePooling2D((2, 2)))\n", + "model.add(Activation('relu'))\n", + "model.add(Conv2D(32, (3, 3)))\n", + "model.add(AveragePooling2D((2, 2)))\n", + "model.add(Activation('relu'))\n", + "model.add(Conv2D(64, (3, 3)))\n", + "model.add(AveragePooling2D((2, 2)))\n", + "model.add(Activation('relu'))\n", + "model.add(Flatten())\n", + "model.add(Dense(num_classes, name=\"logit\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Carregando pesos pré-treinados" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Com a função `load_weights`, você pode carregar facilmente os pesos que você salvou anteriormente após treinar seu modelo." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pre_trained_weights = 'short-conv-mnist.h5'\n", + "model.load_weights(pre_trained_weights)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Lançando os workers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Vamos agora criar TFEWorkers (`alice`,` bob` e `carol`) exigidos pelo TF Encrypted para realizar previsões privadas. Para cada TFEWorker, você apenas precisa especificar um host. Em seguida, fazemos combinar esses workers em um cluster.\n", + "\n", + "Esses trabalhadores executam um [servidor TensorFlow](https://www.tensorflow.org/api_docs/python/tf/distribute/Server), que você pode gerenciar manualmente (`AUTO = False`) ou pedir aos workers que gerenciem para você (`AUTO = True`). Se optar por gerenciá-los manualmente, você será instruído a executar um comando de terminal no dispositivo host de cada trabalhador após chamar `cluster.start()` abaixo. Se todos os workers estiverem hospedados em um único dispositivo (por exemplo, `localhost`), você poderá escolher que o Syft gerencie automaticamente o servidor TensorFlow do worker." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "AUTO = False\n", + "\n", + "alice = sy.TFEWorker(host='localhost:4000', auto_managed=AUTO)\n", + "bob = sy.TFEWorker(host='localhost:4001', auto_managed=AUTO)\n", + "carol = sy.TFEWorker(host='localhost:4002', auto_managed=AUTO)\n", + "\n", + "cluster = sy.TFECluster(alice, bob, carol)\n", + "cluster.start()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Proteja o modelo, compartilhando os pesos" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Graças ao `sy.KerasHook (tf.keras)`, você pode chamar o método `share` para transformar seu modelo em um modelo TF Encrypted Keras.\n", + "\n", + "Se você pediu para gerenciar manualmente os servidores acima, essa etapa não será concluída até que todos tenham sido iniciados. Observe que seu firewall pode solicitar que o Python aceite a conexão de entrada." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.share(cluster)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Servindo o modelo" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Perfeito! Agora, chamando `model.serve`, seu modelo está pronto para fornecer algumas previsões privadas. Você pode definir `num_requests` para definir um limite para o número de requisições de previsões atendidas pelo modelo; se não especificado, o modelo será servido até ser interrompido." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.serve(num_requests=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Você está pronto para ir para o notebook **Parte 13c** para fazer requisições de algumas previsões privadas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Hora da limpeza!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Quando o limite de suas requisições for totalmente utilizado, o modelo não estará mais disponível para atender requisições, mas ainda será compartilhado em segredo entre os três workers acima. Você pode matar os workers executando a célula abaixo.\n", + "\n", + "**Parabéns** por concluir a Parte 13b: Classificação segura com Syft Keras e TFE!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.stop()\n", + "cluster.stop()\n", + "\n", + "if not AUTO:\n", + " process_ids = !ps aux | grep '[p]ython -m tf_encrypted.player --config' | awk '{print $2}'\n", + " for process_id in process_ids:\n", + " !kill {process_id}\n", + " print(\"Process ID {id} has been killed.\".format(id=process_id))" + ] + }, + { + "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 +}