赞
踩
Time: 2019-11-19
教程作者:Théo Ryffel -
GitHub: [@LaRiffle](LaRiffle - Overview)
联邦学习是非常令人激动且日益火热的机器学习技术,只在构建一种这样的系统:能够在分布式数据上学习。
想法是让数据仍然保持在数据生产者手里(也叫作_worker_
),此想法能够保护隐私和所有权,只是将模型在结点之间进行共享。
一个能够立马用上的应用是,在手机上书写文本时帮你预测下一个单词:而你并不想这些数据发送到中心化服务器上用于训练,比如你的短信。
联邦学习的兴起和数据隐私意识的觉醒有很大的关系,比如GDPR法案,强制数据保护。
谷歌和苹果已经开始大量投入资金研发这种技术,但是他们还没有公开自己的工具。
OpenMined相信任何人想要开发、部署机器学习模型应当能够花费少量的力气就能完成隐私保护。
我们构建了一个工具,一行代码就能加密数据,参考[使用SPDZ训练CNN](Training a CNN using SPDZ)。
现在我们发布的联邦学习框架,综合了PyTorch框架用于提供一个易用的接口来完成构建安全和可扩展的模型。
本章,我们将直接使用经典的案例,[使用PyTorch在MNIST上训练CNN模型](https://github.com/pytorch/examples/blob/master/mnist/main.py),然后使用PySyft框架将其改造为联邦学习方法。
本章将会深入讲解每一行代码改造背后的原理。
这个材料也可以参考[博客: 10行代码改造模型为联邦学习]((Federated Learning in 10 lines))。
开始吧。
- import torch
- import torch.nn as nn
- import torch.nn.functional as F
- import torch.optim as optim
- from torchvision import datasets, transforms
然后是PySyft的准备:
- import syft as sy # <-- NEW: import the Pysyft library
- hook = sy.TorchHook(torch) # <-- NEW: hook PyTorch ie add extra functionalities to support Federated Learning
- bob = sy.VirtualWorker(hook, id="bob") # <-- NEW: define remote worker bob
- alice = sy.VirtualWorker(hook, id="alice") # <-- NEW: and alice
- class Arguments():
- def __init__(self):
- self.batch_size = 64
- self.test_batch_size = 1000
- self.epochs = 10
- self.lr = 0.01
- self.momentum = 0.5
- self.no_cuda = False
- self.seed = 1
- self.log_interval = 30
- self.save_model = False
-
- args = Arguments()
-
- use_cuda = not args.no_cuda and torch.cuda.is_available()
-
- torch.manual_seed(args.seed)
-
- device = torch.device("cuda" if use_cuda else "cpu")
-
- kwargs = {'num_workers': 1, 'pin_memory': True} if use_cuda else {}

这个写法可以作为使用PyTorch框架开发的标准流程。
用类来组织在PyTorch中的代码。
节点应该是worker更精准的翻译。
我们首先加载数据然后转换训练数据集为联邦学习用的数据集,并将数据集分割分发到多个节点。
PS. 数据集还是我们本机先有的,处理好发送到的其他节点,如何在其他节点上已经有了数据,远程预处理呢?这些目前还没看到方法。
使用方法.federate
,联邦数据集现在就交给了联邦型的DataLoader
,测试数据集保持不变。
改造训练数据集:
- federated_train_loader = sy.FederatedDataLoader(
- datasets.MNIST('../data', train=True, download=True, transform=transforms.Compose([
- transforms.ToTensor(),
- transforms.Normalize((0.1307,),(0.3081,)) # 这是ImageNet数据集的方差和均值
- ])).federate((bob, alice)), # 将数据集分发到所有的指定节点,现在是联邦数据集了
- batch_size=args.batch_size, shuffle=True, **kwargs)
数据是每个人拿到一样还是如何分割的呢?
无需改造训练数据集:
- 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, **kwargs)
现在我们使用CNN模型,和官方样例相同。
MNIST数据集的单张图片大小是28x28x1
,所以按照下面的模型定义,单张图片最后输出的大小是4x4x50
。
28 x 28 ->(conv1) 24 x 24 ->(max_pool2d) 12 x 12 ->(conv2) 8 x 8 ->(max_pool2d) 4x4
是可以直接推算出来的。
- class Net(nn.Module):
- def __init__(self):
- super(Net, self).__init__()
- self.conv1 = nn.Conv2d(1, 20, 5, 1)
- self.conv2 = nn.Conv2d(20, 50, 5, 1)
- self.fc1 = nn.Linear(4*4*50, 500)
- self.fc2 = nn.Linear(500, 10)
-
- def forward(self, x):
- x = F.relu(self.conv1(x))
- x = F.max_pool2d(x, 2, 2)
- x = F.relu(self.conv2(x))
- x = F.max_pool2d(x, 2, 2)
- x = x.view(-1, 4*4*50)
- x = F.relu(self.fc1(x))
- x = self.fc2(x)
- return F.log_softmax(x, dim=1)
- net = Net()
- print(net)
- '''
- Net(
- (conv1): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
- (conv2): Conv2d(20, 50, kernel_size=(5, 5), stride=(1, 1))
- (fc1): Linear(in_features=800, out_features=500, bias=True)
- (fc2): Linear(in_features=500, out_features=10, bias=True)
- )
- '''

训练过程
针对训练过程而言,因为训练数据是分布在alice
,bob
对应的节点上,每一个batch
,我们都需要将模型发送到正确的位置。
然后,在远程执行这些操作,语法和在本地训练PyTorch模型一致。
结束后,你将更新好的模型和损失函数值拿回来,然后想办法更新模型。
- def train(args, model, device, federated_train_loader, optimizer, epoch):
- model.train() # 设置模型为训练模式
- for batch_index, (data, target) in enumerate(federated_train_loader):
- model.send(data.location)
- data, target = data.to(device), target.to(device) # 如果有GPU搬到GPU计算
- optimizer.zero_grad()
- output = model(data)
- loss = F.nll_loss(output, target)
- loss.backward() # 计算梯度
- optimizer.step() # 更新参数
- model.get() # 取回模型
- if batch_index % args.log_interval == 0:
- loss = loss.get() # 每隔一段时间取回loss看看
- print('Train Epoch: {} [{}/{} ({:.0f}%)]tLoss: {:.6f}'.format(
- epoch, batch_idx * args.batch_size, len(federated_train_loader) * args.batch_size,
- 100. * batch_idx / len(federated_train_loader), loss.item()))
- ```

测试函数不变:
- def test(args, model, device, test_loader):
- model.eval() # 设置模型为评估模式
- test_loss = 0
- correct = 0
- with torch.no_grad():
- for data, target in test_loader:
- data, target = data.to(device), target.to(device)
- output = model(data)
- test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
- pred = output.argmax(1, keepdim=True) # get the index of the max log-probability
- 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)))

model.train(), model.eval()
只在特定的模型上有效果。不要理解为定义好模型就能训练了~~
- %%time
- model = Net().to(device)
- optimizer = optim.SGD(model.parameters(), lr=args.lr) # TODO momentum is not supported at the moment
-
- for epoch in range(1, args.epochs + 1):
- train(args, model, device, federated_train_loader, optimizer, epoch)
- test(args, model, device, test_loader)
-
- if (args.save_model):
- torch.save(model.state_dict(), "mnist_cnn.pt")
训练log(节选):
- Train Epoch: 4 [59520/60032 (99%)] Loss: 0.057204
- Test set: Average loss: 0.0534, Accuracy: 9828/10000 (98%)
-
- Train Epoch: 5 [0/60032 (0%)] Loss: 0.161142
- Train Epoch: 5 [1920/60032 (3%)] Loss: 0.031823
可以看到在测试集上的平均损失是:0.0534,准确率为98%(训练4个epoch)。
非联邦学习的训练方法中,4个epoch的训练效果如下:
- Train Epoch: 4 [56320/60000 (94%)] Loss: 0.029797
- Train Epoch: 4 [56960/60000 (95%)] Loss: 0.016238
- Train Epoch: 4 [57600/60000 (96%)] Loss: 0.090854
- Train Epoch: 4 [58240/60000 (97%)] Loss: 0.088320
- Train Epoch: 4 [58880/60000 (98%)] Loss: 0.097426
- Train Epoch: 4 [59520/60000 (99%)] Loss: 0.006990
-
- Test set: Average loss: 0.0348, Accuracy: 9897/10000 (99%)
相比较于普通的PyTorch训练方式,联邦学习训练方式耗时有多长呢?
计算时间上,是普通的训练方式的1.9倍,不过和后面我们要添加的特性来说这是不值一提的时间消耗。
我们大概修改了10行左右的代码就完成了从官方PyTorch在MNIST数据集上的训练样例到真实的联邦学习设定的改造。
是的,还有很多可以改进的地方。我们可以让计算并行操作(毕竟是分布在多个节点上),以及执行联邦均值,每隔n个batch更新中心服务器上的模型以便减少通信消耗。
此外,还有许多新特性可以添加以使得联邦学习能够用在产品环境中,等到特性公布后我们会即时更新相关教程。
现在你应当能自己实现联邦学习模型了。
Peace & Love.
Enjoy.
END at 2019-11-19 9:24 pm
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。