In [ ]:
epochs = 10
n_test_batches = 200

Parte 11 - Clasificación con Aprendizaje Profundo y Seguro

Tus datos importan, tu modelo también

Los datos son la fuerza detrás del aprendizaje de máquina. Las organizaciones que crean y recolectan datos tienen la capacidad de construir y entrenar sus propios modelos de aprendizaje de máquina. Esto les permite ofrecer a organizaciones externas el uso de tales modelos como un servicio (en inglés, MLaaS). Esto es útil ya que otras organizaciones tal vez no tengan la habilidad de crear estos modelos pero que aún así estarían interesados en usarlos para hacer predicciones de sus propios datos.

Sin embargo, un modelo hospedado en la nube aún preserva un problema de privacidad/propiedad intelectual. Para que organizaciones externas lo usen tienen dos opciones: o suben sus datos (por ejemplo, imágenes a ser clasificadas) o descargan el modelo. Subir sus datos puede ser problemático desde el punto de vista de la privacidad, pero descargar el modelo quizá no es una buena opción si la organización que lo creó o es propietario de ese modelo está preocupado por su propiedad intelectual.

Computando con datos encriptados

En este contexto, una solución potencial sería encriptar tanto el modelo como los datos de tal manera que una organización pueda usar el modelo de otra organización sin socavar su propiedad intelectual. Varios esquemas de encriptación existen que permiten el cómputo sobre datos encriptado, dentro de los cuales existe la Computación Segura Multiparte (que se introdujo en detalle aquí en el tutorial 5) que consiste en una co-propiedad privada. Este esquema depende de protocolos de criptografía tales como SecureNN y SPDZ, cuyos detalles pueden conocerse en esta excelente entrada de blog.

Configuración

La configuración exacta de este tutorial es la siguiente: considera que estás en un servidor y tienes un conjunto de datos. Primero defines y entrenas un modelo con estos datos de entrenamiento privados. Luego, te pones en contacto con un cliente que tiene parte de sus datos y que quisiera acceder tu modelo para hacer algunas predicciones.

Encriptas tu modelo (una red neuronal). El cliente encripta sus datos. Ambos usan sus herramientas encriptadas para usar el modelo y clasificar los datos. Finalmente, el resultado de la predicción regresa al cliente en una manera encriptada para que el servidor (o sea, tú) no pueda saber nada de los datos del cliente (no sabes ni los datos de entrada ni la predicción).

Idealmente querríamos compartir de manera aditiva las entradas del cliente entre el servidor y vice versa con el modelo. Por simplicidad, ambas partes las tendrán dos trabajadores alice y bob. Si consideras que alice es del cliente y bob del servidor entonces este caso es equivalente al anterior.

El cómputo es seguro en el modelo adversarial honesto-pero-seguro (honest-but-curious adversary model), que es estándar en muchos marcos del CMP.

Ahora tenemos todo lo que necesitamos... ¡a comenzar!

Autor:

Traductores:

Imports y especificaciones del modelo


In [ ]:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms

También necesitamos ejecutar comandos específicos para importar/comenzar PySyft. Creamos varios trabajadores (llamados client, bob y alice). Por último, definimos el crypto_provider que nos brinda todas las primitivas criptográficas que potencialmente necesitemos (Ve nuestro tutorial sobre CSMP para más detalles).


In [ ]:
import syft as sy
hook = sy.TorchHook(torch) 
client = sy.VirtualWorker(hook, id="client")
bob = sy.VirtualWorker(hook, id="bob")
alice = sy.VirtualWorker(hook, id="alice")
crypto_provider = sy.VirtualWorker(hook, id="crypto_provider")

Definimos la configuración de la tarea de aprendizaje


In [ ]:
class Arguments():
    def __init__(self):
        self.batch_size = 64
        self.test_batch_size = 50
        self.epochs = epochs
        self.lr = 0.001
        self.log_interval = 100

args = Arguments()

Cargar los datos y mandarla a los trabajadores

En nuestra configuración asumimos que nuestro servidor tiene acceso a datos para entrenar su modelo. Aquí usamos el conjunto de entrenamiento MNIST.


In [ ]:
train_loader = torch.utils.data.DataLoader(
    datasets.MNIST('../data', train=True, download=True,
                   transform=transforms.Compose([
                       transforms.ToTensor(),
                       transforms.Normalize((0.1307,), (0.3081,))
                   ])),
    batch_size=args.batch_size, shuffle=True)

Segundo, el cliente tiene datos y quisiera poder hacer predicciones con ella utilizando el modelo del servidor. Este cliente encripta sus datos compartiendolos aditivamente con los dos trabajadores alice y bob.

El CSMP usa protocolos criptográficos que requieren trabajo sobre enteros. Utilizamos aquí el la abstracción del tensor de PySyft para convertir tensores PyTorch Float a tensores de precisión fija (Fixed Precision Tensors) usando .fix_precision(). Por ejemplo 0.123 con precisión 2 redondea al 2do decimal tal que el número guardado es 12.


In [ ]:
test_loader = torch.utils.data.DataLoader(
    datasets.MNIST('../data', train=False,
                   transform=transforms.Compose([
                       transforms.ToTensor(),
                       transforms.Normalize((0.1307,), (0.3081,))
                   ])),
    batch_size=args.test_batch_size, shuffle=True)

private_test_loader = []
for data, target in test_loader:
    private_test_loader.append((
        data.fix_precision().share(alice, bob, crypto_provider=crypto_provider),
        target.fix_precision().share(alice, bob, crypto_provider=crypto_provider)
    ))

Especificación de la Red Neuronal Prealimentada

Aquí está la especificación de la red utilizada por el servidor.


In [ ]:
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(784, 500)
        self.fc2 = nn.Linear(500, 10)

    def forward(self, x):
        x = x.view(-1, 784)
        x = self.fc1(x)
        x = F.relu(x)
        x = self.fc2(x)
        return x

Comienza el entrenamiento

El entrenamiento es hecho de manera local así que este es meramente un entrenamiento local de PyTorch. Nada especial aquí.


In [ ]:
def train(args, model, train_loader, optimizer, epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        optimizer.zero_grad()
        output = model(data)
        output = F.log_softmax(output, dim=1)
        loss = F.nll_loss(output, target)
        loss.backward()
        optimizer.step()
        if batch_idx % args.log_interval == 0:
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * args.batch_size, len(train_loader) * args.batch_size,
                100. * batch_idx / len(train_loader), loss.item()))

In [ ]:
model = Net()
optimizer = torch.optim.Adam(model.parameters(), lr=args.lr)

for epoch in range(1, args.epochs + 1):
    train(args, model, train_loader, optimizer, epoch)

In [ ]:
def test(args, model, test_loader):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            output = model(data)
            output = F.log_softmax(output, dim=1)
            test_loss += F.nll_loss(output, target, reduction='sum').item() # suma la pérdida
            pred = output.argmax(1, keepdim=True) # obtén el índice del máximo de la probabilidad logarítmica
            correct += pred.eq(target.view_as(pred)).sum().item()

    test_loss /= len(test_loader.dataset)

    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))

In [ ]:
test(args, model, test_loader)

¡Ahora nuestro modelo está entrenado y listo para ser ofrecido como un servicio!

Evaluación segura

Ahora, como el servidor, mandamos el modelo a los trabajadores que tienen los datos. Porque el modelo es sensible ante la información (¡lo has optimizado!), no tienes que compartir sus pesos, así que compartes el modelo de manera secreta tal como lo hicimos con el conjunto de datos un poco antes.


In [ ]:
model.fix_precision().share(alice, bob, crypto_provider=crypto_provider)

Esta función de prueba realiza la evaluación encriptada. Los pesos del modelo, los datos de entrada, la predicción y el objetivo usado para la evaluación todos están encriptados.

Sin embargo, la sintaxis es muy similar a el entrenamiento de un modelo con PyTorch. Increíble ¿no?

La única cosa que podemos desencriptar, del lado del servidor, es la evaluación final para verificar que las predicciones fueron, en promedio, buenas.


In [ ]:
def test(args, model, test_loader):
    model.eval()
    n_correct_priv = 0
    n_total = 0
    with torch.no_grad():
        for data, target in test_loader[:n_test_batches]:
            output = model(data)
            pred = output.argmax(dim=1) 
            n_correct_priv += pred.eq(target.view_as(pred)).sum()
            n_total += args.test_batch_size
# Esta función de prueba realiza la evaluación encriptada. Los pesos del modelo, los datos de entrada, la predicción y el objetivo usado para la evaluación todos están encriptados.

# Sin embargo, la sintaxis es muy similar a el entrenamiento de un modelo con PyTorch. Increíble ¿no?

# La única cosa que podemos desencriptar, del lado del servidor, es la evaluación final para verificar que las predicciones fueron, en promedio, buenas.
            n_correct = n_correct_priv.copy().get().float_precision().long().item()
    
            print('Test set: Accuracy: {}/{} ({:.0f}%)'.format(
                n_correct, n_total,
                100. * n_correct / n_total))

In [ ]:
test(args, model, private_test_loader)

¡Y voilà! Aquí lo tienes. Has aprendido cómo hacer predicciones seguras de punta a punta: los pesos del modelo del servidor no se han filtrado al cliente y el servidor no tiene información acerca de los datos de entrada o de la salida de la clasificación.

En cuanto a rendimiento, clasificar una imagen toma menos de 0.1 segundos, aproximadamente 33ms en mi laptop (2.7 GHz Intel Core i7, 16GB RAM). Sin embargo, esto usa comunicación muy rápida (todos los trabajadores están en mi máquina local). El desempeño dependerá de qué tan rápido se pueden comunicar los trabajadores.

Conclusión

Ya viste lo fácil que es usar PyTorch y PySyft para hacer Aprendizaje de Máquina Seguro de manera práctica y proteger los datos del usuario, sin tener que ser un experto en criptografía.

Tendremos más sobre este tema pronto, incluyendo capas convolucionales para hacer un benchmark apropiado del desempeño de PySyft con relación a otras librerías, así como entrenamiento encriptado y privado de redes neuronales, lo cual es necesario cuando una organización recurre a datos externos y sensibles para entrenar su propio modelo. ¡Mantente al tanto!

¡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!

Dale una estrella a PySyft en GitHub

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.

¡Únete a nuestro Slack!

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

¡Únete a un proyecto de código!

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

Donar

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!

OpenMined's Open Collective Page


In [ ]: