Parte 12: Treinar uma Rede Neural criptografada com dados criptografados

Neste tutorial, iremos utilizar todas as técnicas que aprendemos até agora para realizar o treinamento de uma rede neural (e a previsão) enquanto tanto o modelo quanto os dados são criptografados.

Em particular, iremos apresentar nossa próprio algoritmo Autograd personalizado que funciona em computações criptografadas.

Autores:

Tradução:

Passo 1: Criar workers e dados de exemplo


In [ ]:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import syft as sy

In [ ]:
# Prepare as configurações iniciais
hook = sy.TorchHook(torch) 

alice = sy.VirtualWorker(id="alice", hook=hook)
bob = sy.VirtualWorker(id="bob", hook=hook)
james = sy.VirtualWorker(id="james", hook=hook)

In [ ]:
# Dataset de exemplo
data = torch.tensor([[0,0],[0,1],[1,0],[1,1.]])
target = torch.tensor([[0],[0],[1],[1.]])

# Modelo de exemplo
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(2, 2)
        self.fc2 = nn.Linear(2, 1)

    def forward(self, x):
        x = self.fc1(x)
        x = F.relu(x)
        x = self.fc2(x)
        return x
model = Net()

Passo 2: Criptografar o modelo e os dados

A encriptação aqui vem em dois passos. Como a Computação Multi-Partes Segura (i.e Secure Multi-Party Computation) só funciona em números inteiros, para operar sobre números decimais (como pesos e ativações), precisamos codificar todos os nossos números usando Precisão Fixa, o que nos dará vários bits de precisão decimal. Nós fazemos isso com a chamada .fix_precision().

Podemos então fazer a chamada .share() como temos para outras demonstrações, que irão encriptar todos os valores, partilhando-os entre Alice e Bob. Note que nós também definimos requires_grad para True, que também adiciona um método especial de auto gradiente para dados criptografados. De fato, como a Computação Multi-Partes Segura não funciona em valores de ponto flutuante, não podemos usar o auto gradiente do PyTorch. Portanto, precisamos adicionar um nó especial denominado AutogradTensor que calcula o grafo do gradiente através de retropropagação (i.e backpropagation). Você pode imprimir qualquer um desses elementos para ver se ele inclui um AutogradTensor.


In [ ]:
# Nós criptografamos tudo
data = data.fix_precision().share(bob, alice, crypto_provider=james, requires_grad=True)
target = target.fix_precision().share(bob, alice, crypto_provider=james, requires_grad=True)
model = model.fix_precision().share(bob, alice, crypto_provider=james, requires_grad=True)

In [ ]:
print(data)

Passo 3: Treinamento

E agora nós podemos treinar utilizando operações básicas de tensores


In [ ]:
opt = optim.SGD(params=model.parameters(),lr=0.1).fix_precision()

for iter in range(20):
    # 1) Apague todos os antigos valores de gradient (se existirem)
    opt.zero_grad()

    # 2) Faça uma predição
    pred = model(data)

    # 3) Calcule a função de perda (i.e loss function)
    loss = ((pred - target)**2).sum()

    # 4) Descubra quais pesos estão elevando a função de perda
    loss.backward()

    # 5) Mude esses pesos
    opt.step()

    # 6) Exiba o progresso
    print(loss.get().float_precision())

A função de perda realmente diminuiu!

Impacto da precisão fixa

Você pode estar se perguntando se criptografar tudo pode gerar um impacto no decrescimento da perda (i.e loss). Na verdade, como o cálculo teórico é o mesmo, os números estão muito próximos do treinamento não criptografado. Você pode verificar isso executando o mesmo exemplo sem criptografia e com uma inicialização determinística do modelo. Como esta a seguir, no método __init__:

with torch.no_grad():
    self.fc1.weight.set_(torch.tensor([[ 0.0738, -0.2109],[-0.1579,  0.3174]], requires_grad=True))
    self.fc1.bias.set_(torch.tensor([0.,0.1], requires_grad=True))
    self.fc2.weight.set_(torch.tensor([[-0.5368,  0.7050]], requires_grad=True))
    self.fc2.bias.set_(torch.tensor([-0.0343], requires_grad=True))

A ligeira diferença que você pode observar deve-se ao arredondamento dos valores realizados durante a transformação para precisão fixa. O valor padrão de precision_fractional é 3, e se você reduz para 2 a divergência com o valor original aumenta, enquanto que se você escolher precision_fractional = 4, a divergência reduz.

Parabéns!!! - Hora de se juntar a comunidade!

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!

Dê-nos uma estrela em nosso repo do PySyft no GitHub

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.

Junte-se ao Slack!

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

Contribua com o projeto!

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".

Doar

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!

Página do Open Collective do OpenMined