Recap: En la Parte 2 de este tutorial entrenamos un modelo utilizando una versión muy simple del Aprendizaje Federado. Esto requirió que cada propietario de los datos confiara en el propietario del modelo para poder ver sus gradientes.
Descripción: En este tutorial mostraremos cómo usar los métodos avanzados de agregación de la Parte 3 para que los pesos sean agregados por un "trabajador seguro" confiable antes de que el modelo final sea enviado al propietario del modelo (nosotros).
De esta manera, sólo el trabajador seguro puede ver los gradientes de cada trabajador involucrado. Podríamos deducir qué partes del modelo cambiaron, pero NO sabríamos cuál trabajador (bob o alice) hizo este cambio, lo cual añade una capa de privacidad.
Autores:
Traductores:
In [ ]:
import torch
import syft as sy
import copy
hook = sy.TorchHook(torch)
from torch import nn, optim
Primero, vamos a crear dos propietarios de datos (Bob y Alice) cada uno con una pequeña porción de los datos. También vamos a inicializar una máquina segura llamada "trabajador seguro" ("secure worker"). En la práctica esto podría ser un hardware seguro (como el SGX de Intel) o simplemente un intermediario confiable.
In [ ]:
# crear un par de trabajadores
bob = sy.VirtualWorker(hook, id="bob")
alice = sy.VirtualWorker(hook, id="alice")
secure_worker = sy.VirtualWorker(hook, id="secure_worker")
# Un conjunto de datos de juguete
data = torch.tensor([[0,0],[0,1],[1,0],[1,1.]], requires_grad=True)
target = torch.tensor([[0],[0],[1],[1.]], requires_grad=True)
# Obtén los punteros para realizar el entrenamiento en cada
# trabajador enviando una parte de los datos de entrenamiento
# a bob y 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 [ ]:
# Inicializa el modelo de juguete
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):
# Entrena el 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
# Entrena el 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))
In [ ]:
alices_model.move(secure_worker)
In [ ]:
bobs_model.move(secure_worker)
Finalmente, la última etapa de esta iteración (epoch) es promediar el modelo entrenado de Bob y Alice y usar esto para establecer los valores de nuestro 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):
# Entrena el 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
# Entrena el 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))
Finalmente queremos asegurarnos que nuestro modelo final haya aprendido correctamente, así que lo evaluaremos en un conjunto de datos de prueba. En este ejemplo de juguete utilizaremos los datos originales, pero en la práctica queremos usar nuevos datos para entender bien cómo el modelo se generaliza a nuevos ejemplos.
In [ ]:
preds = model(data)
loss = ((preds - target) ** 2).sum()
In [ ]:
print(preds)
print(target)
print(loss.data)
En este ejemplo de juguete el modelo promediado se está subajustando con relación al comportamiento de un modelo con texto sin formato ("plaintext"). Sin embargo, pudimos entrenarlo sin exponer los datos de los trabajadores. También pudimos agregar los modelos actualizados de cada trabajador con un agregador confiable para prevenir la fuga de datos al propietario del modelo.
En un tutorial futuro intentaremos realizar nuestra agregación confiable directamente con los gradientes, de tal manera que podremos actualizar el modelo con mejores estimaciones de los gradientes y tener un mejor modelo.
In [ ]:
¡Felicitaciones por completar esta parte del tutorial! Si te gustó y quieres unirte al movimiento para preservar la privacidad, propiedad descentralizada de IA y la cadena de suministro de IA (datos), puedes hacerlo de las ¡siguientes formas!
La forma más fácil de ayudar a nuestra comunidad es por darle estrellas a ¡los repositorios de Github! Esto ayuda a crear consciencia de las interesantes herramientas que estamos construyendo.
La mejor manera de mantenerte actualizado con los últimos avances es ¡unirte a la comunidad! Tú lo puedes hacer llenando el formulario en http://slack.openmined.org
La mejor manera de contribuir a nuestra comunidad es convertirte en un ¡contribuidor de código! En cualquier momento puedes ir al Github Issues de PySyft y filtrar por "Proyectos". Esto mostrará todos los tiquetes de nivel superior dando un resumen de los proyectos a los que ¡te puedes unir! Si no te quieres unir a un proyecto, pero quieres hacer un poco de código, también puedes mirar más mini-proyectos "de una persona" buscando por Github Issues con la etiqueta "good first issue".
Si no tienes tiempo para contribuir a nuestra base de código, pero quieres ofrecer tu ayuda, también puedes aportar a nuestro Open Collective". Todas las donaciones van a nuestro web hosting y otros gastos de nuestra comunidad como ¡hackathons y meetups!
In [ ]: