Recapitulando: Na Parte 2 deste tutorial, nós treinamos um modelo usando uma versão bem simples do Aprendizado Federado. Isso exigia que cada proprietário dos dados confiasse no proprietário do modelo para poder ver seus gradientes.
Descrição: Neste tutorial, mostraremos como usar as ferramentas avançadas de agregação da Parte 3 para permitir que os pesos sejam agregados por um \"worker seguro\" confiável antes que o modelo resultante final seja enviado de volta ao proprietário do modelo (nesse caso, nós).
Dessa maneira, somente o worker seguro pode ver de quem são os pesos. Talvez possamos saber quais partes do modelo foram alteradas, mas NÃO saberemos qual worker (bob ou alice) fez tal alteração, o que cria uma camada de privacidade.
Autores:
Tradução:
In [ ]:
import torch
import syft as sy
import copy
hook = sy.TorchHook(torch)
from torch import nn, optim
Primeiro, vamos criar dois proprietários de dados (Bob e Alice), cada um com alguns dados. Também vamos inicializar uma máquina segura chamada "secure_worker". Na prática, pode ser um hardware seguro (como o SGX da Intel) ou simplesmente um intermediário confiável.
In [ ]:
# criando alguns workers
bob = sy.VirtualWorker(hook, id="bob")
alice = sy.VirtualWorker(hook, id="alice")
secure_worker = sy.VirtualWorker(hook, id="secure_worker")
# Nosso Dataset de exemplo
data = torch.tensor([[0,0],[0,1],[1,0],[1,1.]], requires_grad=True)
target = torch.tensor([[0],[0],[1],[1.]], requires_grad=True)
# obtenha apontadores para os dados de treinamento de cada worker
# enviando alguns dados de treinamento para bob e alice
bobs_data = data[0:2].send(bob)
bobs_target = target[0:2].send(bob)
alices_data = data[2:].send(alice)
alices_target = target[2:].send(alice)
In [ ]:
# Inicialize o modelo
model = nn.Linear(2,1)
In [ ]:
bobs_model = model.copy().send(bob)
alices_model = model.copy().send(alice)
bobs_opt = optim.SGD(params=bobs_model.parameters(),lr=0.1)
alices_opt = optim.SGD(params=alices_model.parameters(),lr=0.1)
In [ ]:
In [ ]:
for i in range(10):
# Treina o Modelo de Bob
bobs_opt.zero_grad()
bobs_pred = bobs_model(bobs_data)
bobs_loss = ((bobs_pred - bobs_target)**2).sum()
bobs_loss.backward()
bobs_opt.step()
bobs_loss = bobs_loss.get().data
# Treina o Modelo de Alice
alices_opt.zero_grad()
alices_pred = alices_model(alices_data)
alices_loss = ((alices_pred - alices_target)**2).sum()
alices_loss.backward()
alices_opt.step()
alices_loss = alices_loss.get().data
print("Bob:" + str(bobs_loss) + " Alice:" + str(alices_loss))
Agora que cada proprietário de dados possui um modelo parcialmente treinado, é hora de calcular a média entre eles de maneira segura. Conseguimos isso instruindo Alice e Bob a enviar seus modelos para o servidor seguro (confiável).
Observe que esse uso de nossa API significa que cada modelo é enviado DIRETAMENTE ao secure_worker
. Mas, nunca vemos isso.
In [ ]:
alices_model.move(secure_worker)
In [ ]:
bobs_model.move(secure_worker)
Finalmente, o último passo é calcular a média dos modelos treinados por Bob e Alice e, em seguida, usá-lo para definir os valores dos pesos do nosso "modelo" global.
In [ ]:
with torch.no_grad():
model.weight.set_(((alices_model.weight.data + bobs_model.weight.data) / 2).get())
model.bias.set_(((alices_model.bias.data + bobs_model.bias.data) / 2).get())
In [ ]:
iterations = 10
worker_iters = 5
for a_iter in range(iterations):
bobs_model = model.copy().send(bob)
alices_model = model.copy().send(alice)
bobs_opt = optim.SGD(params=bobs_model.parameters(),lr=0.1)
alices_opt = optim.SGD(params=alices_model.parameters(),lr=0.1)
for wi in range(worker_iters):
# Treina o Modelo de Bob
bobs_opt.zero_grad()
bobs_pred = bobs_model(bobs_data)
bobs_loss = ((bobs_pred - bobs_target)**2).sum()
bobs_loss.backward()
bobs_opt.step()
bobs_loss = bobs_loss.get().data
# Treina o Modelo de Alice
alices_opt.zero_grad()
alices_pred = alices_model(alices_data)
alices_loss = ((alices_pred - alices_target)**2).sum()
alices_loss.backward()
alices_opt.step()
alices_loss = alices_loss.get().data
alices_model.move(secure_worker)
bobs_model.move(secure_worker)
with torch.no_grad():
model.weight.set_(((alices_model.weight.data + bobs_model.weight.data) / 2).get())
model.bias.set_(((alices_model.bias.data + bobs_model.bias.data) / 2).get())
print("Bob:" + str(bobs_loss) + " Alice:" + str(alices_loss))
Por fim, queremos garantir que nosso modelo resultante tenha aprendido corretamente, para que possamos avaliá-lo em um conjunto de dados de teste. Neste problema simples, usamos os dados originais, mas, na prática, queremos usar novos dados para entender o quão generalizado o modelo é para exemplos não vistos.
In [ ]:
preds = model(data)
loss = ((preds - target) ** 2).sum()
In [ ]:
print(preds)
print(target)
print(loss.data)
Neste exemplo, o modelo médio resultante está pouco ajustado em relação a como se comportaria um modelo individual treinado localmente, no entanto, pudemos treiná-lo sem expor os dados de treinamento de cada worker. Também fomos capazes de agregar os modelos atualizados de cada worker em um lugar confiável para evitar vazamento de dados do proprietário do modelo.
Em um tutorial futuro, teremos como objetivo fazer nossa agregação segura e confiável diretamente com os gradientes, para que possamos atualizar o modelo com melhores estimativas de gradientes e chegar a um modelo mais forte.
In [ ]:
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!
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.
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
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".
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!
In [ ]: